Apply route weather to clouds ships and projectiles

This commit is contained in:
Admin 2026-09-21 06:34:12 +00:00
parent 78eec83c91
commit 06b47c8ebf

View File

@ -159,6 +159,7 @@ class GameScene extends Phaser.Scene {
this.multiplayerHasAuthoritativeState = false; this.multiplayerHasAuthoritativeState = false;
this.multiplayerDead = false; this.multiplayerDead = false;
this.multiplayerLastHull = null; this.multiplayerLastHull = null;
this.multiplayerServerClockOffsetMs = 0;
this.multiplayerSendAccumulatorMs = 0; this.multiplayerSendAccumulatorMs = 0;
airshipLog('scene:init', { airshipLog('scene:init', {
@ -226,7 +227,12 @@ class GameScene extends Phaser.Scene {
this.createPlayerSmoke(); this.createPlayerSmoke();
this.handleMultiplayerSnapshot = (event) => { this.handleMultiplayerSnapshot = (event) => {
this.syncMultiplayerSnapshot(event.detail || {}); const detail = event.detail || {};
const serverTime = Number(detail.serverTime) || 0;
if (serverTime > 0) {
this.multiplayerServerClockOffsetMs = serverTime - Date.now();
}
this.syncMultiplayerSnapshot(detail);
}; };
this.handleMultiplayerState = (event) => { this.handleMultiplayerState = (event) => {
if (event.detail?.state === 'online') return; if (event.detail?.state === 'online') return;
@ -242,6 +248,7 @@ class GameScene extends Phaser.Scene {
}; };
window.addEventListener('airship:multiplayer-snapshot', this.handleMultiplayerSnapshot); window.addEventListener('airship:multiplayer-snapshot', this.handleMultiplayerSnapshot);
window.addEventListener('airship:multiplayer-state', this.handleMultiplayerState); window.addEventListener('airship:multiplayer-state', this.handleMultiplayerState);
window.airshipMultiplayer?.setWeatherRoutes?.(this.world.tradeRoutes);
this.pirates = this.physics.add.group(); this.pirates = this.physics.add.group();
this.merchants = this.physics.add.group(); this.merchants = this.physics.add.group();
@ -362,6 +369,25 @@ class GameScene extends Phaser.Scene {
weatherTimeMs() {
if (window.airshipMultiplayer?.isConnected?.()) {
return Date.now() + (this.multiplayerServerClockOffsetMs || 0);
}
return this.time?.now ?? 0;
}
weatherWindAt(x, y, timeMs = this.weatherTimeMs()) {
return environmentWindAt({
x,
y,
timeMs,
islands: this.world?.islands,
routes: this.world?.tradeRoutes,
worldWidth: this.world?.width || WORLD_SIZE,
worldHeight: this.world?.height || WORLD_SIZE,
});
}
createRollingClouds() { createRollingClouds() {
if (!this.textures.exists('rolling-cloud')) { if (!this.textures.exists('rolling-cloud')) {
const cloudTexture = this.make.graphics({ x: 0, y: 0, add: false }); const cloudTexture = this.make.graphics({ x: 0, y: 0, add: false });
@ -434,15 +460,10 @@ class GameScene extends Phaser.Scene {
if (!this.rollingClouds?.length) return; if (!this.rollingClouds?.length) return;
const dt = Math.min(deltaMs / 1000, 0.05); const dt = Math.min(deltaMs / 1000, 0.05);
const now = this.time?.now ?? 0; const now = this.weatherTimeMs();
for (const cloud of this.rollingClouds) { for (const cloud of this.rollingClouds) {
const wind = environmentWindAt({ const wind = this.weatherWindAt(cloud.x, cloud.y, now);
x: cloud.x,
y: cloud.y,
timeMs: now,
islands: this.world?.islands,
});
const driftScale = cloud.getData('driftScale') || 1; const driftScale = cloud.getData('driftScale') || 1;
cloud.x += wind.x * driftScale * dt; cloud.x += wind.x * driftScale * dt;
cloud.y += wind.y * driftScale * dt; cloud.y += wind.y * driftScale * dt;
@ -459,8 +480,17 @@ class GameScene extends Phaser.Scene {
const weatherPhase = cloud.getData('weatherPhase') || 0; const weatherPhase = cloud.getData('weatherPhase') || 0;
const cyclePosition = (now % weatherCycleMs) / weatherCycleMs; const cyclePosition = (now % weatherCycleMs) / weatherCycleMs;
const weatherWave = 0.5 + 0.5 * Math.sin(cyclePosition * Math.PI * 2 + weatherPhase); const weatherWave = 0.5 + 0.5 * Math.sin(cyclePosition * Math.PI * 2 + weatherPhase);
const windPresence = clamp((wind.strength - 5) / 18, 0, 1); const windPresence = clamp((wind.strength - 5) / 24, 0, 1);
const targetPresence = clamp(weatherWave * 0.72 + windPresence * 0.38 - 0.12, 0, 1); const routeCloudiness = 0.32
+ (1 - (wind.routeInfluence || 0)) * 0.68
+ (wind.routeEdge || 0) * 0.3
+ (wind.stormIntensity || 0) * 0.85;
const targetPresence = clamp(
(weatherWave * 0.62 + windPresence * 0.32 - 0.1) * routeCloudiness
+ (wind.stormIntensity || 0) * 0.28,
0,
1,
);
const previousPresence = cloud.getData('presence') ?? targetPresence; const previousPresence = cloud.getData('presence') ?? targetPresence;
presence = Phaser.Math.Linear(previousPresence, targetPresence, Math.min(1, dt * 0.7)); presence = Phaser.Math.Linear(previousPresence, targetPresence, Math.min(1, dt * 0.7));
cloud.setData('presence', presence); cloud.setData('presence', presence);
@ -975,12 +1005,7 @@ class GameScene extends Phaser.Scene {
const spread = Phaser.Math.FloatBetween(-0.16, 0.16); const spread = Phaser.Math.FloatBetween(-0.16, 0.16);
const trailHeading = heading + Math.PI + spread; const trailHeading = heading + Math.PI + spread;
const trailSpeed = Phaser.Math.Linear(16, 46, effectiveForwardThrust) * Phaser.Math.FloatBetween(0.82, 1.16); const trailSpeed = Phaser.Math.Linear(16, 46, effectiveForwardThrust) * Phaser.Math.FloatBetween(0.82, 1.16);
const localWind = environmentWindAt({ const localWind = this.weatherWindAt(sternX, sternY);
x: sternX,
y: sternY,
timeMs: this.time.now,
islands: this.world?.islands,
});
const velocityX = Math.cos(trailHeading) * trailSpeed + localWind.x * 1.4; const velocityX = Math.cos(trailHeading) * trailSpeed + localWind.x * 1.4;
const velocityY = Math.sin(trailHeading) * trailSpeed + localWind.y * 1.4; const velocityY = Math.sin(trailHeading) * trailSpeed + localWind.y * 1.4;
@ -1190,12 +1215,7 @@ class GameScene extends Phaser.Scene {
merchant.angle += clamp(delta, -34 * dt, 34 * dt); merchant.angle += clamp(delta, -34 * dt, 34 * dt);
const h = headingRad(merchant); const h = headingRad(merchant);
const wind = environmentWindAt({ const wind = this.weatherWindAt(merchant.x, merchant.y);
x: merchant.x,
y: merchant.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const velocity = velocityWithEnvironment( const velocity = velocityWithEnvironment(
Math.cos(h) * (merchant.getData('speed') || 76), Math.cos(h) * (merchant.getData('speed') || 76),
Math.sin(h) * (merchant.getData('speed') || 76), Math.sin(h) * (merchant.getData('speed') || 76),
@ -1780,14 +1800,15 @@ class GameScene extends Phaser.Scene {
this.player.anims.timeScale = 1; this.player.anims.timeScale = 1;
} }
this.environmentWind = environmentWindAt({ this.environmentWind = this.weatherWindAt(this.player.x, this.player.y);
x: this.player.x,
y: this.player.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const h = headingRad(this.player); const h = headingRad(this.player);
this.player.setVelocity(Math.cos(h) * this.playerSpeed, Math.sin(h) * this.playerSpeed); const predictedVelocity = velocityWithEnvironment(
Math.cos(h) * this.playerSpeed,
Math.sin(h) * this.playerSpeed,
this.environmentWind,
1,
);
this.player.setVelocity(predictedVelocity.x, predictedVelocity.y);
if (Phaser.Input.Keyboard.JustDown(this.keys.firePort)) this.playerBroadside(-1); if (Phaser.Input.Keyboard.JustDown(this.keys.firePort)) this.playerBroadside(-1);
if (Phaser.Input.Keyboard.JustDown(this.keys.fireStarboard)) this.playerBroadside(1); if (Phaser.Input.Keyboard.JustDown(this.keys.fireStarboard)) this.playerBroadside(1);
@ -1836,12 +1857,7 @@ class GameScene extends Phaser.Scene {
} }
const h = headingRad(this.player); const h = headingRad(this.player);
this.environmentWind = environmentWindAt({ this.environmentWind = this.weatherWindAt(this.player.x, this.player.y);
x: this.player.x,
y: this.player.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const playerVelocity = velocityWithEnvironment( const playerVelocity = velocityWithEnvironment(
Math.cos(h) * this.playerSpeed, Math.cos(h) * this.playerSpeed,
Math.sin(h) * this.playerSpeed, Math.sin(h) * this.playerSpeed,
@ -1902,12 +1918,7 @@ class GameScene extends Phaser.Scene {
baseSpeed, baseSpeed,
}); });
const h = headingRad(pirate); const h = headingRad(pirate);
const pirateWind = environmentWindAt({ const pirateWind = this.weatherWindAt(pirate.x, pirate.y);
x: pirate.x,
y: pirate.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const pirateVelocity = velocityWithEnvironment( const pirateVelocity = velocityWithEnvironment(
Math.cos(h) * speed, Math.cos(h) * speed,
Math.sin(h) * speed, Math.sin(h) * speed,
@ -1984,12 +1995,7 @@ class GameScene extends Phaser.Scene {
shot.setData('projectileOwner', actor); shot.setData('projectileOwner', actor);
shot.setData('sourceShip', ship); shot.setData('sourceShip', ship);
if (actor === 'pirate') shot.setData('playerHitQueued', false); if (actor === 'pirate') shot.setData('playerHitQueued', false);
const shotWind = environmentWindAt({ const shotWind = this.weatherWindAt(x, y);
x,
y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const shotVelocity = velocityWithEnvironment( const shotVelocity = velocityWithEnvironment(
Math.cos(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.x * 0.25, Math.cos(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.x * 0.25,
Math.sin(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.y * 0.25, Math.sin(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.y * 0.25,
@ -2334,6 +2340,10 @@ class GameScene extends Phaser.Scene {
windSpeed: this.environmentWind?.strength || 0, windSpeed: this.environmentWind?.strength || 0,
windHeading: this.environmentWind?.heading || 0, windHeading: this.environmentWind?.heading || 0,
concealment: this.player?.getData?.('cloudConcealment') || 0, concealment: this.player?.getData?.('cloudConcealment') || 0,
weatherZone: this.environmentWind?.zone || 'open-sky',
routeInfluence: this.environmentWind?.routeInfluence || 0,
routeEdge: this.environmentWind?.routeEdge || 0,
stormIntensity: this.environmentWind?.stormIntensity || 0,
}, },
trade: { trade: {
activeMerchants: this.merchants?.countActive?.(true) || 0, activeMerchants: this.merchants?.countActive?.(true) || 0,