/** * Aufbau der Arena: Boden, Dekoration und Begrenzung. * * Mit geladenen Kenney-Kacheln entsteht ein Erdboden mit Felsen, fremdartiger Flora * und Flecken; ohne sie ein einfaches Schachbrett-Platzhaltermuster. Dekoration hat * keine Physik – sie soll nur den Boden lebendig machen, nicht den Weg versperren. * @module systems/Arena */ import { COLORS, GAME } from '../config.js'; import { pick, randFloat, randInt } from '../logic/rng.js'; import { GROUND_FRAMES, GROUND_SHEET } from './Assets.js'; import { TEXTURE_KEYS } from './Placeholders.js'; /** Abdunkelung des Bodens, damit Figuren und Projektile sich klar abheben. */ const GROUND_TINT = 0x6f6458; /** Anzahl Deko-Objekte je Sorte (für eine 2400×1600-Arena abgestimmt). */ const DECOR = Object.freeze({ dirtPatches: 180, rocks: 45, bushes: 28, splats: 14 }); /** Flora und Flecken in „außerirdischen“ Farben statt Erdgrün. */ const ALIEN_TINTS = Object.freeze([0x6d5a8f, 0x3f7f78, 0x7d5f86]); const SPLAT_TINT = 0x2f4a1a; /** Freiraum um die Startposition, damit dort nichts die Sicht stört. */ const CLEAR_RADIUS = 180; /** * @param {Phaser.Scene} scene * @param {() => number} rng */ export function buildArena(scene, rng) { const { width, height } = GAME.world; const depth = GAME.depth.ground; if (scene.textures.exists(GROUND_SHEET.key)) { const key = GROUND_SHEET.key; const tile = GROUND_SHEET.frame; scene.add.tileSprite(0, 0, width, height, key, GROUND_FRAMES.dirtA).setOrigin(0).setDepth(depth).setTint(GROUND_TINT); // Zweite Erdsorte im Kachelraster verstreut: bricht das sichtbare Wiederholungsmuster. for (let i = 0; i < DECOR.dirtPatches; i += 1) { const x = randInt(rng, 0, Math.floor(width / tile) - 1) * tile; const y = randInt(rng, 0, Math.floor(height / tile) - 1) * tile; scene.add.image(x, y, key, GROUND_FRAMES.dirtB).setOrigin(0).setDepth(depth).setTint(GROUND_TINT); } const place = (frame, count, decorate) => { for (let i = 0; i < count; i += 1) { const point = randomPoint(rng, width, height); const image = scene.add.image(point.x, point.y, key, frame(rng)).setDepth(depth + 1); image.setAngle(randFloat(rng, 0, 360)); decorate(image); } }; place((r) => pick(r, GROUND_FRAMES.rocks), DECOR.rocks, (img) => img.setScale(randFloat(rng, 0.45, 0.95)).setTint(0x8d9196)); place((r) => pick(r, GROUND_FRAMES.bushes), DECOR.bushes, (img) => img.setScale(randFloat(rng, 0.5, 1.1)).setTint(pick(rng, ALIEN_TINTS))); place(() => GROUND_FRAMES.splat, DECOR.splats, (img) => img.setScale(randFloat(rng, 0.8, 1.6)).setTint(SPLAT_TINT).setAlpha(0.7)); } else { scene.add.tileSprite(0, 0, width, height, TEXTURE_KEYS.ground).setOrigin(0).setDepth(depth); } // Emblem der Vereinigten Erde als Bodenmarkierung der Landezone. scene.add .image(width / 2, height / 2, TEXTURE_KEYS.emblem) .setDisplaySize(300, 300) .setAlpha(0.14) .setDepth(depth + 1); const border = scene.add.graphics().setDepth(depth + 2); border.lineStyle(10, COLORS.crimson, 0.9); border.strokeRect(5, 5, width - 10, height - 10); border.lineStyle(3, COLORS.gold, 0.9); border.strokeRect(14, 14, width - 28, height - 28); } /** Zufallspunkt in der Arena außerhalb der freien Startzone. */ function randomPoint(rng, width, height) { for (;;) { const x = randFloat(rng, 30, width - 30); const y = randFloat(rng, 30, height - 30); if (Math.hypot(x - width / 2, y - height / 2) > CLEAR_RADIUS) return { x, y }; } }