engine-c: SDL live arid+scale+tracers parity with KKND_SHOT

This commit is contained in:
local-model/engine-L2-grok-4.5 2026-09-19 11:23:13 +02:00
parent 5201f49d1d
commit b03cd2a0e3
No known key found for this signature in database
4 changed files with 129 additions and 63 deletions

Binary file not shown.

View file

@ -175,9 +175,9 @@ const char *unit_name(UnitType t, Faction f) {
SDL_Color faction_color(Faction f) {
switch (f) {
case FACTION_SURVIVOR: return (SDL_Color){ 90, 160, 70, 255 }; /* olive green */
case FACTION_EVOLVED: return (SDL_Color){170, 70, 180, 255 }; /* mutant purple */
case FACTION_SERIES9: return (SDL_Color){200, 200, 60, 255 };
case FACTION_SURVIVOR: return (SDL_Color){ 55, 235, 55, 255 }; /* lime Survivors */
case FACTION_EVOLVED: return (SDL_Color){255, 40, 255, 255 }; /* hot magenta Evolved */
case FACTION_SERIES9: return (SDL_Color){240, 55, 240, 255 };
default: return (SDL_Color){140, 140, 140, 255 };
}
}

View file

@ -166,7 +166,8 @@ void render_units(Game *g) {
int tx = (int)(u->x / TILE), ty = (int)(u->y / TILE);
if (!map_in_bounds(&g->map, tx, ty) || g->map.fog[ty*g->map.w + tx] != 2) continue;
}
int sz = 28;
/* Match snapshot SHOT_UNIT_SCALE 1.45 → live silhouette ~41px */
int sz = 41;
SDL_Texture *tex = g->spr.unit[u->type][u->faction];
draw_tex_world(g, tex, u->x, u->y, sz, sz, u->heading, 255);
/* selection ring */
@ -174,21 +175,21 @@ void render_units(Game *g) {
int cx = (int)(u->x - g->cam.x), cy = (int)(u->y - g->cam.y);
SDL_SetRenderDrawColor(g->ren, 120, 255, 120, 220);
for (int a = 0; a < 360; a += 12) {
int px = cx + (int)(cosf(a*3.14159f/180)*16);
int py = cy + (int)(sinf(a*3.14159f/180)*16);
int px = cx + (int)(cosf(a*3.14159f/180)*22);
int py = cy + (int)(sinf(a*3.14159f/180)*22);
SDL_RenderDrawPoint(g->ren, px, py);
}
}
/* health bar */
if (u->hp < u->max_hp || u->selected) {
int cx = (int)(u->x - g->cam.x), by = (int)(u->y - g->cam.y) - 18;
int bw = 24;
int cx = (int)(u->x - g->cam.x), by = (int)(u->y - g->cam.y) - 26;
int bw = 32;
SDL_Rect bg = { cx - bw/2, by, bw, 4 };
SDL_SetRenderDrawColor(g->ren, 50, 50, 50, 255);
SDL_RenderFillRect(g->ren, &bg);
float f = u->hp / u->max_hp;
SDL_Color c = (u->faction == FACTION_SURVIVOR) ? (SDL_Color){90,200,90,255}
: (SDL_Color){220,110,220,255};
SDL_Color c = (u->faction == FACTION_SURVIVOR) ? (SDL_Color){55,235,55,255}
: (SDL_Color){255,40,255,255};
SDL_Rect fg = { cx - bw/2, by, (int)(bw * f), 4 };
SDL_SetRenderDrawColor(g->ren, c.r, c.g, c.b, 255);
SDL_RenderFillRect(g->ren, &fg);
@ -203,9 +204,57 @@ void render_units(Game *g) {
}
void render_bullets(Game *g) {
/* Kind-aware tracers / shells with streak tails (parity with snapshot.c). */
for (int i = 0; i < g->bullet_count; i++) {
Bullet *b = &g->bullets[i];
int sx = (int)(b->x - g->cam.x), sy = (int)(b->y - g->cam.y);
float dx = b->tx - b->x, dy = b->ty - b->y;
float d = sqrtf(dx * dx + dy * dy);
if (d > 1.0f) {
float ux = dx / d, uy = dy / d;
int len = (b->kind == 2) ? 24 : (b->kind == 3) ? 18
: (b->kind == 1) ? 14 : 10;
for (int t = 1; t <= len; t++) {
int tx = sx - (int)(ux * (float)t * 0.75f);
int ty = sy - (int)(uy * (float)t * 0.75f);
Uint8 fade = (Uint8)(255 - t * 9);
if (b->kind == 2) {
/* laser: cyan→magenta streak */
SDL_SetRenderDrawColor(g->ren, fade, 80, 255, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx - 1, ty - 1,
(t < 3) ? 3 : 2, (t < 3) ? 3 : 2 });
SDL_SetRenderDrawColor(g->ren, 220, 180, 255, 255);
SDL_RenderDrawPoint(g->ren, tx, ty);
} else if (b->kind == 3) {
SDL_SetRenderDrawColor(g->ren, 255, (Uint8)(120 - t * 4), 30, 230);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx - 1, ty - 1,
(t < 4) ? 3 : 2, (t < 4) ? 3 : 2 });
} else {
SDL_SetRenderDrawColor(g->ren, 255, (Uint8)(200 - t * 8), 50, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx, ty, 2, 2 });
}
}
}
int rad = (b->kind == 1 || b->kind == 3) ? 6 : 4;
SDL_SetRenderDrawColor(g->ren, 255, 160, 40, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - rad - 1, sy - rad - 1,
(rad + 1) * 2, (rad + 1) * 2 });
SDL_SetRenderDrawColor(g->ren, 255, 240, 140, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - rad, sy - rad, rad * 2, rad * 2 });
if (b->kind == 2) {
SDL_SetRenderDrawColor(g->ren, 120, 200, 255, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - rad - 1, sy - rad - 1,
(rad + 1) * 2, (rad + 1) * 2 });
SDL_SetRenderDrawColor(g->ren, 240, 220, 255, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - (rad - 1), sy - (rad - 1),
(rad - 1) * 2, (rad - 1) * 2 });
} else if (b->kind == 3) {
SDL_SetRenderDrawColor(g->ren, 255, 70, 30, 240);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - 5, sy - 1, 6, 6 });
SDL_SetRenderDrawColor(g->ren, 255, 200, 80, 240);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx, sy - 4, 4, 4 });
}
/* Keep sprite head as bright core on top of procedural tracer. */
SDL_Texture *tex = g->spr.bullet[b->kind < 4 ? b->kind : 0];
SDL_Rect r = { sx - 5, sy - 5, 10, 10 };
SDL_RenderCopy(g->ren, tex, NULL, &r);
@ -213,13 +262,16 @@ void render_bullets(Game *g) {
}
void render_particles(Game *g) {
/* Larger combat FX (parity with snapshot particle sizes). */
for (int i = 0; i < g->particle_count; i++) {
Particle *p = &g->particles[i];
int sx = (int)(p->x - g->cam.x), sy = (int)(p->y - g->cam.y);
Uint8 a = (Uint8)(255 * (p->life / p->max_life));
float life_a = p->life / (p->max_life > 0 ? p->max_life : 1.0f);
if (life_a < 0.12f) continue;
Uint8 a = (Uint8)(255 * life_a);
SDL_SetRenderDrawColor(g->ren, p->r, p->g, p->b, a);
int sz = (p->kind == 1) ? 3 : 2;
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx-sz/2, sy-sz/2, sz, sz });
int sz = (p->kind == 1) ? 5 : (p->kind == 3) ? 3 : (p->kind == 0) ? 4 : 3;
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - sz/2, sy - sz/2, sz + 1, sz + 1 });
}
}
@ -471,7 +523,7 @@ void render_minimap(Game *g) {
case T_WATER: cr=44;cg=78;cb=120; break;
case T_ROCK: cr=110;cg=104;cb=98; break;
case T_SCRAP: cr=200;cg=170;cb=70; break;
case T_GRASS: cr=86;cg=120;cb=64; break;
case T_GRASS: cr=210;cg=170;cb=78; break; /* arid wasteland */
}
if (fv == 1) { cr/=2; cg/=2; cb/=2; }
SDL_SetRenderDrawColor(g->ren, cr, cg, cb, 255);

