engine-c: denser mid-air tracers + artillery/missile arcs

This commit is contained in:
local-model/engine-L2-grok-4.5 2026-09-19 17:42:57 +02:00
parent 1ef6df4ce5
commit b4d3388701
No known key found for this signature in database
7 changed files with 187 additions and 76 deletions

View file

@ -312,6 +312,9 @@ typedef struct Building {
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
typedef struct { typedef struct {
float x, y, tx, ty; float x, y, tx, ty;
float ox, oy; /* origin for ballistic arc (shell/missile) */
float loft; /* peak height above chord; 0 = linear */
float prog; /* 0..1 along chord for lofted kinds */
float speed; float speed;
float damage; float damage;
Faction faction; Faction faction;
@ -560,6 +563,7 @@ int bld_research(Game *g, Faction f, int up);
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld, void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld,
float dmg, Faction f, int kind); float dmg, Faction f, int kind);
void bullet_place_at_prog(Bullet *b, float prog);
void bullet_update(Game *g, float dt); void bullet_update(Game *g, float dt);
void particle_spawn(Game *g, float x, float y, int kind, uint8_t cr, uint8_t cg, uint8_t cb); void particle_spawn(Game *g, float x, float y, int kind, uint8_t cr, uint8_t cg, uint8_t cb);
void particle_burst(Game *g, float x, float y, int n, int kind, uint8_t cr, uint8_t cg, uint8_t cb); void particle_burst(Game *g, float x, float y, int n, int kind, uint8_t cr, uint8_t cg, uint8_t cb);

View file

@ -210,10 +210,14 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
unit_spawn(g, UT_RAVAGER, human, wx + 78, wy - 58); unit_spawn(g, UT_RAVAGER, human, wx + 78, wy - 58);
unit_spawn(g, UT_ARTILLERY, human, wx + 18, wy + 48); unit_spawn(g, UT_ARTILLERY, human, wx + 18, wy + 48);
unit_spawn(g, UT_ARTILLERY, human, wx + 38, wy + 62); unit_spawn(g, UT_ARTILLERY, human, wx + 38, wy + 62);
unit_spawn(g, UT_ARTILLERY, human, wx + 8, wy + 28);
unit_spawn(g, UT_ARTILLERY, human, wx + 52, wy + 44);
unit_spawn(g, UT_FLAME, human, wx - 40, wy + 12); unit_spawn(g, UT_FLAME, human, wx - 40, wy + 12);
unit_spawn(g, UT_FLAME, human, wx + 105, wy - 5); unit_spawn(g, UT_FLAME, human, wx + 105, wy - 5);
unit_spawn(g, UT_MISSILE, human, wx - 55, wy - 8); unit_spawn(g, UT_MISSILE, human, wx - 55, wy - 8);
unit_spawn(g, UT_MISSILE, human, wx + 110, wy - 40); unit_spawn(g, UT_MISSILE, human, wx + 110, wy - 40);
unit_spawn(g, UT_MISSILE, human, wx + 96, wy - 52);
unit_spawn(g, UT_MISSILE, human, wx + 120, wy - 18);
unit_spawn(g, UT_JEEP, human, wx + 32, wy - 52); unit_spawn(g, UT_JEEP, human, wx + 32, wy - 52);
unit_spawn(g, UT_JEEP, human, wx + 48, wy - 68); unit_spawn(g, UT_JEEP, human, wx + 48, wy - 68);
unit_spawn(g, UT_BUGGY, human, wx + 55, wy - 78); unit_spawn(g, UT_BUGGY, human, wx + 55, wy - 78);
@ -649,8 +653,12 @@ static void place_showcase(Game *g, Faction human, Faction enemy) {
raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 62, ry - 28); raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 62, ry - 28);
raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 78, ry - 8); raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 78, ry - 8);
raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 94, ry + 6); raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 94, ry + 6);
raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 108, ry - 20);
raid[rn++] = unit_spawn(g, UT_MISSILE, enemy, rx + 70, ry + 14);
raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 85, ry + 10); raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 85, ry + 10);
raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 98, ry - 18); raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 98, ry - 18);
raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 112, ry + 4);
raid[rn++] = unit_spawn(g, UT_ARTILLERY, enemy, rx + 76, ry - 40);
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);

