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.multiplayerDead = false;
this.multiplayerLastHull = null;
this.multiplayerServerClockOffsetMs = 0;
this.multiplayerSendAccumulatorMs = 0;
airshipLog('scene:init', {
@ -226,7 +227,12 @@ class GameScene extends Phaser.Scene {
this.createPlayerSmoke();
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) => {
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-state', this.handleMultiplayerState);
window.airshipMultiplayer?.setWeatherRoutes?.(this.world.tradeRoutes);
this.pirates = 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() {
if (!this.textures.exists('rolling-cloud')) {
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;
const dt = Math.min(deltaMs / 1000, 0.05);
const now = this.time?.now ?? 0;
const now = this.weatherTimeMs();
for (const cloud of this.rollingClouds) {
const wind = environmentWindAt({
x: cloud.x,
y: cloud.y,
timeMs: now,
islands: this.world?.islands,
});
const wind = this.weatherWindAt(cloud.x, cloud.y, now);
const driftScale = cloud.getData('driftScale') || 1;
cloud.x += wind.x * driftScale * dt;
cloud.y += wind.y * driftScale * dt;
@ -459,8 +480,17 @@ class GameScene extends Phaser.Scene {
const weatherPhase = cloud.getData('weatherPhase') || 0;
const cyclePosition = (now % weatherCycleMs) / weatherCycleMs;
const weatherWave = 0.5 + 0.5 * Math.sin(cyclePosition * Math.PI * 2 + weatherPhase);
const windPresence = clamp((wind.strength - 5) / 18, 0, 1);
const targetPresence = clamp(weatherWave * 0.72 + windPresence * 0.38 - 0.12, 0, 1);
const windPresence = clamp((wind.strength - 5) / 24, 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;
presence = Phaser.Math.Linear(previousPresence, targetPresence, Math.min(1, dt * 0.7));
cloud.setData('presence', presence);
@ -975,12 +1005,7 @@ class GameScene extends Phaser.Scene {
const spread = Phaser.Math.FloatBetween(-0.16, 0.16);
const trailHeading = heading + Math.PI + spread;
const trailSpeed = Phaser.Math.Linear(16, 46, effectiveForwardThrust) * Phaser.Math.FloatBetween(0.82, 1.16);
const localWind = environmentWindAt({
x: sternX,
y: sternY,
timeMs: this.time.now,
islands: this.world?.islands,
});
const localWind = this.weatherWindAt(sternX, sternY);
const velocityX = Math.cos(trailHeading) * trailSpeed + localWind.x * 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);
const h = headingRad(merchant);
const wind = environmentWindAt({
x: merchant.x,
y: merchant.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const wind = this.weatherWindAt(merchant.x, merchant.y);
const velocity = velocityWithEnvironment(
Math.cos(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.environmentWind = environmentWindAt({
x: this.player.x,
y: this.player.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
this.environmentWind = this.weatherWindAt(this.player.x, this.player.y);
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.fireStarboard)) this.playerBroadside(1);
@ -1836,12 +1857,7 @@ class GameScene extends Phaser.Scene {
}
const h = headingRad(this.player);
this.environmentWind = environmentWindAt({
x: this.player.x,
y: this.player.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
this.environmentWind = this.weatherWindAt(this.player.x, this.player.y);
const playerVelocity = velocityWithEnvironment(
Math.cos(h) * this.playerSpeed,
Math.sin(h) * this.playerSpeed,
@ -1902,12 +1918,7 @@ class GameScene extends Phaser.Scene {
baseSpeed,
});
const h = headingRad(pirate);
const pirateWind = environmentWindAt({
x: pirate.x,
y: pirate.y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const pirateWind = this.weatherWindAt(pirate.x, pirate.y);
const pirateVelocity = velocityWithEnvironment(
Math.cos(h) * speed,
Math.sin(h) * speed,
@ -1984,12 +1995,7 @@ class GameScene extends Phaser.Scene {
shot.setData('projectileOwner', actor);
shot.setData('sourceShip', ship);
if (actor === 'pirate') shot.setData('playerHitQueued', false);
const shotWind = environmentWindAt({
x,
y,
timeMs: this.time.now,
islands: this.world?.islands,
});
const shotWind = this.weatherWindAt(x, y);
const shotVelocity = velocityWithEnvironment(
Math.cos(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.x * 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,
windHeading: this.environmentWind?.heading || 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: {
activeMerchants: this.merchants?.countActive?.(true) || 0,