View file

@ -69,6 +69,7 @@ static SDL_Color lighten(SDL_Color c, float f) {
}
/* ---------------- unit sprites ---------------- */
/* Near-black outline first (KKND2 silhouette), then bright faction fill. */
SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f) {
int S = 30;
SDL_Surface *s = new_surf(S, S);
@ -76,85 +77,93 @@ SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f) {
SDL_Color dark = darken(base, 0.55f);
SDL_Color lite = lighten(base, 0.45f);
SDL_Color metal = { 60, 60, 70, 255 };
const Uint8 ok = 18, og = 16, ob = 14; /* near-black outline */
switch (t) {
case UT_INFANTRY:
disc(s, 15, 15, 8, dark.r, dark.g, dark.b, 255);
disc(s, 15, 15, 6, base.r, base.g, base.b, 255);
disc(s, 15, 15, 9, ok, og, ob, 255);
disc(s, 15, 15, 7, base.r, base.g, base.b, 255);
disc(s, 15, 12, 3, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 15, 2, 30, 30, 30, 255); /* rifle */
line(s, 15, 15, 15, 2, ok, og, ob, 255); /* rifle */
break;
case UT_BUGGY:
fill_rect(s, 6, 9, 18, 12, dark.r, dark.g, dark.b, 255);
fill_rect(s, 8, 11, 14, 8, base.r, base.g, base.b, 255);
fill_rect(s, 5, 8, 20, 14, ok, og, ob, 255);
fill_rect(s, 7, 10, 16, 10, base.r, base.g, base.b, 255);
disc(s, 10, 8, 3, metal.r, metal.g, metal.b, 255);
disc(s, 20, 8, 3, metal.r, metal.g, metal.b, 255);
disc(s, 10, 22, 3, metal.r, metal.g, metal.b, 255);
disc(s, 20, 22, 3, metal.r, metal.g, metal.b, 255);
line(s, 15, 15, 28, 15, 30, 30, 30, 255);
line(s, 15, 15, 28, 15, ok, og, ob, 255);
break;
case UT_TANK:
fill_rect(s, 5, 10, 20, 11, dark.r, dark.g, dark.b, 255);
fill_rect(s, 7, 12, 16, 7, base.r, base.g, base.b, 255);
fill_rect(s, 4, 9, 22, 13, ok, og, ob, 255);
fill_rect(s, 6, 11, 18, 9, base.r, base.g, base.b, 255);
disc(s, 15, 15, 6, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 30, 15, 40, 40, 45, 255);
disc(s, 9, 22, 3, 30, 30, 30, 255);
disc(s, 21, 22, 3, 30, 30, 30, 255);
line(s, 15, 15, 30, 15, ok, og, ob, 255);
disc(s, 9, 22, 3, ok, og, ob, 255);
disc(s, 21, 22, 3, ok, og, ob, 255);
break;
case UT_ARTILLERY:
fill_rect(s, 5, 11, 20, 9, dark.r, dark.g, dark.b, 255);
fill_rect(s, 4, 10, 22, 11, ok, og, ob, 255);
fill_rect(s, 6, 12, 18, 7, base.r, base.g, base.b, 255);
disc(s, 15, 15, 5, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 4, 4, 40, 40, 45, 255);
line(s, 15, 15, 26, 26, 40, 40, 45, 255);
line(s, 15, 15, 4, 4, ok, og, ob, 255);
line(s, 15, 15, 26, 26, ok, og, ob, 255);
break;
case UT_HARVESTER:
fill_rect(s, 4, 8, 22, 14, dark.r, dark.g, dark.b, 255);
fill_rect(s, 6, 10, 18, 10, 110, 90, 50, 255); /* scrap bed */
disc(s, 9, 22, 3, 30, 30, 30, 255);
disc(s, 21, 22, 3, 30, 30, 30, 255);
fill_rect(s, 3, 7, 24, 16, ok, og, ob, 255);
fill_rect(s, 5, 9, 20, 12, dark.r, dark.g, dark.b, 255);
fill_rect(s, 6, 10, 18, 10, 180, 150, 70, 255); /* scrap bed */
disc(s, 9, 22, 3, ok, og, ob, 255);
disc(s, 21, 22, 3, ok, og, ob, 255);
line(s, 6, 8, 6, 4, base.r, base.g, base.b, 255);
break;
case UT_JEEP:
fill_rect(s, 7, 11, 16, 9, dark.r, dark.g, dark.b, 255);
fill_rect(s, 6, 10, 18, 11, ok, og, ob, 255);
fill_rect(s, 8, 12, 14, 7, base.r, base.g, base.b, 255);
disc(s, 10, 21, 3, metal.r, metal.g, metal.b, 255);
disc(s, 20, 21, 3, metal.r, metal.g, metal.b, 255);
line(s, 15, 12, 15, 5, base.r, base.g, base.b, 255); /* roll bar */
line(s, 15, 15, 27, 15, 40, 40, 45, 255);
line(s, 15, 15, 27, 15, ok, og, ob, 255);
break;
case UT_MUTANT:
disc(s, 15, 16, 9, dark.r, dark.g, dark.b, 255);
disc(s, 15, 14, 6, base.r, base.g, base.b, 255);
disc(s, 15, 16, 10, ok, og, ob, 255);
disc(s, 15, 14, 7, base.r, base.g, base.b, 255);
disc(s, 15, 11, 3, lite.r, lite.g, lite.b, 255);
line(s, 15, 16, 23, 10, 30, 30, 30, 255); /* arm/club */
line(s, 15, 16, 23, 10, ok, og, ob, 255); /* arm/club */
break;
case UT_BEAST:
fill_rect(s, 8, 11, 14, 9, dark.r, dark.g, dark.b, 255);
disc(s, 10, 20, 2, 30, 30, 30, 255);
disc(s, 14, 21, 2, 30, 30, 30, 255);
disc(s, 18, 20, 2, 30, 30, 30, 255);
disc(s, 22, 21, 2, 30, 30, 30, 255);
disc(s, 22, 13, 4, base.r, base.g, base.b, 255);
fill_rect(s, 7, 10, 16, 11, ok, og, ob, 255);
fill_rect(s, 9, 12, 12, 7, base.r, base.g, base.b, 255);
disc(s, 10, 20, 2, ok, og, ob, 255);
disc(s, 14, 21, 2, ok, og, ob, 255);
disc(s, 18, 20, 2, ok, og, ob, 255);
disc(s, 22, 21, 2, ok, og, ob, 255);
disc(s, 22, 13, 4, lite.r, lite.g, lite.b, 255);
break;
case UT_RAVAGER:
fill_rect(s, 3, 9, 24, 13, dark.r, dark.g, dark.b, 255);
fill_rect(s, 6, 12, 18, 7, base.r, base.g, base.b, 255);
fill_rect(s, 2, 8, 26, 15, ok, og, ob, 255);
fill_rect(s, 5, 11, 20, 9, base.r, base.g, base.b, 255);
disc(s, 15, 15, 7, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 29, 15, 40, 40, 45, 255); /* big barrel */
disc(s, 8, 22, 3, 30, 30, 30, 255);
disc(s, 22, 22, 3, 30, 30, 30, 255);
line(s, 15, 15, 29, 15, ok, og, ob, 255); /* big barrel */
disc(s, 8, 22, 3, ok, og, ob, 255);
disc(s, 22, 22, 3, ok, og, ob, 255);
break;
case UT_FLAME:
fill_rect(s, 8, 12, 14, 12, dark.r, dark.g, dark.b, 255);
disc(s, 15, 11, 4, base.r, base.g, base.b, 255);
fill_rect(s, 7, 11, 16, 14, ok, og, ob, 255);
fill_rect(s, 9, 13, 12, 10, base.r, base.g, base.b, 255);
disc(s, 15, 11, 4, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 27, 19, 220, 120, 40, 255); /* flame nozzle */
break;
case UT_MISSILE:
fill_rect(s, 7, 12, 16, 11, dark.r, dark.g, dark.b, 255);
fill_rect(s, 6, 11, 18, 13, ok, og, ob, 255);
fill_rect(s, 8, 13, 14, 9, base.r, base.g, base.b, 255);
disc(s, 10, 22, 3, metal.r, metal.g, metal.b, 255);
disc(s, 20, 22, 3, metal.r, metal.g, metal.b, 255);
line(s, 15, 15, 28, 8, 200, 60, 60, 255); /* launcher */
break;
case UT_MEDIC:
fill_rect(s, 7, 10, 16, 14, ok, og, ob, 255);
fill_rect(s, 8, 11, 14, 12, 235, 235, 235, 255); /* white ambulance body */
disc(s, 11, 23, 3, metal.r, metal.g, metal.b, 255);
disc(s, 19, 23, 3, metal.r, metal.g, metal.b, 255);
@ -163,7 +172,8 @@ SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f) {
fill_rect(s, 12, 9, 6, 2, 255, 255, 255, 255);
break;
default:
disc(s, 15, 15, 9, base.r, base.g, base.b, 255);
disc(s, 15, 15, 10, ok, og, ob, 255);
disc(s, 15, 15, 8, base.r, base.g, base.b, 255);
break;
}
SDL_Texture *tex = SDL_CreateTextureFromSurface(r, s);
@ -179,11 +189,14 @@ SDL_Texture *sprites_make_bld(SDL_Renderer *r, BuildingType t, Faction f) {
SDL_Color base = faction_color(f);
SDL_Color dark = darken(base, 0.5f);
SDL_Color lite = lighten(base, 0.4f);
SDL_Color wall = { 90, 84, 78, 255 };
SDL_Color wall2 = { 64, 60, 56, 255 };
/* Bright sand pad + dark ridge outline (arid wasteland plate). */
SDL_Color wall = { 198, 168, 108, 255 };
SDL_Color wall2 = { 155, 128, 78, 255 };
const Uint8 ok = 28, og = 24, ob = 18;
fill_rect(s, 0, 0, W, H, wall2.r, wall2.g, wall2.b, 255);
fill_rect(s, 3, 3, W - 6, H - 6, wall.r, wall.g, wall.b, 255);
fill_rect(s, 2, 2, W - 4, H - 4, ok, og, ob, 255);
fill_rect(s, 4, 4, W - 8, H - 8, wall.r, wall.g, wall.b, 255);
ring(s, W/2, H/2, (W<H?W:H)/2 - 3, 2, dark.r, dark.g, dark.b, 255);
switch (t) {
@ -239,15 +252,16 @@ SDL_Texture *sprites_make_bld(SDL_Renderer *r, BuildingType t, Faction f) {
SDL_Texture *sprites_make_terrain(SDL_Renderer *r, TerrainType t) {
SDL_Surface *s = new_surf(TILE, TILE);
Uint8 tr=0,tg=0,tb=0;
/* Classic KKND2 wasteland: yellow-tan sand (R>>B, G slightly under R). */
switch (t) {
case T_WASTELAND: tr=120; tg=104; tb=78; break;
case T_RUBBLE: tr=96; tg=86; tb=74; break;
case T_ROAD: tr=70; tg=68; tb=64; break;
case T_GRASS: tr=86; tg=120; tb=64; break;
case T_WATER: tr=44; tg=78; tb=120; break;
case T_ROCK: tr=110; tg=104; tb=98; break;
case T_SCRAP: tr=110; tg=96; tb=70; break;
default: tr=120; tg=104; tb=78; break;
case T_WASTELAND: tr=228; tg=188; tb=98; break;
case T_RUBBLE: tr=168; tg=138; tb=88; break;
case T_ROAD: tr=175; tg=148; tb=98; break;
case T_GRASS: tr=242; tg=198; tb=92; break; /* sunlit desert */
case T_WATER: tr=72; tg=128; tb=178; break;
case T_ROCK: tr=155; tg=142; tb=128; break;
case T_SCRAP: tr=240; tg=190; tb=78; break;
default: tr=228; tg=188; tb=98; break;
}
fill_rect(s, 0, 0, TILE, TILE, tr, tg, tb, 255);
/* speckle for texture */