View file

@ -203,36 +203,39 @@ int main(int argc, char **argv) {
} }
} }
/* Mid-flight shells/missiles/lasers: after ~1s sim most live /* Mid-flight shells/missiles/lasers: after ~1s sim most live
* bullets have already impacted. Reseed between nearby opposing * bullets have already impacted. Reseed from attacker origin with
* pairs so the PNG shows KKND2-like tracers across the clash. */ * mid-flight prog so lofted kinds keep their parabolic arc in PNG. */
{ {
int spawned = 0; int spawned = 0;
const float pair_r2 = 180.0f * 180.0f; const float pair_r2 = 180.0f * 180.0f;
for (int i = 0; i < g->unit_count && spawned < 72; i++) { for (int i = 0; i < g->unit_count && spawned < 110; i++) {
Unit *a = &g->units[i]; Unit *a = &g->units[i];
if (a->dead) continue; if (a->dead) continue;
float ax = a->x - clash_x, ay = a->y - clash_y; float ax = a->x - clash_x, ay = a->y - clash_y;
if (ax * ax + ay * ay > r2) continue; if (ax * ax + ay * ay > r2) continue;
int shots = 0; int shots = 0;
for (int j = 0; j < g->unit_count && spawned < 72 && shots < 3; j++) { for (int j = 0; j < g->unit_count && spawned < 110 && shots < 4; j++) {
Unit *b = &g->units[j]; Unit *b = &g->units[j];
if (b->dead || b->faction == a->faction) continue; if (b->dead || b->faction == a->faction) continue;
float dx = b->x - a->x, dy = b->y - a->y; float dx = b->x - a->x, dy = b->y - a->y;
float d2 = dx * dx + dy * dy; float d2 = dx * dx + dy * dy;
if (d2 > pair_r2 || d2 < 20.0f * 20.0f) continue; if (d2 > pair_r2 || d2 < 20.0f * 20.0f) continue;
int kind = (spawned % 4 == 0) ? 3 /* bias shell/missile arcs over MG/laser */
: (spawned % 4 == 1) ? 1 int kind = (spawned % 5 == 0) ? 0
: (spawned % 4 == 2) ? 2 : 0; : (spawned % 5 == 1) ? 2
: (spawned % 5 == 2) ? 1
: (spawned % 5 == 3) ? 3 : 1;
float t = 0.18f + 0.60f * ((spawned * 37) % 100) / 100.0f; float t = 0.18f + 0.60f * ((spawned * 37) % 100) / 100.0f;
float sx = a->x + dx * t; int before = g->bullet_count;
float sy = a->y + dy * t; bullet_spawn(g, a->x, a->y, b->id, -1, 1.0f, a->faction, kind);
bullet_spawn(g, sx, sy, b->id, -1, 1.0f, a->faction, kind); if (g->bullet_count > before)
bullet_place_at_prog(&g->bullets[g->bullet_count - 1], t);
spawned++; spawned++;
shots++; shots++;
} }
} }
/* turret / base guns → nearest enemy in clash */ /* turret / base guns → nearest enemy in clash */
for (int i = 0; i < g->bld_count && spawned < 88; i++) { for (int i = 0; i < g->bld_count && spawned < 130; i++) {
Building *bl = &g->buildings[i]; Building *bl = &g->buildings[i];
if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE)) if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE))
continue; continue;
@ -250,10 +253,12 @@ int main(int argc, char **argv) {
} }
if (best < 0) continue; if (best < 0) continue;
Unit *tgt = &g->units[best]; Unit *tgt = &g->units[best];
float dx = tgt->x - bx, dy = tgt->y - by;
float t = 0.25f + 0.45f * ((spawned * 19) % 100) / 100.0f; float t = 0.25f + 0.45f * ((spawned * 19) % 100) / 100.0f;
bullet_spawn(g, bx + dx * t, by + dy * t, tgt->id, -1, int kind = (spawned % 3 == 0) ? 3 : 1;
2.0f, bl->faction, (spawned & 1) ? 1 : 2); int before = g->bullet_count;
bullet_spawn(g, bx, by, tgt->id, -1, 2.0f, bl->faction, kind);
if (g->bullet_count > before)
bullet_place_at_prog(&g->bullets[g->bullet_count - 1], t);
particle_burst(g, bx, by - 4.0f, 5, 2, 255, 220, 100); particle_burst(g, bx, by - 4.0f, 5, 2, 255, 220, 100);
spawned++; spawned++;
} }
@ -307,29 +312,32 @@ int main(int argc, char **argv) {
int spawned2 = 0; int spawned2 = 0;
const float pair_r2 = 160.0f * 160.0f; const float pair_r2 = 160.0f * 160.0f;
const float r2b = 140.0f * 140.0f; const float r2b = 140.0f * 140.0f;
for (int i = 0; i < g->unit_count && spawned2 < 48; i++) { for (int i = 0; i < g->unit_count && spawned2 < 72; i++) {
Unit *a = &g->units[i]; Unit *a = &g->units[i];
if (a->dead) continue; if (a->dead) continue;
float ax = a->x - clash2_x, ay = a->y - clash2_y; float ax = a->x - clash2_x, ay = a->y - clash2_y;
if (ax * ax + ay * ay > r2b) continue; if (ax * ax + ay * ay > r2b) continue;
int shots = 0; int shots = 0;
for (int j = 0; j < g->unit_count && spawned2 < 48 && shots < 3; j++) { for (int j = 0; j < g->unit_count && spawned2 < 72 && shots < 4; j++) {
Unit *b = &g->units[j]; Unit *b = &g->units[j];
if (b->dead || b->faction == a->faction) continue; if (b->dead || b->faction == a->faction) continue;
float dx = b->x - a->x, dy = b->y - a->y; float dx = b->x - a->x, dy = b->y - a->y;
float d2 = dx * dx + dy * dy; float d2 = dx * dx + dy * dy;
if (d2 > pair_r2 || d2 < 18.0f * 18.0f) continue; if (d2 > pair_r2 || d2 < 18.0f * 18.0f) continue;
int kind = (spawned2 % 4 == 0) ? 3 int kind = (spawned2 % 5 == 0) ? 0
: (spawned2 % 4 == 1) ? 1 : (spawned2 % 5 == 1) ? 2
: (spawned2 % 4 == 2) ? 2 : 0; : (spawned2 % 5 == 2) ? 1
: (spawned2 % 5 == 3) ? 3 : 1;
float t = 0.18f + 0.58f * ((spawned2 * 41) % 100) / 100.0f; float t = 0.18f + 0.58f * ((spawned2 * 41) % 100) / 100.0f;
bullet_spawn(g, a->x + dx * t, a->y + dy * t, int before = g->bullet_count;
b->id, -1, 1.0f, a->faction, kind); bullet_spawn(g, a->x, a->y, b->id, -1, 1.0f, a->faction, kind);
if (g->bullet_count > before)
bullet_place_at_prog(&g->bullets[g->bullet_count - 1], t);
spawned2++; spawned2++;
shots++; shots++;
} }
} }
for (int i = 0; i < g->bld_count && spawned2 < 56; i++) { for (int i = 0; i < g->bld_count && spawned2 < 88; i++) {
Building *bl = &g->buildings[i]; Building *bl = &g->buildings[i];
if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE)) if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE))
continue; continue;
@ -347,10 +355,12 @@ int main(int argc, char **argv) {
} }
if (best < 0) continue; if (best < 0) continue;
Unit *tgt = &g->units[best]; Unit *tgt = &g->units[best];
float dx = tgt->x - bx, dy = tgt->y - by;
float t = 0.26f + 0.44f * ((spawned2 * 23) % 100) / 100.0f; float t = 0.26f + 0.44f * ((spawned2 * 23) % 100) / 100.0f;
bullet_spawn(g, bx + dx * t, by + dy * t, tgt->id, -1, int kind = (spawned2 % 3 == 0) ? 3 : 1;
2.0f, bl->faction, (spawned2 & 1) ? 1 : 2); int before = g->bullet_count;
bullet_spawn(g, bx, by, tgt->id, -1, 2.0f, bl->faction, kind);
if (g->bullet_count > before)
bullet_place_at_prog(&g->bullets[g->bullet_count - 1], t);
spawned2++; spawned2++;
} }
} }

