From 8e43116a1ffb79e8202777869d72481c466e47be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 7 Feb 2017 20:15:21 +0100 Subject: [PATCH] Refactoring of attributes/values system --- TODO | 5 +- src/game/Attribute.spec.ts | 99 ------- src/game/Attribute.ts | 124 --------- src/game/AttributeCollection.spec.ts | 25 -- src/game/AttributeCollection.ts | 55 ---- src/game/Battle.spec.ts | 10 +- src/game/Battle.ts | 2 +- src/game/BattleLog.spec.ts | 4 +- src/game/EffectTemplate.spec.ts | 14 +- src/game/Equipment.spec.ts | 16 +- src/game/Equipment.ts | 8 +- src/game/LootTemplate.spec.ts | 25 +- src/game/LootTemplate.ts | 30 +- src/game/MoveFireSimulator.ts | 2 +- src/game/Ship.spec.ts | 57 ++-- src/game/Ship.ts | 256 +++++++++++------- src/game/ShipAttribute.ts | 16 ++ src/game/ShipValue.spec.ts | 72 +++++ src/game/ShipValue.ts | 72 +++++ src/game/Slot.spec.ts | 4 +- src/game/TestTools.ts | 37 +-- src/game/actions/BaseAction.spec.ts | 8 +- src/game/actions/BaseAction.ts | 2 +- src/game/actions/DeployDroneAction.spec.ts | 2 +- src/game/actions/MoveAction.spec.ts | 20 +- src/game/actions/MoveAction.ts | 4 +- src/game/ai/BullyAI.spec.ts | 46 ++-- src/game/ai/BullyAI.ts | 2 +- src/game/effects/AttributeAddEffect.spec.ts | 18 -- src/game/effects/AttributeAddEffect.ts | 35 --- src/game/effects/AttributeEffect.spec.ts | 0 src/game/effects/AttributeEffect.ts | 36 +++ src/game/effects/AttributeLimitEffect.ts | 21 +- src/game/effects/AttributeMaxEffect.ts | 24 -- src/game/effects/AttributeValueEffect.ts | 24 -- src/game/effects/DamageEffect.ts | 14 +- src/game/effects/ValueEffect.spec.ts | 18 ++ src/game/effects/ValueEffect.ts | 35 +++ src/game/equipments/AbstractWeapon.spec.ts | 30 +- src/game/equipments/BasicForceField.ts | 2 +- src/game/equipments/BasicPowerCore.ts | 8 +- src/game/equipments/ConventionalEngine.ts | 2 +- src/game/equipments/IronHull.ts | 2 +- src/game/equipments/PowerDepleter.spec.ts | 12 +- src/game/equipments/PowerDepleter.ts | 2 +- src/game/equipments/RepairDrone.spec.ts | 2 +- src/game/equipments/RepairDrone.ts | 2 +- .../equipments/SubMunitionMissile.spec.ts | 14 +- src/game/events/AttributeChangeEvent.ts | 15 - src/game/events/ValueChangeEvent.ts | 15 + src/view/battle/ActionBar.spec.ts | 10 +- src/view/battle/ActionBar.ts | 8 +- src/view/battle/ActionIcon.ts | 2 +- src/view/battle/LogProcessor.ts | 11 +- src/view/battle/ShipListItem.ts | 27 +- src/view/battle/ShipTooltip.ts | 20 +- 56 files changed, 657 insertions(+), 769 deletions(-) delete mode 100644 src/game/Attribute.spec.ts delete mode 100644 src/game/Attribute.ts delete mode 100644 src/game/AttributeCollection.spec.ts delete mode 100644 src/game/AttributeCollection.ts create mode 100644 src/game/ShipAttribute.ts create mode 100644 src/game/ShipValue.spec.ts create mode 100644 src/game/ShipValue.ts delete mode 100644 src/game/effects/AttributeAddEffect.spec.ts delete mode 100644 src/game/effects/AttributeAddEffect.ts create mode 100644 src/game/effects/AttributeEffect.spec.ts create mode 100644 src/game/effects/AttributeEffect.ts delete mode 100644 src/game/effects/AttributeMaxEffect.ts delete mode 100644 src/game/effects/AttributeValueEffect.ts create mode 100644 src/game/effects/ValueEffect.spec.ts create mode 100644 src/game/effects/ValueEffect.ts delete mode 100644 src/game/events/AttributeChangeEvent.ts create mode 100644 src/game/events/ValueChangeEvent.ts diff --git a/TODO b/TODO index 0140070..bfd0d4d 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,7 @@ * Restore serialization -* Refactor attribute effects (add/sub, value/max, temporary/permanent) -* Drones: add hooks on events +* Drones: add hooks on game events * Drones: add sprite, radius and tooltip -* Repair drone: add graphics +* Repair drone: add graphics and proper description * Allow to cancel last moves * Effect should be random in a range (eg. "damage target 50-75") * Add an overload/cooling system diff --git a/src/game/Attribute.spec.ts b/src/game/Attribute.spec.ts deleted file mode 100644 index 4d4ec95..0000000 --- a/src/game/Attribute.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -module TS.SpaceTac.Game { - describe("Attribute", function () { - it("is initially not limited", function () { - var attr = new Attribute(); - - attr.set(8888888); - expect(attr.current).toBe(8888888); - }); - - it("enumerates codes", function () { - var result = []; - Attribute.forEachCode((code: AttributeCode) => { - result.push(code); - }); - expect(result).toEqual([ - AttributeCode.Initiative, - AttributeCode.Hull, - AttributeCode.Shield, - AttributeCode.Power, - AttributeCode.Power_Recovery, - AttributeCode.Power_Initial, - AttributeCode.Cap_Material, - AttributeCode.Cap_Energy, - AttributeCode.Cap_Electronics, - AttributeCode.Cap_Human, - AttributeCode.Cap_Time, - AttributeCode.Cap_Gravity, - AttributeCode.Misc - ]); - }); - - it("gets human readable name", function () { - expect(ATTRIBUTE_NAMES[AttributeCode.Initiative]).toEqual("initiative"); - expect(ATTRIBUTE_NAMES[AttributeCode.Power]).toEqual("power"); - }); - - it("applies minimal and maximal value", function () { - var attr = new Attribute(AttributeCode.Misc, 50, 100); - expect(attr.current).toBe(50); - - attr.add(8); - expect(attr.current).toBe(58); - - attr.add(60); - expect(attr.current).toBe(100); - - attr.add(-72); - expect(attr.current).toBe(28); - - attr.add(-60); - expect(attr.current).toBe(0); - - attr.set(8); - expect(attr.current).toBe(8); - - attr.set(-4); - expect(attr.current).toBe(0); - - attr.set(105); - expect(attr.current).toBe(100); - - attr.setMaximal(50); - expect(attr.current).toBe(50); - - attr.setMaximal(80); - expect(attr.current).toBe(50); - }); - - it("tells if value changed", function () { - var result: boolean; - var attr = new Attribute(AttributeCode.Misc, 50, 100); - expect(attr.current).toBe(50); - - result = attr.set(51); - expect(result).toBe(true); - - result = attr.set(51); - expect(result).toBe(false); - - result = attr.add(1); - expect(result).toBe(true); - - result = attr.add(0); - expect(result).toBe(false); - - result = attr.add(1000); - expect(result).toBe(true); - - result = attr.add(2000); - expect(result).toBe(false); - - result = attr.set(-500); - expect(result).toBe(true); - - result = attr.add(-600); - expect(result).toBe(false); - }); - }); -} diff --git a/src/game/Attribute.ts b/src/game/Attribute.ts deleted file mode 100644 index 9732e56..0000000 --- a/src/game/Attribute.ts +++ /dev/null @@ -1,124 +0,0 @@ -module TS.SpaceTac.Game { - // Code to identify - export enum AttributeCode { - // Initiative level - Initiative, - - // Hull points (similar to health points or HP in other games) - Hull, - - // Damage the shield can take - Shield, - - // Power available to make actions (similar to action points or AP in other games) - Power, - - // Power recovered each turn - Power_Recovery, - - // Starting power in a battle - Power_Initial, - - // Capability level in materials - Cap_Material, - - // Capability level in energy - Cap_Energy, - - // Capability level in electronics - Cap_Electronics, - - // Capability level in human things - Cap_Human, - - // Capability level in time manipulation - Cap_Time, - - // Capability level in gravity manipulation - Cap_Gravity, - - // Miscellaneous attribute - Misc - } - - // Name mapping for attributes - export const ATTRIBUTE_NAMES = [ - "initiative", - "hull", - "shield", - "power", - "power recovery", - "initial power", - "materials", - "energy", - "electronics", - "human", - "time", - "gravity" - ] - - // Value computed from equipment - // This value can be altered by effects - // Example attributes are health points, power recovery... - export class Attribute { - // Identifying code of this attribute - code: AttributeCode; - - // Maximal attribute value - maximal: number; - - // Current attribute value - current: number; - - // Create an attribute - constructor(code: AttributeCode = AttributeCode.Misc, current: number = 0, maximal: number = null) { - this.code = code; - this.maximal = maximal; - this.current = current; - } - - // Iterator over each code - static forEachCode(callback: (code: AttributeCode) => void) { - for (var val in AttributeCode) { - var parsed = parseInt(val, 10); - if (!isNaN(parsed)) { - callback(parsed); - } - } - } - - // Set the maximal value - setMaximal(value: number): void { - this.maximal = value; - this.fix(); - } - - // Set an absolute value - // Returns true if the value changed - set(value: number): boolean { - var old_value = this.current; - this.current = value; - this.fix(); - return this.current !== old_value; - } - - // Add an offset to current value - // Returns true if the value changed - add(value: number): boolean { - var old_value = this.current; - this.current += value; - this.fix(); - return this.current !== old_value; - } - - // Fix the value to be integer, positive and lower than maximal - private fix(): void { - if (this.maximal !== null && this.current > this.maximal) { - this.current = this.maximal; - } - if (this.current < 0) { - this.current = 0; - } - } - } -} diff --git a/src/game/AttributeCollection.spec.ts b/src/game/AttributeCollection.spec.ts deleted file mode 100644 index 4f56c37..0000000 --- a/src/game/AttributeCollection.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -module TS.SpaceTac.Game { - describe("AttributeCollection", function () { - it("sets and gets an attribute value", function () { - var coll = new AttributeCollection(); - - coll.setValue(AttributeCode.Initiative, 5); - expect(coll.getValue(AttributeCode.Initiative)).toBe(5); - - expect(coll.getValue(AttributeCode.Hull)).toBe(0); - coll.setValue(AttributeCode.Hull, 2); - expect(coll.getValue(AttributeCode.Hull)).toBe(2); - }); - - it("sets and gets an attribute maximal", function () { - var coll = new AttributeCollection(); - - coll.setMaximum(AttributeCode.Initiative, 5); - expect(coll.getMaximum(AttributeCode.Initiative)).toBe(5); - - expect(coll.getMaximum(AttributeCode.Hull)).toBe(null); - coll.setMaximum(AttributeCode.Hull, 2); - expect(coll.getMaximum(AttributeCode.Hull)).toBe(2); - }); - }); -} diff --git a/src/game/AttributeCollection.ts b/src/game/AttributeCollection.ts deleted file mode 100644 index c5babf9..0000000 --- a/src/game/AttributeCollection.ts +++ /dev/null @@ -1,55 +0,0 @@ -module TS.SpaceTac.Game { - // Collection of several attributes - export class AttributeCollection { - // Attributes - private attributes: Attribute[]; - - // Base constructor - constructor() { - this.attributes = []; - } - - // Get or create an attribute by its code - getRawAttr(code: AttributeCode): Attribute { - var attr = this.attributes[code]; - if (!attr) { - attr = new Attribute(code); - this.attributes[code] = attr; - } - return attr; - } - - // Get an attribute value - getValue(attrcode: AttributeCode): number { - var attr = this.getRawAttr(attrcode); - return attr.current; - } - - // Set an attribute value - setValue(attrcode: AttributeCode, value: number): number { - var attr = this.getRawAttr(attrcode); - attr.set(value); - return attr.current; - } - - // Add an offset to an attribute value - addValue(attrcode: AttributeCode, offset: number): number { - var attr = this.getRawAttr(attrcode); - attr.add(offset); - return attr.current; - } - - // Get an attribute maximum - getMaximum(attrcode: AttributeCode): number { - var attr = this.getRawAttr(attrcode); - return attr.maximal; - } - - // Set an attribute maximum - setMaximum(attrcode: AttributeCode, value: number): number { - var attr = this.getRawAttr(attrcode); - attr.setMaximal(value); - return attr.maximal; - } - } -} diff --git a/src/game/Battle.spec.ts b/src/game/Battle.spec.ts index 6632f3a..8d35ace 100644 --- a/src/game/Battle.spec.ts +++ b/src/game/Battle.spec.ts @@ -5,15 +5,15 @@ module TS.SpaceTac.Game { var fleet2 = new Fleet(null); var ship1 = new Ship(fleet1, "F1S1"); - ship1.initiative.setMaximal(2); + ship1.setAttribute("initiative", 2); var ship2 = new Ship(fleet1, "F1S2"); - ship2.initiative.setMaximal(4); + ship2.setAttribute("initiative", 4); var ship3 = new Ship(fleet1, "F1S3"); - ship3.initiative.setMaximal(1); + ship3.setAttribute("initiative", 1); var ship4 = new Ship(fleet2, "F2S1"); - ship4.initiative.setMaximal(8); + ship4.setAttribute("initiative", 8); var ship5 = new Ship(fleet2, "F2S2"); - ship5.initiative.setMaximal(2); + ship5.setAttribute("initiative", 2); var battle = new Battle(fleet1, fleet2); expect(battle.play_order.length).toBe(0); diff --git a/src/game/Battle.ts b/src/game/Battle.ts index afa163d..2e5fa96 100644 --- a/src/game/Battle.ts +++ b/src/game/Battle.ts @@ -84,7 +84,7 @@ module TS.SpaceTac.Game { // Sort by throw result play_order.sort(function (ship1: Ship, ship2: Ship) { - return (ship2.initiative.current - ship1.initiative.current); + return (ship2.play_priority - ship1.play_priority); }); this.play_order = play_order; } diff --git a/src/game/BattleLog.spec.ts b/src/game/BattleLog.spec.ts index dd794e7..54ae478 100644 --- a/src/game/BattleLog.spec.ts +++ b/src/game/BattleLog.spec.ts @@ -59,7 +59,7 @@ module TS.SpaceTac.Game { it("logs ship change events", function () { var battle = Battle.newQuickRandom(); battle.log.clear(); - battle.log.addFilter("attr"); + battle.log.addFilter("value"); expect(battle.log.events.length).toBe(0); battle.advanceToNextShip(); @@ -70,7 +70,7 @@ module TS.SpaceTac.Game { it("can receive simulated initial state events", function () { var battle = Battle.newQuickRandom(); battle.log.clear(); - battle.log.addFilter("attr"); + battle.log.addFilter("value"); expect(battle.log.events.length).toBe(0); battle.injectInitialEvents(); diff --git a/src/game/EffectTemplate.spec.ts b/src/game/EffectTemplate.spec.ts index e344e19..66bde8e 100644 --- a/src/game/EffectTemplate.spec.ts +++ b/src/game/EffectTemplate.spec.ts @@ -1,21 +1,21 @@ module TS.SpaceTac.Game.Specs { describe("EffectTemplate", () => { it("interpolates between weak and strong effects", () => { - var base_effect = new AttributeMaxEffect(AttributeCode.Hull, 6); + var base_effect = new AttributeEffect("hull_capacity", 6); var template = new EffectTemplate(base_effect); template.addModifier("value", new Range(2, 8)); - var effect = template.generateFixed(0.0); - expect(effect.code).toEqual("attrmax"); + var effect = template.generateFixed(0.0); + expect(effect.code).toEqual("attr"); expect(effect.value).toEqual(2); - effect = template.generateFixed(1.0); - expect(effect.code).toEqual("attrmax"); + effect = template.generateFixed(1.0); + expect(effect.code).toEqual("attr"); expect(effect.value).toEqual(8); - effect = template.generateFixed(0.5); - expect(effect.code).toEqual("attrmax"); + effect = template.generateFixed(0.5); + expect(effect.code).toEqual("attr"); expect(effect.value).toEqual(5); }); }); diff --git a/src/game/Equipment.spec.ts b/src/game/Equipment.spec.ts index 2251646..6feb3e0 100644 --- a/src/game/Equipment.spec.ts +++ b/src/game/Equipment.spec.ts @@ -6,28 +6,28 @@ module TS.SpaceTac.Game.Specs { expect(equipment.canBeEquipped(ship)).toBe(true); - equipment.requirements.push(new Attribute(AttributeCode.Cap_Time, 2)); + equipment.requirements["skill_time"] = 2; expect(equipment.canBeEquipped(ship)).toBe(false); - ship.cap_time.set(1); + ship.attributes.skill_time.set(1); expect(equipment.canBeEquipped(ship)).toBe(false); - ship.cap_time.set(2); + ship.attributes.skill_time.set(2); expect(equipment.canBeEquipped(ship)).toBe(true); - ship.cap_time.set(3); + ship.attributes.skill_time.set(3); expect(equipment.canBeEquipped(ship)).toBe(true); // Second requirement - equipment.requirements.push(new Attribute(AttributeCode.Cap_Material, 3)); + equipment.requirements["skill_material"] = 3; expect(equipment.canBeEquipped(ship)).toBe(false); - ship.cap_material.set(4); + ship.attributes.skill_material.set(4); expect(equipment.canBeEquipped(ship)).toBe(true); }); @@ -44,8 +44,8 @@ module TS.SpaceTac.Game.Specs { expect(equipment.getActionDescription()).toEqual("- 50 damage on all ships in 20km of impact"); equipment.blast = 0; - equipment.target_effects.push(new StickyEffect(new AttributeLimitEffect(AttributeCode.Shield, 200), 3)); - expect(equipment.getActionDescription()).toEqual("- 50 damage on target\n- limit shield to 200 for 3 turns on target"); + equipment.target_effects.push(new StickyEffect(new AttributeLimitEffect("shield_capacity", 200), 3)); + expect(equipment.getActionDescription()).toEqual("- 50 damage on target\n- limit shield capacity to 200 for 3 turns on target"); }); }); } diff --git a/src/game/Equipment.ts b/src/game/Equipment.ts index aa8fcd8..054a556 100644 --- a/src/game/Equipment.ts +++ b/src/game/Equipment.ts @@ -29,7 +29,7 @@ module TS.SpaceTac.Game { min_level: number; // Minimal attribute to be able to equip this equipment - requirements: Attribute[]; + requirements: { [key: string]: number }; // Action associated with this equipment action: BaseAction; @@ -45,7 +45,7 @@ module TS.SpaceTac.Game { this.slot = slot; this.code = code; this.name = code; - this.requirements = []; + this.requirements = {}; this.permanent_effects = []; this.target_effects = []; } @@ -57,8 +57,8 @@ module TS.SpaceTac.Game { return false; } else { var able = true; - this.requirements.forEach((cap: Attribute) => { - if (ship.attributes.getValue(cap.code) < cap.current) { + iteritems(this.requirements, (attr, minvalue) => { + if (ship.getAttribute(attr) < minvalue) { able = false; } }); diff --git a/src/game/LootTemplate.spec.ts b/src/game/LootTemplate.spec.ts index c1a4865..96062bd 100644 --- a/src/game/LootTemplate.spec.ts +++ b/src/game/LootTemplate.spec.ts @@ -8,8 +8,8 @@ module TS.SpaceTac.Game.Specs { template.duration = new IntegerRange(1, 2); template.ap_usage = new Range(4, 12); template.min_level = new IntegerRange(5, 9); - template.addRequirement(AttributeCode.Cap_Energy, 2, 8); - template.addRequirement(AttributeCode.Cap_Human, 5); + template.addRequirement("skill_energy", 2, 8); + template.addRequirement("skill_human", 5); var equipment = template.generateFixed(0.0); @@ -21,9 +21,10 @@ module TS.SpaceTac.Game.Specs { expect(equipment.duration).toEqual(1); expect(equipment.ap_usage).toEqual(4); expect(equipment.min_level).toEqual(5); - expect(equipment.requirements.length).toBe(2); - expect(equipment.requirements[0]).toEqual(new Attribute(AttributeCode.Cap_Energy, 2)); - expect(equipment.requirements[1]).toEqual(new Attribute(AttributeCode.Cap_Human, 5)); + expect(equipment.requirements).toEqual({ + "skill_energy": 2, + "skill_human": 5 + }); equipment = template.generateFixed(1.0); @@ -35,9 +36,10 @@ module TS.SpaceTac.Game.Specs { expect(equipment.duration).toEqual(2); expect(equipment.ap_usage).toEqual(12); expect(equipment.min_level).toEqual(9); - expect(equipment.requirements.length).toBe(2); - expect(equipment.requirements[0]).toEqual(new Attribute(AttributeCode.Cap_Energy, 8)); - expect(equipment.requirements[1]).toEqual(new Attribute(AttributeCode.Cap_Human, 5)); + expect(equipment.requirements).toEqual({ + "skill_energy": 8, + "skill_human": 5 + }); equipment = template.generateFixed(0.5); @@ -49,9 +51,10 @@ module TS.SpaceTac.Game.Specs { expect(equipment.duration).toEqual(2); expect(equipment.ap_usage).toEqual(8); expect(equipment.min_level).toEqual(7); - expect(equipment.requirements.length).toBe(2); - expect(equipment.requirements[0]).toEqual(new Attribute(AttributeCode.Cap_Energy, 5)); - expect(equipment.requirements[1]).toEqual(new Attribute(AttributeCode.Cap_Human, 5)); + expect(equipment.requirements).toEqual({ + "skill_energy": 5, + "skill_human": 5 + }); }); it("restricts power range to stay in a level range", () => { diff --git a/src/game/LootTemplate.ts b/src/game/LootTemplate.ts index ea087e4..af77416 100644 --- a/src/game/LootTemplate.ts +++ b/src/game/LootTemplate.ts @@ -7,8 +7,8 @@ module TS.SpaceTac.Game { // Base name that will be given to generated equipment name: string; - // Capability requirement ranges (indexed by AttributeCode) - requirements: IntegerRange[]; + // Capability requirement ranges (indexed by attributes) + requirements: { [key: string]: IntegerRange }; // Distance to target distance: Range; @@ -35,7 +35,7 @@ module TS.SpaceTac.Game { constructor(slot: SlotType, name: string) { this.slot = slot; this.name = name; - this.requirements = []; + this.requirements = {}; this.distance = new Range(0, 0); this.blast = new Range(0, 0); this.duration = new IntegerRange(0, 0); @@ -46,7 +46,7 @@ module TS.SpaceTac.Game { } // Set a capability requirement - addRequirement(capability: AttributeCode, min: number, max: number = null): void { + addRequirement(capability: keyof ShipAttributes, min: number, max: number = null): void { this.requirements[capability] = new IntegerRange(min, max); } @@ -73,9 +73,9 @@ module TS.SpaceTac.Game { result.action = this.getActionForEquipment(result); - this.requirements.forEach((requirement: IntegerRange, index: AttributeCode) => { + iteritems(this.requirements, (key: string, requirement: IntegerRange) => { if (requirement) { - result.requirements.push(new Attribute(index, requirement.getProportional(power))); + result.requirements[key] = requirement.getProportional(power); } }); @@ -124,23 +124,23 @@ module TS.SpaceTac.Game { } } - // Convenience function to add a permanent attribute effect on equipment - addPermanentAttributeValueEffect(code: AttributeCode, min: number, max: number = null): void { - var template = new EffectTemplate(new AttributeValueEffect(code, 0)); + // Convenience function to add an attribute effect on equipment + addAttributeEffect(code: keyof ShipAttributes, min: number, max: number = null): void { + var template = new EffectTemplate(new AttributeEffect(code, 0)); template.addModifier("value", new IntegerRange(min, max)); this.permanent_effects.push(template); } - // Convenience function to add a permanent attribute max effect on equipment - addPermanentAttributeMaxEffect(code: AttributeCode, min: number, max: number = null): void { - var template = new EffectTemplate(new AttributeMaxEffect(code, 0)); + // Convenience function to add a permanent attribute limit effect on equipment + addAttributeLimitEffect(code: keyof ShipAttributes, min: number, max: number = null): void { + var template = new EffectTemplate(new AttributeLimitEffect(code, 0)); template.addModifier("value", new IntegerRange(min, max)); this.permanent_effects.push(template); } - // Convenience function to add an offset effect on attribute value - addAttributeAddEffect(code: AttributeCode, min: number, max: number | null = null): void { - let template = new EffectTemplate(new AttributeAddEffect(code, 0)); + // Convenience function to add a value effect on equipment + addValueEffectOnTarget(code: keyof ShipValues, min: number, max: number = null): void { + var template = new EffectTemplate(new ValueEffect(code, 0)); template.addModifier("value", new IntegerRange(min, max)); this.target_effects.push(template); } diff --git a/src/game/MoveFireSimulator.ts b/src/game/MoveFireSimulator.ts index d0fe3d9..2fa28fa 100644 --- a/src/game/MoveFireSimulator.ts +++ b/src/game/MoveFireSimulator.ts @@ -63,7 +63,7 @@ module TS.SpaceTac.Game { let dy = target.y - this.ship.arena_y; let distance = Math.sqrt(dx * dx + dy * dy); let result = new MoveFireResult(); - let ap = this.ship.ap_current.current; + let ap = this.ship.values.power.get(); if (distance > action.getRangeRadius(this.ship)) { result.need_move = true; diff --git a/src/game/Ship.spec.ts b/src/game/Ship.spec.ts index c595595..290e882 100644 --- a/src/game/Ship.spec.ts +++ b/src/game/Ship.spec.ts @@ -65,32 +65,32 @@ module TS.SpaceTac.Game.Specs { slot = ship.addSlot(SlotType.Power); equipment = new Equipment(); equipment.slot = slot.type; - equipment.permanent_effects.push(new AttributeMaxEffect(AttributeCode.Power, 4)); + equipment.permanent_effects.push(new AttributeEffect("power_capacity", 4)); slot.attach(equipment); slot = ship.addSlot(SlotType.Power); equipment = new Equipment(); equipment.slot = slot.type; - equipment.permanent_effects.push(new AttributeMaxEffect(AttributeCode.Power, 5)); + equipment.permanent_effects.push(new AttributeEffect("power_capacity", 5)); slot.attach(equipment); ship.updateAttributes(); - expect(ship.ap_current.maximal).toBe(9); + expect(ship.attributes.power_capacity.get()).toBe(9); }); it("repairs hull and recharges shield", function () { var ship = new Ship(null, "Test"); - ship.hull.setMaximal(120); - ship.shield.setMaximal(150); + ship.setAttribute("hull_capacity", 120); + ship.setAttribute("shield_capacity", 150); - expect(ship.hull.current).toEqual(0); - expect(ship.shield.current).toEqual(0); + expect(ship.values.hull.get()).toEqual(0); + expect(ship.values.shield.get()).toEqual(0); ship.restoreHealth(); - expect(ship.hull.current).toEqual(120); - expect(ship.shield.current).toEqual(150); + expect(ship.values.hull.get()).toEqual(120); + expect(ship.values.shield.get()).toEqual(150); }); it("applies and logs hull and shield damage", function () { @@ -98,24 +98,24 @@ module TS.SpaceTac.Game.Specs { var battle = new Battle(fleet); var ship = new Ship(fleet); - ship.hull.setMaximal(50); - ship.shield.setMaximal(100); + ship.setAttribute("hull_capacity", 50); + ship.setAttribute("shield_capacity", 100); ship.restoreHealth(); battle.log.clear(); ship.addDamage(10, 20); - expect(ship.hull.current).toEqual(40); - expect(ship.shield.current).toEqual(80); + expect(ship.values.hull.get()).toEqual(40); + expect(ship.values.shield.get()).toEqual(80); expect(battle.log.events.length).toBe(3); - expect(battle.log.events[0]).toEqual(new AttributeChangeEvent(ship, ship.shield)); - expect(battle.log.events[1]).toEqual(new AttributeChangeEvent(ship, ship.hull)); + expect(battle.log.events[0]).toEqual(new ValueChangeEvent(ship, ship.values.shield)); + expect(battle.log.events[1]).toEqual(new ValueChangeEvent(ship, ship.values.hull)); expect(battle.log.events[2]).toEqual(new DamageEvent(ship, 10, 20)); battle.log.clear(); ship.addDamage(15, 25, false); - expect(ship.hull.current).toEqual(25); - expect(ship.shield.current).toEqual(55); + expect(ship.values.hull.get()).toEqual(25); + expect(ship.values.shield.get()).toEqual(55); expect(battle.log.events.length).toBe(0); }); @@ -164,13 +164,13 @@ module TS.SpaceTac.Game.Specs { expect(ship.alive).toBe(true); - ship.hull.set(10); + ship.values.hull.set(10); battle.log.clear(); ship.addDamage(5, 0); expect(ship.alive).toBe(true); expect(battle.log.events.length).toBe(2); - expect(battle.log.events[0].code).toEqual("attr"); + expect(battle.log.events[0].code).toEqual("value"); expect(battle.log.events[1].code).toEqual("damage"); battle.log.clear(); @@ -178,7 +178,7 @@ module TS.SpaceTac.Game.Specs { expect(ship.alive).toBe(false); expect(battle.log.events.length).toBe(3); - expect(battle.log.events[0].code).toEqual("attr"); + expect(battle.log.events[0].code).toEqual("value"); expect(battle.log.events[1].code).toEqual("damage"); expect(battle.log.events[2].code).toEqual("death"); }); @@ -189,12 +189,12 @@ module TS.SpaceTac.Game.Specs { expect(ship.isAbleToPlay()).toBe(false); expect(ship.isAbleToPlay(false)).toBe(true); - ship.ap_current.set(5); + ship.values.power.set(5); expect(ship.isAbleToPlay()).toBe(true); expect(ship.isAbleToPlay(false)).toBe(true); - ship.hull.set(10); + ship.values.hull.set(10); ship.addDamage(8, 0); expect(ship.isAbleToPlay()).toBe(true); @@ -243,16 +243,17 @@ module TS.SpaceTac.Game.Specs { var power_core_template = new Equipments.BasicPowerCore(); ship.addSlot(SlotType.Power).attach(power_core_template.generateFixed(0)); + ship.updateAttributes(); - expect(ship.ap_current.current).toBe(0); + expect(ship.values.power.get()).toBe(0); ship.initializeActionPoints(); - expect(ship.ap_current.current).toBe(5); - ship.ap_current.set(2); - expect(ship.ap_current.current).toBe(2); + expect(ship.values.power.get()).toBe(5); + ship.values.power.set(2); + expect(ship.values.power.get()).toBe(2); ship.recoverActionPoints(); - expect(ship.ap_current.current).toBe(6); + expect(ship.values.power.get()).toBe(6); ship.recoverActionPoints(); - expect(ship.ap_current.current).toBe(8); + expect(ship.values.power.get()).toBe(8); }); it("checks if a ship is inside a given circle", function () { diff --git a/src/game/Ship.ts b/src/game/Ship.ts index c8216ce..224cd41 100644 --- a/src/game/Ship.ts +++ b/src/game/Ship.ts @@ -1,90 +1,104 @@ +/// +/// + module TS.SpaceTac.Game { - // A single ship in a Fleet + + /** + * Set of ShipAttribute for a ship + */ + export class ShipAttributes { + // Attribute controlling the play order + initiative = new ShipAttribute("initiative") + // Maximal hull value + hull_capacity = new ShipAttribute("hull capacity") + // Maximal shield value + shield_capacity = new ShipAttribute("shield capacity") + // Maximal power value + power_capacity = new ShipAttribute("power capacity") + // Initial power value at the start of a battle + power_initial = new ShipAttribute("initial power") + // Power value recovered each turn + power_recovery = new ShipAttribute("power recovery") + // Skills + skill_material = new ShipAttribute("material skill") + skill_energy = new ShipAttribute("energy skill") + skill_electronics = new ShipAttribute("electronics skill") + skill_human = new ShipAttribute("human skill") + skill_time = new ShipAttribute("time skill") + skill_gravity = new ShipAttribute("gravity skill") + } + + /** + * Set of ShipValue for a ship + */ + export class ShipValues { + hull = new ShipValue("hull") + shield = new ShipValue("shield") + power = new ShipValue("power") + } + + /** + * Static attributes and values object for name queries + */ + export const SHIP_ATTRIBUTES = new ShipAttributes(); + export const SHIP_VALUES = new ShipValues(); + + /** + * A single ship in a fleet + */ export class Ship { // Fleet this ship is a member of - fleet: Fleet; + fleet: Fleet // Level of this ship - level: number; + level: number // Name of the ship - name: string; + name: string // Code of the ShipModel used to create it - model: string; + model: string // Flag indicating if the ship is alive - alive: boolean; + alive: boolean // Position in the arena - arena_x: number; - arena_y: number; + arena_x: number + arena_y: number // Facing direction in the arena - arena_angle: number; - - // Initiative (high numbers will allow this ship to play sooner) - initiative: Attribute; - - // Current number of action points - ap_current: Attribute; - - // Initial number of action points, at the start of a battle - ap_initial: Attribute; - - // Number of action points recovered by turn - ap_recover: Attribute; - - // Number of hull points (once it reaches 0, the ship is dead) - hull: Attribute; - - // Number of shield points (a shield can absorb some damage to protect the hull) - shield: Attribute; + arena_angle: number // Sticky effects that applies a given number of times - sticky_effects: StickyEffect[]; - - // Capabilities level - cap_material: Attribute; - cap_energy: Attribute; - cap_electronics: Attribute; - cap_human: Attribute; - cap_time: Attribute; - cap_gravity: Attribute; + sticky_effects: StickyEffect[] // List of slots, able to contain equipment - slots: Slot[]; + slots: Slot[] - // Collection of available attributes - attributes: AttributeCollection; + // Ship attributes + attributes = new ShipAttributes() + + // Ship values + values = new ShipValues() // Boolean set to true if the ship is currently playing its turn - playing = false; + playing = false + + // Priority in play_order + play_priority = 0; // Create a new ship inside a fleet constructor(fleet: Fleet = null, name: string = null) { - this.attributes = new AttributeCollection(); this.fleet = fleet || new Fleet(); this.level = 1; this.name = name; this.model = "default"; this.alive = true; - this.initiative = this.newAttribute(AttributeCode.Initiative); - this.initiative.setMaximal(1); - this.ap_current = this.newAttribute(AttributeCode.Power); - this.ap_initial = this.newAttribute(AttributeCode.Power_Initial); - this.ap_recover = this.newAttribute(AttributeCode.Power_Recovery); - this.hull = this.newAttribute(AttributeCode.Hull); - this.shield = this.newAttribute(AttributeCode.Shield); - this.cap_material = this.newAttribute(AttributeCode.Cap_Material); - this.cap_energy = this.newAttribute(AttributeCode.Cap_Energy); - this.cap_electronics = this.newAttribute(AttributeCode.Cap_Electronics); - this.cap_human = this.newAttribute(AttributeCode.Cap_Human); - this.cap_time = this.newAttribute(AttributeCode.Cap_Time); - this.cap_gravity = this.newAttribute(AttributeCode.Cap_Gravity); this.sticky_effects = []; this.slots = []; + this.attributes.initiative.set(1); // TODO Should not be needed + this.arena_x = 0; this.arena_y = 0; this.arena_angle = 0; @@ -97,15 +111,10 @@ module TS.SpaceTac.Game { // Returns true if the ship is able to play // If *check_ap* is true, ap_current=0 will make this function return false isAbleToPlay(check_ap: boolean = true): boolean { - var ap_checked = !check_ap || this.ap_current.current > 0; + var ap_checked = !check_ap || this.values.power.get() > 0; return this.alive && ap_checked; } - // Create and register an attribute - newAttribute(code: AttributeCode): Attribute { - return this.attributes.getRawAttr(code); - } - // Set position in the arena // This does not consumes action points setArenaPosition(x: number, y: number) { @@ -125,7 +134,7 @@ module TS.SpaceTac.Game { // Make an initiative throw, to resolve play order in a battle throwInitiative(gen: RandomGenerator): void { - this.initiative.set(gen.throw(this.initiative.maximal)); + this.play_priority = gen.throw(this.attributes.initiative.get()); } // Return the player owning this ship @@ -171,28 +180,68 @@ module TS.SpaceTac.Game { } /** - * Set an attribute value + * Get a ship value + */ + getValue(name: keyof ShipValues): number { + return this.values[name].get(); + } + + /** + * Set a ship value * * If *offset* is true, the value will be added to current value. * If *log* is true, an attribute event will be added to the battle log * - * Returns true if the attribute changed. + * Returns true if the value changed. */ - setAttribute(attr: Attribute | AttributeCode, value: number, offset = false, log = true): boolean { - if (!(attr instanceof Attribute)) { - attr = this.attributes.getRawAttr(attr); - } - - var changed: boolean; + setValue(name: keyof ShipValues, value: number, offset = false, log = true): boolean { + let changed: boolean; + let val = this.values[name]; if (offset) { - changed = attr.add(value); + changed = val.add(value); } else { - changed = attr.set(value); + changed = val.set(value); } if (changed && log) { - this.addBattleEvent(new AttributeChangeEvent(this, attr)); + this.addBattleEvent(new ValueChangeEvent(this, val)); + } + + return changed; + } + + /** + * Get a ship attribute's current value + */ + getAttribute(name: keyof ShipAttributes): number { + return this.attributes[name].get(); + } + + /** + * Set a ship attribute + * + * If *log* is true, an attribute event will be added to the battle log + * + * Returns true if the value changed. + */ + setAttribute(name: keyof ShipAttributes, value: number, log = true): boolean { + let changed: boolean; + let attr = this.attributes[name]; + + changed = attr.set(value); + + // TODO more generic + if (name == "power_capacity") { + this.values.power.setMaximal(attr.get()); + } else if (name == "shield_capacity") { + this.values.shield.setMaximal(attr.get()); + } else if (name == "hull_capacity") { + this.values.hull.setMaximal(attr.get()); + } + + if (changed && log) { + this.addBattleEvent(new ValueChangeEvent(this, attr)); } return changed; @@ -203,9 +252,9 @@ module TS.SpaceTac.Game { // If no value is provided, the attribute ap_initial will be used initializeActionPoints(value: number = null): void { if (value === null) { - value = this.ap_initial.current; + value = this.attributes.power_initial.get(); } - this.setAttribute(this.ap_current, value); + this.setValue("power", value); } // Recover action points @@ -213,14 +262,14 @@ module TS.SpaceTac.Game { // If no value is provided, the current attribute ap_recovery will be used recoverActionPoints(value: number = null): void { if (value === null) { - value = this.ap_recover.current; + value = this.attributes.power_recovery.get(); } - this.setAttribute(this.ap_current, value, true); + this.setValue("power", value, true); } // Consumes action points useActionPoints(value: number): void { - this.setAttribute(this.ap_current, -value, true); + this.setValue("power", -value, true); } // Method called at the start of battle @@ -256,6 +305,7 @@ module TS.SpaceTac.Game { this.playing = false; // Recover action points for next turn + this.updateAttributes(); this.recoverActionPoints(); // Apply sticky effects @@ -317,14 +367,14 @@ module TS.SpaceTac.Game { // Apply damages to hull and/or shield addDamage(hull: number, shield: number, log: boolean = true): void { - this.setAttribute(this.shield, -shield, true, log); - this.setAttribute(this.hull, -hull, true, log); + this.setValue("shield", -shield, true, log); + this.setValue("hull", -hull, true, log); if (log) { this.addBattleEvent(new DamageEvent(this, hull, shield)); } - if (this.hull.current === 0) { + if (this.values.hull.get() === 0) { // Ship is dead this.setDead(log); } @@ -384,47 +434,49 @@ module TS.SpaceTac.Game { // Update attributes, taking into account attached equipment and active effects updateAttributes(): void { - // TODO Something more generic - - // Compute new maximal values for attributes - var new_attrs = new AttributeCollection(); - this.collectEffects("attrmax").forEach((effect: AttributeMaxEffect) => { - new_attrs.addValue(effect.attrcode, effect.value); + // Sum all attribute effects + var new_attrs = new ShipAttributes(); + this.collectEffects("attr").forEach((effect: AttributeEffect) => { + new_attrs[effect.attrcode].add(effect.value); }); - this.initiative.setMaximal(new_attrs.getValue(AttributeCode.Initiative)); - this.ap_current.setMaximal(new_attrs.getValue(AttributeCode.Power)); - this.hull.setMaximal(new_attrs.getValue(AttributeCode.Hull)); - this.shield.setMaximal(new_attrs.getValue(AttributeCode.Shield)); - // Compute new current values for attributes - new_attrs = new AttributeCollection(); - this.collectEffects("attr").forEach((effect: AttributeMaxEffect) => { - new_attrs.addValue(effect.attrcode, effect.value); + // Apply limit attributes + this.collectEffects("attrlimit").forEach((effect: AttributeLimitEffect) => { + new_attrs[effect.attrcode].setMaximal(effect.value); + }); + + // TODO better typing + iteritems(new_attrs, (key, value) => { + this.setAttribute(key, (value).get()); }); - this.ap_initial.set(new_attrs.getValue(AttributeCode.Power_Initial)); - this.ap_recover.set(new_attrs.getValue(AttributeCode.Power_Recovery)); } // Fully restore hull and shield restoreHealth(): void { - this.hull.set(this.hull.maximal); - this.shield.set(this.shield.maximal); + this.values.hull.set(this.attributes.hull_capacity.get()); + this.values.shield.set(this.attributes.shield_capacity.get()); } // Collect all effects to apply for updateAttributes - private collectEffects(code: string = null): BaseEffect[] { + private collectEffects(code: string): BaseEffect[] { var result: BaseEffect[] = []; - this.slots.forEach((slot: Slot) => { + this.slots.forEach(slot => { if (slot.attached) { - slot.attached.permanent_effects.forEach((effect: BaseEffect) => { - if (effect.code === code) { + slot.attached.permanent_effects.forEach(effect => { + if (effect.code == code) { result.push(effect); } }); } }); + this.sticky_effects.forEach(effect => { + if (effect.base.code == code) { + result.push(effect.base); + } + }); + return result; } } diff --git a/src/game/ShipAttribute.ts b/src/game/ShipAttribute.ts new file mode 100644 index 0000000..d8234dc --- /dev/null +++ b/src/game/ShipAttribute.ts @@ -0,0 +1,16 @@ +/// + +module TS.SpaceTac.Game { + /** + * A ship attribute is a value computed by a sum of contributions from equipments and sticky effects. + * + * A value may be limited by other effects. + */ + export class ShipAttribute extends ShipValue { + // Raw contributions value (without limits) + private raw = 0 + + // Temporary limits + private limits: number[] = [] + } +} diff --git a/src/game/ShipValue.spec.ts b/src/game/ShipValue.spec.ts new file mode 100644 index 0000000..c385d73 --- /dev/null +++ b/src/game/ShipValue.spec.ts @@ -0,0 +1,72 @@ +module TS.SpaceTac.Game { + describe("ShipValue", function () { + it("is initially not limited", function () { + var attr = new ShipValue("test"); + + attr.set(8888888); + expect(attr.get()).toBe(8888888); + }); + + it("applies minimal and maximal value", function () { + var attr = new ShipValue("test", 50, 100); + expect(attr.get()).toBe(50); + + attr.add(8); + expect(attr.get()).toBe(58); + + attr.add(60); + expect(attr.get()).toBe(100); + + attr.add(-72); + expect(attr.get()).toBe(28); + + attr.add(-60); + expect(attr.get()).toBe(0); + + attr.set(8); + expect(attr.get()).toBe(8); + + attr.set(-4); + expect(attr.get()).toBe(0); + + attr.set(105); + expect(attr.get()).toBe(100); + + attr.setMaximal(50); + expect(attr.get()).toBe(50); + + attr.setMaximal(80); + expect(attr.get()).toBe(50); + }); + + it("tells if value changed", function () { + var result: boolean; + var attr = new ShipValue("test", 50, 100); + expect(attr.get()).toBe(50); + + result = attr.set(51); + expect(result).toBe(true); + + result = attr.set(51); + expect(result).toBe(false); + + result = attr.add(1); + expect(result).toBe(true); + + result = attr.add(0); + expect(result).toBe(false); + + result = attr.add(1000); + expect(result).toBe(true); + + result = attr.add(2000); + expect(result).toBe(false); + + result = attr.set(-500); + expect(result).toBe(true); + + result = attr.add(-600); + expect(result).toBe(false); + }); + }); +} diff --git a/src/game/ShipValue.ts b/src/game/ShipValue.ts new file mode 100644 index 0000000..cc60fea --- /dev/null +++ b/src/game/ShipValue.ts @@ -0,0 +1,72 @@ +module TS.SpaceTac.Game { + /** + * A ship value is a number that may vary and be constrained in a given range. + */ + export class ShipValue { + // Name of the value + name: string + + // Current value + private current: number + + // Upper bound + private maximal: number | null + + constructor(code: string, current = 0, maximal: number | null = null) { + this.name = code; + this.current = current; + this.maximal = maximal; + } + + /** + * Get the current value + */ + get(): number { + return this.current; + } + + /** + * Set the upper bound the value must not cross + */ + setMaximal(value: number): void { + this.maximal = value; + this.fix(); + } + + /** + * Set an absolute value + * + * Returns true if the value changed + */ + set(value: number): boolean { + var old_value = this.current; + this.current = value; + this.fix(); + return this.current !== old_value; + } + + /** + * Add an offset to current value + * + * Returns true if the value changed + */ + add(value: number): boolean { + var old_value = this.current; + this.current += value; + this.fix(); + return this.current !== old_value; + } + + /** + * Fix the value to be positive and lower than maximal + */ + private fix(): void { + if (this.maximal !== null && this.current > this.maximal) { + this.current = this.maximal; + } + if (this.current < 0) { + this.current = 0; + } + } + } +} diff --git a/src/game/Slot.spec.ts b/src/game/Slot.spec.ts index b548bfa..3b6f4f0 100644 --- a/src/game/Slot.spec.ts +++ b/src/game/Slot.spec.ts @@ -23,13 +23,13 @@ module TS.SpaceTac.Game.Specs { var equipment = new Equipment(); equipment.slot = SlotType.Shield; - equipment.requirements.push(new Attribute(AttributeCode.Cap_Gravity, 5)); + equipment.requirements["skill_gravity"] = 5; expect(slot.attached).toBeNull(); slot.attach(equipment); expect(slot.attached).toBeNull(); - ship.cap_gravity.set(6); + ship.attributes.skill_gravity.set(6); slot.attach(equipment); expect(slot.attached).toBe(equipment); diff --git a/src/game/TestTools.ts b/src/game/TestTools.ts index 1a59a57..40459bf 100644 --- a/src/game/TestTools.ts +++ b/src/game/TestTools.ts @@ -46,23 +46,18 @@ module TS.SpaceTac.Game { static setShipAP(ship: Ship, points: number, recovery: number = 0): void { var equipment = this.getOrGenEquipment(ship, SlotType.Power, new Equipments.BasicPowerCore()); - equipment.permanent_effects.forEach((effect: BaseEffect) => { - if (effect.code === "attrmax") { - var meffect = effect; - if (meffect.attrcode === AttributeCode.Power) { - meffect.value = points; - } - } else if (effect.code === "attr") { - var veffect = effect; - if (veffect.attrcode === AttributeCode.Power_Recovery) { - veffect.value = recovery; + equipment.permanent_effects.forEach(effect => { + if (effect instanceof AttributeEffect) { + if (effect.attrcode === "power_capacity") { + effect.value = points; + } else if (effect.attrcode === "power_recovery") { + effect.value = recovery; } } }); - ship.ap_current.setMaximal(points); - ship.ap_current.set(points); - ship.ap_recover.set(recovery); + ship.updateAttributes(); + ship.setValue("power", points); } // Set a ship hull and shield points, adding/updating an equipment if needed @@ -70,19 +65,17 @@ module TS.SpaceTac.Game { var armor = TestTools.getOrGenEquipment(ship, SlotType.Armor, new Equipments.IronHull()); var shield = TestTools.getOrGenEquipment(ship, SlotType.Shield, new Equipments.BasicForceField()); - armor.permanent_effects.forEach((effect: BaseEffect) => { - if (effect.code === "attrmax") { - var meffect = effect; - if (meffect.attrcode === AttributeCode.Hull) { - meffect.value = hull_points; + armor.permanent_effects.forEach(effect => { + if (effect instanceof AttributeEffect) { + if (effect.attrcode === "hull_capacity") { + effect.value = hull_points; } } }); shield.permanent_effects.forEach((effect: BaseEffect) => { - if (effect.code === "attrmax") { - var meffect = effect; - if (meffect.attrcode === AttributeCode.Shield) { - meffect.value = shield_points; + if (effect instanceof AttributeEffect) { + if (effect.attrcode === "shield_capacity") { + effect.value = shield_points; } } }); diff --git a/src/game/actions/BaseAction.spec.ts b/src/game/actions/BaseAction.spec.ts index d6b9caa..34e3bf0 100644 --- a/src/game/actions/BaseAction.spec.ts +++ b/src/game/actions/BaseAction.spec.ts @@ -6,22 +6,22 @@ module TS.SpaceTac.Game { var action = new BaseAction("test", "Test", false, equipment); var ship = new Ship(); ship.addSlot(SlotType.Armor).attach(equipment); - ship.ap_current.setMaximal(10); + ship.values.power.setMaximal(10); expect(action.canBeUsed(null, ship)).toBe(false); - ship.ap_current.set(5); + ship.values.power.set(5); expect(action.canBeUsed(null, ship)).toBe(true); expect(action.canBeUsed(null, ship, 4)).toBe(true); expect(action.canBeUsed(null, ship, 3)).toBe(true); expect(action.canBeUsed(null, ship, 2)).toBe(false); - ship.ap_current.set(3); + ship.values.power.set(3); expect(action.canBeUsed(null, ship)).toBe(true); - ship.ap_current.set(2); + ship.values.power.set(2); expect(action.canBeUsed(null, ship)).toBe(false); }); diff --git a/src/game/actions/BaseAction.ts b/src/game/actions/BaseAction.ts index f2cef0b..6ec90bc 100644 --- a/src/game/actions/BaseAction.ts +++ b/src/game/actions/BaseAction.ts @@ -31,7 +31,7 @@ module TS.SpaceTac.Game { // Check AP usage if (remaining_ap === null) { - remaining_ap = ship.ap_current.current; + remaining_ap = ship.values.power.get(); } var ap_usage = this.equipment ? this.equipment.ap_usage : 0; return remaining_ap >= ap_usage; diff --git a/src/game/actions/DeployDroneAction.spec.ts b/src/game/actions/DeployDroneAction.spec.ts index 4fdf1c6..5db2e01 100644 --- a/src/game/actions/DeployDroneAction.spec.ts +++ b/src/game/actions/DeployDroneAction.spec.ts @@ -58,7 +58,7 @@ module TS.SpaceTac.Game { new DroneDeployedEvent(drone) ]); - expect(ship.ap_current.current).toEqual(1); + expect(ship.values.power.get()).toEqual(1); }); }); } diff --git a/src/game/actions/MoveAction.spec.ts b/src/game/actions/MoveAction.spec.ts index 7d6cf78..4f7ad63 100644 --- a/src/game/actions/MoveAction.spec.ts +++ b/src/game/actions/MoveAction.spec.ts @@ -4,8 +4,8 @@ module TS.SpaceTac.Game { var ship = new Ship(); var battle = new Battle(ship.fleet); battle.playing_ship = ship; - ship.ap_current.setMaximal(20); - ship.ap_current.set(6); + ship.values.power.setMaximal(20); + ship.values.power.set(6); ship.arena_x = 0; ship.arena_y = 0; var engine = new Equipment(); @@ -21,7 +21,7 @@ module TS.SpaceTac.Game { result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8)); expect(result).toEqual(Target.newFromLocation(0, 3)); - ship.ap_current.set(0); + ship.values.power.set(0); result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8)); expect(result).toBeNull(); }); @@ -41,8 +41,8 @@ module TS.SpaceTac.Game { it("applies to ship location, battle log and AP", function () { var ship = new Ship(); var battle = new Battle(ship.fleet); - ship.ap_current.setMaximal(20); - ship.ap_current.set(5); + ship.values.power.setMaximal(20); + ship.values.power.set(5); ship.arena_x = 0; ship.arena_y = 0; var engine = new Equipment(); @@ -55,13 +55,13 @@ module TS.SpaceTac.Game { expect(result).toBe(true); expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001); - expect(ship.ap_current.current).toEqual(0); + expect(ship.values.power.get()).toEqual(0); result = action.apply(battle, ship, Target.newFromLocation(10, 10)); expect(result).toBe(false); expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001); - expect(ship.ap_current.current).toEqual(0); + expect(ship.values.power.get()).toEqual(0); expect(battle.log.events.length).toBe(2); @@ -71,10 +71,10 @@ module TS.SpaceTac.Game { expect(battle.log.events[0].target.x).toBeCloseTo(3.535533, 0.00001); expect(battle.log.events[0].target.y).toBeCloseTo(3.535533, 0.00001); - expect(battle.log.events[1].code).toEqual("attr"); + expect(battle.log.events[1].code).toEqual("value"); expect(battle.log.events[1].ship).toBe(ship); - expect((battle.log.events[1]).attribute).toEqual( - new Attribute(AttributeCode.Power, 0, 20)); + expect((battle.log.events[1]).value).toEqual( + new ShipValue("power", 0, 20)); }); it("can't move too much near another ship", function () { diff --git a/src/game/actions/MoveAction.ts b/src/game/actions/MoveAction.ts index 0a80be8..843a33e 100644 --- a/src/game/actions/MoveAction.ts +++ b/src/game/actions/MoveAction.ts @@ -18,7 +18,7 @@ module TS.SpaceTac.Game { // Check AP usage if (remaining_ap === null) { - remaining_ap = ship.ap_current.current; + remaining_ap = ship.values.power.get(); } return remaining_ap > 0.0001; } @@ -33,7 +33,7 @@ module TS.SpaceTac.Game { } getRangeRadius(ship: Ship): number { - return ship.ap_current.current * this.equipment.distance / this.equipment.ap_usage; + return ship.values.power.get() * this.equipment.distance / this.equipment.ap_usage; } /** diff --git a/src/game/ai/BullyAI.spec.ts b/src/game/ai/BullyAI.spec.ts index 3f3c83b..dcde737 100644 --- a/src/game/ai/BullyAI.spec.ts +++ b/src/game/ai/BullyAI.spec.ts @@ -43,8 +43,8 @@ module TS.SpaceTac.Game.AI.Specs { engine.ap_usage = 3; engine.distance = 1; ship.addSlot(SlotType.Engine).attach(engine); - ship.ap_current.setMaximal(10); - ship.ap_current.set(8); + ship.values.power.setMaximal(10); + ship.values.power.set(8); var enemy = new Ship(); var ai = new BullyAI(ship.fleet); ai.ship = ship; @@ -54,7 +54,7 @@ module TS.SpaceTac.Game.AI.Specs { weapon.distance = 3; // enemy in range, the ship can fire without moving - ship.ap_current.set(8); + ship.values.power.set(8); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 3; @@ -65,7 +65,7 @@ module TS.SpaceTac.Game.AI.Specs { expect(result.fire.equipment).toBe(weapon); // enemy out of range, but moving can bring it in range - ship.ap_current.set(8); + ship.values.power.set(8); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 6; @@ -77,7 +77,7 @@ module TS.SpaceTac.Game.AI.Specs { // enemy out of range, but moving can bring it in range, except for the safety margin ai.move_margin = 0.1; - ship.ap_current.set(8); + ship.values.power.set(8); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 6; @@ -87,7 +87,7 @@ module TS.SpaceTac.Game.AI.Specs { ai.move_margin = 0; // enemy totally out of range - ship.ap_current.set(8); + ship.values.power.set(8); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 30; @@ -96,7 +96,7 @@ module TS.SpaceTac.Game.AI.Specs { expect(result).toBeNull(); // enemy in range but not enough AP to fire - ship.ap_current.set(1); + ship.values.power.set(1); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 3; @@ -105,7 +105,7 @@ module TS.SpaceTac.Game.AI.Specs { expect(result).toBeNull(); // can move in range of enemy, but not enough AP to fire - ship.ap_current.set(7); + ship.values.power.set(7); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 6; @@ -115,7 +115,7 @@ module TS.SpaceTac.Game.AI.Specs { // no engine, can't move ship.slots[0].attached.detach(); - ship.ap_current.set(8); + ship.values.power.set(8); ship.arena_x = 1; ship.arena_y = 0; enemy.arena_x = 6; @@ -152,8 +152,8 @@ module TS.SpaceTac.Game.AI.Specs { weapon2.ap_usage = 1; ai.ship.addSlot(SlotType.Weapon).attach(weapon2); - ai.ship.ap_current.setMaximal(10); - ai.ship.ap_current.set(8); + ai.ship.values.power.setMaximal(10); + ai.ship.values.power.set(8); result = ai.listAllManeuvers(); expect(result.length).toBe(3); @@ -219,11 +219,11 @@ module TS.SpaceTac.Game.AI.Specs { weapon.action = new FireWeaponAction(weapon); ai.ship.addSlot(SlotType.Weapon).attach(weapon); - ai.ship.ap_current.setMaximal(10); - ai.ship.ap_current.set(6); + ai.ship.values.power.setMaximal(10); + ai.ship.values.power.set(6); - ship2.hull.set(15); - ship2.shield.set(10); + ship2.values.hull.set(15); + ship2.values.shield.set(10); var move = ai.checkBullyManeuver(ship2, weapon); expect(move).not.toBeNull(); @@ -235,17 +235,17 @@ module TS.SpaceTac.Game.AI.Specs { expect(battle.log.events.length).toBe(7); expect(battle.log.events[0]).toEqual(new MoveEvent(ship1, 2, 0)); - expect(battle.log.events[1]).toEqual(new AttributeChangeEvent(ship1, - new Attribute(AttributeCode.Power, 2, 10))); + expect(battle.log.events[1]).toEqual(new ValueChangeEvent(ship1, + new ShipValue("power", 2, 10))); expect(battle.log.events[2]).toEqual(new FireEvent(ship1, weapon, Target.newFromShip(ship2))); - expect(battle.log.events[3]).toEqual(new AttributeChangeEvent(ship2, - new Attribute(AttributeCode.Shield, 0))); - expect(battle.log.events[4]).toEqual(new AttributeChangeEvent(ship2, - new Attribute(AttributeCode.Hull, 5))); + expect(battle.log.events[3]).toEqual(new ValueChangeEvent(ship2, + new ShipValue("shield", 0))); + expect(battle.log.events[4]).toEqual(new ValueChangeEvent(ship2, + new ShipValue("hull", 5))); expect(battle.log.events[5]).toEqual(new DamageEvent(ship2, 10, 10)); - expect(battle.log.events[6]).toEqual(new AttributeChangeEvent(ship1, - new Attribute(AttributeCode.Power, 1, 10))); + expect(battle.log.events[6]).toEqual(new ValueChangeEvent(ship1, + new ShipValue("power", 1, 10))); }); }); } diff --git a/src/game/ai/BullyAI.ts b/src/game/ai/BullyAI.ts index e8507db..f4f030f 100644 --- a/src/game/ai/BullyAI.ts +++ b/src/game/ai/BullyAI.ts @@ -105,7 +105,7 @@ module TS.SpaceTac.Game.AI { var distance = target.getDistanceTo(Target.newFromShip(this.ship)); var move: Target; var engine: Equipment; - var remaining_ap = this.ship.ap_current.current; + var remaining_ap = this.ship.values.power.get(); if (distance <= weapon.distance) { // No need to move move = null; diff --git a/src/game/effects/AttributeAddEffect.spec.ts b/src/game/effects/AttributeAddEffect.spec.ts deleted file mode 100644 index e8ef0a1..0000000 --- a/src/game/effects/AttributeAddEffect.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -module TS.SpaceTac.Game { - describe("AttributeAddEffect", function () { - it("adds an amount to an attribute value", function () { - let effect = new AttributeAddEffect(AttributeCode.Shield, 20); - - let ship = new Ship(); - ship.shield.maximal = 80; - ship.setAttribute(AttributeCode.Shield, 55); - expect(ship.shield.current).toEqual(55); - - effect.applyOnShip(ship); - expect(ship.shield.current).toEqual(75); - - effect.applyOnShip(ship); - expect(ship.shield.current).toEqual(80); - }); - }); -} diff --git a/src/game/effects/AttributeAddEffect.ts b/src/game/effects/AttributeAddEffect.ts deleted file mode 100644 index 3aff1e6..0000000 --- a/src/game/effects/AttributeAddEffect.ts +++ /dev/null @@ -1,35 +0,0 @@ -/// - -module TS.SpaceTac.Game { - /** - * Effect to add (or subtract if negative) an amount to an attribute value. - * - * The effect is "permanent", and will not be removed when the effect ends. - */ - export class AttributeAddEffect extends BaseEffect { - // Affected attribute - attrcode: AttributeCode; - - // Value to add (or subtract if negative) - value: number; - - constructor(attrcode: AttributeCode, value: number) { - super("attradd"); - - this.attrcode = attrcode; - this.value = value; - } - - applyOnShip(ship: Ship): boolean { - return ship.setAttribute(this.attrcode, this.value, true); - } - - isBeneficial(): boolean { - return this.value >= 0; - } - - getFullCode(): string { - return this.code + "-" + AttributeCode[this.attrcode].toLowerCase().replace("_", ""); - } - } -} diff --git a/src/game/effects/AttributeEffect.spec.ts b/src/game/effects/AttributeEffect.spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/game/effects/AttributeEffect.ts b/src/game/effects/AttributeEffect.ts new file mode 100644 index 0000000..4dc2f62 --- /dev/null +++ b/src/game/effects/AttributeEffect.ts @@ -0,0 +1,36 @@ +/// + +module TS.SpaceTac.Game { + /** + * Effect to modify an attribute. + * + * Attribute effects are stacking, and the value of an attribute is in fact the sum of all active attribute effects. + */ + export class AttributeEffect extends BaseEffect { + // Affected attribute + attrcode: keyof ShipAttributes; + + // Base value + value: number; + + constructor(attrcode: keyof ShipAttributes, value: number) { + super("attr"); + + this.attrcode = attrcode; + this.value = value; + } + + applyOnShip(ship: Ship): boolean { + ship.updateAttributes(); + return true; + } + + isBeneficial(): boolean { + return this.value >= 0; + } + + getFullCode(): string { + return this.code + "-" + this.attrcode; + } + } +} diff --git a/src/game/effects/AttributeLimitEffect.ts b/src/game/effects/AttributeLimitEffect.ts index 69c9644..1da7f49 100644 --- a/src/game/effects/AttributeLimitEffect.ts +++ b/src/game/effects/AttributeLimitEffect.ts @@ -1,16 +1,19 @@ /// module TS.SpaceTac.Game { - // Hard limitation on attribute value - // For example, this could be used to slow a target by limiting its action points + /** + * Enforce a limitation on ship attribute final value + * + * For example, this could be used to slow a target by limiting its action points + */ export class AttributeLimitEffect extends BaseEffect { // Affected attribute - attrcode: AttributeCode; + attrcode: keyof ShipAttributes; // Limit of the attribute value value: number; - constructor(attrcode: AttributeCode, value: number = 0) { + constructor(attrcode: keyof ShipAttributes, value = 0) { super("attrlimit"); this.attrcode = attrcode; @@ -18,19 +21,17 @@ module TS.SpaceTac.Game { } applyOnShip(ship: Ship): boolean { - var current = ship.attributes.getValue(this.attrcode); - if (current > this.value) { - ship.setAttribute(ship.attributes.getRawAttr(this.attrcode), this.value); - } + ship.updateAttributes(); return true; } getFullCode(): string { - return this.code + "-" + AttributeCode[this.attrcode].toLowerCase().replace("_", ""); + return this.code + "-" + this.attrcode; } getDescription(): string { - return `limit ${ATTRIBUTE_NAMES[this.attrcode]} to ${this.value}`; + let attrname = SHIP_ATTRIBUTES[this.attrcode].name; + return `limit ${attrname} to ${this.value}`; } } } diff --git a/src/game/effects/AttributeMaxEffect.ts b/src/game/effects/AttributeMaxEffect.ts deleted file mode 100644 index a40f19b..0000000 --- a/src/game/effects/AttributeMaxEffect.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// - -module TS.SpaceTac.Game { - // Effect on attribute maximum - // Typically, these effects are summed up to define an attribute maximum - export class AttributeMaxEffect extends BaseEffect { - // Affected attribute - attrcode: AttributeCode; - - // Value to add to the maximum - value: number; - - constructor(attrcode: AttributeCode, value: number) { - super("attrmax"); - - this.attrcode = attrcode; - this.value = value; - } - - getFullCode(): string { - return this.code + "-" + AttributeCode[this.attrcode].toLowerCase().replace("_", ""); - } - } -} diff --git a/src/game/effects/AttributeValueEffect.ts b/src/game/effects/AttributeValueEffect.ts deleted file mode 100644 index e407d27..0000000 --- a/src/game/effects/AttributeValueEffect.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// - -module TS.SpaceTac.Game { - // Effect on attribute value - // Typically, these effects are summed up to define an attribute value - export class AttributeValueEffect extends BaseEffect { - // Affected attribute - attrcode: AttributeCode; - - // Value to contribute - value: number; - - constructor(attrcode: AttributeCode, value: number) { - super("attr"); - - this.attrcode = attrcode; - this.value = value; - } - - getFullCode(): string { - return this.code + "-" + AttributeCode[this.attrcode].toLowerCase().replace("_", ""); - } - } -} diff --git a/src/game/effects/DamageEffect.ts b/src/game/effects/DamageEffect.ts index cdae0e4..0ccce93 100644 --- a/src/game/effects/DamageEffect.ts +++ b/src/game/effects/DamageEffect.ts @@ -1,7 +1,11 @@ /// module TS.SpaceTac.Game { - // Apply damage to a ship + /** + * Apply damage on a ship. + * + * Damage is applied on shield while there is some, then on the hull. + */ export class DamageEffect extends BaseEffect { // Base damage points value: number; @@ -18,16 +22,16 @@ module TS.SpaceTac.Game { var shield: number; // Apply on shields - if (damage >= ship.shield.current) { - shield = ship.shield.current; + if (damage >= ship.values.shield.get()) { + shield = ship.values.shield.get(); } else { shield = damage; } damage -= shield; // Apply on hull - if (damage >= ship.hull.current) { - hull = ship.hull.current; + if (damage >= ship.values.hull.get()) { + hull = ship.values.hull.get(); } else { hull = damage; } diff --git a/src/game/effects/ValueEffect.spec.ts b/src/game/effects/ValueEffect.spec.ts new file mode 100644 index 0000000..f971327 --- /dev/null +++ b/src/game/effects/ValueEffect.spec.ts @@ -0,0 +1,18 @@ +module TS.SpaceTac.Game { + describe("ValueEffect", function () { + it("adds an amount to a ship value", function () { + let effect = new ValueEffect("shield", 20); + + let ship = new Ship(); + ship.values.shield.setMaximal(80); + ship.setValue("shield", 55); + expect(ship.values.shield.get()).toEqual(55); + + effect.applyOnShip(ship); + expect(ship.values.shield.get()).toEqual(75); + + effect.applyOnShip(ship); + expect(ship.values.shield.get()).toEqual(80); + }); + }); +} diff --git a/src/game/effects/ValueEffect.ts b/src/game/effects/ValueEffect.ts new file mode 100644 index 0000000..9a28183 --- /dev/null +++ b/src/game/effects/ValueEffect.ts @@ -0,0 +1,35 @@ +/// + +module TS.SpaceTac.Game { + /** + * Effect to add (or subtract if negative) an amount to a ship value. + * + * The effect is immediate and permanent. + */ + export class ValueEffect extends BaseEffect { + // Affected value + valuetype: keyof ShipValues; + + // Value to add (or subtract if negative) + value: number; + + constructor(valuetype: keyof ShipValues, value: number) { + super("value"); + + this.valuetype = valuetype; + this.value = value; + } + + applyOnShip(ship: Ship): boolean { + return ship.setValue(this.valuetype, this.value, true); + } + + isBeneficial(): boolean { + return this.value >= 0; + } + + getFullCode(): string { + return `${this.code}-${this.valuetype}`; + } + } +} diff --git a/src/game/equipments/AbstractWeapon.spec.ts b/src/game/equipments/AbstractWeapon.spec.ts index 5f32bd1..94a53c5 100644 --- a/src/game/equipments/AbstractWeapon.spec.ts +++ b/src/game/equipments/AbstractWeapon.spec.ts @@ -32,7 +32,7 @@ module TS.SpaceTac.Game.Specs { it("can't fire without sufficient AP", function () { var ship = new Ship(); - ship.ap_current.set(3); + ship.values.power.set(3); var weapon = new Equipments.AbstractWeapon("Super Fire Weapon", 50); @@ -104,15 +104,15 @@ module TS.SpaceTac.Game.Specs { var fleet2 = new Fleet(new Player()); var ship1 = new Ship(fleet1); - ship1.ap_current.set(50); + ship1.values.power.set(50); var ship2 = new Ship(fleet2); - ship2.hull.setMaximal(100); - ship2.shield.setMaximal(30); + ship2.setAttribute("hull_capacity", 100); + ship2.setAttribute("shield_capacity", 30); ship2.restoreHealth(); - expect(ship2.hull.current).toEqual(100); - expect(ship2.shield.current).toEqual(30); + expect(ship2.values.hull.get()).toEqual(100); + expect(ship2.values.shield.get()).toEqual(30); var weapon = new Equipments.AbstractWeapon("Super Fire Weapon", 20); weapon.ap_usage = new IntegerRange(1, 1); @@ -120,19 +120,19 @@ module TS.SpaceTac.Game.Specs { var equipment = weapon.generateFixed(0); equipment.action.apply(null, ship1, Target.newFromShip(ship2)); - expect(ship2.hull.current).toEqual(100); - expect(ship2.shield.current).toEqual(10); - expect(ship1.ap_current.current).toEqual(49); + expect(ship2.values.hull.get()).toEqual(100); + expect(ship2.values.shield.get()).toEqual(10); + expect(ship1.values.power.get()).toEqual(49); equipment.action.apply(null, ship1, Target.newFromShip(ship2)); - expect(ship2.hull.current).toEqual(90); - expect(ship2.shield.current).toEqual(0); - expect(ship1.ap_current.current).toEqual(48); + expect(ship2.values.hull.get()).toEqual(90); + expect(ship2.values.shield.get()).toEqual(0); + expect(ship1.values.power.get()).toEqual(48); equipment.action.apply(null, ship1, Target.newFromShip(ship2)); - expect(ship2.hull.current).toEqual(70); - expect(ship2.shield.current).toEqual(0); - expect(ship1.ap_current.current).toEqual(47); + expect(ship2.values.hull.get()).toEqual(70); + expect(ship2.values.shield.get()).toEqual(0); + expect(ship1.values.power.get()).toEqual(47); }); }); } diff --git a/src/game/equipments/BasicForceField.ts b/src/game/equipments/BasicForceField.ts index 6bec63e..50ca467 100644 --- a/src/game/equipments/BasicForceField.ts +++ b/src/game/equipments/BasicForceField.ts @@ -7,7 +7,7 @@ module TS.SpaceTac.Game.Equipments { this.min_level = new IntegerRange(1, 3); - this.addPermanentAttributeMaxEffect(AttributeCode.Shield, 100, 200); + this.addAttributeEffect("shield_capacity", 100, 200); } } } diff --git a/src/game/equipments/BasicPowerCore.ts b/src/game/equipments/BasicPowerCore.ts index 2dce0a7..10616d4 100644 --- a/src/game/equipments/BasicPowerCore.ts +++ b/src/game/equipments/BasicPowerCore.ts @@ -7,10 +7,10 @@ module TS.SpaceTac.Game.Equipments { this.min_level = new IntegerRange(1, 1); - this.addPermanentAttributeMaxEffect(AttributeCode.Initiative, 1); - this.addPermanentAttributeMaxEffect(AttributeCode.Power, 8); - this.addPermanentAttributeValueEffect(AttributeCode.Power_Initial, 5); - this.addPermanentAttributeValueEffect(AttributeCode.Power_Recovery, 4); + this.addAttributeEffect("initiative", 1); + this.addAttributeEffect("power_capacity", 8); + this.addAttributeEffect("power_initial", 5); + this.addAttributeEffect("power_recovery", 4); } } } diff --git a/src/game/equipments/ConventionalEngine.ts b/src/game/equipments/ConventionalEngine.ts index 706e955..91ad3ce 100644 --- a/src/game/equipments/ConventionalEngine.ts +++ b/src/game/equipments/ConventionalEngine.ts @@ -10,7 +10,7 @@ module TS.SpaceTac.Game.Equipments { this.distance = new Range(100, 100); this.ap_usage = new IntegerRange(1); - this.addPermanentAttributeMaxEffect(AttributeCode.Initiative, 1); + this.addAttributeEffect("initiative", 1); } protected getActionForEquipment(equipment: Equipment): BaseAction { diff --git a/src/game/equipments/IronHull.ts b/src/game/equipments/IronHull.ts index abc806a..576c64a 100644 --- a/src/game/equipments/IronHull.ts +++ b/src/game/equipments/IronHull.ts @@ -7,7 +7,7 @@ module TS.SpaceTac.Game.Equipments { this.min_level = new IntegerRange(1, 3); - this.addPermanentAttributeMaxEffect(AttributeCode.Hull, 100, 200); + this.addAttributeEffect("hull_capacity", 100, 200); } } } diff --git a/src/game/equipments/PowerDepleter.spec.ts b/src/game/equipments/PowerDepleter.spec.ts index 119062b..33e30ce 100644 --- a/src/game/equipments/PowerDepleter.spec.ts +++ b/src/game/equipments/PowerDepleter.spec.ts @@ -9,29 +9,29 @@ module TS.SpaceTac.Game.Specs { TestTools.setShipAP(target, 7, 2); spyOn(equipment.action, "canBeUsed").and.returnValue(true); - expect(target.ap_current.current).toBe(7); + expect(target.values.power.get()).toBe(7); expect(target.sticky_effects).toEqual([]); // Attribute is immediately limited equipment.action.apply(null, ship, Target.newFromShip(target)); - expect(target.ap_current.current).toBe(4); + expect(target.values.power.get()).toBe(4); expect(target.sticky_effects).toEqual([ - new StickyEffect(new AttributeLimitEffect(AttributeCode.Power, 4), 1, true, false) + new StickyEffect(new AttributeLimitEffect("power_capacity", 4), 1, true, false) ]); // Attribute is limited for one turn, and prevents AP recovery - target.ap_current.set(6); + target.values.power.set(6); target.recoverActionPoints(); target.startTurn(); - expect(target.ap_current.current).toBe(4); + expect(target.values.power.get()).toBe(4); expect(target.sticky_effects).toEqual([]); // Effect vanished, so AP recovery happens target.endTurn(); - expect(target.ap_current.current).toBe(6); + expect(target.values.power.get()).toBe(6); expect(target.sticky_effects).toEqual([]); }); }); diff --git a/src/game/equipments/PowerDepleter.ts b/src/game/equipments/PowerDepleter.ts index f07e5f1..299f920 100644 --- a/src/game/equipments/PowerDepleter.ts +++ b/src/game/equipments/PowerDepleter.ts @@ -10,7 +10,7 @@ module TS.SpaceTac.Game.Equipments { this.ap_usage = new IntegerRange(4, 5); this.min_level = new IntegerRange(1, 3); - this.addSticky(new AttributeLimitEffect(AttributeCode.Power), 4, 3, 1, 2, true); + this.addSticky(new AttributeLimitEffect("power_capacity"), 4, 3, 1, 2, true); } } } diff --git a/src/game/equipments/RepairDrone.spec.ts b/src/game/equipments/RepairDrone.spec.ts index 0616e52..e12f1b8 100644 --- a/src/game/equipments/RepairDrone.spec.ts +++ b/src/game/equipments/RepairDrone.spec.ts @@ -4,7 +4,7 @@ module TS.SpaceTac.Game.Equipments { let template = new RepairDrone(); let equipment = template.generateFixed(0); - expect(equipment.target_effects).toEqual([new AttributeAddEffect(AttributeCode.Hull, 10)]); + expect(equipment.target_effects).toEqual([new ValueEffect("hull", 10)]); }); }); } diff --git a/src/game/equipments/RepairDrone.ts b/src/game/equipments/RepairDrone.ts index e0c8766..186ecdb 100644 --- a/src/game/equipments/RepairDrone.ts +++ b/src/game/equipments/RepairDrone.ts @@ -14,7 +14,7 @@ module TS.SpaceTac.Game.Equipments { this.setEffectRadius(40, 80); this.setPowerConsumption(4, 5); - this.addAttributeAddEffect(AttributeCode.Hull, 10, 20); + this.addValueEffectOnTarget("hull", 10, 20); } } } \ No newline at end of file diff --git a/src/game/equipments/SubMunitionMissile.spec.ts b/src/game/equipments/SubMunitionMissile.spec.ts index ce995a9..249ac21 100644 --- a/src/game/equipments/SubMunitionMissile.spec.ts +++ b/src/game/equipments/SubMunitionMissile.spec.ts @@ -21,17 +21,17 @@ module TS.SpaceTac.Game.Specs { (equipment.target_effects[0]).value = 20; var checkHP = (h1: number, s1: number, h2: number, s2: number, h3: number, s3: number): void => { - expect(ship.hull.current).toBe(h1); - expect(ship.shield.current).toBe(s1); - expect(enemy1.hull.current).toBe(h2); - expect(enemy1.shield.current).toBe(s2); - expect(enemy2.hull.current).toBe(h3); - expect(enemy2.shield.current).toBe(s3); + expect(ship.values.hull.get()).toBe(h1); + expect(ship.values.shield.get()).toBe(s1); + expect(enemy1.values.hull.get()).toBe(h2); + expect(enemy1.values.shield.get()).toBe(s2); + expect(enemy2.values.hull.get()).toBe(h3); + expect(enemy2.values.shield.get()).toBe(s3); }; checkHP(50, 30, 50, 30, 50, 30); battle.log.clear(); - battle.log.addFilter("attr"); + battle.log.addFilter("value"); // Fire at a ship var t = Target.newFromShip(enemy1); diff --git a/src/game/events/AttributeChangeEvent.ts b/src/game/events/AttributeChangeEvent.ts deleted file mode 100644 index 3315e73..0000000 --- a/src/game/events/AttributeChangeEvent.ts +++ /dev/null @@ -1,15 +0,0 @@ -/// - -module TS.SpaceTac.Game { - // Event logged when a ship moves - export class AttributeChangeEvent extends BaseLogEvent { - // Saved version of the attribute - attribute: Attribute; - - constructor(ship: Ship, attribute: Attribute) { - super("attr", ship); - - this.attribute = copy(attribute); - } - } -} diff --git a/src/game/events/ValueChangeEvent.ts b/src/game/events/ValueChangeEvent.ts new file mode 100644 index 0000000..00954eb --- /dev/null +++ b/src/game/events/ValueChangeEvent.ts @@ -0,0 +1,15 @@ +/// + +module TS.SpaceTac.Game { + // Event logged when a ship value or attribute changed + export class ValueChangeEvent extends BaseLogEvent { + // Saved version of the value + value: ShipValue; + + constructor(ship: Ship, value: ShipValue) { + super("value", ship); + + this.value = copy(value); + } + } +} diff --git a/src/view/battle/ActionBar.spec.ts b/src/view/battle/ActionBar.spec.ts index b172fe9..b009bc0 100644 --- a/src/view/battle/ActionBar.spec.ts +++ b/src/view/battle/ActionBar.spec.ts @@ -47,8 +47,8 @@ module TS.SpaceTac.View.Specs { battleview.battle.playing_ship = ship; battleview.player = ship.getPlayer(); - ship.ap_current.setMaximal(10); - ship.ap_current.set(9); + ship.setAttribute("power_capacity", 10); + ship.setValue("power", 9); bar.setShip(ship); expect(bar.actions.length).toBe(4); @@ -75,19 +75,19 @@ module TS.SpaceTac.View.Specs { bar.actionEnded(); // Not enough AP for both weapons - ship.ap_current.set(7); + ship.setValue("power", 7); bar.actions[2].processClick(); checkFading([1, 2], [0, 3]); bar.actionEnded(); // Not enough AP to move - ship.ap_current.set(3); + ship.setValue("power", 3); bar.actions[1].processClick(); checkFading([0, 1, 2], [3]); bar.actionEnded(); // Dynamic AP usage for move actions - ship.ap_current.set(6); + ship.setValue("power", 6); bar.actions[0].processClick(); checkFading([], [0, 1, 2, 3]); bar.actions[0].processHover(Game.Target.newFromLocation(2, 8)); diff --git a/src/view/battle/ActionBar.ts b/src/view/battle/ActionBar.ts index 995387f..974ce62 100644 --- a/src/view/battle/ActionBar.ts +++ b/src/view/battle/ActionBar.ts @@ -65,8 +65,8 @@ module TS.SpaceTac.View { // Update the action points indicator updateActionPoints(): void { if (this.ship) { - this.actionpoints.setValue(this.ship.ap_current.current, this.ship.ap_current.maximal); - this.actionpointstemp.setValue(this.ship.ap_current.current, this.ship.ap_current.maximal); + this.actionpoints.setValue(this.ship.values.power.get(), this.ship.attributes.power_capacity.get()); + this.actionpointstemp.setValue(this.ship.values.power.get(), this.ship.attributes.power_capacity.get()); this.actionpoints.visible = true; this.actionpointstemp.visible = true; } else { @@ -78,7 +78,7 @@ module TS.SpaceTac.View { // Update fading flags // ap_usage is the consumption of currently selected action updateFadings(ap_usage: number): void { - var remaining_ap = this.ship.ap_current.current - ap_usage; + var remaining_ap = this.ship.values.power.get() - ap_usage; if (remaining_ap < 0) { remaining_ap = 0; } @@ -86,7 +86,7 @@ module TS.SpaceTac.View { this.actions.forEach((icon: ActionIcon) => { icon.updateFadingStatus(remaining_ap); }); - this.actionpointstemp.setValue(remaining_ap, this.ship.ap_current.maximal); + this.actionpointstemp.setValue(remaining_ap, this.ship.attributes.power_capacity.get()); } // Set action icons from selected ship diff --git a/src/view/battle/ActionIcon.ts b/src/view/battle/ActionIcon.ts index 1b65e03..454f8fc 100644 --- a/src/view/battle/ActionIcon.ts +++ b/src/view/battle/ActionIcon.ts @@ -143,7 +143,7 @@ module TS.SpaceTac.View { } this.setSelected(false); this.updateActiveStatus(); - this.updateFadingStatus(this.ship.ap_current.current); + this.updateFadingStatus(this.ship.values.power.get()); this.battleview.arena.range_hint.clearPrimary(); } diff --git a/src/view/battle/LogProcessor.ts b/src/view/battle/LogProcessor.ts index 1df6a6c..48865bb 100644 --- a/src/view/battle/LogProcessor.ts +++ b/src/view/battle/LogProcessor.ts @@ -40,8 +40,8 @@ module TS.SpaceTac.View { case "move": this.processMoveEvent(event); break; - case "attr": - this.processAttributeChangedEvent(event); + case "value": + this.processValueChangedEvent(event); break; case "death": this.processDeathEvent(event); @@ -97,12 +97,13 @@ module TS.SpaceTac.View { } } - // Ship attribute changed - private processAttributeChangedEvent(event: Game.AttributeChangeEvent): void { + // Ship value changed + private processValueChangedEvent(event: Game.ValueChangeEvent): void { var item = this.view.ship_list.findItem(event.ship); if (item) { - item.attributeChanged(event.attribute); + item.updateAttributes(); } + // TODO Update tooltip } // A ship died diff --git a/src/view/battle/ShipListItem.ts b/src/view/battle/ShipListItem.ts index cb67907..a9f1eb6 100644 --- a/src/view/battle/ShipListItem.ts +++ b/src/view/battle/ShipListItem.ts @@ -4,15 +4,15 @@ module TS.SpaceTac.View { // Reference to the ship game object ship: Game.Ship; - // Energy display - energy: ValueBar; - // Hull display hull: ValueBar; // Shield display shield: ValueBar; + // Power display + power: ValueBar; + // Portrait layer_portrait: Phaser.Image; @@ -52,8 +52,8 @@ module TS.SpaceTac.View { this.shield = ValueBar.newStyled(this.game, "battle-shiplist-shield", 98, 39, true); this.addChild(this.shield); - this.energy = ValueBar.newStyled(this.game, "battle-shiplist-energy", 106, 39, true); - this.addChild(this.energy); + this.power = ValueBar.newStyled(this.game, "battle-shiplist-energy", 106, 39, true); + this.addChild(this.power); this.updateAttributes(); this.updateEffects(); @@ -63,9 +63,9 @@ module TS.SpaceTac.View { // Update attributes from associated ship updateAttributes() { - this.attributeChanged(this.ship.hull); - this.attributeChanged(this.ship.shield); - this.attributeChanged(this.ship.ap_current); + this.hull.setValue(this.ship.values.hull.get(), this.ship.attributes.hull_capacity.get()); + this.shield.setValue(this.ship.values.shield.get(), this.ship.attributes.shield_capacity.get()); + this.power.setValue(this.ship.values.power.get(), this.ship.attributes.power_capacity.get()); } // Update effects applied on the ship @@ -81,17 +81,6 @@ module TS.SpaceTac.View { }); } - // Called when an attribute for this ship changed through the battle log - attributeChanged(attribute: Game.Attribute): void { - if (attribute.code === Game.AttributeCode.Hull) { - this.hull.setValue(attribute.current, attribute.maximal); - } else if (attribute.code === Game.AttributeCode.Shield) { - this.shield.setValue(attribute.current, attribute.maximal); - } else if (attribute.code === Game.AttributeCode.Power) { - this.energy.setValue(attribute.current, attribute.maximal); - } - } - // Flash a damage indicator setDamageHit() { this.game.tweens.create(this.layer_damage).to({ alpha: 1 }, 100).to({ alpha: 0 }, 150).repeatAll(2).start(); diff --git a/src/view/battle/ShipTooltip.ts b/src/view/battle/ShipTooltip.ts index 9278d21..4538022 100644 --- a/src/view/battle/ShipTooltip.ts +++ b/src/view/battle/ShipTooltip.ts @@ -92,15 +92,15 @@ module TS.SpaceTac.View { // Fill info this.title.setText(ship.name); - this.attr_hull.setText(ship.hull.current.toString()); - this.attr_shield.setText(ship.shield.current.toString()); - this.attr_power.setText(ship.ap_current.current.toString()); - this.attr_materials.setText(ship.cap_material.current.toString()); - this.attr_electronics.setText(ship.cap_electronics.current.toString()); - this.attr_energy.setText(ship.cap_energy.current.toString()); - this.attr_human.setText(ship.cap_human.current.toString()); - this.attr_gravity.setText(ship.cap_gravity.current.toString()); - this.attr_time.setText(ship.cap_time.current.toString()); + this.attr_hull.setText(ship.values.hull.get().toString()); + this.attr_shield.setText(ship.values.shield.get().toString()); + this.attr_power.setText(ship.values.power.get().toString()); + this.attr_materials.setText(ship.attributes.skill_material.get().toString()); + this.attr_electronics.setText(ship.attributes.skill_electronics.get().toString()); + this.attr_energy.setText(ship.attributes.skill_energy.get().toString()); + this.attr_human.setText(ship.attributes.skill_human.get().toString()); + this.attr_gravity.setText(ship.attributes.skill_gravity.get().toString()); + this.attr_time.setText(ship.attributes.skill_time.get().toString()); this.active_effects.removeAll(true); ship.sticky_effects.forEach((effect, index) => { this.addEffect(effect, index); @@ -123,7 +123,7 @@ module TS.SpaceTac.View { this.active_effects.addChild(effect_group); if (effect.base instanceof Game.AttributeLimitEffect) { - let attr_name = Game.AttributeCode[effect.base.attrcode].toLowerCase().replace('_', ''); + let attr_name = effect.base.attrcode.replace('_', ''); let attr_icon = new Phaser.Image(this.game, 30, effect_group.height / 2, `battle-attributes-${attr_name}`); attr_icon.anchor.set(0.5, 0.5); attr_icon.scale.set(0.17, 0.17);