engine-c: denser melee roster + building damage FX at clash

Raid packs grow (raid[96]); Evolved MUTANT/BEAST wedges + Survivor INFANTRY screens; stronger battle-wear + scorch/crack/ember draw in snapshot/live.
This commit is contained in:
local-model/engine-L2-grok-4.5 2026-09-19 16:58:51 +02:00
parent 125dcf837a
commit c551b85912
No known key found for this signature in database
3 changed files with 76 additions and 12 deletions

View file

@ -67,12 +67,13 @@ static void bld_force_complete(Game *g) {
} }
/* After force-complete: leave a few incomplete yards + battle-worn /* After force-complete: leave a few incomplete yards + battle-worn
* fortifications near the raid locus so scaffold/damage FX show in KKND_SHOT. */ * fortifications near both raid loci so scaffold/damage FX show in KKND_SHOT. */
static void showcase_bld_battle_wear(Game *g) { static void showcase_bld_battle_wear(Game *g) {
int incomplete_n = 0, damaged_n = 0; int incomplete_n = 0, damaged_n = 0;
float wx, wy; float wx, wy;
map_tile_to_world(g->map.spawn_x[g->human], g->map.spawn_y[g->human], &wx, &wy); map_tile_to_world(g->map.spawn_x[g->human], g->map.spawn_y[g->human], &wx, &wy);
float rx = wx + 88.0f, ry = wy - 48.0f; float rx = wx + 88.0f, ry = wy - 48.0f;
float rx2 = wx + 140.0f, ry2 = wy + 96.0f;
for (int i = 0; i < g->bld_count; i++) { for (int i = 0; i < g->bld_count; i++) {
Building *b = &g->buildings[i]; Building *b = &g->buildings[i];
if (b->dead) continue; if (b->dead) continue;
@ -80,6 +81,9 @@ static void showcase_bld_battle_wear(Game *g) {
float cy = (b->ty + b->h * 0.5f) * (float)TILE; float cy = (b->ty + b->h * 0.5f) * (float)TILE;
float dx = cx - rx, dy = cy - ry; float dx = cx - rx, dy = cy - ry;
float d2 = dx * dx + dy * dy; float d2 = dx * dx + dy * dy;
float dx2 = cx - rx2, dy2 = cy - ry2;
float d2b = dx2 * dx2 + dy2 * dy2;
float dmin = d2 < d2b ? d2 : d2b;
/* mid-base construction yards (away from clash) */ /* mid-base construction yards (away from clash) */
if (incomplete_n < 2 && b->faction == g->human && d2 > 180.0f * 180.0f && if (incomplete_n < 2 && b->faction == g->human && d2 > 180.0f * 180.0f &&
(b->type == BT_REPAIR || b->type == BT_RESEARCH || b->type == BT_WARFACTORY)) { (b->type == BT_REPAIR || b->type == BT_RESEARCH || b->type == BT_WARFACTORY)) {
@ -88,11 +92,13 @@ static void showcase_bld_battle_wear(Game *g) {
incomplete_n++; incomplete_n++;
continue; continue;
} }
/* clash fortifications: turrets / walls / power take hits */ /* clash fortifications + economy pads take hits at both fronts */
if (damaged_n < 6 && d2 < 160.0f * 160.0f && if (damaged_n < 16 && dmin < 220.0f * 220.0f &&
(b->type == BT_TURRET || b->type == BT_WALL || b->type == BT_POWER)) { (b->type == BT_TURRET || b->type == BT_WALL || b->type == BT_POWER ||
float frac = (damaged_n % 3 == 0) ? 0.35f b->type == BT_REFINERY || b->type == BT_BASE || b->type == BT_WARFACTORY)) {
: (damaged_n % 3 == 1) ? 0.55f : 0.72f; float frac = (damaged_n % 4 == 0) ? 0.22f
: (damaged_n % 4 == 1) ? 0.40f
: (damaged_n % 4 == 2) ? 0.58f : 0.74f;
b->hp = b->max_hp * frac; b->hp = b->max_hp * frac;
damaged_n++; damaged_n++;
} }
@ -216,6 +222,10 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
unit_spawn(g, UT_MEDIC, human, wx + 20, wy + 28); unit_spawn(g, UT_MEDIC, human, wx + 20, wy + 28);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
unit_spawn(g, UT_INFANTRY, human, wx + 88 + (i % 5) * 14, wy + 8 + (i / 5) * 16); unit_spawn(g, UT_INFANTRY, human, wx + 88 + (i % 5) * 14, wy + 8 + (i / 5) * 16);
/* dense melee screen at primary clash (riflemen packed for KKND2 crop) */
for (int i = 0; i < 12; i++)
unit_spawn(g, UT_INFANTRY, human,
wx + 72 + (i % 6) * 11, wy - 28 + (i / 6) * 12);
/* Evolved raid pack — close NE, overlapping Survivor line */ /* Evolved raid pack — close NE, overlapping Survivor line */
float rx = wx + 88.0f, ry = wy - 48.0f; float rx = wx + 88.0f, ry = wy - 48.0f;
@ -602,8 +612,12 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
unit_spawn(g, UT_INFANTRY, human, unit_spawn(g, UT_INFANTRY, human,
rx2 - 12 + (i % 3) * 12, ry2 - 16 + (i / 3) * 14); rx2 - 12 + (i % 3) * 12, ry2 - 16 + (i / 3) * 14);
/* denser Survivor melee at SE clash */
for (int i = 0; i < 8; i++)
unit_spawn(g, UT_INFANTRY, human,
rx2 - 22 + (i % 4) * 10, ry2 - 6 + (i / 4) * 11);
int raid[56]; int raid[96];
int rn = 0; int rn = 0;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
raid[rn++] = unit_spawn(g, UT_MUTANT, enemy, raid[rn++] = unit_spawn(g, UT_MUTANT, enemy,
@ -632,8 +646,18 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy, raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy,
rx - 28 + (i % 4) * 14, ry - 8 + (i / 4) * 14); rx - 28 + (i % 4) * 14, ry - 8 + (i / 4) * 14);
/* dense Evolved melee wedge into Survivor rifle screen */
for (int i = 0; i < 6; i++)
raid[rn++] = unit_spawn(g, UT_MUTANT, enemy,
rx - 8 + (i % 3) * 12, ry + 6 + (i / 3) * 11);
for (int i = 0; i < 4; i++)
raid[rn++] = unit_spawn(g, UT_BEAST, enemy,
rx + 4 + i * 13, ry + 18 + (i & 1) * 8);
for (int i = 0; i < 8; i++)
raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy,
rx - 18 + (i % 4) * 10, ry + 10 + (i / 4) * 9);
/* secondary Evolved pack at SE clash (room left in raid[56]) */ /* secondary Evolved pack at SE clash — melee-heavy */
int rn_pri = rn; int rn_pri = rn;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
raid[rn++] = unit_spawn(g, UT_MUTANT, enemy, raid[rn++] = unit_spawn(g, UT_MUTANT, enemy,
@ -651,6 +675,15 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy, raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy,
rx2 + 6 + i * 12, ry2 + 20 + i * 6); rx2 + 6 + i * 12, ry2 + 20 + i * 6);
for (int i = 0; i < 4; i++)
raid[rn++] = unit_spawn(g, UT_MUTANT, enemy,
rx2 - 4 + (i % 2) * 14, ry2 + 14 + (i / 2) * 12);
for (int i = 0; i < 2; i++)
raid[rn++] = unit_spawn(g, UT_BEAST, enemy,
rx2 + 12 + i * 16, ry2 + 28 + i * 6);
for (int i = 0; i < 6; i++)
raid[rn++] = unit_spawn(g, UT_INFANTRY, enemy,
rx2 - 10 + (i % 3) * 11, ry2 + 8 + (i / 3) * 10);
/* seed Survivor harvester beds so cargo silhouette is visible in shot */ /* seed Survivor harvester beds so cargo silhouette is visible in shot */
{ {

View file

@ -128,23 +128,37 @@ void render_buildings(Game *g) {
} else { } else {
SDL_RenderCopy(g->ren, tex, NULL, &r); SDL_RenderCopy(g->ren, tex, NULL, &r);
} }
/* battle wear: scorch + crack lines when damaged */ /* battle wear: scorch + cracks + rubble + ember (parity w/ snapshot) */
if (b->build >= 1.0f && b->max_hp > 0 && b->hp < b->max_hp * 0.95f) { if (b->build >= 1.0f && b->max_hp > 0 && b->hp < b->max_hp * 0.95f) {
float dmg = 1.0f - (b->hp / b->max_hp); float dmg = 1.0f - (b->hp / b->max_hp);
int pw = W / 4; if (pw < 4) pw = 4; int pw = W / 4; if (pw < 4) pw = 4;
int ph = H / 5; if (ph < 3) ph = 3; int ph = H / 5; if (ph < 3) ph = 3;
SDL_SetRenderDrawColor(g->ren, 42, 32, 22, (Uint8)(90 + (int)(dmg * 110))); SDL_SetRenderDrawColor(g->ren, 42, 32, 22, (Uint8)(90 + (int)(dmg * 110)));
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 4, sy + H / 3, pw, ph }); SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 4, sy + H / 3, pw, ph });
if (dmg > 0.3f) if (dmg > 0.25f)
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 2, sy + H / 2, pw, ph }); SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 2, sy + H / 2, pw, ph });
if (dmg > 0.45f)
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 6, sy + (H * 2) / 3, pw - 1, ph - 1 });
SDL_SetRenderDrawColor(g->ren, 25, 22, 18, 255); SDL_SetRenderDrawColor(g->ren, 25, 22, 18, 255);
SDL_RenderDrawLine(g->ren, sx + 4, sy + H / 4, sx + W / 2, sy + (H * 2) / 3); SDL_RenderDrawLine(g->ren, sx + 4, sy + H / 4, sx + W / 2, sy + (H * 2) / 3);
SDL_RenderDrawLine(g->ren, sx + W / 2, sy + (H * 2) / 3, sx + W - 4, sy + H / 3); SDL_RenderDrawLine(g->ren, sx + W / 2, sy + (H * 2) / 3, sx + W - 4, sy + H / 3);
SDL_RenderDrawLine(g->ren, sx + W / 5, sy + 4, sx + (W * 3) / 5, sy + H / 2);
if (dmg > 0.5f) { if (dmg > 0.5f) {
SDL_RenderDrawLine(g->ren, sx + W / 3, sy + 2, sx + W / 3 + 4, sy + H - 2); SDL_RenderDrawLine(g->ren, sx + W / 3, sy + 2, sx + W / 3 + 4, sy + H - 2);
SDL_RenderDrawLine(g->ren, sx + (W * 2) / 3, sy + H / 5, sx + W / 4, sy + H - 3);
SDL_SetRenderDrawColor(g->ren, 90, 75, 55, 255); SDL_SetRenderDrawColor(g->ren, 90, 75, 55, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + 2, sy + H - 4, 6, 4 }); SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + 2, sy + H - 4, 6, 4 });
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W - 10, sy + H - 5, 8, 5 }); SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W - 10, sy + H - 5, 8, 5 });
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 2 - 2, sy + H - 3, 6, 3 });
}
if (dmg > 0.65f) {
SDL_SetRenderDrawColor(g->ren, 220, 110, 40, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 2 - 1, sy + H / 2 - 1, 3, 3 });
SDL_SetRenderDrawColor(g->ren, 255, 160, 60, 200);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 3 + 1, sy + (H * 2) / 3, 2, 2 });
SDL_SetRenderDrawColor(g->ren, 40, 32, 24, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + 4, sy + 3, 3, 2 });
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W - 8, sy + 4, 4, 3 });
} }
} }
/* outlined HP strip (green→red) when damaged or selected */ /* outlined HP strip (green→red) when damaged or selected */

