89 lines
5.2 KiB
Markdown
89 lines
5.2 KiB
Markdown
# 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.
|