Render procedural world with steampunk island art
This commit is contained in:
parent
327ec37ead
commit
15ffb308c0
@ -4,6 +4,13 @@ import airshipSheetUrl from './assets/airships.svg?url';
|
||||
import playerAirshipUrl from './assets/player-airship-4frame.png?url';
|
||||
import explosionSheetUrl from './assets/explosions.svg?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 {
|
||||
DEFAULT_PIRATE_SHOT_DAMAGE,
|
||||
@ -190,6 +197,13 @@ class GameScene extends Phaser.Scene {
|
||||
frameHeight: 64,
|
||||
});
|
||||
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() {
|
||||
@ -1359,6 +1373,22 @@ class GameScene extends Phaser.Scene {
|
||||
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() {
|
||||
const bg = this.add.graphics();
|
||||
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);
|
||||
}
|
||||
|
||||
const islandGraphics = this.add.graphics().setDepth(2);
|
||||
for (const island of this.world.islands) {
|
||||
islandGraphics.fillStyle(0x5d694e, 1);
|
||||
islandGraphics.fillEllipse(
|
||||
island.x,
|
||||
island.y + 8,
|
||||
island.radius * 2.15,
|
||||
island.radius * 1.45,
|
||||
);
|
||||
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),
|
||||
);
|
||||
const texture = this.islandTextureFor(island);
|
||||
const art = this.add.image(island.x, island.y, texture).setDepth(2);
|
||||
const targetDiameter = island.radius * 2.45;
|
||||
const sourceSize = Math.max(1, art.width, art.height);
|
||||
art.setScale(targetDiameter / sourceSize);
|
||||
art.setData('islandId', island.id);
|
||||
art.setData('collisionRadius', island.radius);
|
||||
}
|
||||
|
||||
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)
|
||||
.setDepth(3);
|
||||
portMarker.setData('portId', port.id);
|
||||
this.add.text(port.x + 13, port.y - 8, port.name, {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '11px',
|
||||
color: '#f5df9a',
|
||||
backgroundColor: '#071018aa',
|
||||
padding: { x: 3, y: 2 },
|
||||
}).setDepth(3);
|
||||
this.add.text(
|
||||
port.x - (island?.radius || 90) * 0.68,
|
||||
port.y + (island?.radius || 90) * 0.72,
|
||||
port.name,
|
||||
{
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '11px',
|
||||
color: '#f5df9a',
|
||||
backgroundColor: '#071018cc',
|
||||
padding: { x: 4, y: 3 },
|
||||
},
|
||||
).setDepth(3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,26 @@ import { readFile } from 'node:fs/promises';
|
||||
|
||||
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 () => {
|
||||
const [entry, settings, multiplayer, game] = await Promise.all([
|
||||
read('src/game-entry.js'),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user