kitchen: README + MISSIONS/engine-c keep-drop notes

This commit is contained in:
Hernâni Marques 2026-09-17 21:48:48 +02:00
parent 1d351918da
commit 575d7d29b8
No known key found for this signature in database
130 changed files with 9817 additions and 1 deletions

341
engine-c/src/sprites.c Normal file
View file

@ -0,0 +1,341 @@
#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 ---------------- */
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);
SDL_Color metal = { 60, 60, 70, 255 };
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, 12, 3, lite.r, lite.g, lite.b, 255);
line(s, 15, 15, 15, 2, 30, 30, 30, 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);
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);
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);
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);
break;
case UT_ARTILLERY:
fill_rect(s, 5, 11, 20, 9, dark.r, dark.g, dark.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);
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);
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);
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);
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, 11, 3, lite.r, lite.g, lite.b, 255);
line(s, 15, 16, 23, 10, 30, 30, 30, 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);
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);
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);
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);
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);
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, 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);
break;
default:
disc(s, 15, 15, 9, base.r, base.g, base.b, 255);
break;
}
SDL_Texture *tex = SDL_CreateTextureFromSurface(r, s);
SDL_FreeSurface(s);
return tex;
}
/* ---------------- building sprites ---------------- */
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.4f);
SDL_Color wall = { 90, 84, 78, 255 };
SDL_Color wall2 = { 64, 60, 56, 255 };
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);
ring(s, W/2, H/2, (W<H?W:H)/2 - 3, 2, dark.r, dark.g, dark.b, 255);
switch (t) {
case BT_BASE:
disc(s, W/2, H/2, W/4, base.r, base.g, base.b, 255);
disc(s, W/2, H/2, W/6, lite.r, lite.g, lite.b, 255);
line(s, W/2, H/2, W/2, 4, 200, 200, 60, 255);
break;
case BT_WARFACTORY:
fill_rect(s, 8, 8, W-16, H-16, dark.r, dark.g, dark.b, 255);
fill_rect(s, 12, 12, W-24, 10, base.r, base.g, base.b, 255);
fill_rect(s, 12, H-24, W-24, 10, base.r, base.g, base.b, 255);
break;
case BT_REFINERY:
disc(s, W/2, H/2, W/4, 120, 100, 60, 255);
ring(s, W/2, H/2, W/4 - 3, 3, 60, 50, 40, 255);
break;
case BT_POWER:
disc(s, W/2, H/2, W/3, 80, 120, 160, 255);
line(s, W/2, 4, W/2, H-4, 220, 240, 255, 255);
line(s, 4, H/2, W-4, H/2, 220, 240, 255, 255);
break;
case BT_TURRET:
disc(s, W/2, H/2, W/3, dark.r, dark.g, dark.b, 255);
disc(s, W/2, H/2, W/5, lite.r, lite.g, lite.b, 255);
line(s, W/2, H/2, W/2 + 12, H/2 - 12, 40, 40, 45, 255);
break;
case BT_WALL:
fill_rect(s, 2, 2, W-4, H-4, 70, 66, 60, 255);
line(s, 2, 2, W-2, H-2, 40, 38, 34, 255);
break;
case BT_REPAIR:
fill_rect(s, 8, 8, W-16, H-16, dark.r, dark.g, dark.b, 255);
ring(s, W/2, H/2, W/4, 3, 120, 220, 120, 255);
line(s, W/2 - 12, H/2, W/2 + 12, H/2, 120, 220, 120, 255);
line(s, W/2, H/2 - 12, W/2, H/2 + 12, 120, 220, 120, 255);
break;
case BT_RESEARCH:
fill_rect(s, 8, 8, W-16, H-16, dark.r, dark.g, dark.b, 255);
disc(s, W/2, H/2, W/5, 180, 150, 60, 255);
line(s, W/2, 6, W/2, H-6, 220, 220, 150, 255);
line(s, 6, H/2, W-6, H/2, 220, 220, 150, 255);
break;
default:
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;
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;
}
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) {
for (int i = 0; i < 10; i++) {
int x = (i*97) % TILE, y = (i*61) % TILE;
disc(s, x, y, 2, 180, 150, 60, 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);
}