From 231484f7a85397d47d5b8c8752a9007eda5808d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Fri, 8 Dec 2017 01:18:15 +0100 Subject: [PATCH] New weapon effectiveness system, based on precision, maneuvrability and luck --- TODO.md | 4 +- src/core/Equipment.spec.ts | 6 +- src/core/LootTemplate.ts | 4 +- src/core/ShipValue.ts | 4 +- src/core/actions/BaseAction.ts | 4 +- src/core/actions/TriggerAction.spec.ts | 111 +++++++++++++++++- src/core/actions/TriggerAction.ts | 82 +++++++++++-- src/core/ai/Maneuver.spec.ts | 19 ++- src/core/ai/Maneuver.ts | 23 +++- src/core/ai/TacticalAIHelpers.ts | 22 ++-- src/core/diffs/ShipDamageDiff.ts | 6 +- src/core/effects/AttributeEffect.spec.ts | 4 +- src/core/effects/AttributeEffect.ts | 2 +- src/core/effects/AttributeLimitEffect.spec.ts | 4 +- src/core/effects/AttributeLimitEffect.ts | 2 +- .../effects/AttributeMultiplyEffect.spec.ts | 4 +- src/core/effects/AttributeMultiplyEffect.ts | 2 +- src/core/effects/BaseEffect.spec.ts | 11 -- src/core/effects/BaseEffect.ts | 17 +-- src/core/effects/CooldownEffect.spec.ts | 8 +- src/core/effects/CooldownEffect.ts | 2 +- src/core/effects/DamageEffect.spec.ts | 22 ++-- src/core/effects/DamageEffect.ts | 45 +++---- src/core/effects/RepelEffect.spec.ts | 8 +- src/core/effects/RepelEffect.ts | 2 +- src/core/effects/StickyEffect.spec.ts | 4 +- src/core/effects/StickyEffect.ts | 2 +- src/core/effects/ValueEffect.spec.ts | 4 +- src/core/effects/ValueEffect.ts | 2 +- src/core/effects/ValueTransferEffect.spec.ts | 4 +- src/core/effects/ValueTransferEffect.ts | 4 +- src/core/equipments/RawWeapons.spec.ts | 24 ++-- src/core/equipments/RawWeapons.ts | 6 +- src/ui/battle/ActionTooltip.spec.ts | 2 +- src/ui/battle/ArenaShip.ts | 5 +- src/ui/battle/Targetting.ts | 19 ++- 36 files changed, 326 insertions(+), 168 deletions(-) delete mode 100644 src/core/effects/BaseEffect.spec.ts diff --git a/TODO.md b/TODO.md index 4112458..db28445 100644 --- a/TODO.md +++ b/TODO.md @@ -36,6 +36,7 @@ Character sheet Battle ------ +* Display precision and maneuvrability in ship tooltip * Add a voluntary retreat option * Add scroll buttons when there are too many actions * Toggle bar/text display in power section of action bar @@ -60,9 +61,8 @@ Ships models and equipments --------------------------- * Add permanent effects and actions to ship models -* Add critical hit/miss +* Add critical hit/miss (or indicate lucky/unlucky throws) * Add damage over time effect (tricky to make intuitive) -* Chance to hit should increase with precision * Add actions with cost dependent of distance (like current move actions) * Add disc targetting (for some jump move actions) * Add "chain" effects diff --git a/src/core/Equipment.spec.ts b/src/core/Equipment.spec.ts index a49a349..7c17aeb 100644 --- a/src/core/Equipment.spec.ts +++ b/src/core/Equipment.spec.ts @@ -49,14 +49,14 @@ module TK.SpaceTac.Specs { let action = new TriggerAction(equipment, [new DamageEffect(50)], 1, 200, 0); equipment.action = action; - check.equals(equipment.getEffectsDescription(), "Fire (power usage 1, max range 200km):\n• do 50 damage on target"); + check.equals(equipment.getEffectsDescription(), "Fire (power 1, range 200km):\n• do 50 damage on target"); action.blast = 20; - check.equals(equipment.getEffectsDescription(), "Fire (power usage 1, max range 200km):\n• do 50 damage in 20km radius"); + check.equals(equipment.getEffectsDescription(), "Fire (power 1, range 200km):\n• do 50 damage in 20km radius"); action.blast = 0; action.effects.push(new StickyEffect(new AttributeLimitEffect("shield_capacity", 200), 3)); - check.equals(equipment.getEffectsDescription(), "Fire (power usage 1, max range 200km):\n• do 50 damage on target\n• limit shield capacity to 200 for 3 turns on target"); + check.equals(equipment.getEffectsDescription(), "Fire (power 1, range 200km):\n• do 50 damage on target\n• limit shield capacity to 200 for 3 turns on target"); }); test.case("gets a minimal level, based on skills requirements", check => { diff --git a/src/core/LootTemplate.ts b/src/core/LootTemplate.ts index cb6e82d..9b462ce 100644 --- a/src/core/LootTemplate.ts +++ b/src/core/LootTemplate.ts @@ -206,10 +206,10 @@ module TK.SpaceTac { /** * Add a trigger action. */ - addTriggerAction(power: LeveledValue, effects: EffectTemplate[], range: LeveledValue = irepeat(0), blast: LeveledValue = irepeat(0), angle: LeveledValue = irepeat(0)): void { + addTriggerAction(power: LeveledValue, effects: EffectTemplate[], range: LeveledValue = irepeat(0), blast: LeveledValue = irepeat(0), angle: LeveledValue = irepeat(0), aim: LeveledValue = irepeat(0), evasion: LeveledValue = irepeat(0), luck: LeveledValue = irepeat(0)): void { this.base_modifiers.push((equipment, level) => { let reffects = effects.map(effect => effect.generate(level)); - equipment.action = new TriggerAction(equipment, reffects, resolveForLevel(power, level), resolveForLevel(range, level), resolveForLevel(blast, level), resolveForLevel(angle, level)); + equipment.action = new TriggerAction(equipment, reffects, resolveForLevel(power, level), resolveForLevel(range, level), resolveForLevel(blast, level), resolveForLevel(angle, level), resolveForLevel(aim, level), resolveForLevel(evasion, level), resolveForLevel(luck, level)); }); } diff --git a/src/core/ShipValue.ts b/src/core/ShipValue.ts index 1314ab3..34a6465 100644 --- a/src/core/ShipValue.ts +++ b/src/core/ShipValue.ts @@ -17,8 +17,8 @@ module TK.SpaceTac { "shield_capacity": "Maximal Shield value to protect the hull from damage", "power_capacity": "Maximal Power value to use equipment", "power_generation": "Power generated at the end of the ship's turn", - "maneuvrability": "Ability to move first and fast", - "precision": "Ability to target far and good", + "maneuvrability": "Ability to move first, fast and to evade weapons", + "precision": "Ability to target far and aim good", } export const SHIP_VALUES_NAMES: ShipValuesMapping = { diff --git a/src/core/actions/BaseAction.ts b/src/core/actions/BaseAction.ts index ada1211..126c649 100644 --- a/src/core/actions/BaseAction.ts +++ b/src/core/actions/BaseAction.ts @@ -120,7 +120,9 @@ module TK.SpaceTac { return 0; } - // Get the range of this action + /** + * Get the range of this action, for targetting purpose + */ getRangeRadius(ship: Ship): number { return 0; } diff --git a/src/core/actions/TriggerAction.spec.ts b/src/core/actions/TriggerAction.spec.ts index a49b99d..029b4cc 100644 --- a/src/core/actions/TriggerAction.spec.ts +++ b/src/core/actions/TriggerAction.spec.ts @@ -36,7 +36,7 @@ module TK.SpaceTac.Specs { action.apply(battle, ship, Target.newFromLocation(50, 50)); check.called(mock_apply, [ - [ship2, ship] + [ship2, ship, 1] ]); }) @@ -85,6 +85,85 @@ module TK.SpaceTac.Specs { check.equals(action.filterImpactedShips({ x: 0, y: 51 }, Target.newFromLocation(30, 50), ships), [ship1, ship2]); }) + test.case("computes a success factor, from precision and maneuvrability", check => { + function verify(precision: number, precision_factor: number, maneuvrability: number, maneuvrability_factor: number, result: number) { + let ship1 = new Ship(); + let ship2 = new Ship(); + + TestTools.setAttribute(ship1, "precision", precision); + TestTools.setAttribute(ship2, "maneuvrability", maneuvrability); + + let action = new TriggerAction(new Equipment()); + action.aim = precision_factor; + action.evasion = maneuvrability_factor; + check.nears(action.getSuccessFactor(ship1, ship2), result, 3, + `precision ${precision} (weight ${precision_factor}), maneuvrability ${maneuvrability} (weight ${maneuvrability_factor})`); + } + + // no weight => always 100% + verify(0, 0, 0, 0, 1); + verify(10, 0, 20, 0, 1); + verify(40, 0, -5, 0, 1); + + // precision only + verify(0, 100, 0, 0, 0); + verify(1, 100, 1, 0, 0.5); + verify(2, 100, 2, 0, 0.8); + verify(10, 100, 10, 0, 0.99); + verify(1, 50, 1, 0, 0.75); + + // maneuvrability only + verify(0, 0, 0, 100, 1); + verify(1, 0, 1, 100, 0.5); + verify(2, 0, 2, 100, 0.2); + verify(10, 0, 10, 100, 0.01); + verify(1, 0, 1, 50, 0.75); + + // precision vs maneuvrability + verify(0, 100, 0, 100, 0); + verify(4, 50, 4, 50, 0.5); + verify(4, 50, 8, 50, 0.283); + verify(4, 50, 20, 50, 0.016); + verify(4, 50, 4, 50, 0.5); + verify(8, 50, 4, 50, 0.717); + verify(20, 50, 4, 50, 0.984); + + // complex example + verify(7, 20, 5, 40, 0.639); + }) + + test.case("computes an effective success value, with random element", check => { + function verify(success_base: number, luck: number, random: number, expected: number) { + let ship1 = new Ship(); + let ship2 = new Ship(); + let action = new TriggerAction(new Equipment()); + action.luck = luck; + check.patch(action, "getSuccessFactor", () => success_base); + check.nears(action.getEffectiveSuccess(ship1, ship2, new SkewedRandomGenerator([random])), expected, 5, + `success ${success_base}, luck ${luck}, random ${random}`); + } + + // no luck influence + verify(0.3, 0, 0.4, 0.3); + verify(0.51, 0, 0.7, 0.51); + + // small luck influence + verify(0.5, 5, 0.0, 0); + verify(0.5, 5, 0.1, 0.47979); + verify(0.5, 5, 0.4, 0.49715); + verify(0.5, 5, 0.8, 0.51161); + verify(0.5, 5, 1.0, 1.0); + verify(0.7, 5, 0.5, 0.69399); + + // large luck influence + verify(0.5, 45, 0.0, 0); + verify(0.5, 45, 0.1, 0.31336); + verify(0.5, 45, 0.4, 0.46864); + verify(0.5, 45, 0.8, 0.61679); + verify(0.5, 45, 1.0, 1); + verify(0.7, 45, 0.5, 0.63485); + }) + test.case("guesses targetting mode", check => { let ship = new Ship(); let equ = new Equipment(); @@ -120,5 +199,35 @@ module TK.SpaceTac.Specs { check.equals(result, true); check.nears(ship.arena_angle, 1.107, 3); }) + + test.case("builds a textual description", check => { + let action = new TriggerAction(new Equipment()); + action.power = 0; + check.equals(action.getEffectsDescription(), ""); + + action.effects.push(new AttributeMultiplyEffect("precision", 20)); + check.equals(action.getEffectsDescription(), "Trigger:\n• precision +20% on self"); + + action.power = 2; + check.equals(action.getEffectsDescription(), "Trigger (power 2):\n• precision +20% on self"); + + action.range = 120; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km):\n• precision +20% on target"); + + action.aim = 10; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%):\n• precision +20% on target"); + + action.evasion = 35; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% on target"); + + action.angle = 80; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% in 80° arc"); + + action.blast = 100; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% in 100km radius"); + + action.luck = 15; + check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%, luck ±15%):\n• precision +20% in 100km radius"); + }) }); } diff --git a/src/core/actions/TriggerAction.ts b/src/core/actions/TriggerAction.ts index b9133c5..28d527f 100644 --- a/src/core/actions/TriggerAction.ts +++ b/src/core/actions/TriggerAction.ts @@ -19,13 +19,22 @@ module TK.SpaceTac { // Angle of the area between the source and the target that will be impacted angle: number + // Influence of "precision" of firing ship (0..100) + aim: number + + // Influence of "maneuvrability" of impacted ship (0..100) + evasion: number + + // Influence of luck + luck: number + // Effects applied on target effects: BaseEffect[] // Equipment cannot be null equipment: Equipment - constructor(equipment: Equipment, effects: BaseEffect[] = [], power = 1, range = 0, blast = 0, angle = 0, code = `fire-${equipment.code}`) { + constructor(equipment: Equipment, effects: BaseEffect[] = [], power = 1, range = 0, blast = 0, angle = 0, aim = 0, evasion = 0, luck = 0, code = `fire-${equipment.code}`) { super(code, equipment); this.power = power; @@ -33,6 +42,9 @@ module TK.SpaceTac { this.effects = effects; this.blast = blast; this.angle = angle; + this.aim = aim; + this.evasion = evasion; + this.luck = luck; } getVerb(): string { @@ -82,6 +94,42 @@ module TK.SpaceTac { return this.range; } + /** + * Get the success factor [0-1] for this action applied from a ship to another + * + * This is a predictible formula, not including random elements. + */ + getSuccessFactor(from: Ship, to: Ship): number { + let aim = this.aim * 0.01; + let evasion = this.evasion * 0.01; + let f1 = (x: number) => (x < 0 ? -1 : 1) * (1 - 1 / (x * x + 1)); + let prec = from.getAttribute("precision"); + let man = to.getAttribute("maneuvrability"); + let delta = Math.min(aim, evasion) * f1(0.2 * (prec - man)); + return clamp(1 - aim * (1 - f1(prec)) - evasion * f1(man) + delta, 0, 1); + } + + /** + * Get the effective success of the action [0-1]. + * + * Result has more chance to be near the success factor, but may be in the whole [0-1] range. + */ + getEffectiveSuccess(from: Ship, to: Ship, random = RandomGenerator.global): number { + let p = this.getSuccessFactor(from, to); + let s = this.luck * 0.01; + if (s) { + let c = (2 / (2 - s)) - 1; + let x = random.random(); + if (x <= p) { + return Math.pow(x, c) / Math.pow(p, c - 1); + } else { + return 1 - Math.pow(1 - x, c) / Math.pow(1 - p, c - 1); + } + } else { + return p; + } + } + filterImpactedShips(source: ArenaLocation, target: Target, ships: Ship[]): Ship[] { if (this.blast) { return ships.filter(ship => arenaDistance(ship.location, target) <= this.blast); @@ -128,12 +176,15 @@ module TK.SpaceTac { /** * Collect the effects applied by this action + * + * If *luck* is specified, an effective success factor is used, instead of an estimated one */ - getEffects(ship: Ship, target: Target, source = ship.location): [Ship, BaseEffect][] { - let result: [Ship, BaseEffect][] = []; + getEffects(ship: Ship, target: Target, source = ship.location, luck?: RandomGenerator): [Ship, BaseEffect, number][] { + let result: [Ship, BaseEffect, number][] = []; let ships = this.getImpactedShips(ship, target, source); - ships.forEach(ship => { - this.effects.forEach(effect => result.push([ship, effect])); + ships.forEach(iship => { + let success = luck ? this.getEffectiveSuccess(ship, iship, luck) : this.getSuccessFactor(ship, iship); + this.effects.forEach(effect => result.push([iship, effect, success])); }); return result; } @@ -157,9 +208,9 @@ module TK.SpaceTac { } // Apply effects - let effects = this.getEffects(ship, target); - effects.forEach(([ship_target, effect]) => { - let diffs = effect.getOnDiffs(ship_target, ship); + let effects = this.getEffects(ship, target, undefined, RandomGenerator.global); + effects.forEach(([ship_target, effect, success]) => { + let diffs = effect.getOnDiffs(ship_target, ship, success); result = result.concat(diffs); }); @@ -173,13 +224,22 @@ module TK.SpaceTac { let info: string[] = []; if (this.power) { - info.push(`power usage ${this.power}`); + info.push(`power ${this.power}`); } if (this.range) { - info.push(`max range ${this.range}km`); + info.push(`range ${this.range}km`); + } + if (this.aim) { + info.push(`aim +${this.aim}%`); + } + if (this.evasion) { + info.push(`evasion -${this.evasion}%`); + } + if (this.luck) { + info.push(`luck ±${this.luck}%`); } - let desc = `${this.getVerb()} (${info.join(", ")})`; + let desc = (info.length) ? `${this.getVerb()} (${info.join(", ")})` : this.getVerb(); let effects = this.effects.map(effect => { let suffix: string; if (this.blast) { diff --git a/src/core/ai/Maneuver.spec.ts b/src/core/ai/Maneuver.spec.ts index e9a29a2..8426717 100644 --- a/src/core/ai/Maneuver.spec.ts +++ b/src/core/ai/Maneuver.spec.ts @@ -1,10 +1,9 @@ module TK.SpaceTac.Specs { testing("Maneuver", test => { - function compare_maneuver_effects(check: TestContext, meff1: [Ship, BaseEffect][], meff2: [Ship, BaseEffect][]): void { - let [ships1, effects1] = unzip(meff1); - let [ships2, effects2] = unzip(meff2); - check.equals(ships1, ships2, "impacted ships"); - compare_effects(check, effects1, effects2); + function compare_maneuver_effects(check: TestContext, meff1: ManeuverEffect[], meff2: ManeuverEffect[]): void { + check.equals(meff1.map(ef => ef.ship), meff2.map(ef => ef.ship), "impacted ships"); + compare_effects(check, meff1.map(ef => ef.effect), meff2.map(ef => ef.effect)); + check.equals(meff1.map(ef => ef.success), meff2.map(ef => ef.success), "success factor"); } test.case("guesses weapon effects", check => { @@ -21,8 +20,8 @@ module TK.SpaceTac.Specs { TestTools.setShipHP(ship3, 30, 30); let maneuver = new Maneuver(ship1, nn(weapon.action), Target.newFromLocation(0, 0)); compare_maneuver_effects(check, maneuver.effects, [ - [ship1, new DamageEffect(50)], - [ship2, new DamageEffect(50)] + { ship: ship1, effect: new DamageEffect(50), success: 1 }, + { ship: ship2, effect: new DamageEffect(50), success: 1 }, ]); }); @@ -41,8 +40,8 @@ module TK.SpaceTac.Specs { TestTools.setShipHP(ship3, 30, 30); let maneuver = new Maneuver(ship1, weapon.action, Target.newFromLocation(0, 0)); compare_maneuver_effects(check, maneuver.effects, [ - [ship1, new ValueEffect("shield", 10)], - [ship2, new ValueEffect("shield", 10)] + { ship: ship1, effect: new ValueEffect("shield", 10), success: 1 }, + { ship: ship2, effect: new ValueEffect("shield", 10), success: 1 }, ]); }); @@ -66,7 +65,7 @@ module TK.SpaceTac.Specs { maneuver = new Maneuver(ship, move, Target.newFromLocation(100, 30)); check.containing(maneuver.getFinalLocation(), { x: 100, y: 30 }); compare_maneuver_effects(check, maneuver.effects, [ - [ship, new AttributeEffect("maneuvrability", 1)] + { ship: ship, effect: new AttributeEffect("maneuvrability", 1), success: 1 }, ]); }); }); diff --git a/src/core/ai/Maneuver.ts b/src/core/ai/Maneuver.ts index 0e24aae..e0c6738 100644 --- a/src/core/ai/Maneuver.ts +++ b/src/core/ai/Maneuver.ts @@ -1,4 +1,11 @@ module TK.SpaceTac { + // Single effect of a maneuver + export type ManeuverEffect = { + ship: Ship + effect: BaseEffect + success: number + } + /** * Ship maneuver for an artifical intelligence * @@ -21,7 +28,7 @@ module TK.SpaceTac { simulation: MoveFireResult // List of guessed effects of this maneuver - effects: [Ship, BaseEffect][] + effects: ManeuverEffect[] constructor(ship: Ship, action: BaseAction, target: Target, move_margin = 1) { this.ship = ship; @@ -74,16 +81,18 @@ module TK.SpaceTac { /** * Guess what will be the effects applied on any ship by this maneuver */ - guessEffects(): [Ship, BaseEffect][] { - let result: [Ship, BaseEffect][] = []; + guessEffects(): ManeuverEffect[] { + let result: ManeuverEffect[] = []; // Effects of weapon if (this.action instanceof TriggerAction) { - result = result.concat(this.action.getEffects(this.ship, this.target)); + this.action.getEffects(this.ship, this.target).forEach(([ship, effect, success]) => { + result.push({ ship: ship, effect: effect, success: success }); + }) } else if (this.action instanceof DeployDroneAction) { let ships = this.battle.collectShipsInCircle(this.target, this.action.drone_radius, true); this.action.drone_effects.forEach(effect => { - result = result.concat(ships.map(ship => <[Ship, BaseEffect]>[ship, effect])); + result = result.concat(ships.map(ship => ({ ship: ship, effect: effect, success: 1 }))); }); } @@ -91,7 +100,9 @@ module TK.SpaceTac { let location = this.getFinalLocation(); let effects = this.battle.drones.list().forEach(drone => { if (Target.newFromLocation(location.x, location.y).isInRange(drone.x, drone.y, drone.radius)) { - result = result.concat(drone.effects.map(effect => <[Ship, BaseEffect]>[this.ship, effect])); + result = result.concat(drone.effects.map(effect => ( + { ship: this.ship, effect: effect, success: 1 } + ))); } }); diff --git a/src/core/ai/TacticalAIHelpers.ts b/src/core/ai/TacticalAIHelpers.ts index f39c35e..2076e7b 100644 --- a/src/core/ai/TacticalAIHelpers.ts +++ b/src/core/ai/TacticalAIHelpers.ts @@ -29,17 +29,17 @@ module TK.SpaceTac { let dhull = 0; let dshield = 0; - maneuver.effects.forEach(([iship, effect]) => { - if (iship === ship) { - if (effect instanceof DamageEffect) { - let [ds, dh] = effect.getEffectiveDamage(ship); - dhull -= dh; - dshield -= ds; - } else if (effect instanceof ValueEffect) { - if (effect.valuetype == "hull") { - dhull = clamp(hull + effect.value_on, 0, chull) - hull; - } else if (effect.valuetype == "shield") { - dshield += clamp(shield + effect.value_on, 0, cshield) - shield; + maneuver.effects.forEach(result => { + if (result.ship === ship) { + if (result.effect instanceof DamageEffect) { + let damage = result.effect.getEffectiveDamage(ship, result.success); + dhull -= damage.hull; + dshield -= damage.shield; + } else if (result.effect instanceof ValueEffect) { + if (result.effect.valuetype == "hull") { + dhull = clamp(hull + result.effect.value_on, 0, chull) - hull; + } else if (result.effect.valuetype == "shield") { + dshield += clamp(shield + result.effect.value_on, 0, cshield) - shield; } } } diff --git a/src/core/diffs/ShipDamageDiff.ts b/src/core/diffs/ShipDamageDiff.ts index 6eeb817..99b52ba 100644 --- a/src/core/diffs/ShipDamageDiff.ts +++ b/src/core/diffs/ShipDamageDiff.ts @@ -13,11 +13,15 @@ module TK.SpaceTac { // Damage to shield shield: number - constructor(ship: Ship, hull: number, shield: number) { + // Theoretical damage value + theoretical: number + + constructor(ship: Ship, hull: number, shield: number, theoretical = hull + shield) { super(ship); this.hull = hull; this.shield = shield; + this.theoretical = theoretical; } protected applyOnShip(ship: Ship, battle: Battle): void { diff --git a/src/core/effects/AttributeEffect.spec.ts b/src/core/effects/AttributeEffect.spec.ts index bf49484..0ef28f4 100644 --- a/src/core/effects/AttributeEffect.spec.ts +++ b/src/core/effects/AttributeEffect.spec.ts @@ -6,11 +6,11 @@ module TK.SpaceTac { check.equals(ship.getAttribute("maneuvrability"), 0, "initial"); let effect1 = new AttributeEffect("maneuvrability", 20); - battle.applyDiffs(effect1.getOnDiffs(ship, ship)); + battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("maneuvrability"), 20, "applied 1"); let effect2 = new AttributeEffect("maneuvrability", 10); - battle.applyDiffs(effect2.getOnDiffs(ship, ship)); + battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("maneuvrability"), 30, "applied 2"); battle.applyDiffs(effect1.getOffDiffs(ship)); diff --git a/src/core/effects/AttributeEffect.ts b/src/core/effects/AttributeEffect.ts index 0e84d37..3c59942 100644 --- a/src/core/effects/AttributeEffect.ts +++ b/src/core/effects/AttributeEffect.ts @@ -20,7 +20,7 @@ module TK.SpaceTac { this.value = value; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { return [ new ShipAttributeDiff(ship, this.attrcode, { cumulative: this.value }, {}), ]; diff --git a/src/core/effects/AttributeLimitEffect.spec.ts b/src/core/effects/AttributeLimitEffect.spec.ts index 117caab..708b7c2 100644 --- a/src/core/effects/AttributeLimitEffect.spec.ts +++ b/src/core/effects/AttributeLimitEffect.spec.ts @@ -7,11 +7,11 @@ module TK.SpaceTac { check.equals(ship.getAttribute("precision"), 12, "initial"); let effect1 = new AttributeLimitEffect("precision", 5); - battle.applyDiffs(effect1.getOnDiffs(ship, ship)); + battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("precision"), 5, "applied 1"); let effect2 = new AttributeLimitEffect("precision", 3); - battle.applyDiffs(effect2.getOnDiffs(ship, ship)); + battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("precision"), 3, "applied 2"); battle.applyDiffs(effect1.getOffDiffs(ship)); diff --git a/src/core/effects/AttributeLimitEffect.ts b/src/core/effects/AttributeLimitEffect.ts index fefc2ae..3e93d7e 100644 --- a/src/core/effects/AttributeLimitEffect.ts +++ b/src/core/effects/AttributeLimitEffect.ts @@ -20,7 +20,7 @@ module TK.SpaceTac { this.value = value; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { return [ new ShipAttributeDiff(ship, this.attrcode, { limit: this.value }, {}), ]; diff --git a/src/core/effects/AttributeMultiplyEffect.spec.ts b/src/core/effects/AttributeMultiplyEffect.spec.ts index 8ffddda..adfb26b 100644 --- a/src/core/effects/AttributeMultiplyEffect.spec.ts +++ b/src/core/effects/AttributeMultiplyEffect.spec.ts @@ -7,11 +7,11 @@ module TK.SpaceTac { check.equals(ship.getAttribute("hull_capacity"), 100, "initial"); let effect1 = new AttributeMultiplyEffect("hull_capacity", 30); - battle.applyDiffs(effect1.getOnDiffs(ship, ship)); + battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("hull_capacity"), 130, "applied 1"); let effect2 = new AttributeMultiplyEffect("hull_capacity", -10); - battle.applyDiffs(effect2.getOnDiffs(ship, ship)); + battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1)); check.equals(ship.getAttribute("hull_capacity"), 120, "applied 2"); battle.applyDiffs(effect1.getOffDiffs(ship)); diff --git a/src/core/effects/AttributeMultiplyEffect.ts b/src/core/effects/AttributeMultiplyEffect.ts index 78bb058..291dc0d 100644 --- a/src/core/effects/AttributeMultiplyEffect.ts +++ b/src/core/effects/AttributeMultiplyEffect.ts @@ -21,7 +21,7 @@ module TK.SpaceTac { this.value = value; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { return [ new ShipAttributeDiff(ship, this.attrcode, { multiplier: this.value }, {}), ]; diff --git a/src/core/effects/BaseEffect.spec.ts b/src/core/effects/BaseEffect.spec.ts deleted file mode 100644 index 732081a..0000000 --- a/src/core/effects/BaseEffect.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -module TK.SpaceTac.Specs { - testing("BaseEffect", test => { - test.case("gets a fixed or variable amount", check => { - let effect = new BaseEffect("test"); - - check.equals(effect.resolveAmount(50), 50); - check.equals(effect.resolveAmount({ base: 20, span: 10 }, new SkewedRandomGenerator([0.3])), 23); - check.equals(effect.resolveAmount({ base: 20, span: 0 }, new SkewedRandomGenerator([0.3])), 20); - }) - }) -} diff --git a/src/core/effects/BaseEffect.ts b/src/core/effects/BaseEffect.ts index b297084..9410d68 100644 --- a/src/core/effects/BaseEffect.ts +++ b/src/core/effects/BaseEffect.ts @@ -1,8 +1,6 @@ /// module TK.SpaceTac { - export type EffectAmount = number | { base: number, span: number }; - /** * Base class for effects of actions that can be applied on ships * @@ -21,7 +19,7 @@ module TK.SpaceTac { /** * Get the list of diffs needed to activate this effect on a ship */ - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success = 1): BaseBattleDiff[] { return []; } @@ -60,18 +58,5 @@ module TK.SpaceTac { getDescription(): string { return "unknown effect"; } - - /** - * Resolve an effect amount - */ - resolveAmount(val: EffectAmount, random = RandomGenerator.global): number { - if (typeof val == "number") { - return val; - } else if (val.span) { - return random.randInt(val.base, val.base + val.span); - } else { - return val.base; - } - } } } diff --git a/src/core/effects/CooldownEffect.spec.ts b/src/core/effects/CooldownEffect.spec.ts index fd2ac9e..e850226 100644 --- a/src/core/effects/CooldownEffect.spec.ts +++ b/src/core/effects/CooldownEffect.spec.ts @@ -8,24 +8,24 @@ module TK.SpaceTac { check.equals(weapons.map(weapon => weapon.cooldown.heat), [0, 0, 0]); let effect = new CooldownEffect(0, 0); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(weapons.map(weapon => weapon.cooldown.heat), [0, 0, 0]); weapons.forEach(weapon => weapon.cooldown.use()); check.equals(weapons.map(weapon => weapon.cooldown.heat), [3, 3, 3]); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(weapons.map(weapon => weapon.cooldown.heat), [0, 0, 0]); weapons.forEach(weapon => weapon.cooldown.use()); check.equals(weapons.map(weapon => weapon.cooldown.heat), [3, 3, 3]); effect = new CooldownEffect(1, 0); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(weapons.map(weapon => weapon.cooldown.heat), [2, 2, 2]); effect = new CooldownEffect(1, 2); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(weapons.map(weapon => weapon.cooldown.heat).sort(), [1, 1, 2]); }) diff --git a/src/core/effects/CooldownEffect.ts b/src/core/effects/CooldownEffect.ts index 05c41c6..f3f6524 100644 --- a/src/core/effects/CooldownEffect.ts +++ b/src/core/effects/CooldownEffect.ts @@ -18,7 +18,7 @@ module TK.SpaceTac { this.maxcount = maxcount; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { let equipments = ship.listEquipment().filter(equ => equ.cooldown.heat > 0); if (this.maxcount && equipments.length > this.maxcount) { diff --git a/src/core/effects/DamageEffect.spec.ts b/src/core/effects/DamageEffect.spec.ts index 0031ffb..4f6d582 100644 --- a/src/core/effects/DamageEffect.spec.ts +++ b/src/core/effects/DamageEffect.spec.ts @@ -20,16 +20,16 @@ module TK.SpaceTac.Specs { checkValues("initial", 150, 400, 0, 0); - battle.applyDiffs(new DamageEffect(50).getOnDiffs(ship, ship)); + battle.applyDiffs(new DamageEffect(50).getOnDiffs(ship, ship, 1)); checkValues("after 50 damage", 150, 350, 0, 5); - battle.applyDiffs(new DamageEffect(250).getOnDiffs(ship, ship)); + battle.applyDiffs(new DamageEffect(250).getOnDiffs(ship, ship, 1)); checkValues("after 250 damage", 150, 100, 0, 30); - battle.applyDiffs(new DamageEffect(201).getOnDiffs(ship, ship)); + battle.applyDiffs(new DamageEffect(201).getOnDiffs(ship, ship, 1)); checkValues("after 201 damage", 49, 0, 11, 40); - battle.applyDiffs(new DamageEffect(8000).getOnDiffs(ship, ship)); + battle.applyDiffs(new DamageEffect(8000).getOnDiffs(ship, ship, 1)); checkValues("after 8000 damage", 0, 0, 16, 40); }); @@ -43,7 +43,7 @@ module TK.SpaceTac.Specs { TestTools.setShipHP(ship, 1000, 1000); let damage = new DamageEffect(200); - check.equals(damage.getEffectiveDamage(ship), [200, 0]); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 200)); check.patch(ship, "ieffects", iterator([ isingle(new DamageModifierEffect(-15)), @@ -54,14 +54,14 @@ module TK.SpaceTac.Specs { isingle(new DamageModifierEffect(3)) ])); - check.equals(damage.getEffectiveDamage(ship), [170, 0]); - check.equals(damage.getEffectiveDamage(ship), [240, 0]); - check.equals(damage.getEffectiveDamage(ship), [0, 0]); - check.equals(damage.getEffectiveDamage(ship), [400, 0]); - check.equals(damage.getEffectiveDamage(ship), [190, 0]); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 170)); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 240)); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 0)); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 400)); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 190)); damage = new DamageEffect(40); - check.equals(damage.getEffectiveDamage(ship), [41, 0]); + check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 41)); }); }); } diff --git a/src/core/effects/DamageEffect.ts b/src/core/effects/DamageEffect.ts index 77a6238..588fe7f 100644 --- a/src/core/effects/DamageEffect.ts +++ b/src/core/effects/DamageEffect.ts @@ -8,10 +8,10 @@ module TK.SpaceTac { */ export class DamageEffect extends BaseEffect { // Base damage points - base: number; + base: number // Range of randomness (effective damage will be between *value* and *value+range*) - span: number; + span: number constructor(value = 0, span = 0) { super("damage"); @@ -36,47 +36,36 @@ module TK.SpaceTac { /** * Get the effective damage done to both shield and hull (in this order) */ - getEffectiveDamage(ship: Ship): [number, number] { - var damage = (this.span > 0) ? RandomGenerator.global.randInt(this.base, this.base + this.span) : this.base; - var hull: number; - var shield: number; - + getEffectiveDamage(ship: Ship, success: number): ShipDamageDiff { // Apply modifiers - damage = Math.round(damage * this.getFactor(ship)); + let theoritical = Math.round((this.base + this.span * success) * this.getFactor(ship)); + let damage = theoritical; // Apply on shields - if (damage >= ship.getValue("shield")) { - shield = ship.getValue("shield"); - } else { - shield = damage; - } + let shield = (damage >= ship.getValue("shield")) ? ship.getValue("shield") : damage; damage -= shield; // Apply on hull - if (damage >= ship.getValue("hull")) { - hull = ship.getValue("hull"); - } else { - hull = damage; - } + let hull = (damage >= ship.getValue("hull")) ? ship.getValue("hull") : damage; - return [shield, hull]; + return new ShipDamageDiff(ship, hull, shield, theoritical); } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { - let [shield, hull] = this.getEffectiveDamage(ship); - + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { let result: BaseBattleDiff[] = []; - if (shield || hull) { - result.push(new ShipDamageDiff(ship, hull, shield)); + let damage = this.getEffectiveDamage(ship, success); + + if (damage.shield || damage.hull) { + result.push(damage); } - if (shield) { - result.push(new ShipValueDiff(ship, "shield", -shield)); + if (damage.shield) { + result.push(new ShipValueDiff(ship, "shield", -damage.shield)); } - if (hull) { - result.push(new ShipValueDiff(ship, "hull", -hull)); + if (damage.hull) { + result.push(new ShipValueDiff(ship, "hull", -damage.hull)); } return result; diff --git a/src/core/effects/RepelEffect.spec.ts b/src/core/effects/RepelEffect.spec.ts index 5c803bd..11d3d41 100644 --- a/src/core/effects/RepelEffect.spec.ts +++ b/src/core/effects/RepelEffect.spec.ts @@ -14,9 +14,9 @@ module TK.SpaceTac.Specs { ship2a.setArenaPosition(100, 280); let effect = new RepelEffect(12); - battle.applyDiffs(effect.getOnDiffs(ship1a, ship1a)); - battle.applyDiffs(effect.getOnDiffs(ship1b, ship1a)); - battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a)); + battle.applyDiffs(effect.getOnDiffs(ship1a, ship1a, 1)); + battle.applyDiffs(effect.getOnDiffs(ship1b, ship1a, 1)); + battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a, 1)); check.equals(ship1a.location, new ArenaLocationAngle(100, 100)); check.equals(ship1b.location, new ArenaLocationAngle(262, 100)); @@ -33,7 +33,7 @@ module TK.SpaceTac.Specs { ship2b.setArenaPosition(100, 350); let effect = new RepelEffect(85); - battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a)); + battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a, 1)); check.equals(ship2a.location, new ArenaLocationAngle(100, 250)); }) }) diff --git a/src/core/effects/RepelEffect.ts b/src/core/effects/RepelEffect.ts index 5beb232..6a4856a 100644 --- a/src/core/effects/RepelEffect.ts +++ b/src/core/effects/RepelEffect.ts @@ -13,7 +13,7 @@ module TK.SpaceTac { this.value = value; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { if (ship != source) { let angle = arenaAngle(source.location, ship.location); let destination = new ArenaLocation(ship.arena_x + Math.cos(angle) * this.value, ship.arena_y + Math.sin(angle) * this.value); diff --git a/src/core/effects/StickyEffect.spec.ts b/src/core/effects/StickyEffect.spec.ts index f2cc7a2..ecc1c1c 100644 --- a/src/core/effects/StickyEffect.spec.ts +++ b/src/core/effects/StickyEffect.spec.ts @@ -10,7 +10,7 @@ module TK.SpaceTac.Specs { }) let effect = new StickyEffect(new AttributeEffect("precision", 1), 2); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.in("after", check => { check.equals(ship.active_effects.count(), 1, "one sticky effect"); @@ -25,7 +25,7 @@ module TK.SpaceTac.Specs { } }) - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.in("after second apply", check => { check.equals(ship.active_effects.count(), 1, "one sticky effect"); diff --git a/src/core/effects/StickyEffect.ts b/src/core/effects/StickyEffect.ts index 48a1368..2e6a443 100644 --- a/src/core/effects/StickyEffect.ts +++ b/src/core/effects/StickyEffect.ts @@ -21,7 +21,7 @@ module TK.SpaceTac { this.duration = duration; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { let result: BaseBattleDiff[] = []; let previous = ship.active_effects.get(this.id); diff --git a/src/core/effects/ValueEffect.spec.ts b/src/core/effects/ValueEffect.spec.ts index e67c432..14f9714 100644 --- a/src/core/effects/ValueEffect.spec.ts +++ b/src/core/effects/ValueEffect.spec.ts @@ -8,10 +8,10 @@ module TK.SpaceTac { ship.setValue("shield", 55); check.equals(ship.getValue("shield"), 55); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(ship.getValue("shield"), 75); - battle.applyDiffs(effect.getOnDiffs(ship, ship)); + battle.applyDiffs(effect.getOnDiffs(ship, ship, 1)); check.equals(ship.getValue("shield"), 95); }); diff --git a/src/core/effects/ValueEffect.ts b/src/core/effects/ValueEffect.ts index b5eaf99..1c0befe 100644 --- a/src/core/effects/ValueEffect.ts +++ b/src/core/effects/ValueEffect.ts @@ -36,7 +36,7 @@ module TK.SpaceTac { this.value_end = value_end; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { if (this.value_on) { return ship.getValueDiffs(this.valuetype, this.value_on, true); } else { diff --git a/src/core/effects/ValueTransferEffect.spec.ts b/src/core/effects/ValueTransferEffect.spec.ts index cda457e..ffc7f7f 100644 --- a/src/core/effects/ValueTransferEffect.spec.ts +++ b/src/core/effects/ValueTransferEffect.spec.ts @@ -9,12 +9,12 @@ module TK.SpaceTac.Specs { TestTools.setShipHP(ship2, 100, 50); let effect = new ValueTransferEffect("hull", -30); - battle.applyDiffs(effect.getOnDiffs(ship2, ship1)); + battle.applyDiffs(effect.getOnDiffs(ship2, ship1, 1)); check.equals(ship1.getValue("hull"), 40); check.equals(ship2.getValue("hull"), 70); effect = new ValueTransferEffect("hull", 1000); - battle.applyDiffs(effect.getOnDiffs(ship2, ship1)); + battle.applyDiffs(effect.getOnDiffs(ship2, ship1, 1)); check.equals(ship1.getValue("hull"), 0); check.equals(ship2.getValue("hull"), 110); // over limit but will be fixed later }) diff --git a/src/core/effects/ValueTransferEffect.ts b/src/core/effects/ValueTransferEffect.ts index be014ba..69ea2a4 100644 --- a/src/core/effects/ValueTransferEffect.ts +++ b/src/core/effects/ValueTransferEffect.ts @@ -18,10 +18,10 @@ module TK.SpaceTac { this.amount = amount; } - getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] { + getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] { if (source instanceof Ship) { if (this.amount < 0) { - return new ValueTransferEffect(this.valuetype, -this.amount).getOnDiffs(source, ship); + return new ValueTransferEffect(this.valuetype, -this.amount).getOnDiffs(source, ship, success); } else { let amount = Math.min(source.getValue(this.valuetype), this.amount); if (amount) { diff --git a/src/core/equipments/RawWeapons.spec.ts b/src/core/equipments/RawWeapons.spec.ts index 40974a0..da1e939 100644 --- a/src/core/equipments/RawWeapons.spec.ts +++ b/src/core/equipments/RawWeapons.spec.ts @@ -5,25 +5,25 @@ module TK.SpaceTac.Specs { let equipment = template.generate(1); check.equals(equipment.requirements, { "skill_materials": 1 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(30, 20)], 3, 400, 0)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(30, 20)], 3, 400, 0, 0, 60, 20, 15)); check.equals(equipment.price, 100); check.equals(equipment.cooldown, new Cooldown(2, 2)); equipment = template.generate(2); check.equals(equipment.requirements, { "skill_materials": 2 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(42, 28)], 3, 412, 0)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(42, 28)], 3, 412, 0, 0, 60, 20, 15)); check.equals(equipment.price, 350); check.equals(equipment.cooldown, new Cooldown(2, 2)); equipment = template.generate(3); check.equals(equipment.requirements, { "skill_materials": 4 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(56, 37)], 3, 426, 0)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(56, 37)], 3, 426, 0, 0, 60, 20, 15)); check.equals(equipment.price, 850); check.equals(equipment.cooldown, new Cooldown(2, 2)); equipment = template.generate(10); check.equals(equipment.requirements, { "skill_materials": 23 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(224, 149)], 3, 594, 0)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(224, 149)], 3, 594, 0, 0, 60, 20, 15)); check.equals(equipment.price, 11350); check.equals(equipment.cooldown, new Cooldown(2, 2)); }); @@ -33,25 +33,25 @@ module TK.SpaceTac.Specs { let equipment = template.generate(1); check.equals(equipment.requirements, { "skill_materials": 1, "skill_photons": 1 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(26, 4)], 4, 500, 150)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(26, 4)], 4, 500, 150, 0, 30, 40, 10)); check.equals(equipment.cooldown, new Cooldown(1, 0)); check.equals(equipment.price, 163); equipment = template.generate(2); check.equals(equipment.requirements, { "skill_materials": 2, "skill_photons": 1 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(28, 5)], 4, 520, 155)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(28, 5)], 4, 520, 155, 0, 30, 40, 10)); check.equals(equipment.cooldown, new Cooldown(1, 0)); check.equals(equipment.price, 570); equipment = template.generate(3); check.equals(equipment.requirements, { "skill_materials": 3, "skill_photons": 2 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(30, 6)], 4, 544, 161)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(30, 6)], 4, 544, 161, 0, 30, 40, 10)); check.equals(equipment.cooldown, new Cooldown(1, 0)); check.equals(equipment.price, 1385); equipment = template.generate(10); check.equals(equipment.requirements, { "skill_materials": 20, "skill_photons": 13 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(58, 20)], 4, 824, 231)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(58, 20)], 4, 824, 231, 0, 30, 40, 10)); check.equals(equipment.cooldown, new Cooldown(1, 0)); check.equals(equipment.price, 18500); }); @@ -61,25 +61,25 @@ module TK.SpaceTac.Specs { let equipment = template.generate(1); check.equals(equipment.requirements, { "skill_photons": 1, "skill_quantum": 1 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(20, 25)], 5, 300, 0, 40)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(20, 25)], 5, 300, 0, 40, 45, 60, 20)); check.equals(equipment.cooldown, new Cooldown(1, 1)); check.equals(equipment.price, 152); equipment = template.generate(2); check.equals(equipment.requirements, { "skill_antimatter": 1, "skill_photons": 2, "skill_quantum": 2 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(28, 35)], 5, 310, 0, 42)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(28, 35)], 5, 310, 0, 42, 45, 60, 20)); check.equals(equipment.cooldown, new Cooldown(1, 1)); check.equals(equipment.price, 532); equipment = template.generate(3); check.equals(equipment.requirements, { "skill_antimatter": 1, "skill_photons": 4, "skill_quantum": 3 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(37, 47)], 5, 322, 0, 44)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(37, 47)], 5, 322, 0, 44, 45, 60, 20)); check.equals(equipment.cooldown, new Cooldown(1, 1)); check.equals(equipment.price, 1292); equipment = template.generate(10); check.equals(equipment.requirements, { "skill_antimatter": 11, "skill_photons": 23, "skill_quantum": 20 }); - compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(149, 187)], 5, 462, 0, 72)); + compare_trigger_action(check, equipment.action, new TriggerAction(equipment, [new DamageEffect(149, 187)], 5, 462, 0, 72, 45, 60, 20)); check.equals(equipment.cooldown, new Cooldown(1, 1)); check.equals(equipment.price, 17252); }); diff --git a/src/core/equipments/RawWeapons.ts b/src/core/equipments/RawWeapons.ts index 35a4017..3a5cf5d 100644 --- a/src/core/equipments/RawWeapons.ts +++ b/src/core/equipments/RawWeapons.ts @@ -9,7 +9,7 @@ module TK.SpaceTac.Equipments { this.setCooldown(irepeat(2), irepeat(2)); this.addTriggerAction(irepeat(3), [ new EffectTemplate(new DamageEffect(), { base: leveled(30), span: leveled(20) }) - ], leveled(400, 12)); + ], leveled(400, 12), undefined, undefined, irepeat(60), irepeat(20), irepeat(15)); } } @@ -21,7 +21,7 @@ module TK.SpaceTac.Equipments { this.setCooldown(irepeat(1), irepeat(0)); this.addTriggerAction(irepeat(4), [ new EffectTemplate(new DamageEffect(), { base: leveled(26, 2), span: leveled(4, 1) }) - ], leveled(500, 20), leveled(150, 5)); + ], leveled(500, 20), leveled(150, 5), undefined, irepeat(30), irepeat(40), irepeat(10)); } } @@ -34,7 +34,7 @@ module TK.SpaceTac.Equipments { this.setCooldown(irepeat(1), irepeat(1)); this.addTriggerAction(irepeat(5), [ new EffectTemplate(new DamageEffect(), { base: leveled(20), span: leveled(25) }) - ], leveled(300, 10), irepeat(0), leveled(40, 2)); + ], leveled(300, 10), irepeat(0), leveled(40, 2), irepeat(45), irepeat(60), irepeat(20)); } } } diff --git a/src/ui/battle/ActionTooltip.spec.ts b/src/ui/battle/ActionTooltip.spec.ts index f0aacb9..e382890 100644 --- a/src/ui/battle/ActionTooltip.spec.ts +++ b/src/ui/battle/ActionTooltip.spec.ts @@ -28,7 +28,7 @@ module TK.SpaceTac.UI.Specs { ActionTooltip.fill(tooltip.getFiller(), ship, action2, 1); checkText(check, (tooltip).container.content.children[1], "Weapon"); checkText(check, (tooltip).container.content.children[2], "Cost: 2 power"); - checkText(check, (tooltip).container.content.children[3], "Fire (power usage 2, max range 50km):\n• do 12 damage on target"); + checkText(check, (tooltip).container.content.children[3], "Fire (power 2, range 50km):\n• do 12 damage on target"); checkText(check, (tooltip).container.content.children[4], "[ 2 ]"); tooltip.hide(); diff --git a/src/ui/battle/ArenaShip.ts b/src/ui/battle/ArenaShip.ts index 2a56cf9..4815a9a 100644 --- a/src/ui/battle/ArenaShip.ts +++ b/src/ui/battle/ArenaShip.ts @@ -68,7 +68,8 @@ module TK.SpaceTac.UI { // Add stasis effect this.stasis = this.battleview.newImage("battle-hud-ship-stasis"); this.stasis.anchor.set(0.5, 0.5); - this.stasis.visible = false; + this.stasis.alpha = 0.9; + this.stasis.visible = !ship.alive; this.add(this.stasis); // HSP display @@ -202,7 +203,7 @@ module TK.SpaceTac.UI { return { background: async (animate, timer) => { if (animate) { - await this.displayEffect(`${diff.hull + diff.shield} damage`, false); + await this.displayEffect(`${diff.theoretical} damage`, false); await timer.sleep(1000); } } diff --git a/src/ui/battle/Targetting.ts b/src/ui/battle/Targetting.ts index 766240b..deb15df 100644 --- a/src/ui/battle/Targetting.ts +++ b/src/ui/battle/Targetting.ts @@ -55,11 +55,11 @@ module TK.SpaceTac.UI { this.impact_area = new Phaser.Graphics(view.game); this.impact_area.visible = false; - this.container.add(this.impact_indicators); this.container.add(this.impact_area); this.container.add(this.drawn_info); this.container.add(this.move_ghost); this.container.add(this.fire_arrow); + this.container.add(this.impact_indicators); } /** @@ -117,7 +117,7 @@ module TK.SpaceTac.UI { } /** - * Update impact indicators (highlighting impacted ships) + * Update impact indicators (highlighting impacted ships, with success factor) */ updateImpactIndicators(impacts: Phaser.Group, ship: Ship, action: BaseAction, target: Target, source: IArenaLocation = ship.location): void { let ships = action.getImpactedShips(ship, target, source); @@ -125,9 +125,18 @@ module TK.SpaceTac.UI { // TODO differential impacts.removeAll(true); ships.forEach(iship => { - let indicator = this.view.newImage("battle-hud-ship-impacted", iship.arena_x, iship.arena_y); - indicator.anchor.set(0.5); - impacts.add(indicator); + let builder = new UIBuilder(this.view, impacts); + + let indicator = builder.image("battle-hud-ship-impacted", iship.arena_x, iship.arena_y); + indicator.anchor.set(0.5, 0.5); + + if (action instanceof TriggerAction) { + let success = action.getSuccessFactor(ship, iship); + builder.in(indicator, builder => { + builder.text(`${Math.round(success * 100)}%`, 0, -32, + { center: true, color: "#c69b70", size: 14, shadow: true }); + }); + } }); impacts.visible = true; } else {