Added escort mission part
This commit is contained in:
parent
f7424692fe
commit
993ed0e897
67
TODO
67
TODO
|
@ -1,67 +0,0 @@
|
||||||
* New battle internal flow: any game state change should be done through revertable events
|
|
||||||
* UI: use a common component class, and a layer abstraction
|
|
||||||
* Character sheet: add initial character creation
|
|
||||||
* Character sheet: disable interaction during battle (except for loot screen)
|
|
||||||
* Character sheet: improve eye-catching for shop and loot section
|
|
||||||
* Character sheet: highlight allowed destinations during drag-and-drop, with text hints
|
|
||||||
* Character sheet: when transferring to another ship, if the item can't be equipped (unmatched requirements), the transfer is cancelled instead of trying cargo
|
|
||||||
* Character sheet: effective skill is sometimes not updated when upgrading base skill
|
|
||||||
* Character sheet: tooltip to show the sources of attributes
|
|
||||||
* Character sheet: add a "loot all" button
|
|
||||||
* Shops: allow to change/buy ship model
|
|
||||||
* Loot: lucky finds should be proportional to cargo space
|
|
||||||
* Ship models: Add permanent effects
|
|
||||||
* Ship models: Add permanent actions
|
|
||||||
* Equipment: add critical hit/miss
|
|
||||||
* Equipment: add damage over time effect
|
|
||||||
* Menu: allow to delete cloud saves
|
|
||||||
* Fix cloud save games with "Level 0 - 0 ships"
|
|
||||||
* Arena: display effects description instead of attribute changes
|
|
||||||
* Arena: display radius for area effects (both on action hover, and while action is active)
|
|
||||||
* Arena: any displayed info should be based on a ship copy stored in ArenaShip, and in sync with current log index (not the game state ship)
|
|
||||||
* Arena: add engine trail
|
|
||||||
* Log processor: Cancel previous animations, and allow no animation mode
|
|
||||||
* Actions: fix targetting not resetting on current cursor location when using keyboard shortcuts
|
|
||||||
* Add actions with cost dependent of distance (like current move actions)
|
|
||||||
* Find incentives to move from starting position
|
|
||||||
* Outcome: disable the loot button if there is no loot
|
|
||||||
* Ensure that tweens and particle emitters get destroyed once animation is done (or view changes)
|
|
||||||
* UI: add standard confirm dialog
|
|
||||||
* Controls: do not focus on ship while targetting for area effects (dissociate hover and target)
|
|
||||||
* Controls: fix hover being stuck when the cursor exits the window, or the item moves or is hidden
|
|
||||||
* Drones: add hull points and take area damage
|
|
||||||
* Drones: find a way to avoid arena cluttering
|
|
||||||
* Drones: repair drone has its activation effect displayed as permanent effect on ships in the radius
|
|
||||||
* Add a battle log display
|
|
||||||
* Allow to undo last moves
|
|
||||||
* Merge identical sticky effects
|
|
||||||
* Allow to skip animations and AI delays in battle
|
|
||||||
* Hide enemy information (shield, hull, weapons), until they are in play, or until a "spy" effect is used
|
|
||||||
* Mobile: think UI layout so that fingers do not block the view (right and left handed)
|
|
||||||
* Mobile: display tooltips larger and on the side of screen where the finger is not
|
|
||||||
* Mobile: targetting in two times, using a draggable target indicator
|
|
||||||
* AI: use a first batch of producers, and only if no "good" move has been found, go on with some infinite producers
|
|
||||||
* AI: evaluate buffs/debuffs
|
|
||||||
* AI: abandon fight
|
|
||||||
* AI: add combination of random small move and actual maneuver, as producer
|
|
||||||
* AI: new duel page with producers/evaluators tweaking
|
|
||||||
* AI: work in a dedicated process
|
|
||||||
* Map: remove jump links that cross the radius of other systems
|
|
||||||
* Tutorial
|
|
||||||
* Campaign: Add ship personality (with icons to identify ?), with reaction to battle and map movements
|
|
||||||
* Campaign: Add factions and reputation
|
|
||||||
* Campaign: Missions/quests system
|
|
||||||
* Campaign: Main story arc
|
|
||||||
|
|
||||||
Equipment ideas:
|
|
||||||
* Shield with toggle effect that absorbs damage in an area
|
|
||||||
* Drive with minimal distance
|
|
||||||
* Cooldown drone or ability
|
|
||||||
|
|
||||||
Later, if possible:
|
|
||||||
* Animated arena background, instead of big picture
|
|
||||||
* Invocation/reinforcements (need to up the 10 ships limit)
|
|
||||||
* Dynamic music composition
|
|
||||||
* Replays
|
|
||||||
* Multiplayer/co-op
|
|
||||||
* Formation or deployment phase
|
|
101
TODO.md
Normal file
101
TODO.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
To-Do-list
|
||||||
|
==========
|
||||||
|
|
||||||
|
Menu/settings/saves
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* Allow to delete cloud saves
|
||||||
|
* Fix cloud save games with "Level 0 - 0 ships"
|
||||||
|
|
||||||
|
Map/story
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Add initial character creation
|
||||||
|
* Remove jump links that cross the radius of other systems
|
||||||
|
* Fix quickly zooming in twice preventing to display some UI parts
|
||||||
|
* Enemy fleet size should start low and increase with system level
|
||||||
|
* Allow to change/buy ship model
|
||||||
|
* Add ship personality (with icons to identify ?), with reaction dialogs
|
||||||
|
* Add factions and reputation
|
||||||
|
* Add generated missions with rewards
|
||||||
|
* Show missions' destination near systems/locations
|
||||||
|
|
||||||
|
Character sheet
|
||||||
|
---------------
|
||||||
|
|
||||||
|
* Disable interaction during battle (except for loot screen)
|
||||||
|
* Improve eye-catching for shop and loot section
|
||||||
|
* Highlight allowed destinations during drag-and-drop, with text hints
|
||||||
|
* When transferring to another ship, if the item can't be equipped (unmatched requirements), the transfer is cancelled instead of trying cargo
|
||||||
|
* Effective skill is sometimes not updated when upgrading base skill
|
||||||
|
* Tooltip to show the sources of attributes
|
||||||
|
* Fix ship list not refreshing when escorted ship is added or removed
|
||||||
|
* Forbid to modify escorted ship
|
||||||
|
* Add merged cargo display for the whole fleet
|
||||||
|
|
||||||
|
Battle
|
||||||
|
------
|
||||||
|
|
||||||
|
* Add a voluntary retreat option
|
||||||
|
* Display effects description instead of attribute changes
|
||||||
|
* Display radius for area effects (both on action hover, and while action is active)
|
||||||
|
* Any displayed info should be based on a ship copy stored in ArenaShip, and in sync with current log index (not the game state ship)
|
||||||
|
* Add engine trail effect, and sound
|
||||||
|
* Fix targetting not resetting on current cursor location when using keyboard shortcuts
|
||||||
|
* Allow to skip animations, and allow no animation mode
|
||||||
|
* Find incentives to move from starting position (permanent drones ?)
|
||||||
|
* Add a "loot all" button, disable the loot button if there is no loot
|
||||||
|
* Do not focus on ship while targetting for area effects (dissociate hover and target)
|
||||||
|
* Repair drone has its activation effect sometimes displayed as permanent effect on ships in the radius
|
||||||
|
* Merge identical sticky effects
|
||||||
|
* Allow to undo last moves
|
||||||
|
* Add a battle log display
|
||||||
|
|
||||||
|
Ships models and equipments
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
* Add permanent effects and actions to ship models
|
||||||
|
* Add critical hit/miss
|
||||||
|
* Add damage over time effect (tricky to make intuitive)
|
||||||
|
* Move distance should increase with maneuvrability
|
||||||
|
* Chance to hit should increase with precision
|
||||||
|
* Add actions with cost dependent of distance (like current move actions)
|
||||||
|
* Add hull points to drones and make them take area damage
|
||||||
|
|
||||||
|
Artificial Intelligence
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
* Use a first batch of producers, and only if no "good" move has been fo go on with some infinite producers
|
||||||
|
* Evaluate buffs/debuffs
|
||||||
|
* Abandon fight if the AI judges there is no hope of victory
|
||||||
|
* Add combination of random small move and actual maneuver, as prer
|
||||||
|
* New duel page with producers/evaluators tweaking
|
||||||
|
* Work in a dedicated process (webworker)
|
||||||
|
|
||||||
|
Technical
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Ensure that tweens and particle emitters get destroyed once animation is done (or view changes)
|
||||||
|
|
||||||
|
Common UI
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Fix hover being stuck when the cursor exits the window, or the item moves or is hidden
|
||||||
|
* Add a standard confirm dialog
|
||||||
|
* Mobile: think UI layout so that fingers do not block the view (right and left handed)
|
||||||
|
* Mobile: display tooltips larger and on the side of screen where the finger is not
|
||||||
|
* Mobile: targetting in two times, using a draggable target indicator
|
||||||
|
|
||||||
|
Postponed
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Tutorial
|
||||||
|
* Secondary story arcs
|
||||||
|
* Replays
|
||||||
|
* Multiplayer/co-op
|
||||||
|
* Formation or deployment phase
|
||||||
|
* New battle internal flow: any game state change should be done through revertable events
|
||||||
|
* Animated arena background, instead of big picture
|
||||||
|
* Hide enemy information (shield, hull, weapons), until they are in play, or until a "spy" effect is used
|
||||||
|
* Invocation/reinforcements (need to up the 10 ships limit)
|
||||||
|
* Dynamic music composition
|
|
@ -14,6 +14,39 @@ module TS.SpaceTac {
|
||||||
expect(fleet.getLevel()).toEqual(4);
|
expect(fleet.getLevel()).toEqual(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("adds and removes ships", function () {
|
||||||
|
let fleet1 = new Fleet();
|
||||||
|
let fleet2 = new Fleet();
|
||||||
|
|
||||||
|
let ship1 = fleet1.addShip();
|
||||||
|
expect(fleet1.ships).toEqual([ship1]);
|
||||||
|
expect(fleet2.ships).toEqual([]);
|
||||||
|
|
||||||
|
let ship2 = new Ship();
|
||||||
|
expect(fleet1.ships).toEqual([ship1]);
|
||||||
|
expect(fleet2.ships).toEqual([]);
|
||||||
|
|
||||||
|
fleet2.addShip(ship2);
|
||||||
|
expect(fleet1.ships).toEqual([ship1]);
|
||||||
|
expect(fleet2.ships).toEqual([ship2]);
|
||||||
|
|
||||||
|
fleet1.addShip(ship2);
|
||||||
|
expect(fleet1.ships).toEqual([ship1, ship2]);
|
||||||
|
expect(fleet2.ships).toEqual([]);
|
||||||
|
|
||||||
|
fleet1.removeShip(ship1, fleet2);
|
||||||
|
expect(fleet1.ships).toEqual([ship2]);
|
||||||
|
expect(fleet2.ships).toEqual([ship1]);
|
||||||
|
|
||||||
|
fleet1.removeShip(ship1);
|
||||||
|
expect(fleet1.ships).toEqual([ship2]);
|
||||||
|
expect(fleet2.ships).toEqual([ship1]);
|
||||||
|
|
||||||
|
fleet1.removeShip(ship2);
|
||||||
|
expect(fleet1.ships).toEqual([]);
|
||||||
|
expect(fleet2.ships).toEqual([ship1]);
|
||||||
|
});
|
||||||
|
|
||||||
it("changes location, only using jumps to travel between systems", function () {
|
it("changes location, only using jumps to travel between systems", function () {
|
||||||
let fleet = new Fleet();
|
let fleet = new Fleet();
|
||||||
let system1 = new Star();
|
let system1 = new Star();
|
||||||
|
@ -48,5 +81,32 @@ module TS.SpaceTac {
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
expect(fleet.location).toBe(jump1);
|
expect(fleet.location).toBe(jump1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("checks if a fleet is alive", function () {
|
||||||
|
let fleet = new Fleet();
|
||||||
|
expect(fleet.isAlive()).toBe(false);
|
||||||
|
|
||||||
|
let ship1 = fleet.addShip();
|
||||||
|
expect(fleet.isAlive()).toBe(true);
|
||||||
|
|
||||||
|
let ship2 = fleet.addShip();
|
||||||
|
expect(fleet.isAlive()).toBe(true);
|
||||||
|
|
||||||
|
ship1.setDead();
|
||||||
|
expect(fleet.isAlive()).toBe(true);
|
||||||
|
|
||||||
|
ship2.setDead();
|
||||||
|
expect(fleet.isAlive()).toBe(false);
|
||||||
|
|
||||||
|
let ship3 = fleet.addShip();
|
||||||
|
expect(fleet.isAlive()).toBe(true);
|
||||||
|
|
||||||
|
let ship4 = fleet.addShip();
|
||||||
|
ship4.critical = true;
|
||||||
|
expect(fleet.isAlive()).toBe(true);
|
||||||
|
|
||||||
|
ship4.setDead();
|
||||||
|
expect(fleet.isAlive()).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ module TS.SpaceTac {
|
||||||
this.ships = [];
|
this.ships = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jasmineToString(): string {
|
||||||
|
return `${this.player.name}'s fleet [${this.ships.map(ship => ship.name).join(",")}]`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current location of the fleet
|
* Set the current location of the fleet
|
||||||
*
|
*
|
||||||
|
@ -48,7 +52,9 @@ module TS.SpaceTac {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a ship in this fleet
|
/**
|
||||||
|
* Add a ship this fleet
|
||||||
|
*/
|
||||||
addShip(ship = new Ship()): Ship {
|
addShip(ship = new Ship()): Ship {
|
||||||
if (ship.fleet && ship.fleet != this) {
|
if (ship.fleet && ship.fleet != this) {
|
||||||
remove(ship.fleet.ships, ship);
|
remove(ship.fleet.ships, ship);
|
||||||
|
@ -58,6 +64,15 @@ module TS.SpaceTac {
|
||||||
return ship;
|
return ship;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the ship from this fleet, transferring it to another fleet
|
||||||
|
*/
|
||||||
|
removeShip(ship: Ship, fleet = new Fleet()): void {
|
||||||
|
if (ship.fleet === this) {
|
||||||
|
fleet.addShip(ship);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the current battle
|
// Set the current battle
|
||||||
setBattle(battle: Battle | null): void {
|
setBattle(battle: Battle | null): void {
|
||||||
this.battle = battle;
|
this.battle = battle;
|
||||||
|
@ -77,15 +92,15 @@ module TS.SpaceTac {
|
||||||
return Math.floor(avg);
|
return Math.floor(avg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the fleet still has living ships
|
/**
|
||||||
|
* Check if the fleet is considered alive (at least one ship alive, and no critical ship dead)
|
||||||
|
*/
|
||||||
isAlive(): boolean {
|
isAlive(): boolean {
|
||||||
var count = 0;
|
if (any(this.ships, ship => ship.critical && !ship.alive)) {
|
||||||
this.ships.forEach((ship: Ship) => {
|
return false;
|
||||||
if (ship.alive) {
|
} else {
|
||||||
count += 1;
|
return any(this.ships, ship => ship.alive);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return (count > 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ module TS.SpaceTac.Specs {
|
||||||
expect(session.start_location.encounter_gen).toBe(true);
|
expect(session.start_location.encounter_gen).toBe(true);
|
||||||
|
|
||||||
session.setCampaignFleet();
|
session.setCampaignFleet();
|
||||||
expect(session.player.fleet.ships.length).toBe(4);
|
expect(session.player.fleet.ships.length).toBe(2);
|
||||||
expect(session.player.fleet.credits).toBe(500);
|
expect(session.player.fleet.credits).toBe(500);
|
||||||
expect(session.player.fleet.location).toBe(session.start_location);
|
expect(session.player.fleet.location).toBe(session.start_location);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ module TS.SpaceTac {
|
||||||
* This represents the current state of game
|
* This represents the current state of game
|
||||||
*/
|
*/
|
||||||
export class GameSession {
|
export class GameSession {
|
||||||
// "Hopefully"" unique session id
|
// "Hopefully" unique session id
|
||||||
id: string
|
id: string
|
||||||
|
|
||||||
// Game universe
|
// Game universe
|
||||||
|
@ -81,7 +81,7 @@ module TS.SpaceTac {
|
||||||
this.player.fleet = fleet;
|
this.player.fleet = fleet;
|
||||||
} else {
|
} else {
|
||||||
let fleet_generator = new FleetGenerator();
|
let fleet_generator = new FleetGenerator();
|
||||||
this.player.fleet = fleet_generator.generate(1, this.player, 4);
|
this.player.fleet = fleet_generator.generate(1, this.player, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.player.fleet.setLocation(this.start_location);
|
this.player.fleet.setLocation(this.start_location);
|
||||||
|
|
|
@ -70,6 +70,9 @@ module TS.SpaceTac {
|
||||||
// Flag indicating if the ship is alive
|
// Flag indicating if the ship is alive
|
||||||
alive: boolean
|
alive: boolean
|
||||||
|
|
||||||
|
// Flag indicating that the ship is mission critical (escorted ship)
|
||||||
|
critical = false
|
||||||
|
|
||||||
// Position in the arena
|
// Position in the arena
|
||||||
arena_x: number
|
arena_x: number
|
||||||
arena_y: number
|
arena_y: number
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
module TS.SpaceTac.Specs {
|
module TS.SpaceTac.Specs {
|
||||||
describe("MainStory", () => {
|
describe("MainStory", () => {
|
||||||
function checkPart(story: Mission, index: number, title: string) {
|
function checkPart(story: Mission, index: number, title: string, completed = false) {
|
||||||
|
let result = story.checkStatus();
|
||||||
expect(story.parts.indexOf(story.current_part)).toBe(index);
|
expect(story.parts.indexOf(story.current_part)).toBe(index);
|
||||||
expect(story.current_part.title).toMatch(title);
|
expect(story.current_part.title).toMatch(title);
|
||||||
expect(story.completed).toBe(false);
|
expect(story.completed).toBe(completed);
|
||||||
|
expect(result).toBe(!completed);
|
||||||
}
|
}
|
||||||
|
|
||||||
function goTo(fleet: Fleet, location: StarLocation, win_encounter = true) {
|
function goTo(fleet: Fleet, location: StarLocation, win_encounter = true) {
|
||||||
|
@ -25,12 +27,21 @@ module TS.SpaceTac.Specs {
|
||||||
|
|
||||||
let missions = session.player.missions;
|
let missions = session.player.missions;
|
||||||
let story = nn(missions.main);
|
let story = nn(missions.main);
|
||||||
checkPart(story, 0, "^Find your contact in .* system$");
|
let fleet_size = fleet.ships.length;
|
||||||
|
|
||||||
goTo(fleet, (<MissionPartGoTo>story.current_part).destination);
|
|
||||||
checkPart(story, 1, "^Speak with your contact .*$");
|
|
||||||
|
|
||||||
|
checkPart(story, 0, "^Travel to Terranax galaxy$");
|
||||||
(<MissionPartDialog>story.current_part).skip();
|
(<MissionPartDialog>story.current_part).skip();
|
||||||
|
|
||||||
|
checkPart(story, 1, "^Find your contact in .*$");
|
||||||
|
goTo(fleet, (<MissionPartGoTo>story.current_part).destination);
|
||||||
|
|
||||||
|
checkPart(story, 2, "^Speak with your contact");
|
||||||
|
(<MissionPartDialog>story.current_part).skip();
|
||||||
|
|
||||||
|
checkPart(story, 3, "^Go with .* in .* system$");
|
||||||
|
expect(fleet.ships.length).toBe(fleet_size + 1);
|
||||||
|
goTo(fleet, (<MissionPartEscort>story.current_part).destination);
|
||||||
|
|
||||||
expect(story.checkStatus()).toBe(false, "story not complete");
|
expect(story.checkStatus()).toBe(false, "story not complete");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,12 +16,18 @@ module TS.SpaceTac {
|
||||||
let random = RandomGenerator.global;
|
let random = RandomGenerator.global;
|
||||||
let start_location = nn(fleet.location);
|
let start_location = nn(fleet.location);
|
||||||
|
|
||||||
|
// Arrival
|
||||||
|
let dialog = this.addPart(new MissionPartDialog(this, [], "Travel to Terranax galaxy"));
|
||||||
|
dialog.addPiece(null, "Wow ! From what my sensors tell me, there is not much activity around here.");
|
||||||
|
dialog.addPiece(null, "I remember the last time I came in this galaxy, you needed to be aware of collisions at all time, so crowded it was.");
|
||||||
|
dialog.addPiece(null, "Well...I did not pick a signal from our contact yet. We should be looking for her in this system.");
|
||||||
|
|
||||||
// Get in touch with our contact
|
// Get in touch with our contact
|
||||||
let contact_location = randomLocation(random, [start_location.star], [start_location]);
|
let contact_location = randomLocation(random, [start_location.star], [start_location]);
|
||||||
let contact_character = new Ship(null, "Osten-37", ShipModel.getRandomModel(1, random));
|
let contact_character = new Ship(null, "Osten-37", ShipModel.getRandomModel(1, random));
|
||||||
contact_character.fleet.setLocation(contact_location, true);
|
contact_character.fleet.setLocation(contact_location, true);
|
||||||
this.addPart(new MissionPartGoTo(this, contact_location, "Find your contact"));
|
this.addPart(new MissionPartGoTo(this, contact_location, `Find your contact in ${contact_location.star.name}`));
|
||||||
let dialog = this.addPart(new MissionPartDialog(this, [contact_character], "Speak with your contact"));
|
dialog = this.addPart(new MissionPartDialog(this, [contact_character], "Speak with your contact"));
|
||||||
dialog.addPiece(contact_character, "Finally, you came!");
|
dialog.addPiece(contact_character, "Finally, you came!");
|
||||||
dialog.addPiece(contact_character, "Sorry for not broadcasting my position. As you may have encountered, this star system is not safe anymore.");
|
dialog.addPiece(contact_character, "Sorry for not broadcasting my position. As you may have encountered, this star system is not safe anymore.");
|
||||||
dialog.addPiece(null, "Nothing we could not handle, we just hope the other teams have not run across more trouble.");
|
dialog.addPiece(null, "Nothing we could not handle, we just hope the other teams have not run across more trouble.");
|
||||||
|
@ -32,6 +38,11 @@ module TS.SpaceTac {
|
||||||
dialog.addPiece(contact_character, "Yes, some merchants and miners have rallied behind a retired TSF general, but I lost contact with them weeks ago.");
|
dialog.addPiece(contact_character, "Yes, some merchants and miners have rallied behind a retired TSF general, but I lost contact with them weeks ago.");
|
||||||
dialog.addPiece(contact_character, "We may go to their last known location, but first I want you to see something in a nearby system.");
|
dialog.addPiece(contact_character, "We may go to their last known location, but first I want you to see something in a nearby system.");
|
||||||
dialog.addPiece(null, "Ok, let's go...");
|
dialog.addPiece(null, "Ok, let's go...");
|
||||||
|
|
||||||
|
// Go take a look at the graveyard
|
||||||
|
let nearby_systems = nna(start_location.star.getLinks().map(link => link.getPeer(contact_location.star)));
|
||||||
|
let graveyard_location = randomLocation(random, [minBy(nearby_systems, system => system.level)]);
|
||||||
|
this.addPart(new MissionPartEscort(this, graveyard_location, contact_character, `Go with ${contact_character.name} in ${graveyard_location.star.name} system`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,12 +53,15 @@ module TS.SpaceTac {
|
||||||
if (this.completed) {
|
if (this.completed) {
|
||||||
return false;
|
return false;
|
||||||
} else if (this.current_part.checkCompleted()) {
|
} else if (this.current_part.checkCompleted()) {
|
||||||
|
this.current_part.onEnded();
|
||||||
|
|
||||||
let current_index = this.parts.indexOf(this.current_part);
|
let current_index = this.parts.indexOf(this.current_part);
|
||||||
if (current_index < 0 || current_index >= this.parts.length - 1) {
|
if (current_index < 0 || current_index >= this.parts.length - 1) {
|
||||||
this.completed = true;
|
this.completed = true;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
this.current_part = this.parts[current_index + 1];
|
this.current_part = this.parts[current_index + 1];
|
||||||
|
this.current_part.onStarted();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -35,5 +35,17 @@ module TS.SpaceTac {
|
||||||
*/
|
*/
|
||||||
forceComplete(): void {
|
forceComplete(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event called when the part starts
|
||||||
|
*/
|
||||||
|
onStarted(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event called when the part ends
|
||||||
|
*/
|
||||||
|
onEnded(): void {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
module TS.SpaceTac.Specs {
|
module TS.SpaceTac.Specs {
|
||||||
describe("MissionPartGoTo", () => {
|
describe("MissionPartDialog", () => {
|
||||||
it("advances through dialog", function () {
|
it("advances through dialog", function () {
|
||||||
let universe = new Universe();
|
let universe = new Universe();
|
||||||
let fleet = new Fleet();
|
let fleet = new Fleet();
|
||||||
let ship1 = new Ship(null, "Tim");
|
let ship1 = new Ship(null, "Tim");
|
||||||
let ship2 = new Ship(null, "Ben");
|
let ship2 = new Ship(null, "Ben");
|
||||||
let part = new MissionPartDialog(new Mission(universe, fleet), [ship1, ship2], "Talk to");
|
let part = new MissionPartDialog(new Mission(universe, fleet), [ship1, ship2], "Talk to Tim");
|
||||||
|
|
||||||
expect(part.title).toEqual("Talk to Tim");
|
expect(part.title).toEqual("Talk to Tim");
|
||||||
expect(part.checkCompleted()).toBe(true, "No dialog piece");
|
expect(part.checkCompleted()).toBe(true, "No dialog piece");
|
||||||
|
|
|
@ -25,8 +25,8 @@ module TS.SpaceTac {
|
||||||
// Current piece
|
// Current piece
|
||||||
current_piece = 0
|
current_piece = 0
|
||||||
|
|
||||||
constructor(mission: Mission, interlocutors: Ship[], directive = "Speak with") {
|
constructor(mission: Mission, interlocutors: Ship[], directive?: string) {
|
||||||
super(mission, `${directive} ${interlocutors[0].name}`);
|
super(mission, directive || `Speak with ${interlocutors[0].name}`);
|
||||||
|
|
||||||
this.interlocutors = interlocutors;
|
this.interlocutors = interlocutors;
|
||||||
}
|
}
|
||||||
|
|
67
src/core/missions/MissionPartEscort.spec.ts
Normal file
67
src/core/missions/MissionPartEscort.spec.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
module TS.SpaceTac.Specs {
|
||||||
|
describe("MissionPartEscort", () => {
|
||||||
|
it("completes when the fleet is at location, with its escort", function () {
|
||||||
|
let destination = new StarLocation(new Star(null, 0, 0, "Atanax"));
|
||||||
|
destination.encounter_random = new SkewedRandomGenerator([0], true);
|
||||||
|
|
||||||
|
let universe = new Universe();
|
||||||
|
let fleet = new Fleet();
|
||||||
|
let ship = new Ship(null, "Zybux");
|
||||||
|
let part = new MissionPartEscort(new Mission(universe, fleet), destination, ship);
|
||||||
|
|
||||||
|
expect(fleet.ships).not.toContain(ship);
|
||||||
|
expect(part.title).toEqual("Escort Zybux to Atanax system");
|
||||||
|
expect(part.checkCompleted()).toBe(false, "Init location");
|
||||||
|
|
||||||
|
part.onStarted();
|
||||||
|
expect(fleet.ships).toContain(ship);
|
||||||
|
|
||||||
|
fleet.setLocation(destination, true);
|
||||||
|
expect(part.checkCompleted()).toBe(false, "Encounter not clear");
|
||||||
|
|
||||||
|
destination.clearEncounter();
|
||||||
|
expect(part.checkCompleted()).toBe(true, "Encouter cleared");
|
||||||
|
|
||||||
|
fleet.setLocation(new StarLocation(), true);
|
||||||
|
expect(part.checkCompleted()).toBe(false, "Went to another system");
|
||||||
|
|
||||||
|
fleet.setLocation(destination, true);
|
||||||
|
expect(part.checkCompleted()).toBe(true, "Back at destination");
|
||||||
|
expect(fleet.ships).toContain(ship);
|
||||||
|
|
||||||
|
part.onEnded();
|
||||||
|
expect(fleet.ships).not.toContain(ship);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("sets the escorted ship as critical in battles", function () {
|
||||||
|
let universe = new Universe();
|
||||||
|
let fleet = new Fleet();
|
||||||
|
let ship1 = fleet.addShip();
|
||||||
|
let ship2 = fleet.addShip();
|
||||||
|
let ship = new Ship();
|
||||||
|
let destination = new StarLocation(new Star());
|
||||||
|
let part = new MissionPartEscort(new Mission(universe, fleet), destination, ship);
|
||||||
|
|
||||||
|
part.onStarted();
|
||||||
|
expect(fleet.ships).toContain(ship);
|
||||||
|
|
||||||
|
let enemy = new Fleet();
|
||||||
|
enemy.addShip();
|
||||||
|
let battle = new Battle(fleet, enemy);
|
||||||
|
battle.start();
|
||||||
|
battle.checkEndBattle();
|
||||||
|
expect(battle.ended).toBe(false);
|
||||||
|
|
||||||
|
// if a fleet member dies, it is not over
|
||||||
|
ship1.setDead();
|
||||||
|
battle.checkEndBattle();
|
||||||
|
expect(battle.ended).toBe(false);
|
||||||
|
|
||||||
|
// if the critical ship dies, it is defeat
|
||||||
|
ship.setDead();
|
||||||
|
battle.checkEndBattle();
|
||||||
|
expect(battle.ended).toBe(true);
|
||||||
|
expect(battle.outcome.winner).not.toBe(fleet);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
29
src/core/missions/MissionPartEscort.ts
Normal file
29
src/core/missions/MissionPartEscort.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/// <reference path="MissionPartGoTo.ts" />
|
||||||
|
|
||||||
|
module TS.SpaceTac {
|
||||||
|
/**
|
||||||
|
* A mission part that requires the fleet to escort a specific ship to a destination
|
||||||
|
*/
|
||||||
|
export class MissionPartEscort extends MissionPartGoTo {
|
||||||
|
ship: Ship
|
||||||
|
|
||||||
|
constructor(mission: Mission, destination: StarLocation, ship: Ship, directive?: string) {
|
||||||
|
super(mission, destination, directive || `Escort ${ship.name} to ${destination.star.name} system`);
|
||||||
|
|
||||||
|
this.ship = ship;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCompleted(): boolean {
|
||||||
|
return super.checkCompleted() && contains(this.fleet.ships, this.ship);
|
||||||
|
}
|
||||||
|
|
||||||
|
onStarted(): void {
|
||||||
|
this.ship.critical = true;
|
||||||
|
this.fleet.addShip(this.ship);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnded(): void {
|
||||||
|
this.fleet.removeShip(this.ship);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,9 @@ module TS.SpaceTac.Specs {
|
||||||
|
|
||||||
let universe = new Universe();
|
let universe = new Universe();
|
||||||
let fleet = new Fleet();
|
let fleet = new Fleet();
|
||||||
let part = new MissionPartGoTo(new Mission(universe, fleet), destination, "Collect gems");
|
let part = new MissionPartGoTo(new Mission(universe, fleet), destination);
|
||||||
|
|
||||||
expect(part.title).toEqual("Collect gems in Atanax system");
|
expect(part.title).toEqual("Go to Atanax system");
|
||||||
expect(part.checkCompleted()).toBe(false, "Init location");
|
expect(part.checkCompleted()).toBe(false, "Init location");
|
||||||
|
|
||||||
fleet.setLocation(destination, true);
|
fleet.setLocation(destination, true);
|
||||||
|
@ -32,6 +32,7 @@ module TS.SpaceTac.Specs {
|
||||||
let fleet = new Fleet();
|
let fleet = new Fleet();
|
||||||
let part = new MissionPartGoTo(new Mission(universe, fleet), destination, "Investigate");
|
let part = new MissionPartGoTo(new Mission(universe, fleet), destination, "Investigate");
|
||||||
|
|
||||||
|
expect(part.title).toEqual("Investigate");
|
||||||
expect(part.checkCompleted()).toBe(false);
|
expect(part.checkCompleted()).toBe(false);
|
||||||
part.forceComplete();
|
part.forceComplete();
|
||||||
expect(part.checkCompleted()).toBe(true);
|
expect(part.checkCompleted()).toBe(true);
|
||||||
|
|
|
@ -7,8 +7,8 @@ module TS.SpaceTac {
|
||||||
export class MissionPartGoTo extends MissionPart {
|
export class MissionPartGoTo extends MissionPart {
|
||||||
destination: StarLocation
|
destination: StarLocation
|
||||||
|
|
||||||
constructor(mission: Mission, destination: StarLocation, directive: string, hint = true) {
|
constructor(mission: Mission, destination: StarLocation, directive?: string) {
|
||||||
super(mission, hint ? `${directive} in ${destination.star.name} system` : directive);
|
super(mission, directive || `Go to ${destination.star.name} system`);
|
||||||
|
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,9 @@ module TS.SpaceTac.UI {
|
||||||
this.credits.setText(fleet.credits.toString());
|
this.credits.setText(fleet.credits.toString());
|
||||||
|
|
||||||
this.portraits.scale.set(980 * this.portraits.scale.x / this.portraits.height, 980 * this.portraits.scale.y / this.portraits.height);
|
this.portraits.scale.set(980 * this.portraits.scale.x / this.portraits.height, 980 * this.portraits.scale.y / this.portraits.height);
|
||||||
|
if (this.portraits.width > 308) {
|
||||||
|
this.portraits.scale.set(308 * this.portraits.scale.x / this.portraits.width, 308 * this.portraits.scale.y / this.portraits.width);
|
||||||
|
}
|
||||||
this.portraits.y = 80 + 160 * this.portraits.scale.x;
|
this.portraits.y = 80 + 160 * this.portraits.scale.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue