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

188
engine-c/src/engine.c Normal file
View file

@ -0,0 +1,188 @@
#include "kknd.h"
/* =========================================================================
* engine.c - Match setup, main simulation step and win/lose logic.
* ========================================================================= */
int g_headless = 0;
int g_auto = 0;
void engine_log(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
char buf[512];
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
printf("[kknd] %s\n", buf);
fflush(stdout);
}
void game_log(Game *g, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(g->log_msg[g->log_next], 96, fmt, ap);
va_end(ap);
g->log_next = (g->log_next + 1) % 32;
if (g->log_count < 32) g->log_count++;
}
void engine_init(Game *g, const char *title) {
memset(g, 0, sizeof(Game));
g->wrecks = (Wreck *)calloc(MAX_WRECKS, sizeof(Wreck));
config_init();
if (!g_headless) {
audio_init();
if (!render_init(g)) {
fprintf(stderr, "Failed to initialise renderer\n");
exit(1);
}
} else {
(void)title;
}
g->state = GAME_MENU;
g->human = FACTION_SURVIVOR;
g->diff = DIFF_NORMAL;
g->speed_mul = 1.0f;
g->unlocked = 1;
g->menu_sel = 0;
}
void engine_shutdown(Game *g) {
free(g->wrecks);
sprites_free(&g->spr);
if (g->ren) SDL_DestroyRenderer(g->ren);
if (g->win) SDL_DestroyWindow(g->win);
audio_shutdown();
SDL_Quit();
map_free(&g->map);
}
static void place_start(Game *g, Faction f) {
int sx = g->map.spawn_x[f], sy = g->map.spawn_y[f];
int bx = sx - 1, by = sy - 1;
bld_spawn(g, BT_BASE, f, bx, by);
bld_spawn(g, BT_REFINERY, f, bx, sy + 2);
bld_spawn(g, BT_WARFACTORY, f, sx + 2, by);
bld_spawn(g, BT_POWER, f, sx + 2, sy + 2);
float wx, wy; map_tile_to_world(sx, sy, &wx, &wy);
/* harvesters */
for (int i = 0; i < 2; i++)
unit_spawn(g, UT_HARVESTER, f, wx + (i*24-12), wy + 30);
/* starting infantry */
for (int i = 0; i < 4; i++)
unit_spawn(g, (f==FACTION_SURVIVOR?UT_INFANTRY:UT_INFANTRY), f,
wx - 40 + i*20, wy - 20);
/* a couple of buggies */
for (int i = 0; i < 2; i++)
unit_spawn(g, (f==FACTION_SURVIVOR?UT_BUGGY:UT_BUGGY), f,
wx - 30 + i*30, wy - 40);
}
void engine_setup_match(Game *g, int mission_id) {
const Mission *m = mission_get(mission_id);
g->mission_id = m->id;
g->objective = m->objective;
g->diff = (Difficulty)m->diff;
g->obj_timer = 0;
/* resolve the enemy faction (never the player's own side) */
Faction enemy = m->enemy;
if (enemy == g->human)
enemy = (g->human == FACTION_SURVIVOR) ? FACTION_EVOLVED : FACTION_SURVIVOR;
g->enemy = enemy;
RNG rng;
rng_seed(&rng, (uint64_t)m->seed ^ 0x4B4B4E44ULL);
g->map.style = m->map_style;
map_init(&g->map, &rng);
g->unit_count = 0; g->unit_next_id = 1;
g->bld_count = 0; g->bld_next_id = 1;
g->bullet_count = 0; g->particle_count = 0;
g->sel_count = 0; g->placing = 0;
g->time = 0; g->frames = 0; g->winner = 0;
g->ai_timer = 5.0f;
g->log_count = 0; g->log_next = 0;
for (int f = 1; f < MAX_FACTIONS; f++) {
g->scrap[f] = START_SCRAP;
g->power[f] = 0; g->pop[f] = 0; g->pop_cap[f] = 0;
for (int u = 0; u < UP_COUNT; u++) g->upg[f].lvl[u] = 0;
g->research_lab[f] = 0;
g->research_cur[f] = -1;
g->research_prog[f] = 0;
}
place_start(g, g->human);
place_start(g, enemy);
/* fortress theme: wall the enemy base (leave a gate on the far edge) */
if (m->map_style == 3) {
int sx = g->map.spawn_x[enemy], sy = g->map.spawn_y[enemy];
for (int dx = -2; dx <= 4; dx++)
for (int dy = -2; dy <= 4; dy++) {
int perimeter = (dx == -2 || dx == 4 || dy == -2 || dy == 4);
if (!perimeter) continue;
if (dy == 4) continue; /* gate on the far edge */
bld_spawn(g, BT_WALL, enemy, sx + dx, sy + dy);
}
}
/* centre camera on player base */
int hx = g->map.spawn_x[g->human], hy = g->map.spawn_y[g->human];
g->cam.x = (float)(hx*TILE - VIEW_W/2);
g->cam.y = (float)(hy*TILE - VIEW_H/2);
if (g->cam.x < 0) g->cam.x = 0;
if (g->cam.y < 0) g->cam.y = 0;
g->state = GAME_PLAYING;
engine_log("Mission %d '%s': %s vs %s (%s)",
m->id, m->name, faction_name(g->human), faction_name(enemy),
m->diff==DIFF_EASY?"Easy":m->diff==DIFF_HARD?"Hard":"Normal");
}
void engine_check_win(Game *g) {
Faction human = g->human;
Faction enemy = g->enemy;
int human_base = 0, enemy_base = 0;
int human_bld = 0, enemy_bld = 0;
for (int i = 0; i < g->bld_count; i++) {
Building *b = &g->buildings[i];
if (b->dead) continue;
if (b->faction == human) { human_bld++; if (b->type==BT_BASE) human_base=1; }
if (b->faction == enemy) { enemy_bld++; if (b->type==BT_BASE) enemy_base=1; }
}
if (!human_base || human_bld == 0) { g->state = GAME_LOST; g->winner = enemy; return; }
switch (g->objective) {
case OBJ_SURVIVE:
if (g->time >= ((const Mission*)mission_get(g->mission_id))->survive_time)
{ g->state = GAME_WON; g->winner = human; }
break;
case OBJ_GATHER:
if (g->scrap[human] >= ((const Mission*)mission_get(g->mission_id))->scrap_goal)
{ g->state = GAME_WON; g->winner = human; }
break;
default: /* OBJ_DESTROY_ALL */
if (!enemy_base || enemy_bld == 0) { g->state = GAME_WON; g->winner = human; }
break;
}
}
void engine_update(Game *g, float dt) {
if (g->state != GAME_PLAYING) return;
dt *= g->speed_mul;
g->time += dt;
g->frames++;
unit_update(g, dt);
bld_update(g, dt);
bullet_update(g, dt);
particle_update(g, dt);
ai_update(g, dt);
map_update_fog(g, g->human);
audio_music_tick(dt);
if (g->cam.shake > 0) { g->cam.shake -= dt * 24.0f; if (g->cam.shake < 0) g->cam.shake = 0; }
engine_check_win(g);
}