OpenKKnD/engine-c/include/kknd.h
2026-09-19 10:04:41 +02:00

645 lines
22 KiB
C

#ifndef KKND_H
#define KKND_H
/* =========================================================================
* openkknd - Krush, Kill 'n' Destroy style 2D Real-Time Strategy engine
* Single merged codebase (was: openkknd + openkknd_full).
*
* This header declares the complete shared type system and the public
* interface of every engine subsystem. The implementation lives in the
* matching src/*.c translation units.
* ========================================================================= */
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
/* SDL_image is optional; engine uses procedural sprites + custom PNG writer. */
#if defined(KKND_HAVE_SDL_IMAGE) && KKND_HAVE_SDL_IMAGE
#include <SDL2/SDL_image.h>
#endif
#include "unit_card.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ----------------------------------------------------------------------- */
/* Global tunables */
/* ----------------------------------------------------------------------- */
#define KKND_NAME "openkknd"
#define KKND_VERSION "1.0.0"
#define TILE 32 /* pixel size of one map tile */
#define MAP_W 128 /* map width in tiles */
#define MAP_H 128 /* map height in tiles */
#define MAP_PIX_W (MAP_W * TILE)
#define MAP_PIX_H (MAP_H * TILE)
#define MAX_UNITS 3000
#define MAX_BUILDINGS 1024
#define MAX_BULLETS 6000
#define MAX_PARTICLES 4000
#define MAX_PATH 1024
#define MAX_SELECTION 256
#define MAX_FACTIONS 4
#define VIEW_W 1280
#define VIEW_H 720
#define START_SCRAP 1500 /* starting resource for each side */
#define SCRAP_PER_TILE 1000 /* scrap contained in one scrap field */
#define SAVE_MAGIC 0x4B4B4E44 /* 'KKND' */
#define SAVE_VERSION 2
/* ----------------------------------------------------------------------- */
/* Enumerations */
/* ----------------------------------------------------------------------- */
typedef enum {
T_WASTELAND = 0,
T_RUBBLE,
T_ROAD,
T_GRASS,
T_WATER,
T_ROCK,
T_SCRAP,
T_COUNT
} TerrainType;
typedef enum {
FACTION_NONE = 0,
FACTION_SURVIVOR, /* player - post-war military remnants */
FACTION_EVOLVED, /* ai - mutated wasteland beasties */
FACTION_SERIES9, /* neutral - rogue war machines */
FACTION_NEUTRAL
} Faction;
typedef enum {
UT_NONE = 0,
UT_INFANTRY,
UT_BUGGY,
UT_TANK,
UT_HARVESTER,
UT_ARTILLERY,
UT_JEEP,
UT_MUTANT,
UT_BEAST,
UT_RAVAGER,
UT_FLAME,
UT_MISSILE,
UT_MEDIC,
UT_COUNT
} UnitType;
typedef enum {
AB_NONE = 0,
AB_SIEGE, /* tanks/artillery: +range, immobile while active */
AB_HEAL /* medics: pulse heal nearby allies */
} AbilityType;
typedef enum {
BT_NONE = 0,
BT_BASE, /* command centre / HQ */
BT_WARFACTORY,
BT_REFINERY,
BT_POWER,
BT_TURRET,
BT_WALL,
BT_REPAIR,
BT_RESEARCH,
BT_COUNT
} BuildingType;
typedef enum {
STATE_IDLE = 0,
STATE_MOVING,
STATE_ATTACKING,
STATE_GATHERING,
STATE_RETURNING,
STATE_DEAD
} UnitState;
typedef enum {
ORDER_NONE = 0,
ORDER_MOVE,
ORDER_ATTACKMOVE,
ORDER_ATTACK,
ORDER_GATHER,
ORDER_RETURN,
ORDER_BUILD,
ORDER_STOP
} OrderType;
typedef enum {
GAME_MENU = 0,
GAME_PLAYING,
GAME_PAUSED,
GAME_WON,
GAME_LOST
} GameState;
typedef enum {
DIFF_EASY = 0,
DIFF_NORMAL,
DIFF_HARD,
DIFF_COUNT
} Difficulty;
/* ----------------------------------------------------------------------- */
/* Small math helpers */
/* ----------------------------------------------------------------------- */
typedef struct { float x, y; } Vec2;
static inline float kknd_dist2(float ax, float ay, float bx, float by) {
float dx = ax - bx, dy = ay - by;
return dx * dx + dy * dy;
}
static inline float kknd_dist(float ax, float ay, float bx, float by) {
return (float)sqrtf(kknd_dist2(ax, ay, bx, by));
}
static inline float kknd_clampf(float v, float lo, float hi) {
return v < lo ? lo : (v > hi ? hi : v);
}
static inline int kknd_clampi(int v, int lo, int hi) {
return v < lo ? lo : (v > hi ? hi : v);
}
/* ----------------------------------------------------------------------- */
/* Deterministic RNG (xoroshiro128**) */
/* ----------------------------------------------------------------------- */
typedef struct {
uint64_t s[2];
} RNG;
void rng_seed(RNG *r, uint64_t seed);
uint64_t rng_u64(RNG *r);
uint32_t rng_u32(RNG *r);
int rng_range(RNG *r, int lo, int hi);
float rng_f32(RNG *r);
float rng_frange(RNG *r, float lo, float hi);
/* ----------------------------------------------------------------------- */
/* Map */
/* ----------------------------------------------------------------------- */
typedef struct {
uint8_t terrain; /* TerrainType */
uint8_t walkable; /* 0 = blocked, 1 = passable */
uint8_t scrap; /* remaining scrap (0..255) */
uint8_t variant; /* cosmetic variation */
uint8_t occupied; /* building footprint marker */
} Tile;
typedef struct {
int w, h;
Tile *tiles; /* w*h */
uint8_t *fog; /* human visibility: 0 hidden,1 explored,2 visible */
int scrap_fields; /* number of scrap deposits */
int style; /* terrain theme */
int spawn_x[MAX_FACTIONS];
int spawn_y[MAX_FACTIONS];
} GameMap;
void map_init(GameMap *m, RNG *rng);
void map_free(GameMap *m);
Tile *map_tile(GameMap *m, int tx, int ty);
int map_walkable(GameMap *m, int tx, int ty);
int map_in_bounds(GameMap *m, int tx, int ty);
void map_world_to_tile(float wx, float wy, int *tx, int *ty);
void map_tile_to_world(int tx, int ty, float *wx, float *wy);
int map_find_nearest_scrap(GameMap *m, float wx, float wy, float *outx, float *outy);
void map_deplete_scrap(GameMap *m, int tx, int ty, int amount);
typedef struct Game Game;
void map_update_fog(Game *g, Faction f);
/* A* pathfinding ------------------------------------------------------- */
typedef struct {
int tx, ty; /* tile coordinate */
} PathNode;
typedef struct {
PathNode nodes[MAX_PATH];
int len;
} Path;
int path_find(GameMap *m, int sx, int sy, int gx, int gy, Path *out);
void path_smooth(GameMap *m, Path *p);
/* ----------------------------------------------------------------------- */
/* Units */
/* ----------------------------------------------------------------------- */
typedef struct Unit {
int id;
UnitType type;
Faction faction;
float x, y; /* world position (pixels) */
float hp;
float max_hp;
float attack;
float defense;
float speed; /* pixels / second */
float range; /* pixels */
float sight; /* pixels */
float heading; /* radians, for sprite rotation */
UnitState state;
OrderType order;
int target_unit; /* id, -1 = none */
int target_bld; /* id, -1 = none */
float dest_x, dest_y;
Path path;
int path_idx;
float atk_cd; /* attack cooldown timer */
float cargo; /* harvester load */
int selected;
int dead;
float anim; /* walk/attack animation phase */
float repath_timer; /* throttle re-pathing */
int hold; /* hold position (don't chase) */
int attack_move; /* move toward dest but engage en route */
AbilityType ability; /* special ability, 0 = none */
float ability_cd; /* cooldown remaining */
int ability_on; /* active toggle (siege) */
float reload_mul; /* reload speed multiplier (upgrades) */
} Unit;
#define MAX_WRECKS 1024
typedef struct {
float x, y;
UnitType type;
Faction faction;
float rot;
} Wreck;
#define MAX_PARTICLES 2048
/* ----------------------------------------------------------------------- */
/* Buildings */
/* ----------------------------------------------------------------------- */
typedef struct Building {
int id;
BuildingType type;
Faction faction;
int tx, ty; /* top-left tile */
int w, h; /* footprint in tiles */
float hp;
float max_hp;
float build; /* 0..1 construction progress */
int producing; /* UnitType being built, 0 = none */
float prod_timer;
float prod_total;
int queue[MAX_SELECTION];
int queue_len;
float rally_x, rally_y;
int selected;
int dead;
float atk_cd;
} Building;
/* ----------------------------------------------------------------------- */
/* Projectiles & particles */
/* ----------------------------------------------------------------------- */
typedef struct {
float x, y, tx, ty;
float speed;
float damage;
Faction faction;
int target_unit;
int target_bld;
float life;
int kind; /* 0 bullet, 1 shell, 2 laser, 3 missile */
} Bullet;
typedef struct {
float x, y;
float vx, vy;
float life;
float max_life;
uint8_t r, g, b;
int kind; /* 0 smoke, 1 fire, 2 spark, 3 debris */
} Particle;
/* ----------------------------------------------------------------------- */
/* Camera */
/* ----------------------------------------------------------------------- */
typedef struct {
float x, y; /* world top-left of view */
float zoom;
int shake;
} Camera;
/* ----------------------------------------------------------------------- */
/* Sprite cache */
/* ----------------------------------------------------------------------- */
typedef struct {
SDL_Texture *unit[UT_COUNT][MAX_FACTIONS];
SDL_Texture *bld[BT_COUNT][MAX_FACTIONS];
SDL_Texture *terrain[T_COUNT];
SDL_Texture *bullet[4];
TTF_Font *font_small;
TTF_Font *font_med;
TTF_Font *font_big;
} Sprites;
/* ----------------------------------------------------------------------- */
/* Research / upgrades (declared early so Game can embed it) */
/* ----------------------------------------------------------------------- */
#define UP_DAMAGE 0
#define UP_ARMOR 1
#define UP_SPEED 2
#define UP_RELOAD 3
#define UP_BUILD 4
#define UP_COUNT 5
#define UP_MAX_LVL 3
typedef struct { int lvl[UP_COUNT]; } Upgrades;
/* ----------------------------------------------------------------------- */
/* Game state */
/* ----------------------------------------------------------------------- */
typedef struct Game {
GameMap map;
Unit units[MAX_UNITS];
int unit_count;
int unit_next_id;
Building buildings[MAX_BUILDINGS];
int bld_count;
int bld_next_id;
Bullet bullets[MAX_BULLETS];
int bullet_count;
Particle particles[MAX_PARTICLES];
int particle_count;
int scrap[MAX_FACTIONS];
int power[MAX_FACTIONS];
int pop[MAX_FACTIONS];
int pop_cap[MAX_FACTIONS];
int selection[MAX_SELECTION];
int sel_count;
/* control groups (Ctrl+1..9 sets, 1..9 recalls) */
int groups[9][MAX_SELECTION];
int group_len[9];
Camera cam;
Sprites spr;
SDL_Renderer *ren;
SDL_Window *win;
GameState state;
Difficulty diff;
Faction human;
float time;
float speed_mul;
int winner;
int pause;
/* drag-selection rectangle (screen space) */
int drag_x0, drag_y0, drag_x1, drag_y1, dragging;
/* build placement ghost */
BuildingType place_type;
int placing;
float ai_timer;
float ai_wave[MAX_FACTIONS];
int frames;
/* on-screen message log (ring buffer) */
char log_msg[32][96];
int log_count;
int log_next;
/* research / upgrades */
Upgrades upg[MAX_FACTIONS];
int research_lab[MAX_FACTIONS];
int research_cur[MAX_FACTIONS];
float research_prog[MAX_FACTIONS];
/* mission */
int mission_id;
int objective;
Faction enemy; /* actual enemy for the active match */
int unlocked; /* highest unlocked mission id + 1 */
int menu_sel; /* highlighted mission in the menu */
float obj_timer;
/* wreckage decals */
Wreck *wrecks;
int wreck_count;
int wreck_next;
} Game;
/* ----------------------------------------------------------------------- */
/* Configuration / unit stats */
/* ----------------------------------------------------------------------- */
typedef struct {
const char *name;
UnitType type;
Faction faction;
int cost;
float hp;
float attack;
float defense;
float speed;
float range;
float sight;
int cargo;
int build_time; /* seconds to produce */
AbilityType ability; /* special ability, 0 = none */
const char *desc;
} UnitDef;
typedef struct {
const char *name;
BuildingType type;
int cost;
int build_time;
float hp;
int w, h;
int provides_power;
int pop_provided;
int produces[UT_COUNT > 8 ? UT_COUNT : 8];
int produces_count;
int is_turret;
float atk;
float range;
int is_research; /* research lab flag */
const char *desc;
} BldDef;
/* ----------------------------------------------------------------------- */
/* Missions / campaign */
/* ----------------------------------------------------------------------- */
#define OBJ_DESTROY_ALL 0
#define OBJ_SURVIVE 1
#define OBJ_GATHER 2
typedef struct {
int id;
const char *name;
const char *brief;
int objective;
Faction enemy;
int diff;
int scrap_goal; /* for OBJ_GATHER */
float survive_time; /* for OBJ_SURVIVE */
int map_style; /* terrain theme */
uint32_t seed;
} Mission;
/* Build the unit-card registries (merged feature) and stat tables. */
void config_init(void);
const UnitDef *unit_def(UnitType t, Faction f);
const BldDef *bld_def(BuildingType t);
UnitRegistry *unit_card_registry(void); /* for save/inspect */
UnitRegistry *bld_card_registry(void);
int mission_count(void);
const Mission *mission_get(int id);
const char *research_name(int up);
int research_cost(int up, int level);
/* ----------------------------------------------------------------------- */
/* Sprites */
/* ----------------------------------------------------------------------- */
int sprites_init(Sprites *s, SDL_Renderer *r);
void sprites_free(Sprites *s);
SDL_Texture *sprites_make_unit(SDL_Renderer *r, UnitType t, Faction f);
SDL_Texture *sprites_make_bld(SDL_Renderer *r, BuildingType t, Faction f);
SDL_Texture *sprites_make_terrain(SDL_Renderer *r, TerrainType t);
/* ----------------------------------------------------------------------- */
/* Units */
/* ----------------------------------------------------------------------- */
int unit_spawn(Game *g, UnitType t, Faction f, float x, float y);
void unit_remove(Game *g, int idx);
void unit_update(Game *g, float dt);
void unit_issue_move(Game *g, int idx, float x, float y, int attackmove);
void unit_issue_move_group(Game *g, const int *idxs, int n, float cx, float cy, int attackmove);
void unit_issue_attack_unit(Game *g, int idx, int target);
void unit_issue_attack_bld(Game *g, int idx, int target);
void unit_issue_gather(Game *g, int idx, float x, float y);
void unit_issue_gather_nearest(Game *g, int idx);
void unit_issue_return(Game *g, int idx);
void unit_issue_stop(Game *g, int idx);
void unit_issue_hold(Game *g, int idx);
void unit_ability_use(Game *g, int idx);
void unit_apply_damage(Game *g, Unit *u, float dmg);
Unit *unit_by_id(Game *g, int id);
void unit_select_in_rect(Game *g, int x0, int y0, int x1, int y1, int add);
void unit_select_single(Game *g, int idx, int add);
void unit_clear_selection(Game *g);
void unit_select_all(Game *g, Faction f);
/* ----------------------------------------------------------------------- */
/* Buildings */
/* ----------------------------------------------------------------------- */
int bld_spawn(Game *g, BuildingType t, Faction f, int tx, int ty);
void bld_remove(Game *g, int idx);
void bld_update(Game *g, float dt);
void bld_queue_unit(Game *g, int idx, UnitType t);
Building *bld_by_id(Game *g, int id);
int bld_can_place(Game *g, BuildingType t, int tx, int ty);
void bld_apply_damage(Game *g, Building *b, float dmg);
int bld_research(Game *g, Faction f, int up);
/* ----------------------------------------------------------------------- */
/* Projectiles & particles */
/* ----------------------------------------------------------------------- */
void bullet_spawn(Game *g, float x, float y, int target_unit, int target_bld,
float dmg, Faction f, int kind);
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_burst(Game *g, float x, float y, int n, int kind, uint8_t cr, uint8_t cg, uint8_t cb);
void particle_update(Game *g, float dt);
/* ----------------------------------------------------------------------- */
/* Rendering */
/* ----------------------------------------------------------------------- */
typedef struct {
int x, y, w, h;
int kind; /* 0 = structure button, 1 = unit button */
int id; /* BuildingType or UnitType */
const char *label;
int enabled;
int cost;
} HudButton;
int render_init(Game *g);
void render_frame(Game *g);
void render_world(Game *g);
void render_units(Game *g);
void render_buildings(Game *g);
void render_bullets(Game *g);
void render_particles(Game *g);
void render_selection(Game *g);
void render_hud(Game *g);
void render_minimap(Game *g);
void render_text(Sprites *s, SDL_Renderer *r, TTF_Font *f,
const char *txt, int x, int y, SDL_Color c);
void render_build_menu(Game *g);
void hud_layout(Game *g, HudButton *out, int *n);
/* ----------------------------------------------------------------------- */
/* Input */
/* ----------------------------------------------------------------------- */
void input_handle(Game *g, SDL_Event *e);
void input_camera(Game *g, float dt);
int input_screen_to_world(Game *g, int sx, int sy, float *wx, float *wy);
/* ----------------------------------------------------------------------- */
/* AI */
/* ----------------------------------------------------------------------- */
void ai_update(Game *g, float dt);
void ai_command(Faction f, int diff);
/* ----------------------------------------------------------------------- */
/* Audio */
/* ----------------------------------------------------------------------- */
int audio_init(void);
void audio_shutdown(void);
void audio_sfx(int id); /* 0 move,1 shoot,2 explode,3 build,4 select,5 error */
void audio_music_start(void);
void audio_music_stop(void);
void audio_music_tick(float dt);
/* ----------------------------------------------------------------------- */
/* Save / load */
/* ----------------------------------------------------------------------- */
int save_game(Game *g, const char *path);
int load_game(Game *g, const char *path);
/* ----------------------------------------------------------------------- */
/* Engine */
/* ----------------------------------------------------------------------- */
extern int g_headless; /* when 1, run simulation without any renderer */
extern int g_auto; /* when 1, the human faction is also AI-driven */
void engine_init(Game *g, const char *title);
void engine_shutdown(Game *g);
void engine_setup_match(Game *g, int mission_id);
void engine_update(Game *g, float dt);
void engine_check_win(Game *g);
void engine_log(const char *fmt, ...);
void game_log(Game *g, const char *fmt, ...);
/* Helpers */
int png_save(const char *path, int w, int h, const uint8_t *rgba);
void snapshot_capture(Game *g, const char *path);
Faction enemy_of(Faction f);
const char *faction_name(Faction f);
const char *unit_name(UnitType t, Faction f);
SDL_Color faction_color(Faction f);
#ifdef __cplusplus
}
#endif
#endif /* KKND_H */