From fc33658f75a9b52b17152f7775ac2b583d24ec2b Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 1 Mar 2026 12:01:38 +0000 Subject: [PATCH] Redesign asteroids: cold moonrock art, bigger impact explosions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace warm brown asteroid sprites with cold grey cratered moonrock using irregular silhouettes and crater detail. No burning/fire trail since there is no atmosphere on the moon — trail is now grey dust debris. Ground impact uses a 4-layer explosion: rock shrapnel, dust plume, ground-level dust spread, and a brief white flash. Player-hit also gets shrapnel + dust cloud instead of the old small puff. --- Containerfile | 3 + assets/levels/moon01.lvl | 6 +- src/game/hazards.c | 142 ++++++++++++++++++++++++++++++++++----- 3 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 Containerfile diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..9baf240 --- /dev/null +++ b/Containerfile @@ -0,0 +1,3 @@ +FROM scratch +ENTRYPOINT ["/game.wasm"] +COPY /src/dist-web/jnr.wasm /game.wasm diff --git a/assets/levels/moon01.lvl b/assets/levels/moon01.lvl index f8307bb..e42ac66 100644 --- a/assets/levels/moon01.lvl +++ b/assets/levels/moon01.lvl @@ -52,6 +52,6 @@ LAYER collision 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 0 0 0 1 1 1 1 1 1 1 2 1 1 1 1 1 0 0 0 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 0 0 0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 -1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 0 0 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 0 0 0 1 1 1 3 1 1 1 1 1 1 1 1 3 0 0 0 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 0 0 0 0 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 0 0 0 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 0 0 0 1 1 1 1 1 1 1 2 1 1 1 1 1 0 0 0 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 0 0 0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 +1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 0 0 0 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 0 0 0 1 1 1 3 1 1 1 1 1 1 1 1 3 0 0 0 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 0 0 0 0 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 0 0 0 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 diff --git a/src/game/hazards.c b/src/game/hazards.c index 81aed48..efc4a6b 100644 --- a/src/game/hazards.c +++ b/src/game/hazards.c @@ -648,28 +648,28 @@ static void asteroid_update(Entity *self, float dt, const Tilemap *map) { /* Tumble animation */ animation_update(&self->anim, dt); - /* Smoke trail particles */ + /* Debris trail — cold grey dust kicked off the tumbling rock */ ad->trail_timer -= dt; if (ad->trail_timer <= 0) { - ad->trail_timer = 0.04f; + ad->trail_timer = 0.06f; Vec2 center = vec2( self->body.pos.x + self->body.size.x * 0.5f, self->body.pos.y ); ParticleBurst trail = { .origin = center, - .count = 2, - .speed_min = 5.0f, - .speed_max = 20.0f, - .life_min = 0.2f, - .life_max = 0.5f, + .count = 3, + .speed_min = 8.0f, + .speed_max = 30.0f, + .life_min = 0.15f, + .life_max = 0.4f, .size_min = 1.0f, - .size_max = 2.0f, - .spread = 0.5f, + .size_max = 2.5f, + .spread = 0.8f, .direction = -(float)M_PI / 2.0f, /* upward */ - .drag = 2.0f, + .drag = 2.5f, .gravity_scale = 0.0f, - .color = {140, 110, 80, 180}, + .color = {160, 160, 165, 150}, /* cold grey dust */ .color_vary = true, }; particle_emit(&trail); @@ -703,8 +703,44 @@ static void asteroid_update(Entity *self, float dt, const Tilemap *map) { player->body.pos.x + player->body.size.x * 0.5f, player->body.pos.y + player->body.size.y * 0.5f ); - particle_emit_spark(hit_pos, (SDL_Color){180, 140, 80, 255}); - particle_emit_death_puff(hit_pos, (SDL_Color){140, 110, 80, 255}); + + /* Rock shrapnel — fast grey chunks */ + ParticleBurst shrapnel = { + .origin = hit_pos, + .count = 10, + .speed_min = 60.0f, + .speed_max = 180.0f, + .life_min = 0.2f, + .life_max = 0.5f, + .size_min = 1.5f, + .size_max = 3.0f, + .spread = (float)M_PI, + .direction = 0, + .drag = 2.0f, + .gravity_scale = 0.4f, + .color = {150, 150, 155, 255}, + .color_vary = true, + }; + particle_emit(&shrapnel); + + /* Dust cloud */ + ParticleBurst dust = { + .origin = hit_pos, + .count = 8, + .speed_min = 20.0f, + .speed_max = 60.0f, + .life_min = 0.3f, + .life_max = 0.6f, + .size_min = 2.0f, + .size_max = 4.0f, + .spread = (float)M_PI, + .direction = 0, + .drag = 4.0f, + .gravity_scale = 0.0f, + .color = {130, 130, 135, 180}, + .color_vary = true, + }; + particle_emit(&dust); if (s_asteroid_sfx_loaded) { audio_play_sound_at(s_sfx_asteroid_impact, 80, hit_pos, 0); @@ -726,12 +762,88 @@ static void asteroid_update(Entity *self, float dt, const Tilemap *map) { /* Also despawn if far below level */ float level_bottom = (float)(map->height * TILE_SIZE) + 32.0f; if (hit_ground || self->body.pos.y > level_bottom) { - /* Impact effect */ Vec2 impact_pos = vec2( self->body.pos.x + self->body.size.x * 0.5f, self->body.pos.y + self->body.size.y ); - particle_emit_death_puff(impact_pos, (SDL_Color){140, 110, 80, 255}); + + /* Rock shrapnel — fast chunks flying outward */ + ParticleBurst shrapnel = { + .origin = impact_pos, + .count = 16, + .speed_min = 80.0f, + .speed_max = 220.0f, + .life_min = 0.3f, + .life_max = 0.7f, + .size_min = 1.5f, + .size_max = 3.5f, + .spread = (float)M_PI, + .direction = 0, + .drag = 1.5f, + .gravity_scale = 0.5f, + .color = {140, 140, 145, 255}, + .color_vary = true, + }; + particle_emit(&shrapnel); + + /* Dust plume — big, slow, billowing upward */ + ParticleBurst dust_plume = { + .origin = impact_pos, + .count = 14, + .speed_min = 30.0f, + .speed_max = 80.0f, + .life_min = 0.4f, + .life_max = 0.9f, + .size_min = 2.5f, + .size_max = 5.0f, + .spread = 1.2f, + .direction = -(float)M_PI / 2.0f, /* upward */ + .drag = 3.0f, + .gravity_scale = -0.1f, + .color = {120, 120, 125, 160}, + .color_vary = true, + }; + particle_emit(&dust_plume); + + /* Ground-level dust spread — hugs the surface */ + ParticleBurst ground_dust = { + .origin = impact_pos, + .count = 10, + .speed_min = 40.0f, + .speed_max = 120.0f, + .life_min = 0.3f, + .life_max = 0.6f, + .size_min = 2.0f, + .size_max = 3.5f, + .spread = 0.3f, + .direction = (float)M_PI, /* leftward half */ + .drag = 3.5f, + .gravity_scale = 0.0f, + .color = {150, 150, 155, 140}, + .color_vary = true, + }; + particle_emit(&ground_dust); + ground_dust.direction = 0; /* rightward half */ + particle_emit(&ground_dust); + + /* Bright flash — brief white pop at impact point */ + ParticleBurst flash = { + .origin = impact_pos, + .count = 4, + .speed_min = 5.0f, + .speed_max = 15.0f, + .life_min = 0.04f, + .life_max = 0.08f, + .size_min = 3.0f, + .size_max = 5.0f, + .spread = (float)M_PI, + .direction = 0, + .drag = 8.0f, + .gravity_scale = 0.0f, + .color = {220, 220, 230, 255}, + .color_vary = false, + }; + particle_emit(&flash); if (s_asteroid_sfx_loaded) { audio_play_sound_at(s_sfx_asteroid_impact, 60, impact_pos, 0);