Browse Source

New title screen

Michaël Lemaire 2 years ago
parent
commit
c625b215de

+ 1 - 0
.gitignore

@@ -5,5 +5,6 @@ coverage
 /out/assets
 /out/build.*
 /graphics/**/*.blend?*
+/graphics/**/output.png
 /typings/
 *.log

BIN
data/stage1/image/menu/arrow-left-hover.png


BIN
data/stage1/image/menu/arrow-left.png


BIN
data/stage1/image/menu/arrow-right-hover.png


BIN
data/stage1/image/menu/arrow-right.png


BIN
data/stage1/image/menu/background.jpg


BIN
data/stage1/image/menu/button-hover.png


BIN
data/stage1/image/menu/button-on.png


BIN
data/stage1/image/menu/button-small-hover.png


BIN
data/stage1/image/menu/button-small.png


BIN
data/stage1/image/menu/button.png


BIN
data/stage1/image/menu/icon-options.png


BIN
data/stage1/image/menu/input-small.png


BIN
data/stage1/image/menu/input.png


BIN
data/stage1/image/menu/load-bg.png


BIN
data/stage1/image/menu/title.png


BIN
graphics/title/metal1.jpeg


BIN
graphics/title/metal2.jpg


BIN
graphics/title/starmap.jpg


BIN
graphics/title/title.blend


File diff suppressed because it is too large
+ 1069 - 427
graphics/ui/title.svg


+ 2 - 2
src/core/GameSession.ts

