1
0
Fork 0

Forbid move and trigger actions while in vigilance

This commit is contained in:
Michaël Lemaire 2018-04-04 18:17:10 +02:00
parent 7bc6378754
commit c3268ebdea
12 changed files with 99 additions and 49 deletions

View file

@ -56,7 +56,6 @@ Battle
Ships models and actions 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 * 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 * Highlight the effects area that will contain the new position when move-targetting
* Add movement attribute (for main engine action, km/power) * Add movement attribute (for main engine action, km/power)

View file

@ -295,7 +295,7 @@ module TK.SpaceTac {
}); });
// Deactivate toggle actions // Deactivate toggle actions
iforeach(this.iToggleActions(true), action => { this.getToggleActions(true).forEach(action => {
result = result.concat(action.getSpecificDiffs(this, battle, Target.newFromShip(this))); result = result.concat(action.getSpecificDiffs(this, battle, Target.newFromShip(this)));
}); });
@ -381,17 +381,19 @@ module TK.SpaceTac {
/** /**
* Iterator over toggle actions * Iterator over toggle actions
*/ */
iToggleActions(only_active = false): Iterator<ToggleAction> { getToggleActions(only_active = false): ToggleAction[] {
return <Iterator<ToggleAction>>ifilter(iarray(this.actions.listAll()), action => { let result = cfilter(this.actions.listAll(), ToggleAction);
return (action instanceof ToggleAction && (!only_active || this.actions.isToggled(action))); 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) * Get the effects that this ship has on another ship (which may be herself)
*/ */
getAreaEffects(ship: Ship): BaseEffect[] { getAreaEffects(ship: Ship): BaseEffect[] {
let toggled = imaterialize(this.iToggleActions(true)); let toggled = this.getToggleActions(true);
let effects = toggled.map(action => { let effects = toggled.map(action => {
if (bool(action.filterImpactedShips(this, this.location, Target.newFromShip(ship), [ship]))) { if (bool(action.filterImpactedShips(this, this.location, Target.newFromShip(ship), [ship]))) {
return action.effects; return action.effects;

View file

@ -44,22 +44,22 @@ module TK.SpaceTac.Specs {
check.patch(action, "getPowerUsage", () => 3); check.patch(action, "getPowerUsage", () => 3);
let ship = new Ship(); 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); ship.actions.addCustom(action);
check.equals(action.checkCannotBeApplied(ship), "not enough power"); check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.POWER);
ship.setValue("power", 5); ship.setValue("power", 5);
check.equals(action.checkCannotBeApplied(ship), null); check.equals(action.checkCannotBeApplied(ship), null);
check.equals(action.checkCannotBeApplied(ship, 4), null); check.equals(action.checkCannotBeApplied(ship, 4), null);
check.equals(action.checkCannotBeApplied(ship, 3), 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); ship.setValue("power", 3);
check.equals(action.checkCannotBeApplied(ship), null); check.equals(action.checkCannotBeApplied(ship), null);
ship.setValue("power", 2); 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 => { test.case("checks against overheat", check => {
@ -80,13 +80,13 @@ module TK.SpaceTac.Specs {
check.equals(action.checkCannotBeApplied(ship), null); check.equals(action.checkCannotBeApplied(ship), null);
cooldown.use(); cooldown.use();
check.equals(action.checkCannotBeApplied(ship), "overheated"); check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED);
cooldown.cool(); cooldown.cool();
check.equals(action.checkCannotBeApplied(ship), "overheated"); check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED);
cooldown.cool(); cooldown.cool();
check.equals(action.checkCannotBeApplied(ship), "overheated"); check.equals(action.checkCannotBeApplied(ship), ActionUnavailability.OVERHEATED);
cooldown.cool(); cooldown.cool();
check.equals(action.checkCannotBeApplied(ship), null); check.equals(action.checkCannotBeApplied(ship), null);

View file

@ -35,6 +35,22 @@ module TK.SpaceTac {
ENEMIES 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. * Base class for a battle action.
* *
@ -106,17 +122,17 @@ module TK.SpaceTac {
* *
* Method to extend to set conditions * 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(); let battle = ship.getBattle();
if (battle && battle.playing_ship !== ship) { if (battle && battle.playing_ship !== ship) {
// Ship is not playing // Ship is not playing
return "ship not playing"; return ActionUnavailability.NOT_PLAYING;
} }
if (!ship.actions.getById(this.id)) { if (!ship.actions.getById(this.id)) {
return "action not available"; return ActionUnavailability.NO_SUCH_ACTION;
} }
// Check AP usage // Check AP usage
@ -125,12 +141,12 @@ module TK.SpaceTac {
} }
var ap_usage = this.getPowerUsage(ship, null); var ap_usage = this.getPowerUsage(ship, null);
if (remaining_ap < ap_usage) { if (remaining_ap < ap_usage) {
return "not enough power"; return ActionUnavailability.POWER;
} }
// Check cooldown // Check cooldown
if (!ship.actions.isUsable(this)) { if (!ship.actions.isUsable(this)) {
return "overheated"; return ActionUnavailability.OVERHEATED;
} }
return null; return null;

View file

@ -8,11 +8,11 @@ module TK.SpaceTac.Specs {
battle.setPlayingShip(battle.play_order[0]); battle.setPlayingShip(battle.play_order[0]);
let action = new EndTurnAction(); 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; action = EndTurnAction.SINGLETON;
check.equals(action.checkCannotBeApplied(battle.play_order[0]), null); 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 => { test.case("changes active ship", check => {

View file

@ -23,7 +23,7 @@ module TK.SpaceTac {
} }
getPowerUsage(ship: Ship, target: Target | null): number { 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"); return ship.getValue("power") + toggled_cost - ship.getAttribute("power_capacity");
} }

View file

@ -125,5 +125,16 @@ module TK.SpaceTac.Specs {
action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 12 }); action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 12 });
check.equals(action.getEffectsDescription(), "Move: 58km per power point (safety: 12km)"); 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);
});
}); });
} }

View file

@ -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); 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); let base = super.checkCannotBeApplied(ship, Infinity);
if (base) { if (base) {
return base; return base;
@ -58,11 +58,16 @@ module TK.SpaceTac {
if (remaining_ap === null) { if (remaining_ap === null) {
remaining_ap = ship.getValue("power"); remaining_ap = ship.getValue("power");
} }
if (remaining_ap > 0.0001) { if (remaining_ap < 0.0001) {
return null; return ActionUnavailability.POWER;
} else {
return "not enough 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 { getPowerUsage(ship: Ship, target: Target | null): number {

View file

@ -136,5 +136,16 @@ module TK.SpaceTac.Specs {
action.configureTrigger({ effects: effects, power: 2, range: 120, blast: 100, angle: 80 }); 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"); 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);
})
}); });
} }

