New attribute system for action points
This commit is contained in:
parent
fc5c8a7c55
commit
be434b4eb4
81
src/scripts/game/Attribute.ts
Normal file
81
src/scripts/game/Attribute.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,6 +90,9 @@ module SpaceTac.Game {
|
||||||
// This will call all necessary initialization steps (initiative, placement...)
|
// This will call all necessary initialization steps (initiative, placement...)
|
||||||
// This will not add any event to the battle log
|
// This will not add any event to the battle log
|
||||||
start(): void {
|
start(): void {
|
||||||
|
this.fleets.forEach((fleet: Fleet) => {
|
||||||
|
fleet.setBattle(this);
|
||||||
|
});
|
||||||
this.placeShips();
|
this.placeShips();
|
||||||
this.throwInitiative();
|
this.throwInitiative();
|
||||||
this.advanceToNextShip(false);
|
this.advanceToNextShip(false);
|
||||||
|
|
|
@ -9,10 +9,14 @@ module SpaceTac.Game {
|
||||||
// List of ships
|
// List of ships
|
||||||
ships: Ship[];
|
ships: Ship[];
|
||||||
|
|
||||||
|
// Current battle in which the fleet is engaged (null if not fighting)
|
||||||
|
battle: Battle;
|
||||||
|
|
||||||
// Create a fleet, bound to a player
|
// Create a fleet, bound to a player
|
||||||
constructor(player: Player) {
|
constructor(player: Player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.ships = [];
|
this.ships = [];
|
||||||
|
this.battle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a ship in this fleet
|
// Add a ship in this fleet
|
||||||
|
@ -20,5 +24,10 @@ module SpaceTac.Game {
|
||||||
ship.fleet = this;
|
ship.fleet = this;
|
||||||
this.ships.push(ship);
|
this.ships.push(ship);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the current battle
|
||||||
|
setBattle(battle: Battle): void {
|
||||||
|
this.battle = battle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ module SpaceTac.Game {
|
||||||
initative_throw: number;
|
initative_throw: number;
|
||||||
|
|
||||||
// Current number of action points
|
// Current number of action points
|
||||||
ap_current: number;
|
ap_current: Attribute;
|
||||||
|
|
||||||
// Maximal number of action points
|
// Initial number of action points, at the start of a battle
|
||||||
ap_maximal: number;
|
ap_initial: Attribute;
|
||||||
|
|
||||||
// Number of action points recovered by turn
|
// Number of action points recovered by turn
|
||||||
ap_recover: number;
|
ap_recover: Attribute;
|
||||||
|
|
||||||
// Number of action points used to make a 1.0 move
|
// Number of action points used to make a 1.0 move
|
||||||
movement_cost: number;
|
movement_cost: number;
|
||||||
|
@ -51,9 +51,9 @@ module SpaceTac.Game {
|
||||||
this.fleet = fleet;
|
this.fleet = fleet;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.initiative_level = 1;
|
this.initiative_level = 1;
|
||||||
this.ap_current = 10;
|
this.ap_current = new Attribute(AttributeCode.AP);
|
||||||
this.ap_maximal = 20;
|
this.ap_initial = new Attribute(AttributeCode.AP_Initial);
|
||||||
this.ap_recover = 5;
|
this.ap_recover = new Attribute(AttributeCode.AP_Recovery);
|
||||||
this.movement_cost = 0.1;
|
this.movement_cost = 0.1;
|
||||||
this.slots = [];
|
this.slots = [];
|
||||||
|
|
||||||
|
@ -86,7 +86,20 @@ module SpaceTac.Game {
|
||||||
|
|
||||||
// Return the player owning this ship
|
// Return the player owning this ship
|
||||||
getPlayer(): Player {
|
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
|
// Get the list of actions available
|
||||||
|
@ -105,13 +118,61 @@ module SpaceTac.Game {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consumes action points
|
// Set an attribute value
|
||||||
useActionPoints(ap: number): void {
|
// If offset is true, the value will be added to current value
|
||||||
this.ap_current -= ap;
|
// 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) {
|
if (offset) {
|
||||||
this.ap_current = 0;
|
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
|
// 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 dx = x - this.arena_x;
|
||||||
var dy = y - this.arena_y;
|
var dy = y - this.arena_y;
|
||||||
var length = Math.sqrt(dx * dx + dy * dy);
|
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) {
|
if (max_length >= length) {
|
||||||
return [x, y];
|
return [x, y];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,7 @@ module SpaceTac.Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
canBeUsed(battle: Battle, ship: Ship): boolean {
|
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 {
|
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target {
|
||||||
|
|
13
src/scripts/game/equipments/BasicPowerCore.ts
Normal file
13
src/scripts/game/equipments/BasicPowerCore.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/// <reference path="../LootTemplate.ts"/>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/scripts/game/events/AttributeChangeEvent.ts
Normal file
12
src/scripts/game/events/AttributeChangeEvent.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/// <reference path="BaseLogEvent.ts"/>
|
||||||
|
|
||||||
|
module SpaceTac.Game {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Event logged when a ship moves
|
||||||
|
export class AttributeChangeEvent extends BaseLogEvent {
|
||||||
|
constructor(ship: Ship, attribute: Attribute) {
|
||||||
|
super("attr", ship);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
src/scripts/game/specs/Attribute.spec.ts
Normal file
63
src/scripts/game/specs/Attribute.spec.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/// <reference path="../../definitions/jasmine.d.ts"/>
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -6,7 +6,8 @@ module SpaceTac.Game {
|
||||||
describe("MoveAction", function () {
|
describe("MoveAction", function () {
|
||||||
it("checks movement against remaining AP", function () {
|
it("checks movement against remaining AP", function () {
|
||||||
var ship = new Ship(null, "Test");
|
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.movement_cost = 2;
|
||||||
ship.arena_x = 0;
|
ship.arena_x = 0;
|
||||||
ship.arena_y = 0;
|
ship.arena_y = 0;
|
||||||
|
@ -18,7 +19,7 @@ module SpaceTac.Game {
|
||||||
result = action.checkTarget(null, ship, Target.newFromLocation(0, 8));
|
result = action.checkTarget(null, ship, Target.newFromLocation(0, 8));
|
||||||
expect(result).toEqual(Target.newFromLocation(0, 3));
|
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));
|
result = action.checkTarget(null, ship, Target.newFromLocation(0, 8));
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -38,7 +39,8 @@ module SpaceTac.Game {
|
||||||
it("applies to ship location, battle log and AP", function () {
|
it("applies to ship location, battle log and AP", function () {
|
||||||
var battle = new Battle(null, null);
|
var battle = new Battle(null, null);
|
||||||
var ship = new Ship(null, "Test");
|
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.movement_cost = 1;
|
||||||
ship.arena_x = 0;
|
ship.arena_x = 0;
|
||||||
ship.arena_y = 0;
|
ship.arena_y = 0;
|
||||||
|
@ -48,13 +50,13 @@ module SpaceTac.Game {
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
|
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
|
||||||
expect(ship.arena_y).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));
|
result = action.apply(battle, ship, Target.newFromLocation(10, 10));
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
|
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
|
||||||
expect(ship.arena_y).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.length).toBe(1);
|
||||||
expect(battle.log.events[0].code).toEqual("move");
|
expect(battle.log.events[0].code).toEqual("move");
|
||||||
|
|
|
@ -6,7 +6,8 @@ module SpaceTac.Game {
|
||||||
describe("Ship", function () {
|
describe("Ship", function () {
|
||||||
it("limits movement range by action points", function () {
|
it("limits movement range by action points", function () {
|
||||||
var ship = new Ship(null, "Test");
|
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.movement_cost = 3;
|
||||||
ship.setArenaPosition(50, 50);
|
ship.setArenaPosition(50, 50);
|
||||||
|
|
||||||
|
@ -20,17 +21,18 @@ module SpaceTac.Game {
|
||||||
|
|
||||||
it("moves and consumes action points", function () {
|
it("moves and consumes action points", function () {
|
||||||
var ship = new Ship(null, "Test");
|
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.movement_cost = 3;
|
||||||
ship.setArenaPosition(50, 50);
|
ship.setArenaPosition(50, 50);
|
||||||
|
|
||||||
ship.moveTo(51, 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_x).toEqual(51);
|
||||||
expect(ship.arena_y).toEqual(50);
|
expect(ship.arena_y).toEqual(50);
|
||||||
|
|
||||||
ship.moveTo(53, 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_x).toBeCloseTo(52.333333, 0.00001);
|
||||||
expect(ship.arena_y).toEqual(50);
|
expect(ship.arena_y).toEqual(50);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue