Atlas: Deploy-Buendel vor Staustufen-Sprint

- Logistik: Musik-Player Playlist-Modus (5 Gruppen, 20 Tracks)
- Heli: End-Screen auf .ggs-endscreen, Mission-Bilder, Voice-Lines, Briefing-Audio
- Logistik: Layout-Tausch Auftraege+Fahrzeuge links, Karte rechts
- Logistik: Tier-1-Staedte ausgebaut, Auto-Timescale-Badge
- Logistik: Roadnet/Railnet Dijkstra-Routing, Balance-Updates
- Atlas: 5 Atlas-Inbox-Nachrichten (Cards-Pattern, DALL-E-Key, Musik-Playlists)
- Status-Updates Heli + Logistik

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 01:05:13 +02:00
parent f811437a35
commit 79ac9dad84
773 changed files with 17482 additions and 541 deletions
+29 -19
View File
@@ -16,7 +16,7 @@
import { KlimawaechterGame } from './game'
interface PlacedObject {
type: 'tree' | 'solar' | 'wind' | 'green-roof' | 'dike' | 'sea-wall'
type: 'tree' | 'solar' | 'wind' | 'green-roof' | 'dike' | 'sea-wall' | 'bikes' | 'mangrove' | 'sand-fill' | 'coal' | 'airport' | 'cloud-seed'
x: number // 0..1
scale: number
ownerId: string
@@ -184,13 +184,14 @@ export class KlimawaechterRenderer {
return { type: 'green-roof', x: 0, scale: 1, ownerId: stableId, builtTick: tick }
}
if (id === 'dike') {
const slot = index % 4
const x = 0.83 + slot * 0.035
// Deiche breiter entlang der Kueste verteilen
const x = 0.55 + index * 0.06 + this.seededRand(index * 41 + 9) * 0.02
return { type: 'dike', x, scale: 1, ownerId: stableId, builtTick: tick }
}
if (id === 'sea-wall') {
const x = 0.95
return { type: 'sea-wall', x, scale: 1 + index * 0.08, ownerId: stableId, builtTick: tick }
// Mehrere Mauern entlang der Kueste verteilen
const x = 0.6 + index * 0.07 + this.seededRand(index * 29 + 3) * 0.03
return { type: 'sea-wall', x, scale: 1, ownerId: stableId, builtTick: tick }
}
if (id === 'bikes') {
return { type: 'bikes', x: 0.35 + index * 0.04, scale: 1, ownerId: stableId, builtTick: tick }
@@ -200,8 +201,9 @@ export class KlimawaechterRenderer {
return { type: 'mangrove', x, scale: baseScale, ownerId: stableId, builtTick: tick }
}
if (id === 'sand-fill') {
const x = 0.82 + index * 0.025
return { type: 'sand-fill', x, scale: 1, ownerId: stableId, builtTick: tick }
// Entlang der ganzen Kuestenlinie verteilen
const x = 0.55 + index * 0.06 + this.seededRand(index * 37 + 5) * 0.03
return { type: 'sand-fill', x, scale: 1 + this.seededRand(index * 19) * 0.3, ownerId: stableId, builtTick: tick }
}
if (id === 'coal') {
return { type: 'coal', x: 0.12, scale: 1 + index * 0.05, ownerId: stableId, builtTick: tick }
@@ -468,16 +470,22 @@ export class KlimawaechterRenderer {
// ===== KÜSTENBAUWERKE =====
for (const obj of this.placed.filter(p => p.type === 'sand-fill')) {
// Sand-Aufschüttung: kleine Hügel an der Wasserlinie
// Sand-Aufschüttung: sichtbare Hügel an der Wasserlinie
const sx = obj.x * W, sy = seaY
const sc = obj.scale
ctx.fillStyle = '#d4c090'
ctx.beginPath()
ctx.ellipse(sx, sy, 12, 5, 0, Math.PI, 0)
ctx.ellipse(sx, sy, 16 * sc, 7 * sc, 0, Math.PI, 0)
ctx.fill()
ctx.fillStyle = '#c8b480'
ctx.beginPath()
ctx.ellipse(sx + 8, sy, 8, 3, 0, Math.PI, 0)
ctx.ellipse(sx + 10 * sc, sy, 10 * sc, 4 * sc, 0, Math.PI, 0)
ctx.fill()
// Rand
ctx.strokeStyle = '#b8a470'; ctx.lineWidth = 0.5
ctx.beginPath()
ctx.ellipse(sx, sy, 16 * sc, 7 * sc, 0, Math.PI, 0)
ctx.stroke()
}
for (const obj of this.placed.filter(p => p.type === 'mangrove')) {
// Mangroven: grüne Büsche an der Uferlinie
@@ -521,18 +529,20 @@ export class KlimawaechterRenderer {
}
// Flughafen
for (const obj of this.placed.filter(p => p.type === 'airport')) {
const ax = obj.x * W, ay = groundY - 8
const ax = obj.x * W, ay = groundY + 4 // weiter unten, nicht auf der Strasse
// Landebahn
ctx.fillStyle = '#5a5a5a'; ctx.fillRect(ax - 18, ay, 36, 4)
ctx.fillStyle = '#5a5a5a'; ctx.fillRect(ax - 20, ay, 40, 5)
ctx.strokeStyle = '#fff'; ctx.lineWidth = 0.5; ctx.setLineDash([3, 2])
ctx.beginPath(); ctx.moveTo(ax - 16, ay + 2); ctx.lineTo(ax + 16, ay + 2); ctx.stroke()
ctx.beginPath(); ctx.moveTo(ax - 18, ay + 2.5); ctx.lineTo(ax + 18, ay + 2.5); ctx.stroke()
ctx.setLineDash([])
// Flugzeug
const flyX = ax + Math.sin(this.t * 0.3) * 40
const flyY = ay - 15 - Math.abs(Math.sin(this.t * 0.3)) * 20
ctx.fillStyle = '#eee'; ctx.globalAlpha = 0.7
ctx.beginPath(); ctx.ellipse(flyX, flyY, 6, 1.5, Math.sin(this.t * 0.3) * 0.3, 0, Math.PI * 2); ctx.fill()
ctx.globalAlpha = 1
// Helikopter auf der Landebahn (klein)
ctx.font = '8px serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'
ctx.fillText('🚁', ax, ay + 2.5)
// Fliegender Helikopter oben
const flyX = ax + Math.sin(this.t * 0.3) * 50
const flyY = ay - 25 - Math.abs(Math.sin(this.t * 0.3)) * 25
ctx.font = '6px serif'
ctx.fillText('🚁', flyX, flyY)
}
// Wolkenimpfung
for (const obj of this.placed.filter(p => p.type === 'cloud-seed')) {