View file

@ -90,6 +90,21 @@ module TK.SpaceTac {
return this.range; 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[] { filterImpactedShips(ship: Ship, source: ArenaLocation, target: Target, ships: Ship[]): Ship[] {
if (this.blast) { if (this.blast) {
return ships.filter(ship => arenaDistance(ship.location, target) <= this.blast); return ships.filter(ship => arenaDistance(ship.location, target) <= this.blast);

View file

@ -9,9 +9,9 @@ module TK.SpaceTac.UI.Specs {
let ship = new Ship(); let ship = new Ship();
TestTools.setShipModel(ship, 100, 0, 10); TestTools.setShipModel(ship, 100, 0, 10);
let action1 = new MoveAction("Thruster"); let action1 = ship.actions.addCustom(new MoveAction("Thruster"));
let action2 = new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 }); let action2 = ship.actions.addCustom(new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 }));
let action3 = new EndTurnAction(); let action3 = ship.actions.addCustom(new EndTurnAction());
ActionTooltip.fill(tooltip.getBuilder(), ship, action1, 0); ActionTooltip.fill(tooltip.getBuilder(), ship, action1, 0);
checkText(check, (<any>tooltip).container.content.children[1], "Use Thruster"); checkText(check, (<any>tooltip).container.content.children[1], "Use Thruster");

View file

@ -14,34 +14,25 @@ module TK.SpaceTac.UI {
builder.text(action.getTitle(ship), 150, 0, { size: 24 }); builder.text(action.getTitle(ship), 150, 0, { size: 24 });
let cost = ""; let unavailable = action.checkCannotBeApplied(ship);
if (action instanceof MoveAction) { if (unavailable != null) {
if (ship.getValue("power") == 0) { builder.text(unavailable, 150, 40, { color: "#e54d2b" });
cost = "Not enough power"; } else if (action instanceof MoveAction) {
} else { let cost = `Cost: 1 power per ${action.distance_per_power}km`;
cost = `Cost: 1 power per ${action.distance_per_power}km`; builder.text(cost, 150, 40, { color: "#ffdd4b" });
}
} else { } else {
let power_usage = action.getPowerUsage(ship, null); let power_usage = action.getPowerUsage(ship, null);
if (power_usage) { if (power_usage) {
if (ship.getValue("power") < power_usage) { let cost = (power_usage > 0) ? `Cost: ${power_usage} power` : `Recover: ${-power_usage} power`;
cost = "Not enough power"; builder.text(cost, 150, 40, { color: "#ffdd4b" });
} else if (power_usage > 0) {
cost = `Cost: ${power_usage} power`;
} else {
cost = `Recover: ${-power_usage} power`;
}
} }
} }
if (cost) {
builder.text(cost, 150, 40, { color: "#ffdd4b" });
}
let cooldown = ship.actions.getCooldown(action); let cooldown = ship.actions.getCooldown(action);
if (cooldown.overheat) { if (cooldown.overheat) {
if (cooldown.heat > 0) { if (cooldown.heat > 0) {
builder.text("Cooling down ...", 150, 80, { color: "#d8894d" }); 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) { if (cooldown.cooling > 1) {
let turns = cooldown.cooling - 1; let turns = cooldown.cooling - 1;
builder.text(`Unavailable for ${turns} turn${turns > 1 ? "s" : ""} if used`, 150, 80, { color: "#d8894d" }); builder.text(`Unavailable for ${turns} turn${turns > 1 ? "s" : ""} if used`, 150, 80, { color: "#d8894d" });