feat: Hex Wars - Schlangen-Mechanik, nur vom Kopf aus expandieren

This commit is contained in:
2026-06-28 13:02:55 +02:00
parent c9de0867e5
commit 93807f7672
2 changed files with 73 additions and 22 deletions

View File

@@ -35,7 +35,7 @@ function HelpModal({ onClose }) {
</p>
<p><strong style={{ color:'#fff' }}>Ablauf:</strong><br/>
Beide starten in gegenüberliegenden Ecken. Reihum wählst du ein freies Feld das an dein Gebiet grenzt (auch diagonal) und besetzt es. Tippe es an, bestätige fertig.
Beide starten in gegenüberliegenden Ecken. Du hast einen <strong style={{ color:myColor }}>Kopf</strong> dein letzter Zug, markiert mit einem leuchtenden Rahmen. Du kannst <strong>nur an den Kopf anbauen</strong> (alle 8 Richtungen). Dein Gebiet bleibt erhalten, aber du kannst nicht mehr von alten Feldern aus expandieren. Tippe ein Feld an, bestätige fertig.
</p>
<div style={{ background:'rgba(255,255,255,0.04)', borderRadius:8, padding:'12px 14px', marginBottom:12, border:'1px solid rgba(255,255,255,0.08)' }}>
@@ -169,18 +169,27 @@ function GameBoard({ game, myId, onMove, toast }) {
} catch {}
}, [game.last_event]);
const isAdj = (r, c) => DIRS8.some(([dr,dc]) => {
const nr=r+dr, nc=c+dc;
return nr>=0&&nr<GRID&&nc>=0&&nc<GRID&&grid[nr][nc]===myId;
});
// Schlangen-Kopf aus Server-Response
const myHeadStr = game.myHead;
const myHead = myHeadStr ? myHeadStr.split(',').map(Number) : null;
const [myHR, myHC] = myHead ?? [null, null];
const isAdjacentToHead = (r, c) => {
if (myHR === null) return false;
return Math.abs(r - myHR) <= 1 && Math.abs(c - myHC) <= 1 && !(r === myHR && c === myHC);
};
const canClick = (r, c) => {
if (!isMyTurn || game.status !== 'active') return false;
if (grid[r][c] !== 0) return false;
// T_ROCK kommt vom Server auch im Nebel nicht — Felsen sind immer sichtbar
// T_FOG darf canClick NICHT blocken: Felder können klickbar aber noch im Nebel sein
if (hasTerrain && terrain[r][c] === T_ROCK) return false;
return isAdj(r, c);
// Schlangen-Regel: nur Felder an den Kopf
if (myHead) return isAdjacentToHead(r, c);
// Fallback für alte Spiele ohne head
return DIRS8.some(([dr,dc]) => {
const nr=r+dr, nc=c+dc;
return nr>=0&&nr<GRID&&nc>=0&&nc<GRID&&grid[nr][nc]===myId;
});
};
const handleCellClick = (r, c) => {
@@ -243,6 +252,7 @@ function GameBoard({ game, myId, onMove, toast }) {
const clic = canClick(r, c);
const isPend = pending?.row === r && pending?.col === c;
const isBlast = blastHighlight.has(`${r}-${c}`);
const isHead = myHR === r && myHC === c; // eigener Kopf
const isFog = t === T_FOG;
const isRock = t === T_ROCK;
const isGold = t === T_GOLD;
@@ -271,8 +281,9 @@ function GameBoard({ game, myId, onMove, toast }) {
}
if (isBlast) { bg = '#ff440066'; border = '2px solid #ff4400'; }
if (isHead && !isPend) { border = `2px solid ${myColor}`; } // Kopf immer markieren
if (isPend) { bg = myColor; border = `2px solid ${myColor}`; }
else if (clic && !isPend && !isBlast) border = `1px solid ${myColor}44`;
else if (clic && !isPend && !isBlast && !isHead) border = `1px solid ${myColor}44`;
return (
<div
@@ -341,7 +352,7 @@ function GameBoard({ game, myId, onMove, toast }) {
)}
{isMyTurn && !pending && (
<div style={{ color:myColor, fontFamily:'monospace', fontSize:11, textAlign:'center', padding:'4px 0' }}>
Wähle ein Feld Vorsicht vor Minen! 💣
Baue vom Kopf aus Vorsicht vor Minen! 💣
</div>
)}
{pending && (