From be434b4eb4021fd0d640982eedd712818ff240ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 19 Jan 2015 01:00:00 +0100 Subject: [PATCH] New attribute system for action points --- src/scripts/game/Attribute.ts | 81 +++++++++++++++++ src/scripts/game/Battle.ts | 3 + src/scripts/game/Fleet.ts | 9 ++ src/scripts/game/Ship.ts | 89 ++++++++++++++++--- src/scripts/game/actions/MoveAction.ts | 2 +- src/scripts/game/equipments/BasicPowerCore.ts | 13 +++ .../game/events/AttributeChangeEvent.ts | 12 +++ src/scripts/game/specs/Attribute.spec.ts | 63 +++++++++++++ src/scripts/game/specs/MoveAction.spec.ts | 12 +-- src/scripts/game/specs/Ship.spec.ts | 10 ++- 10 files changed, 270 insertions(+), 24 deletions(-) create mode 100644 src/scripts/game/Attribute.ts create mode 100644 src/scripts/game/equipments/BasicPowerCore.ts create mode 100644 src/scripts/game/events/AttributeChangeEvent.ts create mode 100644 src/scripts/game/specs/Attribute.spec.ts diff --git a/src/scripts/game/Attribute.ts b/src/scripts/game/Attribute.ts new file mode 100644 index 0000000..11af208 --- /dev/null +++ b/src/scripts/game/Attribute.ts @@ -0,0 +1,81 @@ +module SpaceTac.Game { + "use strict"; + + // Code to identify + export enum AttributeCode { + // Initiative level + Initiative, + + // Hull points (similar to health points in other games) + Hull, + + // Damage the shield can take + Shield, + + // Current level of action points + AP, + + // Action points recovered by turn + AP_Recovery, + + // Starting action points in a battle + AP_Initial, + + // Miscellaneous attribute + Misc + } + + // Value computed from equipment + // This value can be altered by effects + // Example attributes are health points, action points 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, maximal: number = 1, current: number = 0) { + this.code = code; + this.maximal = maximal; + this.current = current; + } + + // Set the maximal value + setMaximal(value: number): void { + this.maximal = value; + } + + // 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 remain lower than maximal, and positive + private fix(): void { + if (this.current > this.maximal) { + this.current = this.maximal; + } + if (this.current < 0.0001) { + this.current = 0; + } + } + } +} diff --git a/src/scripts/game/Battle.ts b/src/scripts/game/Battle.ts index f64f059..484de19 100644 --- a/src/scripts/game/Battle.ts +++ b/src/scripts/game/Battle.ts @@ -90,6 +90,9 @@ module SpaceTac.Game { // This will call all necessary initialization steps (initiative, placement...) // This will not add any event to the battle log start(): void { + this.fleets.forEach((fleet: Fleet) => { + fleet.setBattle(this); + }); this.placeShips(); this.throwInitiative(); this.advanceToNextShip(false); diff --git a/src/scripts/game/Fleet.ts b/src/scripts/game/Fleet.ts index 7c052b3..60cb25e 100644 --- a/src/scripts/game/Fleet.ts +++ b/src/scripts/game/Fleet.ts @@ -9,10 +9,14 @@ module SpaceTac.Game { // List of ships ships: Ship[]; + // Current battle in which the fleet is engaged (null if not fighting) + battle: Battle; + // Create a fleet, bound to a player constructor(player: Player) { this.player = player; this.ships = []; + this.battle = null; } // Add a ship in this fleet @@ -20,5 +24,10 @@ module SpaceTac.Game { ship.fleet = this; this.ships.push(ship); } + + // Set the current battle + setBattle(battle: Battle): void { + this.battle = battle; + } } } diff --git a/src/scripts/game/Ship.ts b/src/scripts/game/Ship.ts index f0283b4..1711da6 100644 --- a/src/scripts/game/Ship.ts +++ b/src/scripts/game/Ship.ts @@ -32,13 +32,13 @@ module SpaceTac.Game { initative_throw: number; // Current number of action points - ap_current: number; + ap_current: Attribute; - // Maximal number of action points - ap_maximal: number; + // Initial number of action points, at the start of a battle + ap_initial: Attribute; // Number of action points recovered by turn - ap_recover: number; + ap_recover: Attribute; // Number of action points used to make a 1.0 move movement_cost: number; @@ -51,9 +51,9 @@ module SpaceTac.Game { this.fleet = fleet; this.name = name; this.initiative_level = 1; - this.ap_current = 10; - this.ap_maximal = 20; - this.ap_recover = 5; + this.ap_current = new Attribute(AttributeCode.AP); + this.ap_initial = new Attribute(AttributeCode.AP_Initial); + this.ap_recover = new Attribute(AttributeCode.AP_Recovery); this.movement_cost = 0.1; this.slots = []; @@ -86,7 +86,20 @@ module SpaceTac.Game { // Return the player owning this ship getPlayer(): Player { - return this.fleet.player; + if (this.fleet) { + return this.fleet.player; + } else { + return null; + } + } + + // get the current battle this ship is engaged in + getBattle(): Battle { + if (this.fleet) { + return this.fleet.battle; + } else { + return null; + } } // Get the list of actions available @@ -105,13 +118,61 @@ module SpaceTac.Game { return actions; } - // Consumes action points - useActionPoints(ap: number): void { - this.ap_current -= ap; + // Set an attribute 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 + setAttribute(attr: Attribute, value: number, offset: boolean = false, log: boolean = true) { + var changed: boolean; - if (this.ap_current <= 0.001) { - this.ap_current = 0; + if (offset) { + changed = attr.add(value); + } else { + changed = attr.set(value); } + + if (changed && log) { + var battle = this.getBattle(); + if (battle) { + battle.log.add(new AttributeChangeEvent(this, attr)); + } + } + } + + // Initialize the action points counter + // This should be called once at the start of a battle + // 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; + } + this.setAttribute(this.ap_current, value); + } + + // Recover action points + // This should be called once at the start of a turn + // 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; + } + this.setAttribute(this.ap_current, value, true); + } + + // Consumes action points + useActionPoints(value: number): void { + this.setAttribute(this.ap_current, -value, true); + } + + // Method called at the start of this ship turn + startTurn(first: boolean): void { + // Manage action points + if (first) { + this.initializeActionPoints(); + } else { + this.recoverActionPoints(); + } + + // TODO Apply active effects } // Get the maximal position reachable in the arena with current action points @@ -119,7 +180,7 @@ module SpaceTac.Game { var dx = x - this.arena_x; var dy = y - this.arena_y; var length = Math.sqrt(dx * dx + dy * dy); - var max_length = this.ap_current / this.movement_cost; + var max_length = this.ap_current.current / this.movement_cost; if (max_length >= length) { return [x, y]; } else { diff --git a/src/scripts/game/actions/MoveAction.ts b/src/scripts/game/actions/MoveAction.ts index 8a4843a..2ddee6b 100644 --- a/src/scripts/game/actions/MoveAction.ts +++ b/src/scripts/game/actions/MoveAction.ts @@ -8,7 +8,7 @@ module SpaceTac.Game { } canBeUsed(battle: Battle, ship: Ship): boolean { - return ship.ap_current > 0; + return ship.ap_current.current > 0; } checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { diff --git a/src/scripts/game/equipments/BasicPowerCore.ts b/src/scripts/game/equipments/BasicPowerCore.ts new file mode 100644 index 0000000..e5ca068 --- /dev/null +++ b/src/scripts/game/equipments/BasicPowerCore.ts @@ -0,0 +1,13 @@ +/// + +module SpaceTac.Game.Equipments { + "use strict"; + + export class BasicPowerCore extends LootTemplate { + constructor() { + super(SlotType.Power, "Basic Power Core"); + + this.min_level = new IntegerRange(1, 1); + } + } +} diff --git a/src/scripts/game/events/AttributeChangeEvent.ts b/src/scripts/game/events/AttributeChangeEvent.ts new file mode 100644 index 0000000..c1618b6 --- /dev/null +++ b/src/scripts/game/events/AttributeChangeEvent.ts @@ -0,0 +1,12 @@ +/// + +module SpaceTac.Game { + "use strict"; + + // Event logged when a ship moves + export class AttributeChangeEvent extends BaseLogEvent { + constructor(ship: Ship, attribute: Attribute) { + super("attr", ship); + } + } +} diff --git a/src/scripts/game/specs/Attribute.spec.ts b/src/scripts/game/specs/Attribute.spec.ts new file mode 100644 index 0000000..dae0a0c --- /dev/null +++ b/src/scripts/game/specs/Attribute.spec.ts @@ -0,0 +1,63 @@ +/// + +module SpaceTac.Game { + "use strict"; + + describe("Attribute", function () { + it("applies minimal and maximal value", function () { + var attr = new Attribute(AttributeCode.Misc, 100, 50); + 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); + }); + + it("tells if value changed", function () { + var result: boolean; + var attr = new Attribute(AttributeCode.Misc, 100, 50); + 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/scripts/game/specs/MoveAction.spec.ts b/src/scripts/game/specs/MoveAction.spec.ts index 4981a0e..5e5bdfa 100644 --- a/src/scripts/game/specs/MoveAction.spec.ts +++ b/src/scripts/game/specs/MoveAction.spec.ts @@ -6,7 +6,8 @@ module SpaceTac.Game { describe("MoveAction", function () { it("checks movement against remaining AP", function () { var ship = new Ship(null, "Test"); - ship.ap_current = 6; + ship.ap_current.setMaximal(20); + ship.ap_current.set(6); ship.movement_cost = 2; ship.arena_x = 0; ship.arena_y = 0; @@ -18,7 +19,7 @@ module SpaceTac.Game { result = action.checkTarget(null, ship, Target.newFromLocation(0, 8)); expect(result).toEqual(Target.newFromLocation(0, 3)); - ship.ap_current = 0; + ship.ap_current.set(0); result = action.checkTarget(null, ship, Target.newFromLocation(0, 8)); expect(result).toBeNull(); }); @@ -38,7 +39,8 @@ module SpaceTac.Game { it("applies to ship location, battle log and AP", function () { var battle = new Battle(null, null); var ship = new Ship(null, "Test"); - ship.ap_current = 5; + ship.ap_current.setMaximal(20); + ship.ap_current.set(5); ship.movement_cost = 1; ship.arena_x = 0; ship.arena_y = 0; @@ -48,13 +50,13 @@ module 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).toEqual(0); + expect(ship.ap_current.current).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).toEqual(0); + expect(ship.ap_current.current).toEqual(0); expect(battle.log.events.length).toBe(1); expect(battle.log.events[0].code).toEqual("move"); diff --git a/src/scripts/game/specs/Ship.spec.ts b/src/scripts/game/specs/Ship.spec.ts index eee45be..dea63ff 100644 --- a/src/scripts/game/specs/Ship.spec.ts +++ b/src/scripts/game/specs/Ship.spec.ts @@ -6,7 +6,8 @@ module SpaceTac.Game { describe("Ship", function () { it("limits movement range by action points", function () { var ship = new Ship(null, "Test"); - ship.ap_current = 8; + ship.ap_current.setMaximal(20); + ship.ap_current.set(8); ship.movement_cost = 3; ship.setArenaPosition(50, 50); @@ -20,17 +21,18 @@ module SpaceTac.Game { it("moves and consumes action points", function () { var ship = new Ship(null, "Test"); - ship.ap_current = 8; + ship.ap_current.setMaximal(20); + ship.ap_current.set(8); ship.movement_cost = 3; ship.setArenaPosition(50, 50); ship.moveTo(51, 50); - expect(ship.ap_current).toEqual(5); + expect(ship.ap_current.current).toEqual(5); expect(ship.arena_x).toEqual(51); expect(ship.arena_y).toEqual(50); ship.moveTo(53, 50); - expect(ship.ap_current).toBe(0); + expect(ship.ap_current.current).toBe(0); expect(ship.arena_x).toBeCloseTo(52.333333, 0.00001); expect(ship.arena_y).toEqual(50); });