1
0
Fork 0

story: Fixed initial contact ship being without equipment

This commit is contained in:
Michaël Lemaire 2017-12-12 23:17:25 +01:00
parent e51594214f
commit cda87c3f06
23 changed files with 354 additions and 49 deletions

View File

@ -12,7 +12,6 @@ Menu/settings/saves
Map/story
---------
* Initial contact has no equipment, and dies immediately in the first fight
* Add sound effects and more visual effects (jumps...)
* Add factions and reputation
* Allow to cancel secondary missions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -24,6 +24,32 @@
inkscape:export-ydpi="96">
<defs
id="defs2">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-164.92705 : 60.028537 : 0"
inkscape:vp_y="0 : 679.1556 : 0"
inkscape:vp_z="157.74885 : 76.939258 : 0"
inkscape:persp3d-origin="583.80424 : 781.51005 : 1"
id="perspective6177" />
<linearGradient
inkscape:collect="always"
id="linearGradient6135">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop6131" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop6133" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-26.772427 : 9.7443671 : 0"
inkscape:vp_y="0 : 110.24659 : 0"
inkscape:vp_z="25.607198 : 12.489466 : 0"
inkscape:persp3d-origin="100.91463 : 234.96764 : 1"
id="perspective5657" />
<linearGradient
inkscape:collect="always"
id="linearGradient4790">
@ -586,6 +612,136 @@
y1="227.4481"
x2="106.11795"
y2="227.4481" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Feather"
id="filter6117">
<feGaussianBlur
stdDeviation="2.39071"
result="blur"
id="feGaussianBlur6109" />
<feComposite
in="SourceGraphic"
in2="blur"
operator="atop"
result="composite1"
id="feComposite6111" />
<feComposite
in2="composite1"
operator="in"
result="composite2"
id="feComposite6113" />
<feComposite
in2="composite2"
operator="in"
result="composite3"
id="feComposite6115" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6135"
id="linearGradient6137"
x1="94.919073"
y1="17.197918"
x2="104.65235"
y2="17.197918"
gradientUnits="userSpaceOnUse" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter9551-3">
<feFlood
flood-opacity="0.211765"
flood-color="rgb(255,255,255)"
result="flood"
id="feFlood9541-6" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="out"
result="composite1"
id="feComposite9543-7" />
<feGaussianBlur
in="composite1"
stdDeviation="1.4"
result="blur"
id="feGaussianBlur9545-5" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset9547-3" />
<feComposite
in="offset"
in2="SourceGraphic"
operator="atop"
result="composite2"
id="feComposite9549-5" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter6205">
<feFlood
flood-opacity="0.211765"
flood-color="rgb(255,255,255)"
result="flood"
id="feFlood6195" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="out"
result="composite1"
id="feComposite6197" />
<feGaussianBlur
in="composite1"
stdDeviation="1.4"
result="blur"
id="feGaussianBlur6199" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset6201" />
<feComposite
in="offset"
in2="SourceGraphic"
operator="atop"
result="composite2"
id="feComposite6203" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter7193-6">
<feFlood
flood-opacity="1"
flood-color="rgb(255,255,255)"
result="flood"
id="feFlood7183-2" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite7185-9" />
<feGaussianBlur
in="composite1"
stdDeviation="0.4"
result="blur"
id="feGaussianBlur7187-1" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset7189-2" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite7191-7" />
</filter>
</defs>
<sodipodi:namedview
id="base"
@ -594,11 +750,11 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.9899495"
inkscape:cx="851.7681"
inkscape:cy="453.52267"
inkscape:zoom="1.979899"
inkscape:cx="539.66193"
inkscape:cy="989.27912"
inkscape:document-units="px"
inkscape:current-layer="layer2"
inkscape:current-layer="layer10"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1037"
@ -666,7 +822,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -1788,4 +1944,140 @@
sodipodi:role="line">X</tspan></text>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer10"
inkscape:label="Creation View"
style="display:inline">
<g
id="g6129"
transform="matrix(0.61352657,0,0,0.61352657,38.14638,1.3548715)"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/graphics/exported/character/random-off.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<rect
ry="4.8755188"
y="4.1146145"
x="86.745537"
height="26.080357"
width="26.080357"
id="rect5651"
style="fill:#708683;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter9551)" />
<g
style="opacity:1;vector-effect:none;fill:#282c2e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;filter:url(#filter6117)"
inkscape:corner7="0.061260645 : 0.2867262 : 0.25398508 : 1"
inkscape:corner0="0.30566585 : 0.35934224 : 0 : 1"
inkscape:perspectiveID="#perspective5657"
id="g5743"
sodipodi:type="inkscape:box3d">
<path
style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-width:0.33371606px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
points="88.968984,24.185411 99.569742,19.015076 99.569742,5.9664628 88.968984,11.136798 "
d="M 88.968984,11.136798 V 24.185411 L 99.569742,19.015076 V 5.9664628 Z"
inkscape:box3dsidetype="6"
id="path5745"
sodipodi:type="inkscape:box3dside" />
<path
style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:0.33371606px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
points="110.23484,9.8482409 110.23484,22.896854 99.569742,19.015076 99.569742,5.9664628 "
d="M 99.569742,5.9664628 110.23484,9.8482409 V 22.896854 L 99.569742,19.015076 Z"
inkscape:box3dsidetype="11"
id="path5755"
sodipodi:type="inkscape:box3dside" />
<path
style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-width:0.33371606px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
points="99.634082,28.067189 110.23484,22.896854 99.569742,19.015076 88.968984,24.185411 "
d="M 88.968984,24.185411 99.634082,28.067189 110.23484,22.896854 99.569742,19.015076 Z"
inkscape:box3dsidetype="13"
id="path5753"
sodipodi:type="inkscape:box3dside" />
<path
style="fill:#b9b9b9;fill-opacity:1;stroke-width:3.33716083"
points="99.634082,15.018576 110.23484,9.8482409 99.569742,5.9664628 88.968984,11.136798 "
d="M 88.968984,11.136798 99.634082,15.018576 110.23484,9.8482409 99.569742,5.9664628 Z"
inkscape:box3dsidetype="5"
id="path5747"
sodipodi:type="inkscape:box3dside" />
<path
style="fill:#bfbfbf;fill-opacity:1;stroke-width:3.33716083"
points="99.634082,28.067189 110.23484,22.896854 110.23484,9.8482409 99.634082,15.018576 "
d="M 99.634082,15.018576 V 28.067189 L 110.23484,22.896854 V 9.8482409 Z"
inkscape:box3dsidetype="14"
id="path5751"
sodipodi:type="inkscape:box3dside" />
<path
style="fill:#454545;fill-opacity:1;stroke-width:3.33716083"
points="99.634082,15.018576 99.634082,28.067189 88.968984,24.185411 88.968984,11.136798 "
d="m 88.968984,11.136798 10.665098,3.881778 V 28.067189 L 88.968984,24.185411 Z"
inkscape:box3dsidetype="3"
id="path5749"
sodipodi:type="inkscape:box3dside" />
</g>
<text
id="text5655"
y="23.547918"
x="95.899513"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.93333435px;line-height:6.61458349px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;fill:#e4e47d;fill-opacity:1;stroke:#000000;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.3;filter:url(#filter7193)"
xml:space="preserve"><tspan
style="fill:#e4e47d;fill-opacity:1;stroke:#000000;stroke-width:0.79374999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.3"
y="23.547918"
x="95.899513"
id="tspan5653"
sodipodi:role="line">?</tspan></text>
</g>
<g
id="g6277">
<g
id="g6282"
transform="translate(-8.5526249)">
<use
transform="matrix(0.61352658,0,0,0.61352658,249.02204,1.3813324)"
style="display:inline"
x="0"
y="0"
xlink:href="#rect5651"
id="use6179"
width="100%"
height="100%" />
<text
transform="matrix(0.61352658,0,0,0.61352658,248.36688,1.7818954)"
id="text5655-0"
y="23.547918"
x="95.899513"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.93333435px;line-height:6.61458349px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#e4e47d;fill-opacity:1;stroke:#000000;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.3;filter:url(#filter7193-6)"
xml:space="preserve"><tspan
style="fill:#e4e47d;fill-opacity:1;stroke:#000000;stroke-width:0.79374999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.3"
y="23.547918"
x="95.899513"
id="tspan5653-9"
sodipodi:role="line">...</tspan></text>
</g>
</g>
<g
mask="url(#mask4786)"
inkscape:export-ydpi="91.800003"
inkscape:export-xdpi="91.800003"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/graphics/exported/character/validate.png"
id="g6290"
style="display:inline;opacity:1"
transform="translate(0,-11.249983)">
<circle
style="fill:#3e3e3e;fill-opacity:1;stroke:#c1c1c1;stroke-width:0.79374999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter7193)"
id="circle6284"
cx="508"
cy="11.249985"
r="20.847025" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10.58333397px;line-height:6.61458349px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;fill:#ff000d;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7193)"
x="493.37958"
y="23.716892"
id="text6288"><tspan
sodipodi:role="line"
id="tspan6286"
x="493.37958"
y="23.716892"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:DAGGERSQUARE;-inkscape-font-specification:'DAGGERSQUARE Bold';fill:#5eda48;fill-opacity:1;stroke-width:0.26458335px">OK</tspan></text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -61,7 +61,7 @@ module TK.SpaceTac {
}
jasmineToString() {
return this.attached_to ? `${this.attached_to.ship.getFullName()} - ${this.name}` : this.name;
return this.attached_to ? `${this.attached_to.ship.getName()} - ${this.name}` : this.name;
}
/**

View File

@ -26,7 +26,7 @@ module TK.SpaceTac {
}
jasmineToString(): string {
return `${this.player.name}'s fleet [${this.ships.map(ship => ship.getFullName()).join(",")}]`;
return `${this.player.name}'s fleet [${this.ships.map(ship => ship.getName()).join(",")}]`;
}
/**

View File

@ -2,16 +2,20 @@ module TK.SpaceTac.Specs {
testing("Ship", test => {
test.case("creates a full name", check => {
let ship = new Ship();
check.equals(ship.getFullName(false), "Level 1 unnamed");
check.equals(ship.getName(false), "Ship");
check.equals(ship.getName(true), "Level 1 Ship");
ship.name = "Titan";
check.equals(ship.getFullName(false), "Level 1 Titan");
ship.model = new ShipModel("test", "Hauler");
check.equals(ship.getName(false), "Hauler");
check.equals(ship.getName(true), "Level 1 Hauler");
ship.name = "Titan-W12";
check.equals(ship.getName(false), "Titan-W12");
check.equals(ship.getName(true), "Level 1 Titan-W12");
ship.level.forceLevel(3);
check.equals(ship.getFullName(false), "Level 3 Titan");
ship.fleet.player.name = "Emperor";
check.equals(ship.getFullName(true), "Emperor's Level 3 Titan");
check.equals(ship.getName(false), "Titan-W12");
check.equals(ship.getName(true), "Level 3 Titan-W12");
});
test.case("moves in the arena", check => {

View File

@ -12,8 +12,8 @@ module TK.SpaceTac {
level = new ShipLevel()
skills = new ShipSkills()
// Name of the ship
name: string
// Name of the ship, null if unimportant
name: string | null
// Code of the ShipModel used to create it
model: ShipModel
@ -57,7 +57,7 @@ module TK.SpaceTac {
play_priority = 0;
// Create a new ship inside a fleet
constructor(fleet: Fleet | null = null, name = "unnamed", model = new ShipModel("default", "Default", 1, 0, false, 0)) {
constructor(fleet: Fleet | null = null, name: string | null = null, model = new ShipModel("default", "Ship", 1, 0, false, 0)) {
super();
this.fleet = fleet || new Fleet();
@ -84,11 +84,11 @@ module TK.SpaceTac {
}
/**
* Returns the full name of this ship
* Returns the name of this ship
*/
getFullName(owner = true): string {
let result = `Level ${this.level.get()} ${this.name}`;
return owner ? `${this.fleet.player.name}'s ${result}` : result;
getName(level = true): string {
let name = this.name || this.model.name;
return level ? `Level ${this.level.get()} ${name}` : name;
}
// Returns true if the ship is able to play

View File

@ -3,7 +3,7 @@ module TK.SpaceTac.Specs {
test.case("can use ship model", check => {
var gen = new ShipGenerator();
var model = new ShipModel("test", "Test", 1, 2, true, 3);
var ship = gen.generate(1, model);
var ship = gen.generate(1, model, false);
check.same(ship.model, model);
check.equals(ship.cargo_space, 2);
check.equals(ship.slots.length, 7);

View File

@ -17,7 +17,7 @@ module TK.SpaceTac {
*
* If *force_damage_equipment, at least one "damaging" weapon will be chosen
*/
generate(level: number, model: ShipModel | null = null, upgrade = false, force_damage_equipment = true): Ship {
generate(level: number, model: ShipModel | null = null, upgrade = true, force_damage_equipment = true): Ship {
if (!model) {
// Get a random model
model = ShipModel.getRandomModel(level, this.random);

View File

@ -43,6 +43,8 @@ module TK.SpaceTac.Specs {
checkPart(story, 3, /^Go with .* in .* system$/);
check.same(fleet.ships.length, fleet_size + 1);
check.same(fleet.ships[fleet_size].critical, true);
check.greater(fleet.ships[fleet_size].getAttribute("hull_capacity"), 0);
goTo(fleet, (<MissionPartEscort>story.current_part).destination);
checkPart(story, 4, /^Listen to .*$/);

View File

@ -15,6 +15,7 @@ module TK.SpaceTac {
let random = RandomGenerator.global;
let start_location = nn(fleet.location);
let mission_generator = new MissionGenerator(universe, start_location);
// Arrival
let conversation = this.addPart(new MissionPartConversation(this, [], "Travel to Terranax galaxy"));
@ -24,7 +25,7 @@ module TK.SpaceTac {
// 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));
let contact_character = mission_generator.generateShip(1);
contact_character.fleet.setLocation(contact_location, true);
this.addPart(new MissionPartGoTo(this, contact_location, `Find your contact in ${contact_location.star.name}`, MissionPartDestinationHint.SYSTEM));
conversation = this.addPart(new MissionPartConversation(this, [contact_character], "Speak with your contact"));

View File

@ -1,17 +1,17 @@
module TK.SpaceTac {
const POOL_SHIP_NAMES = [
"Zert",
"Ob'tec",
"Paayk",
"Fen_amr",
"TempZst",
"croNt",
"Appn",
"Vertix",
"Opan-vel",
"Yz-aol",
"Arkant",
"PNX",
"Zert", "Zark", "Zeem",
"Ob'tec", "Ob'vac", "Ob'sig",
"Paayk", "Paakt",
"Fen_amr", "Fin_am", "Fen_AA",
"TempZst", "TriZth",
"croNt", "coRzt",
"Appn", "Appq",
"Vertix", "Vortix",
"Opan-vel", "Ipal-ven", "Epan-vek",
"Yz-aol", "Yz-aib",
"Arkant", "Arkyan",
"PNX", "PGV", "PXT", "PRZ",
]
/**
@ -47,13 +47,20 @@ module TK.SpaceTac {
return result;
}
/**
* Generate a character name
*/
static generateCharacterName(random = RandomGenerator.global): string {
return `${random.choice(POOL_SHIP_NAMES)}-${random.randInt(10, 999)}`;
}
/**
* Generate a new ship that may be used in a mission
*/
generateShip(level: number) {
let generator = new ShipGenerator(this.random);
let result = generator.generate(level, null, true);
result.name = `${this.random.choice(POOL_SHIP_NAMES)}-${this.random.randInt(10, 999)}`;
result.name = MissionGenerator.generateCharacterName(this.random);
return result;
}

View File

@ -27,7 +27,7 @@ module TK.SpaceTac.UI.Specs {
check.contains(images, "ship-fake-portrait");
check.contains(images, "equipment-equipment");
check.equals(texts, [
"Phil's Level 1 Fury", "Plays in 2 turns",
"Level 1 Fury", "Plays in 2 turns",
"7", "3", "9", "max", "12", "57", "max", "58", "100", "max", "140",
"Active effects", "• hull capacity +50", "• damage -15% for 3 turns", "• limit precision to 10",
"Weapons", "equipment Mk1"

View File

@ -31,7 +31,7 @@ module TK.SpaceTac.UI {
});
let enemy = !ship.getPlayer().is(this.battleview.player);
builder.text(ship.getFullName(), 168, 0, { color: enemy ? "#cc0d00" : "#ffffff", size: 22, bold: true });
builder.text(ship.getName(), 168, 0, { color: enemy ? "#cc0d00" : "#ffffff", size: 22, bold: true });
if (ship.alive) {
let turns = this.battleview.battle.getPlayOrder(ship);

View File

@ -26,7 +26,7 @@ module TK.SpaceTac.UI {
this.levelup.visible = this.ship.getAvailableUpgradePoints() > 0;
this.addChild(this.levelup);
sheet.view.tooltip.bindDynamicText(this, () => ship.getFullName());
sheet.view.tooltip.bindDynamicText(this, () => ship.getName());
}
/**

View File

@ -28,14 +28,14 @@ module TK.SpaceTac.UI.Specs {
check.equals(sheet.x, 0);
check.equals(sheet.portraits.length, 2);
check.equals(sheet.ship_name.text, "Player's Level 1 Ship 1");
check.equals(sheet.ship_name.text, "Ship 1");
check.equals(sheet.ship_slots.length, 4);
check.equals(sheet.ship_cargo.length, 3);
let portrait = <Phaser.Button>sheet.portraits.getChildAt(1);
portrait.onInputUp.dispatch();
check.equals(sheet.ship_name.text, "Player's Level 1 Ship 2");
check.equals(sheet.ship_name.text, "Ship 2");
check.equals(sheet.ship_slots.length, 1);
check.equals(sheet.ship_cargo.length, 2);
});
@ -126,7 +126,7 @@ module TK.SpaceTac.UI.Specs {
ship.critical = true;
sheet.show(ship);
check.equals(sheet.isInteractive(), false, "critical ship");
ship.critical = false;
sheet.show(ship);
check.equals(sheet.isInteractive(), true, "normal ship");
@ -139,7 +139,7 @@ module TK.SpaceTac.UI.Specs {
sheet.show(ship, undefined, undefined, true);
check.equals(sheet.isInteractive(), true, "interactivity reenabled");
});
});
});
test.case("fits slots in area", check => {

View File

@ -216,7 +216,7 @@ module TK.SpaceTac.UI {
let upgrade_points = ship.getAvailableUpgradePoints();
this.ship_name.setText(ship.getFullName());
this.ship_name.setText(ship.getName(false));
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());

View File

@ -15,8 +15,8 @@ module TK.SpaceTac.UI {
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.addShip(new Ship(null, MissionGenerator.generateCharacterName(), models[0]));
this.built_fleet.addShip(new Ship(null, MissionGenerator.generateCharacterName(), models[1]));
this.built_fleet.credits = this.built_fleet.ships.length * 1000;
let basic_equipments = () => {

View File

@ -136,7 +136,7 @@ module TK.SpaceTac.UI {
setCurrentShipMessage(ship: Ship, content: string): void {
let style = new UIConversationStyle();
style.image = `ship-${ship.model.code}-portrait`;
style.image_caption = ship.name;
style.image_caption = ship.getName(false);
style.image_size = 256;
let own = ship.getPlayer() == this.view.gameui.session.player;