From bab388723fe0b4d8ddd8bcd52ef30d717c4bb207 Mon Sep 17 00:00:00 2001 From: "local-model/engine-L2-grok-4.5" Date: Sat, 19 Sep 2026 11:50:41 +0200 Subject: [PATCH] engine-c: lasting death RUBBLE/SCRAP scars + denser scorch --- engine-c/include/kknd.h | 5 +++ engine-c/src/buildings.c | 12 ++++--- engine-c/src/map.c | 74 ++++++++++++++++++++++++++++++++++++++++ engine-c/src/units.c | 14 +++++--- 4 files changed, 96 insertions(+), 9 deletions(-) diff --git a/engine-c/include/kknd.h b/engine-c/include/kknd.h index 8ec139b..8afbe0a 100644 --- a/engine-c/include/kknd.h +++ b/engine-c/include/kknd.h @@ -216,6 +216,11 @@ void map_world_to_tile(float wx, float wy, int *tx, int *ty); void map_tile_to_world(int tx, int ty, float *wx, float *wy); int map_find_nearest_scrap(GameMap *m, float wx, float wy, float *outx, float *outy); void map_deplete_scrap(GameMap *m, int tx, int ty, int amount); +/* lasting battlefield scars — soft only (no WATER/ROCK overwrite; stay walkable) */ +void map_scar_rubble(GameMap *m, int tx, int ty); +void map_scar_scrap_fleck(GameMap *m, int tx, int ty, uint8_t amount); +void map_scar_unit_death(GameMap *m, float wx, float wy, UnitType type); +void map_scar_bld_death(GameMap *m, int tx, int ty, int w, int h); typedef struct Game Game; void map_update_fog(Game *g, Faction f); diff --git a/engine-c/src/buildings.c b/engine-c/src/buildings.c index 1ebd7ba..22a5fad 100644 --- a/engine-c/src/buildings.c +++ b/engine-c/src/buildings.c @@ -73,14 +73,18 @@ void bld_apply_damage(Game *g, Building *b, float dmg) { (float)(b->ty*TILE+b->h*TILE/2), 2, 2, 255, 200, 80); if (b->hp <= 0 && !b->dead) { b->dead = 1; - particle_burst(g, (float)(b->tx*TILE+b->w*TILE/2), - (float)(b->ty*TILE+b->h*TILE/2), 20, 1, 255, 140, 40); - particle_burst(g, (float)(b->tx*TILE+b->w*TILE/2), - (float)(b->ty*TILE+b->h*TILE/2), 12, 3, 90, 80, 70); + float cx = (float)(b->tx*TILE+b->w*TILE/2); + float cy = (float)(b->ty*TILE+b->h*TILE/2); + particle_burst(g, cx, cy, 28, 1, 255, 140, 40); + particle_burst(g, cx, cy, 16, 3, 90, 80, 70); + particle_burst(g, cx + 10, cy - 8, 10, 0, 100, 90, 80); + particle_burst(g, cx - 8, cy + 10, 8, 1, 55, 45, 40); /* scorch */ audio_sfx(2); if (g->cam.shake < 9) g->cam.shake = 9; if (b->faction == g->human) game_log(g, "Structure lost: %s", bld_def(b->type)->name); + /* lasting rubble under footprint; occupied cleared later by bld_remove */ + map_scar_bld_death(&g->map, b->tx, b->ty, b->w, b->h); } } diff --git a/engine-c/src/map.c b/engine-c/src/map.c index f56c61c..54cd1c3 100644 --- a/engine-c/src/map.c +++ b/engine-c/src/map.c @@ -254,6 +254,80 @@ void map_deplete_scrap(GameMap *m, int tx, int ty, int amount) { } } +/* Soft scar: never overwrite WATER/ROCK; never clear walkable. */ +static int map_scar_ok(Tile *t) { + if (!t) return 0; + if (t->terrain == T_WATER || t->terrain == T_ROCK) return 0; + return 1; +} + +void map_scar_rubble(GameMap *m, int tx, int ty) { + Tile *t = map_tile(m, tx, ty); + if (!map_scar_ok(t)) return; + /* keep ROAD as approach cue; scrap fields stay harvestable */ + if (t->terrain == T_ROAD || t->terrain == T_SCRAP) return; + t->terrain = T_RUBBLE; + t->scrap = 0; + t->walkable = 1; +} + +void map_scar_scrap_fleck(GameMap *m, int tx, int ty, uint8_t amount) { + Tile *t = map_tile(m, tx, ty); + if (!map_scar_ok(t)) return; + if (t->terrain == T_ROAD) return; + if (t->terrain == T_SCRAP && t->scrap > 0) return; /* leave live fields alone */ + t->terrain = T_SCRAP; + t->scrap = amount ? amount : 40; + t->walkable = 1; +} + +void map_scar_unit_death(GameMap *m, float wx, float wy, UnitType type) { + int tx, ty; + map_world_to_tile(wx, wy, &tx, &ty); + map_scar_rubble(m, tx, ty); + + int vehicle = (type == UT_BUGGY || type == UT_TANK || type == UT_HARVESTER + || type == UT_ARTILLERY || type == UT_JEEP || type == UT_RAVAGER + || type == UT_MISSILE || type == UT_BEAST); + int heavy = (type == UT_TANK || type == UT_RAVAGER || type == UT_ARTILLERY + || type == UT_HARVESTER); + if (!vehicle) return; + + /* adjacent rubble ring — still soft / walkable */ + static const int ox[8] = { 1,-1, 0, 0, 1, 1,-1,-1 }; + static const int oy[8] = { 0, 0, 1,-1, 1,-1, 1,-1 }; + int n = heavy ? 6 : 3; + for (int i = 0; i < n; i++) + map_scar_rubble(m, tx + ox[i], ty + oy[i]); + + /* small scrap flecks from wrecked chassis */ + if (heavy) { + map_scar_scrap_fleck(m, tx + 1, ty - 1, (uint8_t)(55 + ((tx * 7 + ty) & 63))); + map_scar_scrap_fleck(m, tx - 1, ty + 1, (uint8_t)(40 + ((tx * 3 + ty * 5) & 47))); + } else { + map_scar_scrap_fleck(m, tx + ox[tx & 3], ty + oy[ty & 3], + (uint8_t)(35 + ((tx + ty) & 31))); + } +} + +void map_scar_bld_death(GameMap *m, int tx, int ty, int w, int h) { + if (w < 1) w = 1; + if (h < 1) h = 1; + for (int y = ty; y < ty + h; y++) { + for (int x = tx; x < tx + w; x++) { + map_scar_rubble(m, x, y); + /* outer flecks just outside footprint */ + if (x == tx) map_scar_rubble(m, x - 1, y); + if (x == tx + w - 1) map_scar_rubble(m, x + 1, y); + if (y == ty) map_scar_rubble(m, x, y - 1); + if (y == ty + h - 1) map_scar_rubble(m, x, y + 1); + } + } + /* one scrap pile from collapsed structure */ + map_scar_scrap_fleck(m, tx + w / 2, ty + h / 2, + (uint8_t)(70 + ((tx * 11 + ty * 13) & 80))); +} + /* ----------------------------- A* ----------------------------- */ /* Binary heap of tile indices keyed by f-score in a parallel array. */ #define ASTAR_NODES (MAP_W * MAP_H) diff --git a/engine-c/src/units.c b/engine-c/src/units.c index 999b918..40a9a86 100644 --- a/engine-c/src/units.c +++ b/engine-c/src/units.c @@ -66,15 +66,19 @@ void unit_apply_damage(Game *g, Unit *u, float dmg) { if (u->hp <= 0 && !u->dead) { u->dead = 1; u->state = STATE_DEAD; - /* denser KKND2-like death: fire core + debris ring + smoke */ - particle_burst(g, u->x, u->y, 18, 1, 255, 140, 40); - particle_burst(g, u->x, u->y, 10, 3, 90, 80, 70); - particle_burst(g, u->x + 6, u->y - 4, 8, 0, 110, 100, 90); - particle_burst(g, u->x - 5, u->y + 5, 6, 2, 255, 220, 90); + /* denser KKND2-like death: fire core + debris ring + smoke + scorch */ + particle_burst(g, u->x, u->y, 22, 1, 255, 140, 40); + particle_burst(g, u->x, u->y, 14, 3, 90, 80, 70); + particle_burst(g, u->x + 6, u->y - 4, 10, 0, 110, 100, 90); + particle_burst(g, u->x - 5, u->y + 5, 8, 2, 255, 220, 90); + particle_burst(g, u->x + 3, u->y + 7, 8, 1, 60, 50, 45); /* scorch soot */ + particle_burst(g, u->x - 4, u->y - 6, 6, 0, 70, 55, 40); audio_sfx(2); if (g->cam.shake < 7) g->cam.shake = 7; if (u->faction == g->human) game_log(g, "Unit lost: %s", unit_def(u->type, u->faction)->name); + /* lasting terrain scar under corpse (RUBBLE/SCRAP, soft/walkable) */ + map_scar_unit_death(&g->map, u->x, u->y, u->type); /* leave a wreck decal on the battlefield */ if (g->wreck_count < MAX_WRECKS) { Wreck *w = &g->wrecks[g->wreck_next];