Files
CosmicClash/TODO.md
T
Josh Creek 1c08ab3566 fix: de-duplicate hero star diffraction-spike stamps in nebula sky
Every bright star in sky_nebula.png reused the exact same diffraction-
spike stamp, just relocated. Commit the generator (recovered from an
ephemeral scratchpad) to tools/textures/gen_nebula_sky.py, randomize
each hero star's rotation, arm count, spike length, and brightness,
and regenerate the texture.
2026-08-03 20:04:36 +01:00

14 KiB

TODO

Deferred work, in rough priority order. The current architecture (ShipAction/ShipController seam, Arena/GameMode split, code-driven spawning, group-tagged ball/goals) was chosen specifically so these bolt on without rework.

AI opponent (reinforcement learning)

The training pipeline is built — see TRAINING.md (self-play PPO via the vendored godot_rl_agents bridge, JSON policy export, in-game GDScript inference, eval ladder). Remaining:

  • Promote a first tier: curric-s6-unmask copied into Game/bots/promoted/easy.json as the shipped "easy" bot (see TRAINING.md's "Promoted bots" section) — match.tscn/spectate.tscn now default there instead of run05.json.
  • Long training runs on the Linux/3090 box to produce actually-good bots; promote further checkpoints into Game/bots/promoted/ as medium/hard tiers once they clear easy.json in evaluate.py.
  • Staged curriculum (score → defend → avoid draws → full mechanics) via train.py's --opponent-mode/--draw-penalty/--attack-goal-bias/--vertical-ramp/--pitch-roll-ramp flags — see TRAINING.md's "Curriculum training" section. --opponent-mode=frozen is a single-fixed-model slice of the league idea below, not the full sampled pool.
  • Frozen-opponent league: train the live policy against a pool of past exported checkpoints, sampled per-episode (today's --opponent-mode=frozen only supports one fixed model per run) to prevent self-play strategy collapse on long runs.
  • Richer state setter / curriculum: aerial states, wall plays, rebound scenarios as skill grows (beyond the score/defend/draw staging already in place).
  • Main-menu difficulty picker: Easy/Medium/Hard presets in main_menu.gd set bot_model_path/bot_reaction_ticks/bot_action_noise via GameSettings. Raw-checkpoint testing and Spectate mode moved into a DevSection hidden outside debug builds. All three tiers currently reuse promoted/easy.json with different handicaps until medium/hard checkpoints are promoted.

Multiplayer (long term)

  • RemoteShipController extends ShipController — feeds replicated ShipActions from a network peer into the local ship simulation.
  • Networked GameMode subclass: per-peer ship spawning (MultiplayerSpawner or custom), authoritative server for ball/score.
  • C# backend / online servers per README roadmap (not started).
  • Possible v0.2 split-screen: spawn one ship_camera_rig + viewport per local player (camera is already outside the ship scene to allow this).

General

  • More arenas: arena_02.tscn (Nebula) and arena_03.tscn (Asteroid Field) added alongside arena_01.tscn, all following the same template (goals/spawns/arena_boundary.tscn unchanged) and listed in scripts/arena_registry.gd. Free Play lets the player pick an arena from the main menu; Match/Spectate each pick one at random per session; Training keeps its own fixed arena_01.tscn untouched.
  • arena_02 (Nebula) redone RL-style: arena_boundary.tscn's field material is now near-invisible glass (shared by all arenas), the sky is a baked photoreal-style equirect nebula texture (assets/textures/sky_nebula.png, procedurally generated offline — no copied astrophotography) instead of a procedural cell-shader, a NebulaDust GPUParticles3D gives a drifting-motes "weather" effect, and the Decoration node holds a station/debris/planet environment (Blender-modeled, assets/blender_models/nebula_decoration.blendassets/models/nebula_*.glb) visible in multiple directions. arena_01/arena_03 still use the older embedded-shader sky — same treatment for those is unstarted follow-up work.

Visual quality pass (arena_02 critique follow-up)

A subagent ran the game and critiqued arena_02 head-on against Rocket League 2 in Unreal Engine 5: verdict was "a flat-shaded, unlit-looking blockout dressed up with two nice noise textures" — not fundamentally unfixable in Godot (SDFGI/Nanite-tier GI and hero-sculpted geometry aside), but several concrete gaps. Work through these one at a time, in this order (cheapest/highest-impact first). Each item has a ready-to-use prompt — paste it into a fresh session to tackle just that piece. The texture-generation Python scripts used for sky_nebula.png/planet_surface.png currently only exist in an ephemeral scratchpad, not the repo — the first item that touches them should commit a copy into the repo (e.g. tools/textures/) so they're reproducible.

  • Post-processing pipeline (no models needed — cheapest, highest-impact item on the list). Nothing currently sits on top of the raw render: no bloom/glow, no AA, no color grading, no vignette beyond the floor's baked fade. Prompt: "In Cosmic Clash (Godot 4.7), the arena Environment resources (start with Game/scenes/arena_02.tscn's Environment_nebula, then apply consistently across arena_01/arena_03 too) currently have no post-processing. Enable and tune Glow (bloom) so the nebula core, emissive accent strips on the station model, and bright stars actually bleed light; enable Adjustments (adjustment_enabled) for a subtle contrast/saturation grade. Godot's Environment has no built-in vignette property — if that's still wanted, it needs a small custom full-screen shader (a CompositorEffect or a screen-space ColorRect overlay), not a toggle; treat it as a separate, smaller sub-task rather than assuming it's free. Also enable FXAA and/or TAA via project.godot's rendering/anti_aliasing/quality/* settings project-wide to fix the aliased hard edges visible on ship geometry. Motion blur and depth-of-field are deliberately out of scope here — Godot has no built-in equivalent, and faking either well needs a custom CompositorEffect, which is a much bigger task than this pass. Verify with before/after screenshots via godot-mcp (run_project on free_play.tscn with the Nebula arena selected) — don't just eyeball the editor, actually run the game."

  • De-duplicate the nebula sky's star sprites. Every bright star in sky_nebula.png is the exact same diffraction-spike stamp at the same size/brightness, just relocated — called out as "the single most amateur-looking tell in the whole scene" once you look for more than a second. Fixed: the generator is now committed at tools/textures/gen_nebula_sky.py; the hero-star loop randomizes rotation, arm count (4 or 8), spike length, and brightness per star, and sky_nebula.png was regenerated/reimported and confirmed via in-game screenshot. Prompt: "Commit the nebula sky texture generator (currently only in an ephemeral scratchpad — recreate it if needed: numpy/Pillow script generating a 4096x2048 equirect nebula via layered FFT/domain-warped noise, color-graded, with drawn hero stars) into the repo at tools/textures/gen_nebula_sky.py. Fix the hero-star drawing loop so each star's diffraction-spike stamp gets randomized rotation, spike length, and brightness instead of reusing one identical stamp at every location. Regenerate Game/assets/textures/sky_nebula.png, reimport, and confirm via an in-game screenshot that the repeated-stamp tell is gone."

  • Real PBR lighting + materials, fresnel glass boundary (no new models — material/lighting setup only). Everything is flat-shaded/unshaded with a single directional light: no GI, no reflections, no bounce/AO, and the "glass" boundary is a flat unshaded tinted overlay with no fresnel rim or reflection. Prompt: "In Cosmic Clash, enable SDFGI and add reflection probes to the arena scenes (start with arena_02.tscn) so surfaces get real bounce lighting/reflections instead of flat ambient. Convert arena_boundary.tscn's StandardMaterial3D_field from unshaded to a proper shaded material with a fresnel-based rim highlight (bright at grazing angles, near-invisible face-on) so it reads as glass rather than a tinted overlay — check the performance impact of moving it off unshaded, given it's a large always-visible surface. Verify visually via godot-mcp screenshots, and confirm headless runs (free_play/match/spectate/training) still show zero errors."

  • Greeble/detail pass on the station + debris models (Blender remodel — explicit models to redo). nebula_station.glb/nebula_debris.glb are unmistakably Blender primitive kitbashes up close: plain boxes/cylinders with bevel modifiers, no panel lines, damage decals, or surface detail, and debris chunks look undifferentiated from the station. Prompt: "Using the blender MCP, rebuild nebula_station (source in Game/assets/blender_models/nebula_decoration.blend) with actual surface detail: greebled panel-line insets via bmesh inset-and-extrude on selected faces (more scriptable and robust than chained boolean cuts — prefer this over booleans for the main detailing pass), and 2-3 emissive window/light strips distinct from the existing single accent strip. A full normal-map/AO bake pipeline (low-poly + high-poly pair, UV unwrap, bake settings) is the ideal AAA-style finish but is fragile to script end-to-end in one pass — attempt it, but if it proves too unreliable, fall back to the inset/extrude geometric detail alone plus a simple tiled trim-sheet-style texture rather than forcing a bake that doesn't work. Give nebula_debris chunks a rockier/damaged material distinct from the station's clean hull (darker, rougher, maybe scorch-mark variation) so they read as separate debris rather than clones of the station's material. Re-export both to Game/assets/models/nebula_station.glb/nebula_debris.glb, re-save the shared .blend, and re-verify placement/transforms in arena_02.tscn still look right (screenshot check)."

  • Richer nebula sky + planet surface textures (extends the existing procedural generation — no new asset types). sky_nebula.png is essentially one noise-filter pass over a bright core; planet_surface.png is a flat gradient with a single vortex swirl and no bands, craters, or day/night terminator. Prompt: "Extend the nebula/planet texture generators (see tools/textures/ once committed, per the star-sprite TODO item above) with more detail layers: for the sky, add a second/third dust-lane layer at a different scale plus subtler color variation within the bright core (real nebulae aren't one flat color); for the planet, add a proper lit/unlit terminator gradient (the side facing the arena's directional light should read brighter), more band variation at different latitudes, and a couple more storm-vortex features so it doesn't read as a single gradient with one twist. Regenerate both textures, reimport, and screenshot-verify."

  • Particle lighting response for the nebula dust ("weather") effect (no models — particle material/shader only). The NebulaDust GPUParticles3D motes are generic soft glow sprites with no lighting interaction, no depth-based fade, and no secondary motion (no sparkle/color shift). Prompt: "In arena_02.tscn's NebulaDust particle system, replace the current unshaded glow-sprite material with a custom shader that fakes lighting response — note the sprites are camera-facing billboards, so they have no fixed world-space normal and true per-pixel PBR lighting won't behave like it would on a solid mesh. A practical approach: derive a fake per-pixel normal from the quad's local UV (as if each sprite were a small sphere/puff, same trick used for 2D lit particle effects) and light that against the directional light + a fixed 'glow color' bias toward the nebula core direction, rather than relying on the mesh's real (camera-facing) normal. Add soft-particle depth fade (compare particle depth to the depth buffer) so motes don't hard-clip through the boundary/decoration geometry, and add subtle per-particle brightness flicker via the color ramp or shader for sparkle. Verify visually via godot-mcp screenshots that the dust reads as lit rather than flat-glowing, from a couple of different camera angles (billboard lighting tricks can look wrong from some angles even when right from others)."

  • Ship + ball visual pass (Blender remodel — explicit models to make). The ship (Game/objects/ship.tscn) is not a single placeholder box — it's already 5 separate primitive MeshInstance3D parts (Hull a BoxMesh, Nose a PrismMesh, Canopy and EngineGlow boxes, TailFin a thin box), several with emission already (the cyan canopy, the orange EngineGlow, team-tinted Nose/TailFin). It's the same underlying critique as everything else, though: plain primitives, no greebling/panel detail, no imported model — just built further already than the write-up above implied. Prompt: "Using the blender MCP, model an actual low-poly-but-detailed ship hull (greebled fuselage panel lines, a proper cockpit canopy shape, 1-2 engine nacelles with emissive exhaust glow) to replace Game/objects/ship.tscn's current 5 primitive MeshInstance3D parts (Hull/Nose/Canopy/TailFin/EngineGlow), built the same procedural way as the arena decoration (bpy/bmesh primitives + bevels + inset/extrude detail — kept original, no borrowed/asset-store art). Two hard constraints this must respect: (1) scripts/ship.gd's _apply_team_color() recolors two child nodes by exact name, get_node_or_null(\"Nose\") and get_node_or_null(\"TailFin\"), at runtime per-team (team 0 blue, team 1 orange, set via game_mode.gd's ship.team = team) — either keep two MeshInstance3D parts of the new model literally named Nose and TailFin so this keeps working untouched, or update _apply_team_color()'s node-name list in the same change if the new model's parts are named differently; don't let this silently break. (2) Do not touch ship.tscn's single CollisionShape3D (BoxShape3D, size 1 x 1 x 4) or the RigidBody3D's mass = 5.0/inertia = (1,1,1)/physics_material_override — those are exactly what the trained RL bots and flight feel are tuned against (per CLAUDE.md). Keep the new visual mesh's silhouette reasonably close to that 1 x 1 x 4 box so the ship doesn't visually stick out of or float inside its own collision bounds. Separately, audit the existing gold ball's material (Game/assets/models/gold_ball.glb/.res, referenced from Game/objects/ball.tscn's MeshInstance3D with no scene-level material override, so its look currently comes entirely from whatever's embedded in the imported mesh) and upgrade it to a proper metallic/emissive-accent PBR look if it turns out flat/unshaded once the lighting pass above lands — don't assume it needs work without checking first. Verify both visually (screenshot, including a look at team-color recoloring in Match/Spectate) and via a headless run of free_play.tscn/training.tscn to confirm physics/RL behavior is unaffected."