engine-c: unit mid-layers/tyres + denser combat FX

This commit is contained in:
local-model/engine-L2-grok-4.5 2026-09-19 11:36:11 +02:00
parent c60752b878
commit aac30cb097
No known key found for this signature in database
4 changed files with 95 additions and 69 deletions

Binary file not shown.

View file

@ -57,8 +57,12 @@ void bullet_update(Game *g, float dt) {
Building *t = bld_by_id(g, b->target_bld);
if (t) bld_apply_damage(g, t, b->damage);
}
particle_burst(g, b->tx, b->ty, 4,
(b->kind==2)?2:1, 255, 200, 80);
/* denser impact: fire/laser core + smoke ring */
int hit_kind = (b->kind == 2) ? 2 : 1;
particle_burst(g, b->tx, b->ty, 7, hit_kind, 255, 200, 80);
particle_burst(g, b->tx, b->ty, 4, 0, 90, 80, 60);
if (b->kind == 1 || b->kind == 3)
particle_burst(g, b->tx, b->ty, 3, 3, 255, 140, 40);
/* swap-remove */
g->bullets[i] = g->bullets[g->bullet_count - 1];
g->bullet_count--;
@ -66,9 +70,12 @@ void bullet_update(Game *g, float dt) {
}
b->x += dx/d * step;
b->y += dy/d * step;
/* trail */
/* trail — denser muzzle streak for non-ballistic shells */
if (b->kind != 0)
particle_spawn(g, b->x, b->y, 2, 255, 180, 60);
if (b->kind == 1 || b->kind == 3)
particle_spawn(g, b->x - dx / d * 3.0f, b->y - dy / d * 3.0f,
3, 255, 160, 50);
}
}

View file

