Files
WarhammerQuest/src/engine/game/CombatSystem.js

102 lines
3.3 KiB
JavaScript

import { CombatMechanics } from './CombatMechanics.js';
export class CombatSystem {
constructor(gameEngine) {
this.game = gameEngine;
}
/**
* Handles the complete flow of a Melee Attack request
* @param {Object} attacker
* @param {Object} defender
* @returns {Object} Result object { success: boolean, result: logObject, reason: string }
*/
handleMeleeAttack(attacker, defender) {
// 1. Validations
if (!attacker || !defender) return { success: false, reason: 'invalid_target' };
// Check Phase (Hero Phase for heroes)
// Note: Monsters use this too, but their phase check is in AI loop.
// We might want to enforce "Monster Phase" check here later if we pass 'source' context.
if (attacker.type === 'hero' && this.game.turnManager.currentPhase !== 'hero') {
return { success: false, reason: 'phase' };
}
// Check Action Economy (Cooldown)
if (attacker.hasAttacked) {
return { success: false, reason: 'cooldown' };
}
// Check Adjacency (Melee Range)
// Logic: Manhattan distance == 1
const dx = Math.abs(attacker.x - defender.x);
const dy = Math.abs(attacker.y - defender.y);
if (dx + dy !== 1) {
return { success: false, reason: 'range' };
}
// 2. Execution (Math)
// Calls the pure math module
const result = CombatMechanics.resolveMeleeAttack(attacker, defender, this.game);
// 3. Update State
attacker.hasAttacked = true;
// 4. Side Effects (Sound, UI Events)
if (window.SOUND_MANAGER) {
// Logic to choose sound could be expanded here based on Weapon Type
window.SOUND_MANAGER.playSound('sword');
}
if (this.game.onCombatResult) {
this.game.onCombatResult(result);
}
return { success: true, result };
}
/**
* Handles the complete flow of a Ranged Attack request
* @param {Object} attacker
* @param {Object} defender
* @returns {Object} Result object
*/
handleRangedAttack(attacker, defender) {
if (!attacker || !defender) return { success: false, reason: 'invalid_target' };
// 1. Validations
if (attacker.type === 'hero' && this.game.turnManager.currentPhase !== 'hero') {
return { success: false, reason: 'phase' };
}
if (attacker.hasAttacked) {
return { success: false, reason: 'cooldown' };
}
// Check "Pinned" Status (Can't shoot if enemies are adjacent)
// Using GameEngine's helper for now as it holds entity lists
if (this.game.isEntityPinned(attacker)) {
return { success: false, reason: 'pinned' };
}
// Line of Sight is assumed checked by UI/Input, but we could enforce it here if strict.
// 2. Execution (Math)
const result = CombatMechanics.resolveRangedAttack(attacker, defender, this.game);
// 3. Update State
attacker.hasAttacked = true;
// 4. Side Effects
if (window.SOUND_MANAGER) {
window.SOUND_MANAGER.playSound('arrow');
}
if (this.game.onCombatResult) {
this.game.onCombatResult(result);
}
return { success: true, result };
}
}