# Grenades and growing waves ## Rifle grenade: contact detonation - Right mouse button / touch GRENADE: launch while alive (mouse captured on desktop). - Cooldown: **15 seconds from launch**, displayed in HUD; repeated clicks cannot bypass it. - Physical `RigidBody3D`: sphere radius 0.12 m, mass 0.4 kg, **no bounce**, friction 0.65, gravity and continuous collision detection (CCD). Velocity: forward 12 m/s + up 3 m/s + player velocity. The thrower is excluded from projectile collision, not blast damage. - **First contact detonates immediately**: ground, walls, office props and enemies. `contact_monitor=true`, `max_contacts_reported=8`; `_integrate_forces()` captures the physics position, `body_entered` is a fallback. One synchronous `exploded` guard and one deferred `_explode()` prevent duplicate effects/damage and scene changes during physics callbacks. Damage rays start at the sphere centre, on the free side of the wall. - Window panes are `Area3D`, layer 8, not physical bodies: a swept ray between physics positions detects these separately, including fast crossings of thin glass. This does not add environmental destruction (the existing zombie-entry glass shatter is unchanged). - **No 4-second fuse anymore**. A lost projectile is deleted after 15 seconds without an explosion, avoiding orphan nodes or delayed airborne blasts. - Blast radius **5 m**, maximum damage **320**, linear falloff; a world-mask ray prevents damage through walls. The living thrower is still included. ## Original Half-Life explosion `scripts/grenade_explosion.gd` uses Valve's `sprites/zerogxplode.spr`, the fireball referenced by Half-Life's `TE_EXPLOSION`. The original SDK uses **15 FPS** and the sprite has **15 frames, 64×64 each**: one second, no looping. `tools/extract_explosion_sprite.py` reads the local Steam installation without changing it, validates IDSP v2 / additive format, and converts the original RGB palette/frames to `assets/effects/explosion/zerogxplode_atlas.png`. Source/output SHA256, frame origins, format and timing are recorded in the adjacent `source_manifest.json`. - PNG atlas **340×204**, 5×3 frames with 2-pixel black gutters; UVs crop the gutters to avoid bleeding between frames under linear filtering. - Camera-facing 3.2 m quad, **additive and unshaded**, normal depth testing (not visible through walls), no shadows. Original RGB is preserved: black contributes zero under additive blending; do not treat the last palette index as alpha-test transparency. - Fireball raised 0.8 m above the physical blast centre to reduce floor clipping. Damage stays at the actual impact; visual position does not change blast distance or occlusion. - Orange flash fades over 0.3 s, with no shadow-casting light; 18 small CPU embers are secondary to the fireball. Original `weapons/explode3–5.wav` unchanged. The effect node remains alive 4 s for audio, while the sprite disappears after its single playback. - Plain PNG/QuadMesh/StandardMaterial3D and CPU particles: compatible with the Web Compatibility renderer; no native SPR importer or GPU-only effects at runtime. Other original assets: `grenade.mdl` (two embedded textures), native `grenade.scn` for Web and explosion WAVs; provenance `assets/half_life_manifest.json`, extraction `tools/extract_grenade_assets.py`. ## Growing waves Ordinary wave size: **4 + 2 × wave** (6, 8, 10, ...), no old cap of 24. Every tenth wave (10, 20, 30, ...) is instead **one Alien Grunt boss**, with no ordinary zombies; see `docs/BOSS.md` for scaling, teleport arrival and the native-only test sandbox. Next wave three seconds after all enemies die and spawning completes. Scheduling guard prevents duplicate wave timers. Corpses remain. ## Tests and visual verification Godot **4.7.1**, not the older binary on PATH: ```bash G="$HOME/.local/opt/godot-4.7.1/Godot_v4.7.1-stable_linux.x86_64" "$G" --headless --path . --script res://tools/test_grenade.gd "$G" --headless --path . --script res://tools/test_wave_growth.gd DISPLAY=:0 "$G" --display-driver x11 --rendering-method gl_compatibility --path . \ --script res://tools/shot_grenade.gd ``` `test_grenade`: **24 checks**, real floor/desk/thin-wall/enemy/glass collisions, rapid crossings at 80 m/s, single detonation, enemy damage, motion/gravity/thrower exclusion, real 15-second cooldown, blast LOS/range/self-damage, absence of a 4-second fuse, non-explosive cleanup, additive billboard atlas and one-shot frame timing, wave growth. Visual harness captures original frames 0/3/7/11/14 **after an actual floor impact** in the office (`/tmp/zombie-grenade-frame-*.png`). Desktop tests use no public scores API. The start-screen GUI regression needs a real display, not the headless dummy driver. Sources: - https://github.com/ValveSoftware/halflife/blob/master/dlls/ggrenade.cpp (`ShootContact`, `ExplodeTouch`, `TE_EXPLOSION`, frame rate 15). - https://github.com/ValveSoftware/halflife/blob/master/dlls/weapons.cpp (fireball precache). - https://github.com/ValveSoftware/halflife/blob/master/utils/sprgen/sprgen.c (SPR format). - https://docs.godotengine.org/en/stable/classes/class_rigidbody3d.html (contact reporting). Historical first grenade release: `4e181f0fe5d02cc9` used the old 4-second fuse and a particle-only explosion. It is superseded; current behavior is described above.