1
0
Fork 0

typescript: Added strict null checks

This commit is contained in:
Michaël Lemaire 2017-03-09 18:11:00 +01:00
parent e64e3955b3
commit d3e12fa8e1
78 changed files with 461 additions and 363 deletions

1
TODO
View file

@ -1,4 +1,3 @@
* Enable strict null checking in typescript
* Ensure that tweens and particle emitters get destroyed once animation is done (or view changes)
* Highlight ships that will be included as target of current action
* Controls: Do not focus on ship while targetting for area effects (dissociate hover and target)

View file

@ -9,7 +9,7 @@
"homepage": "",
"private": true,
"dependencies": {
"phaser": "2.6.2",
"phaser-ce": "2.7.3",
"jasmine-core": "jasmine#^2.5.2",
"deep-diff": "0.3.0"
}

View file

@ -22,13 +22,13 @@
"bower": "~1.8",
"codecov": "~1.0",
"jasmine": "~2.5",
"karma": "~1.4",
"karma": "~1.5",
"karma-coverage": "~1.1",
"karma-jasmine": "~1.1",
"karma-phantomjs-launcher": "~1.0",
"remap-istanbul": "~0.8",
"remap-istanbul": "~0.9",
"live-server": "~1.2",
"typescript": "~2.2",
"typings": "~1.4"
"typings": "~2.1"
}
}

@ -1 +1 @@
Subproject commit db61f921e8afa9beca31bf1de1d30870f48a17e4
Subproject commit 79e3c649cf7a411d06af7372aa825518e0d8df30

View file