View file

@ -717,20 +717,37 @@ static void draw_bld(Building *b, int sx, int sy) {
fill_rect(sx, sy - 7, W, 4, 55, 50, 40); fill_rect(sx, sy - 7, W, 4, 55, 50, 40);
fill_rect(sx, sy - 7, (int)(W * b->build + 0.5f), 4, 120, 220, 120); fill_rect(sx, sy - 7, (int)(W * b->build + 0.5f), 4, 120, 220, 120);
} }
/* battle damage: scorch patches + crack lines + rubble chips */ /* battle damage: scorch + cracks + rubble + ember (reads in KKND_SHOT) */
if (b->max_hp > 0 && b->hp < b->max_hp * 0.95f) { if (b->max_hp > 0 && b->hp < b->max_hp * 0.95f) {
float dmg = 1.0f - (b->hp / b->max_hp); float dmg = 1.0f - (b->hp / b->max_hp);
int pw = W / 4; if (pw < 3) pw = 3; int pw = W / 4; if (pw < 3) pw = 3;
int ph = H / 5; if (ph < 2) ph = 2; int ph = H / 5; if (ph < 2) ph = 2;
fill_rect(sx + W / 4, sy + H / 3, pw, ph, 45, 35, 25); fill_rect(sx + W / 4, sy + H / 3, pw, ph, 45, 35, 25);
if (dmg > 0.3f) disc(sx + W / 3, sy + H / 2, pw / 2 + 1, 38, 30, 22);
if (dmg > 0.25f) {
fill_rect(sx + W / 2, sy + H / 2, pw, ph, 55, 40, 28); fill_rect(sx + W / 2, sy + H / 2, pw, ph, 55, 40, 28);
disc(sx + (W * 2) / 3, sy + H / 3, pw / 3 + 1, 48, 36, 24);
}
if (dmg > 0.45f) {
fill_rect(sx + W / 6, sy + (H * 2) / 3, pw - 1, ph - 1, 50, 38, 26);
disc(sx + W / 5, sy + H / 4, 2, 60, 42, 28);
}
line(sx + 3, sy + H / 4, sx + W / 2, sy + (H * 2) / 3, 28, 24, 18); line(sx + 3, sy + H / 4, sx + W / 2, sy + (H * 2) / 3, 28, 24, 18);
line(sx + W / 2, sy + (H * 2) / 3, sx + W - 3, sy + H / 3, 28, 24, 18); line(sx + W / 2, sy + (H * 2) / 3, sx + W - 3, sy + H / 3, 28, 24, 18);
line(sx + W / 5, sy + 4, sx + (W * 3) / 5, sy + H / 2, 32, 26, 20);
if (dmg > 0.5f) { if (dmg > 0.5f) {
line(sx + W / 3, sy + 2, sx + W / 3 + 3, sy + H - 2, 22, 18, 14); line(sx + W / 3, sy + 2, sx + W / 3 + 3, sy + H - 2, 22, 18, 14);
line(sx + (W * 2) / 3, sy + H / 5, sx + W / 4, sy + H - 3, 24, 20, 16);
fill_rect(sx + 2, sy + H - 4, 5, 3, 95, 78, 55); fill_rect(sx + 2, sy + H - 4, 5, 3, 95, 78, 55);
fill_rect(sx + W - 9, sy + H - 5, 7, 4, 85, 70, 50); fill_rect(sx + W - 9, sy + H - 5, 7, 4, 85, 70, 50);
fill_rect(sx + W / 2 - 2, sy + H - 3, 6, 3, 90, 72, 48);
}
if (dmg > 0.65f) {
/* ember glow in heavy breaches */
disc(sx + W / 2, sy + H / 2, 2, 220, 110, 40);
disc(sx + W / 3 + 2, sy + (H * 2) / 3, 1, 255, 160, 60);
fill_rect(sx + 4, sy + 3, 3, 2, 40, 32, 24);
fill_rect(sx + W - 8, sy + 4, 4, 3, 35, 28, 20);
} }
/* thin HP bar so damage reads in KKND_SHOT */ /* thin HP bar so damage reads in KKND_SHOT */
{ {