Redesign asteroids: cold moonrock art, bigger impact explosions

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.
This commit is contained in:
Thomas
2026-03-01 12:01:38 +00:00
parent df325ca160
commit fc33658f75
3 changed files with 133 additions and 18 deletions

3
Containerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM scratch
ENTRYPOINT ["/game.wasm"]
COPY /src/dist-web/jnr.wasm /game.wasm

View File

@@ -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 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 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 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 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 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 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 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

View File

@@ -648,28 +648,28 @@ static void asteroid_update(Entity *self, float dt, const Tilemap *map) {
/* Tumble animation */ /* Tumble animation */
animation_update(&self->anim, dt); animation_update(&self->anim, dt);
/* Smoke trail particles */ /* Debris trail — cold grey dust kicked off the tumbling rock */
ad->trail_timer -= dt; ad->trail_timer -= dt;
if (ad->trail_timer <= 0) { if (ad->trail_timer <= 0) {
ad->trail_timer = 0.04f; ad->trail_timer = 0.06f;
Vec2 center = vec2( Vec2 center = vec2(
self->body.pos.x + self->body.size.x * 0.5f, self->body.pos.x + self->body.size.x * 0.5f,
self->body.pos.y self->body.pos.y
); );
ParticleBurst trail = { ParticleBurst trail = {
.origin = center, .origin = center,
.count = 2, .count = 3,
.speed_min = 5.0f, .speed_min = 8.0f,
.speed_max = 20.0f, .speed_max = 30.0f,
.life_min = 0.2f, .life_min = 0.15f,
.life_max = 0.5f, .life_max = 0.4f,
.size_min = 1.0f, .size_min = 1.0f,
.size_max = 2.0f, .size_max = 2.5f,
.spread = 0.5f, .spread = 0.8f,
.direction = -(float)M_PI / 2.0f, /* upward */ .direction = -(float)M_PI / 2.0f, /* upward */
.drag = 2.0f, .drag = 2.5f,
.gravity_scale = 0.0f, .gravity_scale = 0.0f,
.color = {140, 110, 80, 180}, .color = {160, 160, 165, 150}, /* cold grey dust */
.color_vary = true, .color_vary = true,
}; };
particle_emit(&trail); 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.x + player->body.size.x * 0.5f,
player->body.pos.y + player->body.size.y * 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) { if (s_asteroid_sfx_loaded) {
audio_play_sound_at(s_sfx_asteroid_impact, 80, hit_pos, 0); 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 */ /* Also despawn if far below level */
float level_bottom = (float)(map->height * TILE_SIZE) + 32.0f; float level_bottom = (float)(map->height * TILE_SIZE) + 32.0f;
if (hit_ground || self->body.pos.y > level_bottom) { if (hit_ground || self->body.pos.y > level_bottom) {
/* Impact effect */
Vec2 impact_pos = vec2( Vec2 impact_pos = vec2(
self->body.pos.x + self->body.size.x * 0.5f, self->body.pos.x + self->body.size.x * 0.5f,
self->body.pos.y + self->body.size.y 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) { if (s_asteroid_sfx_loaded) {
audio_play_sound_at(s_sfx_asteroid_impact, 60, impact_pos, 0); audio_play_sound_at(s_sfx_asteroid_impact, 60, impact_pos, 0);