@ -1,8 +1,8 @@
module TS.SpaceTac {
describe("Battle", function () {
it("defines play order by initiative throws", function () {
var fleet1 = new Fleet(null);
var fleet2 = new Fleet(null);
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "F1S1");
ship1.setAttribute("initiative", 2);
@ -26,8 +26,8 @@ module TS.SpaceTac {
});
it("places ships on lines, facing the arena center", function () {
var fleet1 = new Fleet(null);
var fleet2 = new Fleet(null);
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "F1S1");
var ship2 = new Ship(fleet1, "F1S2");
@ -60,8 +60,8 @@ module TS.SpaceTac {
});
it("advances to next ship in play order", function () {
var fleet1 = new Fleet(null);
var fleet2 = new Fleet(null);
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "F1S1");
var ship2 = new Ship(fleet1, "F1S2");
@ -113,8 +113,8 @@ module TS.SpaceTac {
});
it("calls startTurn on ships", function () {
var fleet1 = new Fleet(null);
var fleet2 = new Fleet(null);
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "F1S1");
var ship2 = new Ship(fleet1, "F1S2");

View file

@ -20,8 +20,8 @@ module TS.SpaceTac {
turn: number;
// Current ship whose turn it is to play
playing_ship_index: number;
playing_ship: Ship;
playing_ship_index: number | null;
playing_ship: Ship | null;
// List of deployed drones
drones: Drone[] = [];
@ -34,9 +34,9 @@ module TS.SpaceTac {
timer = Timer.global;
// Create a battle between two fleets
constructor(fleet1: Fleet = null, fleet2: Fleet = null, width = 1780, height = 948) {
constructor(fleet1 = new Fleet(), fleet2 = new Fleet(), width = 1780, height = 948) {
this.log = new BattleLog();
this.fleets = [fleet1 || new Fleet(), fleet2 || new Fleet()];
this.fleets = [fleet1, fleet2];
this.play_order = [];
this.playing_ship_index = null;
this.playing_ship = null;
@ -129,7 +129,7 @@ module TS.SpaceTac {
}
// Ends a battle and sets the outcome
endBattle(winner: Fleet, log: boolean = true) {
endBattle(winner: Fleet | null, log: boolean = true) {
this.ended = true;
this.outcome = new BattleOutcome(winner);
if (winner) {
@ -153,7 +153,7 @@ module TS.SpaceTac {
this.endBattle(null, log);
} else if (alive_fleets === 1) {
// We have a winner
var winner: Fleet = null;
var winner: Fleet | null = null;
this.fleets.forEach((fleet: Fleet) => {
if (fleet.isAlive()) {
winner = fleet;
@ -198,7 +198,7 @@ module TS.SpaceTac {
this.playing_ship.startTurn();
}
if (log) {
if (log && previous_ship && this.playing_ship) {
this.log.add(new ShipChangeEvent(previous_ship, this.playing_ship));
}
}
@ -207,11 +207,13 @@ module TS.SpaceTac {
* Make an AI play the current ship
*/
playAI(ai: AbstractAI | null = null) {
if (!ai) {
// TODO Use an AI adapted to the fleet
ai = new BullyAI(this.playing_ship, this.timer);
if (this.playing_ship) {
if (!ai) {
// TODO Use an AI adapted to the fleet
ai = new BullyAI(this.playing_ship, this.timer);
}
ai.play();
}
ai.play();
}
// Start the battle

View file

@ -3,7 +3,7 @@
module TS.SpaceTac {
// Check a single game log event
function checkEvent(got: BaseLogEvent, ship: Ship, code: string,
target_ship: Ship = null, target_x: number = null, target_y: number = null): void {
target_ship: Ship | null = null, target_x: number | null = null, target_y: number | null = null): void {
if (target_ship) {
if (target_x === null) {
target_x = target_ship.arena_x;
@ -15,23 +15,27 @@ module TS.SpaceTac {
expect(got.ship).toBe(ship);
expect(got.code).toEqual(code);
expect(got.target.ship).toBe(target_ship);
if (target_x === null) {
expect(got.target.x).toBeNull();
if (got.target) {
expect(got.target.ship).toBe(target_ship);
if (target_x === null) {
expect(got.target.x).toBeNull();
} else {
expect(got.target.x).toBeCloseTo(target_x, 0.000001);
}
if (target_y === null) {
expect(got.target.y).toBeNull();
} else {
expect(got.target.y).toBeCloseTo(target_y, 0.000001);
}
} else {
expect(got.target.x).toBeCloseTo(target_x, 0.000001);
}
if (target_y === null) {
expect(got.target.y).toBeNull();
} else {
expect(got.target.y).toBeCloseTo(target_y, 0.000001);
fail("Got no target");
}
}
// Fake event
class FakeEvent extends BaseLogEvent {
constructor() {
super("fake");
super("fake", new Ship());
}
}
@ -68,7 +72,8 @@ module TS.SpaceTac {
});
it("can receive simulated initial state events", function () {
var battle = Battle.newQuickRandom();
let battle = Battle.newQuickRandom();
let playing = nn(battle.playing_ship);
battle.log.clear();
battle.log.addFilter("value");
expect(battle.log.events.length).toBe(0);
@ -80,7 +85,7 @@ module TS.SpaceTac {
checkEvent(battle.log.events[i], battle.play_order[i], "move", null,
battle.play_order[i].arena_x, battle.play_order[i].arena_y);
}
checkEvent(battle.log.events[8], battle.playing_ship, "ship_change", battle.playing_ship);
checkEvent(battle.log.events[8], playing, "ship_change", playing);
});
});
}

View file

@ -5,12 +5,12 @@ module TS.SpaceTac {
draw: boolean;
// Victorious fleet
winner: Fleet;
winner: Fleet | null;
// Retrievable loot
loot: Equipment[];
constructor(winner: Fleet) {
constructor(winner: Fleet | null) {
this.winner = winner;
this.draw = winner ? false : true;
this.loot = [];
@ -27,8 +27,10 @@ module TS.SpaceTac {
var count = random.randInt(0, ship.getEquipmentCount());
while (count > 0) {
var salvaged = ship.getRandomEquipment(random);
salvaged.detach();
this.loot.push(salvaged);
if (salvaged) {
salvaged.detach();
this.loot.push(salvaged);
}
count--;
}
@ -61,7 +63,7 @@ module TS.SpaceTac {
// Generate a special loot item for the winner fleet
// The equipment will be in the dead ship range
generateLootItem(random: RandomGenerator, base_level: number): Equipment {
generateLootItem(random: RandomGenerator, base_level: number): Equipment | null {
var generator = this.getLootGenerator(random);
var level = new IntegerRange(base_level - 1, base_level + 1);
return generator.generate(level);

View file

@ -2,10 +2,10 @@ module TS.SpaceTac {
// Piece of equipment to attach in slots
export class Equipment {
// Actual slot this equipment is attached to
attached_to: Slot;
attached_to: Slot | null = null;
// Type of slot this equipment can fit in
slot: SlotType;
slot: SlotType | null;
// Identifiable equipment code (may be used by UI to customize visual effects)
code: string;
@ -41,7 +41,7 @@ module TS.SpaceTac {
target_effects: BaseEffect[];
// Basic constructor
constructor(slot: SlotType = null, code: string = null) {
constructor(slot: SlotType | null = null, code = "equiment") {
this.slot = slot;
this.code = code;
this.name = code;

View file

@ -10,17 +10,17 @@ module TS.SpaceTac {
ships: Ship[];
// Current fleet location
location: StarLocation;
location: StarLocation | null;
// Current battle in which the fleet is engaged (null if not fighting)
battle: Battle;
battle: Battle | null;
// Amount of credits available
credits = 0;
// Create a fleet, bound to a player
constructor(player: Player = null) {
this.player = player || new Player();
constructor(player = new Player()) {
this.player = player;
this.ships = [];
this.location = null;
this.battle = null;
@ -59,7 +59,7 @@ module TS.SpaceTac {
}
// Set the current battle
setBattle(battle: Battle): void {
setBattle(battle: Battle | null): void {
this.battle = battle;
}

View file

@ -4,12 +4,12 @@ module TS.SpaceTac {
// Random generator to use
random: RandomGenerator;
constructor(random: RandomGenerator = new RandomGenerator()) {
constructor(random = RandomGenerator.global) {
this.random = random;
}
// Generate a fleet of a given level
generate(level: number, player: Player = null, ship_count: number = 3): Fleet {
generate(level: number, player?: Player, ship_count = 3): Fleet {
var fleet = new Fleet(player);
var ship_generator = new ShipGenerator(this.random);

View file

@ -12,7 +12,7 @@ module TS.SpaceTac.Specs {
* Apply deterministic game steps
*/
function applyGameSteps(session: GameSession): void {
var battle = session.getBattle();
var battle = nn(session.getBattle());
battle.advanceToNextShip();
// TODO Make some fixed moves (AI?)
battle.endBattle(battle.fleets[0]);

View file

@ -48,7 +48,7 @@ module TS.SpaceTac {
}
// Get currently played battle, null when none is in progress
getBattle(): Battle {
getBattle(): Battle | null {
return this.player.getBattle();
}

View file

@ -17,11 +17,14 @@ module TS.SpaceTac.Specs {
generator.random = new SkewedRandomGenerator([0.5]);
var equipment = generator.generate(new IntegerRange(3, 6));
expect(equipment.slot).toBe(SlotType.Shield);
expect(equipment.name).toEqual("Hexagrid Shield");
expect(equipment.min_level).toBe(5);
expect(equipment.ap_usage).toBeCloseTo(6.2727, 0.00001);
if (equipment) {
expect(equipment.slot).toBe(SlotType.Shield);
expect(equipment.name).toEqual("Hexagrid Shield");
expect(equipment.min_level).toBe(5);
expect(equipment.ap_usage).toBeCloseTo(6.2727, 0.00001);
} else {
fail("No equipment generated");
}
});
});
}

View file

@ -35,22 +35,22 @@ module TS.SpaceTac {
// If slot is specified, it will generate an equipment for this slot type specifically
// If level is specified, it will generate an equipment with level requirement inside this range
// If no equipment could be generated from available templates, null is returned
generate(level: IntegerRange = null, slot: SlotType = null): Equipment {
generate(level: IntegerRange | null = null, slot: SlotType | null = null): Equipment | null {
// Generate equipments matching conditions, with each template
var equipments: Equipment[] = [];
this.templates.forEach((template: LootTemplate) => {
if (slot !== null && slot !== template.slot) {
if (slot && slot != template.slot) {
return;
}
var equipment: Equipment;
if (level === null) {
equipment = template.generate(this.random);
} else {
var equipment: Equipment | null;
if (level) {
equipment = template.generateInLevelRange(level, this.random);
} else {
equipment = template.generate(this.random);
}
if (equipment !== null) {
if (equipment) {
equipments.push(equipment);
}
});

View file

@ -61,7 +61,7 @@ module TS.SpaceTac.Specs {
var template = new LootTemplate(SlotType.Weapon, "Bulletator");
template.min_level = new IntegerRange(4, 7);
var result: Range;
var result: any;
result = template.getPowerRangeForLevel(new IntegerRange(4, 7));
expect(result.min).toBe(0);

View file

@ -46,7 +46,7 @@ module TS.SpaceTac {
}
// Set a capability requirement
addRequirement(capability: keyof ShipAttributes, min: number, max: number = null): void {
addRequirement(capability: keyof ShipAttributes, min: number, max: number | null = null): void {
this.requirements[capability] = new IntegerRange(min, max);
}
@ -70,7 +70,10 @@ module TS.SpaceTac {
result.ap_usage = this.ap_usage.getProportional(power);
result.min_level = this.min_level.getProportional(power);
result.action = this.getActionForEquipment(result);
let action = this.getActionForEquipment(result)
if (action) {
result.action = action;
}
iteritems(this.requirements, (key: string, requirement: IntegerRange) => {
if (requirement) {
@ -89,7 +92,7 @@ module TS.SpaceTac {
}
// Find the power range that will result in the level range
getPowerRangeForLevel(level: IntegerRange): Range {
getPowerRangeForLevel(level: IntegerRange): Range | null {
if (level.min > this.min_level.max || level.max < this.min_level.min) {
return null;
} else {
@ -113,7 +116,7 @@ module TS.SpaceTac {
// Generate an equipment that will have its level requirement in the given range
// May return null if level range is not compatible with the template
generateInLevelRange(level: IntegerRange, random = RandomGenerator.global): Equipment {
generateInLevelRange(level: IntegerRange, random = RandomGenerator.global): Equipment | null {
var random_range = this.getPowerRangeForLevel(level);
if (random_range) {
var power = random.random() * (random_range.max - random_range.min) + random_range.min;
@ -126,7 +129,7 @@ module TS.SpaceTac {
/**
* Convenience function to add a modulated effect to the equipment
*/
addEffect(effect: BaseEffect, min_value: number, max_value: number = null, target = true) {
addEffect(effect: BaseEffect, min_value: number, max_value: number | null = null, target = true) {
var template = new EffectTemplate(effect);
template.addModifier("value", new IntegerRange(min_value, max_value));
if (target) {
@ -139,8 +142,8 @@ module TS.SpaceTac {
/**
* Convenience function to add a modulated sticky effect to the equipment
*/
addStickyEffect(effect: BaseEffect, min_value: number, max_value: number = null, min_duration: number = 1,
max_duration: number = null, on_stick = false, on_turn_start = false, target = true): void {
addStickyEffect(effect: BaseEffect, min_value: number, max_value: number | null = null, min_duration: number = 1,
max_duration: number | null = null, on_stick = false, on_turn_start = false, target = true): void {
var template = new EffectTemplate(new StickyEffect(effect, 0, on_stick, on_turn_start));
template.addModifier("value", new IntegerRange(min_value, max_value));
template.addModifier("duration", new IntegerRange(min_duration, max_duration));
@ -177,7 +180,7 @@ module TS.SpaceTac {
}
// Method to reimplement to assign an action to a generated equipment
protected getActionForEquipment(equipment: Equipment): BaseAction {
protected getActionForEquipment(equipment: Equipment): BaseAction | null {
return null;
}
}

View file

@ -21,8 +21,9 @@ module TS.SpaceTac.Specs {
let engine2 = TestTools.addEngine(ship, 120);
let engine3 = TestTools.addEngine(ship, 150);
let engine4 = TestTools.addEngine(ship, 70);
expect(simulator.findBestEngine()).toBe(engine3);
expect(simulator.findBestEngine().distance).toBe(150);
let best = simulator.findBestEngine();
expect(best).toBe(engine3);
expect((<Equipment>best).distance).toBe(150);
});
it("fires directly when in range", function () {
@ -36,7 +37,7 @@ module TS.SpaceTac.Specs {
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
expect(result.parts).toEqual([
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: true }
{ action: jasmine.objectContaining({ code: "fire-equiment" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: true }
]);
});
@ -50,7 +51,7 @@ module TS.SpaceTac.Specs {
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
expect(result.parts).toEqual([
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: false }
{ action: jasmine.objectContaining({ code: "fire-equiment" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: false }
]);
});
@ -68,7 +69,7 @@ module TS.SpaceTac.Specs {
expect(result.parts).toEqual([
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 1, possible: true },
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 15, ship.arena_y, null), ap: 3, possible: true }
{ action: jasmine.objectContaining({ code: "fire-equiment" }), target: new Target(ship.arena_x + 15, ship.arena_y, null), ap: 3, possible: true }
]);
});
@ -86,7 +87,7 @@ module TS.SpaceTac.Specs {
expect(result.parts).toEqual([
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 10, ship.arena_y, null), ap: 2, possible: true },
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2, possible: false }
{ action: jasmine.objectContaining({ code: "fire-equiment" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2, possible: false }
]);
});

View file

@ -13,7 +13,7 @@ module TS.SpaceTac {
}
// Get a new unique name from available choices
getName(): string {
getName(): string | null {
if (this.choices.length === 0) {
return null;
}

View file

@ -63,10 +63,10 @@ module TS.SpaceTac {
}
// Get currently played battle, null when none is in progress
getBattle(): Battle {
getBattle(): Battle | null {
return this.fleet.battle;
}
setBattle(battle: Battle): void {
setBattle(battle: Battle | null): void {
this.fleet.setBattle(battle);
}

View file

@ -8,12 +8,12 @@ module TS.SpaceTac {
max: number;
// Create a range of values
constructor(min: number, max: number = null) {
constructor(min: number, max: number | null = null) {
this.set(min, max);
}
// Change the range
set(min: number, max: number = null) {
set(min: number, max: number | null = null) {
this.min = min;
if (max === null) {
this.max = this.min;

View file

@ -95,7 +95,7 @@ module TS.SpaceTac {
upgrade_points = 0;
// Create a new ship inside a fleet
constructor(fleet: Fleet = null, name = "Ship") {
constructor(fleet: Fleet | null = null, name = "Ship") {
this.fleet = fleet || new Fleet();
this.level = 1;
this.name = name;
@ -146,20 +146,12 @@ module TS.SpaceTac {
// Return the player owning this ship
getPlayer(): Player {
if (this.fleet) {
return this.fleet.player;
} else {
return null;
}
return this.fleet.player;
}
// get the current battle this ship is engaged in
getBattle(): Battle {
if (this.fleet) {
return this.fleet.battle;
} else {
return null;
}
getBattle(): Battle | null {
return this.fleet.battle;
}
// Get the list of actions available
@ -257,7 +249,7 @@ module TS.SpaceTac {
// 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 {
initializeActionPoints(value: number | null = null): void {
if (value === null) {
value = this.attributes.power_initial.get();
}
@ -267,7 +259,7 @@ module TS.SpaceTac {
// Recover action points
// This should be called once at the end of a turn
// If no value is provided, the current attribute ap_recovery will be used
recoverActionPoints(value: number = null): void {
recoverActionPoints(value: number | null = null): void {
if (this.alive) {
if (value === null) {
value = this.attributes.power_recovery.get();
@ -510,7 +502,7 @@ module TS.SpaceTac {
* List all equipments attached to slots of a given type (any slot type if null)
*/
listEquipment(slottype: SlotType | null = null): Equipment[] {
return this.slots.filter(slot => slot.attached && (slottype == null || slot.type == slottype)).map(slot => slot.attached);
return nna(this.slots.filter(slot => slot.attached && (slottype == null || slot.type == slottype)).map(slot => slot.attached));
}
// Get the number of attached equipments
@ -525,13 +517,13 @@ module TS.SpaceTac {
}
// Get a random attached equipment, null if no equipment is attached
getRandomEquipment(random = RandomGenerator.global): Equipment {
getRandomEquipment(random = RandomGenerator.global): Equipment | null {
var count = this.getEquipmentCount();
if (count === 0) {
return null;
} else {
var picked = random.randInt(0, count - 1);
var result: Equipment = null;
var result: Equipment | null = null;
var index = 0;
this.slots.forEach((slot: Slot) => {
if (slot.attached) {

View file

@ -4,14 +4,13 @@ module TS.SpaceTac {
// Random number generator used
random: RandomGenerator;
// Create a default ship generator
constructor(random: RandomGenerator = null) {
this.random = random || new RandomGenerator();
constructor(random = RandomGenerator.global) {
this.random = random;
}
// Generate a ship of a given level
// The ship will not be named, nor will be a member of any fleet
generate(level: number, model: ShipModel = null): Ship {
generate(level: number, model: ShipModel | null = null): Ship {
var result = new Ship();
var loot = new LootGenerator(this.random);
@ -30,7 +29,9 @@ module TS.SpaceTac {
// Fill equipment slots
result.slots.forEach((slot: Slot) => {
var equipment = loot.generate(new IntegerRange(level, level), slot.type);
slot.attach(equipment);
if (equipment) {
slot.attach(equipment);
}
});
return result;

View file

@ -24,7 +24,7 @@ module TS.SpaceTac {
// Get the default ship model collection available in-game
static getDefaultCollection(): ShipModel[] {
// TODO Store in cache
var result = [];
var result: ShipModel[] = [];
result.push(new ShipModel("scout", 1, 2, SlotType.Hull, SlotType.Power, SlotType.Power, SlotType.Engine, SlotType.Weapon));

View file

@ -17,7 +17,7 @@ module TS.SpaceTac {
type: SlotType;
// Currently attached equipment, null if none
attached: Equipment;
attached: Equipment | null;
// Create an empty slot for a ship
constructor(ship: Ship, type: SlotType) {
@ -27,7 +27,7 @@ module TS.SpaceTac {
}
// Attach an equipment in this slot
attach(equipment: Equipment): Equipment | null {
attach(equipment: Equipment): Equipment {
if (this.type === equipment.slot && equipment.canBeEquipped(this.ship)) {
this.attached = equipment;
equipment.attached_to = this;
@ -35,11 +35,8 @@ module TS.SpaceTac {
if (this.ship) {
this.ship.updateAttributes();
}
return equipment;
} else {
return null;
}
return equipment;
}
}

View file

@ -75,7 +75,7 @@ module TS.SpaceTac {
// Base level for encounters in this system
level: number;
constructor(universe: Universe = null, x = 0, y = 0, name = "") {
constructor(universe: Universe | null = null, x = 0, y = 0, name = "") {
this.universe = universe || new Universe();
this.x = x;
this.y = y;

View file

@ -36,7 +36,7 @@ module TS.SpaceTac {
}
// Get the other side of the link, for a given side
getPeer(star: Star): Star {
getPeer(star: Star): Star | null {
if (star === this.first) {
return this.second;
} else if (star === this.second) {

View file

@ -1,7 +1,7 @@
module TS.SpaceTac.Specs {
describe("StarLocation", () => {
it("removes generated encounters that lose", function () {
var location = new StarLocation(null, StarLocationType.PLANET, 0, 0);
var location = new StarLocation(undefined, StarLocationType.PLANET, 0, 0);
var fleet = new Fleet();
var random = new SkewedRandomGenerator([0]);
var battle = location.enterLocation(fleet, random);
@ -9,13 +9,13 @@ module TS.SpaceTac.Specs {
expect(location.encounter).not.toBeNull();
expect(battle).not.toBeNull();
battle.endBattle(fleet);
nn(battle).endBattle(fleet);
expect(location.encounter).toBeNull();
});
it("leaves generated encounters that win", function () {
var location = new StarLocation(null, StarLocationType.PLANET, 0, 0);
var location = new StarLocation(undefined, StarLocationType.PLANET, 0, 0);
var fleet = new Fleet();
var random = new SkewedRandomGenerator([0]);
var battle = location.enterLocation(fleet, random);
@ -23,7 +23,7 @@ module TS.SpaceTac.Specs {
expect(location.encounter).not.toBeNull();
expect(battle).not.toBeNull();
battle.endBattle(location.encounter);
nn(battle).endBattle(location.encounter);
expect(location.encounter).not.toBeNull();
});

View file

@ -24,14 +24,14 @@ module TS.SpaceTac {
universe_y: number;
// Destination for jump, if its a WARP location
jump_dest: StarLocation;
jump_dest: StarLocation | null;
// Enemy encounter
encounter: Fleet;
encounter: Fleet | null;
encounter_gen: boolean;
constructor(star: Star, type: StarLocationType = StarLocationType.PLANET, x: number = 0, y: number = 0) {
this.star = star || new Star();
constructor(star = new Star(), type: StarLocationType = StarLocationType.PLANET, x: number = 0, y: number = 0) {
this.star = star;
this.type = type;
this.x = x;
this.y = y;
@ -52,14 +52,14 @@ module TS.SpaceTac {
// Call this when first probing a location to generate the possible encounter
// Returns the encountered fleet, null if no encounter happens
tryGenerateEncounter(random = RandomGenerator.global): Fleet {
tryGenerateEncounter(random = RandomGenerator.global): Fleet | null {
if (!this.encounter_gen) {
this.encounter_gen = true;
if (random.random() < 0.8) {
var fleet_generator = new FleetGenerator(random);
var ship_count = random.randInt(1, 5);
this.encounter = fleet_generator.generate(this.star.level, null, ship_count);
this.encounter = fleet_generator.generate(this.star.level, undefined, ship_count);
}
}
@ -69,7 +69,7 @@ module TS.SpaceTac {
// Call this when entering a location to generate the possible encounter
// *fleet* is the player fleet, entering the location
// Returns the engaged battle, null if no encounter happens
enterLocation(fleet: Fleet, random = RandomGenerator.global): Battle {
enterLocation(fleet: Fleet, random = RandomGenerator.global): Battle | null {
var encounter = this.tryGenerateEncounter(random);
if (encounter) {
var battle = new Battle(fleet, encounter);

View file

@ -60,17 +60,17 @@ module TS.SpaceTac.Specs {
var warps = getWarps(universe.stars[0]);
expect(warps.length).toBe(2);
expect(warps[0].jump_dest.star).toBe(universe.stars[1]);
expect(warps[1].jump_dest.star).toBe(universe.stars[2]);
expect(nn(warps[0].jump_dest).star).toBe(universe.stars[1]);
expect(nn(warps[1].jump_dest).star).toBe(universe.stars[2]);
expect(universe.stars[0].getWarpLocationTo(universe.stars[1])).toBe(warps[0]);
expect(universe.stars[0].getWarpLocationTo(universe.stars[2])).toBe(warps[1]);
warps = getWarps(universe.stars[1]);
expect(warps.length).toBe(1);
expect(warps[0].jump_dest.star).toBe(universe.stars[0]);
expect(nn(warps[0].jump_dest).star).toBe(universe.stars[0]);
expect(universe.stars[1].getWarpLocationTo(universe.stars[2])).toBeNull();
warps = getWarps(universe.stars[2]);
expect(warps.length).toBe(1);
expect(warps[0].jump_dest.star).toBe(universe.stars[0]);
expect(nn(warps[0].jump_dest).star).toBe(universe.stars[0]);
});
});
}

View file

@ -46,7 +46,7 @@ module TS.SpaceTac {
continue;
}
star.name = names.getName();
star.name = names.getName() || "Star";
result.push(star);
count--;
@ -104,15 +104,12 @@ module TS.SpaceTac {
}
// Get the star nearest to another
getNearestTo(star: Star, others: Star[] = null): Star {
if (others === null) {
others = this.stars;
}
getNearestTo(star: Star, others = this.stars): Star | null {
if (others.length === 0) {
return null;
} else {
var mindist = this.radius * 2.0;
var nearest: Star = null;
var nearest: Star | null = null;
others.forEach((istar: Star) => {
if (istar !== star) {
var dist = star.getDistanceTo(istar);

View file

@ -11,10 +11,10 @@ module TS.SpaceTac {
needs_target: boolean;
// Equipment that triggers this action
equipment: Equipment;
equipment: Equipment | null;
// Create the action
constructor(code: string, name: string, needs_target: boolean, equipment: Equipment = null) {
constructor(code: string, name: string, needs_target: boolean, equipment: Equipment | null = null) {
this.code = code;
this.name = name;
this.needs_target = needs_target;
@ -28,7 +28,7 @@ module TS.SpaceTac {
*
* Returns an informative message indicating why the action cannot be used, null otherwise
*/
checkCannotBeApplied(ship: Ship, remaining_ap: number = null): string | null {
checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): string | null {
let battle = ship.getBattle();
if (battle && battle.playing_ship !== ship) {
// Ship is not playing
@ -48,7 +48,7 @@ module TS.SpaceTac {
}
// Get the number of action points the action applied to a target would use
getActionPointsUsage(ship: Ship, target: Target): number {
getActionPointsUsage(ship: Ship, target: Target | null): number {
if (this.equipment) {
return this.equipment.ap_usage;
} else {
@ -76,7 +76,7 @@ module TS.SpaceTac {
// Method to check if a target is applicable for this action
// Will call checkLocationTarget or checkShipTarget by default
checkTarget(ship: Ship, target: Target): Target {
checkTarget(ship: Ship, target: Target | null): Target | null {
if (this.checkCannotBeApplied(ship)) {
return null;
} else if (target) {
@ -92,18 +92,18 @@ module TS.SpaceTac {
// Method to reimplement to check if a space target is applicable
// Must return null if the target can't be applied, an altered target, or the original target
checkLocationTarget(ship: Ship, target: Target): Target {
checkLocationTarget(ship: Ship, target: Target): Target | null {
return null;
}
// Method to reimplement to check if a ship target is applicable
// Must return null if the target can't be applied, an altered target, or the original target
checkShipTarget(ship: Ship, target: Target): Target {
checkShipTarget(ship: Ship, target: Target): Target | null {
return null;
}
// Apply an action, returning true if it was successful
apply(ship: Ship, target: Target): boolean {
apply(ship: Ship, target: Target | null): boolean {
let reject = this.checkCannotBeApplied(ship);
if (reject == null) {
let checked_target = this.checkTarget(ship, target);
@ -127,7 +127,7 @@ module TS.SpaceTac {
}
// Method to reimplement to apply a action
protected customApply(ship: Ship, target: Target) {
protected customApply(ship: Ship, target: Target | null) {
}
}
}

View file

@ -5,6 +5,8 @@ module TS.SpaceTac {
* Action to deploy a drone in space
*/
export class DeployDroneAction extends BaseAction {
equipment: Equipment;
constructor(equipment: Equipment) {
super("deploy-" + equipment.code, "Deploy", true, equipment);
}

View file

@ -6,14 +6,17 @@ module TS.SpaceTac {
// Boolean set to true if the weapon can target space
can_target_space: boolean;
// Equipment cannot be null
equipment: Equipment;
constructor(equipment: Equipment, can_target_space = false, name = "Fire") {
super("fire-" + equipment.code, name, true, equipment);
this.can_target_space = can_target_space;
}
checkLocationTarget(ship: Ship, target: Target): Target {
if (this.can_target_space) {
checkLocationTarget(ship: Ship, target: Target): Target | null {
if (target && this.can_target_space) {
target = target.constraintInRange(ship.arena_x, ship.arena_y, this.equipment.distance);
return target;
} else {
@ -21,8 +24,8 @@ module TS.SpaceTac {
}
}
checkShipTarget(ship: Ship, target: Target): Target {
if (ship.getPlayer() === target.ship.getPlayer()) {
checkShipTarget(ship: Ship, target: Target): Target | null {
if (target.ship && ship.getPlayer() === target.ship.getPlayer()) {
// No friendly fire
return null;
} else {
@ -40,10 +43,11 @@ module TS.SpaceTac {
/**
* Collect the effects applied by this action
*/
getEffects(battle: Battle, ship: Ship, target: Target): [Ship, BaseEffect][] {
getEffects(ship: Ship, target: Target): [Ship, BaseEffect][] {
let result: [Ship, BaseEffect][] = [];
let blast = this.getBlastRadius(ship);
let ships = blast ? battle.collectShipsInCircle(target, blast, true) : ((target.ship && target.ship.alive) ? [target.ship] : []);
let battle = ship.getBattle();
let ships = (blast && battle) ? battle.collectShipsInCircle(target, blast, true) : ((target.ship && target.ship.alive) ? [target.ship] : []);
ships.forEach(ship => {
this.equipment.target_effects.forEach(effect => result.push([ship, effect]));
});
@ -58,7 +62,7 @@ module TS.SpaceTac {
ship.addBattleEvent(new FireEvent(ship, this.equipment, target));
// Apply effects
let effects = this.getEffects(ship.getBattle(), ship, target);
let effects = this.getEffects(ship, target);
effects.forEach(([ship, effect]) => effect.applyOnShip(ship));
}
}

View file

@ -29,7 +29,7 @@ module TS.SpaceTac {
it("forbids targetting a ship", function () {
var ship1 = new Ship(null, "Test1");
var ship2 = new Ship(null, "Test2");
var action = new MoveAction(null);
var action = new MoveAction(new Equipment());
var result = action.checkTarget(ship1, Target.newFromShip(ship1));
expect(result).toBeNull();
@ -74,9 +74,10 @@ module TS.SpaceTac {
expect(battle.log.events[1].code).toEqual("move");
expect(battle.log.events[1].ship).toBe(ship);
expect(battle.log.events[1].target.ship).toBeNull();
expect(battle.log.events[1].target.x).toBeCloseTo(3.535533, 0.00001);
expect(battle.log.events[1].target.y).toBeCloseTo(3.535533, 0.00001);
let target: any = battle.log.events[1].target;
expect(target.ship).toBeNull();
expect(target.x).toBeCloseTo(3.535533, 0.00001);
expect(target.y).toBeCloseTo(3.535533, 0.00001);
});
it("can't move too much near another ship", function () {

View file

@ -5,13 +5,16 @@ module TS.SpaceTac {
// Safety distance from other ships
safety_distance: number;
// Equipment cannot be null (engine)
equipment: Equipment;
constructor(equipment: Equipment) {
super("move", "Move", true, equipment);
this.safety_distance = 50;
}
checkCannotBeApplied(ship: Ship, remaining_ap: number = null): string | null {
checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): string | null {
let base = super.checkCannotBeApplied(ship, Infinity);
if (base) {
return base;

View file

@ -76,18 +76,23 @@ module TS.SpaceTac {
// console.debug(`Turn ${battle.turn} - Ship ${battle.play_order.indexOf(playing)} - Player ${battle.fleets.indexOf(playing.fleet)}`);
let ai = (playing.fleet == battle.fleets[0]) ? this.ai1 : this.ai2;
ai.timer = Timer.synchronous;
ai.ship = playing;
ai.play();
if (playing) {
let ai = (playing.fleet == battle.fleets[0]) ? this.ai1 : this.ai2;
ai.timer = Timer.synchronous;
ai.ship = playing;
ai.play();
} else {
console.error("No ship playing");
break;
}
if (!battle.ended && battle.playing_ship == playing) {
console.error(`${ai.name} did not end its turn !`);
console.error("AI did not end its turn !");
battle.advanceToNextShip();
}
}
if (battle.ended && !battle.outcome.draw) {
if (battle.ended && !battle.outcome.draw && battle.outcome.winner) {
this.update(battle.fleets.indexOf(battle.outcome.winner));
} else {
this.update(-1);
@ -101,7 +106,8 @@ module TS.SpaceTac {
* Setup the duel HTML page
*/
static setup(element: HTMLElement) {
let ais = [new BullyAI(null), new TacticalAI(null), new AbstractAI(null)];
let fakeship = new Ship();
let ais = [new BullyAI(fakeship), new TacticalAI(fakeship), new AbstractAI(fakeship)];
ais.forEach((ai, idx) => {
let selects = element.getElementsByTagName("select");
for (let i = 0; i < selects.length; i++) {
@ -122,11 +128,12 @@ module TS.SpaceTac {
console.clear();
let ai1 = parseInt(element.getElementsByTagName("select").item(0).value);
let ai2 = parseInt(element.getElementsByTagName("select").item(1).value);
AIDuel.current = new AIDuel(ais[ai1], ais[ai2]);
AIDuel.current.start(() => {
element.getElementsByClassName("win1").item(0).textContent = AIDuel.current.win1.toString();
element.getElementsByClassName("win2").item(0).textContent = AIDuel.current.win2.toString();
element.getElementsByClassName("draw").item(0).textContent = AIDuel.current.draw.toString();
let duel = new AIDuel(ais[ai1], ais[ai2]);
AIDuel.current = duel;
duel.start(() => {
element.getElementsByClassName("win1").item(0).textContent = duel.win1.toString();
element.getElementsByClassName("win2").item(0).textContent = duel.win2.toString();
element.getElementsByClassName("draw").item(0).textContent = duel.draw.toString();
});
button.textContent = "Stop !";
}

View file

@ -22,7 +22,7 @@ module TS.SpaceTac {
// When the queue is empty, the ship will end its turn.
private workqueue: Function[];
constructor(ship: Ship, timer = Timer.global, name: string = null) {
constructor(ship: Ship, timer = Timer.global, name?: string) {
this.name = name || classname(this);
this.ship = ship;
this.workqueue = [];
@ -52,7 +52,7 @@ module TS.SpaceTac {
}
// Add a work item to the work queue
addWorkItem(item: Function, delay = 100): void {
addWorkItem(item: Function | null, delay = 100): void {
if (this.timer.isSynchronous()) {
if (item) {
item();
@ -90,7 +90,9 @@ module TS.SpaceTac {
} else {
// Take the first item
var item = this.workqueue.shift();
item();
if (item) {
item();
}
}
} else {
this.endTurn();
@ -108,7 +110,6 @@ module TS.SpaceTac {
if (this.ship.playing) {
let battle = this.ship.getBattle();
this.ship.endTurn();
this.ship = null;
if (battle) {
battle.advanceToNextShip();
}

View file

@ -56,9 +56,13 @@ module TS.SpaceTac.Specs {
enemy.arena_x = 3;
enemy.arena_y = 0;
var result = ai.checkBullyManeuver(enemy, weapon);
expect(result.simulation.need_move).toBe(false);
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
expect(result.equipment).toBe(weapon);
if (result) {
expect(result.simulation.need_move).toBe(false);
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
expect(result.equipment).toBe(weapon);
} else {
fail("No maneuver proposed");
}
// enemy out of range, but moving can bring it in range
ship.values.power.set(8);
@ -67,9 +71,13 @@ module TS.SpaceTac.Specs {
enemy.arena_x = 6;
enemy.arena_y = 0;
result = ai.checkBullyManeuver(enemy, weapon);
expect(result.simulation.move_location).toEqual(Target.newFromLocation(3, 0));
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
expect(result.equipment).toBe(weapon);
if (result) {
expect(result.simulation.move_location).toEqual(Target.newFromLocation(3, 0));
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
expect(result.equipment).toBe(weapon);
} else {
fail("No maneuver proposed");
}
// enemy out of range, but moving can bring it in range, except for the safety margin
ai.move_margin = 0.1;
@ -110,7 +118,7 @@ module TS.SpaceTac.Specs {
expect(result).toBeNull();
// no engine, can't move
ship.slots[0].attached.detach();
engine.detach();
ship.values.power.set(8);
ship.arena_x = 1;
ship.arena_y = 0;
@ -155,7 +163,7 @@ module TS.SpaceTac.Specs {
var engine = TestTools.addEngine(ai.ship, 100);
(<MoveAction>engine.action).safety_distance = 20;
var maneuver: BullyManeuver;
var maneuver: BullyManeuver | null;
battle.fleets[1].ships.forEach((ship: Ship) => {
ai.ship.setArenaPosition(0, 0);
@ -172,10 +180,18 @@ module TS.SpaceTac.Specs {
// Move towards an enemy (up to minimal distance)
ai.ship.setArenaPosition(30, 0);
maneuver = ai.getFallbackManeuver();
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(25, 0));
if (maneuver) {
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(25, 0));
} else {
fail("No maneuver proposed");
}
ai.ship.setArenaPosition(25, 0);
maneuver = ai.getFallbackManeuver();
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(22.5, 0));
if (maneuver) {
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(22.5, 0));
} else {
fail("No maneuver proposed");
}
});
it("applies the chosen move", function () {

View file

@ -18,7 +18,7 @@ module TS.SpaceTac {
if (this.ship.getValue("power") > 0) {
this.addWorkItem(() => {
var maneuvers = this.listAllManeuvers();
var maneuver: BullyManeuver;
var maneuver: BullyManeuver | null;
if (maneuvers.length > 0) {
maneuver = this.pickManeuver(maneuvers);
@ -39,11 +39,14 @@ module TS.SpaceTac {
listAllEnemies(): Ship[] {
var result: Ship[] = [];
this.ship.getBattle().play_order.forEach((ship: Ship) => {
if (ship.alive && ship.getPlayer() !== this.ship.getPlayer()) {
result.push(ship);
}
});
let battle = this.ship.getBattle();
if (battle) {
battle.play_order.forEach((ship: Ship) => {
if (ship.alive && ship.getPlayer() !== this.ship.getPlayer()) {
result.push(ship);
}
});
}
return result;
}
@ -73,7 +76,7 @@ module TS.SpaceTac {
}
// Get an equipped engine to make a move
getEngine(): Equipment {
getEngine(): Equipment | null {
var engines = this.ship.listEquipment(SlotType.Engine);
if (engines.length === 0) {
return null;
@ -95,7 +98,7 @@ module TS.SpaceTac {
}
// When no bully action is available, pick a random enemy, and go towards it
getFallbackManeuver(): BullyManeuver {
getFallbackManeuver(): BullyManeuver | null {
var enemies = this.listAllEnemies();
if (enemies.length === 0) {
return null;
@ -107,12 +110,20 @@ module TS.SpaceTac {
var target = Target.newFromShip(picked);
var distance = target.getDistanceTo(Target.newFromShip(this.ship));
var engine = this.getEngine();
var safety_distance = (<MoveAction>engine.action).safety_distance;
if (distance > safety_distance) { // Don't move too close
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y,
(distance - safety_distance) * APPROACH_FACTOR);
target = engine.action.checkLocationTarget(this.ship, target);
return new BullyManeuver(this.ship, engine, target);
if (engine) {
var safety_distance = (<MoveAction>engine.action).safety_distance;
if (distance > safety_distance) { // Don't move too close
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y,
(distance - safety_distance) * APPROACH_FACTOR);
let loctarget = engine.action.checkLocationTarget(this.ship, target);
if (loctarget) {
return new BullyManeuver(this.ship, engine, loctarget);
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
@ -120,7 +131,7 @@ module TS.SpaceTac {
// Pick a maneuver from a list of available ones
// By default, it chooses the nearest enemy
pickManeuver(available: BullyManeuver[]): BullyManeuver {
pickManeuver(available: BullyManeuver[]): BullyManeuver | null {
if (available.length === 0) {
return null;
}
@ -134,7 +145,7 @@ module TS.SpaceTac {
}
// Effectively apply the chosen maneuver
applyManeuver(maneuver: BullyManeuver): void {
applyManeuver(maneuver: BullyManeuver | null): void {
if (maneuver) {
this.addWorkItem(() => {
maneuver.apply();

View file

@ -15,7 +15,7 @@ module TS.SpaceTac.Specs {
// producer of FixedManeuver from a list of scores
let producer = (...scores: number[]) => imap(iarray(scores), score => new FixedManeuver(score));
let applied = [];
let applied: number[] = [];
beforeEach(function () {
applied = [];

View file

@ -52,12 +52,16 @@ module TS.SpaceTac {
while (done < 1000 && this.producers.length > 0) {
// Produce a maneuver
let maneuver: Maneuver;
let maneuver: Maneuver | null = null;
let producer = this.producers.shift();
[maneuver, producer] = producer();
if (producer) {
[maneuver, producer] = producer();
}
if (maneuver) {
this.producers.push(producer);
if (producer) {
this.producers.push(producer);
}
// Evaluate the maneuver
let score = this.evaluate(maneuver);
@ -88,7 +92,7 @@ module TS.SpaceTac {
TacticalAIHelpers.produceBlastShots,
TacticalAIHelpers.produceRandomMoves,
]
producers.forEach(producer => this.producers.push(producer(this.ship, this.ship.getBattle())));
producers.forEach(producer => this.producers.push(producer(this.ship, this.ship.getBattle() || new Battle())));
}
/**
@ -101,6 +105,7 @@ module TS.SpaceTac {
scaled(TacticalAIHelpers.evaluateDamageToEnemy, 30),
scaled(TacticalAIHelpers.evaluateClustering, 3),
]
// TODO evaluator typing is lost
evaluators.forEach(evaluator => this.evaluators.push((maneuver: Maneuver) => evaluator(this.ship, this.ship.getBattle(), maneuver)));
}
}

View file

@ -72,7 +72,7 @@ module TS.SpaceTac {
}
let damage = 0;
let dead = 0;
let effects = action.getEffects(battle, ship, maneuver.target);
let effects = action.getEffects(ship, maneuver.target);
effects.forEach(([ship, effect]) => {
if (effect instanceof DamageEffect && contains(enemies, ship)) {
let [shield, hull] = effect.getEffectiveDamage(ship);

View file

@ -14,21 +14,21 @@ module TS.SpaceTac.Equipments {
*
* Be aware that *min_distance* means the MAXIMAL reachable distance, but on a low-power loot !
*/
setDeployDistance(min_distance: number, max_distance: number = null): void {
setDeployDistance(min_distance: number, max_distance: number | null = null): void {
this.distance = new Range(min_distance, max_distance);
}
/**
* Set the effect radius of the deployed drone
*/
setEffectRadius(min_radius: number, max_radius: number = null): void {
setEffectRadius(min_radius: number, max_radius: number | null = null): void {
this.blast = new IntegerRange(min_radius, max_radius);
}
/**
* Set the drone lifetime
*/
setLifetime(min_lifetime: number, max_lifetime: number = null): void {
setLifetime(min_lifetime: number, max_lifetime: number | null = null): void {
this.duration = new IntegerRange(min_lifetime, max_lifetime);
}

View file

@ -6,7 +6,7 @@ module TS.SpaceTac.Equipments {
// Boolean set to true if the weapon can target space
can_target_space: boolean;
constructor(name: string, min_damage: number = 0, max_damage: number = null) {
constructor(name: string, min_damage: number = 0, max_damage: number | null = null) {
super(SlotType.Weapon, name);
this.can_target_space = false;
@ -18,13 +18,13 @@ module TS.SpaceTac.Equipments {
// Set the range for this weapon
// Pay attention that *min_distance* means the MAXIMAL reachable distance, but on a low-power loot
setRange(min_distance: number, max_distance: number = null, can_target_space: boolean = false): void {
setRange(min_distance: number, max_distance: number | null = null, can_target_space = false): void {
this.distance = new Range(min_distance, max_distance);
this.can_target_space = can_target_space;
}
// Set the effect radius (blast) for this weapon
setBlast(min_blast: number, max_blast: number = null): void {
setBlast(min_blast: number, max_blast: number | null = null): void {
this.blast = new IntegerRange(min_blast, max_blast);
}

View file

@ -5,18 +5,36 @@ module TS.SpaceTac {
code: string;
// The ship causing the event (the one whose turn it is to play)
ship: Ship;
ship: Ship | null;
// Target of the event
target: Target;
target: Target | null;
// Boolean at true if the event is used to set initial battle conditions
initial = false;
constructor(code: string, ship: Ship = null, target: Target = null) {
constructor(code: string, ship: Ship | null = null, target: Target | null = null) {
this.code = code;
this.ship = ship;
this.target = target;
}
}
// Base class for a BattleLog event linked to a ship
export class BaseLogShipEvent extends BaseLogEvent {
ship: Ship;
constructor(code: string, ship: Ship, target: Target | null = null) {
super(code, ship, target);
}
}
// Base class for a BattleLog event linked to a ship, and with a target
export class BaseLogShipTargetEvent extends BaseLogShipEvent {
target: Target;
constructor(code: string, ship: Ship, target: Target) {
super(code, ship, target);
}
}
}

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a ship takes damage
export class DamageEvent extends BaseLogEvent {
export class DamageEvent extends BaseLogShipEvent {
// Damage to hull
hull: number;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a ship is dead
export class DeathEvent extends BaseLogEvent {
export class DeathEvent extends BaseLogShipEvent {
constructor(ship: Ship) {
super("death", ship);
}

View file

@ -4,7 +4,7 @@ module TS.SpaceTac {
/**
* Event logged when a drone applies its effects
*/
export class DroneAppliedEvent extends BaseLogEvent {
export class DroneAppliedEvent extends BaseLogShipEvent {
// Pointer to the drone
drone: Drone;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a drone is deployed by a ship
export class DroneDeployedEvent extends BaseLogEvent {
export class DroneDeployedEvent extends BaseLogShipEvent {
// Pointer to the drone
drone: Drone;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a drone is destroyed
export class DroneDestroyedEvent extends BaseLogEvent {
export class DroneDestroyedEvent extends BaseLogShipEvent {
// Pointer to the drone
drone: Drone;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a sticky effect is added to a ship
export class EffectAddedEvent extends BaseLogEvent {
export class EffectAddedEvent extends BaseLogShipEvent {
// Pointer to the effect
effect: StickyEffect;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a sticky effect is added to a ship
export class EffectDurationChangedEvent extends BaseLogEvent {
export class EffectDurationChangedEvent extends BaseLogShipEvent {
// Pointer to the effect
effect: StickyEffect;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a sticky effect is removed from a ship
export class EffectRemovedEvent extends BaseLogEvent {
export class EffectRemovedEvent extends BaseLogShipEvent {
// Pointer to the effect
effect: StickyEffect;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a weapon is used on a target
export class FireEvent extends BaseLogEvent {
export class FireEvent extends BaseLogShipTargetEvent {
// Weapon used
weapon: Equipment;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a ship moves
export class MoveEvent extends BaseLogEvent {
export class MoveEvent extends BaseLogShipTargetEvent {
// New facing angle, in radians
facing_angle: number;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Battle event, when a ship turn ended, and advanced to a new one
export class ShipChangeEvent extends BaseLogEvent {
export class ShipChangeEvent extends BaseLogShipEvent {
// Ship that starts playing
new_ship: Ship;

View file

@ -2,7 +2,7 @@
module TS.SpaceTac {
// Event logged when a ship value or attribute changed
export class ValueChangeEvent extends BaseLogEvent {
export class ValueChangeEvent extends BaseLogShipEvent {
// Saved version of the current value
value: ShipValue;

View file

@ -45,12 +45,11 @@ module TS.SpaceTac.UI.Specs {
afterEach(function () {
let ui = testgame.ui;
window.requestAnimationFrame(() => ui.destroy());
testgame.ui = null;
testgame.baseview = null;
testgame.battleview = null;
testgame.mapview = null;
window.requestAnimationFrame(() => {
if (ui) {
ui.destroy();
}
});
});
return testgame;
@ -74,7 +73,7 @@ module TS.SpaceTac.UI.Specs {
testgame.battleview = new BattleView();
let battle = Battle.newQuickRandom();
let player = battle.playing_ship.getPlayer();
let player = battle.playing_ship ? battle.playing_ship.getPlayer() : new Player();
return [testgame.battleview, [player, battle]];
});

View file

@ -18,7 +18,7 @@ module TS.SpaceTac.UI {
icon_waiting: Phaser.Image;
// Current ship, whose actions are displayed
ship: Ship;
ship: Ship | null;
ship_power_capacity: number;
ship_power_value: number;
@ -170,7 +170,7 @@ module TS.SpaceTac.UI {
* *power_usage* is the consumption of currently selected action.
*/
updateSelectedActionPower(power_usage: number): void {
var remaining_ap = this.ship.values.power.get() - power_usage;
var remaining_ap = this.ship ? (this.ship.values.power.get() - power_usage) : 0;
if (remaining_ap < 0) {
remaining_ap = 0;
}

View file

@ -23,7 +23,7 @@ module TS.SpaceTac.UI {
fading: boolean;
// Current targetting
private targetting: Targetting;
private targetting: Targetting | null;
// Action icon - image representing the action
private layer_icon: Phaser.Image;
@ -101,7 +101,9 @@ module TS.SpaceTac.UI {
this.bar.actionStarted();
// Update range hint
this.battleview.arena.range_hint.setPrimary(this.ship, this.action);
if (this.battleview.arena.range_hint) {
this.battleview.arena.range_hint.setPrimary(this.ship, this.action);
}
// Update fading statuses
this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, null));
@ -110,13 +112,18 @@ module TS.SpaceTac.UI {
this.setSelected(true);
if (this.action.needs_target) {
// Switch to targetting mode (will apply action when a target is selected)
this.targetting = this.battleview.enterTargettingMode();
this.targetting.setSource(this.battleview.arena.findShipSprite(this.ship));
this.targetting.targetSelected.add(this.processSelection, this);
this.targetting.targetHovered.add(this.processHover, this);
if (this.action instanceof MoveAction) {
this.targetting.setApIndicatorsInterval(this.action.getDistanceByActionPoint(this.ship));
let sprite = this.battleview.arena.findShipSprite(this.ship);
if (sprite) {
// Switch to targetting mode (will apply action when a target is selected)
this.targetting = this.battleview.enterTargettingMode();
if (this.targetting) {
this.targetting.setSource(sprite);
this.targetting.targetSelected.add(this.processSelection, this);
this.targetting.targetHovered.add(this.processHover, this);
if (this.action instanceof MoveAction) {
this.targetting.setApIndicatorsInterval(this.action.getDistanceByActionPoint(this.ship));
}
}
}
} else {
// No target needed, apply action immediately
@ -127,13 +134,15 @@ module TS.SpaceTac.UI {
// Called when a target is hovered
// This will check the target against current action and adjust it if needed
processHover(target: Target): void {
target = this.action.checkTarget(this.ship, target);
this.targetting.setTarget(target, false, this.action.getBlastRadius(this.ship));
this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, target));
let correct_target = this.action.checkTarget(this.ship, target);
if (this.targetting) {
this.targetting.setTarget(correct_target, false, this.action.getBlastRadius(this.ship));
}
this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, correct_target));
}
// Called when a target is selected
processSelection(target: Target): void {
processSelection(target: Target | null): void {
if (this.action.apply(this.ship, target)) {
this.bar.actionEnded();
}

View file

@ -10,13 +10,14 @@ module TS.SpaceTac.UI.Specs {
let tooltip = bar.tooltip;
bar.clearAll();
let a1 = bar.addAction(battleview.battle.playing_ship, new MoveAction(new Equipment()));
a1.action.equipment.name = "Engine";
let ship = nn(battleview.battle.playing_ship);
let a1 = bar.addAction(ship, new MoveAction(new Equipment()));
nn(a1.action.equipment).name = "Engine";
a1.action.name = "Move";
let a2 = bar.addAction(battleview.battle.playing_ship, new FireWeaponAction(new Equipment()));
a2.action.equipment.name = "Weapon";
let a2 = bar.addAction(ship, new FireWeaponAction(new Equipment()));
nn(a2.action.equipment).name = "Weapon";
a2.action.name = "Fire";
let a3 = bar.addAction(battleview.battle.playing_ship, new EndTurnAction());
let a3 = bar.addAction(ship, new EndTurnAction());
a3.action.name = "End turn";
tooltip.setAction(a1);

View file

@ -39,7 +39,7 @@ module TS.SpaceTac.UI {
}
// Set current action to display, null to hide
setAction(action: ActionIcon): void {
setAction(action: ActionIcon | null): void {
if (action) {
if (this.icon) {
this.icon.destroy(true);

View file

@ -21,9 +21,9 @@ module TS.SpaceTac.UI {
private drone_sprites: ArenaDrone[] = [];
// Currently hovered ship
private hovered: ArenaShip;
private hovered: ArenaShip | null;
// Currently playing ship
private playing: ArenaShip;
private playing: ArenaShip | null;
// Layer for particles
layer_weapon_effects: Phaser.Group;
@ -35,7 +35,7 @@ module TS.SpaceTac.UI {
this.battleview = battleview;
this.playing = null;
this.hovered = null;
this.range_hint = null;
this.range_hint = new RangeHint(this);
var offset_x = 133;
var offset_y = 132;
@ -60,9 +60,8 @@ module TS.SpaceTac.UI {
}, null);
this.position.set(offset_x, offset_y);
this.addChild(this.background);
this.range_hint = new RangeHint(this);
this.addChild(this.background);
this.addChild(this.range_hint);
this.init();
@ -100,8 +99,8 @@ module TS.SpaceTac.UI {
}
// Find the sprite for a ship
findShipSprite(ship: Ship): ArenaShip {
var result: ArenaShip = null;
findShipSprite(ship: Ship): ArenaShip | null {
var result: ArenaShip | null = null;
this.ship_sprites.forEach((sprite: ArenaShip) => {
if (sprite.ship === ship) {
result = sprite;
@ -111,27 +110,36 @@ module TS.SpaceTac.UI {
}
// Set the hovered state on a ship sprite
setShipHovered(ship: Ship): void {
setShipHovered(ship: Ship | null): void {
if (this.hovered) {
this.hovered.setHovered(false);
}
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
arena_ship.setHovered(true);
if (ship) {
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
arena_ship.setHovered(true);
}
this.hovered = arena_ship;
} else {
this.hovered = null;
}
this.hovered = arena_ship;
}
// Set the playing state on a ship sprite
setShipPlaying(ship: Ship): void {
setShipPlaying(ship: Ship | null): void {
if (this.playing) {
this.playing.setPlaying(false);
this.playing = null;
}
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
arena_ship.setPlaying(true);
if (ship) {
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
arena_ship.setPlaying(true);
}
this.playing = arena_ship;
}
this.playing = arena_ship;
this.battleview.gameui.audio.playOnce("battle-ship-change");
}

View file

@ -5,8 +5,8 @@ module TS.SpaceTac.UI.Specs {
let testgame = setupBattleview();
it("adds effects display", function () {
let ship = testgame.battleview.battle.playing_ship;
let sprite = testgame.battleview.arena.findShipSprite(ship);
let ship = nn(testgame.battleview.battle.playing_ship);
let sprite = nn(testgame.battleview.arena.findShipSprite(ship));
expect(sprite.effects.children.length).toBe(0);

View file

@ -13,7 +13,7 @@ module TS.SpaceTac.UI.Specs {
expect(battleview.targetting).toBeNull();
// Enter targetting mode
var result = battleview.enterTargettingMode();
var result = nn(battleview.enterTargettingMode());
expect(battleview.targetting).toBeTruthy();
expect(result).toBe(battleview.targetting);
@ -32,7 +32,7 @@ module TS.SpaceTac.UI.Specs {
battleview.cursorInSpace(8, 4);
expect(battleview.ship_hovered).toBeNull();
expect(battleview.targetting.target_corrected).toEqual(Target.newFromLocation(8, 4));
expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromLocation(8, 4));
// Process a click on space
battleview.cursorClicked();
@ -40,20 +40,20 @@ module TS.SpaceTac.UI.Specs {
// Forward ship hovering
battleview.cursorOnShip(battleview.battle.play_order[0]);
expect(battleview.ship_hovered).toEqual(battleview.battle.playing_ship);
expect(battleview.targetting.target_corrected).toEqual(Target.newFromShip(battleview.battle.playing_ship));
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Don't leave a ship we're not hovering
battleview.cursorOffShip(battleview.battle.play_order[1]);
expect(battleview.ship_hovered).toEqual(battleview.battle.playing_ship);
expect(battleview.targetting.target_corrected).toEqual(Target.newFromShip(battleview.battle.playing_ship));
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Don't move in space while on ship
battleview.cursorInSpace(1, 3);
expect(battleview.ship_hovered).toEqual(battleview.battle.playing_ship);
expect(battleview.targetting.target_corrected).toEqual(Target.newFromShip(battleview.battle.playing_ship));
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Process a click on ship
battleview.cursorClicked();
@ -62,7 +62,7 @@ module TS.SpaceTac.UI.Specs {
battleview.cursorOffShip(battleview.battle.play_order[0]);
expect(battleview.ship_hovered).toBeNull();
expect(battleview.targetting.target_corrected).toBeNull();
expect(nn(battleview.targetting).target_corrected).toBeNull();
// Quit targetting
battleview.exitTargettingMode();
@ -73,7 +73,7 @@ module TS.SpaceTac.UI.Specs {
battleview.cursorInSpace(8, 4);
expect(battleview.ship_hovered).toBeNull();
battleview.cursorOnShip(battleview.battle.play_order[0]);
expect(battleview.ship_hovered).toEqual(battleview.battle.playing_ship);
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
// Quit twice don't do anything
battleview.exitTargettingMode();
@ -81,12 +81,12 @@ module TS.SpaceTac.UI.Specs {
// Check collected targetting events
expect(hovered).toEqual([
Target.newFromLocation(8, 4),
Target.newFromShip(battleview.battle.playing_ship),
Target.newFromShip(battleview.battle.play_order[0]),
null
]);
expect(clicked).toEqual([
Target.newFromLocation(8, 4),
Target.newFromShip(battleview.battle.playing_ship),
Target.newFromShip(battleview.battle.play_order[0]),
]);
});
});

View file

@ -17,10 +17,10 @@ module TS.SpaceTac.UI {
arena: Arena;
// Background image
background: Phaser.Image;
background: Phaser.Image | null;
// Targetting mode (null if we're not in this mode)
targetting: Targetting;
targetting: Targetting | null;
// Ship list
ship_list: ShipList;
@ -29,7 +29,7 @@ module TS.SpaceTac.UI {
action_bar: ActionBar;
// Currently hovered ship
ship_hovered: Ship;
ship_hovered: Ship | null;
// Ship tooltip
ship_tooltip: ShipTooltip;
@ -51,7 +51,6 @@ module TS.SpaceTac.UI {
this.battle = battle;
this.targetting = null;
this.ship_hovered = null;
this.log_processor = null;
this.background = null;
this.battle.timer = this.timer;
@ -97,7 +96,7 @@ module TS.SpaceTac.UI {
this.battle.endBattle(this.player.fleet);
});
this.inputs.bindCheat(Phaser.Keyboard.A, "Use AI to play", () => {
if (this.interacting) {
if (this.interacting && this.battle.playing_ship) {
this.setInteractionEnabled(false);
this.battle.playAI(new TacticalAI(this.battle.playing_ship));
}
@ -111,22 +110,9 @@ module TS.SpaceTac.UI {
shutdown() {
this.exitTargettingMode();
if (this.log_processor) {
this.log_processor.destroy();
this.log_processor = null;
}
if (this.ui) {
this.ui.destroy();
this.ui = null;
}
if (this.arena) {
this.arena.destroy();
this.arena = null;
}
this.battle = null;
this.log_processor.destroy();
this.ui.destroy();
this.arena.destroy();
super.shutdown();
}
@ -178,7 +164,7 @@ module TS.SpaceTac.UI {
}
// Set the currently hovered ship
setShipHovered(ship: Ship): void {
setShipHovered(ship: Ship | null): void {
this.ship_hovered = ship;
this.arena.setShipHovered(ship);
this.ship_list.setHovered(ship);
@ -201,7 +187,7 @@ module TS.SpaceTac.UI {
// Enter targetting mode
// While in this mode, the Targetting object will receive hover and click events, and handle them
enterTargettingMode(): Targetting {
enterTargettingMode(): Targetting | null {
if (!this.interacting) {
return null;
}

View file

@ -101,7 +101,7 @@ module TS.SpaceTac.UI {
this.processDroneDestroyedEvent(event);
} else if (event instanceof DroneAppliedEvent) {
this.processDroneAppliedEvent(event);
} else if (event.code == "effectadd" || event.code == "effectduration" || event.code == "effectdel") {
} else if (event instanceof EffectAddedEvent || event instanceof EffectRemovedEvent ||  event instanceof EffectDurationChangedEvent) {
this.processEffectEvent(event);
}
}
@ -116,8 +116,13 @@ module TS.SpaceTac.UI {
// Playing ship changed
private processShipChangeEvent(event: ShipChangeEvent): void {
this.view.arena.setShipPlaying(event.target.ship);
this.view.ship_list.setPlaying(event.target.ship);
if (event.target && event.target.ship) {
this.view.arena.setShipPlaying(event.target.ship);
this.view.ship_list.setPlaying(event.target.ship);
} else {
this.view.arena.setShipPlaying(null);
this.view.ship_list.setPlaying(null);
}
if (this.battle.canPlay(this.view.player)) {
// Player turn
@ -196,7 +201,7 @@ module TS.SpaceTac.UI {
private processEndBattleEvent(event: EndBattleEvent): void {
this.view.setInteractionEnabled(false);
if (event.outcome.winner.player === this.view.player) {
if (event.outcome.winner && event.outcome.winner.player === this.view.player) {
// Victory !
// TODO Loot screen
this.view.player.exitBattle();
@ -207,7 +212,7 @@ module TS.SpaceTac.UI {
}
// Sticky effect on ship added, changed or removed
private processEffectEvent(event: BaseLogEvent): void {
private processEffectEvent(event: EffectAddedEvent | EffectRemovedEvent | EffectDurationChangedEvent): void {
var item = this.view.ship_list.findItem(event.ship);
if (item) {
item.updateEffects();

View file

@ -8,7 +8,7 @@ module TS.SpaceTac.UI {
circle: Phaser.Graphics;
// Stored information of primary circle, when secondary one overrides it
primary: Phaser.Circle;
primary: Phaser.Circle | null;
constructor(parent: Arena) {
super(parent.game, parent);

View file

@ -8,10 +8,10 @@ module TS.SpaceTac.UI {
ships: ShipListItem[];
// Playing ship
playing: ShipListItem;
playing: ShipListItem | null;
// Hovered ship
hovered: ShipListItem;
hovered: ShipListItem | null;
// Create an empty action bar
constructor(battleview: BattleView) {
@ -57,8 +57,8 @@ module TS.SpaceTac.UI {
// Find an item for a ship
// Returns null if not found
findItem(ship: Ship): ShipListItem {
var found: ShipListItem = null;
findItem(ship: Ship): ShipListItem | null {
var found: ShipListItem | null = null;
this.ships.forEach((item: ShipListItem) => {
if (item.ship === ship) {
found = item;
@ -71,7 +71,7 @@ module TS.SpaceTac.UI {
findPlayPosition(ship: Ship): number {
var battle = this.battleview.battle;
var idx = battle.play_order.indexOf(ship);
var diff = idx - battle.playing_ship_index;
var diff = idx - (battle.playing_ship_index || 0);
if (diff < 0) {
diff += battle.play_order.length;
}
@ -100,19 +100,26 @@ module TS.SpaceTac.UI {
}
// Set the currently playing ship
setPlaying(ship: Ship): void {
this.playing = this.findItem(ship);
setPlaying(ship: Ship | null): void {
if (ship) {
this.playing = this.findItem(ship);
} else {
this.playing = null;
}
this.updateItemsLocation();
}
// Set the currently hovered ship
setHovered(ship: Ship): void {
setHovered(ship: Ship | null): void {
if (this.hovered) {
this.hovered.setHovered(false);
this.hovered = null;
}
this.hovered = this.findItem(ship);
if (this.hovered) {
this.hovered.setHovered(true);
if (ship) {
this.hovered = this.findItem(ship);
if (this.hovered) {
this.hovered.setHovered(true);
}
}
}
}

View file

@ -3,11 +3,11 @@ module TS.SpaceTac.UI {
// Allows to pick a target for an action
export class Targetting {
// Initial target (as pointed by the user)
target_initial: Target;
target_initial: Target | null;
line_initial: Phaser.Graphics;
// Corrected target (applying action rules)
target_corrected: Target;
target_corrected: Target | null;
line_corrected: Phaser.Graphics;
// Circle for effect radius
@ -25,13 +25,13 @@ module TS.SpaceTac.UI {
ap_indicators: Phaser.Image[] = [];
// Access to the parent battle view
private battleview: BattleView;
private battleview: BattleView | null;
// Source of the targetting
private source: PIXI.DisplayObject;
private source: PIXI.DisplayObject | null;
// Create a default targetting mode
constructor(battleview: BattleView) {
constructor(battleview: BattleView | null) {
this.battleview = battleview;
this.targetHovered = new Phaser.Signal();
this.targetSelected = new Phaser.Signal();
@ -116,6 +116,10 @@ module TS.SpaceTac.UI {
// Update the AP indicators display
updateApIndicators() {
if (!this.battleview || !this.source || !this.target_corrected) {
return;
}
// Get indicator count
let count = 0;
let distance = 0;
@ -139,10 +143,11 @@ module TS.SpaceTac.UI {
// Spread indicators
if (count > 0 && distance > 0) {
let dx = this.ap_interval * (this.target_corrected.x - this.source.x) / distance;
let dy = this.ap_interval * (this.target_corrected.y - this.source.y) / distance;
let source = this.source;
let dx = this.ap_interval * (this.target_corrected.x - source.x) / distance;
let dy = this.ap_interval * (this.target_corrected.y - source.y) / distance;
this.ap_indicators.forEach((indicator, index) => {
indicator.position.set(this.source.x + dx * index, this.source.y + dy * index);
indicator.position.set(source.x + dx * index, source.y + dy * index);
});
}
}
@ -153,7 +158,7 @@ module TS.SpaceTac.UI {
}
// Set a target from a target object
setTarget(target: Target, dispatch: boolean = true, blast_radius: number = 0): void {
setTarget(target: Target | null, dispatch: boolean = true, blast_radius: number = 0): void {
this.target_corrected = target;
this.blast_radius = blast_radius;
if (dispatch) {

View file

@ -4,7 +4,7 @@ module TS.SpaceTac.UI {
private game: MainUI;
private music: Phaser.Sound;
private music: Phaser.Sound | null;
constructor(game: MainUI) {
this.game = game;

View file

@ -33,7 +33,7 @@ module TS.SpaceTac.UI {
this.bind(Phaser.Keyboard.M, "Toggle sound", () => {
this.game.audio.toggleMute();
});
this.bind(Phaser.Keyboard.NUMPAD_ADD, null, () => {
this.bind(Phaser.Keyboard.NUMPAD_ADD, "", () => {
if (this.cheats_enabled) {
this.cheat = !this.cheat;
this.game.displayMessage(this.cheat ? "Cheats enabled" : "Cheats disabled");
@ -48,9 +48,9 @@ module TS.SpaceTac.UI {
// Bind a key to a cheat action
bindCheat(key: number, desc: string, action: Function): void {
this.bind(key, null, () => {
this.bind(key, `Cheat: ${desc}`, () => {
if (this.cheat) {
console.warn("Cheat ! " + desc);
console.warn(`Cheat ! ${desc}`);
action();
}
});

View file

@ -11,11 +11,15 @@ module TS.SpaceTac.UI.Specs {
mapview.game.tweens.update();
let tween = first(mapview.game.tweens.getAll(), tw => tw.target == fleet);
let tweendata = tween.generateData(0.1);
expect(tweendata.length).toEqual(3);
expect(tweendata[0].rotation).toBeCloseTo(-Math.PI * 2 / 3);
expect(tweendata[1].rotation).toBeCloseTo(-Math.PI * 4 / 3);
expect(tweendata[2].rotation).toBeCloseTo(-Math.PI * 2);
if (tween) {
let tweendata = tween.generateData(0.1);
expect(tweendata.length).toEqual(3);
expect(tweendata[0].rotation).toBeCloseTo(-Math.PI * 2 / 3);
expect(tweendata[1].rotation).toBeCloseTo(-Math.PI * 4 / 3);
expect(tweendata[2].rotation).toBeCloseTo(-Math.PI * 2);
} else {
fail("No tween found");
}
});
});
}

View file

@ -29,7 +29,9 @@ module TS.SpaceTac.UI {
sprite.anchor.set(0.5, 0.5);
});
this.position.set(fleet.location.star.x + fleet.location.x, fleet.location.star.y + fleet.location.y);
if (fleet.location) {
this.position.set(fleet.location.star.x + fleet.location.x, fleet.location.star.y + fleet.location.y);
}
this.scale.set(SCALING, SCALING);
this.tween = this.game.tweens.create(this);
@ -68,8 +70,8 @@ module TS.SpaceTac.UI {
/**
* Make the fleet move to another location in the same system
*/
moveToLocation(location: StarLocation, speed = 1, on_leave: (duration: number) => any | null = null) {
if (location != this.fleet.location) {
moveToLocation(location: StarLocation, speed = 1, on_leave: ((duration: number) => any) | null = null) {
if (this.fleet.location && location != this.fleet.location) {
let dx = location.universe_x - this.fleet.location.universe_x;
let dy = location.universe_y - this.fleet.location.universe_y;
let distance = Math.sqrt(dx * dx + dy * dy);

View file

@ -6,10 +6,10 @@ module TS.SpaceTac.UI {
*/
export class UniverseMapView extends BaseView {
// Displayed universe
universe: Universe;
universe = new Universe();
// Interacting player
player: Player;
player = new Player();
// Star systems
group: Phaser.Group;
@ -52,9 +52,11 @@ module TS.SpaceTac.UI {
let loc2 = starlink.second.getWarpLocationTo(starlink.first);
let result = new Phaser.Graphics(this.game);
result.lineStyle(0.005, 0x8bbeff);
result.moveTo(starlink.first.x - 0.5 + loc1.x, starlink.first.y - 0.5 + loc1.y);
result.lineTo(starlink.second.x - 0.5 + loc2.x, starlink.second.y - 0.5 + loc2.y);
if (loc1 && loc2) {
result.lineStyle(0.005, 0x8bbeff);
result.moveTo(starlink.first.x - 0.5 + loc1.x, starlink.first.y - 0.5 + loc1.y);
result.lineTo(starlink.second.x - 0.5 + loc2.x, starlink.second.y - 0.5 + loc2.y);
}
return result;
});
this.starlinks.forEach(starlink => this.group.addChild(starlink));
@ -92,8 +94,8 @@ module TS.SpaceTac.UI {
* Leaving the view, unbind and destroy
*/
shutdown() {
this.universe = null;
this.player = null;
this.universe = new Universe();
this.player = new Player();
super.shutdown();
}
@ -105,7 +107,7 @@ module TS.SpaceTac.UI {
this.starsystems.forEach(system => system.updateInfo());
let location = this.player.fleet.location;
if (location.type == StarLocationType.WARP) {
if (location && location.type == StarLocationType.WARP) {
let angle = Math.atan2(location.y, location.x);
this.button_jump.scale.set(location.star.radius * 0.002, location.star.radius * 0.002);
this.button_jump.position.set(location.star.x + location.x + 0.02 * Math.cos(angle), location.star.y + location.y + 0.02 * Math.sin(angle));
@ -140,8 +142,8 @@ module TS.SpaceTac.UI {
* Set the current zoom level (0, 1 or 2)
*/
setZoom(level: number) {
let current_star = this.player.fleet.location.star;
if (level <= 0) {
let current_star = this.player.fleet.location ? this.player.fleet.location.star : null;
if (!current_star || level <= 0) {
this.setCamera(0, 0, this.universe.radius * 2);
this.zoom = 0;
} else if (level == 1) {
@ -158,7 +160,7 @@ module TS.SpaceTac.UI {
* Do the jump animation to another system
*/
doJump() {
if (this.player.fleet.location.type == StarLocationType.WARP && this.player.fleet.location.jump_dest) {
if (this.player.fleet.location && this.player.fleet.location.type == StarLocationType.WARP && this.player.fleet.location.jump_dest) {
Animation.setVisibility(this.game, this.button_jump, false, 300);
let dest_location = this.player.fleet.location.jump_dest;

View file

@ -10,7 +10,7 @@
"removeComments": true,
"preserveConstEnums": true,
"out": "out/build.js",
"strictNullChecks": false,
"strictNullChecks": true,
"sourceMap": true,
"target": "es5"
},

View file

@ -1,8 +1,8 @@
{
"name": "succession",
"name": "spacetac",
"dependencies": {},
"globalDependencies": {
"jasmine": "registry:dt/jasmine#2.5.0+20161003201800",
"phaser": "github:photonstorm/phaser/typescript/typings.json#v2.6.2"
"phaser": "github:thunderk/phaser-ce/typescript/typings.json#v2.7.3a"
}
}