Sync screenshot-driven mobile shipyard fixes

This commit is contained in:
Admin 2026-09-20 22:23:57 +00:00
parent 844e226aca
commit a6b7b541ec
5 changed files with 38 additions and 16 deletions

View File

@ -414,6 +414,10 @@ if (!isDesktopPointer.matches) {
filter: grayscale(.7); filter: grayscale(.7);
} }
#mobile-controls.controller-active .mobile-control.ship-action[hidden] {
display: none !important;
}
#mobile-controls.controller-active .mobile-control.ship-action.active, #mobile-controls.controller-active .mobile-control.ship-action.active,
#mobile-controls.controller-active .mobile-control.ship-action:active { #mobile-controls.controller-active .mobile-control.ship-action:active {
background: rgba(117, 78, 31, .9); background: rgba(117, 78, 31, .9);
@ -799,17 +803,17 @@ if (!isDesktopPointer.matches) {
const progression = state.progression || {}; const progression = state.progression || {};
if (buyButton) { if (buyButton) {
buyButton.hidden = !progression.docked;
buyButton.disabled = !progression.docked; buyButton.disabled = !progression.docked;
buyButton.textContent = progression.docked buyButton.textContent = 'SHIPYARD';
? 'SHIPYARD'
: 'SHIPYARD\nPORT';
} }
if (fitButton) { if (fitButton) {
const module = progression.salvageModule; const module = progression.salvageModule;
fitButton.hidden = !module;
fitButton.disabled = !module; fitButton.disabled = !module;
fitButton.textContent = module fitButton.textContent = module
? `FIT\n${module.name}` ? `FIT\n${module.name}`
: 'FIT\nEMPTY'; : 'FIT';
} }
const statusText = String(state.status || '').trim(); const statusText = String(state.status || '').trim();

View File

@ -79,6 +79,7 @@ export const buyAndInstallModule = (progression, moduleId) => {
export const marketForPort = (port) => { export const marketForPort = (port) => {
const stock = purchasableModules().filter((module) => { const stock = purchasableModules().filter((module) => {
if (module.slot === 'rudder') return true;
if (port?.type === 'industrial') return ['engine', 'hull', 'utility', 'cargo'].includes(module.slot); if (port?.type === 'industrial') return ['engine', 'hull', 'utility', 'cargo'].includes(module.slot);
if (port?.type === 'military') return ['weapon', 'hull', 'navigation'].includes(module.slot); if (port?.type === 'military') return ['weapon', 'hull', 'navigation'].includes(module.slot);
if (port?.type === 'fuel') return ['engine', 'navigation', 'cargo'].includes(module.slot); if (port?.type === 'fuel') return ['engine', 'navigation', 'cargo'].includes(module.slot);

View File

@ -65,8 +65,7 @@ overlay.innerHTML = `
`).join('')} `).join('')}
</div> </div>
<div class="shipyard-active-plate"> <div class="shipyard-active-plate">
<img src="${activeUrl}" alt="" draggable="false" /> <img src="${activeUrl}" alt="Sparrow active" draggable="false" />
<span data-shipyard="active">SPARROW ACTIVE</span>
</div> </div>
</div> </div>
</div> </div>
@ -253,15 +252,10 @@ style.textContent = `
transform: translateX(-50%); transform: translateX(-50%);
} }
.shipyard-active-plate img { display: block; width: 100%; } .shipyard-active-plate img {
.shipyard-active-plate span { display: block;
position: absolute; width: 100%;
inset: 0; height: auto;
display: grid;
place-items: center;
color: #b9efc0;
font-size: clamp(9px, 1.15vw, 13px);
letter-spacing: .08em;
} }
.shipyard-footer { .shipyard-footer {
@ -351,11 +345,21 @@ style.textContent = `
} }
@media (orientation: portrait) and (max-width: 760px) { @media (orientation: portrait) and (max-width: 760px) {
.shipyard-shell {
min-height: calc(100dvh - 8px);
display: flex;
flex-direction: column;
}
.shipyard-title { width: min(500px, 80vw); } .shipyard-title { width: min(500px, 80vw); }
.shipyard-grid { grid-template-columns: 1fr; } .shipyard-grid { grid-template-columns: 1fr; }
.shipyard-preview-wrap { display: none; } .shipyard-preview-wrap { display: none; }
.shipyard-upgrade-wrap { width: min(96vw, 520px); margin: 0 auto; } .shipyard-upgrade-wrap { width: min(96vw, 520px); margin: 0 auto; }
.shipyard-footer { margin-top: 48px; }
.shipyard-footer {
margin-top: auto;
padding: 58px 0 max(14px, env(safe-area-inset-bottom));
}
} }
`; `;
document.head.appendChild(style); document.head.appendChild(style);

View File

@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
import { import {
bestAffordableMarketUpgrade, bestAffordableMarketUpgrade,
buyAndInstallModule, buyAndInstallModule,
marketForPort,
collectSalvage, collectSalvage,
createStarterProgression, createStarterProgression,
deriveShipStats, deriveShipStats,
@ -58,6 +59,14 @@ test('market can select an affordable upgrade for a port', () => {
}); });
test('rudder is stocked at every port type so the shipyard card is actionable', () => {
const portTypes = ['industrial', 'military', 'fuel', 'trade'];
for (const type of portTypes) {
const stock = marketForPort({ type });
assert.ok(stock.some(({ id }) => id === 'reinforced-rudder'));
}
});
test('rudder tier matches shipyard art and improves handling', () => { test('rudder tier matches shipyard art and improves handling', () => {
const starter = { ...createStarterProgression(), credits: 650 }; const starter = { ...createStarterProgression(), credits: 650 };
const result = buyAndInstallModule(starter, 'reinforced-rudder'); const result = buyAndInstallModule(starter, 'reinforced-rudder');

View File

@ -280,6 +280,8 @@ test('mobile controls expose native buy and salvage-fit actions', async () => {
assert.match(game, /salvageModule: salvageModule \? \{/); assert.match(game, /salvageModule: salvageModule \? \{/);
assert.match(hud, /root\.querySelector\('\.mobile-control\.buy'\)/); assert.match(hud, /root\.querySelector\('\.mobile-control\.buy'\)/);
assert.match(hud, /root\.querySelector\('\.mobile-control\.fit'\)/); assert.match(hud, /root\.querySelector\('\.mobile-control\.fit'\)/);
assert.match(hud, /buyButton\.hidden = !progression\.docked/);
assert.match(hud, /fitButton\.hidden = !module/);
assert.match(hud, /buyButton\.disabled = !progression\.docked/); assert.match(hud, /buyButton\.disabled = !progression\.docked/);
assert.match(hud, /fitButton\.disabled = !module/); assert.match(hud, /fitButton\.disabled = !module/);
}); });
@ -299,6 +301,8 @@ test('shipyard overlay uses supplied component art and explicit module purchases
assert.match(shipyard, /shipyard-ui\/upgrades\.png\?url/); assert.match(shipyard, /shipyard-ui\/upgrades\.png\?url/);
assert.match(shipyard, /airship:shipyard-open/); assert.match(shipyard, /airship:shipyard-open/);
assert.match(shipyard, /dispatchAction\('purchase-module', \{ moduleId \}\)/); assert.match(shipyard, /dispatchAction\('purchase-module', \{ moduleId \}\)/);
assert.doesNotMatch(shipyard, /data-shipyard="active"/);
assert.match(shipyard, /min-height: calc\(100dvh - 8px\)/);
assert.match(game, /marketForPort\(port\)/); assert.match(game, /marketForPort\(port\)/);
assert.match(game, /airship:shipyard-state/); assert.match(game, /airship:shipyard-state/);
assert.match(game, /this\.shipyardOpen = true/); assert.match(game, /this\.shipyardOpen = true/);