View file

@ -7,11 +7,36 @@
static RNG g_prng; static RNG g_prng;
static int g_prng_init = 0; static int g_prng_init = 0;
/* Place bullet on chord+parabola: loft peaks at prog=0.5 (screen-y up = -). */
static void bullet_place_arc(Bullet *b) {
float t = b->prog;
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
b->x = b->ox + (b->tx - b->ox) * t;
b->y = b->oy + (b->ty - b->oy) * t - b->loft * 4.0f * t * (1.0f - t);
}
/* Mid-flight place: set prog then arc (shell/missile) or linear chord. */
void bullet_place_at_prog(Bullet *b, float prog) {
if (prog < 0.0f) prog = 0.0f;
if (prog > 1.0f) prog = 1.0f;
b->prog = prog;
if ((b->kind == 1 || b->kind == 3) && b->loft > 0.5f)
bullet_place_arc(b);
else {
b->x = b->ox + (b->tx - b->ox) * prog;
b->y = b->oy + (b->ty - b->oy) * prog;
}
}
void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld, void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld,
float dmg, Faction f, int kind) { float dmg, Faction f, int kind) {
if (g->bullet_count >= MAX_BULLETS) return; if (g->bullet_count >= MAX_BULLETS) return;
Bullet *b = &g->bullets[g->bullet_count++]; Bullet *b = &g->bullets[g->bullet_count++];
b->x = x; b->y = y; b->x = x; b->y = y;
b->ox = x; b->oy = y;
b->prog = 0.0f;
b->loft = 0.0f;
b->target_unit = target_unit; b->target_unit = target_unit;
b->target_bld = target_bld; b->target_bld = target_bld;
b->damage = dmg; b->damage = dmg;
@ -19,6 +44,7 @@ void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld,
b->kind = kind; b->kind = kind;
b->speed = (kind == 3) ? 320 : (kind == 1 ? 420 : 520); b->speed = (kind == 3) ? 320 : (kind == 1 ? 420 : 520);
b->life = 3.0f; b->life = 3.0f;
b->tx = x; b->ty = y;
if (target_unit >= 0) { if (target_unit >= 0) {
Unit *t = unit_by_id(g, target_unit); Unit *t = unit_by_id(g, target_unit);
if (t) { b->tx = t->x; b->ty = t->y; } if (t) { b->tx = t->x; b->ty = t->y; }
@ -27,6 +53,15 @@ void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld,
if (t) { b->tx = (float)(t->tx*TILE + t->w*TILE/2); if (t) { b->tx = (float)(t->tx*TILE + t->w*TILE/2);
b->ty = (float)(t->ty*TILE + t->h*TILE/2); } b->ty = (float)(t->ty*TILE + t->h*TILE/2); }
} }
/* shells / missiles get a parabolic loft scaled by range */
if (kind == 1 || kind == 3) {
float dx = b->tx - b->ox, dy = b->ty - b->oy;
float dist = sqrtf(dx * dx + dy * dy);
float base = (kind == 3) ? 28.0f : 18.0f;
b->loft = base + dist * ((kind == 3) ? 0.22f : 0.14f);
if (b->loft > 90.0f) b->loft = 90.0f;
bullet_place_arc(b);
}
} }
void bullet_update(Game *g, float dt) { void bullet_update(Game *g, float dt) {
@ -45,10 +80,26 @@ void bullet_update(Game *g, float dt) {
else b->target_bld = -1; else b->target_bld = -1;
} }
int lofted = (b->kind == 1 || b->kind == 3) && b->loft > 0.5f;
float hit = 0;
if (lofted) {
float cdx = b->tx - b->ox, cdy = b->ty - b->oy;
float chord = sqrtf(cdx * cdx + cdy * cdy);
if (chord < 1.0f) chord = 1.0f;
b->prog += (b->speed * dt) / chord;
bullet_place_arc(b);
hit = (b->prog >= 1.0f || b->life <= 0) ? 1.0f : 0.0f;
} else {
float dx = b->tx - b->x, dy = b->ty - b->y; float dx = b->tx - b->x, dy = b->ty - b->y;
float d = (float)sqrtf(dx*dx + dy*dy); float d = (float)sqrtf(dx*dx + dy*dy);
float step = b->speed * dt; float step = b->speed * dt;
if (d <= step + 4 || b->life <= 0) { if (d <= step + 4 || b->life <= 0) hit = 1.0f;
else {
b->x += dx/d * step;
b->y += dy/d * step;
}
}
if (hit > 0) {
/* impact */ /* impact */
if (b->target_unit >= 0) { if (b->target_unit >= 0) {
Unit *t = unit_by_id(g, b->target_unit); Unit *t = unit_by_id(g, b->target_unit);
@ -69,16 +120,22 @@ void bullet_update(Game *g, float dt) {
g->bullet_count--; g->bullet_count--;
continue; continue;
} }
b->x += dx/d * step; /* trail — denser muzzle streak for non-MG */
b->y += dy/d * step;
/* trail — denser muzzle streak for non-ballistic shells */
if (b->kind != 0) if (b->kind != 0)
particle_spawn(g, b->x, b->y, 2, 255, 180, 60); particle_spawn(g, b->x, b->y, 2, 255, 180, 60);
if (b->kind == 1 || b->kind == 3) { if (b->kind == 1 || b->kind == 3) {
particle_spawn(g, b->x - dx / d * 3.0f, b->y - dy / d * 3.0f, float tback = b->prog - 0.04f;
3, 255, 160, 50); if (tback < 0.0f) tback = 0.0f;
particle_spawn(g, b->x - dx / d * 6.0f, b->y - dy / d * 6.0f, float bx = b->ox + (b->tx - b->ox) * tback;
1, 255, 140, 40); float by = b->oy + (b->ty - b->oy) * tback
- b->loft * 4.0f * tback * (1.0f - tback);
particle_spawn(g, bx, by, 3, 255, 160, 50);
tback = b->prog - 0.08f;
if (tback < 0.0f) tback = 0.0f;
bx = b->ox + (b->tx - b->ox) * tback;
by = b->oy + (b->ty - b->oy) * tback
- b->loft * 4.0f * tback * (1.0f - tback);
particle_spawn(g, bx, by, 1, 255, 140, 40);
} }
if (b->kind == 2) if (b->kind == 2)
particle_spawn(g, b->x, b->y, 2, 180, 120, 255); particle_spawn(g, b->x, b->y, 2, 180, 120, 255);

View file

@ -439,37 +439,54 @@ void render_units(Game *g) {
} }
void render_bullets(Game *g) { void render_bullets(Game *g) {
/* Kind-aware tracers / shells with streak tails (parity with snapshot.c). */ /* Kind-aware tracers / shells; lofted kinds sample the parabola trail. */
for (int i = 0; i < g->bullet_count; i++) { for (int i = 0; i < g->bullet_count; i++) {
Bullet *b = &g->bullets[i]; Bullet *b = &g->bullets[i];
int sx = (int)(b->x - g->cam.x), sy = (int)(b->y - g->cam.y); int sx = (int)(b->x - g->cam.x), sy = (int)(b->y - g->cam.y);
int lofted = (b->kind == 1 || b->kind == 3) && b->loft > 0.5f;
if (lofted) {
int len = (b->kind == 3) ? 28 : 22;
float step = 0.018f;
for (int t = 1; t <= len; t++) {
float tp = b->prog - step * (float)t;
if (tp < 0.0f) break;
float wx = b->ox + (b->tx - b->ox) * tp;
float wy = b->oy + (b->ty - b->oy) * tp
- b->loft * 4.0f * tp * (1.0f - tp);
int tx = (int)(wx - g->cam.x);
int ty = (int)(wy - g->cam.y);
if (b->kind == 3) {
SDL_SetRenderDrawColor(g->ren, 255, (Uint8)(120 - t * 3), 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 * 7), 50, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx, ty, 2, 2 });
}
}
} else {
float dx = b->tx - b->x, dy = b->ty - b->y; float dx = b->tx - b->x, dy = b->ty - b->y;
float d = sqrtf(dx * dx + dy * dy); float d = sqrtf(dx * dx + dy * dy);
if (d > 1.0f) { if (d > 1.0f) {
float ux = dx / d, uy = dy / d; float ux = dx / d, uy = dy / d;
int len = (b->kind == 2) ? 32 : (b->kind == 3) ? 24 int len = (b->kind == 2) ? 32 : 14;
: (b->kind == 1) ? 20 : 14;
for (int t = 1; t <= len; t++) { for (int t = 1; t <= len; t++) {
int tx = sx - (int)(ux * (float)t * 0.75f); int tx = sx - (int)(ux * (float)t * 0.75f);
int ty = sy - (int)(uy * (float)t * 0.75f); int ty = sy - (int)(uy * (float)t * 0.75f);
Uint8 fade = (Uint8)(255 - t * 9); Uint8 fade = (Uint8)(255 - t * 9);
if (b->kind == 2) { if (b->kind == 2) {
/* laser: cyan→magenta streak */
SDL_SetRenderDrawColor(g->ren, fade, 80, 255, 220); SDL_SetRenderDrawColor(g->ren, fade, 80, 255, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx - 1, ty - 1, SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx - 1, ty - 1,
(t < 3) ? 3 : 2, (t < 3) ? 3 : 2 }); (t < 3) ? 3 : 2, (t < 3) ? 3 : 2 });
SDL_SetRenderDrawColor(g->ren, 220, 180, 255, 255); SDL_SetRenderDrawColor(g->ren, 220, 180, 255, 255);
SDL_RenderDrawPoint(g->ren, tx, ty); 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 { } else {
SDL_SetRenderDrawColor(g->ren, 255, (Uint8)(200 - t * 8), 50, 220); SDL_SetRenderDrawColor(g->ren, 255, (Uint8)(200 - t * 8), 50, 220);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx, ty, 2, 2 }); SDL_RenderFillRect(g->ren, &(SDL_Rect){ tx, ty, 2, 2 });
} }
} }
} }
}
int rad = (b->kind == 1 || b->kind == 3) ? 8 : 5; int rad = (b->kind == 1 || b->kind == 3) ? 8 : 5;
SDL_SetRenderDrawColor(g->ren, 255, 160, 40, 255); SDL_SetRenderDrawColor(g->ren, 255, 160, 40, 255);
SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - rad - 1, sy - rad - 1, SDL_RenderFillRect(g->ren, &(SDL_Rect){ sx - rad - 1, sy - rad - 1,

View file

@ -925,31 +925,46 @@ void snapshot_capture(Game *g, const char *path) {
draw_unit(u, sx, sy); draw_unit(u, sx, sy);
} }
/* projectiles — bright tracers / shells with streak tails (KKND2-like) */ /* projectiles — lofted shell/missile sample parabola; else linear streak */
for (int i = 0; i < g->bullet_count; i++) { for (int i = 0; i < g->bullet_count; i++) {
Bullet *p = &g->bullets[i]; Bullet *p = &g->bullets[i];
int sx = (int)(p->x - camx), sy = (int)(p->y - camy); int sx = (int)(p->x - camx), sy = (int)(p->y - camy);
int lofted = (p->kind == 1 || p->kind == 3) && p->loft > 0.5f;
if (lofted) {
int len = (p->kind == 3) ? 28 : 22;
float step = 0.018f;
for (int t = 1; t <= len; t++) {
float tp = p->prog - step * (float)t;
if (tp < 0.0f) break;
float wx = p->ox + (p->tx - p->ox) * tp;
float wy = p->oy + (p->ty - p->oy) * tp
- p->loft * 4.0f * tp * (1.0f - tp);
int tx = (int)(wx - camx);
int ty = (int)(wy - camy);
if (p->kind == 3)
disc(tx, ty, (t < 4) ? 2 : 1, 255, (uint8_t)(120 - t * 3), 30);
else
disc(tx, ty, 1, 255, (uint8_t)(200 - t * 7), 50);
}
} else {
float dx = p->tx - p->x, dy = p->ty - p->y; float dx = p->tx - p->x, dy = p->ty - p->y;
float d = sqrtf(dx * dx + dy * dy); float d = sqrtf(dx * dx + dy * dy);
if (d > 1.0f) { if (d > 1.0f) {
float ux = dx / d, uy = dy / d; float ux = dx / d, uy = dy / d;
int len = (p->kind == 2) ? 32 : (p->kind == 3) ? 24 int len = (p->kind == 2) ? 32 : 14;
: (p->kind == 1) ? 20 : 14;
for (int t = 1; t <= len; t++) { for (int t = 1; t <= len; t++) {
int tx = sx - (int)(ux * (float)t * 0.75f); int tx = sx - (int)(ux * (float)t * 0.75f);
int ty = sy - (int)(uy * (float)t * 0.75f); int ty = sy - (int)(uy * (float)t * 0.75f);
uint8_t fade = (uint8_t)(255 - t * 9); uint8_t fade = (uint8_t)(255 - t * 9);
if (p->kind == 2) { if (p->kind == 2) {
/* laser beam: cyan→magenta streak */
disc(tx, ty, (t < 3) ? 2 : 1, fade, 80, 255); disc(tx, ty, (t < 3) ? 2 : 1, fade, 80, 255);
px(tx, ty, 220, 180, 255); px(tx, ty, 220, 180, 255);
} else if (p->kind == 3) {
disc(tx, ty, (t < 4) ? 2 : 1, 255, (uint8_t)(120 - t * 4), 30);
} else { } else {
disc(tx, ty, 1, 255, (uint8_t)(200 - t * 8), 50); disc(tx, ty, 1, 255, (uint8_t)(200 - t * 8), 50);
} }
} }
} }
}
int rad = (p->kind == 1 || p->kind == 3) ? 8 : 5; int rad = (p->kind == 1 || p->kind == 3) ? 8 : 5;
disc(sx, sy, rad + 1, 255, 160, 40); disc(sx, sy, rad + 1, 255, 160, 40);
disc(sx, sy, rad, 255, 240, 140); disc(sx, sy, rad, 255, 240, 140);

View file

@ -351,8 +351,8 @@ static void unit_combat_step(Game *g, Unit *u, float dt) {
if ((tu || tb) && d <= rng) { if ((tu || tb) && d <= rng) {
u->heading = (float)atan2(ty - u->y, tx - u->x); u->heading = (float)atan2(ty - u->y, tx - u->x);
if (u->atk_cd <= 0) { if (u->atk_cd <= 0) {
int kind = (u->type == UT_ARTILLERY || u->type == UT_MISSILE || u->type == UT_TANK) int kind = (u->type == UT_MISSILE) ? 3
? 1 : 0; : (u->type == UT_ARTILLERY || u->type == UT_TANK) ? 1 : 0;
/* muzzle offset along heading so shot/live FX sit at barrel tip */ /* muzzle offset along heading so shot/live FX sit at barrel tip */
float mx = u->x + cosf(u->heading) * 14.0f; float mx = u->x + cosf(u->heading) * 14.0f;
float my = u->y + sinf(u->heading) * 14.0f; float my = u->y + sinf(u->heading) * 14.0f;