/* * sh_game.h -- Space Hulk turn-based game system for Quakespasm * * Copyright (C) 2026 fish fvch studios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #ifndef SH_GAME_H #define SH_GAME_H /* ============================================================ * CONSTANTS * ============================================================ */ #define SH_GRID_SIZE 128 /* quake units per grid cell */ #define SH_MAX_MARINES 5 #define SH_MAX_GENESTEALERS 32 #define SH_MAX_DOORS 16 #define SH_MAX_BLIP_ENTRIES 8 #define SH_MAX_LOG_MESSAGES 32 #define SH_LOG_MSG_LEN 80 #define SH_DEFAULT_AP 4 #define SH_MAX_COMMAND_POINTS 6 /* 1d6 bonus AP per round */ #define SH_GENESTEALER_MP 6 /* movement points per alien phase */ /* AP costs */ #define SH_AP_MOVE_FORWARD 1 #define SH_AP_TURN 0 /* free turns for snappy gameplay */ #define SH_AP_SHOOT 1 #define SH_AP_OVERWATCH 2 #define SH_AP_MELEE 1 #define SH_AP_DOOR 1 /* animation durations (seconds) */ #define SH_ANIM_MOVE_DURATION 0.2 #define SH_ANIM_TURN_DURATION 0.15 #define SH_ANIM_SHOOT_DURATION 0.3 #define SH_MAX_ANIMS 8 /* standing eye height above grid floor */ #define SH_EYE_HEIGHT 32 /* ============================================================ * ENUMERATIONS * ============================================================ */ typedef enum { SH_PHASE_INACTIVE, /* game not started */ SH_PHASE_BRIEFING, /* pre-mission screen */ SH_PHASE_PLAYER, /* player marine phase */ SH_PHASE_ALIEN, /* AI genestealer phase */ SH_PHASE_OVERWATCH_REACT, /* interrupt: marine reaction fire */ SH_PHASE_ROUND_END, /* cleanup, check win/lose */ SH_PHASE_VICTORY, SH_PHASE_DEFEAT } sh_phase_t; typedef enum { SH_ACTION_NONE, SH_ACTION_MOVE_FORWARD, SH_ACTION_TURN_LEFT, SH_ACTION_TURN_RIGHT, SH_ACTION_TURN_180, SH_ACTION_SHOOT, SH_ACTION_OVERWATCH, SH_ACTION_MELEE, SH_ACTION_DOOR, SH_ACTION_END_TURN } sh_action_t; typedef enum { SH_FACING_NORTH, /* +Y: yaw 90 */ SH_FACING_EAST, /* +X: yaw 0 */ SH_FACING_SOUTH, /* -Y: yaw 270 */ SH_FACING_WEST /* -X: yaw 180 */ } sh_facing_t; typedef enum { SH_WEAPON_STORM_BOLTER, SH_WEAPON_ASSAULT_CANNON, SH_WEAPON_POWER_FIST, SH_WEAPON_FLAMER } sh_weapon_t; typedef enum { SH_LOG_INFO, SH_LOG_COMBAT, SH_LOG_ALERT, SH_LOG_KILL } sh_log_type_t; typedef enum { SH_AI_IDLE, SH_AI_ADVANCE, SH_AI_ATTACK, SH_AI_FLANK } sh_ai_state_t; /* ============================================================ * DATA STRUCTURES * ============================================================ */ /* Sidecar data for a marine entity */ typedef struct { qboolean active; edict_t *ent; /* quake edict for rendering/collision */ int squad_index; int grid_x, grid_y; sh_facing_t facing; int ap; /* action points remaining this turn */ int ap_max; /* base AP (default 4) */ sh_weapon_t weapon; int health; /* 1 = alive in Space Hulk */ qboolean on_overwatch; int overwatch_ap; qboolean has_acted; /* activated this phase */ qboolean alive; char name[32]; } sh_marine_t; /* Sidecar data for a genestealer entity */ typedef struct { qboolean active; edict_t *ent; int grid_x, grid_y; sh_facing_t facing; int mp; /* movement points remaining this phase */ sh_ai_state_t ai_state; int target_marine; /* index into marines[], or -1 */ qboolean alive; qboolean is_blip; int blip_count; /* 1-3 genestealers per blip */ } sh_genestealer_t; /* Door entity */ typedef struct { qboolean active; edict_t *ent; int grid_x, grid_y; qboolean is_open; } sh_door_t; /* Blip spawn entry point */ typedef struct { int grid_x, grid_y; sh_facing_t entry_facing; qboolean active; } sh_blip_entry_t; /* Message log (adapted from ideas/turn_manager.c) */ typedef struct { char text[SH_LOG_MSG_LEN]; sh_log_type_t type; int round; int stack_count; } sh_log_msg_t; typedef struct { sh_log_msg_t messages[SH_MAX_LOG_MESSAGES]; int write_index; int count; qboolean updated; } sh_message_log_t; /* Animation lerp for smooth grid movement in 3D */ typedef struct { qboolean active; edict_t *ent; vec3_t start_pos; vec3_t end_pos; float start_yaw; float end_yaw; double start_time; double duration; } sh_anim_t; /* ============================================================ * MASTER GAME STATE * ============================================================ */ typedef struct { /* phase management */ sh_phase_t phase; sh_phase_t phase_before_interrupt; int round_number; int command_points; int command_points_used; /* marines */ sh_marine_t marines[SH_MAX_MARINES]; int num_marines; int selected_marine; /* genestealers */ sh_genestealer_t genestealers[SH_MAX_GENESTEALERS]; int num_genestealers; int current_alien; /* doors */ sh_door_t doors[SH_MAX_DOORS]; int num_doors; /* spawn points */ sh_blip_entry_t blip_entries[SH_MAX_BLIP_ENTRIES]; int num_blip_entries; /* animation queue */ sh_anim_t anims[SH_MAX_ANIMS]; int num_anims; /* message log */ sh_message_log_t log; /* camera (follows selected marine for now) */ int camera_marine; } sh_gamestate_t; extern sh_gamestate_t sh_game; /* ============================================================ * PUBLIC API -- sh_game.c * ============================================================ */ void SH_Init (void); void SH_Shutdown (void); void SH_NewGame (void); void SH_Frame (void); qboolean SH_Active (void); /* coordinate conversion */ void SH_GridToWorld (int gx, int gy, vec3_t out); void SH_WorldToGrid (vec3_t pos, int *gx, int *gy); /* facing helpers */ float SH_FacingToYaw (sh_facing_t facing); sh_facing_t SH_YawToFacing (float yaw); void SH_FacingToDir (sh_facing_t facing, int *dx, int *dy); /* grid collision */ qboolean SH_GridBlocked (int gx, int gy, edict_t *ignore); /* animation */ void SH_AnimPush (edict_t *ent, vec3_t start, vec3_t end, float start_yaw, float end_yaw, double duration); qboolean SH_AnimBusy (void); /* logging */ void SH_Log (sh_log_type_t type, const char *fmt, ...) FUNC_PRINTF(2,3); /* ============================================================ * PUBLIC API -- sh_marine.c * ============================================================ */ void SH_Marine_Spawn (int index, int gx, int gy, sh_facing_t facing, sh_weapon_t weapon, const char *name); qboolean SH_Marine_TryAction (int marine_index, sh_action_t action); void SH_Marine_BeginPlayerPhase (void); qboolean SH_Marine_AllDone (void); void SH_Marine_SelectNext (void); void SH_Marine_SelectPrev (void); /* ============================================================ * PUBLIC API -- sh_input.c * ============================================================ */ void SH_Input_Init (void); qboolean SH_Input_KeyEvent (int key, qboolean down); #endif /* SH_GAME_H */