feat: spell book UI, iron skin spell, buffs system and devlog update
This commit is contained in:
@@ -53,6 +53,11 @@ export class GameEngine {
|
||||
this.resetHeroMoves();
|
||||
}
|
||||
});
|
||||
|
||||
// End of Turn Logic (Buffs, cooldowns, etc)
|
||||
this.turnManager.on('turn_ended', (turn) => {
|
||||
this.handleEndTurn();
|
||||
});
|
||||
}
|
||||
|
||||
resetHeroMoves() {
|
||||
@@ -64,6 +69,49 @@ export class GameEngine {
|
||||
console.log("Refilled Hero Moves");
|
||||
}
|
||||
|
||||
handleEndTurn() {
|
||||
console.log("[GameEngine] Handling End of Turn Effects...");
|
||||
|
||||
if (!this.heroes) return;
|
||||
|
||||
this.heroes.forEach(hero => {
|
||||
if (hero.buffs && hero.buffs.length > 0) {
|
||||
// Decrement duration
|
||||
hero.buffs.forEach(buff => {
|
||||
buff.duration--;
|
||||
});
|
||||
|
||||
// Remove expired
|
||||
const activeBuffs = [];
|
||||
const expiredBuffs = [];
|
||||
|
||||
hero.buffs.forEach(buff => {
|
||||
if (buff.duration > 0) {
|
||||
activeBuffs.push(buff);
|
||||
} else {
|
||||
expiredBuffs.push(buff);
|
||||
}
|
||||
});
|
||||
|
||||
// Revert expired
|
||||
expiredBuffs.forEach(buff => {
|
||||
if (buff.stat === 'toughness') {
|
||||
hero.stats.toughness -= buff.value;
|
||||
if (hero.tempStats && hero.tempStats.toughnessBonus) {
|
||||
hero.tempStats.toughnessBonus -= buff.value;
|
||||
}
|
||||
console.log(`[GameEngine] Buff expired: ${buff.id} on ${hero.name}. -${buff.value} ${buff.stat}`);
|
||||
if (this.onShowMessage) {
|
||||
this.onShowMessage("Efecto Finalizado", `La ${buff.id === 'iron_skin' ? 'Piel de Hierro' : 'Magia'} de ${hero.name} se desvanece.`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hero.buffs = activeBuffs;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createParty() {
|
||||
this.heroes = [];
|
||||
this.monsters = []; // Initialize monsters array
|
||||
@@ -204,17 +252,32 @@ export class GameEngine {
|
||||
targetCells.push({ x: x, y: y });
|
||||
}
|
||||
|
||||
// NEW: Enforce LOS Check before execution
|
||||
const caster = this.selectedEntity;
|
||||
if (caster) {
|
||||
const targetObj = { x: x, y: y };
|
||||
const los = this.checkLineOfSightStrict(caster, targetObj);
|
||||
if (!los || !los.clear) {
|
||||
if (this.onShowMessage) this.onShowMessage('Bloqueado', 'No tienes línea de visión.');
|
||||
// Do NOT cancel targeting, let them try again
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute Spell
|
||||
const result = this.executeSpell(this.currentSpell, targetCells);
|
||||
|
||||
if (result.success) {
|
||||
// Success
|
||||
this.cancelTargeting();
|
||||
if (window.RENDERER) window.RENDERER.hideAreaPreview();
|
||||
} else {
|
||||
if (this.onShowMessage) this.onShowMessage('Fallo', result.reason || 'No se pudo lanzar el hechizo.');
|
||||
this.cancelTargeting(); // Cancel on error? maybe keep open? usually cancel.
|
||||
if (window.RENDERER) window.RENDERER.hideAreaPreview();
|
||||
}
|
||||
|
||||
this.cancelTargeting();
|
||||
if (window.RENDERER) window.RENDERER.hideAreaPreview();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ export class MagicSystem {
|
||||
return this.resolveHeal(caster, spell);
|
||||
} else if (spell.type === 'attack') {
|
||||
return this.resolveAttack(caster, spell, targetCells);
|
||||
} else if (spell.type === 'defense') {
|
||||
return this.resolveDefense(caster, spell, targetCells);
|
||||
}
|
||||
|
||||
return { success: false, reason: 'unknown_spell_type' };
|
||||
@@ -156,4 +158,67 @@ export class MagicSystem {
|
||||
|
||||
return { success: true, type: 'attack', hits: 1 }; // Return success immediately
|
||||
}
|
||||
resolveDefense(caster, spell, targetCells) {
|
||||
// Needs a target hero
|
||||
let targetHero = null;
|
||||
|
||||
// Find hero in target cells
|
||||
for (const cell of targetCells) {
|
||||
const h = this.game.heroes.find(h => h.x === cell.x && h.y === cell.y);
|
||||
if (h) {
|
||||
targetHero = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetHero) {
|
||||
return { success: false, reason: 'no_target_hero' };
|
||||
}
|
||||
|
||||
const effect = spell.effect;
|
||||
if (!effect) return { success: false };
|
||||
|
||||
// Apply Buff
|
||||
if (effect.stat === 'toughness') {
|
||||
// Store original if not already stored (simple buffering)
|
||||
if (!targetHero.tempStats) targetHero.tempStats = {};
|
||||
|
||||
// Stackable? Probably not for same spell.
|
||||
// Check if already has this buff?
|
||||
// For simplicity: Add modifier
|
||||
if (!targetHero.tempStats.toughnessBonus) targetHero.tempStats.toughnessBonus = 0;
|
||||
|
||||
targetHero.tempStats.toughnessBonus += effect.value;
|
||||
// Also modify actual stat for calculation access?
|
||||
// Usually stats are accessed via getter or direct.
|
||||
// If direct property, we modify it and store original?
|
||||
// Let's modify the stat directly for now and trust Turn Manager to revert or track it.
|
||||
// BETTER: modify 'toughness' in stats, store 'buff_iron_skin' in activeBuffs?
|
||||
|
||||
targetHero.stats.toughness += effect.value;
|
||||
|
||||
// Mark for cleanup (Pseudo-implementation for cleanup)
|
||||
if (!targetHero.buffs) targetHero.buffs = [];
|
||||
targetHero.buffs.push({
|
||||
id: spell.id,
|
||||
stat: 'toughness',
|
||||
value: effect.value,
|
||||
duration: effect.duration
|
||||
});
|
||||
|
||||
if (this.game.onShowMessage) {
|
||||
this.game.onShowMessage('Piel de Hierro', `Resistencia de ${targetHero.name} +${effect.value}`);
|
||||
}
|
||||
|
||||
// Visual Effect
|
||||
if (window.RENDERER) {
|
||||
window.RENDERER.triggerVisualEffect('defense_buff', targetHero.x, targetHero.y);
|
||||
// Highlight or keep aura?
|
||||
}
|
||||
|
||||
console.log(`[MagicSystem] Applied ${spell.name} to ${targetHero.name}`);
|
||||
}
|
||||
|
||||
return { success: true, type: 'defense', target: targetHero.name };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +230,8 @@ export class MonsterAI {
|
||||
// Step 2: Attack animation delay (500ms)
|
||||
setTimeout(() => {
|
||||
// Step 3: Trigger hit visual on defender (if hit succeeded)
|
||||
if (result.hitSuccess && this.game.onEntityHit) {
|
||||
this.game.onEntityHit(hero.id);
|
||||
}
|
||||
// Step 3: Trigger hit visual on defender REMOVED (Handled by onCombatResult)
|
||||
|
||||
|
||||
// Step 4: Remove green ring after red ring appears (1200ms for red ring duration)
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -88,6 +88,7 @@ export class TurnManager {
|
||||
|
||||
endTurn() {
|
||||
console.log(`--- TURN ${this.currentTurn} END ---`);
|
||||
this.emit('turn_ended', this.currentTurn);
|
||||
this.currentTurn++;
|
||||
this.startPowerPhase();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user