diff --git a/TODO b/TODO deleted file mode 100644 index 01b39e3..0000000 --- a/TODO +++ /dev/null @@ -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 diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..934dda3 --- /dev/null +++ b/TODO.md @@ -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 diff --git a/src/core/Fleet.spec.ts b/src/core/Fleet.spec.ts index 8d43906..10009d4 100644 --- a/src/core/Fleet.spec.ts +++ b/src/core/Fleet.spec.ts @@ -14,6 +14,39 @@ module TS.SpaceTac { 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 () { let fleet = new Fleet(); let system1 = new Star(); @@ -48,5 +81,32 @@ module TS.SpaceTac { expect(result).toBe(true); 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); + }); }); } diff --git a/src/core/Fleet.ts b/src/core/Fleet.ts index ee369f1..08dcd80 100644 --- a/src/core/Fleet.ts +++ b/src/core/Fleet.ts @@ -25,6 +25,10 @@ module TS.SpaceTac { this.ships = []; } + jasmineToString(): string { + return `${this.player.name}'s fleet [${this.ships.map(ship => ship.name).join(",")}]`; + } + /** * Set the current location of the fleet * @@ -48,7 +52,9 @@ module TS.SpaceTac { return true; } - // Add a ship in this fleet + /** + * Add a ship this fleet + */ addShip(ship = new Ship()): Ship { if (ship.fleet && ship.fleet != this) { remove(ship.fleet.ships, ship); @@ -58,6 +64,15 @@ module TS.SpaceTac { 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 setBattle(battle: Battle | null): void { this.battle = battle; @@ -77,15 +92,15 @@ module TS.SpaceTac { 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 { - var count = 0; - this.ships.forEach((ship: Ship) => { - if (ship.alive) { - count += 1; - } - }); - return (count > 0); + if (any(this.ships, ship => ship.critical && !ship.alive)) { + return false; + } else { + return any(this.ships, ship => ship.alive); + } } } } diff --git a/src/core/GameSession.spec.ts b/src/core/GameSession.spec.ts index 0733fb3..6fa5e2b 100644 --- a/src/core/GameSession.spec.ts +++ b/src/core/GameSession.spec.ts @@ -86,7 +86,7 @@ module TS.SpaceTac.Specs { expect(session.start_location.encounter_gen).toBe(true); 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.location).toBe(session.start_location); }); diff --git a/src/core/GameSession.ts b/src/core/GameSession.ts index 596d1cb..025574a 100644 --- a/src/core/GameSession.ts +++ b/src/core/GameSession.ts @@ -5,7 +5,7 @@ module TS.SpaceTac { * This represents the current state of game */ export class GameSession { - // "Hopefully"" unique session id + // "Hopefully" unique session id id: string // Game universe @@ -81,7 +81,7 @@ module TS.SpaceTac { this.player.fleet = fleet; } else { 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); diff --git a/src/core/Ship.ts b/src/core/Ship.ts index a1b4f14..5f48411 100644 --- a/src/core/Ship.ts +++ b/src/core/Ship.ts @@ -70,6 +70,9 @@ module TS.SpaceTac { // Flag indicating if the ship is alive alive: boolean + // Flag indicating that the ship is mission critical (escorted ship) + critical = false + // Position in the arena arena_x: number arena_y: number diff --git a/src/core/missions/MainStory.spec.ts b/src/core/missions/MainStory.spec.ts index afbc3aa..987dffb 100644 --- a/src/core/missions/MainStory.spec.ts +++ b/src/core/missions/MainStory.spec.ts @@ -1,9 +1,11 @@ module TS.SpaceTac.Specs { 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.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) { @@ -25,12 +27,21 @@ module TS.SpaceTac.Specs { let missions = session.player.missions; let story = nn(missions.main); - checkPart(story, 0, "^Find your contact in .* system$"); - - goTo(fleet, (story.current_part).destination); - checkPart(story, 1, "^Speak with your contact .*$"); + let fleet_size = fleet.ships.length; + checkPart(story, 0, "^Travel to Terranax galaxy$"); (story.current_part).skip(); + + checkPart(story, 1, "^Find your contact in .*$"); + goTo(fleet, (story.current_part).destination); + + checkPart(story, 2, "^Speak with your contact"); + (story.current_part).skip(); + + checkPart(story, 3, "^Go with .* in .* system$"); + expect(fleet.ships.length).toBe(fleet_size + 1); + goTo(fleet, (story.current_part).destination); + expect(story.checkStatus()).toBe(false, "story not complete"); }) }) diff --git a/src/core/missions/MainStory.ts b/src/core/missions/MainStory.ts index 179f8a1..12051f0 100644 --- a/src/core/missions/MainStory.ts +++ b/src/core/missions/MainStory.ts @@ -16,12 +16,18 @@ module TS.SpaceTac { let random = RandomGenerator.global; 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 let contact_location = randomLocation(random, [start_location.star], [start_location]); let contact_character = new Ship(null, "Osten-37", ShipModel.getRandomModel(1, random)); contact_character.fleet.setLocation(contact_location, true); - this.addPart(new MissionPartGoTo(this, contact_location, "Find your contact")); - let dialog = this.addPart(new MissionPartDialog(this, [contact_character], "Speak with your contact")); + this.addPart(new MissionPartGoTo(this, contact_location, `Find your contact in ${contact_location.star.name}`)); + dialog = this.addPart(new MissionPartDialog(this, [contact_character], "Speak with your contact")); 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(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, "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..."); + + // 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`)); } } } diff --git a/src/core/missions/Mission.ts b/src/core/missions/Mission.ts index f8f2f92..734c83c 100644 --- a/src/core/missions/Mission.ts +++ b/src/core/missions/Mission.ts @@ -53,12 +53,15 @@ module TS.SpaceTac { if (this.completed) { return false; } else if (this.current_part.checkCompleted()) { + this.current_part.onEnded(); + let current_index = this.parts.indexOf(this.current_part); if (current_index < 0 || current_index >= this.parts.length - 1) { this.completed = true; return false; } else { this.current_part = this.parts[current_index + 1]; + this.current_part.onStarted(); return true; } } else { diff --git a/src/core/missions/MissionPart.ts b/src/core/missions/MissionPart.ts index 6f95750..475295c 100644 --- a/src/core/missions/MissionPart.ts +++ b/src/core/missions/MissionPart.ts @@ -35,5 +35,17 @@ module TS.SpaceTac { */ forceComplete(): void { } + + /** + * Event called when the part starts + */ + onStarted(): void { + } + + /** + * Event called when the part ends + */ + onEnded(): void { + } } } diff --git a/src/core/missions/MissionPartDialog.spec.ts b/src/core/missions/MissionPartDialog.spec.ts index 204a924..0ed8087 100644 --- a/src/core/missions/MissionPartDialog.spec.ts +++ b/src/core/missions/MissionPartDialog.spec.ts @@ -1,11 +1,11 @@ module TS.SpaceTac.Specs { - describe("MissionPartGoTo", () => { + describe("MissionPartDialog", () => { it("advances through dialog", function () { let universe = new Universe(); let fleet = new Fleet(); let ship1 = new Ship(null, "Tim"); 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.checkCompleted()).toBe(true, "No dialog piece"); diff --git a/src/core/missions/MissionPartDialog.ts b/src/core/missions/MissionPartDialog.ts index 07c0c39..e9295d7 100644 --- a/src/core/missions/MissionPartDialog.ts +++ b/src/core/missions/MissionPartDialog.ts @@ -25,8 +25,8 @@ module TS.SpaceTac { // Current piece current_piece = 0 - constructor(mission: Mission, interlocutors: Ship[], directive = "Speak with") { - super(mission, `${directive} ${interlocutors[0].name}`); + constructor(mission: Mission, interlocutors: Ship[], directive?: string) { + super(mission, directive || `Speak with ${interlocutors[0].name}`); this.interlocutors = interlocutors; } diff --git a/src/core/missions/MissionPartEscort.spec.ts b/src/core/missions/MissionPartEscort.spec.ts new file mode 100644 index 0000000..f81e5bb --- /dev/null +++ b/src/core/missions/MissionPartEscort.spec.ts @@ -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); + }) + }) +} diff --git a/src/core/missions/MissionPartEscort.ts b/src/core/missions/MissionPartEscort.ts new file mode 100644 index 0000000..86bc36f --- /dev/null +++ b/src/core/missions/MissionPartEscort.ts @@ -0,0 +1,29 @@ +/// + +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); + } + } +} diff --git a/src/core/missions/MissionPartGoTo.spec.ts b/src/core/missions/MissionPartGoTo.spec.ts index b7a622b..193bead 100644 --- a/src/core/missions/MissionPartGoTo.spec.ts +++ b/src/core/missions/MissionPartGoTo.spec.ts @@ -6,9 +6,9 @@ module TS.SpaceTac.Specs { let universe = new Universe(); 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"); fleet.setLocation(destination, true); @@ -32,6 +32,7 @@ module TS.SpaceTac.Specs { let fleet = new Fleet(); let part = new MissionPartGoTo(new Mission(universe, fleet), destination, "Investigate"); + expect(part.title).toEqual("Investigate"); expect(part.checkCompleted()).toBe(false); part.forceComplete(); expect(part.checkCompleted()).toBe(true); diff --git a/src/core/missions/MissionPartGoTo.ts b/src/core/missions/MissionPartGoTo.ts index 87ef954..df656bb 100644 --- a/src/core/missions/MissionPartGoTo.ts +++ b/src/core/missions/MissionPartGoTo.ts @@ -7,8 +7,8 @@ module TS.SpaceTac { export class MissionPartGoTo extends MissionPart { destination: StarLocation - constructor(mission: Mission, destination: StarLocation, directive: string, hint = true) { - super(mission, hint ? `${directive} in ${destination.star.name} system` : directive); + constructor(mission: Mission, destination: StarLocation, directive?: string) { + super(mission, directive || `Go to ${destination.star.name} system`); this.destination = destination; } diff --git a/src/ui/character/CharacterSheet.ts b/src/ui/character/CharacterSheet.ts index 1413105..77cb77f 100644 --- a/src/ui/character/CharacterSheet.ts +++ b/src/ui/character/CharacterSheet.ts @@ -201,6 +201,9 @@ module TS.SpaceTac.UI { 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); + 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; }