Added initial character creation screen
This commit is contained in:
parent
867dd54f10
commit
ee17c37a74
11
TODO.md
11
TODO.md
|
@ -12,10 +12,8 @@ Menu/settings/saves
|
|||
Map/story
|
||||
---------
|
||||
|
||||
* Add initial character creation
|
||||
* Add sound effects and more visual effects (jumps...)
|
||||
* Fix quickly zooming in twice preventing to display some UI parts
|
||||
* Allow to change/buy ship model
|
||||
* Add factions and reputation
|
||||
* Allow to cancel secondary missions
|
||||
* Forbid to end up with more than 5 ships in the fleet because of escorts
|
||||
|
@ -27,14 +25,20 @@ 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
|
||||
* Highlight allowed destinations during drag-and-drop, with text hints (for success or error)
|
||||
* 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
|
||||
* Add merged cargo display for the whole fleet
|
||||
* Allow to change/buy ship model
|
||||
* Add personality indicators (editable in creation view)
|
||||
* Allow to cancel spent skill points (and confirm when closing the sheet)
|
||||
* Add filters and sort options for cargo and shop
|
||||
* Display level and slot type on equipment
|
||||
|
||||
Battle
|
||||
------
|
||||
|
||||
* Fix arena's ship hovering happening even when the character sheet is open on top
|
||||
* Add a voluntary retreat option
|
||||
* Add scroll buttons when there are too many actions
|
||||
* Remove dead ships from ship list and play order
|
||||
|
@ -90,7 +94,6 @@ Common UI
|
|||
---------
|
||||
|
||||
* Add caret/focus to text input
|
||||
* 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
|
||||
|
|
|
@ -43,6 +43,7 @@ module TK.SpaceTac {
|
|||
this.state.add('router', UI.Router);
|
||||
this.state.add('battle', UI.BattleView);
|
||||
this.state.add('intro', UI.IntroView);
|
||||
this.state.add('creation', UI.FleetCreationView);
|
||||
this.state.add('universe', UI.UniverseMapView);
|
||||
|
||||
this.state.start('boot');
|
||||
|
@ -64,6 +65,14 @@ module TK.SpaceTac {
|
|||
this.options = new UI.GameOptions(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the game session
|
||||
*/
|
||||
resetSession(): void {
|
||||
this.session = new GameSession();
|
||||
this.session_token = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a popup message in current view
|
||||
*/
|
||||
|
@ -78,8 +87,7 @@ module TK.SpaceTac {
|
|||
* Quit the current session, and go back to mainmenu
|
||||
*/
|
||||
quitGame() {
|
||||
this.session = new GameSession();
|
||||
this.session_token = null;
|
||||
this.resetSession();
|
||||
this.state.start('router');
|
||||
}
|
||||
|
||||
|
|
|
@ -80,14 +80,13 @@ module TK.SpaceTac.Specs {
|
|||
expect(session.player.fleet.credits).toBe(0);
|
||||
expect(session.player.universe.stars.length).toBe(50);
|
||||
expect(session.getBattle()).toBeNull();
|
||||
expect(session.start_location.shop).not.toBeNull();
|
||||
expect(nn(session.start_location.shop).getStock().length).toBeGreaterThan(20);
|
||||
expect(session.start_location.shop).toBeNull();
|
||||
expect(session.start_location.encounter).toBeNull();
|
||||
expect(session.start_location.encounter_gen).toBe(true);
|
||||
|
||||
session.setCampaignFleet();
|
||||
expect(session.player.fleet.ships.length).toBe(2);
|
||||
expect(session.player.fleet.credits).toBe(500);
|
||||
expect(session.player.fleet.credits).toBe(0);
|
||||
expect(session.player.fleet.location).toBe(session.start_location);
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ module TK.SpaceTac {
|
|||
// Indicator of spectator mode
|
||||
spectator = false
|
||||
|
||||
// Indicator that introduction has been watched
|
||||
introduced = false
|
||||
|
||||
constructor() {
|
||||
this.id = RandomGenerator.global.id(20);
|
||||
this.universe = new Universe();
|
||||
|
@ -66,7 +69,7 @@ module TK.SpaceTac {
|
|||
|
||||
this.start_location = this.universe.getStartLocation();
|
||||
this.start_location.clearEncounter();
|
||||
this.start_location.addShop();
|
||||
this.start_location.removeShop();
|
||||
|
||||
this.player = new Player(this.universe);
|
||||
|
||||
|
@ -85,13 +88,13 @@ module TK.SpaceTac {
|
|||
setCampaignFleet(fleet: Fleet | null = null, story = true) {
|
||||
if (fleet) {
|
||||
this.player.fleet = fleet;
|
||||
fleet.player = this.player;
|
||||
} else {
|
||||
let fleet_generator = new FleetGenerator();
|
||||
this.player.fleet = fleet_generator.generate(1, this.player, 2);
|
||||
}
|
||||
|
||||
this.player.fleet.setLocation(this.start_location);
|
||||
this.player.fleet.credits = 500;
|
||||
this.player.fleet.setLocation(this.start_location, true);
|
||||
|
||||
if (story) {
|
||||
this.player.missions.startMainStory(this.universe, this.player.fleet);
|
||||
|
@ -135,7 +138,7 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the session has a universe to explore
|
||||
* Returns true if the session has an universe to explore (campaign mode)
|
||||
*/
|
||||
hasUniverse(): boolean {
|
||||
return this.universe.stars.length > 0;
|
||||
|
@ -147,5 +150,12 @@ module TK.SpaceTac {
|
|||
isFleetCreated(): boolean {
|
||||
return this.player.fleet.ships.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if campaign introduction has been watched
|
||||
*/
|
||||
isIntroViewed(): boolean {
|
||||
return this.introduced;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
src/core/ShipModel.spec.ts
Normal file
21
src/core/ShipModel.spec.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
module TK.SpaceTac.Specs {
|
||||
describe("ShipModel", () => {
|
||||
it("picks random models from default collection", function () {
|
||||
spyOn(console, "error").and.stub();
|
||||
spyOn(ShipModel, "getDefaultCollection").and.returnValues(
|
||||
[new ShipModel("a")],
|
||||
[],
|
||||
[new ShipModel("a"), new ShipModel("b")],
|
||||
[new ShipModel("a")],
|
||||
[],
|
||||
);
|
||||
|
||||
expect(ShipModel.getRandomModel()).toEqual(new ShipModel("a"), "pick from a one-item list");
|
||||
expect(ShipModel.getRandomModel()).toEqual(new ShipModel(), "pick from an empty list");
|
||||
|
||||
expect(sorted(ShipModel.getRandomModels(2), (a, b) => cmp(a.code, b.code))).toEqual([new ShipModel("a"), new ShipModel("b")], "sample from good-sized list");
|
||||
expect(ShipModel.getRandomModels(2)).toEqual([new ShipModel("a"), new ShipModel("a")], "sample from too small list");
|
||||
expect(ShipModel.getRandomModels(2)).toEqual([new ShipModel(), new ShipModel()], "sample from empty list");
|
||||
});
|
||||
});
|
||||
}
|
|
@ -17,7 +17,7 @@ module TK.SpaceTac {
|
|||
// Available slots
|
||||
slots: SlotType[];
|
||||
|
||||
constructor(code: string, name: string, level = 1, cargo = 6, default_slots = true, weapon_slots = 2) {
|
||||
constructor(code = "unknown", name = "Unknown", level = 1, cargo = 6, default_slots = true, weapon_slots = 2) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.level = level;
|
||||
|
@ -51,7 +51,7 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Pick a random model in the default collection
|
||||
*/
|
||||
static getRandomModel(level?: number, random: RandomGenerator = new RandomGenerator()): ShipModel {
|
||||
static getRandomModel(level?: number, random = RandomGenerator.global): ShipModel {
|
||||
let collection = this.getDefaultCollection();
|
||||
if (level) {
|
||||
collection = collection.filter(model => model.level <= level);
|
||||
|
@ -59,10 +59,35 @@ module TK.SpaceTac {
|
|||
|
||||
if (collection.length == 0) {
|
||||
console.error("Couldn't pick a random model");
|
||||
return new ShipModel("undefined", "Undefined");
|
||||
return new ShipModel();
|
||||
} else {
|
||||
return random.choice(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick random models in the default collection
|
||||
*
|
||||
* At first it tries to pick unique models, then fill with duplicates
|
||||
*/
|
||||
static getRandomModels(count: number, level?: number, random = RandomGenerator.global): ShipModel[] {
|
||||
let collection = this.getDefaultCollection();
|
||||
if (level) {
|
||||
collection = collection.filter(model => model.level <= level);
|
||||
}
|
||||
|
||||
if (collection.length == 0) {
|
||||
console.error("Couldn't pick a random model");
|
||||
return range(count).map(() => new ShipModel());
|
||||
} else {
|
||||
let result: ShipModel[] = [];
|
||||
while (count > 0) {
|
||||
let picked = random.sample(collection, Math.min(count, collection.length));
|
||||
result = result.concat(picked);
|
||||
count -= picked.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,28 +7,27 @@ module TK.SpaceTac.Specs {
|
|||
});
|
||||
|
||||
it("buys and sells items", function () {
|
||||
let shop = <any>new Shop();
|
||||
let equ1 = new Equipment(SlotType.Shield, "shield");
|
||||
equ1.price = 50;
|
||||
let equ2 = new Equipment(SlotType.Hull, "hull");
|
||||
equ2.price = 150;
|
||||
shop.stock = [equ1, equ2];
|
||||
let shop = new Shop(1, [equ1, equ2], 0);
|
||||
let fleet = new Fleet();
|
||||
fleet.credits = 1000;
|
||||
spyOn(shop, "getPrice").and.returnValue(800);
|
||||
|
||||
let result = shop.sellToFleet(equ1, fleet);
|
||||
expect(result).toBe(true);
|
||||
expect(shop.stock).toEqual([equ2]);
|
||||
expect(shop.getStock()).toEqual([equ2]);
|
||||
expect(fleet.credits).toEqual(200);
|
||||
result = shop.sellToFleet(equ2, fleet);
|
||||
expect(result).toBe(false);
|
||||
expect(shop.stock).toEqual([equ2]);
|
||||
expect(shop.getStock()).toEqual([equ2]);
|
||||
expect(fleet.credits).toEqual(200);
|
||||
|
||||
result = shop.buyFromFleet(equ1, fleet);
|
||||
expect(result).toBe(true);
|
||||
expect(shop.stock).toEqual([equ1, equ2]);
|
||||
expect(shop.getStock()).toEqual([equ1, equ2]);
|
||||
expect(fleet.credits).toEqual(1000);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
module TK.SpaceTac {
|
||||
type ShopStockCallback = (stock: Equipment[]) => Equipment[]
|
||||
|
||||
/**
|
||||
* A shop is a place to buy/sell equipments
|
||||
*/
|
||||
|
@ -18,24 +20,27 @@ module TK.SpaceTac {
|
|||
// Available missions
|
||||
private missions: Mission[] = []
|
||||
|
||||
constructor(level = 1, stock: Equipment[] = [], count = 40) {
|
||||
// Callback when the equipment changes
|
||||
private onchange: ShopStockCallback
|
||||
|
||||
constructor(level = 1, stock: Equipment[] = [], count = 40, onchange?: ShopStockCallback) {
|
||||
this.level = level;
|
||||
this.stock = stock;
|
||||
this.count = count;
|
||||
this.random = new RandomGenerator();
|
||||
this.onchange = onchange || (stock => stock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available stock to display
|
||||
* Get available stock to display (sorted by level then price by default)
|
||||
*/
|
||||
getStock() {
|
||||
if (this.stock.length < this.count * 0.5) {
|
||||
let count = this.random.randInt(Math.floor(this.count * 0.8), Math.ceil(this.count * 1.2));
|
||||
this.stock = this.stock.concat(this.generateStock(count - this.stock.length, this.level, this.random));
|
||||
this.sortStock();
|
||||
}
|
||||
|
||||
return this.stock;
|
||||
return sorted(this.stock, (a, b) => (a.level == b.level) ? cmp(a.getPrice(), b.getPrice()) : cmp(a.level, b.level));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,10 +59,10 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sort the stock by equipment level, then by value
|
||||
* Update the stock after a buying or selling occured
|
||||
*/
|
||||
sortStock() {
|
||||
this.stock.sort((a, b) => (a.level == b.level) ? cmp(a.getPrice(), b.getPrice()) : cmp(a.level, b.level));
|
||||
refreshStock() {
|
||||
this.stock = this.onchange(this.stock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,6 +81,7 @@ module TK.SpaceTac {
|
|||
let price = this.getPrice(equipment);
|
||||
if (price <= fleet.credits) {
|
||||
if (remove(this.stock, equipment)) {
|
||||
this.refreshStock();
|
||||
fleet.credits -= price;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -94,7 +100,7 @@ module TK.SpaceTac {
|
|||
buyFromFleet(equipment: Equipment, fleet: Fleet) {
|
||||
let price = this.getPrice(equipment);
|
||||
if (add(this.stock, equipment)) {
|
||||
this.sortStock();
|
||||
this.refreshStock();
|
||||
fleet.credits += price;
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -51,6 +51,13 @@ module TK.SpaceTac {
|
|||
this.shop = new Shop(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a potential shop in this location
|
||||
*/
|
||||
removeShop(): void {
|
||||
this.shop = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the location is clear of encounter
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ module TK.SpaceTac.UI {
|
|||
|
||||
// Modal dialogs
|
||||
dialogs_layer: Phaser.Group
|
||||
dialogs_opened: UIDialog[] = []
|
||||
|
||||
// Get the size of display
|
||||
getWidth(): number {
|
||||
|
@ -81,6 +82,8 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
shutdown() {
|
||||
this.audio.stopMusic();
|
||||
|
||||
super.shutdown();
|
||||
|
||||
this.timer.cancelAll(true);
|
||||
|
|
|
@ -6,8 +6,10 @@ module TK.SpaceTac.UI {
|
|||
*/
|
||||
export class Boot extends Phaser.State {
|
||||
preload() {
|
||||
this.load.image("preload-background", "assets/images/preload/bar-background.png");
|
||||
this.load.image("preload-bar", "assets/images/preload/bar-content.png");
|
||||
if (!(<MainUI>this.game).headless) {
|
||||
this.load.image("preload-background", "assets/images/preload/bar-background.png");
|
||||
this.load.image("preload-bar", "assets/images/preload/bar-content.png");
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
|
@ -11,21 +11,23 @@ module TK.SpaceTac.UI {
|
|||
var ui = <MainUI>this.game;
|
||||
var session = ui.session;
|
||||
|
||||
if (!session) {
|
||||
// No session, go back to main menu
|
||||
this.goToState("mainmenu", AssetLoadingRange.MENU);
|
||||
} else if (session.getBattle()) {
|
||||
if (session.getBattle()) {
|
||||
// A battle is raging, go to it
|
||||
this.goToState("battle", AssetLoadingRange.BATTLE, session.player, session.getBattle());
|
||||
} else if (session.hasUniverse()) {
|
||||
// Campaign mode
|
||||
if (session.isFleetCreated()) {
|
||||
// Go to the universe map
|
||||
this.goToState("universe", AssetLoadingRange.CAMPAIGN, session.universe, session.player);
|
||||
} else if (session.isIntroViewed()) {
|
||||
// Build initial fleet
|
||||
this.goToState("creation", AssetLoadingRange.CAMPAIGN);
|
||||
} else {
|
||||
// Show intro
|
||||
this.goToState("intro", AssetLoadingRange.CAMPAIGN);
|
||||
}
|
||||
} else {
|
||||
// No battle, no universe, go back to menu
|
||||
// No battle, no campaign, go back to menu to decide what to do
|
||||
this.goToState("mainmenu", AssetLoadingRange.MENU);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ module TK.SpaceTac.UI.Specs {
|
|||
ui: MainUI;
|
||||
view: T;
|
||||
multistorage: Multi.FakeRemoteStorage;
|
||||
state: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,6 +35,7 @@ module TK.SpaceTac.UI.Specs {
|
|||
}
|
||||
|
||||
testgame.ui = test_ui;
|
||||
testgame.ui.resetSession();
|
||||
|
||||
let [state, stateargs] = buildView();
|
||||
|
||||
|
@ -52,6 +54,11 @@ module TK.SpaceTac.UI.Specs {
|
|||
testgame.ui.state.add("test", state);
|
||||
testgame.ui.state.start("test", true, false, ...stateargs);
|
||||
|
||||
testgame.state = "test_initial";
|
||||
spyOn(testgame.ui.state, "start").and.callFake((name: string) => {
|
||||
testgame.state = name;
|
||||
});
|
||||
|
||||
if (!testgame.ui.isBooted) {
|
||||
testgame.ui.device.canvas = true;
|
||||
testgame.ui.boot();
|
||||
|
@ -127,4 +134,12 @@ module TK.SpaceTac.UI.Specs {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a click on a button
|
||||
*/
|
||||
export function testClick(button: Phaser.Button): void {
|
||||
button.onInputDown.dispatch();
|
||||
button.onInputUp.dispatch();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,59 +9,62 @@ module TK.SpaceTac.UI {
|
|||
*/
|
||||
export class CharacterSheet extends Phaser.Image {
|
||||
// Parent view
|
||||
view: BaseView;
|
||||
view: BaseView
|
||||
|
||||
// X positions
|
||||
xshown: number;
|
||||
xhidden: number;
|
||||
xshown: number
|
||||
xhidden: number
|
||||
|
||||
// Close button
|
||||
close_button: Phaser.Button
|
||||
|
||||
// Currently displayed fleet
|
||||
fleet: Fleet;
|
||||
fleet: Fleet
|
||||
|
||||
// Currently displayed ship
|
||||
ship: Ship;
|
||||
ship: Ship
|
||||
|
||||
// Ship name
|
||||
ship_name: Phaser.Text;
|
||||
ship_name: Phaser.Text
|
||||
|
||||
// Ship level
|
||||
ship_level: Phaser.Text;
|
||||
ship_experience: ValueBar;
|
||||
ship_level: Phaser.Text
|
||||
ship_experience: ValueBar
|
||||
|
||||
// Ship skill upgrade
|
||||
ship_upgrade_points: Phaser.Text;
|
||||
ship_upgrades: Phaser.Group;
|
||||
ship_upgrade_points: Phaser.Text
|
||||
ship_upgrades: Phaser.Group
|
||||
|
||||
// Ship slots
|
||||
ship_slots: Phaser.Group;
|
||||
ship_slots: Phaser.Group
|
||||
|
||||
// Ship cargo
|
||||
ship_cargo: Phaser.Group;
|
||||
ship_cargo: Phaser.Group
|
||||
|
||||
// Mode title
|
||||
mode_title: Phaser.Text;
|
||||
mode_title: Phaser.Text
|
||||
|
||||
// Loot items
|
||||
loot_slots: Phaser.Group;
|
||||
loot_items: Equipment[] = [];
|
||||
loot_page = 0;
|
||||
loot_next: Phaser.Button;
|
||||
loot_prev: Phaser.Button;
|
||||
loot_slots: Phaser.Group
|
||||
loot_items: Equipment[] = []
|
||||
loot_page = 0
|
||||
loot_next: Phaser.Button
|
||||
loot_prev: Phaser.Button
|
||||
|
||||
// Shop
|
||||
shop: Shop | null = null;
|
||||
shop: Shop | null = null
|
||||
|
||||
// Fleet's portraits
|
||||
portraits: Phaser.Group;
|
||||
portraits: Phaser.Group
|
||||
|
||||
// Layer for draggable equipments
|
||||
equipments: Phaser.Group;
|
||||
equipments: Phaser.Group
|
||||
|
||||
// Credits
|
||||
credits: Phaser.Text;
|
||||
credits: Phaser.Text
|
||||
|
||||
// Attributes and skills
|
||||
attributes: { [key: string]: Phaser.Text } = {};
|
||||
attributes: { [key: string]: Phaser.Text } = {}
|
||||
|
||||
constructor(view: BaseView, xhidden = -2000, xshown = 0, onclose?: Function) {
|
||||
super(view.game, 0, 0, "character-sheet");
|
||||
|
@ -76,11 +79,11 @@ module TK.SpaceTac.UI {
|
|||
if (!onclose) {
|
||||
onclose = () => this.hide();
|
||||
}
|
||||
let close_button = new Phaser.Button(this.game, view.getWidth(), 0, "character-close", onclose);
|
||||
close_button.anchor.set(1, 0);
|
||||
UIComponent.setButtonSound(close_button);
|
||||
this.addChild(close_button);
|
||||
view.tooltip.bindStaticText(close_button, "Close the character sheet");
|
||||
this.close_button = new Phaser.Button(this.game, view.getWidth(), 0, "character-close", onclose);
|
||||
this.close_button.anchor.set(1, 0);
|
||||
UIComponent.setButtonSound(this.close_button);
|
||||
this.addChild(this.close_button);
|
||||
view.tooltip.bindStaticText(this.close_button, "Close the character sheet");
|
||||
|
||||
this.ship_name = new Phaser.Text(this.game, 758, 48, "", { align: "center", font: "30pt SpaceTac", fill: "#FFFFFF" });
|
||||
this.ship_name.anchor.set(0.5, 0.5);
|
||||
|
@ -335,14 +338,14 @@ module TK.SpaceTac.UI {
|
|||
*
|
||||
* This shop will be shown until sheet is closed
|
||||
*/
|
||||
setShop(shop: Shop) {
|
||||
setShop(shop: Shop, title = "Dockyard's equipment") {
|
||||
this.loot_page = 0;
|
||||
|
||||
this.shop = shop;
|
||||
this.updateLoot();
|
||||
this.loot_slots.visible = true;
|
||||
|
||||
this.mode_title.setText("Dockyard's equipment");
|
||||
this.mode_title.setText(title);
|
||||
this.mode_title.visible = true;
|
||||
}
|
||||
|
||||
|
|
53
src/ui/character/FleetCreationView.spec.ts
Normal file
53
src/ui/character/FleetCreationView.spec.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/// <reference path="../TestGame.ts"/>
|
||||
|
||||
module TK.SpaceTac.UI.Specs {
|
||||
describe("FleetCreationView", function () {
|
||||
let testgame = setupSingleView(() => [new FleetCreationView, []]);
|
||||
|
||||
it("has a basic equipment shop with infinite stock", function () {
|
||||
let shop = testgame.view.infinite_shop;
|
||||
let itemcount = shop.getStock().length;
|
||||
expect(unique(shop.getStock().map(equ => equ.code)).length).toEqual(itemcount);
|
||||
|
||||
let fleet = new Fleet();
|
||||
fleet.credits = 100000;
|
||||
let item = shop.getStock()[0];
|
||||
shop.sellToFleet(item, fleet);
|
||||
expect(fleet.credits).toBe(100000 - item.getPrice());
|
||||
expect(shop.getStock().length).toBe(itemcount);
|
||||
|
||||
shop.buyFromFleet(item, fleet);
|
||||
expect(fleet.credits).toBe(100000);
|
||||
expect(shop.getStock().length).toBe(itemcount);
|
||||
})
|
||||
|
||||
async_it("validates the fleet creation", async function () {
|
||||
expect(testgame.ui.session.isFleetCreated()).toBe(false, "no fleet created");
|
||||
expect(testgame.ui.session.player.fleet.ships.length).toBe(0, "empty session fleet");
|
||||
expect(testgame.view.dialogs_layer.children.length).toBe(0, "no dialogs");
|
||||
expect(testgame.view.character_sheet.fleet).toBe(testgame.view.built_fleet);
|
||||
expect(testgame.view.built_fleet.ships.length).toBe(2, "initial fleet should have two ships");
|
||||
|
||||
// close sheet
|
||||
testClick(testgame.view.character_sheet.close_button);
|
||||
expect(testgame.view.dialogs_opened.length).toBe(1, "confirmation dialog opened");
|
||||
expect(testgame.ui.session.isFleetCreated()).toBe(false, "still no fleet created");
|
||||
|
||||
// click on no in confirmation dialog
|
||||
let dialog = <UIConfirmDialog>testgame.view.dialogs_opened[0];
|
||||
await dialog.forceResult(false);
|
||||
expect(testgame.view.dialogs_opened.length).toBe(0, "confirmation dialog destroyed after 'no'");
|
||||
expect(testgame.ui.session.isFleetCreated()).toBe(false, "still no fleet created after 'no'");
|
||||
expect(testgame.state).toEqual("test_initial");
|
||||
|
||||
// close sheet, click on yes in confirmation dialog
|
||||
testClick(testgame.view.character_sheet.close_button);
|
||||
dialog = <UIConfirmDialog>testgame.view.dialogs_opened[0];
|
||||
await dialog.forceResult(true);
|
||||
expect(testgame.view.dialogs_opened.length).toBe(0, "confirmation dialog destroyed after 'yes'");
|
||||
expect(testgame.ui.session.isFleetCreated()).toBe(true, "fleet created");
|
||||
expect(testgame.ui.session.player.fleet.ships.length).toBe(2, "session fleet now has two ships");
|
||||
expect(testgame.state).toEqual("router");
|
||||
})
|
||||
})
|
||||
}
|
46
src/ui/character/FleetCreationView.ts
Normal file
46
src/ui/character/FleetCreationView.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/// <reference path="../BaseView.ts"/>
|
||||
|
||||
module TK.SpaceTac.UI {
|
||||
/**
|
||||
* View to configure the initial characters in the fleet
|
||||
*/
|
||||
export class FleetCreationView extends BaseView {
|
||||
built_fleet: Fleet
|
||||
infinite_shop: Shop
|
||||
character_sheet: CharacterSheet
|
||||
|
||||
create() {
|
||||
super.create();
|
||||
|
||||
let models = ShipModel.getRandomModels(2);
|
||||
|
||||
this.built_fleet = new Fleet();
|
||||
this.built_fleet.addShip(new Ship(null, "First", models[0]));
|
||||
this.built_fleet.addShip(new Ship(null, "Second", models[1]));
|
||||
this.built_fleet.credits = this.built_fleet.ships.length * 1000;
|
||||
|
||||
let basic_equipments = () => {
|
||||
let generator = new LootGenerator();
|
||||
let equipments = generator.templates.map(template => template.generate(1));
|
||||
return sortedBy(equipments, equipment => equipment.slot_type);
|
||||
}
|
||||
this.infinite_shop = new Shop(1, basic_equipments(), 0, basic_equipments);
|
||||
|
||||
this.character_sheet = new CharacterSheet(this, undefined, undefined, () => this.validateFleet());
|
||||
this.character_sheet.setShop(this.infinite_shop, "Initial basic equipment");
|
||||
this.character_sheet.show(this.built_fleet.ships[0], false);
|
||||
this.addLayer("characters").add(this.character_sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the configured fleet and move on
|
||||
*/
|
||||
async validateFleet() {
|
||||
let confirmed = await UIConfirmDialog.ask(this, "Do you confirm these initial fleet settings ?");
|
||||
if (confirmed) {
|
||||
this.session.setCampaignFleet(this.built_fleet, this.session.hasUniverse());
|
||||
this.backToRouter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
src/ui/common/UIConfirmDialog.ts
Normal file
41
src/ui/common/UIConfirmDialog.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
module TK.SpaceTac.UI {
|
||||
/**
|
||||
* Dialog asking for a confirmation
|
||||
*/
|
||||
export class UIConfirmDialog extends UIDialog {
|
||||
private result: Promise<boolean>
|
||||
private result_resolver: (confirmed: boolean) => void
|
||||
|
||||
constructor(view: BaseView, message: string) {
|
||||
super(view);
|
||||
|
||||
this.addText(this.width * 0.5, this.height * 0.3, message, "#90FEE3", 32);
|
||||
|
||||
this.result = new Promise((resolve, reject) => {
|
||||
this.result_resolver = resolve;
|
||||
this.addButton(this.width * 0.4, this.height * 0.6, () => resolve(false), "common-button-cancel");
|
||||
this.addButton(this.width * 0.6, this.height * 0.6, () => resolve(true), "common-button-ok");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the result (simulate clicking the appropriate button)
|
||||
*/
|
||||
async forceResult(confirmed: boolean): Promise<void> {
|
||||
this.result_resolver(confirmed);
|
||||
await this.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient function to ask for a confirmation, and have a promise of result
|
||||
*/
|
||||
static ask(view: BaseView, message: string): Promise<boolean> {
|
||||
let dlg = new UIConfirmDialog(view, message);
|
||||
let result = dlg.result;
|
||||
return result.then(confirmed => {
|
||||
dlg.close();
|
||||
return confirmed;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,9 +10,10 @@ module TK.SpaceTac.UI {
|
|||
constructor(parent: BaseView, width = 1495, height = 1080, background = "common-dialog") {
|
||||
super(parent, width, height, background);
|
||||
|
||||
if (parent.dialogs_layer.children.length == 0) {
|
||||
if (parent.dialogs_opened.length == 0) {
|
||||
this.addOverlay(parent.dialogs_layer);
|
||||
}
|
||||
add(parent.dialogs_opened, this);
|
||||
|
||||
this.view.audio.playOnce("ui-dialog-open");
|
||||
|
||||
|
@ -46,7 +47,9 @@ module TK.SpaceTac.UI {
|
|||
|
||||
this.view.audio.playOnce("ui-dialog-close");
|
||||
|
||||
if (this.view.dialogs_layer.children.length == 1) {
|
||||
remove(this.view.dialogs_opened, this);
|
||||
if (this.view.dialogs_opened.length == 0) {
|
||||
// Remove overlay
|
||||
this.view.dialogs_layer.removeAll(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ module TK.SpaceTac.UI {
|
|||
|
||||
let nextStep = () => {
|
||||
if (!steps.nextStep()) {
|
||||
// For now, we create a random fleet
|
||||
this.gameui.session.setCampaignFleet();
|
||||
this.session.introduced = true;
|
||||
this.backToRouter();
|
||||
return false;
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue