#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 #include #include #include #include #include #include #include #include /* SDL_image is optional; engine uses procedural sprites + custom PNG writer. */ #if defined(KKND_HAVE_SDL_IMAGE) && KKND_HAVE_SDL_IMAGE #include #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 */