From c3268ebdeaae6b311842318f0039727b6deff4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 4 Apr 2018 18:17:10 +0200 Subject: [PATCH] Forbid move and trigger actions while in vigilance --- TODO.md | 1 - src/core/Ship.ts | 14 +++++++------ src/core/actions/BaseAction.spec.ts | 14 ++++++------- src/core/actions/BaseAction.ts | 28 ++++++++++++++++++++------ src/core/actions/EndTurnAction.spec.ts | 4 ++-- src/core/actions/EndTurnAction.ts | 2 +- src/core/actions/MoveAction.spec.ts | 11 ++++++++++ src/core/actions/MoveAction.ts | 15 +++++++++----- src/core/actions/TriggerAction.spec.ts | 11 ++++++++++ src/core/actions/TriggerAction.ts | 15 ++++++++++++++ src/ui/battle/ActionTooltip.spec.ts | 6 +++--- src/ui/battle/ActionTooltip.ts | 27 +++++++++---------------- 12 files changed, 99 insertions(+), 49 deletions(-) diff --git a/TODO.md b/TODO.md index 8f3a1ec..b138d93 100644 --- a/TODO.md +++ b/TODO.md @@ -56,7 +56,6 @@ Battle Ships models and actions ------------------------ -* Fix vigilance action triggering when the ship moves with one active (moving should disable vigilance actions) * Fix vigilance action not disabling when reaching the maximum number of triggerings * Highlight the effects area that will contain the new position when move-targetting * Add movement attribute (for main engine action, km/power) diff --git a/src/core/Ship.ts b/src/core/Ship.ts index 3ed5c1b..dbd335c 100644 --- a/src/core/Ship.ts +++ b/src/core/Ship.ts @@ -295,7 +295,7 @@ module TK.SpaceTac { }); // Deactivate toggle actions - iforeach(this.iToggleActions(true), action => { + this.getToggleActions(true).forEach(action => { result = result.concat(action.getSpecificDiffs(this, battle, Target.newFromShip(this))); }); @@ -381,17 +381,19 @@ module TK.SpaceTac { /** * Iterator over toggle actions */ - iToggleActions(only_active = false): Iterator { - return >ifilter(iarray(this.actions.listAll()), action => { - return (action instanceof ToggleAction && (!only_active || this.actions.isToggled(action))); - }); + getToggleActions(only_active = false): ToggleAction[] { + let result = cfilter(this.actions.listAll(), ToggleAction); + if (only_active) { + result = result.filter(action => this.actions.isToggled(action)); + } + return result; } /** * Get the effects that this ship has on another ship (which may be herself) */ getAreaEffects(ship: Ship): BaseEffect[] { - let toggled = imaterialize(this.iToggleActions(true)); + let toggled = this.getToggleActions(true); let effects = toggled.map(action => { if (bool(action.filterImpactedShips(this, this.location, Target.newFromShip(ship), [ship]))) { return action.effects; diff --git a/src/core/actions/BaseAction.spec.ts b/src/core/actions/BaseAction.spec.ts index 40cd825..09f45ba 100644 --- a/src/core/actions/BaseAction.spec.ts +++ b/src/core/actions/BaseAction.spec.ts @@ -44,22 +44,22 @@ module TK.SpaceTac.Specs { check.patch(action, "getPowerUsage", () => 3); let ship = new Ship(); - check.equals(action.checkCannotBeApplied(ship), "action not available"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.NO_SUCH_ACTION); ship.actions.addCustom(action); - check.equals(action.checkCannotBeApplied(ship), "not enough power"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.POWER); ship.setValue("power", 5); check.equals(action.checkCannotBeApplied(ship), null); check.equals(action.checkCannotBeApplied(ship, 4), null); check.equals(action.checkCannotBeApplied(ship, 3), null); - check.equals(action.checkCannotBeApplied(ship, 2), "not enough power"); + check.equals(action.checkCannotBeApplied(ship, 2), ActionUnavailability.POWER); ship.setValue("power", 3); check.equals(action.checkCannotBeApplied(ship), null); ship.setValue("power", 2); - check.equals(action.checkCannotBeApplied(ship), "not enough power"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.POWER); }) test.case("checks against overheat", check => { @@ -80,13 +80,13 @@ module TK.SpaceTac.Specs { check.equals(action.checkCannotBeApplied(ship), null); cooldown.use(); - check.equals(action.checkCannotBeApplied(ship), "overheated"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED); cooldown.cool(); - check.equals(action.checkCannotBeApplied(ship), "overheated"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED); cooldown.cool(); - check.equals(action.checkCannotBeApplied(ship), "overheated"); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED); cooldown.cool(); check.equals(action.checkCannotBeApplied(ship), null); diff --git a/src/core/actions/BaseAction.ts b/src/core/actions/BaseAction.ts index e177d50..35552da 100644 --- a/src/core/actions/BaseAction.ts +++ b/src/core/actions/BaseAction.ts @@ -35,6 +35,22 @@ module TK.SpaceTac { ENEMIES } + /** + * Reasons for action unavailibility + */ + export enum ActionUnavailability { + // Ship is not playing + NOT_PLAYING = "Not this ship turn", + // Action is not available + NO_SUCH_ACTION = "Action not available", + // Not enough power remaining + POWER = "Not enough power", + // Action is overheated + OVERHEATED = "Overheated", + // Vigilance is activated + VIGILANCE = "In vigilance" + } + /** * Base class for a battle action. * @@ -106,17 +122,17 @@ module TK.SpaceTac { * * Method to extend to set conditions * - * Returns an informative message indicating why the action cannot be used, null otherwise + * Returns an unavalability reason, null otherwise */ - checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): string | null { + checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null { let battle = ship.getBattle(); if (battle && battle.playing_ship !== ship) { // Ship is not playing - return "ship not playing"; + return ActionUnavailability.NOT_PLAYING; } if (!ship.actions.getById(this.id)) { - return "action not available"; + return ActionUnavailability.NO_SUCH_ACTION; } // Check AP usage @@ -125,12 +141,12 @@ module TK.SpaceTac { } var ap_usage = this.getPowerUsage(ship, null); if (remaining_ap < ap_usage) { - return "not enough power"; + return ActionUnavailability.POWER; } // Check cooldown if (!ship.actions.isUsable(this)) { - return "overheated"; + return ActionUnavailability.OVERHEATED; } return null; diff --git a/src/core/actions/EndTurnAction.spec.ts b/src/core/actions/EndTurnAction.spec.ts index a444393..de9a3b8 100644 --- a/src/core/actions/EndTurnAction.spec.ts +++ b/src/core/actions/EndTurnAction.spec.ts @@ -8,11 +8,11 @@ module TK.SpaceTac.Specs { battle.setPlayingShip(battle.play_order[0]); let action = new EndTurnAction(); - check.equals(action.checkCannotBeApplied(battle.play_order[0]), "action not available"); + check.equals(action.checkCannotBeApplied(battle.play_order[0]), ActionUnavailability.NO_SUCH_ACTION); action = EndTurnAction.SINGLETON; check.equals(action.checkCannotBeApplied(battle.play_order[0]), null); - check.equals(action.checkCannotBeApplied(battle.play_order[1]), "ship not playing"); + check.equals(action.checkCannotBeApplied(battle.play_order[1]), ActionUnavailability.NOT_PLAYING); }); test.case("changes active ship", check => { diff --git a/src/core/actions/EndTurnAction.ts b/src/core/actions/EndTurnAction.ts index 2378aa9..6e2d0b0 100644 --- a/src/core/actions/EndTurnAction.ts +++ b/src/core/actions/EndTurnAction.ts @@ -23,7 +23,7 @@ module TK.SpaceTac { } getPowerUsage(ship: Ship, target: Target | null): number { - let toggled_cost = isum(imap(ship.iToggleActions(true), action => action.power)); + let toggled_cost = sum(ship.getToggleActions(true).map(action => action.power)); return ship.getValue("power") + toggled_cost - ship.getAttribute("power_capacity"); } diff --git a/src/core/actions/MoveAction.spec.ts b/src/core/actions/MoveAction.spec.ts index cf3e4db..216407e 100644 --- a/src/core/actions/MoveAction.spec.ts +++ b/src/core/actions/MoveAction.spec.ts @@ -125,5 +125,16 @@ module TK.SpaceTac.Specs { action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 12 }); check.equals(action.getEffectsDescription(), "Move: 58km per power point (safety: 12km)"); }); + + test.case("can't be used while in vigilance", check => { + let ship = new Ship(); + TestTools.setShipModel(ship, 10, 10, 10); + let vigilance = ship.actions.addCustom(new VigilanceAction("Vigilance")); + let action = ship.actions.addCustom(new MoveAction("Engine")); + + check.equals(action.checkCannotBeApplied(ship), null); + ship.actions.toggle(vigilance, true); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.VIGILANCE); + }); }); } diff --git a/src/core/actions/MoveAction.ts b/src/core/actions/MoveAction.ts index 2002612..d3145eb 100644 --- a/src/core/actions/MoveAction.ts +++ b/src/core/actions/MoveAction.ts @@ -48,7 +48,7 @@ module TK.SpaceTac { return Target.newFromLocation(ship.arena_x + Math.cos(ship.arena_angle) * 100, ship.arena_y + Math.sin(ship.arena_angle) * 100); } - checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): string | null { + checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null { let base = super.checkCannotBeApplied(ship, Infinity); if (base) { return base; @@ -58,11 +58,16 @@ module TK.SpaceTac { if (remaining_ap === null) { remaining_ap = ship.getValue("power"); } - if (remaining_ap > 0.0001) { - return null; - } else { - return "not enough power"; + if (remaining_ap < 0.0001) { + return ActionUnavailability.POWER; } + + // Check vigilance actions + if (any(ship.getToggleActions(true), action => action instanceof VigilanceAction)) { + return ActionUnavailability.VIGILANCE; + } + + return null; } getPowerUsage(ship: Ship, target: Target | null): number { diff --git a/src/core/actions/TriggerAction.spec.ts b/src/core/actions/TriggerAction.spec.ts index 26cda5a..05e4a18 100644 --- a/src/core/actions/TriggerAction.spec.ts +++ b/src/core/actions/TriggerAction.spec.ts @@ -136,5 +136,16 @@ module TK.SpaceTac.Specs { action.configureTrigger({ effects: effects, power: 2, range: 120, blast: 100, angle: 80 }); check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km):\n• evasion +20% in 100km radius"); }) + + test.case("can't be used while in vigilance", check => { + let ship = new Ship(); + TestTools.setShipModel(ship, 10, 10, 10); + let vigilance = ship.actions.addCustom(new VigilanceAction("Vigilance")); + let action = ship.actions.addCustom(new TriggerAction("Weapon")); + + check.equals(action.checkCannotBeApplied(ship), null); + ship.actions.toggle(vigilance, true); + check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.VIGILANCE); + }) }); } diff --git a/src/core/actions/TriggerAction.ts b/src/core/actions/TriggerAction.ts index b1467d6..b375518 100644 --- a/src/core/actions/TriggerAction.ts +++ b/src/core/actions/TriggerAction.ts @@ -90,6 +90,21 @@ module TK.SpaceTac { return this.range; } + checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null { + let base = super.checkCannotBeApplied(ship, remaining_ap); + if (base) { + return base; + } + + // Check vigilance actions + if (any(ship.getToggleActions(true), action => action instanceof VigilanceAction)) { + // TODO Could allow trigger actions that does not involve a move effect + return ActionUnavailability.VIGILANCE; + } + + return null; + } + filterImpactedShips(ship: Ship, source: ArenaLocation, target: Target, ships: Ship[]): Ship[] { if (this.blast) { return ships.filter(ship => arenaDistance(ship.location, target) <= this.blast); diff --git a/src/ui/battle/ActionTooltip.spec.ts b/src/ui/battle/ActionTooltip.spec.ts index 70f20fa..96324ab 100644 --- a/src/ui/battle/ActionTooltip.spec.ts +++ b/src/ui/battle/ActionTooltip.spec.ts @@ -9,9 +9,9 @@ module TK.SpaceTac.UI.Specs { let ship = new Ship(); TestTools.setShipModel(ship, 100, 0, 10); - let action1 = new MoveAction("Thruster"); - let action2 = new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 }); - let action3 = new EndTurnAction(); + let action1 = ship.actions.addCustom(new MoveAction("Thruster")); + let action2 = ship.actions.addCustom(new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 })); + let action3 = ship.actions.addCustom(new EndTurnAction()); ActionTooltip.fill(tooltip.getBuilder(), ship, action1, 0); checkText(check, (tooltip).container.content.children[1], "Use Thruster"); diff --git a/src/ui/battle/ActionTooltip.ts b/src/ui/battle/ActionTooltip.ts index 13b6519..59fbb81 100644 --- a/src/ui/battle/ActionTooltip.ts +++ b/src/ui/battle/ActionTooltip.ts @@ -14,34 +14,25 @@ module TK.SpaceTac.UI { builder.text(action.getTitle(ship), 150, 0, { size: 24 }); - let cost = ""; - if (action instanceof MoveAction) { - if (ship.getValue("power") == 0) { - cost = "Not enough power"; - } else { - cost = `Cost: 1 power per ${action.distance_per_power}km`; - } + let unavailable = action.checkCannotBeApplied(ship); + if (unavailable != null) { + builder.text(unavailable, 150, 40, { color: "#e54d2b" }); + } else if (action instanceof MoveAction) { + let cost = `Cost: 1 power per ${action.distance_per_power}km`; + builder.text(cost, 150, 40, { color: "#ffdd4b" }); } else { let power_usage = action.getPowerUsage(ship, null); if (power_usage) { - if (ship.getValue("power") < power_usage) { - cost = "Not enough power"; - } else if (power_usage > 0) { - cost = `Cost: ${power_usage} power`; - } else { - cost = `Recover: ${-power_usage} power`; - } + let cost = (power_usage > 0) ? `Cost: ${power_usage} power` : `Recover: ${-power_usage} power`; + builder.text(cost, 150, 40, { color: "#ffdd4b" }); } } - if (cost) { - builder.text(cost, 150, 40, { color: "#ffdd4b" }); - } let cooldown = ship.actions.getCooldown(action); if (cooldown.overheat) { if (cooldown.heat > 0) { builder.text("Cooling down ...", 150, 80, { color: "#d8894d" }); - } else if (cooldown.willOverheat() && cost != "Not enough power") { + } else if (!unavailable && cooldown.willOverheat()) { if (cooldown.cooling > 1) { let turns = cooldown.cooling - 1; builder.text(`Unavailable for ${turns} turn${turns > 1 ? "s" : ""} if used`, 150, 80, { color: "#d8894d" });