139 lines
4.7 KiB
JavaScript
139 lines
4.7 KiB
JavaScript
/**
|
||
* sockets/combat.js – Server-seitige Kampfphasen-Logik für 1v1
|
||
*
|
||
* ZWEI PHASEN PRO ZUG:
|
||
*
|
||
* Phase 1 – BEWEGUNG (nur Karten des aktiven Spielers, vorderste zuerst)
|
||
* Phase 2 – ANGRIFF (nur Karten des aktiven Spielers, Slot 11→1)
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
/**
|
||
* @param {Object} boardState – wird in-place verändert
|
||
* @param {string} leftSlot – 'player1' oder 'player2' (wer links steht)
|
||
* @param {string} activeSlot – 'player1' oder 'player2' (wer gerade am Zug ist)
|
||
* @returns {Array} – geordnete Event-Liste für den Client
|
||
*/
|
||
function runCombatPhase(boardState, leftSlot, activeSlot) {
|
||
const events = [];
|
||
|
||
/* ════════════════════════════════════════════════════════
|
||
PHASE 1: BEWEGUNG
|
||
Nur Karten des aktiven Spielers bewegen sich.
|
||
Vorderste Karte zuerst → Kette kann aufrücken.
|
||
Linker Spieler (dir +1): höchste Slot-Zahl zuerst
|
||
Rechter Spieler (dir -1): niedrigste Slot-Zahl zuerst
|
||
════════════════════════════════════════════════════════ */
|
||
|
||
const myCards = [];
|
||
|
||
for (let slotIndex = 1; slotIndex <= 11; slotIndex++) {
|
||
for (const row of ['row1', 'row2']) {
|
||
const slotId = `${row}-slot-${slotIndex}`;
|
||
if (boardState[slotId]?.owner === activeSlot) {
|
||
myCards.push(slotId);
|
||
}
|
||
}
|
||
}
|
||
|
||
const isActiveLeft = activeSlot === leftSlot;
|
||
const dir = isActiveLeft ? 1 : -1;
|
||
|
||
// Vorderste Karte zuerst
|
||
myCards.sort((a, b) => {
|
||
const ia = parseInt(a.split('-slot-')[1], 10);
|
||
const ib = parseInt(b.split('-slot-')[1], 10);
|
||
return isActiveLeft ? ib - ia : ia - ib; // links: 11→1 | rechts: 1→11
|
||
});
|
||
|
||
for (const startSlotId of myCards) {
|
||
const entry = boardState[startSlotId];
|
||
if (!entry) continue;
|
||
|
||
const { card } = entry;
|
||
const row = startSlotId.split('-slot-')[0];
|
||
const race = card.race ?? 0;
|
||
|
||
let currentPos = parseInt(startSlotId.split('-slot-')[1], 10);
|
||
let currentSlotId = startSlotId;
|
||
|
||
for (let step = 0; step < race; step++) {
|
||
const nextPos = currentPos + dir;
|
||
if (nextPos < 1 || nextPos > 11) break;
|
||
|
||
const nextSlotId = `${row}-slot-${nextPos}`;
|
||
if (boardState[nextSlotId]) break; // eigene oder feindliche Karte blockiert
|
||
|
||
delete boardState[currentSlotId];
|
||
boardState[nextSlotId] = entry;
|
||
|
||
events.push({ type: 'move', from: currentSlotId, to: nextSlotId, owner: activeSlot });
|
||
|
||
currentSlotId = nextSlotId;
|
||
currentPos = nextPos;
|
||
}
|
||
}
|
||
|
||
/* ════════════════════════════════════════════════════════
|
||
PHASE 2: ANGRIFF
|
||
Nur Karten des aktiven Spielers greifen an.
|
||
Feste Reihenfolge: Slot 11 → 1, row1 vor row2.
|
||
Snapshot nach den Bewegungen.
|
||
════════════════════════════════════════════════════════ */
|
||
|
||
const attackOrder = [];
|
||
for (let slotIndex = 11; slotIndex >= 1; slotIndex--) {
|
||
for (const row of ['row1', 'row2']) {
|
||
const slotId = `${row}-slot-${slotIndex}`;
|
||
if (boardState[slotId]?.owner === activeSlot) {
|
||
attackOrder.push(slotId);
|
||
}
|
||
}
|
||
}
|
||
|
||
for (const slotId of attackOrder) {
|
||
const entry = boardState[slotId];
|
||
if (!entry) continue;
|
||
|
||
const { card } = entry;
|
||
const row = slotId.split('-slot-')[0];
|
||
const currentPos = parseInt(slotId.split('-slot-')[1], 10);
|
||
const range = card.range ?? 0;
|
||
|
||
for (let r = 1; r <= range; r++) {
|
||
const targetPos = currentPos + dir * r;
|
||
if (targetPos < 1 || targetPos > 11) break;
|
||
|
||
const targetSlotId = `${row}-slot-${targetPos}`;
|
||
const target = boardState[targetSlotId];
|
||
|
||
if (!target) continue; // leeres Feld → weiter scannen
|
||
if (target.owner === activeSlot) continue; // eigene Karte → Range geht hindurch
|
||
|
||
// Feindliche Karte → Angriff
|
||
const atk = card.attack ?? 0;
|
||
target.card = { ...target.card, defends: (target.card.defends ?? 0) - atk };
|
||
|
||
events.push({
|
||
type : 'attack',
|
||
from : slotId,
|
||
to : targetSlotId,
|
||
damage : atk,
|
||
remainingDef: target.card.defends,
|
||
});
|
||
|
||
if (target.card.defends <= 0) {
|
||
delete boardState[targetSlotId];
|
||
events.push({ type: 'die', slotId: targetSlotId });
|
||
}
|
||
|
||
break; // nur erste feindliche Karte angreifen
|
||
}
|
||
}
|
||
|
||
return events;
|
||
}
|
||
|
||
module.exports = { runCombatPhase };
|