diff --git a/TODO.md b/TODO.md index 8d78890..4decccd 100644 --- a/TODO.md +++ b/TODO.md @@ -34,7 +34,6 @@ Character sheet * 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 -* Fix dragged equipment being under attributes (put attributes in a layer) Battle ------ @@ -96,6 +95,7 @@ Common UI * Split atlases by asset stage * UIBuilder.button should be able to handle hover and pushed images +* If ProgressiveMessage animation performance is bad, show the text directly * Add caret/focus to text input * 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 diff --git a/graphics/exported/common/arrow-down.png b/graphics/exported/common/arrow-down.png new file mode 100644 index 0000000..b54afbf Binary files /dev/null and b/graphics/exported/common/arrow-down.png differ diff --git a/graphics/exported/common/arrow-left.png b/graphics/exported/common/arrow-left.png new file mode 100644 index 0000000..a436012 Binary files /dev/null and b/graphics/exported/common/arrow-left.png differ diff --git a/graphics/exported/common/arrow-right.png b/graphics/exported/common/arrow-right.png new file mode 100644 index 0000000..6f8b869 Binary files /dev/null and b/graphics/exported/common/arrow-right.png differ diff --git a/graphics/exported/common/arrow-up.png b/graphics/exported/common/arrow-up.png new file mode 100644 index 0000000..fbd547a Binary files /dev/null and b/graphics/exported/common/arrow-up.png differ diff --git a/src/ui/character/CharacterFleetMember.spec.ts b/src/ui/character/CharacterFleetMember.spec.ts index 2e44d22..4f9e11c 100644 --- a/src/ui/character/CharacterFleetMember.spec.ts +++ b/src/ui/character/CharacterFleetMember.spec.ts @@ -21,13 +21,13 @@ module TK.SpaceTac.UI.Specs { sheet.show(ship1); expect(sheet.portraits.length).toBe(2); - expect(sheet.equipments.length).toBe(3); + expect(sheet.layer_equipments.length).toBe(3); expect(sheet.ship_cargo.length).toBe(3); // First item fits in the free slot let source = sheet.ship_cargo.children[0]; let dest = sheet.portraits.children[1]; - let equ = sheet.equipments.children[0]; + let equ = sheet.layer_equipments.children[0]; expect(dest.ship).toBe(ship2); expect(equ.item).toBe(equ1); expect(ship1.cargo).toContain(equ1); @@ -39,7 +39,7 @@ module TK.SpaceTac.UI.Specs { // Second item goes to cargo source = sheet.ship_cargo.children[0]; dest = sheet.portraits.children[1]; - equ = sheet.equipments.children[1]; + equ = sheet.layer_equipments.children[1]; expect(dest.ship).toBe(ship2); expect(equ.item).toBe(equ2); expect(ship1.cargo).toContain(equ2); @@ -51,7 +51,7 @@ module TK.SpaceTac.UI.Specs { // Third item has no more room source = sheet.ship_cargo.children[0]; dest = sheet.portraits.children[1]; - equ = sheet.equipments.children[2]; + equ = sheet.layer_equipments.children[2]; expect(dest.ship).toBe(ship2); expect(equ.item).toBe(equ3); expect(ship1.cargo).toContain(equ3); diff --git a/src/ui/character/CharacterLootSlot.spec.ts b/src/ui/character/CharacterLootSlot.spec.ts index 48842a6..f5bfcea 100644 --- a/src/ui/character/CharacterLootSlot.spec.ts +++ b/src/ui/character/CharacterLootSlot.spec.ts @@ -26,14 +26,14 @@ module TK.SpaceTac.UI.Specs { expect(loot_slot instanceof CharacterLootSlot).toBe(true); // loot to cargo - let equ2s = sheet.equipments.children[1]; + let equ2s = sheet.layer_equipments.children[1]; expect(equ2s.item).toBe(equ2); equ2s.applyDragDrop(loot_slot, cargo_slot, false); expect(ship.cargo).toEqual([equ1, equ2]); expect(loot).toEqual([]); // discard to cargo - let equ1s = sheet.equipments.children[0]; + let equ1s = sheet.layer_equipments.children[0]; expect(equ1s.item).toBe(equ1); equ1s.applyDragDrop(cargo_slot, loot_slot, false); expect(ship.cargo).toEqual([equ2]); diff --git a/src/ui/character/CharacterSheet.spec.ts b/src/ui/character/CharacterSheet.spec.ts index 2f91a94..572cbd1 100644 --- a/src/ui/character/CharacterSheet.spec.ts +++ b/src/ui/character/CharacterSheet.spec.ts @@ -56,14 +56,14 @@ module TK.SpaceTac.UI.Specs { sheet.show(ship, false); expect(sheet.loot_slots.visible).toBe(false); - expect(sheet.equipments.children.length).toBe(2); + expect(sheet.layer_equipments.children.length).toBe(2); sheet.setLoot(loot); expect(sheet.loot_slots.visible).toBe(true); - expect(sheet.equipments.children.length).toBe(4); + expect(sheet.layer_equipments.children.length).toBe(4); - let findsprite = (equ: Equipment) => nn(first(sheet.equipments.children, sp => sp.item == equ)); + let findsprite = (equ: Equipment) => nn(first(sheet.layer_equipments.children, sp => sp.item == equ)); let draddrop = (sp: CharacterEquipment, dest: CharacterCargo | CharacterSlot) => { sp.applyDragDrop(sp.container, dest, false); } diff --git a/src/ui/character/CharacterSheet.ts b/src/ui/character/CharacterSheet.ts index 6822e47..87cd6cc 100644 --- a/src/ui/character/CharacterSheet.ts +++ b/src/ui/character/CharacterSheet.ts @@ -11,6 +11,9 @@ module TK.SpaceTac.UI { // Parent view view: BaseView + // UI components builder + builder: UIBuilder + // X positions xshown: number xhidden: number @@ -33,7 +36,7 @@ module TK.SpaceTac.UI { // Ship skill upgrade ship_upgrade_points: Phaser.Text - ship_upgrades: Phaser.Group + layer_upgrades: Phaser.Group // Ship slots ship_slots: Phaser.Group @@ -58,8 +61,9 @@ module TK.SpaceTac.UI { members: CharacterFleetMember[] = [] portraits: Phaser.Group - // Layer for draggable equipments - equipments: Phaser.Group + // Layers + layer_attibutes: Phaser.Group + layer_equipments: Phaser.Group // Credits credits: Phaser.Text @@ -71,6 +75,7 @@ module TK.SpaceTac.UI { super(view.game, 0, 0, "character-sheet"); this.view = view; + this.builder = new UIBuilder(view, this); this.x = xhidden; this.xshown = xshown; @@ -80,65 +85,33 @@ module TK.SpaceTac.UI { if (!onclose) { onclose = () => this.hide(); } - this.close_button = view.newButton("character-close", 1920, 0, onclose); + this.close_button = this.builder.button("character-close", 1920, 0, onclose, "Close the character sheet"); this.close_button.anchor.set(1, 0); - this.addChild(this.close_button); - view.tooltip.bindStaticText(this.close_button, "Close the character sheet"); - this.addChild(view.newText("Level", 420, 1052, 24)); - this.addChild(view.newText("Available points", 894, 1052, 24)); + this.builder.text("Level", 420, 1052, { size: 24 }); + this.builder.text("Available points", 894, 1052, { size: 24 }); - this.ship_name = view.newText("", 758, 48, 30); - this.addChild(this.ship_name); - - this.ship_level = view.newText("", 554, 1052, 30); - this.addChild(this.ship_level); + this.ship_name = this.builder.text("", 758, 48, { size: 30 }); + this.ship_level = this.builder.text("", 554, 1052, { size: 30 }); + this.ship_upgrade_points = this.builder.text("", 1068, 1052, { size: 30 }); + this.ship_slots = this.builder.group("slots", 372, 120); + this.ship_cargo = this.builder.group("cargo", 1240, 86); + this.loot_slots = this.builder.group("loot", 1270, 670); + this.loot_slots.visible = false; + this.portraits = this.builder.group("portraits", 152, 0); + this.credits = this.builder.text("", 136, 38, { size: 30 }); + this.mode_title = this.builder.text("", 1566, 648, { size: 18 }); + this.loot_next = this.builder.button("common-arrow-right", 1890, 850, () => this.paginate(1), "Show next items"); + this.loot_next.anchor.set(0.5); + this.loot_prev = this.builder.button("common-arrow-left", 1238, 850, () => this.paginate(-1), "Show previous items"); + this.loot_prev.anchor.set(0.5); this.ship_experience = new ValueBar(this.view, "character-experience", ValueBarOrientation.EAST, 516, 1067); this.addChild(this.ship_experience.node); - this.ship_upgrade_points = view.newText("", 1068, 1052, 30); - this.addChild(this.ship_upgrade_points); - - this.ship_upgrades = new Phaser.Group(this.game); - this.addChild(this.ship_upgrades); - - this.ship_slots = new Phaser.Group(this.game); - this.ship_slots.position.set(372, 120); - this.addChild(this.ship_slots); - - this.ship_cargo = new Phaser.Group(this.game); - this.ship_cargo.position.set(1240, 86); - this.addChild(this.ship_cargo); - - this.loot_slots = new Phaser.Group(this.game); - this.loot_slots.position.set(1270, 670); - this.loot_slots.visible = false; - this.addChild(this.loot_slots); - - this.portraits = new Phaser.Group(this.game); - this.portraits.position.set(152, 0); - this.addChild(this.portraits); - - this.credits = view.newText("", 136, 38, 30); - this.addChild(this.credits); - - this.equipments = new Phaser.Group(this.game); - this.addChild(this.equipments); - - this.mode_title = view.newText("", 1566, 648, 18); - this.addChild(this.mode_title); - - this.loot_next = new Phaser.Button(this.game, 1890, 850, "common-arrow", () => this.paginate(1)); - this.loot_next.anchor.set(0.5, 0.5); - UIComponent.setButtonSound(this.loot_next); - this.addChild(this.loot_next); - - this.loot_prev = new Phaser.Button(this.game, 1238, 850, "common-arrow", () => this.paginate(-1)); - this.loot_prev.anchor.set(0.5, 0.5); - this.loot_prev.angle = 180; - UIComponent.setButtonSound(this.loot_prev); - this.addChild(this.loot_prev); + this.layer_attibutes = this.builder.group("attributes"); + this.layer_upgrades = this.builder.group("upgrades"); + this.layer_equipments = this.builder.group("equipments"); let x1 = 402; let x2 = 802; @@ -161,29 +134,22 @@ module TK.SpaceTac.UI { * Add an attribute display */ private addAttribute(attribute: keyof ShipAttributes, x: number, y: number) { - let button = this.view.newButton("character-attribute", x, y); - this.addChild(button); - this.view.tooltip.bindDynamicText(button, () => this.ship.getAttributeDescription(attribute)); + let builder = this.builder.in(this.layer_attibutes); + + let button = builder.button("character-attribute", x, y, undefined, () => this.ship.getAttributeDescription(attribute)); let attrname = capitalize(SHIP_ATTRIBUTES[attribute].name); - let name = new Phaser.Text(this.game, 120, 22, attrname, - { align: "center", font: "20pt SpaceTac", fill: "#c9d8ef", stroke: "#395665", strokeThickness: 1 }); - name.anchor.set(0.5); - button.addChild(name); + builder.in(button).text(attrname, 120, 22, { size: 20, color: "#c9d8ef", stroke_width: 1, stroke_color: "#395665" }); - let value = this.view.newText("", 264, 24, 18, "#ffffff", true, true); - button.addChild(value); + let value = builder.in(button).text("", 264, 24, { size: 18, bold: true }); this.attributes[attribute] = value; if (SHIP_SKILLS.hasOwnProperty(attribute)) { - let upgrade_button = this.view.newButton("character-skill-upgrade", x + 292, y, () => { + this.builder.in(this.layer_upgrades).button("character-skill-upgrade", x + 292, y, () => { this.ship.upgradeSkill(attribute); this.refresh(); - }); - this.ship_upgrades.add(upgrade_button); - - this.view.tooltip.bindStaticText(upgrade_button, `Spend one point to upgrade ${attrname}`); + }, `Spend one point to upgrade ${attrname}`); } } @@ -222,7 +188,7 @@ module TK.SpaceTac.UI { show(ship: Ship, animate = true, sound = true) { this.ship = ship; - this.equipments.removeAll(true); + this.layer_equipments.removeAll(true); let upgrade_points = ship.getAvailableUpgradePoints(); @@ -230,7 +196,7 @@ module TK.SpaceTac.UI { this.ship_level.setText(ship.level.get().toString()); this.ship_experience.setValue(ship.level.getExperience(), ship.level.getNextGoal()); this.ship_upgrade_points.setText(upgrade_points.toString()); - this.ship_upgrades.visible = !ship.critical && upgrade_points > 0; + this.layer_upgrades.visible = !ship.critical && upgrade_points > 0; iteritems(ship.attributes, (key, value: ShipAttribute) => { let text = this.attributes[key]; @@ -249,7 +215,7 @@ module TK.SpaceTac.UI { if (slot.attached) { let equipment = new CharacterEquipment(this, slot.attached, slot_display); - this.equipments.addChild(equipment); + this.layer_equipments.addChild(equipment); } }); @@ -263,7 +229,7 @@ module TK.SpaceTac.UI { if (idx < this.ship.cargo.length) { let equipment = new CharacterEquipment(this, this.ship.cargo[idx], cargo_slot); - this.equipments.addChild(equipment); + this.layer_equipments.addChild(equipment); } }); @@ -345,7 +311,7 @@ module TK.SpaceTac.UI { * Update the price tags on each equipment, for a specific shop */ updatePrices(shop: Shop) { - this.equipments.children.forEach((equipement: CharacterEquipment) => { + this.layer_equipments.children.forEach((equipement: CharacterEquipment) => { equipement.setPrice(shop.getPrice(equipement.item)); }); } @@ -377,7 +343,7 @@ module TK.SpaceTac.UI { if (idx < items.length) { let equipment = new CharacterEquipment(this, items[idx], loot_slot); - this.equipments.addChild(equipment); + this.layer_equipments.addChild(equipment); } }); diff --git a/src/ui/character/CharacterShopSlot.spec.ts b/src/ui/character/CharacterShopSlot.spec.ts index 15c8bf2..3e684d9 100644 --- a/src/ui/character/CharacterShopSlot.spec.ts +++ b/src/ui/character/CharacterShopSlot.spec.ts @@ -29,7 +29,7 @@ module TK.SpaceTac.UI.Specs { expect(shop_slot instanceof CharacterShopSlot).toBe(true); // sell - let equ1s = sheet.equipments.children[0]; + let equ1s = sheet.layer_equipments.children[0]; expect(equ1s.item).toBe(equ1); equ1s.applyDragDrop(cargo_slot, shop_slot, false); expect(ship.cargo).toEqual([]); @@ -37,7 +37,7 @@ module TK.SpaceTac.UI.Specs { expect(fleet.credits).toBe(220); // buy - let equ2s = sheet.equipments.children[1]; + let equ2s = sheet.layer_equipments.children[1]; expect(equ2s.item).toBe(equ2); equ2s.applyDragDrop(shop_slot, cargo_slot, false); expect(ship.cargo).toEqual([equ2]); @@ -45,7 +45,7 @@ module TK.SpaceTac.UI.Specs { expect(fleet.credits).toBe(100); // not enough money - equ1s = sheet.equipments.children[0]; + equ1s = sheet.layer_equipments.children[0]; expect(equ1s.item).toBe(equ1); equ1s.applyDragDrop(shop_slot, cargo_slot, false); expect(ship.cargo).toEqual([equ2]); diff --git a/src/ui/common/InputManager.ts b/src/ui/common/InputManager.ts index 63fb7d8..b2608f6 100644 --- a/src/ui/common/InputManager.ts +++ b/src/ui/common/InputManager.ts @@ -50,20 +50,22 @@ module TK.SpaceTac.UI { } }); - this.input.keyboard.addCallbacks(this, undefined, (event: KeyboardEvent) => { - if (this.debug) { - console.log(event); - } - - this.forceLeaveHovered(); - - if (!contains(["Control", "Shift", "Alt", "Meta"], event.key)) { - this.keyPress(event.key); - if (event.code != event.key) { - this.keyPress(event.code); + if (!this.game.headless) { + this.input.keyboard.addCallbacks(this, undefined, (event: KeyboardEvent) => { + if (this.debug) { + console.log(event); } - } - }); + + this.forceLeaveHovered(); + + if (!contains(["Control", "Shift", "Alt", "Meta"], event.key)) { + this.keyPress(event.key); + if (event.code != event.key) { + this.keyPress(event.code); + } + } + }); + } } /** diff --git a/src/ui/common/UIBuilder.spec.ts b/src/ui/common/UIBuilder.spec.ts index 869493c..beb9f81 100644 --- a/src/ui/common/UIBuilder.spec.ts +++ b/src/ui/common/UIBuilder.spec.ts @@ -111,6 +111,12 @@ module TK.SpaceTac.UI.Specs { check(["View layers", "base", 0], Phaser.Text, "", { shadowColor: "rgba(0,0,0,0)" }); check(["View layers", "base", 1], Phaser.Text, "", { shadowColor: "rgba(0,0,0,0.6)", shadowFill: true, shadowOffsetX: 3, shadowOffsetY: 4, shadowBlur: 6, shadowStroke: true }); + builder.clear(); + builder.text("", 0, 0, {}); + builder.text("", 0, 0, { stroke_width: 2, stroke_color: "#ff0000" }); + check(["View layers", "base", 0], Phaser.Text, "", { stroke: "black", strokeThickness: 0 }); + check(["View layers", "base", 1], Phaser.Text, "", { stroke: "#ff0000", strokeThickness: 2 }); + builder.clear(); builder.text("", 0, 0, {}); builder.text("", 0, 0, { bold: true }); diff --git a/src/ui/common/UIBuilder.ts b/src/ui/common/UIBuilder.ts index e9b5ee9..0aec167 100644 --- a/src/ui/common/UIBuilder.ts +++ b/src/ui/common/UIBuilder.ts @@ -5,6 +5,7 @@ module TK.SpaceTac.UI { export type UIText = Phaser.Text export type UIImage = Phaser.Image export type UIButton = Phaser.Button + export type UIGroup = Phaser.Group export type UIContainer = Phaser.Group | Phaser.Image /** @@ -14,6 +15,8 @@ module TK.SpaceTac.UI { size?: number color?: string shadow?: boolean + stroke_width?: number + stroke_color?: string bold?: boolean center?: boolean vcenter?: boolean @@ -33,6 +36,10 @@ module TK.SpaceTac.UI { // Shadow under the text shadow = false + // Stroke around the letters + stroke_width = 0 + stroke_color = "#ffffff" + // Bold text bold = false @@ -105,7 +112,7 @@ module TK.SpaceTac.UI { /** * Add a group of components */ - group(name: string, x = 0, y = 0): UIContainer { + group(name: string, x = 0, y = 0): UIGroup { let result = new Phaser.Group(this.game, undefined, name); result.position.set(x, y); this.add(result); @@ -132,6 +139,10 @@ module TK.SpaceTac.UI { if (style.shadow) { result.setShadow(3, 4, "rgba(0,0,0,0.6)", 6); } + if (style.stroke_width) { + result.stroke = style.stroke_color; + result.strokeThickness = style.stroke_width; + } this.add(result); return result; } @@ -154,7 +165,7 @@ module TK.SpaceTac.UI { /** * Add a clickable button */ - button(name: string, x = 0, y = 0, onclick?: Function): UIButton { + button(name: string, x = 0, y = 0, onclick?: Function, tooltip?: string | (() => string)): UIButton { let info = this.view.getImageInfo(name); let result = new Phaser.Button(this.game, x, y, info.key, onclick || nop, null, info.frame, info.frame); result.name = name; @@ -163,6 +174,13 @@ module TK.SpaceTac.UI { if (clickable) { UIComponent.setButtonSound(result); } + if (tooltip) { + if (typeof tooltip == "string") { + this.view.tooltip.bindStaticText(result, tooltip); + } else { + this.view.tooltip.bindDynamicText(result, tooltip); + } + } this.add(result); return result; }