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);
});