diff --git a/engine-c/src/engine.c b/engine-c/src/engine.c index 9c897ed..4355244 100644 --- a/engine-c/src/engine.c +++ b/engine-c/src/engine.c @@ -66,6 +66,39 @@ static void bld_force_complete(Game *g) { } } +/* After force-complete: leave a few incomplete yards + battle-worn + * fortifications near the raid locus so scaffold/damage FX show in KKND_SHOT. */ +static void showcase_bld_battle_wear(Game *g) { + int incomplete_n = 0, damaged_n = 0; + float 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; + for (int i = 0; i < g->bld_count; i++) { + Building *b = &g->buildings[i]; + if (b->dead) continue; + float cx = (b->tx + b->w * 0.5f) * (float)TILE; + float cy = (b->ty + b->h * 0.5f) * (float)TILE; + float dx = cx - rx, dy = cy - ry; + float d2 = dx * dx + dy * dy; + /* mid-base construction yards (away from clash) */ + 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->build = (incomplete_n == 0) ? 0.55f : 0.72f; + b->hp = b->max_hp * b->build * 0.9f; + incomplete_n++; + continue; + } + /* clash fortifications: turrets / walls / power take hits */ + if (damaged_n < 6 && d2 < 160.0f * 160.0f && + (b->type == BT_TURRET || b->type == BT_WALL || b->type == BT_POWER)) { + float frac = (damaged_n % 3 == 0) ? 0.35f + : (damaged_n % 3 == 1) ? 0.55f : 0.72f; + b->hp = b->max_hp * frac; + damaged_n++; + } + } +} + static void place_start(Game *g, Faction f) { int sx = g->map.spawn_x[f], sy = g->map.spawn_y[f]; int bx = sx - 1, by = sy - 1; @@ -465,6 +498,9 @@ void engine_setup_match(Game *g, int mission_id) { bld_force_complete(g); } + /* demo incomplete yards + damaged clash fortifications for scaffold/FX */ + showcase_bld_battle_wear(g); + /* centre camera on player base */ int hx = g->map.spawn_x[g->human], hy = g->map.spawn_y[g->human]; g->cam.x = (float)(hx*TILE - VIEW_W/2); diff --git a/engine-c/src/render.c b/engine-c/src/render.c index 222c2d8..39eda33 100644 --- a/engine-c/src/render.c +++ b/engine-c/src/render.c @@ -99,11 +99,26 @@ void render_buildings(Game *g) { SDL_Rect r = { sx, sy, W, H }; SDL_Texture *tex = g->spr.bld[b->type][b->faction]; if (b->build < 1.0f) { - /* construction: draw then scaffold overlay */ + /* construction: ghost body + KKND2-style yard scaffold */ SDL_SetTextureAlphaMod(tex, 160); SDL_RenderCopy(g->ren, tex, NULL, &r); SDL_SetTextureAlphaMod(tex, 255); - int pct = (int)(b->build * 100); + int mid = sy + (int)(H * b->build); + /* corner posts + rails */ + SDL_SetRenderDrawColor(g->ren, 175, 145, 85, 255); + SDL_RenderDrawLine(g->ren, sx, sy, sx, sy + H); + SDL_RenderDrawLine(g->ren, sx + W - 1, sy, sx + W - 1, sy + H); + SDL_RenderDrawLine(g->ren, sx, sy + H - 1, sx + W, sy + H - 1); + SDL_SetRenderDrawColor(g->ren, 210, 175, 100, 255); + SDL_RenderDrawLine(g->ren, sx, sy, sx + W, sy); + SDL_RenderDrawLine(g->ren, sx, mid, sx + W, mid); + /* X-braces + optional mid post */ + SDL_SetRenderDrawColor(g->ren, 155, 125, 65, 230); + SDL_RenderDrawLine(g->ren, sx + 2, sy + 2, sx + W - 2, sy + H - 2); + SDL_RenderDrawLine(g->ren, sx + W - 2, sy + 2, sx + 2, sy + H - 2); + if (W >= 48) + SDL_RenderDrawLine(g->ren, sx + W / 2, sy, sx + W / 2, sy + H); + /* progress bar above */ SDL_Rect bar = { sx, sy - 8, W, 5 }; SDL_SetRenderDrawColor(g->ren, 200, 200, 200, 255); SDL_RenderFillRect(g->ren, &bar); @@ -113,6 +128,25 @@ void render_buildings(Game *g) { } else { SDL_RenderCopy(g->ren, tex, NULL, &r); } + /* battle wear: scorch + crack lines when damaged */ + 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); + int pw = W / 4; if (pw < 4) pw = 4; + int ph = H / 5; if (ph < 3) ph = 3; + 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 }); + if (dmg > 0.3f) + SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx + W / 2, sy + H / 2, pw, ph }); + 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 + W / 2, sy + (H * 2) / 3, sx + W - 4, sy + H / 3); + if (dmg > 0.5f) { + SDL_RenderDrawLine(g->ren, sx + W / 3, sy + 2, sx + W / 3 + 4, sy + H - 2); + 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 + W - 10, sy + H - 5, 8, 5 }); + } + } /* health bar if damaged or selected */ if (b->hp < b->max_hp || b->selected) { SDL_Rect bar = { sx, sy - 6, W, 4 }; diff --git a/engine-c/src/snapshot.c b/engine-c/src/snapshot.c index d760209..abadaa4 100644 --- a/engine-c/src/snapshot.c +++ b/engine-c/src/snapshot.c @@ -318,8 +318,48 @@ static void draw_bld(Building *b, int sx, int sy) { default: break; } - if (b->build < 1.0f) - fill_rect(sx, sy, W, (int)(H * b->build), 255, 255, 255); + /* construction scaffold: yard posts/rails/braces (not flat white wash) */ + if (b->build < 1.0f) { + int mid = sy + (int)(H * b->build); + if (mid > sy + 2) + fill_rect(sx + 2, sy + 2, W - 4, mid - sy - 2, 235, 232, 220); + line(sx, sy, sx, sy + H, 175, 145, 85); + line(sx + W - 1, sy, sx + W - 1, sy + H, 175, 145, 85); + line(sx, sy, sx + W, sy, 205, 175, 105); + line(sx, mid, sx + W, mid, 215, 185, 115); + line(sx, sy + H - 1, sx + W, sy + H - 1, 155, 125, 70); + line(sx + 2, sy + 2, sx + W - 2, sy + H - 2, 150, 120, 65); + line(sx + W - 2, sy + 2, sx + 2, sy + H - 2, 150, 120, 65); + if (W >= 48) + line(sx + W / 2, sy, sx + W / 2, sy + H, 165, 135, 75); + /* progress tick above pad */ + 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); + } + /* battle damage: scorch patches + crack lines + rubble chips */ + if (b->max_hp > 0 && b->hp < b->max_hp * 0.95f) { + float dmg = 1.0f - (b->hp / b->max_hp); + int pw = W / 4; if (pw < 3) pw = 3; + int ph = H / 5; if (ph < 2) ph = 2; + fill_rect(sx + W / 4, sy + H / 3, pw, ph, 45, 35, 25); + if (dmg > 0.3f) + fill_rect(sx + W / 2, sy + H / 2, pw, ph, 55, 40, 28); + 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); + if (dmg > 0.5f) { + line(sx + W / 3, sy + 2, sx + W / 3 + 3, sy + H - 2, 22, 18, 14); + 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); + } + /* thin HP bar so damage reads in KKND_SHOT */ + { + int by = sy - 6; + float f = b->hp / b->max_hp; + fill_rect(sx - 1, by - 1, W + 2, 5, 20, 18, 14); + fill_rect(sx, by, W, 3, 55, 48, 36); + fill_rect(sx, by, (int)(W * f + 0.5f), 3, br, bg, 40); + } + } } void snapshot_capture(Game *g, const char *path) {