diff --git a/engine-c/include/kknd.h b/engine-c/include/kknd.h index 8afbe0a..de679a7 100644 --- a/engine-c/include/kknd.h +++ b/engine-c/include/kknd.h @@ -282,7 +282,7 @@ typedef struct { float rot; } Wreck; -#define MAX_PARTICLES 2048 +/* MAX_PARTICLES set above (4000) — keep one SoT for denser clash FX */ /* ----------------------------------------------------------------------- */ /* Buildings */ diff --git a/engine-c/src/main.c b/engine-c/src/main.c index d351989..a1ffb77 100644 --- a/engine-c/src/main.c +++ b/engine-c/src/main.c @@ -147,40 +147,74 @@ int main(int argc, char **argv) { } if (n > 0) { cx /= n; cy /= n; } else { cx = clash_x; cy = clash_y; } - particle_burst(g, cx, cy, 56, 1, 255, 140, 40); - particle_burst(g, cx + 22, cy - 12, 36, 0, 110, 100, 90); - particle_burst(g, cx - 18, cy + 14, 40, 2, 255, 230, 90); - particle_burst(g, cx + 36, cy + 8, 30, 1, 255, 90, 30); - particle_burst(g, cx - 8, cy - 28, 32, 3, 95, 80, 65); - particle_burst(g, cx + 12, cy + 30, 28, 1, 255, 170, 55); - particle_burst(g, cx - 30, cy - 6, 26, 1, 255, 120, 40); - particle_burst(g, cx + 28, cy + 22, 24, 2, 255, 210, 80); - particle_burst(g, cx - 40, cy + 18, 22, 1, 255, 100, 35); - particle_burst(g, cx + 44, cy - 16, 20, 2, 255, 240, 100); - particle_burst(g, cx + 8, cy - 40, 18, 1, 255, 150, 45); - particle_burst(g, cx - 22, cy + 36, 18, 0, 120, 110, 95); + /* denser mid-fight plate: fire cores + smoke columns + sparks */ + particle_burst(g, cx, cy, 72, 1, 255, 140, 40); + particle_burst(g, cx + 22, cy - 12, 48, 0, 110, 100, 90); + particle_burst(g, cx - 18, cy + 14, 52, 2, 255, 230, 90); + particle_burst(g, cx + 36, cy + 8, 40, 1, 255, 90, 30); + particle_burst(g, cx - 8, cy - 28, 42, 3, 95, 80, 65); + particle_burst(g, cx + 12, cy + 30, 36, 1, 255, 170, 55); + particle_burst(g, cx - 30, cy - 6, 34, 1, 255, 120, 40); + particle_burst(g, cx + 28, cy + 22, 32, 2, 255, 210, 80); + particle_burst(g, cx - 40, cy + 18, 30, 1, 255, 100, 35); + particle_burst(g, cx + 44, cy - 16, 28, 2, 255, 240, 100); + particle_burst(g, cx + 8, cy - 40, 26, 1, 255, 150, 45); + particle_burst(g, cx - 22, cy + 36, 26, 0, 120, 110, 95); + particle_burst(g, cx + 52, cy + 4, 24, 1, 255, 110, 30); + particle_burst(g, cx - 48, cy - 14, 22, 0, 100, 95, 85); + particle_burst(g, cx + 16, cy + 48, 20, 3, 85, 70, 55); + particle_burst(g, cx - 12, cy - 48, 20, 1, 255, 160, 50); + /* building smoke / fire columns near clash (HQ, walls, turrets) */ + for (int i = 0; i < g->bld_count; i++) { + Building *bl = &g->buildings[i]; + if (bl->dead || bl->build < 1.0f) continue; + float bx = (float)(bl->tx * TILE + bl->w * TILE / 2); + float by = (float)(bl->ty * TILE + bl->h * TILE / 2); + float bdx = bx - clash_x, bdy = by - clash_y; + if (bdx * bdx + bdy * bdy > r2) continue; + int smoke_n = (bl->type == BT_BASE || bl->type == BT_TURRET) ? 18 : 12; + particle_burst(g, bx, by - 10.0f, smoke_n, 0, 105, 95, 85); + if (bl->hp < bl->max_hp * 0.85f || bl->type == BT_TURRET) { + particle_burst(g, bx + 4.0f, by - 6.0f, 14, 1, 255, 130, 40); + particle_burst(g, bx - 3.0f, by - 14.0f, 8, 3, 90, 75, 60); + } + } + /* muzzle flashes on units in the clash radius */ + { + int muz = 0; + for (int i = 0; i < g->unit_count && muz < 28; i++) { + Unit *u = &g->units[i]; + if (u->dead) continue; + float ux = u->x - clash_x, uy = u->y - clash_y; + if (ux * ux + uy * uy > r2) continue; + if ((i + muz) % 3 != 0) continue; + particle_burst(g, u->x + 6.0f, u->y - 4.0f, 6, 2, 255, 230, 120); + particle_burst(g, u->x + 4.0f, u->y - 2.0f, 4, 1, 255, 160, 50); + muz++; + } + } /* Mid-flight shells/missiles/lasers: after ~1s sim most live * bullets have already impacted. Reseed between nearby opposing * pairs so the PNG shows KKND2-like tracers across the clash. */ { int spawned = 0; - const float pair_r2 = 170.0f * 170.0f; - for (int i = 0; i < g->unit_count && spawned < 56; i++) { + const float pair_r2 = 180.0f * 180.0f; + for (int i = 0; i < g->unit_count && spawned < 72; i++) { Unit *a = &g->units[i]; if (a->dead) continue; float ax = a->x - clash_x, ay = a->y - clash_y; if (ax * ax + ay * ay > r2) continue; int shots = 0; - for (int j = 0; j < g->unit_count && spawned < 56 && shots < 2; j++) { + for (int j = 0; j < g->unit_count && spawned < 72 && shots < 3; j++) { Unit *b = &g->units[j]; if (b->dead || b->faction == a->faction) continue; float dx = b->x - a->x, dy = b->y - a->y; float d2 = dx * dx + dy * dy; - if (d2 > pair_r2 || d2 < 24.0f * 24.0f) continue; + if (d2 > pair_r2 || d2 < 20.0f * 20.0f) continue; int kind = (spawned % 4 == 0) ? 3 : (spawned % 4 == 1) ? 1 : (spawned % 4 == 2) ? 2 : 0; - float t = 0.22f + 0.55f * ((spawned * 37) % 100) / 100.0f; + float t = 0.18f + 0.60f * ((spawned * 37) % 100) / 100.0f; float sx = a->x + dx * t; float sy = a->y + dy * t; bullet_spawn(g, sx, sy, b->id, -1, 1.0f, a->faction, kind); @@ -189,7 +223,7 @@ int main(int argc, char **argv) { } } /* turret / base guns → nearest enemy in clash */ - for (int i = 0; i < g->bld_count && spawned < 64; i++) { + for (int i = 0; i < g->bld_count && spawned < 88; i++) { Building *bl = &g->buildings[i]; if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE)) continue; @@ -208,9 +242,10 @@ int main(int argc, char **argv) { if (best < 0) continue; Unit *tgt = &g->units[best]; float dx = tgt->x - bx, dy = tgt->y - by; - float t = 0.30f + 0.40f * ((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, 2.0f, bl->faction, (spawned & 1) ? 1 : 2); + particle_burst(g, bx, by - 4.0f, 5, 2, 255, 220, 100); spawned++; } } @@ -227,39 +262,41 @@ int main(int argc, char **argv) { sx2 += u->x; sy2 += u->y; n2++; } if (n2 > 0) { c2x = sx2 / n2; c2y = sy2 / n2; } - particle_burst(g, c2x, c2y, 40, 1, 255, 135, 35); - particle_burst(g, c2x + 18, c2y - 10, 28, 0, 115, 105, 90); - particle_burst(g, c2x - 14, c2y + 12, 30, 2, 255, 220, 85); - particle_burst(g, c2x + 26, c2y + 8, 22, 1, 255, 95, 28); - particle_burst(g, c2x - 6, c2y - 20, 24, 3, 90, 78, 62); - particle_burst(g, c2x + 10, c2y + 24, 20, 1, 255, 160, 50); + particle_burst(g, c2x, c2y, 52, 1, 255, 135, 35); + particle_burst(g, c2x + 18, c2y - 10, 36, 0, 115, 105, 90); + particle_burst(g, c2x - 14, c2y + 12, 40, 2, 255, 220, 85); + particle_burst(g, c2x + 26, c2y + 8, 30, 1, 255, 95, 28); + particle_burst(g, c2x - 6, c2y - 20, 32, 3, 90, 78, 62); + particle_burst(g, c2x + 10, c2y + 24, 28, 1, 255, 160, 50); + particle_burst(g, c2x - 28, c2y + 8, 24, 0, 100, 95, 80); + particle_burst(g, c2x + 32, c2y - 16, 22, 1, 255, 120, 40); { int spawned2 = 0; - const float pair_r2 = 150.0f * 150.0f; + const float pair_r2 = 160.0f * 160.0f; const float r2b = 140.0f * 140.0f; - for (int i = 0; i < g->unit_count && spawned2 < 36; i++) { + for (int i = 0; i < g->unit_count && spawned2 < 48; i++) { Unit *a = &g->units[i]; if (a->dead) continue; float ax = a->x - clash2_x, ay = a->y - clash2_y; if (ax * ax + ay * ay > r2b) continue; int shots = 0; - for (int j = 0; j < g->unit_count && spawned2 < 36 && shots < 2; j++) { + for (int j = 0; j < g->unit_count && spawned2 < 48 && shots < 3; j++) { Unit *b = &g->units[j]; if (b->dead || b->faction == a->faction) continue; float dx = b->x - a->x, dy = b->y - a->y; float d2 = dx * dx + dy * dy; - if (d2 > pair_r2 || d2 < 20.0f * 20.0f) continue; + if (d2 > pair_r2 || d2 < 18.0f * 18.0f) continue; int kind = (spawned2 % 4 == 0) ? 3 : (spawned2 % 4 == 1) ? 1 : (spawned2 % 4 == 2) ? 2 : 0; - float t = 0.20f + 0.55f * ((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, b->id, -1, 1.0f, a->faction, kind); spawned2++; shots++; } } - for (int i = 0; i < g->bld_count && spawned2 < 44; i++) { + for (int i = 0; i < g->bld_count && spawned2 < 56; i++) { Building *bl = &g->buildings[i]; if (bl->dead || (bl->type != BT_TURRET && bl->type != BT_BASE)) continue; @@ -278,7 +315,7 @@ int main(int argc, char **argv) { if (best < 0) continue; Unit *tgt = &g->units[best]; float dx = tgt->x - bx, dy = tgt->y - by; - float t = 0.28f + 0.42f * ((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, 2.0f, bl->faction, (spawned2 & 1) ? 1 : 2); spawned2++; diff --git a/engine-c/src/projectiles.c b/engine-c/src/projectiles.c index 4d688f2..bf41d54 100644 --- a/engine-c/src/projectiles.c +++ b/engine-c/src/projectiles.c @@ -57,12 +57,13 @@ 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); } - /* denser impact: fire/laser core + smoke ring */ + /* denser impact: fire/laser core + smoke ring + debris */ 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); + particle_burst(g, b->tx, b->ty, 12, hit_kind, 255, 200, 80); + particle_burst(g, b->tx, b->ty, 8, 0, 90, 80, 60); + particle_burst(g, b->tx, b->ty, 5, 1, 255, 120, 35); if (b->kind == 1 || b->kind == 3) - particle_burst(g, b->tx, b->ty, 3, 3, 255, 140, 40); + particle_burst(g, b->tx, b->ty, 6, 3, 255, 140, 40); /* swap-remove */ g->bullets[i] = g->bullets[g->bullet_count - 1]; g->bullet_count--; @@ -73,9 +74,14 @@ void bullet_update(Game *g, float dt) { /* 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) + 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); + particle_spawn(g, b->x - dx / d * 6.0f, b->y - dy / d * 6.0f, + 1, 255, 140, 40); + } + if (b->kind == 2) + particle_spawn(g, b->x, b->y, 2, 180, 120, 255); } } @@ -89,9 +95,9 @@ void particle_spawn(Game *g, float x, float y, int kind, uint8_t cr, uint8_t cg, p->vx = (float)cosf(ang) * sp; p->vy = (float)sinf(ang) * sp; /* lingering columns: smoke / fire / spark|debris (KKND2 clash plate) */ - if (kind == 0) p->life = p->max_life = 2.8f; - else if (kind == 1) p->life = p->max_life = 1.8f; - else p->life = p->max_life = 1.0f; + if (kind == 0) p->life = p->max_life = 3.6f; + else if (kind == 1) p->life = p->max_life = 2.6f; + else p->life = p->max_life = 1.4f; p->r = cr; p->g = cg; p->b = cb; p->kind = kind; } @@ -107,8 +113,9 @@ void particle_update(Game *g, float dt) { p->x += p->vx * dt; p->y += p->vy * dt; p->vx *= 0.92f; p->vy *= 0.92f; - if (p->kind == 1) p->vy += 40 * dt; /* fire rises/slows */ - if (p->kind == 0) p->vy -= 18 * dt; /* smoke drifts up */ + /* screen-y grows downward: subtract to rise (fire + smoke columns) */ + if (p->kind == 1) p->vy -= 42 * dt; + if (p->kind == 0) p->vy -= 22 * dt; if (p->life <= 0) { g->particles[i] = g->particles[g->particle_count - 1]; g->particle_count--; diff --git a/engine-c/src/render.c b/engine-c/src/render.c index 5a989d0..53665fd 100644 --- a/engine-c/src/render.c +++ b/engine-c/src/render.c @@ -398,8 +398,8 @@ void render_bullets(Game *g) { 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; + int len = (b->kind == 2) ? 32 : (b->kind == 3) ? 24 + : (b->kind == 1) ? 20 : 14; 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); @@ -421,7 +421,7 @@ void render_bullets(Game *g) { } } } - int rad = (b->kind == 1 || b->kind == 3) ? 6 : 4; + int rad = (b->kind == 1 || b->kind == 3) ? 8 : 5; 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 }); @@ -467,9 +467,14 @@ void render_particles(Game *g) { Uint8 a = (Uint8)(255 * life_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; + int sz = (p->kind == 1) ? 9 : (p->kind == 3) ? 5 : (p->kind == 0) ? 7 : 4; if (p->kind == 0 || p->kind == 1) { + draw_particle_disc(g->ren, sx, sy, sz + 2, (Uint8)(rr / 2), (Uint8)(gg / 2), + (Uint8)(bb / 2), (Uint8)(a * 0.55f)); draw_particle_disc(g->ren, sx, sy, sz + 1, rr, gg, bb, a); + if (p->kind == 1 && life_a > 0.45f) + draw_particle_disc(g->ren, sx, sy - 1, sz / 2 + 1, 255, + (Uint8)(220 * life_a), 80, 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 }); diff --git a/engine-c/src/snapshot.c b/engine-c/src/snapshot.c index 4680e3c..f5efeae 100644 --- a/engine-c/src/snapshot.c +++ b/engine-c/src/snapshot.c @@ -792,8 +792,8 @@ void snapshot_capture(Game *g, const char *path) { float d = sqrtf(dx * dx + dy * dy); if (d > 1.0f) { float ux = dx / d, uy = dy / d; - int len = (p->kind == 2) ? 24 : (p->kind == 3) ? 18 - : (p->kind == 1) ? 14 : 10; + int len = (p->kind == 2) ? 32 : (p->kind == 3) ? 24 + : (p->kind == 1) ? 20 : 14; 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); @@ -809,13 +809,13 @@ void snapshot_capture(Game *g, const char *path) { } } } - int rad = (p->kind == 1 || p->kind == 3) ? 6 : 4; + int rad = (p->kind == 1 || p->kind == 3) ? 8 : 5; disc(sx, sy, rad + 1, 255, 160, 40); disc(sx, sy, rad, 255, 240, 140); px(sx + 1, sy, 255, 255, 220); if (p->kind == 3) { - disc(sx - 3, sy + 1, 3, 255, 70, 30); - disc(sx + 2, sy - 2, 2, 255, 200, 80); + disc(sx - 3, sy + 1, 4, 255, 70, 30); + disc(sx + 2, sy - 2, 3, 255, 200, 80); } else if (p->kind == 2) { disc(sx, sy, rad + 1, 120, 200, 255); disc(sx, sy, rad - 1, 240, 220, 255); @@ -827,13 +827,19 @@ void snapshot_capture(Game *g, const char *path) { Particle *p = &g->particles[i]; if (p->life <= 0) continue; int sx = (int)(p->x - camx), sy = (int)(p->y - camy); - if (sx < -8 || sy < -8 || sx > g_sw + 8 || sy > g_sh + 8) continue; + if (sx < -12 || sy < -12 || sx > g_sw + 12 || sy > g_sh + 12) continue; float a = p->life / (p->max_life > 0 ? p->max_life : 1.0f); - if (a < 0.12f) continue; - int sz = (p->kind == 1) ? 5 : (p->kind == 3) ? 3 : (p->kind == 0) ? 4 : 3; + if (a < 0.10f) continue; + int sz = (p->kind == 1) ? 9 : (p->kind == 3) ? 5 : (p->kind == 0) ? 7 : 4; uint8_t r = (uint8_t)(p->r * a), gv = (uint8_t)(p->g * a), b = (uint8_t)(p->b * a); - if (p->kind == 0 || p->kind == 1) disc(sx, sy, sz + 1, r, gv, b); - else fill_rect(sx - sz/2, sy - sz/2, sz + 1, sz + 1, r, gv, b); + if (p->kind == 0 || p->kind == 1) { + disc(sx, sy, sz + 2, r / 2, gv / 2, b / 2); + disc(sx, sy, sz + 1, r, gv, b); + if (p->kind == 1 && a > 0.45f) + disc(sx, sy - 1, sz / 2 + 1, 255, (uint8_t)(220 * a), 80); + } else { + fill_rect(sx - sz/2, sy - sz/2, sz + 1, sz + 1, r, gv, b); + } } /* KKND2-like chrome: top resource bar + corner minimap + bottom command strip. * Procedural bars (no font) so the shot still reads as classic RTS UI. */