dok/sockets/combat.js
2026-04-13 18:08:43 +01:00

110 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* sockets/combat.js Server-seitige Kampfphasen-Logik für 1v1
*
* PRO KARTE: erst bewegen, dann sofort angreifen → dann nächste Karte.
*
* Reihenfolge (nur Karten des aktiven Spielers):
* Linker Spieler (dir +1): höchste Slot-Zahl zuerst (11→1)
* Rechter Spieler (dir -1): niedrigste Slot-Zahl zuerst (1→11)
* → Vorderste Karte verarbeitet zuerst, macht Platz für die dahinter.
*/
'use strict';
/**
* @param {Object} boardState wird in-place verändert
* @param {string} leftSlot wer links steht ('player1'|'player2')
* @param {string} activeSlot wer gerade am Zug ist ('player1'|'player2')
* @returns {Array} geordnete Event-Liste für den Client
*/
function runCombatPhase(boardState, leftSlot, activeSlot) {
const events = [];
const isActiveLeft = activeSlot === leftSlot;
const dir = isActiveLeft ? 1 : -1;
/* ── Karten des aktiven Spielers sammeln ────────────────── */
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);
}
}
}
/* ── 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;
});
/* ── Jede Karte: erst bewegen, dann sofort angreifen ────── */
for (const startSlotId of myCards) {
const entry = boardState[startSlotId];
if (!entry) continue; // wurde durch vorherigen Angriff bereits entfernt (sollte nicht vorkommen, aber sicher ist sicher)
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;
/* ── 1. BEWEGEN ─────────────────────────────────────── */
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;
}
/* ── 2. ANGREIFEN (sofort nach der Bewegung) ────────── */
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 gefunden → Angriff
const atk = card.attack ?? 0;
target.card = { ...target.card, defends: (target.card.defends ?? 0) - atk };
events.push({
type : 'attack',
from : currentSlotId,
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 };