#include "kknd.h" /* ========================================================================= * sprites.c - Procedural 2D sprite/terrain generation. * Recreates the chunky post-apocalyptic KKND look without using any * original game assets. All art is drawn at runtime into SDL textures. * ========================================================================= */ #define SURF_FMT SDL_PIXELFORMAT_RGBA32 static SDL_Surface *new_surf(int w, int h) { return SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SURF_FMT); } static void px(SDL_Surface *s, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (x < 0 || y < 0 || x >= s->w || y >= s->h) return; Uint32 *p = (Uint32 *)((Uint8 *)s->pixels + y * s->pitch); Uint32 pix = (a << 24) | (b << 16) | (g << 8) | r; p[x] = pix; } static void fill_rect(SDL_Surface *s, int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { for (int j = y; j < y + h; j++) for (int i = x; i < x + w; i++) px(s, i, j, r, g, b, a); } static void disc(SDL_Surface *s, int cx, int cy, int rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { for (int j = -rad; j <= rad; j++) for (int i = -rad; i <= rad; i++) if (i*i + j*j <= rad*rad) px(s, cx + i, cy + j, r, g, b, a); } static void ring(SDL_Surface *s, int cx, int cy, int rad, int t, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { for (int j = -rad; j <= rad; j++) for (int i = -rad; i <= rad; i++) { int d = i*i + j*j; if (d <= rad*rad && d >= (rad - t)*(rad - t)) px(s, cx + i, cy + j, r, g, b, a); } } static void line(SDL_Surface *s, int x0, int y0, int x1, int y1, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { int dx = abs(x1 - x0), dy = -abs(y1 - y0); int sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1; int err = dx + dy; for (;;) { px(s, x0, y0, r, g, b, a); if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 >= dy) { err += dy; x0 += sx; } if (e2 <= dx) { err += dx; y0 += sy; } } } static SDL_Color darken(SDL_Color c, float f) { return (SDL_Color){ (Uint8)(c.r*f), (Uint8)(c.g*f), (Uint8)(c.b*f), c.a }; } static SDL_Color lighten(SDL_Color c, float f) { return (SDL_Color){ (Uint8)(c.r + (255-c.r)*f), (Uint8)(c.g + (255-c.g)*f), (Uint8)(c.b + (255-c.b)*f), c.a }; } /* ---------------- unit sprites ---------------- */ /* Near-black outline first (KKND2 silhouette), then bright faction fill. * Geometry mirrors snapshot.c draw_unit atlas coords (center 15,15); SDL rotates live. */ SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f) { int S = 30; SDL_Surface *s = new_surf(S, S); SDL_Color base = faction_color(f); SDL_Color dark = darken(base, 0.55f); SDL_Color lite = lighten(base, 0.45f); /* 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: /* boots/knees → torso/belt/pouches → pads → pack → helmet/visor/antenna → rifle+bayonet */ disc(s, 11, 23, 3, ok, og, ob, 255); disc(s, 19, 23, 3, ok, og, ob, 255); disc(s, 11, 23, 2, base.r, base.g, base.b, 255); disc(s, 19, 23, 2, base.r, base.g, base.b, 255); fill_rect(s, 10, 24, 3, 2, 40, 36, 30, 255); /* boot L */ fill_rect(s, 18, 24, 3, 2, 40, 36, 30, 255); /* boot R */ fill_rect(s, 10, 20, 3, 2, 55, 50, 42, 255); /* knee pad L */ fill_rect(s, 18, 20, 3, 2, 55, 50, 42, 255); /* knee pad R */ disc(s, 15, 15, 9, ok, og, ob, 255); disc(s, 15, 15, 7, base.r, base.g, base.b, 255); fill_rect(s, 11, 17, 8, 2, 45, 42, 36, 255); /* belt */ fill_rect(s, 12, 16, 2, 2, 60, 55, 45, 255); /* pouch L */ fill_rect(s, 16, 16, 2, 2, 60, 55, 45, 255); /* pouch R */ fill_rect(s, 9, 12, 3, 3, 50, 46, 40, 255); /* shoulder pad L */ fill_rect(s, 19, 12, 3, 3, 50, 46, 40, 255); /* shoulder pad R */ disc(s, 10, 16, 3, ok, og, ob, 255); /* backpack */ disc(s, 10, 16, 2, 50, 46, 40, 255); fill_rect(s, 8, 15, 2, 3, 40, 38, 32, 255); /* pack strap */ disc(s, 15, 8, 5, ok, og, ob, 255); disc(s, 15, 8, 4, lite.r, lite.g, lite.b, 255); fill_rect(s, 12, 7, 5, 2, 30, 40, 50, 255); /* visor */ line(s, 14, 6, 14, 1, 50, 46, 40, 255); /* antenna */ line(s, 15, 6, 15, 0, lite.r, lite.g, lite.b, 255); line(s, 16, 14, 16, 0, 40, 36, 30, 255); line(s, 17, 14, 17, 1, 55, 50, 40, 255); line(s, 18, 14, 18, 2, 45, 40, 34, 255); disc(s, 17, 1, 2, 60, 55, 45, 255); /* muzzle tip */ line(s, 17, 2, 20, 0, 70, 65, 55, 255); /* bayonet */ line(s, 18, 2, 21, 1, 55, 50, 40, 255); break; case UT_BUGGY: /* fenders + bumper + cabin + roll bar + spare + exhaust + lights + MG */ fill_rect(s, 2, 6, 26, 18, ok, og, ob, 255); fill_rect(s, 4, 8, 22, 14, base.r, base.g, base.b, 255); fill_rect(s, 3, 6, 6, 3, 45, 42, 36, 255); /* front fender L */ fill_rect(s, 21, 6, 6, 3, 45, 42, 36, 255); /* front fender R */ fill_rect(s, 3, 22, 6, 3, 45, 42, 36, 255); /* rear fender L */ fill_rect(s, 21, 22, 6, 3, 45, 42, 36, 255); /* rear fender R */ fill_rect(s, 1, 12, 3, 6, 55, 50, 40, 255); /* bumper */ fill_rect(s, 2, 13, 1, 4, 90, 80, 55, 255); fill_rect(s, 6, 9, 10, 6, lite.r, lite.g, lite.b, 255); fill_rect(s, 7, 8, 8, 3, 35, 55, 75, 255); /* windshield */ line(s, 8, 10, 8, 4, lite.r, lite.g, lite.b, 255); /* roll bar L */ line(s, 9, 10, 9, 4, 50, 46, 40, 255); line(s, 18, 10, 18, 4, lite.r, lite.g, lite.b, 255); /* roll bar R */ line(s, 17, 10, 17, 4, 50, 46, 40, 255); line(s, 8, 4, 18, 4, lite.r, lite.g, lite.b, 255); line(s, 8, 5, 18, 5, 50, 46, 40, 255); disc(s, 25, 16, 3, ok, og, ob, 255); /* spare tyre */ disc(s, 25, 16, 2, tr, tg, tb, 255); disc(s, 25, 16, 1, 90, 85, 75, 255); fill_rect(s, 26, 20, 3, 2, 40, 38, 32, 255); /* exhaust */ fill_rect(s, 27, 19, 2, 1, 70, 65, 55, 255); disc(s, 4, 11, 2, 220, 200, 80, 255); /* headlight L */ disc(s, 4, 19, 2, 220, 200, 80, 255); /* headlight R */ disc(s, 8, 7, 5, ok, og, ob, 255); disc(s, 22, 7, 5, ok, og, ob, 255); disc(s, 8, 23, 5, ok, og, ob, 255); disc(s, 22, 23, 5, ok, og, ob, 255); 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); disc(s, 8, 7, 2, 90, 85, 75, 255); disc(s, 22, 7, 2, 90, 85, 75, 255); disc(s, 8, 23, 2, 90, 85, 75, 255); disc(s, 22, 23, 2, 90, 85, 75, 255); line(s, 15, 14, 29, 14, 40, 36, 30, 255); line(s, 15, 15, 29, 15, 50, 45, 35, 255); line(s, 15, 16, 29, 16, 40, 36, 30, 255); disc(s, 29, 15, 2, 60, 55, 45, 255); break; case UT_TANK: /* skirts + road wheels + glacis + mantlet + coax + exhaust + antenna + barrel */ fill_rect(s, 2, 8, 5, 12, ok, og, ob, 255); fill_rect(s, 23, 8, 5, 12, ok, og, ob, 255); fill_rect(s, 3, 10, 3, 10, tr, tg, tb, 255); fill_rect(s, 24, 10, 3, 10, tr, tg, tb, 255); fill_rect(s, 2, 9, 5, 2, 50, 46, 40, 255); /* side skirt L top */ fill_rect(s, 23, 9, 5, 2, 50, 46, 40, 255); /* side skirt R top */ fill_rect(s, 2, 18, 5, 2, 50, 46, 40, 255); /* side skirt L bot */ fill_rect(s, 23, 18, 5, 2, 50, 46, 40, 255); disc(s, 5, 11, 2, 40, 38, 32, 255); /* sprocket */ disc(s, 5, 15, 2, 40, 38, 32, 255); /* road wheel */ disc(s, 5, 20, 2, 40, 38, 32, 255); disc(s, 26, 11, 2, 40, 38, 32, 255); disc(s, 26, 15, 2, 40, 38, 32, 255); disc(s, 26, 20, 2, 40, 38, 32, 255); fill_rect(s, 4, 10, 22, 10, ok, og, ob, 255); fill_rect(s, 6, 11, 18, 8, base.r, base.g, base.b, 255); fill_rect(s, 5, 10, 8, 3, 55, 50, 42, 255); /* glacis plate */ fill_rect(s, 6, 11, 6, 2, lite.r, lite.g, lite.b, 255); disc(s, 15, 15, 7, ok, og, ob, 255); disc(s, 15, 15, 6, lite.r, lite.g, lite.b, 255); fill_rect(s, 18, 13, 4, 5, 45, 42, 36, 255); /* mantlet */ fill_rect(s, 19, 14, 2, 3, 60, 55, 45, 255); disc(s, 15, 12, 2, 40, 38, 32, 255); /* hatch */ line(s, 14, 12, 14, 2, 50, 46, 40, 255); /* antenna */ line(s, 15, 12, 15, 1, lite.r, lite.g, lite.b, 255); fill_rect(s, 7, 20, 3, 2, 40, 38, 32, 255); /* exhaust */ fill_rect(s, 8, 19, 1, 1, 70, 65, 55, 255); line(s, 15, 13, 29, 13, 45, 40, 32, 255); line(s, 15, 14, 29, 14, 50, 45, 35, 255); line(s, 15, 15, 29, 15, 55, 50, 40, 255); line(s, 15, 16, 29, 16, 50, 45, 35, 255); line(s, 15, 17, 28, 17, 45, 40, 32, 255); line(s, 18, 12, 27, 11, 40, 36, 30, 255); /* coax MG */ line(s, 18, 12, 27, 12, 50, 45, 35, 255); disc(s, 29, 15, 2, 60, 55, 45, 255); disc(s, 8, 23, 4, tr, tg, tb, 255); disc(s, 22, 23, 4, tr, tg, tb, 255); break; case UT_ARTILLERY: /* howitzer: outriggers + shield + ammo rack + elevated tube + brake */ fill_rect(s, 1, 18, 8, 3, ok, og, ob, 255); /* left outrigger */ fill_rect(s, 21, 18, 8, 3, ok, og, ob, 255); /* right outrigger */ fill_rect(s, 0, 19, 4, 2, 40, 38, 32, 255); fill_rect(s, 26, 19, 4, 2, 40, 38, 32, 255); fill_rect(s, 4, 11, 22, 10, ok, og, ob, 255); fill_rect(s, 6, 13, 18, 7, base.r, base.g, base.b, 255); fill_rect(s, 22, 12, 6, 8, 55, 50, 40, 255); /* ammo rack */ line(s, 23, 13, 27, 13, 90, 80, 55, 255); line(s, 23, 15, 27, 15, 90, 80, 55, 255); line(s, 23, 17, 27, 17, 90, 80, 55, 255); fill_rect(s, 10, 8, 10, 5, ok, og, ob, 255); /* gun shield */ fill_rect(s, 11, 9, 8, 3, lite.r, lite.g, lite.b, 255); disc(s, 15, 14, 4, ok, og, ob, 255); disc(s, 15, 14, 3, lite.r, lite.g, lite.b, 255); line(s, 15, 11, 3, 1, 45, 40, 32, 255); /* elevated tube */ line(s, 15, 12, 3, 2, 50, 45, 35, 255); line(s, 15, 13, 2, 1, 55, 50, 40, 255); line(s, 15, 14, 3, 3, 50, 45, 35, 255); line(s, 15, 15, 4, 4, 45, 40, 32, 255); fill_rect(s, 1, 0, 5, 4, 60, 55, 45, 255); /* muzzle brake */ fill_rect(s, 2, 1, 3, 2, 30, 28, 24, 255); disc(s, 8, 23, 4, tr, tg, tb, 255); disc(s, 22, 23, 4, tr, tg, tb, 255); break; case UT_HARVESTER: /* cabin + boom/scoop + conveyor + hopper panels + exhaust/lights + hubbed wheels */ 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, 3, 8, 8, 8, lite.r, lite.g, lite.b, 255); fill_rect(s, 4, 8, 6, 3, 35, 55, 75, 255); /* glass */ fill_rect(s, 5, 12, 4, 2, 45, 42, 36, 255); /* cabin panel */ line(s, 2, 14, 8, 11, 50, 46, 40, 255); /* boom arm */ line(s, 2, 15, 8, 12, 70, 65, 55, 255); fill_rect(s, 0, 14, 5, 5, 90, 80, 55, 255); /* scoop lip */ fill_rect(s, 1, 15, 3, 3, 70, 60, 40, 255); fill_rect(s, 11, 10, 14, 10, 55, 48, 32, 255); /* hopper bed */ fill_rect(s, 11, 10, 8, 10, 200, 170, 90, 255); line(s, 14, 10, 24, 10, 160, 130, 70, 255); /* conveyor ribs */ line(s, 14, 13, 24, 13, 160, 130, 70, 255); line(s, 14, 16, 24, 16, 160, 130, 70, 255); fill_rect(s, 12, 9, 12, 1, 45, 40, 32, 255); /* hopper rim */ fill_rect(s, 24, 11, 3, 8, 50, 46, 40, 255); /* side panel */ fill_rect(s, 25, 12, 1, 6, 70, 65, 55, 255); fill_rect(s, 20, 18, 4, 2, 40, 38, 32, 255); /* exhaust */ fill_rect(s, 21, 17, 2, 1, 70, 65, 55, 255); disc(s, 4, 10, 2, 220, 200, 80, 255); /* cabin light */ disc(s, 2, 16, 1, 220, 180, 60, 255); /* scoop lamp */ disc(s, 8, 23, 5, ok, og, ob, 255); disc(s, 22, 23, 5, ok, og, ob, 255); disc(s, 8, 23, 4, tr, tg, tb, 255); disc(s, 22, 23, 4, tr, tg, tb, 255); disc(s, 8, 23, 2, 90, 85, 75, 255); disc(s, 22, 23, 2, 90, 85, 75, 255); line(s, 5, 7, 5, 2, lite.r, lite.g, lite.b, 255); line(s, 6, 7, 6, 3, 50, 46, 40, 255); break; case UT_JEEP: /* bumper + grill + seats + jerry cans + spare + lights + antenna + cage + MG */ 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); fill_rect(s, 3, 12, 3, 6, 55, 50, 40, 255); /* bumper */ fill_rect(s, 4, 13, 1, 4, 90, 80, 55, 255); fill_rect(s, 5, 13, 3, 4, 40, 38, 32, 255); /* grill */ line(s, 5, 14, 7, 14, 70, 65, 55, 255); line(s, 5, 16, 7, 16, 70, 65, 55, 255); fill_rect(s, 7, 10, 8, 5, lite.r, lite.g, lite.b, 255); fill_rect(s, 8, 12, 3, 3, 45, 40, 35, 255); /* seat L */ fill_rect(s, 14, 12, 3, 3, 45, 40, 35, 255); /* seat R */ fill_rect(s, 22, 11, 3, 4, 70, 90, 50, 255); /* jerry can */ fill_rect(s, 23, 12, 1, 2, 90, 110, 60, 255); disc(s, 24, 18, 3, ok, og, ob, 255); /* spare */ disc(s, 24, 18, 2, tr, tg, tb, 255); disc(s, 24, 18, 1, 90, 85, 75, 255); disc(s, 5, 11, 2, 220, 200, 80, 255); /* light L */ disc(s, 5, 19, 2, 220, 200, 80, 255); /* light R */ line(s, 16, 10, 16, 1, 50, 46, 40, 255); /* antenna */ line(s, 17, 10, 17, 2, lite.r, lite.g, lite.b, 255); disc(s, 9, 22, 5, ok, og, ob, 255); disc(s, 21, 22, 5, ok, og, ob, 255); disc(s, 9, 22, 4, tr, tg, tb, 255); disc(s, 21, 22, 4, tr, tg, tb, 255); disc(s, 9, 22, 2, 90, 85, 75, 255); disc(s, 21, 22, 2, 90, 85, 75, 255); line(s, 11, 11, 11, 3, lite.r, lite.g, lite.b, 255); line(s, 12, 11, 12, 3, 50, 46, 40, 255); line(s, 19, 11, 19, 3, lite.r, lite.g, lite.b, 255); line(s, 18, 11, 18, 3, 50, 46, 40, 255); line(s, 11, 3, 19, 3, lite.r, lite.g, lite.b, 255); line(s, 11, 4, 19, 4, 50, 46, 40, 255); line(s, 15, 14, 28, 14, 40, 36, 30, 255); line(s, 15, 15, 28, 15, 50, 45, 35, 255); line(s, 15, 16, 28, 16, 40, 36, 30, 255); disc(s, 28, 15, 2, 60, 55, 45, 255); break; case UT_MUTANT: /* plates + eyes/jaw + spikes + dual claws + tail */ disc(s, 15, 17, 11, ok, og, ob, 255); disc(s, 15, 15, 9, base.r, base.g, base.b, 255); fill_rect(s, 10, 14, 4, 3, 45, 40, 35, 255); /* chest plate L */ fill_rect(s, 17, 14, 4, 3, 45, 40, 35, 255); /* chest plate R */ disc(s, 15, 9, 5, ok, og, ob, 255); disc(s, 15, 9, 4, lite.r, lite.g, lite.b, 255); fill_rect(s, 11, 7, 7, 2, 30, 40, 50, 255); /* brow */ disc(s, 12, 9, 1, 200, 60, 40, 255); /* eye L */ disc(s, 18, 9, 1, 200, 60, 40, 255); /* eye R */ fill_rect(s, 13, 11, 5, 2, 40, 30, 28, 255); /* jaw */ line(s, 14, 12, 14, 13, 70, 55, 45, 255); /* fang */ line(s, 16, 12, 16, 13, 70, 55, 45, 255); line(s, 12, 6, 9, 0, 40, 36, 30, 255); line(s, 13, 6, 10, 1, 55, 50, 40, 255); line(s, 15, 5, 15, 0, 40, 36, 30, 255); line(s, 16, 5, 16, 0, 55, 50, 40, 255); line(s, 18, 6, 21, 0, 40, 36, 30, 255); line(s, 17, 6, 20, 1, 55, 50, 40, 255); line(s, 14, 4, 12, 0, 50, 45, 35, 255); /* extra spike */ line(s, 15, 16, 26, 8, 40, 36, 30, 255); line(s, 15, 17, 26, 9, 50, 45, 35, 255); line(s, 24, 9, 29, 5, 55, 50, 40, 255); line(s, 24, 9, 29, 11, 55, 50, 40, 255); line(s, 24, 10, 28, 8, 70, 60, 45, 255); disc(s, 28, 8, 2, 70, 60, 45, 255); /* claw tip R */ line(s, 12, 16, 4, 10, 40, 36, 30, 255); /* second claw L */ line(s, 12, 17, 4, 11, 50, 45, 35, 255); line(s, 5, 10, 1, 7, 55, 50, 40, 255); line(s, 5, 11, 1, 13, 55, 50, 40, 255); disc(s, 2, 10, 2, 70, 60, 45, 255); line(s, 15, 20, 15, 27, 45, 40, 35, 255); /* tail */ line(s, 16, 20, 18, 28, 55, 50, 40, 255); disc(s, 17, 27, 2, 60, 50, 40, 255); disc(s, 10, 22, 4, ok, og, ob, 255); disc(s, 20, 22, 4, ok, og, ob, 255); disc(s, 10, 22, 3, base.r, base.g, base.b, 255); disc(s, 20, 22, 3, base.r, base.g, base.b, 255); break; case UT_BEAST: /* carapace plates/spines + eye + vents + tusks + hubbed multi-wheel */ 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); fill_rect(s, 9, 10, 12, 3, lite.r, lite.g, lite.b, 255); /* carapace ridge */ fill_rect(s, 8, 13, 5, 3, 50, 46, 40, 255); /* plate L */ fill_rect(s, 17, 13, 5, 3, 50, 46, 40, 255); /* plate R */ line(s, 10, 9, 10, 5, 40, 36, 30, 255); /* spine */ line(s, 13, 9, 13, 4, 55, 50, 40, 255); line(s, 16, 9, 16, 4, 55, 50, 40, 255); line(s, 19, 9, 19, 5, 40, 36, 30, 255); fill_rect(s, 11, 16, 2, 2, 30, 28, 24, 255); /* vent L */ fill_rect(s, 17, 16, 2, 2, 30, 28, 24, 255); /* vent R */ disc(s, 9, 21, 4, ok, og, ob, 255); disc(s, 14, 22, 4, ok, og, ob, 255); disc(s, 19, 21, 4, ok, og, ob, 255); disc(s, 23, 22, 4, ok, og, ob, 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, 9, 21, 1, 90, 85, 75, 255); /* hub */ disc(s, 14, 22, 1, 90, 85, 75, 255); disc(s, 19, 21, 1, 90, 85, 75, 255); disc(s, 23, 22, 1, 90, 85, 75, 255); disc(s, 24, 12, 6, ok, og, ob, 255); disc(s, 24, 12, 5, lite.r, lite.g, lite.b, 255); /* snout */ disc(s, 26, 11, 1, 200, 80, 40, 255); /* eye */ line(s, 26, 10, 29, 8, 40, 36, 30, 255); /* tusk */ line(s, 26, 14, 29, 16, 40, 36, 30, 255); line(s, 26, 12, 29, 12, 55, 50, 40, 255); disc(s, 29, 12, 2, 60, 55, 45, 255); break; case UT_RAVAGER: /* prow + ammo boxes + triple gatling + exhaust (vs tank cupola) */ fill_rect(s, 0, 7, 5, 14, ok, og, ob, 255); fill_rect(s, 25, 7, 5, 14, ok, og, ob, 255); fill_rect(s, 1, 10, 3, 10, tr, tg, tb, 255); fill_rect(s, 26, 10, 3, 10, tr, tg, tb, 255); disc(s, 3, 12, 2, 40, 38, 32, 255); disc(s, 3, 20, 2, 40, 38, 32, 255); disc(s, 28, 12, 2, 40, 38, 32, 255); disc(s, 28, 20, 2, 40, 38, 32, 255); fill_rect(s, 3, 9, 24, 12, ok, og, ob, 255); fill_rect(s, 5, 10, 20, 10, base.r, base.g, base.b, 255); fill_rect(s, 24, 8, 6, 6, 55, 48, 38, 255); /* prow plate */ line(s, 25, 9, 29, 11, 70, 60, 45, 255); line(s, 25, 13, 29, 11, 70, 60, 45, 255); fill_rect(s, 4, 8, 5, 4, 70, 55, 35, 255); /* ammo boxes */ fill_rect(s, 5, 9, 3, 2, 200, 160, 60, 255); fill_rect(s, 10, 8, 5, 4, 70, 55, 35, 255); fill_rect(s, 11, 9, 3, 2, 200, 160, 60, 255); fill_rect(s, 12, 12, 10, 8, ok, og, ob, 255); /* gatling block */ fill_rect(s, 13, 13, 8, 6, lite.r, lite.g, lite.b, 255); line(s, 20, 13, 29, 12, 45, 40, 32, 255); /* triple barrels */ line(s, 20, 15, 29, 15, 55, 50, 40, 255); line(s, 20, 17, 29, 18, 45, 40, 32, 255); disc(s, 29, 12, 1, 60, 55, 45, 255); disc(s, 29, 15, 2, 60, 55, 45, 255); disc(s, 29, 18, 1, 60, 55, 45, 255); fill_rect(s, 2, 14, 3, 4, 40, 38, 32, 255); /* exhaust */ disc(s, 2, 15, 2, 90, 70, 40, 255); disc(s, 2, 17, 1, 200, 100, 40, 255); disc(s, 7, 23, 5, ok, og, ob, 255); disc(s, 23, 23, 5, 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: /* dual tanks + hose + heat shield + nozzle jet */ fill_rect(s, 7, 12, 16, 12, ok, og, ob, 255); fill_rect(s, 9, 14, 12, 9, base.r, base.g, base.b, 255); disc(s, 15, 9, 5, ok, og, ob, 255); disc(s, 15, 9, 4, lite.r, lite.g, lite.b, 255); fill_rect(s, 12, 8, 5, 2, 30, 40, 50, 255); /* visor */ disc(s, 6, 13, 5, ok, og, ob, 255); /* dual fuel tanks */ disc(s, 6, 13, 4, 90, 70, 40, 255); disc(s, 6, 13, 2, 200, 120, 40, 255); disc(s, 6, 19, 5, ok, og, ob, 255); disc(s, 6, 19, 4, 90, 70, 40, 255); disc(s, 6, 19, 2, 200, 120, 40, 255); disc(s, 6, 11, 1, 60, 55, 45, 255); disc(s, 6, 17, 1, 60, 55, 45, 255); disc(s, 6, 21, 1, 60, 55, 45, 255); line(s, 10, 14, 16, 16, 50, 45, 35, 255); /* hose */ line(s, 10, 15, 16, 17, 70, 55, 35, 255); fill_rect(s, 16, 12, 5, 8, 55, 50, 40, 255); /* heat shield */ fill_rect(s, 17, 13, 3, 6, 90, 70, 40, 255); line(s, 20, 14, 28, 19, 255, 120, 40, 255); line(s, 20, 15, 28, 20, 255, 150, 50, 255); line(s, 20, 16, 27, 21, 255, 180, 60, 255); line(s, 20, 17, 26, 21, 255, 200, 80, 255); disc(s, 28, 20, 3, 255, 200, 80, 255); disc(s, 28, 20, 1, 255, 240, 160, 255); break; case UT_MISSILE: /* MLRS box launcher + rails + warning stripes + hubbed chassis */ fill_rect(s, 3, 12, 24, 12, ok, og, ob, 255); fill_rect(s, 5, 14, 20, 8, base.r, base.g, base.b, 255); fill_rect(s, 8, 4, 16, 10, ok, og, ob, 255); /* box launcher */ fill_rect(s, 9, 5, 14, 8, 50, 48, 42, 255); line(s, 10, 6, 22, 6, 90, 85, 70, 255); /* rails */ line(s, 10, 8, 22, 8, 90, 85, 70, 255); line(s, 10, 10, 22, 10, 90, 85, 70, 255); line(s, 10, 12, 22, 12, 90, 85, 70, 255); fill_rect(s, 9, 5, 3, 8, 255, 200, 40, 255); /* warning stripes */ fill_rect(s, 12, 5, 2, 8, 30, 28, 24, 255); fill_rect(s, 14, 5, 3, 8, 255, 200, 40, 255); disc(s, 23, 7, 2, 255, 120, 80, 255); /* nose tips */ disc(s, 23, 10, 2, 255, 140, 90, 255); disc(s, 9, 23, 5, ok, og, ob, 255); disc(s, 21, 23, 5, ok, og, ob, 255); disc(s, 9, 23, 4, tr, tg, tb, 255); disc(s, 21, 23, 4, tr, tg, tb, 255); disc(s, 9, 23, 2, 90, 85, 75, 255); disc(s, 21, 23, 2, 90, 85, 75, 255); line(s, 22, 8, 29, 4, 255, 80, 60, 255); line(s, 22, 10, 29, 6, 255, 100, 70, 255); disc(s, 29, 5, 2, 255, 140, 90, 255); break; case UT_MEDIC: /* white van + light bar + bold red cross + hubs (read at live scale) */ fill_rect(s, 3, 9, 24, 17, ok, og, ob, 255); fill_rect(s, 5, 11, 20, 13, 245, 245, 245, 255); fill_rect(s, 6, 12, 18, 11, 255, 255, 255, 255); fill_rect(s, 5, 8, 7, 5, 190, 215, 235, 255); /* cab glass */ fill_rect(s, 8, 3, 14, 3, 240, 40, 40, 255); /* light bar */ fill_rect(s, 10, 2, 10, 2, 255, 80, 80, 255); disc(s, 9, 24, 5, ok, og, ob, 255); disc(s, 21, 24, 5, ok, og, ob, 255); disc(s, 9, 24, 4, tr, tg, tb, 255); disc(s, 21, 24, 4, tr, tg, tb, 255); disc(s, 9, 24, 2, 90, 85, 75, 255); disc(s, 21, 24, 2, 90, 85, 75, 255); disc(s, 15, 10, 7, 230, 35, 35, 255); fill_rect(s, 13, 4, 4, 12, 255, 255, 255, 255); fill_rect(s, 9, 8, 12, 4, 255, 255, 255, 255); break; default: 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); SDL_FreeSurface(s); return tex; } /* ---------------- building sprites ---------------- */ /* Bright concrete pad + BT_* interiors (parity with snapshot draw_bld). */ SDL_Texture *sprites_make_bld(SDL_Renderer *r, BuildingType t, Faction f) { int w = bld_def(t)->w, h = bld_def(t)->h; int W = w * TILE, H = h * TILE; SDL_Surface *s = new_surf(W, H); SDL_Color base = faction_color(f); SDL_Color dark = darken(base, 0.5f); SDL_Color lite = lighten(base, 0.35f); const Uint8 ok = 22, og = 20, ob = 16; const Uint8 wall = 205, wall2 = 175; /* Bright concrete pad on arid sand */ fill_rect(s, 0, 0, W, H, ok, og, ob, 255); fill_rect(s, 2, 2, W - 4, H - 4, 210, 185, 130, 255); fill_rect(s, 4, 4, W - 8, H - 8, wall2, (Uint8)(wall2 - 18), (Uint8)(wall2 - 45), 255); fill_rect(s, 5, 5, W - 10, H - 10, wall, (Uint8)(wall - 22), (Uint8)(wall - 55), 255); fill_rect(s, 5, 5, W - 10, 6, (Uint8)(wall + 30), (Uint8)(wall + 10), (Uint8)(wall - 20), 255); switch (t) { case BT_BASE: if (f == FACTION_EVOLVED) { /* hive: lobes + membrane ridges + spine + pit eye (KKND HQ read) */ disc(s, 14, 18, 14, ok, og, ob, 255); disc(s, 14, 18, 12, base.r, base.g, base.b, 255); disc(s, W - 14, 18, 14, ok, og, ob, 255); disc(s, W - 14, 18, 12, base.r, base.g, base.b, 255); disc(s, 16, H - 16, 12, ok, og, ob, 255); disc(s, 16, H - 16, 10, base.r, base.g, base.b, 255); disc(s, W - 16, H - 16, 12, ok, og, ob, 255); disc(s, W - 16, H - 16, 10, base.r, base.g, base.b, 255); disc(s, W/2, H/2, 16, ok, og, ob, 255); disc(s, W/2, H/2, 14, base.r, base.g, base.b, 255); /* membrane ridges between lobes */ line(s, 18, 20, W/2 - 8, H/2 - 4, ok, og, ob, 255); line(s, W - 18, 20, W/2 + 8, H/2 - 4, ok, og, ob, 255); line(s, 18, H - 18, W/2 - 6, H/2 + 6, ok, og, ob, 255); line(s, W - 18, H - 18, W/2 + 6, H/2 + 6, ok, og, ob, 255); /* dorsal spine + tendrils */ fill_rect(s, W/2 - 2, 4, 4, H/2 - 8, ok, og, ob, 255); fill_rect(s, W/2 - 1, 5, 2, H/2 - 10, 90, 40, 55, 255); line(s, W/2, 8, W/2 - 10, 18, 70, 30, 45, 255); line(s, W/2, 8, W/2 + 10, 18, 70, 30, 45, 255); /* dark pit + glowing eye */ disc(s, W/2, H/2, 8, 28, 18, 22, 255); disc(s, W/2, H/2, 5, 60, 20, 30, 255); disc(s, W/2, H/2, 3, 220, 60, 90, 255); disc(s, W/2, H/2, 1, 255, 180, 200, 255); ring(s, W/2, H/2, 10, 2, 50, 25, 35, 255); /* pit rim */ /* extra blisters */ disc(s, W/2 - 10, 10, 5, lite.r, lite.g, lite.b, 255); disc(s, W/2 + 12, H - 12, 4, lite.r, lite.g, lite.b, 255); disc(s, 10, H/2 + 2, 3, lite.r, lite.g, lite.b, 255); disc(s, W - 10, H/2 - 4, 3, lite.r, lite.g, lite.b, 255); disc(s, W/2 + 8, 14, 2, 180, 70, 100, 255); } else { /* Survivors HQ: towers + walkways + dome + dish + entry (KKND HQ read) */ fill_rect(s, 4, 4, 14, 14, ok, og, ob, 255); fill_rect(s, 6, 6, 10, 10, base.r, base.g, base.b, 255); fill_rect(s, W - 18, 4, 14, 14, ok, og, ob, 255); fill_rect(s, W - 16, 6, 10, 10, base.r, base.g, base.b, 255); fill_rect(s, 4, H - 18, 14, 14, ok, og, ob, 255); fill_rect(s, 6, H - 16, 10, 10, base.r, base.g, base.b, 255); fill_rect(s, W - 18, H - 18, 14, 14, ok, og, ob, 255); fill_rect(s, W - 16, H - 16, 10, 10, base.r, base.g, base.b, 255); /* tower windows + corner lights */ fill_rect(s, 8, 8, 3, 3, 200, 230, 255, 255); fill_rect(s, W - 13, 8, 3, 3, 200, 230, 255, 255); fill_rect(s, 8, H - 13, 3, 3, 200, 230, 255, 255); fill_rect(s, W - 13, H - 13, 3, 3, 200, 230, 255, 255); disc(s, 7, 5, 2, 255, 220, 80, 255); disc(s, W - 7, 5, 2, 255, 220, 80, 255); /* courtyard body + roof band */ fill_rect(s, 14, 14, W - 28, H - 28, ok, og, ob, 255); fill_rect(s, 16, 16, W - 32, H - 32, base.r, base.g, base.b, 255); fill_rect(s, 18, 18, W - 36, 8, lite.r, lite.g, lite.b, 255); /* walkways between towers */ fill_rect(s, 16, 8, W - 32, 3, 90, 85, 70, 255); fill_rect(s, 16, H - 11, W - 32, 3, 90, 85, 70, 255); fill_rect(s, 8, 16, 3, H - 32, 90, 85, 70, 255); fill_rect(s, W - 11, 16, 3, H - 32, 90, 85, 70, 255); /* command dome + mast + radar dish */ disc(s, W/2, H/2 - 2, 10, ok, og, ob, 255); disc(s, W/2, H/2 - 2, 8, lite.r, lite.g, lite.b, 255); disc(s, W/2, H/2 - 2, 4, 255, 250, 180, 255); fill_rect(s, W/2 - 2, 2, 4, H/2 - 6, 200, 180, 60, 255); fill_rect(s, W/2 - 6, 2, 12, 3, 220, 210, 80, 255); disc(s, W/2 + 8, 8, 5, ok, og, ob, 255); /* dish */ disc(s, W/2 + 8, 8, 3, 180, 200, 220, 255); line(s, W/2 + 2, 6, W/2 + 8, 8, 160, 150, 100, 255); /* entry bunker + blast doors */ fill_rect(s, W/2 - 8, H - 14, 16, 8, ok, og, ob, 255); fill_rect(s, W/2 - 6, H - 12, 12, 6, 40, 38, 34, 255); fill_rect(s, W/2 - 4, H - 11, 3, 4, 70, 65, 55, 255); fill_rect(s, W/2 + 1, H - 11, 3, 4, 70, 65, 55, 255); /* side annex slits */ fill_rect(s, 18, H/2 + 2, 6, 4, 160, 200, 230, 255); fill_rect(s, W - 24, H/2 + 2, 6, 4, 160, 200, 230, 255); } break; case BT_WARFACTORY: /* hangar + crane mast/jib + segmented bay doors (KKND factory read) */ fill_rect(s, 4, 6, W - 8, H - 12, ok, og, ob, 255); fill_rect(s, 6, 8, W - 12, H - 16, base.r, base.g, base.b, 255); fill_rect(s, 8, 10, W - 16, 12, lite.r, lite.g, lite.b, 255); /* roof ridge */ fill_rect(s, 10, 12, 5, 5, 55, 50, 42, 255); /* ridge vents */ fill_rect(s, W/2 - 8, 12, 5, 5, 55, 50, 42, 255); fill_rect(s, W/2 + 3, 12, 5, 5, 55, 50, 42, 255); fill_rect(s, W - 15, 12, 5, 5, 55, 50, 42, 255); /* side workshop annex */ fill_rect(s, 4, H/2 - 4, 10, H/2 - 6, ok, og, ob, 255); fill_rect(s, 5, H/2 - 2, 8, H/2 - 10, dark.r, dark.g, dark.b, 255); fill_rect(s, 6, H/2, 6, 4, 90, 80, 55, 255); /* window slit */ /* overhead crane mast + jib */ fill_rect(s, W - 14, 4, 6, H - 18, ok, og, ob, 255); fill_rect(s, W - 13, 5, 4, H - 20, 95, 88, 70, 255); fill_rect(s, 14, 6, W - 28, 4, ok, og, ob, 255); fill_rect(s, 15, 7, W - 30, 2, 120, 110, 85, 255); line(s, 18, 10, 18, 22, 70, 65, 50, 255); disc(s, 18, 24, 3, 80, 75, 55, 255); /* hoist block */ /* segmented bay doors */ fill_rect(s, 12, H - 30, W - 28, 20, ok, og, ob, 255); fill_rect(s, 14, H - 28, W - 32, 16, 32, 32, 38, 255); line(s, W/2 - 6, H - 28, W/2 - 6, H - 12, 70, 68, 60, 255); line(s, W/2, H - 28, W/2, H - 12, 70, 68, 60, 255); line(s, W/2 + 6, H - 28, W/2 + 6, H - 12, 70, 68, 60, 255); line(s, 14, H - 20, W - 18, H - 20, 55, 52, 48, 255); fill_rect(s, 10, H - 10, W - 20, 5, 50, 48, 42, 255); /* ramp lip */ fill_rect(s, 12, H - 9, W - 24, 2, 90, 75, 45, 255); break; case BT_REFINERY: /* oil-field: derrick + pumpjack + twin tanks + pipes + hopper (KKND read) */ fill_rect(s, 4, H - 12, W - 8, 10, ok, og, ob, 255); /* slab */ fill_rect(s, 6, H - 10, W - 12, 6, 70, 62, 48, 255); /* latticed derrick mast */ fill_rect(s, W/2 - 3, 4, 6, H - 18, ok, og, ob, 255); fill_rect(s, W/2 - 2, 5, 4, H - 20, 95, 85, 65, 255); line(s, W/2 - 6, 10, W/2 + 6, 22, 55, 48, 38, 255); line(s, W/2 + 6, 10, W/2 - 6, 22, 55, 48, 38, 255); line(s, W/2 - 6, 24, W/2 + 6, 36, 55, 48, 38, 255); line(s, W/2 + 6, 24, W/2 - 6, 36, 55, 48, 38, 255); line(s, W/2 - 6, 38, W/2 + 6, H - 18, 55, 48, 38, 255); line(s, W/2 + 6, 38, W/2 - 6, H - 18, 55, 48, 38, 255); line(s, W/2 - 5, 8, W/2 - 5, H - 16, 40, 36, 30, 255); line(s, W/2 + 5, 8, W/2 + 5, H - 16, 40, 36, 30, 255); fill_rect(s, W/2 - 7, 2, 14, 5, ok, og, ob, 255); /* crown */ fill_rect(s, W/2 - 5, 3, 10, 3, 200, 170, 80, 255); /* beacon */ /* pumpjack horse-head (left of mast) */ fill_rect(s, 8, H/2 - 2, 4, H/2 - 8, ok, og, ob, 255); fill_rect(s, 9, H/2, 2, H/2 - 12, 90, 80, 60, 255); fill_rect(s, 6, H/2 - 6, 14, 4, ok, og, ob, 255); fill_rect(s, 7, H/2 - 5, 12, 2, 110, 95, 70, 255); disc(s, 8, H/2 - 8, 4, ok, og, ob, 255); /* horse head */ disc(s, 8, H/2 - 8, 2, 130, 110, 80, 255); line(s, 10, H/2 - 4, W/2 - 4, 16, 80, 70, 50, 255); /* bridle cable */ /* gantry arm + walkway toward hopper */ fill_rect(s, W/2 + 2, 14, W/2 - 10, 4, ok, og, ob, 255); fill_rect(s, W/2 + 3, 15, W/2 - 12, 2, 120, 100, 60, 255); fill_rect(s, W/2 + 4, 18, W/2 - 16, 2, 70, 60, 45, 255); /* catwalk */ line(s, W/2 + 4, 18, W - 12, H - 22, 90, 75, 50, 255); /* hopper / processing shed + windows */ fill_rect(s, W - 24, H - 32, 18, 18, ok, og, ob, 255); fill_rect(s, W - 22, H - 30, 14, 14, base.r, base.g, base.b, 255); fill_rect(s, W - 20, H - 28, 10, 6, 140, 110, 55, 255); fill_rect(s, W - 18, H - 26, 3, 3, 180, 220, 255, 255); /* window */ fill_rect(s, W - 13, H - 26, 3, 3, 180, 220, 255, 255); fill_rect(s, W - 16, H - 18, 4, 4, 40, 36, 30, 255); /* chute */ /* twin scrap tanks */ disc(s, 14, H - 18, 9, ok, og, ob, 255); disc(s, 14, H - 18, 7, 200, 165, 85, 255); ring(s, 14, H - 18, 4, 2, 90, 70, 45, 255); disc(s, 28, H - 16, 7, ok, og, ob, 255); disc(s, 28, H - 16, 5, 180, 150, 75, 255); ring(s, 28, H - 16, 3, 1, 80, 60, 40, 255); /* pipe runs tank→mast→hopper */ line(s, 20, H - 16, W/2 - 4, H - 12, 110, 90, 50, 255); line(s, 28, H - 14, W/2 - 2, H - 14, 100, 85, 45, 255); line(s, W/2 + 4, H - 14, W - 20, H - 20, 100, 85, 45, 255); fill_rect(s, W/2 - 1, H - 16, 3, 4, 80, 70, 50, 255); /* valve */ /* oil stain pool under slab */ disc(s, W/2 - 8, H - 6, 4, 40, 32, 20, 255); disc(s, W/2 + 6, H - 5, 3, 35, 28, 18, 255); break; case BT_POWER: /* tall cooling towers + reactor block + steam plumes (KKND plant read) */ fill_rect(s, 3, H - 10, W - 6, 8, ok, og, ob, 255); fill_rect(s, 4, H - 8, W - 8, 5, 55, 50, 42, 255); /* pad */ /* left cooling tower (taller cone) */ fill_rect(s, 4, 8, 10, H - 18, ok, og, ob, 255); fill_rect(s, 5, 9, 8, H - 20, 95, 145, 195, 255); fill_rect(s, 6, 6, 6, 5, ok, og, ob, 255); fill_rect(s, 7, 5, 4, 4, 70, 120, 170, 255); /* rim */ disc(s, 9, 4, 3, 240, 248, 255, 255); /* steam puff */ disc(s, 7, 2, 2, 220, 235, 250, 255); /* right cooling tower */ fill_rect(s, W - 14, 10, 10, H - 20, ok, og, ob, 255); fill_rect(s, W - 13, 11, 8, H - 22, 85, 135, 185, 255); fill_rect(s, W - 12, 8, 6, 4, ok, og, ob, 255); fill_rect(s, W - 11, 7, 4, 3, 65, 115, 165, 255); disc(s, W - 9, 5, 3, 235, 245, 255, 255); disc(s, W - 7, 3, 2, 210, 230, 250, 255); /* central reactor block + glow */ fill_rect(s, W/2 - 5, H/2 - 4, 10, H/2 - 2, ok, og, ob, 255); fill_rect(s, W/2 - 4, H/2 - 2, 8, H/2 - 6, 55, 95, 150, 255); disc(s, W/2, H/2 + 2, 4, 120, 200, 255, 255); disc(s, W/2, H/2 + 2, 2, 250, 252, 255, 255); line(s, W/2, 8, W/2, H - 10, 200, 230, 250, 255); line(s, 8, H/2 + 2, W - 8, H/2 + 2, 180, 220, 245, 255); break; case BT_TURRET: /* armored bunker + ringed cupola + thick barrel (KKND turret read) */ fill_rect(s, 2, H - 14, W - 4, 12, ok, og, ob, 255); fill_rect(s, 3, H - 12, W - 6, 9, 48, 44, 38, 255); /* bunker slab */ fill_rect(s, 4, H - 11, 4, 4, 70, 65, 55, 255); /* armor plate L */ fill_rect(s, W - 8, H - 11, 4, 4, 70, 65, 55, 255); /* armor plate R */ fill_rect(s, W/2 - 5, H - 8, 10, 3, 35, 32, 28, 255); /* embrasure */ /* ringed cupola */ disc(s, W/2, H/2, W/3 + 3, ok, og, ob, 255); disc(s, W/2, H/2, W/3 + 1, base.r, base.g, base.b, 255); ring(s, W/2, H/2, W/4 + 1, 2, dark.r, dark.g, dark.b, 255); disc(s, W/2, H/2, W/5, lite.r, lite.g, lite.b, 255); disc(s, W/2, H/2 - 1, 3, 35, 32, 28, 255); /* hatch */ /* thick armored barrel */ fill_rect(s, W/2 + 2, H/2 - 3, 12, 5, ok, og, ob, 255); fill_rect(s, W/2 + 3, H/2 - 2, 11, 3, 40, 36, 30, 255); line(s, W/2, H/2, W/2 + 14, H/2 - 10, 28, 26, 22, 255); line(s, W/2 + 1, H/2 + 1, W/2 + 14, H/2 - 9, 55, 48, 38, 255); disc(s, W/2 + 13, H/2 - 10, 2, 255, 200, 80, 255); /* muzzle tip */ break; case BT_WALL: /* thick fort wall + crenels + seams + embrasure (KKND fort read) */ fill_rect(s, 0, 4, W, H - 4, ok, og, ob, 255); fill_rect(s, 1, 6, W - 2, H - 8, 165, 145, 110, 255); fill_rect(s, 2, H - 6, W - 4, 4, 90, 78, 58, 255); /* plinth */ /* thick merlons */ fill_rect(s, 1, 1, 8, 10, ok, og, ob, 255); fill_rect(s, 2, 2, 6, 8, 195, 175, 140, 255); fill_rect(s, W/2 - 4, 1, 8, 10, ok, og, ob, 255); fill_rect(s, W/2 - 3, 2, 6, 8, 200, 180, 145, 255); fill_rect(s, W - 9, 1, 8, 10, ok, og, ob, 255); fill_rect(s, W - 8, 2, 6, 8, 190, 170, 135, 255); fill_rect(s, 2, 10, W - 4, 3, 215, 195, 155, 255); /* coping */ /* block seams + arrow slit */ line(s, 2, H/2 + 2, W - 2, H/2 + 2, 55, 48, 38, 255); line(s, W/3, 8, W/3, H - 4, 65, 55, 42, 255); line(s, 2 * W/3, 8, 2 * W/3, H - 4, 65, 55, 42, 255); fill_rect(s, W/2 - 2, H/2 - 2, 4, 8, 35, 30, 24, 255); /* embrasure */ fill_rect(s, W/2 - 1, H/2, 2, 4, 20, 18, 14, 255); break; case BT_REPAIR: /* garage + vehicle lift + tool rack + crane (KKND depot read) */ fill_rect(s, 4, 6, W - 8, H - 10, ok, og, ob, 255); fill_rect(s, 6, 8, W - 12, H - 14, base.r, base.g, base.b, 255); fill_rect(s, 8, 10, W - 16, 8, lite.r, lite.g, lite.b, 255); /* roof */ fill_rect(s, 10, 12, 4, 4, 50, 48, 40, 255); /* roof vents */ fill_rect(s, W/2 - 2, 12, 4, 4, 50, 48, 40, 255); fill_rect(s, W - 14, 12, 4, 4, 50, 48, 40, 255); /* open bay + lift pad */ fill_rect(s, 10, H - 28, W - 28, 18, ok, og, ob, 255); fill_rect(s, 12, H - 26, W - 32, 14, 38, 36, 32, 255); fill_rect(s, W/2 - 10, H - 22, 20, 8, 70, 68, 58, 255); /* lift platform */ fill_rect(s, W/2 - 8, H - 20, 16, 4, 110, 100, 70, 255); line(s, W/2 - 10, H - 22, W/2 - 10, H - 12, 90, 85, 70, 255); line(s, W/2 + 10, H - 22, W/2 + 10, H - 12, 90, 85, 70, 255); /* tool rack annex (left) */ fill_rect(s, 4, H/2 - 2, 10, H/2 - 6, ok, og, ob, 255); fill_rect(s, 5, H/2, 8, H/2 - 10, dark.r, dark.g, dark.b, 255); fill_rect(s, 6, H/2 + 2, 2, 6, 200, 160, 60, 255); /* tools */ fill_rect(s, 9, H/2 + 1, 2, 7, 180, 180, 190, 255); /* overhead crane mast + jib + hook */ fill_rect(s, W - 16, 4, 8, H - 20, ok, og, ob, 255); fill_rect(s, W - 14, 5, 5, H - 22, 95, 88, 70, 255); fill_rect(s, 16, 6, W - 32, 4, ok, og, ob, 255); fill_rect(s, 17, 7, W - 34, 2, 120, 110, 85, 255); line(s, W/2, 10, W/2, 20, 70, 65, 50, 255); disc(s, W/2, 22, 3, 80, 75, 55, 255); /* green service cross (clearer) */ ring(s, W/2 - 6, H/2 + 4, 9, 2, 90, 200, 90, 255); fill_rect(s, W/2 - 14, H/2 + 2, 16, 4, 120, 220, 120, 255); fill_rect(s, W/2 - 8, H/2 - 4, 4, 16, 120, 220, 120, 255); /* parts crates */ fill_rect(s, W - 22, H - 14, 8, 6, 140, 100, 55, 255); fill_rect(s, W - 20, H - 12, 4, 3, 180, 140, 70, 255); break; case BT_RESEARCH: /* tall lab + dish mast + instrument bank (KKND lab read) */ fill_rect(s, 5, 12, W - 10, H - 16, ok, og, ob, 255); fill_rect(s, 7, 14, W - 14, H - 20, base.r, base.g, base.b, 255); fill_rect(s, 9, 16, W - 18, 10, lite.r, lite.g, lite.b, 255); /* upper band */ /* glowing lab windows */ fill_rect(s, 10, H/2 - 2, 8, 6, 180, 230, 255, 255); fill_rect(s, 22, H/2 - 2, 8, 6, 160, 220, 250, 255); fill_rect(s, 34, H/2 - 2, 8, 6, 180, 230, 255, 255); fill_rect(s, 12, H/2, 4, 2, 240, 250, 255, 255); fill_rect(s, 24, H/2, 4, 2, 230, 245, 255, 255); /* dish on tall mast */ fill_rect(s, W/2 + 4, 2, 5, 18, ok, og, ob, 255); fill_rect(s, W/2 + 5, 3, 3, 16, 110, 100, 75, 255); disc(s, W/2 + 6, 12, 13, ok, og, ob, 255); disc(s, W/2 + 6, 12, 11, 215, 185, 85, 255); ring(s, W/2 + 6, 12, 8, 2, 170, 140, 55, 255); disc(s, W/2 + 6, 12, 4, 255, 245, 170, 255); disc(s, W/2 + 6, 12, 2, 255, 255, 220, 255); /* feed */ line(s, W/2 + 6, 2, W/2 + 6, 12, 200, 190, 120, 255); /* side antenna stubs */ fill_rect(s, 10, 6, 3, 10, 200, 200, 140, 255); fill_rect(s, 18, 4, 2, 12, 220, 220, 150, 255); disc(s, 11, 5, 2, 255, 240, 120, 255); disc(s, 19, 3, 2, 255, 230, 100, 255); /* instrument bank / console strip */ fill_rect(s, 10, H - 18, W - 28, 8, dark.r, dark.g, dark.b, 255); fill_rect(s, 12, H - 16, 4, 4, 80, 220, 120, 255); fill_rect(s, 18, H - 16, 4, 4, 80, 160, 255, 255); fill_rect(s, 24, H - 16, 4, 4, 255, 180, 80, 255); fill_rect(s, 30, H - 16, 4, 4, 200, 100, 255, 255); break; default: (void)dark; break; } SDL_Texture *tex = SDL_CreateTextureFromSurface(r, s); SDL_FreeSurface(s); return tex; } /* ---------------- terrain tiles ---------------- */ 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=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 */ for (int i = 0; i < 40; i++) { int x = (i*73) % TILE, y = (i*131) % TILE; int v = ((i*53) % 24) - 12; px(s, x, y, (Uint8)kknd_clampi(tr+v,0,255), (Uint8)kknd_clampi(tg+v,0,255), (Uint8)kknd_clampi(tb+v,0,255), 255); } if (t == T_SCRAP) { /* stacked scrap mounds (live parity with snapshot) */ int cc = TILE / 2, cr = TILE / 2 + 2; for (int k = 0; k < 7; k++) { int ox = ((k * 7) % 14) - 7; int oy = ((k * 5) % 10) - 5 - (k / 3) * 2; int rad = 3 + (k & 1); disc(s, cc + ox, cr + oy, rad, 235, 190, 85, 255); disc(s, cc + ox - 1, cr + oy - rad / 2, rad - 1, 210, 165, 70, 255); disc(s, cc + ox + 1, cr + oy - rad + 1, (rad > 2) ? rad - 2 : 1, 255, 215, 120, 255); } } if (t == T_WATER) { for (int i = 0; i < TILE; i += 4) line(s, 0, i, TILE, i, 60, 100, 150, 255); } if (t == T_ROCK) { for (int i = 0; i < 12; i++) { int x = (i*89) % TILE, y = (i*149) % TILE; disc(s, x, y, 3, 130, 124, 118, 255); } } SDL_Texture *tex = SDL_CreateTextureFromSurface(r, s); SDL_FreeSurface(s); return tex; } /* ---------------- bullets ---------------- */ static SDL_Texture *make_bullet(SDL_Renderer *r, int kind) { int S = 10; SDL_Surface *s = new_surf(S, S); switch (kind) { case 0: disc(s, 5, 5, 3, 255, 230, 120, 255); break; /* MG tracer */ case 1: disc(s, 5, 5, 4, 255, 160, 60, 255); break; /* shell */ case 2: disc(s, 5, 5, 3, 120, 255, 160, 255); break; /* laser */ case 3: disc(s, 5, 5, 4, 255, 90, 90, 255); break; /* missile */ default: disc(s, 5, 5, 3, 255, 255, 255, 255); } SDL_Texture *tex = SDL_CreateTextureFromSurface(r, s); SDL_FreeSurface(s); return tex; } int sprites_init(Sprites *sp, SDL_Renderer *r) { memset(sp, 0, sizeof(Sprites)); for (int t = 0; t < T_COUNT; t++) sp->terrain[t] = sprites_make_terrain(r, (TerrainType)t); for (int t = 1; t < UT_COUNT; t++) { sp->unit[t][FACTION_SURVIVOR] = sprites_make_unit(r, (UnitType)t, FACTION_SURVIVOR); sp->unit[t][FACTION_EVOLVED] = sprites_make_unit(r, (UnitType)t, FACTION_EVOLVED); sp->unit[t][FACTION_SERIES9] = sprites_make_unit(r, (UnitType)t, FACTION_SERIES9); } for (int t = 1; t < BT_COUNT; t++) { sp->bld[t][FACTION_SURVIVOR] = sprites_make_bld(r, (BuildingType)t, FACTION_SURVIVOR); sp->bld[t][FACTION_EVOLVED] = sprites_make_bld(r, (BuildingType)t, FACTION_EVOLVED); sp->bld[t][FACTION_SERIES9] = sprites_make_bld(r, (BuildingType)t, FACTION_SERIES9); } for (int k = 0; k < 4; k++) sp->bullet[k] = make_bullet(r, k); sp->font_small = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSans.ttf", 14); if (!sp->font_small) sp->font_small = TTF_OpenFont("/usr/share/fonts/gnu-free/FreeSans.ttf", 14); sp->font_med = sp->font_small ? TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSans.ttf", 20) : NULL; sp->font_big = sp->font_small ? TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSans.ttf", 34) : NULL; if (!sp->font_small) { sp->font_small = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSans-Bold.ttf", 14); } return 1; } void sprites_free(Sprites *sp) { for (int t = 0; t < T_COUNT; t++) if (sp->terrain[t]) SDL_DestroyTexture(sp->terrain[t]); for (int t = 1; t < UT_COUNT; t++) for (int f = 0; f < MAX_FACTIONS; f++) if (sp->unit[t][f]) SDL_DestroyTexture(sp->unit[t][f]); for (int t = 1; t < BT_COUNT; t++) for (int f = 0; f < MAX_FACTIONS; f++) if (sp->bld[t][f]) SDL_DestroyTexture(sp->bld[t][f]); for (int k = 0; k < 4; k++) if (sp->bullet[k]) SDL_DestroyTexture(sp->bullet[k]); if (sp->font_small) TTF_CloseFont(sp->font_small); if (sp->font_med) TTF_CloseFont(sp->font_med); if (sp->font_big) TTF_CloseFont(sp->font_big); }