@@ -110,11 +110,11 @@ module TK.SpaceTac {
         /**
          * Start a new "quick battle" game
          */
-        startQuickBattle(with_ai: boolean = false): void {
+        startQuickBattle(with_ai: boolean = false, level?: number, shipcount?: number): void {
             this.player = new Player();
             this.universe = new Universe();
 
-            let battle = Battle.newQuickRandom(true, RandomGenerator.global.randInt(1, 10));
+            let battle = Battle.newQuickRandom(true, level || RandomGenerator.global.randInt(1, 10), shipcount);
             this.player.setFleet(battle.fleets[0]);
             this.player.setBattle(battle);
 

+ 1 - 0
src/ui/common/Animations.ts

@@ -85,6 +85,7 @@ module TK.SpaceTac.UI {
                 }
                 tween.start();
             } else {
+                this.tweens.removeFrom(obj, false);
                 obj.alpha = alpha;
                 if (obj.input) {
                     obj.input.enabled = true;

+ 11 - 1
src/ui/common/UIBuilder.ts

@@ -80,6 +80,11 @@ module TK.SpaceTac.UI {
 
         // Text content style override
         text_style?: UITextStyleI
+
+        // Icon content
+        icon?: string
+        icon_x?: number
+        icon_y?: number
     }
 
     /**
@@ -155,9 +160,10 @@ module TK.SpaceTac.UI {
         /**
          * Add a group of components
          */
-        group(name: string, x = 0, y = 0): UIGroup {
+        group(name: string, x = 0, y = 0, visible = true): UIGroup {
             let result = new Phaser.Group(this.game, undefined, name);
             result.position.set(x, y);
+            result.visible = visible;
             this.add(result);
             return result;
         }
@@ -293,6 +299,10 @@ module TK.SpaceTac.UI {
                 this.in(result).text(options.text, options.text_x || 0, options.text_y || 0, options.text_style);
             }
 
+            if (options.icon) {
+                this.in(result).image(options.icon, options.icon_x || 0, options.icon_y || 0, options.center);
+            }
+
             this.add(result);
             return result;
         }

+ 1 - 2
src/ui/common/UITextDialog.ts

@@ -11,8 +11,7 @@ module TK.SpaceTac.UI {
 
             this.addText(this.width * 0.5, this.height * 0.3, message, "#90FEE3", 32);
 
-            let input = new UITextInput(this, 600, 80, 16);
-            input.setPositionInsideParent(0.5, 0.5);
+            let input = new UITextInput(this.builder.styled({ size: 24 }), "menu-input", this.width / 2, this.height / 2, 12);
             if (initial) {
                 input.setContent(initial);
             }

+ 15 - 19
src/ui/common/UITextInput.ts

@@ -1,29 +1,24 @@
-/// <reference path="UIComponent.ts" />
-
 module TK.SpaceTac.UI {
     /**
      * UI component to allow the user to enter a small text
      */
-    export class UITextInput extends UIComponent {
+    export class UITextInput {
         private content: Phaser.Text
+        private placeholder: Phaser.Text
         private maxlength: number
 
-        constructor(parent: UIComponent, width: number, height: number, maxlength?: number, fontcolor = "#FFFFFF") {
-            super(parent, width, height);
-
-            let input_bg = this.builder.image("common-transparent", 0, 0, false);
-            input_bg.scale.set(width, height);
+        constructor(builder: UIBuilder, background: string, x = 0, y = 0, maxlength: number, placeholder = "") {
+            let input_bg = builder.image(background, x, y, true);
             input_bg.inputEnabled = true;
             input_bg.input.useHandCursor = true;
-            input_bg.events.onInputUp.add(() => this.setKeyboardFocus(key => this.processKey(key)));
-            this.addInternalChild(input_bg);
-
-            let fontsize = Math.ceil(height * 0.8);
-            this.content = new Phaser.Text(this.game, width / 2, height / 2, "", { align: "center", font: `${fontsize}px SpaceTac`, fill: fontcolor });
-            this.content.anchor.set(0.5, 0.5);
-            this.addInternalChild(this.content);
-
-            this.maxlength = maxlength || (width / fontsize);
+            input_bg.events.onInputUp.add(() => {
+                builder.view.inputs.grabKeyboard(this, key => this.processKey(key));
+            });
+
+            this.content = builder.in(input_bg).text("");
+            this.placeholder = builder.in(input_bg).text(placeholder);
+            this.placeholder.alpha = 0.5;
+            this.maxlength = maxlength;
         }
 
         /**
@@ -31,9 +26,9 @@ module TK.SpaceTac.UI {
          */
         processKey(key: string): void {
             if (key.length == 1 && this.content.text.length < this.maxlength) {
-                this.content.text += key;
+                this.setContent(this.content.text + key);
             } else if (key == "Backspace" && this.content.text.length > 0) {
-                this.content.text = this.content.text.substr(0, this.content.text.length - 1);
+                this.setContent(this.content.text.substr(0, this.content.text.length - 1));
             }
         }
 
@@ -49,6 +44,7 @@ module TK.SpaceTac.UI {
          */
         setContent(content: string): void {
             this.content.text = content.slice(0, this.maxlength);
+            this.placeholder.visible = !this.content.text;
         }
     }
 }

+ 2 - 2
src/ui/menu/LoadDialog.spec.ts → src/ui/menu/InputInviteCode.spec.ts

@@ -3,7 +3,7 @@
 
 module TK.SpaceTac.UI.Specs {
     testing("LoadDialog", test => {
-        let testgame = setupSingleView(test, () => [new MainMenu(), []]);
+        let testgame = setupEmptyView(test);
 
         test.acase("joins remote sessions as spectator", async check => {
             return new Promise((resolve, reject) => {
@@ -13,7 +13,7 @@ module TK.SpaceTac.UI.Specs {
                 check.equals(session.primary, true);
                 check.equals(session.spectator, false);
                 view.getConnection().publish(session, "Test").then(token => {
-                    let dialog = new LoadDialog(view);
+                    let dialog = new InputInviteCode(view, new UIBuilder(view), 0, 0);
                     dialog.token_input.setContent(token);
 
                     check.patch(view.gameui, "setSession", (joined: GameSession) => {

+ 30 - 0
src/ui/menu/InputInviteCode.ts

@@ -0,0 +1,30 @@
+module TK.SpaceTac.UI {
+    /**
+     * Input to display available save games, and load one
+     */
+    export class InputInviteCode {
+        token_input: UITextInput
+
+        constructor(private view: BaseView, private builder: UIBuilder, x: number, y: number) {
+            this.token_input = new UITextInput(builder, "menu-input", x, y, 8, "Invite code");
+        }
+
+        /**
+         * Join an online game
+         */
+        join(): void {
+            let token = this.token_input.getContent();
+            let connection = this.view.getConnection();
+
+            connection.loadByToken(token).then(session => {
+                if (session) {
+                    // For now, we will only spectate
+                    session.primary = false;
+                    session.spectator = true;
+
+                    this.view.gameui.setSession(session, token);
+                }
+            });
+        }
+    }
+}

+ 17 - 41
src/ui/menu/LoadDialog.ts → src/ui/menu/InputSavegames.ts

@@ -1,29 +1,19 @@
-/// <reference path="../common/UIComponent.ts"/>
-
 module TK.SpaceTac.UI {
     /**
-     * Dialog to load a saved game, or join an online one
+     * Input to display available save games, and load one
      */
-    export class LoadDialog extends UIComponent {
-        saves: [string, string][] = []
-        save_selected = 0
-        save_name: UILabel
-        token_input: UITextInput
-
-        constructor(parent: MainMenu) {
-            super(parent, 1344, 566, "menu-load-bg");
+    export class InputSavegames {
+        private saves: [string, string][] = []
+        private save_selected = 0
+        private save_name?: UIText
 
-            let button = this.addButton(600, 115, () => this.paginateSave(-1), "common-arrow", 0, 0, "Scroll to newer saves");
-            button.angle = 180;
-            this.addButton(1038, 115, () => this.paginateSave(1), "common-arrow", 0, 0, "Scroll to older saves");
-            this.addButton(1224, 115, () => this.load(), "common-button-ok");
-            this.addButton(1224, 341, () => this.join(), "common-button-ok");
+        constructor(private view: BaseView, private builder: UIBuilder, x: number, y: number) {
+            builder.in(builder.image("menu-input", x, y, true), builder => {
+                builder.button("menu-arrow-left", -196, 0, () => this.paginateSave(1), "Older saves", undefined, { center: true });
+                builder.button("menu-arrow-right", 196, 0, () => this.paginateSave(-1), "Newer saves", undefined, { center: true });
 
-            this.save_name = new UILabel(this, 351, 185, "", 32, "#000000");
-            this.save_name.setPosition(645, 28);
-
-            this.token_input = new UITextInput(this, 468, 68, 10, "#000000");
-            this.token_input.setPosition(585, 304);
+                this.save_name = builder.text("", 0, 0, { size: 24 });
+            });
 
             this.refreshSaves();
         }
@@ -47,13 +37,17 @@ module TK.SpaceTac.UI {
          * Set the current selected save game
          */
         setCurrentSave(position: number): void {
+            if (!this.save_name) {
+                return;
+            }
+
             if (this.saves.length == 0) {
-                this.save_name.setContent("No save game found");
+                this.builder.change(this.save_name, "No save game found");
             } else {
                 this.save_selected = clamp(position, 0, this.saves.length - 1);
 
                 let [saveid, saveinfo] = this.saves[this.save_selected];
-                this.save_name.setContent(saveinfo);
+                this.builder.change(this.save_name, saveinfo);
             }
         }
 
@@ -64,24 +58,6 @@ module TK.SpaceTac.UI {
             this.setCurrentSave(this.save_selected + offset);
         }
 
-        /**
-         * Join an online game
-         */
-        join(): void {
-            let token = this.token_input.getContent();
-            let connection = this.view.getConnection();
-
-            connection.loadByToken(token).then(session => {
-                if (session) {
-                    // For now, we will only spectate
-                    session.primary = false;
-                    session.spectator = true;
-
-                    this.view.gameui.setSession(session, token);
-                }
-            });
-        }
-
         /**
          * Load selected save game
          */

+ 0 - 19
src/ui/menu/MainMenu.spec.ts

@@ -1,19 +0,0 @@
-/// <reference path="../TestGame.ts" />
-/// <reference path="MainMenu.ts" />
-
-module TK.SpaceTac.UI.Specs {
-    testing("MainMenu", test => {
-        let testgame = setupSingleView(test, () => [new MainMenu(), []]);
-
-        test.case("adds moving stars, a title and three buttons", check => {
-            let view = <MainMenu>testgame.ui.state.getCurrentState();
-
-            check.equals(view.layer_stars.children.length, 300);
-            check.equals(view.layer_title.children.length, 6);
-            check.equals(view.layer_title.children[0] instanceof Phaser.Button, true);
-            check.equals(view.layer_title.children[1] instanceof Phaser.Button, true);
-            check.equals(view.layer_title.children[2] instanceof Phaser.Button, true);
-            check.equals(view.layer_title.children[3] instanceof Phaser.Image, true);
-        });
-    });
-}

+ 117 - 76
src/ui/menu/MainMenu.ts

@@ -6,75 +6,114 @@ module TK.SpaceTac.UI {
      */
     export class MainMenu extends BaseView {
         static returned = false
-        layer_stars!: Phaser.Group
-        layer_presents!: Phaser.Group
-        layer_title!: Phaser.Group
-        button_new_game!: Phaser.Button
-        button_quick_battle!: Phaser.Button
-        button_load_game!: Phaser.Button
-        dialog_load_game!: LoadDialog
 
         create() {
             super.create();
 
             let builder = new UIBuilder(this);
 
-            this.layer_stars = this.getLayer("stars");
-            this.layer_presents = this.getLayer("presents");
-            this.layer_title = this.getLayer("title");
+            // Layers
+            let layer_background = this.getLayer("background");
+            let layer_presents = this.getLayer("presents");
+            let layer_title = this.getLayer("title");
 
-            // Stars
-            for (let i = 0; i < 300; i++) {
-                let fade = Math.random() * 0.5 + 0.5;
-                let x = Math.random() * 0.998 + 0.001;
-                let star = this.add.image(1920 * x, Math.random() * 1080, "common-particles", 32, this.layer_stars);
-                star.anchor.set(0.5, 0.5);
-                star.angle = 225;
-                star.alpha = 0.7 * fade;
-                star.scale.set(0.8 * fade, 0.8 * fade);
-                this.tweens.create(star).to({ x: -30 }, 30000 * x / fade).to({ x: 1950 }, 0.00001).to({ x: 1920 * x }, 30000 * (1 - x) / fade).loop().start();
-            }
+            // Background
+            builder.in(layer_background, builder => {
+                builder.image("menu-background");
+            });
 
             // Presents...
-            builder.in(this.layer_presents, builder => {
+            builder.in(layer_presents, builder => {
                 builder.styled({ center: true }, builder => {
                     builder.text("Michael Lemaire", this.getMidWidth(), this.getHeight() * 0.4, { size: 32 });
                     builder.text("presents", this.getMidWidth(), this.getHeight() * 0.6, { size: 24 });
                 });
             });
 
-            // Menu buttons
-            this.button_new_game = this.addButton(320, 730, "New Game", "Start a new campaign in a generated universe", () => this.onNewGame());
-            this.button_load_game = this.addButton(958, 730, "Load / Join", "Load a saved game or join a friend", () => this.onLoadGame());
-            this.button_quick_battle = this.addButton(1604, 730, "Quick Battle", "Play a single generated battle", () => this.onQuickBattle());
-
-            // Fullscreen button
-            let button = builder.in(this.layer_title).button("options-option-fullscreen", this.getWidth(), 0, () => this.options.toggleBoolean("fullscreen"), "Toggle full-screen");
-            button.anchor.set(1, 0);
-
-            // Title
-            let title = builder.in(this.layer_title).image("menu-title", 274, 187);
-
-            // Dialogs
-            this.dialog_load_game = new LoadDialog(this);
-            this.dialog_load_game.setPosition(286, 120);
-            this.dialog_load_game.moveToLayer(this.layer_title);
-            this.dialog_load_game.setVisible(false);
+            builder.styled({ color: "#9fc4d6", size: 40, shadow: true }).in(layer_title, builder => {
+                // Title
+                let title = builder.in(layer_title).image("menu-title", 960, 784, true);
+
+                // Buttons
+                let group_new_game = builder.group("new-game", 0, 0, false);
+                let group_load_game = builder.group("load-game", 0, 0, false);
+                let group_join_game = builder.group("join-game", 0, 0, false);
+                let group_skirmish = builder.in(group_new_game).group("skirmish", 0, 0, false);
+                let button_new_game = builder.button("menu-button", 280, 106, undefined, "Start a new game", (on: boolean) => {
+                    if (on) {
+                        this.animations.show(group_new_game, 200);
+                        builder.switch(button_load_game, false);
+                        builder.switch(button_join_game, false);
+                    } else {
+                        this.animations.hide(group_new_game, 200);
+                    }
+                    return on;
+                }, { text: "New game", center: true, on_bottom: true });
+                let button_campaign = builder.in(group_new_game).button("menu-button", 770, 106, () => this.startCampaign(), "Start a campaign in story mode", undefined, {
+                    text: "Campaign", center: true
+                });
+                let button_skirmish = builder.in(group_new_game).button("menu-button", 770, 266, undefined, "Start a quick battle", (on: boolean) => {
+                    this.animations.setVisible(group_skirmish, on, 200);
+                    return on;
+                }, { text: "Skirmish", center: true, on_bottom: true });
+                let button_skirmish_shipcount = this.addNumberSelector(builder.in(group_skirmish), 1130, 266, "Ships", 2, 5, 3);
+                let button_skirmish_level = this.addNumberSelector(builder.in(group_skirmish), 1386, 266, "Level", 1, 10, 1);
+                let button_skirmish_go = builder.in(group_skirmish).button("menu-button-small", 1632, 266, () => {
+                    this.startSkirmish(button_skirmish_shipcount(), button_skirmish_level())
+                }, "Start the skirmish with selected settings", undefined, { text: "Go", center: true });
+                let button_load_game = builder.button("menu-button", 280, 266, undefined, "Load a previously saved game", (on: boolean) => {
+                    if (on) {
+                        this.animations.show(group_load_game, 200);
+                        builder.switch(button_new_game, false);
+                        builder.switch(button_join_game, false);
+                    } else {
+                        this.animations.hide(group_load_game, 200);
+                    }
+                    return on;
+                }, { text: "Load game", center: true, on_bottom: true });
+                builder.in(group_load_game, builder => {
+                    let input = new InputSavegames(this, builder, 770, 266);
+                    builder.button("menu-button-small", 1112, 266, () => input.load(), "Load the selected save game", undefined, {
+                        text: "Go", center: true
+                    });
+                })
+                let button_join_game = builder.button("menu-button", 280, 426, undefined, "Join a friend's game", (on: boolean) => {
+                    if (on) {
+                        this.animations.show(group_join_game, 200);
+                        builder.switch(button_new_game, false);
+                        builder.switch(button_load_game, false);
+                    } else {
+                        this.animations.hide(group_join_game, 200);
+                    }
+                    return on;
+                }, { text: "Join game", center: true, on_bottom: true });
+                builder.in(group_join_game, builder => {
+                    let input = new InputInviteCode(this, builder, 770, 426);
+                    builder.button("menu-button-small", 1112, 426, () => input.join(), "Join the game", undefined, {
+                        text: "Go", center: true
+                    });
+                })
+                let button_options = builder.button("menu-button-small", 1780, 106, () => this.showOptions(), "Options", undefined, {
+                    center: true,
+                    icon: "menu-icon-options",
+                });
+            });
 
             // Animations
-            this.layer_stars.visible = false;
-            this.layer_presents.visible = false;
-            this.layer_title.visible = false;
-            this.animations.show(this.layer_presents, 500);
-            this.animations.show(this.layer_stars, 5000);
+            layer_background.visible = false;
+            layer_presents.visible = false;
+            layer_title.visible = false;
+            this.animations.show(layer_presents, 500);
+            this.animations.show(layer_background, 5000);
             let fading = this.timer.schedule(5000, () => {
-                this.animations.show(this.layer_title, 1000);
-                this.animations.hide(this.layer_presents, 300);
+                this.animations.show(layer_title, 1000);
+                this.animations.hide(layer_presents, 300);
             });
             let pass = () => {
                 this.timer.cancel(fading);
-                this.animations.show(this.layer_title, 0);
-                this.animations.hide(this.layer_presents, 0);
+                this.animations.show(layer_background, 0);
+                this.animations.show(layer_title, 0);
+                this.animations.hide(layer_presents, 0);
             };
             if (MainMenu.returned) {
                 pass();
@@ -86,38 +125,40 @@ module TK.SpaceTac.UI {
             this.gameui.audio.startMusic("supernatural");
         }
 
-        addButton(x: number, y: number, caption: string, tooltip: string, callback: Function): Phaser.Button {
-            let builder = new UIBuilder(this).in(this.layer_title);
-
-            let result = builder.button("menu-button", x, y, callback, tooltip);
-            result.anchor.set(0.5);
-
-            builder.in(result).text(caption, 0, 0, { bold: true, size: 40, color: "#529aee" });
-
-            return result;
-        }
-
-        // Called when "New Game" is clicked
-        onNewGame(): void {
-            var gameui = <MainUI>this.game;
-
-            gameui.session.startNewGame(false);
-
-            this.game.state.start("router");
+        /**
+         * Add a number selector in a given range
+         */
+        addNumberSelector(builder: UIBuilder, x: number, y: number, label: string, min: number, max: number, initial: number): () => number {
+            let value = initial;
+            builder.in(builder.image("menu-input-small", x, y + 30, true), builder => {
+                let display = builder.text(`${value}`, 0, -32);
+                builder.text(label, 0, 54, { color: "#6690a4", size: 28 });
+                builder.button("menu-arrow-left", -68, -32, () => {
+                    value = Math.max(min, value - 1);
+                    builder.change(display, `${value}`);
+                }, undefined, undefined, { center: true });
+                builder.button("menu-arrow-right", 68, -32, () => {
+                    value = Math.min(max, value + 1);
+                    builder.change(display, `${value}`);
+                }, undefined, undefined, { center: true });
+            });
+            return () => value;
         }
 
-        // Called when "Quick Battle" is clicked
-        onQuickBattle(): void {
-            var gameui = <MainUI>this.game;
-
-            gameui.session.startQuickBattle(true);
-
-            this.game.state.start("router");
+        /**
+         * Start a campaign mode
+         */
+        startCampaign(): void {
+            this.session.startNewGame(false);
+            this.backToRouter();
         }
 
-        // Called when "Load Game" is clicked
-        onLoadGame(): void {
-            this.dialog_load_game.setVisible(true);
+        /**
+         * Start a skirmish
+         */
+        startSkirmish(shipcount: number, level: number): void {
+            this.session.startQuickBattle(true, level, shipcount);
+            this.backToRouter();
         }
     }
 }

Some files were not shown because too many files changed in this diff