Revert to git.kimchi, add curl diagnostics to understand why
auth fails even after login succeeds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Gitea container registry token service scopes tokens to ROOT_URL
(git.schick-web.site). Pushing to the internal hostname (git.kimchi)
causes auth failures because the token domain doesn't match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
buildah login succeeds but push doesn't pick up the stored auth.
Skip login and pass --creds directly to each push command instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove unnecessary registries.conf write (host already has it).
Add set -ex and echo markers between commands to pinpoint the hang.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
--tls-verify=false on login/push alone was not sufficient to prevent
the deploy from hanging. Register git.kimchi as an insecure registry
via registries.conf and add --tls-verify=false to buildah bud as well.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the editor is active, the JS shell level-select dropdown was
unconditionally switching to MODE_PLAY. Now it detects MODE_EDITOR
and calls editor_load() to load the selected level into the editor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the WASM build into a multi-stage Containerfile so the emscripten
compilation happens inside the Docker build. This eliminates the separate
container action step, enables Docker layer caching for faster rebuilds,
and makes the Containerfile self-contained.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display the player's current score using the font renderer,
right-aligned with an 8px margin from the top-right screen edge.
Closes#5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The score computation used wrong weights and terms compared to DESIGN.md.
Updated to: enemies_killed*100 + levels_completed*500 - deaths*200 - time_elapsed
Closes#6
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Double pressing the down arrow while airborne triggers a free downward
dash that doesn't consume jetpack charges. Uses a 0.3s window for the
double-tap detection, resets on landing.
Closes#9
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mars02.lvl (Mars Base interior) incorrectly used PARALLAX_STYLE 5
(Mars surface), causing atmosphere dust particles to spawn indoors.
Changed to PARALLAX_STYLE 2 (Interior) to match the level's setting.
Closes#4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When wind is zero or near-zero, dust particles were always spawning at
the left viewport edge (the "upwind" edge for wind >= 0). Without wind
force to carry them across, they accumulated on the left side. Now
particles spawn across the full viewport when wind is calm (< 5 px/s²),
with random drift directions for even distribution.
Closes#2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set the Horchposten backend URL in the shell data attribute and replace
the plaintext key lookup with XOR-encoded byte arrays decoded at runtime,
so the API key never appears as a readable string in source, HTML, or the
compiled WASM binary.
1. Race condition: session_end now waits for an in-flight
session_start promise before sending, so quick restarts
don't drop the end call.
2. beforeunload: replaced sendBeacon (which can't set headers)
with fetch(..., keepalive: true) so the X-API-Key header
is included and the backend doesn't 401.
3. Stats double-counting: removed stats_record_damage_dealt
and stats_record_kill from damage_entity (which was called
for all damage including player deaths). Now only recorded
at player-sourced call sites (projectile hits, stomps).
4. Removed const-cast: analytics_session_end now takes
GameStats* (non-const) since stats_update_score mutates it.
5. beforeunload now uses stashed stats from the last C-side
session_end call instead of hardcoded zeroes. Session ID is
cleared synchronously before async fetch to prevent races.
6. Removed unused stdint.h include from stats.h.
Implements session-based analytics tracking that sends gameplay
stats to the Horchposten API. Adds stats.{c,h} for accumulating
per-session metrics (kills, deaths, shots, dashes, jumps, pickups,
damage, time) and analytics.{c,h} with EM_JS bridge for fetch()
calls to the backend. Client ID is persisted in localStorage.
Session start/end hooks are wired into all game lifecycle events
(level transitions, restart, quit, tab close via sendBeacon).
Analytics URL/key are configured via data attributes on the canvas
container. Non-WASM builds compile with no-op stubs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
static-web-server's default cache-control sends max-age=31536000 (1 year)
for .js files but only 1 day for .wasm. After redeployment, Cloudflare CDN
serves the cached old .js with a fresh .wasm, causing EM_ASM address table
mismatches and runtime crashes. Disable built-in cache headers at the origin
so Cloudflare respects new content on each deploy.
Also update AGENTS.md: add deploy commands, fix emsdk path, document the
Cloudflare cache-purge requirement, and correct stale MAX_ENTITY_SPAWNS
and MAX_EXIT_ZONES values.
Beam raycast now skips the tile the turret is mounted on, so
wall-embedded laser turrets can shoot outward.
Extract entity_is_enemy() into engine/entity.c as single source
of truth for enemy-type checks. Replaces triplicated type lists
in level.c, drone.c, and projectile.c that caused the collision
bug when new enemy types were added.
All three new entity types were absent from the hardcoded enemy-type
checks in is_enemy() (contact damage + projectile hits), homing
projectile targeting, and drone targeting. Also adds proper death
particle colors for charger (orange) and spawner (purple).
Replace colored-rect placeholder rendering with proper 16x16
spritesheet art. Charger: orange armored rhino with horn, eyes,
walk cycle (row 7). Spawner: purple hive pod with glowing core
and tendrils (row 8). Laser turret: angular metallic housing
with red lens that brightens when firing (row 8). All three
retain colored-rect fallback when spritesheet is unavailable.
Dedicated Mars Base generator with 6 segment types (entry, shaft, corridor,
turret hall, hive, arena), depth-scaled difficulty, and exit to boss arena
after 2 generated levels. Mars themes integrated into all 7 generic segment
generators. Full level chain: moon03 → mars01 → mars02 → mars_base (×2) →
mars03 → station.
Also: jetpack fuel pickup preserves recharge progress, depth counters reset
on game loop, NULL checks on entity spawns, charger charge timeout, bg
decoration in Mars Base generator.
Implement four feature phases:
Phase 1 - Pause menu: extract bitmap font into shared engine/font
module, add MODE_PAUSED with Resume/Restart/Quit overlay.
Phase 2 - Laser turret hazard: ENT_LASER_TURRET with charge/fire/
cooldown state machine, per-pixel beam raycast, two variants (fixed
and tracking). Registered in entity registry with editor icons.
Phase 3 - Charger and Spawner enemies: charger ground patrol with
detect/telegraph/charge/stun cycle (2s charge timeout), spawner that
periodically creates grunts up to a global cap of 3.
Phase 4 - Mars campaign: two handcrafted levels (mars01 surface,
mars02 base), mars_tileset.png, PARALLAX_STYLE_MARS with salmon sky
and red mesas, THEME_MARS_SURFACE/THEME_MARS_BASE for the procedural
generator with per-theme gravity/tileset/parallax. Moon campaign now
chains moon03 -> mars01 -> mars02 -> victory.
Also fix review findings: deterministic seed on generated level
restart, NULL checks on calloc in spawn functions, charge timeout
to prevent infinite charge on flat terrain, and stop suppressing
stderr in Makefile web-serve target so real errors are visible.