@ -293,18 +293,34 @@ void render_bullets(Game *g) {
}
}
/* Soft disc for smoke/fire particles (parity with snapshot disc FX). */
static void draw_particle_disc(SDL_Renderer *ren, int cx, int cy, int rad,
Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
SDL_SetRenderDrawColor(ren, r, g, b, a);
for (int j = -rad; j <= rad; j++) {
int half = (int)sqrtf((float)(rad * rad - j * j));
SDL_RenderDrawLine(ren, cx - half, cy + j, cx + half, cy + j);
}
}
void render_particles(Game *g) {
/* Larger combat FX (parity with snapshot particle sizes). */
/* Kind-aware combat FX: discs for smoke/fire, rects for sparks/debris. */
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);
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);
Uint8 rr = (Uint8)(p->r * life_a), gg = (Uint8)(p->g * life_a),
bb = (Uint8)(p->b * life_a);
int sz = (p->kind == 1) ? 5 : (p->kind == 3) ? 3 : (p->kind == 0) ? 4 : 3;
if (p->kind == 0 || p->kind == 1) {
draw_particle_disc(g->ren, sx, sy, sz + 1, rr, gg, bb, a);
} else {
SDL_SetRenderDrawColor(g->ren, rr, gg, bb, a);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - sz/2, sy - sz/2, sz + 1, sz + 1 });
}
}
}
void render_selection(Game *g) {

View file

@ -76,100 +76,103 @@ SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f) {
SDL_Color base = faction_color(f);
SDL_Color dark = darken(base, 0.55f);
SDL_Color lite = lighten(base, 0.45f);
SDL_Color metal = { 60, 60, 70, 255 };
/* Tyre / mid-tone parity with snapshot draw_unit (mr/mg/mb). */
const Uint8 tr = 70, tg = 65, tb = 58;
const Uint8 ok = 18, og = 16, ob = 14; /* near-black outline */
(void)dark;
switch (t) {
case UT_INFANTRY:
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, ok, og, ob, 255); /* rifle */
disc(s, 15, 11, 4, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 15, 1, ok, og, ob, 255); /* rifle */
break;
case UT_BUGGY:
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, ok, og, ob, 255);
fill_rect(s, 3, 7, 24, 16, ok, og, ob, 255);
fill_rect(s, 5, 9, 20, 12, base.r, base.g, base.b, 255);
fill_rect(s, 7, 11, 16, 8, lite.r, lite.g, lite.b, 255); /* mid body */
disc(s, 8, 7, 4, tr, tg, tb, 255);
disc(s, 22, 7, 4, tr, tg, tb, 255);
disc(s, 8, 23, 4, tr, tg, tb, 255);
disc(s, 22, 23, 4, tr, tg, tb, 255);
line(s, 15, 15, 29, 15, ok, og, ob, 255);
break;
case UT_TANK:
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);
fill_rect(s, 2, 8, 26, 14, ok, og, ob, 255);
fill_rect(s, 4, 10, 22, 10, base.r, base.g, base.b, 255);
disc(s, 15, 15, 7, lite.r, lite.g, lite.b, 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);
disc(s, 8, 23, 4, tr, tg, tb, 255);
disc(s, 22, 23, 4, tr, tg, tb, 255);
break;
case UT_ARTILLERY:
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, ok, og, ob, 255);
line(s, 15, 15, 26, 26, ok, og, ob, 255);
fill_rect(s, 2, 10, 26, 12, ok, og, ob, 255);
fill_rect(s, 4, 12, 22, 8, base.r, base.g, base.b, 255);
disc(s, 15, 15, 6, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 3, 3, ok, og, ob, 255);
line(s, 15, 15, 27, 27, ok, og, ob, 255);
break;
case UT_HARVESTER:
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);
fill_rect(s, 1, 6, 28, 18, ok, og, ob, 255);
fill_rect(s, 3, 8, 24, 14, base.r, base.g, base.b, 255);
fill_rect(s, 5, 10, 20, 10, 200, 170, 90, 255); /* scrap bed */
disc(s, 8, 23, 4, tr, tg, tb, 255);
disc(s, 22, 23, 4, tr, tg, tb, 255);
line(s, 5, 7, 5, 2, lite.r, lite.g, lite.b, 255);
break;
case UT_JEEP:
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, ok, og, ob, 255);
fill_rect(s, 4, 9, 22, 12, ok, og, ob, 255);
fill_rect(s, 6, 11, 18, 8, base.r, base.g, base.b, 255);
disc(s, 9, 22, 4, tr, tg, tb, 255);
disc(s, 21, 22, 4, tr, tg, tb, 255);
line(s, 15, 11, 15, 3, lite.r, lite.g, lite.b, 255); /* roll bar */
line(s, 15, 15, 28, 15, ok, og, ob, 255);
break;
case UT_MUTANT:
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, ok, og, ob, 255); /* arm/club */
disc(s, 15, 14, 8, base.r, base.g, base.b, 255);
disc(s, 15, 10, 4, lite.r, lite.g, lite.b, 255);
line(s, 15, 16, 24, 9, ok, og, ob, 255); /* arm/club */
break;
case UT_BEAST:
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);
fill_rect(s, 5, 9, 20, 12, ok, og, ob, 255);
fill_rect(s, 7, 11, 16, 8, base.r, base.g, base.b, 255);
disc(s, 9, 21, 3, tr, tg, tb, 255);
disc(s, 14, 22, 3, tr, tg, tb, 255);
disc(s, 19, 21, 3, tr, tg, tb, 255);
disc(s, 23, 22, 3, tr, tg, tb, 255);
disc(s, 23, 12, 5, lite.r, lite.g, lite.b, 255);
break;
case UT_RAVAGER:
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);
fill_rect(s, 0, 7, 30, 16, ok, og, ob, 255);
fill_rect(s, 2, 9, 26, 12, base.r, base.g, base.b, 255);
disc(s, 15, 15, 8, lite.r, lite.g, lite.b, 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);
disc(s, 7, 23, 4, tr, tg, tb, 255);
disc(s, 23, 23, 4, tr, tg, tb, 255);
break;
case UT_FLAME:
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 */
fill_rect(s, 5, 11, 20, 15, ok, og, ob, 255);
fill_rect(s, 7, 13, 16, 11, base.r, base.g, base.b, 255);
disc(s, 15, 10, 5, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 28, 20, 255, 150, 50, 255); /* flame nozzle */
break;
case UT_MISSILE:
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 */
fill_rect(s, 4, 11, 22, 14, ok, og, ob, 255);
fill_rect(s, 6, 13, 18, 10, base.r, base.g, base.b, 255);
disc(s, 9, 23, 4, tr, tg, tb, 255);
disc(s, 21, 23, 4, tr, tg, tb, 255);
line(s, 15, 15, 28, 6, 255, 80, 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);
disc(s, 15, 10, 4, 220, 60, 60, 255); /* red cross */
fill_rect(s, 14, 7, 2, 6, 255, 255, 255, 255);
fill_rect(s, 12, 9, 6, 2, 255, 255, 255, 255);
fill_rect(s, 5, 10, 20, 15, ok, og, ob, 255);
fill_rect(s, 7, 12, 16, 11, 245, 245, 245, 255); /* white ambulance */
disc(s, 10, 24, 4, tr, tg, tb, 255);
disc(s, 20, 24, 4, tr, tg, tb, 255);
disc(s, 15, 9, 5, 240, 50, 50, 255); /* red cross */
fill_rect(s, 14, 5, 3, 8, 255, 255, 255, 255);
fill_rect(s, 11, 8, 8, 3, 255, 255, 255, 255);
break;
default:
disc(s, 15, 15, 10, ok, og, ob, 255);