/*
Copyright (C) 1997-2001 Id Software, Inc.

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.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// g_local.h -- local definitions for game module

#include "q_shared.h"

#include <alias/ecs.h>

// define GAME_INCLUDE so that game.h does not define the
// short, server-visible gclient_t and edict_t structures,
// because we define the full size ones in this file
#define GAME_INCLUDE
#include "game.h"

// the "gameversion" client command will print this plus compile date
#define GAMEVERSION "baseq2"

// protocol bytes that can be directly added to messages
#define svc_muzzleflash 1
#define svc_muzzleflash2 2
#define svc_temp_entity 3
#define svc_layout 4
#define svc_inventory 5
#define svc_stufftext 11

//==================================================================

// view pitching times
#define DAMAGE_TIME 0.5
#define FALL_TIME 0.3

// edict->spawnflags
// these are set with checkboxes on each entity in the map editor
#define SPAWNFLAG_NOT_EASY 0x00000100
#define SPAWNFLAG_NOT_MEDIUM 0x00000200
#define SPAWNFLAG_NOT_HARD 0x00000400
#define SPAWNFLAG_NOT_DEATHMATCH 0x00000800
#define SPAWNFLAG_NOT_COOP 0x00001000

// edict->flags
#define FL_FLY 0x00000001
#define FL_SWIM 0x00000002 // implied immunity to drowining
#define FL_IMMUNE_LASER 0x00000004
#define FL_INWATER 0x00000008
#define FL_GODMODE 0x00000010
#define FL_NOTARGET 0x00000020
#define FL_IMMUNE_SLIME 0x00000040
#define FL_IMMUNE_LAVA 0x00000080
#define FL_PARTIALGROUND 0x00000100 // not all corners are valid
#define FL_WATERJUMP 0x00000200     // player jumping out of water
#define FL_TEAMSLAVE 0x00000400     // not the first on the team
#define FL_NO_KNOCKBACK 0x00000800
#define FL_POWER_ARMOR 0x00001000 // power armor (if any) is active
#define FL_RESPAWN 0x80000000     // used for item respawning

#define FRAMETIME 0.1

// memory tags to allow dynamic memory to be cleaned up
#define TAG_GAME 765  // clear when unloading the dll
#define TAG_LEVEL 766 // clear when loading a new level

#define MELEE_DISTANCE 80

#define BODY_QUEUE_SIZE 8

typedef enum {
  DAMAGE_NO,
  DAMAGE_YES, // will take damage if hit
  DAMAGE_AIM  // auto targeting recognizes this
} damage_t;

typedef enum { WEAPON_READY, WEAPON_ACTIVATING, WEAPON_DROPPING, WEAPON_FIRING } weaponstate_t;

typedef enum { AMMO_BULLETS, AMMO_SHELLS, AMMO_ROCKETS, AMMO_GRENADES, AMMO_CELLS, AMMO_SLUGS } ammo_t;

// deadflag
#define DEAD_NO 0
#define DEAD_DYING 1
#define DEAD_DEAD 2
#define DEAD_RESPAWNABLE 3

// range
#define RANGE_MELEE 0
#define RANGE_NEAR 1
#define RANGE_MID 2
#define RANGE_FAR 3

// gib types
#define GIB_ORGANIC 0
#define GIB_METALLIC 1

// monster ai flags
#define AI_STAND_GROUND 0x00000001
#define AI_TEMP_STAND_GROUND 0x00000002
#define AI_SOUND_TARGET 0x00000004
#define AI_LOST_SIGHT 0x00000008
#define AI_PURSUIT_LAST_SEEN 0x00000010
#define AI_PURSUE_NEXT 0x00000020
#define AI_PURSUE_TEMP 0x00000040
#define AI_HOLD_FRAME 0x00000080
#define AI_GOOD_GUY 0x00000100
#define AI_BRUTAL 0x00000200
#define AI_NOSTEP 0x00000400
#define AI_DUCKED 0x00000800
#define AI_COMBAT_POINT 0x00001000
#define AI_MEDIC 0x00002000
#define AI_RESURRECTING 0x00004000

// monster attack state
#define AS_STRAIGHT 1
#define AS_SLIDING 2
#define AS_MELEE 3
#define AS_MISSILE 4

// armor types
#define ARMOR_NONE 0
#define ARMOR_JACKET 1
#define ARMOR_COMBAT 2
#define ARMOR_BODY 3
#define ARMOR_SHARD 4

// power armor types
#define POWER_ARMOR_NONE 0
#define POWER_ARMOR_SCREEN 1
#define POWER_ARMOR_SHIELD 2

// handedness values
#define RIGHT_HANDED 0
#define LEFT_HANDED 1
#define CENTER_HANDED 2

// game.serverflags values
#define SFL_CROSS_TRIGGER_1 0x00000001
#define SFL_CROSS_TRIGGER_2 0x00000002
#define SFL_CROSS_TRIGGER_3 0x00000004
#define SFL_CROSS_TRIGGER_4 0x00000008
#define SFL_CROSS_TRIGGER_5 0x00000010
#define SFL_CROSS_TRIGGER_6 0x00000020
#define SFL_CROSS_TRIGGER_7 0x00000040
#define SFL_CROSS_TRIGGER_8 0x00000080
#define SFL_CROSS_TRIGGER_MASK 0x000000ff

// noise types for PlayerNoise
#define PNOISE_SELF 0
#define PNOISE_WEAPON 1
#define PNOISE_IMPACT 2

// edict->movetype values
typedef enum {
  MOVETYPE_NONE,   // never moves
  MOVETYPE_NOCLIP, // origin and angles change with no interaction
  MOVETYPE_PUSH,   // no clip to world, push on box contact
  MOVETYPE_STOP,   // no clip to world, stops on box contact

  MOVETYPE_WALK, // gravity
  MOVETYPE_STEP, // gravity, special edge handling
  MOVETYPE_FLY,
  MOVETYPE_TOSS,       // gravity
  MOVETYPE_FLYMISSILE, // extra size to monsters
  MOVETYPE_BOUNCE
} movetype_t;

typedef struct {
  int base_count;
  int max_count;
  float normal_protection;
  float energy_protection;
  int armor;
} gitem_armor_t;

// gitem_t->flags
#define IT_WEAPON 1 // use makes active weapon
#define IT_AMMO 2
#define IT_ARMOR 4
#define IT_STAY_COOP 8
#define IT_KEY 16
#define IT_POWERUP 32

// gitem_t->weapmodel for weapons indicates model index
#define WEAP_BLASTER 1
#define WEAP_SHOTGUN 2
#define WEAP_SUPERSHOTGUN 3
#define WEAP_MACHINEGUN 4
#define WEAP_CHAINGUN 5
#define WEAP_GRENADES 6
#define WEAP_GRENADELAUNCHER 7
#define WEAP_ROCKETLAUNCHER 8
#define WEAP_HYPERBLASTER 9
#define WEAP_RAILGUN 10
#define WEAP_BFG 11

typedef struct {
  float value;
  float minimum_time; // time since last at minimum
  float maximum_time; // time since last at maximum
} DynamicValue;

static inline void dv_set(DynamicValue *dv, float value) {
  memset(dv, 0, sizeof(*dv));
  dv->value = value;
}

static inline void dv_adjust(DynamicValue *dv, float time, float amount, float maximum) {
  bool pre_at_min = dv->value <= 0.0f;
  bool pre_at_max = dv->value >= maximum;
  dv->value += amount;
  if(dv->value <= 0.0f) {
    if(!pre_at_min) {
      dv->minimum_time = time;
    }
    // Quake 2 logic is not yet ready
    // dv->value = 0.0f;
  }
  if(dv->value >= maximum) {
    if(!pre_at_max) {
      dv->maximum_time = time;
    }
    // Quake 2 logic is not yet ready
    // dv->value = maximum;
  }
}

typedef struct {
  float a;
  float b;
  float c;
  float d;
  union {
    uint32_t ru;
    float rf;
  };
} ComputedValue;

static inline void cv_initialize(ComputedValue *cv, float base) {
  memset(cv, 0, sizeof(*cv));
  cv->a = base;
  cv->d = 1.0;
}

static inline void cv_add(ComputedValue *cv, float amount) {
  cv->b += amount;
  cv->ru = UINT32_MAX;
}

static inline void cv_increase(ComputedValue *cv, float amount) {
  cv->c += amount;
  cv->ru = UINT32_MAX;
}

static inline void cv_more(ComputedValue *cv, float amount) {
  cv->d *= (amount / 100.0f);
  cv->ru = UINT32_MAX;
}
static inline void cv_less(ComputedValue *cv, float amount) {
  cv->d /= (100.f / amount);
  cv->ru = UINT32_MAX;
}

static inline float cv_get(const ComputedValue *cv) {
  if(cv->ru == UINT32_MAX) {
    *(float *)&cv->rf = (cv->a + cv->b) * (1.0f + cv->c) * cv->d;
  }
  return cv->rf;
}

typedef struct gitem_s {
  char *classname; // spawning name
  bool (*pickup)(struct edict_s *ent, struct edict_s *other);
  void (*use)(struct edict_s *ent, struct gitem_s *item);
  void (*drop)(struct edict_s *ent, struct gitem_s *item);
  void (*weaponthink)(struct edict_s *ent);
  char *pickup_sound;
  char *world_model;
  int world_model_flags;
  char *view_model;

  // client side info
  char *icon;
  char *pickup_name; // for printing on pickup
  int count_width;   // number of digits to display by icon

  int quantity; // for ammo how much, for weapons how much is used per shot
  char *ammo;   // for weapons
  int flags;    // IT_* flags

  int weapmodel; // weapon model index (for weapons)

  void *info;
  int tag;

  char *precaches; // string of all models, sounds, and images this item will use
} gitem_t;

//
// this structure is left intact through an entire game
// it should be initialized at dll load time, and read/written to
// the server.ssv file for savegames
//
typedef struct {
  char helpmessage1[512];
  char helpmessage2[512];
  int helpchanged; // flash F1 icon if non 0, play sound
                   // and increment only if 1, 2, or 3

  gclient_t *clients; // [maxclients]

  // can't store spawnpoint in level, because
  // it would get overwritten by the savegame restore
  char spawnpoint[512]; // needed for coop respawns

  // store latched cvars here that we want to get at often
  int maxclients;
  int maxentities;

  // cross level triggers
  int serverflags;

  // items
  int num_items;

  bool autosaved;
} game_locals_t;

//
// this structure is cleared as each map is entered
// it is read/written to the level.sav file for savegames
//
typedef struct {
  int framenum;
  float time;

  char level_name[MAX_QPATH]; // the descriptive name (Outer Base, etc)
  char mapname[MAX_QPATH];    // the server name (base1, etc)
  char nextmap[MAX_QPATH];    // go here when fraglimit is hit

  // intermission state
  float intermissiontime; // time the intermission was started
  char *changemap;
  int exitintermission;
  vec3_t intermission_origin;
  vec3_t intermission_angle;

  edict_t *sight_client; // changed once each frame for coop games

  edict_t *sight_entity;
  int sight_entity_framenum;
  edict_t *sound_entity;
  int sound_entity_framenum;
  edict_t *sound2_entity;
  int sound2_entity_framenum;

  int pic_health;

  int total_secrets;
  int found_secrets;

  int total_goals;
  int found_goals;

  int total_monsters;
  int killed_monsters;

  edict_t *current_entity; // entity running from G_RunFrame
  int body_que;            // dead bodies

  int power_cubes; // ugly necessity for coop
} level_locals_t;

// spawn_temp_t is only used to hold entity field values that
// can be set from the editor, but aren't actualy present
// in edict_t during gameplay
typedef struct {
  // world vars
  char *sky;
  float skyrotate;
  vec3_t skyaxis;
  char *nextmap;

  int lip;
  int distance;
  int height;
  char *noise;
  float pausetime;
  char *item;
  char *gravity;

  float minyaw;
  float maxyaw;
  float minpitch;
  float maxpitch;
} spawn_temp_t;

typedef struct {
  // fixed data
  vec3_t start_origin;
  vec3_t start_angles;
  vec3_t end_origin;
  vec3_t end_angles;

  int sound_start;
  int sound_middle;
  int sound_end;

  float accel;
  float speed;
  float decel;
  float distance;

  float wait;

  // state data
  int state;
  vec3_t dir;
  float current_speed;
  float move_speed;
  float next_speed;
  float remaining_distance;
  float decel_distance;
  void (*endfunc)(edict_t *);
} moveinfo_t;

typedef struct {
  void (*aifunc)(edict_t *self, float dist);
  float dist;
  void (*thinkfunc)(edict_t *self);
} mframe_t;

typedef struct {
  int firstframe;
  int lastframe;
  mframe_t *frame;
  void (*endfunc)(edict_t *self);
} mmove_t;

typedef struct {
  mmove_t *currentmove;
  int aiflags;
  int nextframe;
  float scale;

  void (*stand)(edict_t *self);
  void (*idle)(edict_t *self);
  void (*search)(edict_t *self);
  void (*walk)(edict_t *self);
  void (*run)(edict_t *self);
  void (*dodge)(edict_t *self, edict_t *other, float eta);
  void (*attack)(edict_t *self);
  void (*melee)(edict_t *self);
  void (*sight)(edict_t *self, edict_t *other);
  bool (*checkattack)(edict_t *self);

  float pausetime;
  float attack_finished;

  vec3_t saved_goal;
  float search_time;
  float trail_time;
  vec3_t last_sighting;
  int attack_state;
  int lefty;
  float idle_time;
  int linkcount;

  int power_armor_type;
  int power_armor_power;
} monsterinfo_t;

extern game_locals_t game;
extern level_locals_t level;
extern game_import_t gi;
extern game_export_t globals;
extern spawn_temp_t st;

extern int sm_meat_index;
extern int snd_fry;

extern int jacket_armor_index;
extern int combat_armor_index;
extern int body_armor_index;

// means of death
#define MOD_UNKNOWN 0
#define MOD_BLASTER 1
#define MOD_SHOTGUN 2
#define MOD_SSHOTGUN 3
#define MOD_MACHINEGUN 4
#define MOD_CHAINGUN 5
#define MOD_GRENADE 6
#define MOD_G_SPLASH 7
#define MOD_ROCKET 8
#define MOD_R_SPLASH 9
#define MOD_HYPERBLASTER 10
#define MOD_RAILGUN 11
#define MOD_BFG_LASER 12
#define MOD_BFG_BLAST 13
#define MOD_BFG_EFFECT 14
#define MOD_HANDGRENADE 15
#define MOD_HG_SPLASH 16
#define MOD_WATER 17
#define MOD_SLIME 18
#define MOD_LAVA 19
#define MOD_CRUSH 20
#define MOD_TELEFRAG 21
#define MOD_FALLING 22
#define MOD_SUICIDE 23
#define MOD_HELD_GRENADE 24
#define MOD_EXPLOSIVE 25
#define MOD_BARREL 26
#define MOD_BOMB 27
#define MOD_EXIT 28
#define MOD_SPLASH 29
#define MOD_TARGET_LASER 30
#define MOD_TRIGGER_HURT 31
#define MOD_HIT 32
#define MOD_TARGET_BLASTER 33
#define MOD_FRIENDLY_FIRE 0x8000000

extern int meansOfDeath;

extern edict_t *g_edicts;

#define FOFS(x) (uintptr_t) & (((edict_t *)0)->x)
#define STOFS(x) (uintptr_t) & (((spawn_temp_t *)0)->x)
#define LLOFS(x) (uintptr_t) & (((level_locals_t *)0)->x)
#define CLOFS(x) (uintptr_t) & (((gclient_t *)0)->x)

#define random() ((rand() & 0x7fff) / ((float)0x7fff))
#define crandom() (2.0 * (random() - 0.5))

extern cvar_t *maxentities;
extern cvar_t *deathmatch;
extern cvar_t *coop;
extern cvar_t *dmflags;
extern cvar_t *skill;
extern cvar_t *fraglimit;
extern cvar_t *timelimit;
extern cvar_t *password;
extern cvar_t *spectator_password;
extern cvar_t *g_select_empty;
extern cvar_t *dedicated;

extern cvar_t *filterban;

extern cvar_t *sv_gravity;
extern cvar_t *sv_maxvelocity;

extern cvar_t *gun_x, *gun_y, *gun_z;
extern cvar_t *sv_rollspeed;
extern cvar_t *sv_rollangle;

extern cvar_t *run_pitch;
extern cvar_t *run_roll;
extern cvar_t *bob_up;
extern cvar_t *bob_pitch;
extern cvar_t *bob_roll;

extern cvar_t *sv_cheats;
extern cvar_t *maxclients;
extern cvar_t *maxspectators;

extern cvar_t *flood_msgs;
extern cvar_t *flood_persecond;
extern cvar_t *flood_waitdelay;

extern cvar_t *sv_maplist;

#define world (&g_edicts[0])

// item spawnflags
#define ITEM_TRIGGER_SPAWN 0x00000001
#define ITEM_NO_TOUCH 0x00000002
// 6 bits reserved for editor flags
// 8 bits used as power cube id bits for coop games
#define DROPPED_ITEM 0x00010000
#define DROPPED_PLAYER_ITEM 0x00020000
#define ITEM_TARGETS_USED 0x00040000

//
// fields are needed for spawning from the entity string
// and saving / loading games
//
#define FFL_SPAWNTEMP 1
#define FFL_NOSPAWN 2

typedef enum field_type {
  F_INT,
  F_FLOAT,
  F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL
  F_GSTRING, // string on disk, pointer in memory, TAG_GAME
  F_VECTOR,
  F_ANGLEHACK,
  F_EDICT,  // index on disk, pointer in memory
  F_ITEM,   // index on disk, pointer in memory
  F_CLIENT, // index on disk, pointer in memory
  F_FUNCTION,
  F_MMOVE,
  F_IGNORE
} fieldtype_t;

struct field_value {
  enum field_type type;
  union {
    intmax_t integer;
    double floating;
    char *string;
    vec3_t vector;
    edict_t *edict;
    gitem_t *item;
    gclient_t *client;
    void *function;
    int mmove;
  };
};

typedef struct {
  char *name;
  // int ofs;
  fieldtype_t type;
  int flags;

  void (*set)(edict_t *ent, struct field_value value);
  struct field_value (*get)(const edict_t *ent);

  void (*set_temp)(spawn_temp_t *ent, struct field_value value);
} field_t;

extern field_t fields[];
extern gitem_t itemlist[];

//
// g_cmds.c
//
void Cmd_Help_f(edict_t *ent);
void Cmd_Score_f(edict_t *ent);

//
// g_items.c
//
void PrecacheItem(gitem_t *it);
void InitItems(void);
void SetItemNames(void);
gitem_t *FindItem(char *pickup_name);
gitem_t *FindItemByClassname(char *classname);
#define ITEM_INDEX(x) ((x)-itemlist)
edict_t *Drop_Item(edict_t *ent, gitem_t *item);
void SetRespawn(edict_t *ent, float delay);
void ChangeWeapon(edict_t *ent);
void SpawnItem(edict_t *ent, gitem_t *item);
void Think_Weapon(edict_t *ent);
int ArmorIndex(edict_t *ent);
int PowerArmorType(edict_t *ent);
gitem_t *GetItemByIndex(int index);
bool Add_Ammo(edict_t *ent, gitem_t *item, int count);
void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);

//
// g_utils.c
//
bool KillBox(edict_t *ent);
void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
edict_t *G_Find(int cmodel_index, edict_t *from, int fieldofs, char *match);
edict_t *findradius(edict_t *from, vec3_t org, float rad);
edict_t *G_PickTarget(int cmodel_index, char *targetname);
void G_UseTargets(edict_t *ent, edict_t *activator);
void G_SetMovedir(vec3_t angles, vec3_t movedir);

void G_InitEdict(int cmodel_index, edict_t *e);
edict_t *G_Spawn(int cmodel_index);
void G_FreeEdict(edict_t *e);

void G_TouchTriggers(edict_t *ent);
void G_TouchSolids(edict_t *ent);

char *G_CopyString(char *in);

float *tv(float x, float y, float z);
char *vtos(vec3_t v);

float vectoyaw(vec3_t vec);
void vectoangles(vec3_t vec, vec3_t angles);

//
// g_combat.c
//
bool OnSameTeam(edict_t *ent1, edict_t *ent2);
bool CanDamage(edict_t *targ, edict_t *inflictor);
void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage,
              int knockback, int dflags, int mod);
void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod);

// damage flags
#define DAMAGE_RADIUS 0x00000001 // damage was indirect
#define DAMAGE_NO_ARMOR 0x00000002 // armour does not protect from this damage
#define DAMAGE_ENERGY 0x00000004 // damage is from an energy based weapon
#define DAMAGE_NO_KNOCKBACK 0x00000008 // do not affect velocity, just view angles
#define DAMAGE_BULLET 0x00000010 // damage is from a bullet (used for ricochets)
#define DAMAGE_NO_PROTECTION 0x00000020 // armor, shields, invulnerability, and godmode have no effect

#define DEFAULT_BULLET_HSPREAD 300
#define DEFAULT_BULLET_VSPREAD 500
#define DEFAULT_SHOTGUN_HSPREAD 1000
#define DEFAULT_SHOTGUN_VSPREAD 500
#define DEFAULT_DEATHMATCH_SHOTGUN_COUNT 12
#define DEFAULT_SHOTGUN_COUNT 12
#define DEFAULT_SSHOTGUN_COUNT 20

//
// g_monster.c
//
void monster_fire_bullet(edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread,
                         int flashtype);
void monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread,
                          int count, int flashtype);
void monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect);
void monster_fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype);
void monster_fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype);
void monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype);
void monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius,
                      int flashtype);
void M_droptofloor(edict_t *ent);
void monster_think(edict_t *self);
void walkmonster_start(edict_t *self);
void swimmonster_start(edict_t *self);
void flymonster_start(edict_t *self);
void AttackFinished(edict_t *self, float time);
void monster_death_use(edict_t *self);
void M_CatagorizePosition(edict_t *ent);
bool M_CheckAttack(edict_t *self);
void M_FlyCheck(edict_t *self);
void M_CheckGround(edict_t *ent);

//
// g_misc.c
//
void ThrowHead(edict_t *self, char *gibname, int damage, int type);
void ThrowClientHead(edict_t *self, int damage);
void ThrowGib(edict_t *self, char *gibname, int damage, int type);
void BecomeExplosion1(edict_t *self);

//
// g_ai.c
//
void AI_SetSightClient(void);

void ai_stand(edict_t *self, float dist);
void ai_move(edict_t *self, float dist);
void ai_walk(edict_t *self, float dist);
void ai_turn(edict_t *self, float dist);
void ai_run(edict_t *self, float dist);
void ai_charge(edict_t *self, float dist);
int range(edict_t *self, edict_t *other);

void FoundTarget(edict_t *self);
bool infront(edict_t *self, edict_t *other);
bool visible(edict_t *self, edict_t *other);
bool FacingIdeal(edict_t *self);

//
// g_weapon.c
//
void ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin);
bool fire_hit(edict_t *self, vec3_t aim, int damage, int kick);
void fire_bullet(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod);
void fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count,
                  int mod);
void fire_blaster(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int effect, bool hyper);
void fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius);
void fire_grenade2(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius,
                   bool held);
void fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius,
                 int radius_damage);
void fire_rail(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick);
void fire_bfg(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius);

//
// g_ptrail.c
//
void PlayerTrail_Init(void);
void PlayerTrail_Add(vec3_t spot);
void PlayerTrail_New(vec3_t spot);
edict_t *PlayerTrail_PickFirst(edict_t *self);
edict_t *PlayerTrail_PickNext(edict_t *self);
edict_t *PlayerTrail_LastSpot(void);

//
// g_client.c
//
void respawn(edict_t *ent);
void BeginIntermission(edict_t *targ);
void PutClientInServer(edict_t *ent);
void InitClientPersistant(gclient_t *client);
void InitClientResp(gclient_t *client);
void InitBodyQue(void);
void ClientBeginServerFrame(edict_t *ent);

//
// g_player.c
//
void player_pain(edict_t *self, edict_t *other, float kick, int damage);
void player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);

//
// g_svcmds.c
//
void ServerCommand(void);
bool SV_FilterPacket(char *from);

//
// p_view.c
//
void ClientEndServerFrame(edict_t *ent);

//
// p_hud.c
//
void MoveClientToIntermission(edict_t *client);
void G_SetStats(edict_t *ent);
void G_SetSpectatorStats(edict_t *ent);
void G_CheckChaseStats(edict_t *ent);
void ValidateSelectedItem(edict_t *ent);
void DeathmatchScoreboardMessage(edict_t *client, edict_t *killer);

//
// g_pweapon.c
//
void PlayerNoise(edict_t *who, vec3_t where, int type);

//
// m_move.c
//
bool M_CheckBottom(edict_t *ent);
bool M_walkmove(edict_t *ent, float yaw, float dist);
void M_MoveToGoal(edict_t *ent, float dist);
void M_ChangeYaw(edict_t *ent);

//
// g_phys.c
//
void G_RunEntity(edict_t *ent);

//
// g_main.c
//
void SaveClientData(void);
void FetchClientEntData(edict_t *ent);

//
// g_chase.c
//
void UpdateChaseCam(edict_t *ent);
void ChaseNext(edict_t *ent);
void ChasePrev(edict_t *ent);
void GetChaseTarget(edict_t *ent);

//============================================================================

// client_t->anim_priority
#define ANIM_BASIC 0 // stand / run
#define ANIM_WAVE 1
#define ANIM_JUMP 2
#define ANIM_PAIN 3
#define ANIM_ATTACK 4
#define ANIM_DEATH 5
#define ANIM_REVERSE 6

// client data that stays across multiple level loads
typedef struct {
  char userinfo[MAX_INFO_STRING];
  char netname[16];
  int hand;

  bool connected; // a loadgame will leave valid entities that
                  // just don't have a connection yet

  // values saved and restored from edicts when changing levels
  int health;
  int max_health;
  int savedFlags;

  int selected_item;
  int inventory[MAX_ITEMS];

  // ammo capacities
  int max_bullets;
  int max_shells;
  int max_rockets;
  int max_grenades;
  int max_cells;
  int max_slugs;

  gitem_t *weapon;
  gitem_t *lastweapon;

  int power_cubes; // used for tracking the cubes in coop games
  int score;       // for calculating total unit score in coop games

  int game_helpchanged;
  int helpchanged;

  bool spectator; // client is a spectator
} client_persistant_t;

// client data that stays across deathmatch respawns
typedef struct {
  client_persistant_t coop_respawn; // what to set client->pers to on a respawn
  int enterframe;                   // level.framenum the client entered the game
  int score;                        // frags, etc
  vec3_t cmd_angles;                // angles sent over in the last command

  bool spectator; // client is a spectator
} client_respawn_t;

// this structure is cleared on each PutClientInServer(),
// except for 'client->pers'
struct gclient_s {
  // known to server
  player_state_t ps; // communicated by server to clients
  int ping;

  // private to game
  client_persistant_t pers;
  client_respawn_t resp;
  pmove_state_t old_pmove; // for detecting out-of-pmove changes

  bool showscores;    // set layout stat
  bool showinventory; // set layout stat
  bool showhelp;
  bool showhelpicon;

  int ammo_index;

  int buttons;
  int oldbuttons;
  int latched_buttons;

  bool weapon_thunk;

  gitem_t *newweapon;

  // sum up damage over an entire frame, so
  // shotgun blasts give a single big kick
  int damage_armor;     // damage absorbed by armor
  int damage_parmor;    // damage absorbed by power armor
  int damage_blood;     // damage taken out of health
  int damage_knockback; // impact damage
  vec3_t damage_from;   // origin for vector calculation

  float killer_yaw; // when dead, look at killer

  weaponstate_t weaponstate;
  vec3_t kick_angles; // weapon kicks
  vec3_t kick_origin;
  float v_dmg_roll, v_dmg_pitch, v_dmg_time; // damage kicks
  float fall_time, fall_value;               // for view drop on fall
  float damage_alpha;
  float bonus_alpha;
  vec3_t damage_blend;
  vec3_t v_angle; // aiming direction
  float bobtime;  // so off-ground doesn't change it
  vec3_t oldviewangles;
  vec3_t oldvelocity;

  float next_drown_time;
  int old_waterlevel;
  int breather_sound;

  int machinegun_shots; // for weapon raising

  // animation vars
  int anim_end;
  int anim_priority;
  bool anim_duck;
  bool anim_run;

  // powerup timers
  float quad_framenum;
  float invincible_framenum;
  float breather_framenum;
  float enviro_framenum;

  bool grenade_blew_up;
  float grenade_time;
  int silencer_shots;
  int weapon_sound;

  float pickup_msg_time;

  float flood_locktill; // locked from talking
  float flood_when[10]; // when messages were said
  int flood_whenhead;   // head pointer for when said

  float respawn_time; // can respawn when time > this

  edict_t *chase_target; // player we are chasing
  bool update_chase;     // need to update chase info?
};

struct edict_s {
  entity_state_t s;
  struct gclient_s *client; // NULL if not a player
                            // the server expects the first part
                            // of gclient_s to be a player_state_t
                            // but the rest of it is opaque

  bool inuse;
  int linkcount;

  // FIXME: move these fields to a server private sv_entity_t
  link_t area; // linked to a division node or leaf

  int num_clusters; // if -1, use headnode instead
  int clusternums[MAX_ENT_CLUSTERS];
  int headnode; // unused if num_clusters != -1
  int areanum, areanum2;

  //================================

  int svflags;
  vec3_t mins, maxs;
  vec3_t absmin, absmax, size;
  solid_t solid;
  int clipmask;
  edict_t *owner;

  // DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER
  // EXPECTS THE FIELDS IN THAT ORDER!

  //================================
  int movetype;
  int flags;

  char *model;
  float freetime; // sv.time when the object was freed

  //
  // only used locally in game, not by server
  //
  char *message;
  char *classname;
  int spawnflags;

  float timestamp;

  float angle; // set in qe3, -1 = up, -2 = down
  char *target;
  char *targetname;
  char *killtarget;
  char *team;
  char *pathtarget;
  char *deathtarget;
  char *combattarget;
  edict_t *target_ent;

  float speed, accel, decel;
  vec3_t movedir;
  vec3_t pos1, pos2;

  vec3_t velocity;
  vec3_t avelocity;
  int mass;
  float air_finished;
  float gravity; // per entity gravity multiplier (1.0 is normal)
                 // use for lowgrav artifact, flares

  edict_t *goalentity;
  edict_t *movetarget;
  float yaw_speed;
  float ideal_yaw;

  float nextthink;
  void (*prethink)(edict_t *ent);
  void (*think)(edict_t *self);
  void (*blocked)(edict_t *self, edict_t *other); // move to moveinfo?
  void (*touch)(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
  void (*use)(edict_t *self, edict_t *other, edict_t *activator);
  void (*pain)(edict_t *self, edict_t *other, float kick, int damage);
  void (*die)(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);

  float touch_debounce_time; // are all these legit?  do we need more/less of them?
  float pain_debounce_time;
  float damage_debounce_time;
  float fly_sound_debounce_time; // move to clientinfo
  float last_move_time;

  DynamicValue ___health;
  ComputedValue ___max_health;
  // int health;
  // int max_health;

  int gib_health;
  int deadflag;
  bool show_hostile;

  float powerarmor_time;

  char *map; // target_changelevel

  int viewheight; // height above origin where eyesight is determined
  int takedamage;
  int dmg;
  int radius_dmg;
  float dmg_radius;
  int sounds; // make this a spawntemp var?
  int count;

  edict_t *chain;
  edict_t *enemy;
  edict_t *oldenemy;
  edict_t *activator;
  edict_t *groundentity;
  int groundentity_linkcount;
  edict_t *teamchain;
  edict_t *teammaster;

  edict_t *mynoise; // can go in client only
  edict_t *mynoise2;

  int noise_index;
  float volume;
  float attenuation;

  // timing variables
  float wait;
  float delay; // before firing targets
  float random;

  float teleport_time;

  int watertype;
  int waterlevel;

  vec3_t move_origin;
  vec3_t move_angles;

  // move this to clientinfo?
  int light_level;

  int style; // also used as areaportal number

  // used by: g_items.c
  gitem_t *item;

  // used by: g_func.c, g_misc.c
  moveinfo_t moveinfo;

  // used by: g_ai.c, g_combat.c, g_misc.c, g_save.c, m_boss2.c, m_gladiator.c
  monsterinfo_t monsterinfo;

  // things will move into this
  alias_ecs_EntityHandle entity_handle;
};

static inline alias_ecs_Instance *GAME_ECS_INSTANCE(void) {
  extern alias_ecs_Instance *game_global_ecs_instance;
  if(game_global_ecs_instance == NULL) {
    alias_ecs_create_instance(NULL, &game_global_ecs_instance);
  }
  return game_global_ecs_instance;
}

static inline alias_ecs_LayerHandle GAME_MAIN_LAYER(int cmodel_index) {
  extern alias_ecs_LayerHandle game_global_ecs_main_layer[CMODEL_COUNT];
  if(game_global_ecs_main_layer[cmodel_index] == ALIAS_ECS_INVALID_LAYER) {
    alias_ecs_create_layer(GAME_ECS_INSTANCE(), &(alias_ecs_LayerCreateInfo){.max_entities = 0},
                           &game_global_ecs_main_layer[cmodel_index]);
  }
  return game_global_ecs_main_layer[cmodel_index];
}

static inline alias_ecs_ComponentHandle GAME_EDICT_COMPONENT_HANDLE(void) {
  extern alias_ecs_ComponentHandle game_global_ecs_edict_component;
  if(game_global_ecs_edict_component == 0) {
    alias_ecs_register_component(GAME_ECS_INSTANCE(), &(alias_ecs_ComponentCreateInfo){.size = sizeof(edict_t)},
                                 &game_global_ecs_edict_component);
  }
  return game_global_ecs_edict_component;
}

static inline const ComputedValue *ent_read_max_health(const edict_t *e) { return &e->___max_health; }
static inline ComputedValue *ent_write_max_health(edict_t *e) { return &e->___max_health; }

static inline const DynamicValue *ent_read_health(const edict_t *e) { return &e->___health; }
static inline DynamicValue *ent_write_health(edict_t *e) { return &e->___health; }