Sync steampunk island asset pack
BIN
src/assets/islands/dock_island.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
src/assets/islands/medium_island.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
src/assets/islands/pirate_island.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
src/assets/islands/rocky_island.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
src/assets/islands/small_island.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
src/assets/islands/trading_outpost_island.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
src/assets/islands/volcanic_island.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
@ -4,6 +4,13 @@ import airshipSheetUrl from './assets/airships.svg?url';
|
|||||||
import playerAirshipUrl from './assets/player-airship-4frame.png?url';
|
import playerAirshipUrl from './assets/player-airship-4frame.png?url';
|
||||||
import explosionSheetUrl from './assets/explosions.svg?url';
|
import explosionSheetUrl from './assets/explosions.svg?url';
|
||||||
import cannonballUrl from './assets/cannonball.png?url';
|
import cannonballUrl from './assets/cannonball.png?url';
|
||||||
|
import smallIslandUrl from './assets/islands/small_island.png?url';
|
||||||
|
import mediumIslandUrl from './assets/islands/medium_island.png?url';
|
||||||
|
import rockyIslandUrl from './assets/islands/rocky_island.png?url';
|
||||||
|
import pirateIslandUrl from './assets/islands/pirate_island.png?url';
|
||||||
|
import dockIslandUrl from './assets/islands/dock_island.png?url';
|
||||||
|
import tradingOutpostIslandUrl from './assets/islands/trading_outpost_island.png?url';
|
||||||
|
import volcanicIslandUrl from './assets/islands/volcanic_island.png?url';
|
||||||
import { airshipLog, registerDebugSnapshot } from './diagnostics.js';
|
import { airshipLog, registerDebugSnapshot } from './diagnostics.js';
|
||||||
import {
|
import {
|
||||||
DEFAULT_PIRATE_SHOT_DAMAGE,
|
DEFAULT_PIRATE_SHOT_DAMAGE,
|
||||||
@ -190,6 +197,13 @@ class GameScene extends Phaser.Scene {
|
|||||||
frameHeight: 64,
|
frameHeight: 64,
|
||||||
});
|
});
|
||||||
this.load.image('cannonball', cannonballUrl);
|
this.load.image('cannonball', cannonballUrl);
|
||||||
|
this.load.image('island-small', smallIslandUrl);
|
||||||
|
this.load.image('island-medium', mediumIslandUrl);
|
||||||
|
this.load.image('island-rocky', rockyIslandUrl);
|
||||||
|
this.load.image('island-pirate', pirateIslandUrl);
|
||||||
|
this.load.image('island-dock', dockIslandUrl);
|
||||||
|
this.load.image('island-trading-outpost', tradingOutpostIslandUrl);
|
||||||
|
this.load.image('island-volcanic', volcanicIslandUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
@ -1359,6 +1373,22 @@ class GameScene extends Phaser.Scene {
|
|||||||
return pirate;
|
return pirate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
islandTextureFor(island) {
|
||||||
|
const port = this.world.ports.find(({ islandId }) => islandId === island.id);
|
||||||
|
const pirateHaven = this.world.pirateTerritories
|
||||||
|
.some(({ havenIslandId }) => havenIslandId === island.id);
|
||||||
|
|
||||||
|
if (pirateHaven) return 'island-pirate';
|
||||||
|
if (port?.type === 'capital') return 'island-trading-outpost';
|
||||||
|
if (port?.type === 'industrial' || port?.type === 'fuel') return 'island-trading-outpost';
|
||||||
|
if (port?.type === 'military' || port?.type === 'trade') return 'island-dock';
|
||||||
|
if (island.resource === 'fuel') return 'island-volcanic';
|
||||||
|
if (island.resource === 'ore') return 'island-rocky';
|
||||||
|
|
||||||
|
const islandNumber = Number(String(island.id).split('-').pop()) || 0;
|
||||||
|
return islandNumber % 2 === 0 ? 'island-medium' : 'island-small';
|
||||||
|
}
|
||||||
|
|
||||||
drawWorld() {
|
drawWorld() {
|
||||||
const bg = this.add.graphics();
|
const bg = this.add.graphics();
|
||||||
bg.fillStyle(0x132335, 1);
|
bg.fillStyle(0x132335, 1);
|
||||||
@ -1383,42 +1413,41 @@ class GameScene extends Phaser.Scene {
|
|||||||
routeGraphics.lineBetween(route.from.x, route.from.y, route.to.x, route.to.y);
|
routeGraphics.lineBetween(route.from.x, route.from.y, route.to.x, route.to.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
const islandGraphics = this.add.graphics().setDepth(2);
|
|
||||||
for (const island of this.world.islands) {
|
for (const island of this.world.islands) {
|
||||||
islandGraphics.fillStyle(0x5d694e, 1);
|
const texture = this.islandTextureFor(island);
|
||||||
islandGraphics.fillEllipse(
|
const art = this.add.image(island.x, island.y, texture).setDepth(2);
|
||||||
island.x,
|
const targetDiameter = island.radius * 2.45;
|
||||||
island.y + 8,
|
const sourceSize = Math.max(1, art.width, art.height);
|
||||||
island.radius * 2.15,
|
art.setScale(targetDiameter / sourceSize);
|
||||||
island.radius * 1.45,
|
art.setData('islandId', island.id);
|
||||||
);
|
art.setData('collisionRadius', island.radius);
|
||||||
islandGraphics.fillStyle(0xa18a62, 1);
|
|
||||||
islandGraphics.fillEllipse(
|
|
||||||
island.x,
|
|
||||||
island.y,
|
|
||||||
island.radius * 1.78,
|
|
||||||
island.radius * 1.08,
|
|
||||||
);
|
|
||||||
islandGraphics.fillStyle(0x283827, 0.95);
|
|
||||||
islandGraphics.fillCircle(
|
|
||||||
island.x - island.radius * 0.2,
|
|
||||||
island.y - island.radius * 0.18,
|
|
||||||
Math.max(8, island.radius * 0.16),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const port of this.world.ports) {
|
for (const port of this.world.ports) {
|
||||||
const portMarker = this.add.circle(port.x, port.y, 9, 0xf4d27a, 0.95)
|
const island = this.world.islands.find(({ id }) => id === port.islandId);
|
||||||
|
const markerOffset = Math.max(20, (island?.radius || 90) * 0.62);
|
||||||
|
const portMarker = this.add.circle(
|
||||||
|
port.x + markerOffset * 0.38,
|
||||||
|
port.y + markerOffset * 0.32,
|
||||||
|
7,
|
||||||
|
0xf4d27a,
|
||||||
|
0.95,
|
||||||
|
)
|
||||||
.setStrokeStyle(2, 0x4a3016, 0.95)
|
.setStrokeStyle(2, 0x4a3016, 0.95)
|
||||||
.setDepth(3);
|
.setDepth(3);
|
||||||
portMarker.setData('portId', port.id);
|
portMarker.setData('portId', port.id);
|
||||||
this.add.text(port.x + 13, port.y - 8, port.name, {
|
this.add.text(
|
||||||
|
port.x - (island?.radius || 90) * 0.68,
|
||||||
|
port.y + (island?.radius || 90) * 0.72,
|
||||||
|
port.name,
|
||||||
|
{
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
color: '#f5df9a',
|
color: '#f5df9a',
|
||||||
backgroundColor: '#071018aa',
|
backgroundColor: '#071018cc',
|
||||||
padding: { x: 3, y: 2 },
|
padding: { x: 4, y: 3 },
|
||||||
}).setDepth(3);
|
},
|
||||||
|
).setDepth(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,26 @@ import { readFile } from 'node:fs/promises';
|
|||||||
|
|
||||||
const read = (path) => readFile(new URL(`../${path}`, import.meta.url), 'utf8');
|
const read = (path) => readFile(new URL(`../${path}`, import.meta.url), 'utf8');
|
||||||
|
|
||||||
|
test('procedural world renders uploaded steampunk island variants while keeping soft terrain physics', async () => {
|
||||||
|
const source = await read('src/game-v4.js');
|
||||||
|
|
||||||
|
assert.match(source, /small_island\.png\?url/);
|
||||||
|
assert.match(source, /medium_island\.png\?url/);
|
||||||
|
assert.match(source, /rocky_island\.png\?url/);
|
||||||
|
assert.match(source, /pirate_island\.png\?url/);
|
||||||
|
assert.match(source, /dock_island\.png\?url/);
|
||||||
|
assert.match(source, /trading_outpost_island\.png\?url/);
|
||||||
|
assert.match(source, /volcanic_island\.png\?url/);
|
||||||
|
assert.match(source, /islandTextureFor\(island\)/);
|
||||||
|
assert.match(source, /pirateHaven/);
|
||||||
|
assert.match(source, /port\?\.type === 'capital'/);
|
||||||
|
assert.match(source, /island\.resource === 'fuel'/);
|
||||||
|
assert.match(source, /this\.add\.image\(island\.x, island\.y, texture\)/);
|
||||||
|
assert.doesNotMatch(source, /islandGraphics\.fillEllipse/);
|
||||||
|
assert.match(source, /this\.physics\.add\.overlap\(this\.player, obstacle/);
|
||||||
|
assert.doesNotMatch(source, /this\.physics\.add\.collider\(this\.player, obstacle/);
|
||||||
|
});
|
||||||
|
|
||||||
test('player settings owns account, multiplayer and local flight preferences', async () => {
|
test('player settings owns account, multiplayer and local flight preferences', async () => {
|
||||||
const [entry, settings, multiplayer, game] = await Promise.all([
|
const [entry, settings, multiplayer, game] = await Promise.all([
|
||||||
read('src/game-entry.js'),
|
read('src/game-entry.js'),
|
||||||
|
|||||||