quake2-rerelease-dll/rerelease/g_local.h
2023-08-07 14:48:30 -05:00

3500 lines
No EOL
104 KiB
C++

// Copyright (c) ZeniMax Media Inc.
// Licensed under the GNU General Public License 2.0.
// g_local.h -- local definitions for game module
#pragma once
#include "bg_local.h"
// the "gameversion" client command will print this plus compile date
constexpr const char *GAMEVERSION = "baseq2";
//==================================================================
constexpr vec3_t PLAYER_MINS = { -16, -16, -24 };
constexpr vec3_t PLAYER_MAXS = { 16, 16, 32 };
#include <charconv>
template<typename T>
constexpr bool is_char_ptr_v = std::is_convertible_v<T, const char*>;
template<typename T>
constexpr bool is_valid_loc_embed_v = !std::is_null_pointer_v<T> && (std::is_floating_point_v<std::remove_reference_t<T>> || std::is_integral_v<std::remove_reference_t<T>> || is_char_ptr_v<T>);
struct local_game_import_t : game_import_t
{
inline local_game_import_t() = default;
inline local_game_import_t(const game_import_t &imports) :
game_import_t(imports)
{
}
private:
// shared buffer for wrappers below
static char print_buffer[0x10000];
public:
#ifdef USE_CPP20_FORMAT
template<typename... Args>
inline void Com_PrintFmt(std::format_string<Args...> format_str, Args &&... args)
#else
#define Com_PrintFmt(str, ...) \
Com_PrintFmt_(FMT_STRING(str), __VA_ARGS__)
template<typename S, typename... Args>
inline void Com_PrintFmt_(const S &format_str, Args &&... args)
#endif
{
G_FmtTo_(print_buffer, format_str, std::forward<Args>(args)...);
Com_Print(print_buffer);
}
#ifdef USE_CPP20_FORMAT
template<typename... Args>
inline void Com_ErrorFmt(std::format_string<Args...> format_str, Args &&... args)
#else
#define Com_ErrorFmt(str, ...) \
Com_ErrorFmt_(FMT_STRING(str), __VA_ARGS__)
template<typename S, typename... Args>
inline void Com_ErrorFmt_(const S &format_str, Args &&... args)
#endif
{
G_FmtTo_(print_buffer, format_str, std::forward<Args>(args)...);
Com_Error(print_buffer);
}
private:
// localized print functions
template<typename T>
inline void loc_embed(T input, char* buffer, const char*& output)
{
if constexpr (std::is_floating_point_v<T> || std::is_integral_v<T>)
{
auto result = std::to_chars(buffer, buffer + MAX_INFO_STRING - 1, input);
*result.ptr = '\0';
output = buffer;
}
else if constexpr (is_char_ptr_v<T>)
{
if (!input)
Com_Error("null const char ptr passed to loc");
output = input;
}
else
Com_Error("invalid loc argument");
}
static std::array<char[MAX_INFO_STRING], MAX_LOCALIZATION_ARGS> buffers;
static std::array<const char*, MAX_LOCALIZATION_ARGS> buffer_ptrs;
public:
template<typename... Args>
inline void LocClient_Print(edict_t* e, print_type_t level, const char* base, Args&& ...args)
{
static_assert(sizeof...(args) < MAX_LOCALIZATION_ARGS, "too many arguments to gi.LocClient_Print");
static_assert(((is_valid_loc_embed_v<Args>) && ...), "invalid argument passed to gi.LocClient_Print");
size_t n = 0;
((loc_embed(args, buffers[n], buffer_ptrs[n]), ++n), ...);
Loc_Print(e, level, base, &buffer_ptrs.front(), sizeof...(args));
}
template<typename... Args>
inline void LocBroadcast_Print(print_type_t level, const char* base, Args&& ...args)
{
static_assert(sizeof...(args) < MAX_LOCALIZATION_ARGS, "too many arguments to gi.LocBroadcast_Print");
static_assert(((is_valid_loc_embed_v<Args>) && ...), "invalid argument passed to gi.LocBroadcast_Print");
size_t n = 0;
((loc_embed(args, buffers[n], buffer_ptrs[n]), ++n), ...);
Loc_Print(nullptr, (print_type_t)(level | print_type_t::PRINT_BROADCAST), base, &buffer_ptrs.front(), sizeof...(args));
}
template<typename... Args>
inline void LocCenter_Print(edict_t* e, const char* base, Args&& ...args)
{
static_assert(sizeof...(args) < MAX_LOCALIZATION_ARGS, "too many arguments to gi.LocCenter_Print");
static_assert(((is_valid_loc_embed_v<Args>) && ...), "invalid argument passed to gi.LocCenter_Print");
size_t n = 0;
((loc_embed(args, buffers[n], buffer_ptrs[n]), ++n), ...);
Loc_Print(e, PRINT_CENTER, base, &buffer_ptrs.front(), sizeof...(args));
}
// collision detection
[[nodiscard]] inline trace_t trace(const vec3_t &start, const vec3_t &mins, const vec3_t &maxs, const vec3_t &end, const edict_t *passent, contents_t contentmask)
{
return game_import_t::trace(start, &mins, &maxs, end, passent, contentmask);
}
[[nodiscard]] inline trace_t traceline(const vec3_t &start, const vec3_t &end, const edict_t *passent, contents_t contentmask)
{
return game_import_t::trace(start, nullptr, nullptr, end, passent, contentmask);
}
// [Paril-KEX] clip the box against the specified entity
[[nodiscard]] inline trace_t clip(edict_t *entity, const vec3_t &start, const vec3_t &mins, const vec3_t &maxs, const vec3_t &end, contents_t contentmask)
{
return game_import_t::clip(entity, start, &mins, &maxs, end, contentmask);
}
[[nodiscard]] inline trace_t clip(edict_t *entity, const vec3_t &start, const vec3_t &end, contents_t contentmask)
{
return game_import_t::clip(entity, start, nullptr, nullptr, end, contentmask);
}
void unicast(edict_t *ent, bool reliable, uint32_t dupe_key = 0)
{
game_import_t::unicast(ent, reliable, dupe_key);
}
void local_sound(edict_t *target, const vec3_t &origin, edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs, uint32_t dupe_key = 0)
{
game_import_t::local_sound(target, &origin, ent, channel, soundindex, volume, attenuation, timeofs, dupe_key);
}
void local_sound(edict_t *target, edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs, uint32_t dupe_key = 0)
{
game_import_t::local_sound(target, nullptr, ent, channel, soundindex, volume, attenuation, timeofs, dupe_key);
}
void local_sound(const vec3_t &origin, edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs, uint32_t dupe_key = 0)
{
game_import_t::local_sound(ent, &origin, ent, channel, soundindex, volume, attenuation, timeofs, dupe_key);
}
void local_sound(edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs, uint32_t dupe_key = 0)
{
game_import_t::local_sound(ent, nullptr, ent, channel, soundindex, volume, attenuation, timeofs, dupe_key);
}
};
extern local_game_import_t gi;
// edict->spawnflags
// these are set with checkboxes on each entity in the map editor.
// the following 8 are reserved and should never be used by any entity.
// (power cubes in coop use these after spawning as well)
struct spawnflags_t
{
uint32_t value;
explicit constexpr spawnflags_t(uint32_t v) :
value(v)
{
}
explicit operator uint32_t() const
{
return value;
}
// has any flags at all (!!a)
constexpr bool any() const { return !!value; }
// has any of the given flags (!!(a & b))
constexpr bool has(const spawnflags_t &flags) const { return !!(value & flags.value); }
// has all of the given flags ((a & b) == b)
constexpr bool has_all(const spawnflags_t &flags) const { return (value & flags.value) == flags.value; }
constexpr bool operator!() const { return !value; }
constexpr bool operator==(const spawnflags_t &flags) const
{
return value == flags.value;
}
constexpr bool operator!=(const spawnflags_t &flags) const
{
return value != flags.value;
}
constexpr spawnflags_t operator~() const
{
return spawnflags_t(~value);
}
constexpr spawnflags_t operator|(const spawnflags_t &v2) const
{
return spawnflags_t(value | v2.value);
}
constexpr spawnflags_t operator&(const spawnflags_t &v2) const
{
return spawnflags_t(value & v2.value);
}
constexpr spawnflags_t operator^(const spawnflags_t &v2) const
{
return spawnflags_t(value ^ v2.value);
}
constexpr spawnflags_t &operator|=(const spawnflags_t &v2)
{
value |= v2.value;
return *this;
}
constexpr spawnflags_t &operator&=(const spawnflags_t &v2)
{
value &= v2.value;
return *this;
}
constexpr spawnflags_t &operator^=(const spawnflags_t &v2)
{
value ^= v2.value;
return *this;
}
};
// these spawnflags affect every entity. note that items are a bit special
// because these 8 bits are instead used for power cube bits.
constexpr spawnflags_t SPAWNFLAG_NONE = spawnflags_t(0);
constexpr spawnflags_t SPAWNFLAG_NOT_EASY = spawnflags_t(0x00000100),
SPAWNFLAG_NOT_MEDIUM = spawnflags_t(0x00000200),
SPAWNFLAG_NOT_HARD = spawnflags_t(0x00000400),
SPAWNFLAG_NOT_DEATHMATCH = spawnflags_t(0x00000800),
SPAWNFLAG_NOT_COOP = spawnflags_t(0x00001000),
SPAWNFLAG_RESERVED1 = spawnflags_t(0x00002000),
SPAWNFLAG_COOP_ONLY = spawnflags_t(0x00004000),
SPAWNFLAG_RESERVED2 = spawnflags_t(0x00008000);
constexpr spawnflags_t SPAWNFLAG_EDITOR_MASK = (SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_DEATHMATCH |
SPAWNFLAG_NOT_COOP | SPAWNFLAG_RESERVED1 | SPAWNFLAG_COOP_ONLY | SPAWNFLAG_RESERVED2);
// use this for global spawnflags
constexpr spawnflags_t operator "" _spawnflag(unsigned long long int v)
{
if (v & SPAWNFLAG_EDITOR_MASK.value)
throw std::invalid_argument("attempting to use reserved spawnflag");
return static_cast<spawnflags_t>(static_cast<uint32_t>(v));
}
// use this for global spawnflags
constexpr spawnflags_t operator "" _spawnflag_bit(unsigned long long int v)
{
v = 1ull << v;
if (v & SPAWNFLAG_EDITOR_MASK.value)
throw std::invalid_argument("attempting to use reserved spawnflag");
return static_cast<spawnflags_t>(static_cast<uint32_t>(v));
}
// stores a level time; most newer engines use int64_t for
// time storage, but seconds are handy for compatibility
// with Quake and older mods.
struct gtime_t
{
private:
// times always start at zero, just to prevent memory issues
int64_t _ms = 0;
// internal; use _sec/_ms/_min or gtime_t::from_sec(n)/gtime_t::from_ms(n)/gtime_t::from_min(n)
constexpr explicit gtime_t(const int64_t &ms) : _ms(ms)
{
}
public:
constexpr gtime_t() = default;
constexpr gtime_t(const gtime_t &) = default;
constexpr gtime_t &operator=(const gtime_t &) = default;
// constructors are here, explicitly named, so you always
// know what you're getting.
// new time from ms
static constexpr gtime_t from_ms(const int64_t &ms)
{
return gtime_t(ms);
}
// new time from seconds
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
static constexpr gtime_t from_sec(const T &s)
{
return gtime_t(static_cast<int64_t>(s * 1000));
}
// new time from minutes
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
static constexpr gtime_t from_min(const T &s)
{
return gtime_t(static_cast<int64_t>(s * 60000));
}
// new time from hz
static constexpr gtime_t from_hz(const uint64_t &hz)
{
return from_ms(static_cast<int64_t>((1.0 / hz) * 1000));
}
// get value in minutes (truncated for integers)
template<typename T = float>
constexpr T minutes() const
{
return static_cast<T>(_ms / static_cast<std::conditional_t<std::is_floating_point_v<T>, T, float>>(60000));
}
// get value in seconds (truncated for integers)
template<typename T = float>
constexpr T seconds() const
{
return static_cast<T>(_ms / static_cast<std::conditional_t<std::is_floating_point_v<T>, T, float>>(1000));
}
// get value in milliseconds
constexpr const int64_t &milliseconds() const
{
return _ms;
}
int64_t frames() const
{
return _ms / gi.frame_time_ms;
}
// check if non-zero
constexpr explicit operator bool() const
{
return !!_ms;
}
// invert time
constexpr gtime_t operator-() const
{
return gtime_t(-_ms);
}
// operations with other times as input
constexpr gtime_t operator+(const gtime_t &r) const
{
return gtime_t(_ms + r._ms);
}
constexpr gtime_t operator-(const gtime_t &r) const
{
return gtime_t(_ms - r._ms);
}
constexpr gtime_t &operator+=(const gtime_t &r)
{
return *this = *this + r;
}
constexpr gtime_t &operator-=(const gtime_t &r)
{
return *this = *this - r;
}
// operations with scalars as input
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
constexpr gtime_t operator*(const T &r) const
{
return gtime_t::from_ms(static_cast<int64_t>(_ms * r));
}
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
constexpr gtime_t operator/(const T &r) const
{
return gtime_t::from_ms(static_cast<int64_t>(_ms / r));
}
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
constexpr gtime_t &operator*=(const T &r)
{
return *this = *this * r;
}
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
constexpr gtime_t &operator/=(const T &r)
{
return *this = *this / r;
}
// comparisons with gtime_ts
constexpr bool operator==(const gtime_t &time) const
{
return _ms == time._ms;
}
constexpr bool operator!=(const gtime_t &time) const
{
return _ms != time._ms;
}
constexpr bool operator<(const gtime_t &time) const
{
return _ms < time._ms;
}
constexpr bool operator>(const gtime_t &time) const
{
return _ms > time._ms;
}
constexpr bool operator<=(const gtime_t &time) const
{
return _ms <= time._ms;
}
constexpr bool operator>=(const gtime_t &time) const
{
return _ms >= time._ms;
}
};
// user literals, allowing you to specify times
// as 128_sec and 128_ms
constexpr gtime_t operator"" _min(long double s)
{
return gtime_t::from_min(s);
}
constexpr gtime_t operator"" _min(unsigned long long int s)
{
return gtime_t::from_min(s);
}
constexpr gtime_t operator"" _sec(long double s)
{
return gtime_t::from_sec(s);
}
constexpr gtime_t operator"" _sec(unsigned long long int s)
{
return gtime_t::from_sec(s);
}
constexpr gtime_t operator"" _ms(long double s)
{
return gtime_t::from_ms(static_cast<int64_t>(s));
}
constexpr gtime_t operator"" _ms(unsigned long long int s)
{
return gtime_t::from_ms(static_cast<int64_t>(s));
}
constexpr gtime_t operator"" _hz(unsigned long long int s)
{
return gtime_t::from_ms(static_cast<int64_t>((1.0 / s) * 1000));
}
#define SERVER_TICK_RATE gi.tick_rate // in hz
extern gtime_t FRAME_TIME_S;
extern gtime_t FRAME_TIME_MS;
// view pitching times
inline gtime_t DAMAGE_TIME_SLACK()
{
return (100_ms - FRAME_TIME_MS);
}
inline gtime_t DAMAGE_TIME()
{
return 500_ms + DAMAGE_TIME_SLACK();
}
inline gtime_t FALL_TIME()
{
return 300_ms + DAMAGE_TIME_SLACK();
}
// every save_data_list_t has a tag
// which is used for integrity checks.
enum save_data_tag_t
{
SAVE_DATA_MMOVE,
SAVE_FUNC_MONSTERINFO_STAND,
SAVE_FUNC_MONSTERINFO_IDLE,
SAVE_FUNC_MONSTERINFO_SEARCH,
SAVE_FUNC_MONSTERINFO_WALK,
SAVE_FUNC_MONSTERINFO_RUN,
SAVE_FUNC_MONSTERINFO_DODGE,
SAVE_FUNC_MONSTERINFO_ATTACK,
SAVE_FUNC_MONSTERINFO_MELEE,
SAVE_FUNC_MONSTERINFO_SIGHT,
SAVE_FUNC_MONSTERINFO_CHECKATTACK,
SAVE_FUNC_MONSTERINFO_SETSKIN,
SAVE_FUNC_MONSTERINFO_BLOCKED,
SAVE_FUNC_MONSTERINFO_DUCK,
SAVE_FUNC_MONSTERINFO_UNDUCK,
SAVE_FUNC_MONSTERINFO_SIDESTEP,
SAVE_FUNC_MONSTERINFO_PHYSCHANGED,
SAVE_FUNC_MOVEINFO_ENDFUNC,
SAVE_FUNC_MOVEINFO_BLOCKED,
SAVE_FUNC_PRETHINK,
SAVE_FUNC_THINK,
SAVE_FUNC_TOUCH,
SAVE_FUNC_USE,
SAVE_FUNC_PAIN,
SAVE_FUNC_DIE
};
// forward-linked list, storing data for
// saving pointers. every save_data_ptr has an
// instance of this; there's one head instance of this
// in g_save.cpp.
struct save_data_list_t
{
const char *name; // name of savable object; persisted in the JSON file
save_data_tag_t tag;
const void *ptr; // pointer to raw data
const save_data_list_t *next; // next in list
save_data_list_t(const char *name, save_data_tag_t tag, const void *ptr);
static const save_data_list_t *fetch(const void *link_ptr, save_data_tag_t tag);
};
#include <functional>
// save data wrapper, which holds a pointer to a T
// and the tag value for integrity. this is how you
// store a savable pointer of data safely.
template<typename T, size_t Tag>
struct save_data_t
{
using value_type = typename std::conditional<std::is_pointer<T>::value &&
std::is_function<typename std::remove_pointer<T>::type>::value,
T,
const T *>::type;
private:
value_type value;
const save_data_list_t *list;
public:
constexpr save_data_t() :
value(nullptr),
list(nullptr)
{
}
constexpr save_data_t(nullptr_t) :
save_data_t()
{
}
constexpr save_data_t(const save_data_list_t *list_in) :
value(list_in->ptr),
list(list_in)
{
}
inline save_data_t(value_type ptr_in) :
value(ptr_in),
list(ptr_in ? save_data_list_t::fetch(reinterpret_cast<const void *>(ptr_in), static_cast<save_data_tag_t>(Tag)) : nullptr)
{
}
inline save_data_t(const save_data_t<T, Tag> &ref_in) :
save_data_t(ref_in.value)
{
}
inline save_data_t &operator=(value_type ptr_in)
{
if (value != ptr_in)
{
value = ptr_in;
list = value ? save_data_list_t::fetch(reinterpret_cast<const void *>(ptr_in), static_cast<save_data_tag_t>(Tag)) : nullptr;
}
return *this;
}
constexpr const value_type pointer() const { return value; }
constexpr const save_data_list_t *save_list() const { return list; }
constexpr const char *name() const { return value ? list->name : "null"; }
constexpr const value_type operator->() const { return value; }
constexpr explicit operator bool() const { return value; }
constexpr bool operator==(value_type ptr_in) const { return value == ptr_in; }
constexpr bool operator!=(value_type ptr_in) const { return value != ptr_in; }
constexpr bool operator==(const save_data_t<T, Tag> *ptr_in) const { return value == ptr_in->value; }
constexpr bool operator==(const save_data_t<T, Tag> &ref_in) const { return value == ref_in.value; }
constexpr bool operator!=(const save_data_t<T, Tag> *ptr_in) const { return value != ptr_in->value; }
constexpr bool operator!=(const save_data_t<T, Tag> &ref_in) const { return value != ref_in.value; }
// invoke wrapper, for function-likes
template<typename... Args>
inline auto operator()(Args&& ...args) const
{
static_assert(std::is_invocable_v<std::remove_pointer_t<T>, Args...>, "bad invoke args");
return std::invoke(value, std::forward<Args>(args)...);
}
};
// memory tags to allow dynamic memory to be cleaned up
enum
{
TAG_GAME = 765, // clear when unloading the dll
TAG_LEVEL = 766 // clear when loading a new level
};
constexpr float MELEE_DISTANCE = 50;
constexpr size_t BODY_QUEUE_SIZE = 8;
// null trace used when touches don't need a trace
constexpr trace_t null_trace {};
enum weaponstate_t
{
WEAPON_READY,
WEAPON_ACTIVATING,
WEAPON_DROPPING,
WEAPON_FIRING
};
// gib flags
enum gib_type_t
{
GIB_NONE = 0, // no flags (organic)
GIB_METALLIC = 1, // bouncier
GIB_ACID = 2, // acidic (gekk)
GIB_HEAD = 4, // head gib; the input entity will transform into this
GIB_DEBRIS = 8, // explode outwards rather than in velocity, no blood
GIB_SKINNED = 16, // use skinnum
GIB_UPRIGHT = 32, // stay upright on ground
};
MAKE_ENUM_BITFLAGS(gib_type_t);
// monster ai flags
enum monster_ai_flags_t : uint64_t
{
AI_NONE = 0,
AI_STAND_GROUND = bit_v<0>,
AI_TEMP_STAND_GROUND = bit_v<1>,
AI_SOUND_TARGET = bit_v<2>,
AI_LOST_SIGHT = bit_v<3>,
AI_PURSUIT_LAST_SEEN = bit_v<4>,
AI_PURSUE_NEXT = bit_v<5>,
AI_PURSUE_TEMP = bit_v<6>,
AI_HOLD_FRAME = bit_v<7>,
AI_GOOD_GUY = bit_v<8>,
AI_BRUTAL = bit_v<9>,
AI_NOSTEP = bit_v<10>,
AI_DUCKED = bit_v<11>,
AI_COMBAT_POINT = bit_v<12>,
AI_MEDIC = bit_v<13>,
AI_RESURRECTING = bit_v<14>,
// ROGUE
AI_MANUAL_STEERING = bit_v<15>,
AI_TARGET_ANGER = bit_v<16>,
AI_DODGING = bit_v<17>,
AI_CHARGING = bit_v<18>,
AI_HINT_PATH = bit_v<19>,
AI_IGNORE_SHOTS = bit_v<20>,
// PMM - FIXME - last second added for E3 .. there's probably a better way to do this, but
// this works
AI_DO_NOT_COUNT = bit_v<21>, // set for healed monsters
AI_SPAWNED_CARRIER = bit_v<22>, // both do_not_count and spawned are set for spawned monsters
AI_SPAWNED_MEDIC_C = bit_v<23>, // both do_not_count and spawned are set for spawned monsters
AI_SPAWNED_WIDOW = bit_v<24>, // both do_not_count and spawned are set for spawned monsters
AI_BLOCKED = bit_v<25>, // used by blocked_checkattack: set to say I'm attacking while blocked
// (prevents run-attacks)
// ROGUE
AI_SPAWNED_ALIVE = bit_v<26>, // [Paril-KEX] for spawning dead
AI_SPAWNED_DEAD = bit_v<27>,
AI_HIGH_TICK_RATE = bit_v<28>, // not limited by 10hz actions
AI_NO_PATH_FINDING = bit_v<29>, // don't try nav nodes for path finding
AI_PATHING = bit_v<30>, // using nav nodes currently
AI_STINKY = bit_v<31>, // spawn flies
AI_STUNK = bit_v<32>, // already spawned files
AI_ALTERNATE_FLY = bit_v<33>, // use alternate flying mechanics; see monsterinfo.fly_xxx
AI_TEMP_MELEE_COMBAT = bit_v<34>, // temporarily switch to the melee combat style
AI_FORGET_ENEMY = bit_v<35>, // forget the current enemy
AI_DOUBLE_TROUBLE = bit_v<36>, // JORG only
AI_REACHED_HOLD_COMBAT = bit_v<37>,
AI_THIRD_EYE = bit_v<38>
};
MAKE_ENUM_BITFLAGS(monster_ai_flags_t);
constexpr monster_ai_flags_t AI_SPAWNED_MASK =
AI_SPAWNED_CARRIER | AI_SPAWNED_MEDIC_C | AI_SPAWNED_WIDOW; // mask to catch all three flavors of spawned
// monster attack state
enum monster_attack_state_t
{
AS_NONE,
AS_STRAIGHT,
AS_SLIDING,
AS_MELEE,
AS_MISSILE,
AS_BLIND // PMM - used by boss code to do nasty things even if it can't see you
};
// handedness values
enum handedness_t
{
RIGHT_HANDED,
LEFT_HANDED,
CENTER_HANDED
};
enum class auto_switch_t
{
SMART,
ALWAYS,
ALWAYS_NO_AMMO,
NEVER
};
constexpr uint32_t SFL_CROSS_TRIGGER_MASK = (0xffffffffu & ~SPAWNFLAG_EDITOR_MASK.value);
// noise types for PlayerNoise
enum player_noise_t
{
PNOISE_SELF,
PNOISE_WEAPON,
PNOISE_IMPACT
};
struct gitem_armor_t
{
int32_t base_count;
int32_t max_count;
float normal_protection;
float energy_protection;
};
static constexpr gitem_armor_t jacketarmor_info = { 25, 50, .30f, .00f };
static constexpr gitem_armor_t combatarmor_info = { 50, 100, .60f, .30f };
static constexpr gitem_armor_t bodyarmor_info = { 100, 200, .80f, .60f };
// edict->movetype values
enum movetype_t {
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,
// RAFAEL
MOVETYPE_WALLBOUNCE,
// RAFAEL
// ROGUE
MOVETYPE_NEWTOSS // PGM - for deathball
// ROGUE
};
// edict->flags
enum ent_flags_t : uint64_t {
FL_NONE = 0, // no flags
FL_FLY = bit_v<0>,
FL_SWIM = bit_v<1>, // implied immunity to drowning
FL_IMMUNE_LASER = bit_v<2>,
FL_INWATER = bit_v<3>,
FL_GODMODE = bit_v<4>,
FL_NOTARGET = bit_v<5>,
FL_IMMUNE_SLIME = bit_v<6>,
FL_IMMUNE_LAVA = bit_v<7>,
FL_PARTIALGROUND = bit_v<8>, // not all corners are valid
FL_WATERJUMP = bit_v<9>, // player jumping out of water
FL_TEAMSLAVE = bit_v<10>, // not the first on the team
FL_NO_KNOCKBACK = bit_v<11>,
FL_POWER_ARMOR = bit_v<12>, // power armor (if any) is active
// ROGUE
FL_MECHANICAL = bit_v<13>, // entity is mechanical, use sparks not blood
FL_SAM_RAIMI = bit_v<14>, // entity is in sam raimi cam mode
FL_DISGUISED = bit_v<15>, // entity is in disguise, monsters will not recognize.
FL_NOGIB = bit_v<16>, // player has been vaporized by a nuke, drop no gibs
FL_DAMAGEABLE = bit_v<17>,
FL_STATIONARY = bit_v<18>,
// ROGUE
FL_ALIVE_KNOCKBACK_ONLY = bit_v<19>, // only apply knockback if alive or on same frame as death
FL_NO_DAMAGE_EFFECTS = bit_v<20>,
// [Paril-KEX] gets scaled by coop health scaling
FL_COOP_HEALTH_SCALE = bit_v<21>,
FL_FLASHLIGHT = bit_v<22>, // enable flashlight
FL_KILL_VELOCITY = bit_v<23>, // for berserker slam
FL_NOVISIBLE = bit_v<24>, // super invisibility
FL_DODGE = bit_v<25>, // monster should try to dodge this
FL_TEAMMASTER = bit_v<26>, // is a team master (only here so that entities abusing teammaster/teamchain for stuff don't break)
FL_LOCKED = bit_v<27>, // entity is locked for the purposes of navigation
FL_ALWAYS_TOUCH = bit_v<28>, // always touch, even if we normally wouldn't
FL_NO_STANDING = bit_v<29>, // don't allow "standing" on non-brush entities
FL_WANTS_POWER_ARMOR = bit_v<30>, // for players, auto-shield
FL_RESPAWN = bit_v<31>, // used for item respawning
FL_TRAP = bit_v<32>, // entity is a trap of some kind
FL_TRAP_LASER_FIELD = bit_v<33>, // enough of a special case to get it's own flag...
FL_IMMORTAL = bit_v<34> // never go below 1hp
};
MAKE_ENUM_BITFLAGS( ent_flags_t );
// gitem_t->flags
enum item_flags_t : uint32_t
{
IF_NONE = 0,
IF_WEAPON = bit_v<0>, // use makes active weapon
IF_AMMO = bit_v<1>,
IF_ARMOR = bit_v<2>,
IF_STAY_COOP = bit_v<3>,
IF_KEY = bit_v<4>,
IF_POWERUP = bit_v<5>,
// ROGUE
IF_NOT_GIVEABLE = bit_v<6>, // item can not be given
// ROGUE
IF_HEALTH = bit_v<7>,
// ZOID
IF_TECH = bit_v<8>,
IF_NO_HASTE = bit_v<9>,
// ZOID
IF_NO_INFINITE_AMMO = bit_v<10>, // [Paril-KEX] don't allow infinite ammo to affect
IF_POWERUP_WHEEL = bit_v<11>, // [Paril-KEX] item should be in powerup wheel
IF_POWERUP_ONOFF = bit_v<12>, // [Paril-KEX] for wheel; can't store more than one, show on/off state
IF_NOT_RANDOM = bit_v<13>, // [Paril-KEX] item never shows up in randomizations
IF_ANY = 0xFFFFFFFF
};
MAKE_ENUM_BITFLAGS(item_flags_t);
// health edict_t->style
enum
{
HEALTH_IGNORE_MAX = 1,
HEALTH_TIMED = 2
};
// item IDs; must match itemlist order
enum item_id_t : int32_t {
IT_NULL, // must always be zero
IT_ARMOR_BODY,
IT_ARMOR_COMBAT,
IT_ARMOR_JACKET,
IT_ARMOR_SHARD,
IT_ITEM_POWER_SCREEN,
IT_ITEM_POWER_SHIELD,
IT_WEAPON_GRAPPLE,
IT_WEAPON_BLASTER,
IT_WEAPON_CHAINFIST,
IT_WEAPON_SHOTGUN,
IT_WEAPON_SSHOTGUN,
IT_WEAPON_MACHINEGUN,
IT_WEAPON_ETF_RIFLE,
IT_WEAPON_CHAINGUN,
IT_AMMO_GRENADES,
IT_AMMO_TRAP,
IT_AMMO_TESLA,
IT_WEAPON_GLAUNCHER,
IT_WEAPON_PROXLAUNCHER,
IT_WEAPON_RLAUNCHER,
IT_WEAPON_HYPERBLASTER,
IT_WEAPON_IONRIPPER,
IT_WEAPON_PLASMABEAM,
IT_WEAPON_RAILGUN,
IT_WEAPON_PHALANX,
IT_WEAPON_BFG,
IT_WEAPON_DISRUPTOR,
#if 0
IT_WEAPON_DISINTEGRATOR,
#endif
IT_AMMO_SHELLS,
IT_AMMO_BULLETS,
IT_AMMO_CELLS,
IT_AMMO_ROCKETS,
IT_AMMO_SLUGS,
IT_AMMO_MAGSLUG,
IT_AMMO_FLECHETTES,
IT_AMMO_PROX,
IT_AMMO_NUKE,
IT_AMMO_ROUNDS,
IT_ITEM_QUAD,
IT_ITEM_QUADFIRE,
IT_ITEM_INVULNERABILITY,
IT_ITEM_INVISIBILITY,
IT_ITEM_SILENCER,
IT_ITEM_REBREATHER,
IT_ITEM_ENVIROSUIT,
IT_ITEM_ANCIENT_HEAD,
IT_ITEM_LEGACY_HEAD,
IT_ITEM_ADRENALINE,
IT_ITEM_BANDOLIER,
IT_ITEM_PACK,
IT_ITEM_IR_GOGGLES,
IT_ITEM_DOUBLE,
IT_ITEM_SPHERE_VENGEANCE,
IT_ITEM_SPHERE_HUNTER,
IT_ITEM_SPHERE_DEFENDER,
IT_ITEM_DOPPELGANGER,
IT_ITEM_TAG_TOKEN,
IT_KEY_DATA_CD,
IT_KEY_POWER_CUBE,
IT_KEY_EXPLOSIVE_CHARGES,
IT_KEY_YELLOW,
IT_KEY_POWER_CORE,
IT_KEY_PYRAMID,
IT_KEY_DATA_SPINNER,
IT_KEY_PASS,
IT_KEY_BLUE_KEY,
IT_KEY_RED_KEY,
IT_KEY_GREEN_KEY,
IT_KEY_COMMANDER_HEAD,
IT_KEY_AIRSTRIKE,
IT_KEY_NUKE_CONTAINER,
IT_KEY_NUKE,
IT_HEALTH_SMALL,
IT_HEALTH_MEDIUM,
IT_HEALTH_LARGE,
IT_HEALTH_MEGA,
IT_FLAG1,
IT_FLAG2,
IT_TECH_RESISTANCE,
IT_TECH_STRENGTH,
IT_TECH_HASTE,
IT_TECH_REGENERATION,
IT_ITEM_FLASHLIGHT,
IT_ITEM_COMPASS,
IT_TOTAL
};
struct gitem_t
{
item_id_t id; // matches item list index
const char *classname; // spawning name
bool (*pickup)(edict_t *ent, edict_t *other);
void (*use)(edict_t *ent, gitem_t *item);
void (*drop)(edict_t *ent, gitem_t *item);
void (*weaponthink)(edict_t *ent);
const char *pickup_sound;
const char *world_model;
effects_t world_model_flags;
const char *view_model;
// client side info
const char *icon;
const char *use_name; // for use command, english only
const char *pickup_name; // for printing on pickup
const char *pickup_name_definite; // definite article version for languages that need it
int quantity = 0; // for ammo how much, for weapons how much is used per shot
item_id_t ammo = IT_NULL; // for weapons
item_id_t chain = IT_NULL; // weapon chain root
item_flags_t flags = IF_NONE; // IT_* flags
const char *vwep_model = nullptr; // vwep model string (for weapons)
const gitem_armor_t *armor_info = nullptr;
int tag = 0;
const char *precaches = nullptr; // string of all models, sounds, and images this item will use
int32_t sort_id = 0; // used by some items to control their sorting
int32_t quantity_warn = 5; // when to warn on low ammo
// set in InitItems, don't set by hand
// circular list of chained weapons
gitem_t *chain_next = nullptr;
// set in SP_worldspawn, don't set by hand
// model index for vwep
int32_t vwep_index = 0;
// set in SetItemNames, don't set by hand
// offset into CS_WHEEL_AMMO/CS_WHEEL_WEAPONS/CS_WHEEL_POWERUPS
int32_t ammo_wheel_index = -1;
int32_t weapon_wheel_index = -1;
int32_t powerup_wheel_index = -1;
};
// means of death
enum mod_id_t : uint8_t
{
MOD_UNKNOWN,
MOD_BLASTER,
MOD_SHOTGUN,
MOD_SSHOTGUN,
MOD_MACHINEGUN,
MOD_CHAINGUN,
MOD_GRENADE,
MOD_G_SPLASH,
MOD_ROCKET,
MOD_R_SPLASH,
MOD_HYPERBLASTER,
MOD_RAILGUN,
MOD_BFG_LASER,
MOD_BFG_BLAST,
MOD_BFG_EFFECT,
MOD_HANDGRENADE,
MOD_HG_SPLASH,
MOD_WATER,
MOD_SLIME,
MOD_LAVA,
MOD_CRUSH,
MOD_TELEFRAG,
MOD_TELEFRAG_SPAWN,
MOD_FALLING,
MOD_SUICIDE,
MOD_HELD_GRENADE,
MOD_EXPLOSIVE,
MOD_BARREL,
MOD_BOMB,
MOD_EXIT,
MOD_SPLASH,
MOD_TARGET_LASER,
MOD_TRIGGER_HURT,
MOD_HIT,
MOD_TARGET_BLASTER,
// RAFAEL 14-APR-98
MOD_RIPPER,
MOD_PHALANX,
MOD_BRAINTENTACLE,
MOD_BLASTOFF,
MOD_GEKK,
MOD_TRAP,
// END 14-APR-98
//========
// ROGUE
MOD_CHAINFIST,
MOD_DISINTEGRATOR,
MOD_ETF_RIFLE,
MOD_BLASTER2,
MOD_HEATBEAM,
MOD_TESLA,
MOD_PROX,
MOD_NUKE,
MOD_VENGEANCE_SPHERE,
MOD_HUNTER_SPHERE,
MOD_DEFENDER_SPHERE,
MOD_TRACKER,
MOD_DBALL_CRUSH,
MOD_DOPPLE_EXPLODE,
MOD_DOPPLE_VENGEANCE,
MOD_DOPPLE_HUNTER,
// ROGUE
//========
MOD_GRAPPLE,
MOD_BLUEBLASTER
};
struct mod_t
{
mod_id_t id;
bool friendly_fire = false;
bool no_point_loss = false;
mod_t() = default;
constexpr mod_t(mod_id_t id, bool no_point_loss = false) :
id(id),
no_point_loss(no_point_loss)
{
}
};
// the total number of levels we'll track for the
// end of unit screen.
constexpr size_t MAX_LEVELS_PER_UNIT = 8;
struct level_entry_t
{
// bsp name
char map_name[MAX_QPATH];
// map name
char pretty_name[MAX_QPATH];
// these are set when we leave the level
int32_t total_secrets;
int32_t found_secrets;
int32_t total_monsters;
int32_t killed_monsters;
// total time spent in the level, for end screen
gtime_t time;
// the order we visited levels in
int32_t visit_order;
};
//
// 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
//
struct game_locals_t
{
char helpmessage1[MAX_TOKEN_CHARS];
char helpmessage2[MAX_TOKEN_CHARS];
int32_t help1changed, help2changed;
gclient_t *clients; // [maxclients]
// can't store spawnpoint in level, because
// it would get overwritten by the savegame restore
char spawnpoint[MAX_TOKEN_CHARS]; // needed for coop respawns
// store latched cvars here that we want to get at often
uint32_t maxclients;
uint32_t maxentities;
// cross level triggers
uint32_t cross_level_flags, cross_unit_flags;
bool autosaved;
// [Paril-KEX]
int32_t airacceleration_modified, gravity_modified;
std::array<level_entry_t, MAX_LEVELS_PER_UNIT> level_entries;
int32_t max_lag_origins;
vec3_t *lag_origins; // maxclients * max_lag_origins
};
constexpr size_t MAX_HEALTH_BARS = 2;
//
// this structure is cleared as each map is entered
// it is read/written to the level.sav file for savegames
//
struct level_locals_t
{
bool in_frame;
gtime_t 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
char forcemap[MAX_QPATH]; // go here
// intermission state
gtime_t intermissiontime; // time the intermission was started
const char *changemap;
const char *achievement;
bool exitintermission;
bool intermission_eou;
bool intermission_clear; // [Paril-KEX] clear inventory on switch
bool level_intermission_set; // [Paril-KEX] for target_camera switches; don't find intermission point
bool intermission_fade, intermission_fading; // [Paril-KEX] fade on exit instead of immediately leaving
gtime_t intermission_fade_time;
vec3_t intermission_origin;
vec3_t intermission_angle;
bool respawn_intermission; // only set once for respawning players
int32_t pic_health;
int32_t total_secrets;
int32_t found_secrets;
int32_t total_goals;
int32_t found_goals;
int32_t total_monsters;
std::array<edict_t *, MAX_EDICTS> monsters_registered; // only for debug
int32_t killed_monsters;
edict_t *current_entity; // entity running from G_RunFrame
int32_t body_que; // dead bodies
int32_t power_cubes; // ugly necessity for coop
// ROGUE
edict_t *disguise_violator;
gtime_t disguise_violation_time;
int32_t disguise_icon; // [Paril-KEX]
// ROGUE
int32_t shadow_light_count; // [Sam-KEX]
bool is_n64;
gtime_t coop_level_restart_time; // restart the level after this time
bool instantitems; // instantitems 1 set in worldspawn
// N64 goal stuff
const char *goals; // nullptr if no goals in world
int32_t goal_num; // current relative goal number, increased with each target_goal
// offset for the first vwep model, for
// skinnum encoding
int32_t vwep_offset;
// coop health scaling factor;
// this percentage of health is added
// to the monster's health per player.
float coop_health_scaling;
// this isn't saved in the save file, but stores
// the amount of players currently active in the
// level, compared against monsters' individual
// scale #
int32_t coop_scale_players;
// [Paril-KEX] current level entry
level_entry_t *entry;
// [Paril-KEX] current poi
bool valid_poi;
vec3_t current_poi;
int32_t current_poi_image;
int32_t current_poi_stage;
edict_t *current_dynamic_poi;
vec3_t *poi_points[MAX_SPLIT_PLAYERS]; // temporary storage for POIs in coop
// start items
const char *start_items;
// disable grappling hook
bool no_grapple;
// saved gravity
float gravity;
// level is a hub map, and shouldn't be included in EOU stuff
bool hub_map;
// active health bar entities
std::array<edict_t *, MAX_HEALTH_BARS> health_bar_entities;
int32_t intermission_server_frame;
bool deadly_kill_box;
bool story_active;
gtime_t next_auto_save;
gtime_t next_match_report;
};
struct shadow_light_temp_t
{
shadow_light_data_t data;
const char *lightstyletarget = nullptr;
};
#include <unordered_set>
// 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.
// defaults can/should be set in the struct.
struct spawn_temp_t
{
// world vars
const char *sky;
float skyrotate;
vec3_t skyaxis;
int32_t skyautorotate = 1;
const char *nextmap;
int32_t lip;
int32_t distance;
int32_t height;
const char *noise;
float pausetime;
const char *item;
const char *gravity;
float minyaw;
float maxyaw;
float minpitch;
float maxpitch;
shadow_light_temp_t sl; // [Sam-KEX]
const char* music; // [Edward-KEX]
int instantitems;
float radius; // [Paril-KEX]
bool hub_map; // [Paril-KEX]
const char *achievement; // [Paril-KEX]
// [Paril-KEX]
const char *goals;
// [Paril-KEX]
const char *image;
int fade_start_dist = 96;
int fade_end_dist = 384;
const char *start_items;
int no_grapple = 0;
float health_multiplier = 1.0f;
const char *reinforcements; // [Paril-KEX]
const char *noise_start, *noise_middle, *noise_end; // [Paril-KEX]
int32_t loop_count; // [Paril-KEX]
std::unordered_set<const char *> keys_specified;
inline bool was_key_specified(const char *key) const
{
return keys_specified.find(key) != keys_specified.end();
}
};
enum move_state_t
{
STATE_TOP,
STATE_BOTTOM,
STATE_UP,
STATE_DOWN
};
#define DEFINE_DATA_FUNC(ns_lower, ns_upper, returnType, ...) \
using save_##ns_lower##_t = save_data_t<returnType(*)(__VA_ARGS__), SAVE_FUNC_##ns_upper>
#define SAVE_DATA_FUNC(n, ns, returnType, ...) \
using save_##n##_t = save_data_t<returnType(*)(__VA_ARGS__), SAVE_FUNC_##ns>; \
extern returnType n(__VA_ARGS__); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_##ns, reinterpret_cast<const void *>(n)); \
auto n
DEFINE_DATA_FUNC(moveinfo_endfunc, MOVEINFO_ENDFUNC, void, edict_t *self);
#define MOVEINFO_ENDFUNC(n) \
SAVE_DATA_FUNC(n, MOVEINFO_ENDFUNC, void, edict_t *self)
DEFINE_DATA_FUNC(moveinfo_blocked, MOVEINFO_BLOCKED, void, edict_t *self, edict_t *other);
#define MOVEINFO_BLOCKED(n) \
SAVE_DATA_FUNC(n, MOVEINFO_BLOCKED, void, edict_t *self, edict_t *other)
struct moveinfo_t
{
// fixed data
vec3_t start_origin;
vec3_t start_angles;
vec3_t end_origin;
vec3_t end_angles, end_angles_reversed;
int32_t sound_start;
int32_t sound_middle;
int32_t sound_end;
float accel;
float speed;
float decel;
float distance;
float wait;
// state data
move_state_t state;
bool reversing;
vec3_t dir;
vec3_t dest;
float current_speed;
float move_speed;
float next_speed;
float remaining_distance;
float decel_distance;
save_moveinfo_endfunc_t endfunc;
save_moveinfo_blocked_t blocked;
};
struct mframe_t
{
void (*aifunc)(edict_t *self, float dist) = nullptr;
float dist = 0;
void (*thinkfunc)(edict_t *self) = nullptr;
int32_t lerp_frame = -1;
};
// this check only works on windows, and is only
// of importance to developers anyways
#if defined(_WIN32) && defined(_MSC_VER)
#if _MSC_VER >= 1934
#define COMPILE_TIME_MOVE_CHECK
#endif
#endif
struct mmove_t
{
int32_t firstframe;
int32_t lastframe;
const mframe_t *frame;
void (*endfunc)(edict_t *self);
float sidestep_scale;
#ifdef COMPILE_TIME_MOVE_CHECK
template<size_t N>
constexpr mmove_t(int32_t firstframe, int32_t lastframe, const mframe_t (&frames)[N], void (*endfunc)(edict_t *self) = nullptr, float sidestep_scale = 0.0f) :
firstframe(firstframe),
lastframe(lastframe),
frame(frames),
endfunc(endfunc),
sidestep_scale(sidestep_scale)
{
if ((lastframe - firstframe + 1) != N)
throw std::exception("bad animation frames; check your numbers!");
}
#endif
};
using save_mmove_t = save_data_t<mmove_t, SAVE_DATA_MMOVE>;
#ifdef COMPILE_TIME_MOVE_CHECK
#define MMOVE_T(n) \
extern const mmove_t n; \
static const save_data_list_t save__##n(#n, SAVE_DATA_MMOVE, &n); \
constexpr mmove_t n
#else
#define MMOVE_T(n) \
extern const mmove_t n; \
static const save_data_list_t save__##n(#n, SAVE_DATA_MMOVE, &n); \
const mmove_t n
#endif
DEFINE_DATA_FUNC(monsterinfo_stand, MONSTERINFO_STAND, void, edict_t *self);
#define MONSTERINFO_STAND(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_STAND, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_idle, MONSTERINFO_IDLE, void, edict_t *self);
#define MONSTERINFO_IDLE(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_IDLE, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_search, MONSTERINFO_SEARCH, void, edict_t *self);
#define MONSTERINFO_SEARCH(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_SEARCH, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_walk, MONSTERINFO_WALK, void, edict_t *self);
#define MONSTERINFO_WALK(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_WALK, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_run, MONSTERINFO_RUN, void, edict_t *self);
#define MONSTERINFO_RUN(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_RUN, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_dodge, MONSTERINFO_DODGE, void, edict_t *self, edict_t *attacker, gtime_t eta, trace_t *tr, bool gravity);
#define MONSTERINFO_DODGE(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_DODGE, void, edict_t *self, edict_t *attacker, gtime_t eta, trace_t *tr, bool gravity)
DEFINE_DATA_FUNC(monsterinfo_attack, MONSTERINFO_ATTACK, void, edict_t *self);
#define MONSTERINFO_ATTACK(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_ATTACK, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_melee, MONSTERINFO_MELEE, void, edict_t *self);
#define MONSTERINFO_MELEE(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_MELEE, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_sight, MONSTERINFO_SIGHT, void, edict_t *self, edict_t *other);
#define MONSTERINFO_SIGHT(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_SIGHT, void, edict_t *self, edict_t *other)
DEFINE_DATA_FUNC(monsterinfo_checkattack, MONSTERINFO_CHECKATTACK, bool, edict_t *self);
#define MONSTERINFO_CHECKATTACK(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_CHECKATTACK, bool, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_setskin, MONSTERINFO_SETSKIN, void, edict_t *self);
#define MONSTERINFO_SETSKIN(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_SETSKIN, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_blocked, MONSTERINFO_BLOCKED, bool, edict_t *self, float dist);
#define MONSTERINFO_BLOCKED(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_BLOCKED, bool, edict_t *self, float dist)
DEFINE_DATA_FUNC(monsterinfo_physicschange, MONSTERINFO_PHYSCHANGED, void, edict_t *self);
#define MONSTERINFO_PHYSCHANGED(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_PHYSCHANGED, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_duck, MONSTERINFO_DUCK, bool, edict_t *self, gtime_t eta);
#define MONSTERINFO_DUCK(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_DUCK, bool, edict_t *self, gtime_t eta)
DEFINE_DATA_FUNC(monsterinfo_unduck, MONSTERINFO_UNDUCK, void, edict_t *self);
#define MONSTERINFO_UNDUCK(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_UNDUCK, void, edict_t *self)
DEFINE_DATA_FUNC(monsterinfo_sidestep, MONSTERINFO_SIDESTEP, bool, edict_t *self);
#define MONSTERINFO_SIDESTEP(n) \
SAVE_DATA_FUNC(n, MONSTERINFO_SIDESTEP, bool, edict_t *self)
// combat styles, for navigation
enum combat_style_t
{
COMBAT_UNKNOWN, // automatically choose based on attack functions
COMBAT_MELEE, // should attempt to get up close for melee
COMBAT_MIXED, // has mixed melee/ranged; runs to get up close if far enough away
COMBAT_RANGED // don't bother pathing if we can see the player
};
struct reinforcement_t
{
const char *classname;
int32_t strength;
vec3_t mins, maxs;
};
struct reinforcement_list_t
{
reinforcement_t *reinforcements;
uint32_t num_reinforcements;
};
constexpr size_t MAX_REINFORCEMENTS = 5; // max number of spawns we can do at once.
constexpr gtime_t HOLD_FOREVER = gtime_t::from_ms(std::numeric_limits<int64_t>::max());
struct monsterinfo_t
{
// [Paril-KEX] allow some moves to be done instantaneously, but
// others can wait the full frame.
// NB: always use `M_SetAnimation` as it handles edge cases.
save_mmove_t active_move, next_move;
monster_ai_flags_t aiflags; // PGM - unsigned, since we're close to the max
int32_t nextframe; // if next_move is set, this is ignored until a frame is ran
float scale;
save_monsterinfo_stand_t stand;
save_monsterinfo_idle_t idle;
save_monsterinfo_search_t search;
save_monsterinfo_walk_t walk;
save_monsterinfo_run_t run;
save_monsterinfo_dodge_t dodge;
save_monsterinfo_attack_t attack;
save_monsterinfo_melee_t melee;
save_monsterinfo_sight_t sight;
save_monsterinfo_checkattack_t checkattack;
save_monsterinfo_setskin_t setskin;
save_monsterinfo_physicschange_t physics_change;
gtime_t pausetime;
gtime_t attack_finished;
gtime_t fire_wait;
vec3_t saved_goal;
gtime_t search_time;
gtime_t trail_time;
vec3_t last_sighting;
monster_attack_state_t attack_state;
bool lefty;
gtime_t idle_time;
int32_t linkcount;
item_id_t power_armor_type;
int32_t power_armor_power;
// for monster revive
item_id_t initial_power_armor_type;
int32_t max_power_armor_power;
int32_t weapon_sound, engine_sound;
// ROGUE
save_monsterinfo_blocked_t blocked;
gtime_t last_hint_time; // last time the monster checked for hintpaths.
edict_t *goal_hint; // which hint_path we're trying to get to
int32_t medicTries;
edict_t *badMedic1, *badMedic2; // these medics have declared this monster "unhealable"
edict_t *healer; // this is who is healing this monster
save_monsterinfo_duck_t duck;
save_monsterinfo_unduck_t unduck;
save_monsterinfo_sidestep_t sidestep;
float base_height;
gtime_t next_duck_time;
gtime_t duck_wait_time;
edict_t *last_player_enemy;
// blindfire stuff .. the boolean says whether the monster will do it, and blind_fire_time is the timing
// (set in the monster) of the next shot
bool blindfire; // will the monster blindfire?
bool can_jump; // will the monster jump?
bool had_visibility; // Paril: used for blindfire
float drop_height, jump_height;
gtime_t blind_fire_delay;
vec3_t blind_fire_target;
// used by the spawners to not spawn too much and keep track of #s of monsters spawned
int32_t monster_slots; // nb: for spawned monsters, this is how many slots we took from our commander
int32_t monster_used;
edict_t *commander;
// powerup timers, used by widow, our friend
gtime_t quad_time;
gtime_t invincible_time;
gtime_t double_time;
// ROGUE
// Paril
gtime_t surprise_time;
item_id_t armor_type;
int32_t armor_power;
bool close_sight_tripped;
gtime_t melee_debounce_time; // don't melee until this time has passed
gtime_t strafe_check_time; // time until we should reconsider strafing
int32_t base_health; // health that we had on spawn, before any co-op adjustments
int32_t health_scaling; // number of players we've been scaled up to
gtime_t next_move_time; // high tick rate
gtime_t bad_move_time; // don't try straight moves until this is over
gtime_t bump_time; // don't slide against walls for a bit
gtime_t random_change_time; // high tickrate
gtime_t path_blocked_counter; // break out of paths when > a certain time
gtime_t path_wait_time; // don't try nav nodes until this is over
PathInfo nav_path; // if AI_PATHING, this is where we are trying to reach
gtime_t nav_path_cache_time; // cache nav_path result for this much time
combat_style_t combat_style; // pathing style
edict_t *damage_attacker;
edict_t *damage_inflictor;
int32_t damage_blood, damage_knockback;
vec3_t damage_from;
mod_t damage_mod;
// alternate flying mechanics
float fly_max_distance, fly_min_distance; // how far we should try to stay
float fly_acceleration; // accel/decel speed
float fly_speed; // max speed from flying
vec3_t fly_ideal_position; // ideally where we want to end up to hover, relative to our target if not pinned
gtime_t fly_position_time; // if <= level.time, we can try changing positions
bool fly_buzzard, fly_above; // orbit around all sides of their enemy, not just the sides
bool fly_pinned; // whether we're currently pinned to ideal position (made absolute)
bool fly_thrusters; // slightly different flight mechanics, for melee attacks
gtime_t fly_recovery_time; // time to try a new dir to get away from hazards
vec3_t fly_recovery_dir;
gtime_t checkattack_time;
int32_t start_frame;
gtime_t dodge_time;
int32_t move_block_counter;
gtime_t move_block_change_time;
gtime_t react_to_damage_time;
reinforcement_list_t reinforcements;
std::array<uint8_t, MAX_REINFORCEMENTS> chosen_reinforcements; // readied for spawn; 255 is value for none
gtime_t jump_time;
// NOTE: if adding new elements, make sure to add them
// in g_save.cpp too!
};
// non-monsterinfo save stuff
using save_prethink_t = save_data_t<void(*)(edict_t *self), SAVE_FUNC_PRETHINK>;
#define PRETHINK(n) \
void n(edict_t *self); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_PRETHINK, reinterpret_cast<const void *>(n)); \
auto n
using save_think_t = save_data_t<void(*)(edict_t *self), SAVE_FUNC_THINK>;
#define THINK(n) \
void n(edict_t *self); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_THINK, reinterpret_cast<const void *>(n)); \
auto n
using save_touch_t = save_data_t<void(*)(edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self), SAVE_FUNC_TOUCH>;
#define TOUCH(n) \
void n(edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_TOUCH, reinterpret_cast<const void *>(n)); \
auto n
using save_use_t = save_data_t<void(*)(edict_t *self, edict_t *other, edict_t *activator), SAVE_FUNC_USE>;
#define USE(n) \
void n(edict_t *self, edict_t *other, edict_t *activator); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_USE, reinterpret_cast<const void *>(n)); \
auto n
using save_pain_t = save_data_t<void(*)(edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod), SAVE_FUNC_PAIN>;
#define PAIN(n) \
void n(edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_PAIN, reinterpret_cast<const void *>(n)); \
auto n
using save_die_t = save_data_t<void(*)(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod), SAVE_FUNC_DIE>;
#define DIE(n) \
void n(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod); \
static const save_data_list_t save__##n(#n, SAVE_FUNC_DIE, reinterpret_cast<const void *>(n)); \
auto n
// ROGUE
// this determines how long to wait after a duck to duck again.
// if we finish a duck-up, this gets cut in half.
constexpr gtime_t DUCK_INTERVAL = 5000_ms;
// ROGUE
extern game_locals_t game;
extern level_locals_t level;
extern game_export_t globals;
extern spawn_temp_t st;
extern int sm_meat_index;
extern int snd_fry;
extern edict_t *g_edicts;
#include <random>
extern std::mt19937 mt_rand;
// uniform float [0, 1)
[[nodiscard]] inline float frandom()
{
return std::uniform_real_distribution<float>()(mt_rand);
}
// uniform float [min_inclusive, max_exclusive)
[[nodiscard]] inline float frandom(float min_inclusive, float max_exclusive)
{
return std::uniform_real_distribution<float>(min_inclusive, max_exclusive)(mt_rand);
}
// uniform float [0, max_exclusive)
[[nodiscard]] inline float frandom(float max_exclusive)
{
return std::uniform_real_distribution<float>(0, max_exclusive)(mt_rand);
}
// uniform time [min_inclusive, max_exclusive)
[[nodiscard]] inline gtime_t random_time(gtime_t min_inclusive, gtime_t max_exclusive)
{
return gtime_t::from_ms(std::uniform_int_distribution<int64_t>(min_inclusive.milliseconds(), max_exclusive.milliseconds())(mt_rand));
}
// uniform time [0, max_exclusive)
[[nodiscard]] inline gtime_t random_time(gtime_t max_exclusive)
{
return gtime_t::from_ms(std::uniform_int_distribution<int64_t>(0, max_exclusive.milliseconds())(mt_rand));
}
// uniform float [-1, 1)
// note: closed on min but not max
// to match vanilla behavior
[[nodiscard]] inline float crandom()
{
return std::uniform_real_distribution<float>(-1.f, 1.f)(mt_rand);
}
// uniform float (-1, 1)
[[nodiscard]] inline float crandom_open()
{
return std::uniform_real_distribution<float>(std::nextafterf(-1.f, 0.f), 1.f)(mt_rand);
}
// raw unsigned int32 value from random
[[nodiscard]] inline uint32_t irandom()
{
return mt_rand();
}
// uniform int [min, max)
// always returns min if min == (max - 1)
// undefined behavior if min > (max - 1)
[[nodiscard]] inline int32_t irandom(int32_t min_inclusive, int32_t max_exclusive)
{
if (min_inclusive == max_exclusive - 1)
return min_inclusive;
return std::uniform_int_distribution<int32_t>(min_inclusive, max_exclusive - 1)(mt_rand);
}
// uniform int [0, max)
// always returns 0 if max <= 0
// note for Q2 code:
// - to fix rand()%x, do irandom(x)
// - to fix rand()&x, do irandom(x + 1)
[[nodiscard]] inline int32_t irandom(int32_t max_exclusive)
{
if (max_exclusive <= 0)
return 0;
return irandom(0, max_exclusive);
}
// uniform random index from given container
template<typename T>
[[nodiscard]] inline int32_t random_index(const T &container)
{
return irandom(std::size(container));
}
// uniform random element from given container
template<typename T>
[[nodiscard]] inline auto random_element(T &container) -> decltype(*std::begin(container))
{
return *(std::begin(container) + random_index(container));
}
// flip a coin
[[nodiscard]]inline bool brandom()
{
return irandom(2) == 0;
}
extern cvar_t *deathmatch;
extern cvar_t *coop;
extern cvar_t *skill;
extern cvar_t *fraglimit;
extern cvar_t *timelimit;
// ZOID
extern cvar_t *capturelimit;
extern cvar_t *g_quick_weapon_switch;
extern cvar_t *g_instant_weapon_switch;
// ZOID
extern cvar_t *password;
extern cvar_t *spectator_password;
extern cvar_t *needpass;
extern cvar_t *g_select_empty;
extern cvar_t *sv_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 *g_debug_monster_paths;
extern cvar_t *g_debug_monster_kills;
extern cvar_t *maxspectators;
extern cvar_t *bot_debug_follow_actor;
extern cvar_t *bot_debug_move_to_point;
extern cvar_t *flood_msgs;
extern cvar_t *flood_persecond;
extern cvar_t *flood_waitdelay;
extern cvar_t *sv_maplist;
extern cvar_t *g_skipViewModifiers;
extern cvar_t *sv_stopspeed; // PGM - this was a define in g_phys.c
extern cvar_t *g_strict_saves;
extern cvar_t *g_coop_health_scaling;
extern cvar_t *g_weapon_respawn_time;
extern cvar_t* g_no_health;
extern cvar_t* g_no_items;
extern cvar_t* g_dm_weapons_stay;
extern cvar_t* g_dm_no_fall_damage;
extern cvar_t* g_dm_instant_items;
extern cvar_t* g_dm_same_level;
extern cvar_t* g_friendly_fire;
extern cvar_t* g_dm_force_respawn;
extern cvar_t* g_dm_force_respawn_time;
extern cvar_t* g_dm_spawn_farthest;
extern cvar_t* g_no_armor;
extern cvar_t* g_dm_allow_exit;
extern cvar_t* g_infinite_ammo;
extern cvar_t* g_dm_no_quad_drop;
extern cvar_t* g_dm_no_quadfire_drop;
extern cvar_t* g_no_mines;
extern cvar_t* g_dm_no_stack_double;
extern cvar_t* g_no_nukes;
extern cvar_t* g_no_spheres;
extern cvar_t* g_teamplay_armor_protect;
extern cvar_t* g_allow_techs;
extern cvar_t* g_start_items;
extern cvar_t* g_map_list;
extern cvar_t* g_map_list_shuffle;
extern cvar_t *g_lag_compensation;
// ROGUE
extern cvar_t *gamerules;
extern cvar_t *huntercam;
extern cvar_t *g_dm_strong_mines;
extern cvar_t *g_dm_random_items;
// ROGUE
// [Kex]
extern cvar_t* g_instagib;
extern cvar_t* g_coop_player_collision;
extern cvar_t* g_coop_squad_respawn;
extern cvar_t* g_coop_enable_lives;
extern cvar_t* g_coop_num_lives;
extern cvar_t* g_coop_instanced_items;
extern cvar_t* g_allow_grapple;
extern cvar_t* g_grapple_fly_speed;
extern cvar_t* g_grapple_pull_speed;
extern cvar_t* g_grapple_damage;
extern cvar_t *sv_airaccelerate;
extern cvar_t *g_damage_scale;
extern cvar_t *g_disable_player_collision;
extern cvar_t *ai_damage_scale;
extern cvar_t *ai_model_scale;
extern cvar_t *ai_allow_dm_spawn;
extern cvar_t *ai_movement_disabled;
#define world (&g_edicts[0])
uint32_t GetUnicastKey();
// item spawnflags
constexpr spawnflags_t SPAWNFLAG_ITEM_TRIGGER_SPAWN = 0x00000001_spawnflag;
constexpr spawnflags_t SPAWNFLAG_ITEM_NO_TOUCH = 0x00000002_spawnflag;
constexpr spawnflags_t SPAWNFLAG_ITEM_TOSS_SPAWN = 0x00000004_spawnflag;
constexpr spawnflags_t SPAWNFLAG_ITEM_MAX = 0x00000008_spawnflag;
// 8 bits reserved for editor flags & power cube bits
// (see SPAWNFLAG_NOT_EASY above)
constexpr spawnflags_t SPAWNFLAG_ITEM_DROPPED = 0x00010000_spawnflag;
constexpr spawnflags_t SPAWNFLAG_ITEM_DROPPED_PLAYER = 0x00020000_spawnflag;
constexpr spawnflags_t SPAWNFLAG_ITEM_TARGETS_USED = 0x00040000_spawnflag;
extern gitem_t itemlist[IT_TOTAL];
//
// g_cmds.c
//
bool CheckFlood(edict_t *ent);
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 SetItemNames();
gitem_t *FindItem(const char *pickup_name);
gitem_t *FindItemByClassname(const char *classname);
edict_t *Drop_Item(edict_t *ent, gitem_t *item);
void SetRespawn(edict_t *ent, gtime_t delay, bool hide_self = true);
void ChangeWeapon(edict_t *ent);
void SpawnItem(edict_t *ent, gitem_t *item);
void Think_Weapon(edict_t *ent);
item_id_t ArmorIndex(edict_t *ent);
item_id_t PowerArmorType(edict_t *ent);
gitem_t *GetItemByIndex(item_id_t index);
gitem_t *GetItemByAmmo(ammo_t ammo);
gitem_t *GetItemByPowerup(powerup_t powerup);
bool Add_Ammo(edict_t *ent, gitem_t *item, int count);
void G_CheckPowerArmor(edict_t *ent);
void Touch_Item(edict_t *ent, edict_t *other, const trace_t &tr, bool other_touching_self);
void droptofloor(edict_t *ent);
void P_ToggleFlashlight(edict_t *ent, bool state);
bool Entity_IsVisibleToPlayer(edict_t* ent, edict_t* player);
void Compass_Update(edict_t *ent, bool first);
//
// g_utils.c
//
bool KillBox(edict_t *ent, bool from_spawning, mod_id_t mod = MOD_TELEFRAG, bool bsp_clipping = true);
edict_t *G_Find(edict_t *from, std::function<bool(edict_t *e)> matcher);
// utility template for getting the type of a field
template<typename>
struct member_object_type { };
template<typename T1, typename T2>
struct member_object_type<T1 T2::*> { using type = T1; };
template<typename T>
using member_object_type_t = typename member_object_type<std::remove_cv_t<T>>::type;
template<auto M>
edict_t *G_FindByString(edict_t *from, const std::string_view &value)
{
static_assert(std::is_same_v<member_object_type_t<decltype(M)>, const char *>, "can only use string member functions");
return G_Find(from, [&](edict_t *e) {
return e->*M && strlen(e->*M) == value.length() && !Q_strncasecmp(e->*M, value.data(), value.length());
});
}
edict_t *findradius(edict_t *from, const vec3_t &org, float rad);
edict_t *G_PickTarget(const char *targetname);
void G_UseTargets(edict_t *ent, edict_t *activator);
void G_PrintActivationMessage(edict_t *ent, edict_t *activator, bool coop_global);
void G_SetMovedir(vec3_t &angles, vec3_t &movedir);
void G_InitEdict(edict_t *e);
edict_t *G_Spawn();
void G_FreeEdict(edict_t *e);
void G_TouchTriggers(edict_t *ent);
void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin);
char *G_CopyString(const char *in, int32_t tag);
char *G_CopyString(const char *in, size_t len, int32_t tag);
// ROGUE
edict_t *findradius2(edict_t *from, const vec3_t &org, float rad);
// ROGUE
void G_PlayerNotifyGoal(edict_t *player);
//
// g_spawn.c
//
void ED_CallSpawn(edict_t *ent);
char *ED_NewString(char *string);
//
// g_target.c
//
void target_laser_think(edict_t *self);
void target_laser_off(edict_t *self);
constexpr spawnflags_t SPAWNFLAG_LASER_ON = 0x0001_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_RED = 0x0002_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_GREEN = 0x0004_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_BLUE = 0x0008_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_YELLOW = 0x0010_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_ORANGE = 0x0020_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_FAT = 0x0040_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_ZAP = 0x80000000_spawnflag;
constexpr spawnflags_t SPAWNFLAG_LASER_LIGHTNING = 0x10000_spawnflag;
constexpr spawnflags_t SPAWNFLAG_HEALTHBAR_PVS_ONLY = 1_spawnflag;
// damage flags
enum damageflags_t
{
DAMAGE_NONE = 0, // no damage flags
DAMAGE_RADIUS = 0x00000001, // damage was indirect
DAMAGE_NO_ARMOR = 0x00000002, // armour does not protect from this damage
DAMAGE_ENERGY = 0x00000004, // damage is from an energy based weapon
DAMAGE_NO_KNOCKBACK = 0x00000008, // do not affect velocity, just view angles
DAMAGE_BULLET = 0x00000010, // damage is from a bullet (used for ricochets)
DAMAGE_NO_PROTECTION = 0x00000020, // armor, shields, invulnerability, and godmode have no effect
// ROGUE
DAMAGE_DESTROY_ARMOR = 0x00000040, // damage is done to armor and health.
DAMAGE_NO_REG_ARMOR = 0x00000080, // damage skips regular armor
DAMAGE_NO_POWER_ARMOR = 0x00000100,// damage skips power armor
// ROGUE
DAMAGE_NO_INDICATOR = 0x00000200 // for clients: no damage indicators
};
MAKE_ENUM_BITFLAGS(damageflags_t);
//
// g_combat.c
//
bool OnSameTeam(edict_t *ent1, edict_t *ent2);
bool CanDamage(edict_t *targ, edict_t *inflictor);
bool CheckTeamDamage(edict_t *targ, edict_t *attacker);
void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, const vec3_t &dir, const vec3_t &point,
const vec3_t &normal, int damage, int knockback, damageflags_t dflags, mod_t mod);
void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, damageflags_t dflags, mod_t mod);
void Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, mod_t mod);
// ROGUE
void T_RadiusNukeDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, mod_t mod);
void T_RadiusClassDamage(edict_t *inflictor, edict_t *attacker, float damage, char *ignoreClass, float radius,
mod_t mod);
void cleanupHealTarget(edict_t *ent);
// ROGUE
constexpr int32_t DEFAULT_BULLET_HSPREAD = 300;
constexpr int32_t DEFAULT_BULLET_VSPREAD = 500;
constexpr int32_t DEFAULT_SHOTGUN_HSPREAD = 1000;
constexpr int32_t DEFAULT_SHOTGUN_VSPREAD = 500;
constexpr int32_t DEFAULT_DEATHMATCH_SHOTGUN_COUNT = 12;
constexpr int32_t DEFAULT_SHOTGUN_COUNT = 12;
constexpr int32_t DEFAULT_SSHOTGUN_COUNT = 20;
//
// g_func.c
//
void train_use(edict_t *self, edict_t *other, edict_t *activator);
void func_train_find(edict_t *self);
edict_t *plat_spawn_inside_trigger(edict_t *ent);
void Move_Calc(edict_t *ent, const vec3_t &dest, void(*endfunc)(edict_t *self));
void G_SetMoveinfoSounds(edict_t *self, const char *default_start, const char *default_mid, const char *default_end);
constexpr spawnflags_t SPAWNFLAG_TRAIN_START_ON = 1_spawnflag;
constexpr spawnflags_t SPAWNFLAG_WATER_SMART = 2_spawnflag;
constexpr spawnflags_t SPAWNFLAG_TRAIN_MOVE_TEAMCHAIN = 8_spawnflag;
constexpr spawnflags_t SPAWNFLAG_DOOR_REVERSE = 2_spawnflag;
//
// g_monster.c
//
void monster_muzzleflash(edict_t *self, const vec3_t &start, monster_muzzleflash_id_t id);
void monster_fire_bullet(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int kick, int hspread,
int vspread, monster_muzzleflash_id_t flashtype);
void monster_fire_shotgun(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int kick, int hspread,
int vspread, int count, monster_muzzleflash_id_t flashtype);
void monster_fire_blaster(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype, effects_t effect);
void monster_fire_flechette(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype);
void monster_fire_grenade(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed,
monster_muzzleflash_id_t flashtype, float right_adjust, float up_adjust);
void monster_fire_rocket(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype);
void monster_fire_railgun(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int kick,
monster_muzzleflash_id_t flashtype);
void monster_fire_bfg(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, int kick,
float damage_radius, monster_muzzleflash_id_t flashtype);
bool M_CheckClearShot(edict_t *self, const vec3_t &offset);
bool M_CheckClearShot(edict_t *self, const vec3_t &offset, vec3_t &start);
vec3_t M_ProjectFlashSource(edict_t *self, const vec3_t &offset, const vec3_t &forward, const vec3_t &right);
bool M_droptofloor_generic(vec3_t &origin, const vec3_t &mins, const vec3_t &maxs, bool ceiling, edict_t *ignore, contents_t mask, bool allow_partial);
bool M_droptofloor(edict_t *ent);
void monster_think(edict_t *self);
void monster_dead_think(edict_t *self);
void monster_dead(edict_t *self);
void walkmonster_start(edict_t *self);
void swimmonster_start(edict_t *self);
void flymonster_start(edict_t *self);
void monster_death_use(edict_t *self);
void M_CatagorizePosition(edict_t *self, const vec3_t &in_point, water_level_t &waterlevel, contents_t &watertype);
void M_WorldEffects(edict_t *ent);
bool M_CheckAttack(edict_t *self);
void M_CheckGround(edict_t *ent, contents_t mask);
void monster_use(edict_t *self, edict_t *other, edict_t *activator);
void M_ProcessPain(edict_t *e);
bool M_ShouldReactToPain(edict_t *self, const mod_t &mod);
void M_SetAnimation(edict_t *self, const save_mmove_t &move, bool instant = true);
bool M_AllowSpawn( edict_t * self );
// Paril: used in N64. causes them to be mad at the player
// regardless of circumstance.
constexpr size_t HACKFLAG_ATTACK_PLAYER = 1;
// used in N64, appears to change their behavior for the end scene.
constexpr size_t HACKFLAG_END_CUTSCENE = 4;
bool monster_start(edict_t *self);
void monster_start_go(edict_t *self);
// RAFAEL
void monster_fire_ionripper(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype, effects_t effect);
void monster_fire_heat(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype, float lerp_factor);
void monster_fire_dabeam(edict_t *self, int damage, bool secondary, void(*update_func)(edict_t *self));
void dabeam_update(edict_t *self, bool damage);
void monster_fire_blueblaster(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype, effects_t effect);
void G_Monster_CheckCoopHealthScaling();
// RAFAEL
// ROGUE
void monster_fire_blaster2(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed,
monster_muzzleflash_id_t flashtype, effects_t effect);
void monster_fire_tracker(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, edict_t *enemy,
monster_muzzleflash_id_t flashtype);
void monster_fire_heatbeam(edict_t *self, const vec3_t &start, const vec3_t &dir, const vec3_t &offset, int damage,
int kick, monster_muzzleflash_id_t flashtype);
void stationarymonster_start(edict_t *self);
void monster_done_dodge(edict_t *self);
// ROGUE
stuck_result_t G_FixStuckObject(edict_t *self, vec3_t check);
// this is for the count of monsters
int32_t M_SlotsLeft(edict_t *self);
// shared with monsters
constexpr spawnflags_t SPAWNFLAG_MONSTER_AMBUSH = 1_spawnflag;
constexpr spawnflags_t SPAWNFLAG_MONSTER_TRIGGER_SPAWN = 2_spawnflag;
constexpr spawnflags_t SPAWNFLAG_MONSTER_DEAD = 16_spawnflag_bit;
constexpr spawnflags_t SPAWNFLAG_MONSTER_SUPER_STEP = 17_spawnflag_bit;
constexpr spawnflags_t SPAWNFLAG_MONSTER_NO_DROP = 18_spawnflag_bit;
constexpr spawnflags_t SPAWNFLAG_MONSTER_SCENIC = 19_spawnflag_bit;
// fixbot spawnflags
constexpr spawnflags_t SPAWNFLAG_FIXBOT_FIXIT = 4_spawnflag;
constexpr spawnflags_t SPAWNFLAG_FIXBOT_TAKEOFF = 8_spawnflag;
constexpr spawnflags_t SPAWNFLAG_FIXBOT_LANDING = 16_spawnflag;
constexpr spawnflags_t SPAWNFLAG_FIXBOT_WORKING = 32_spawnflag;
//
// g_misc.c
//
void ThrowClientHead(edict_t *self, int damage);
void gib_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod);
edict_t *ThrowGib(edict_t *self, const char *gibname, int damage, gib_type_t type, float scale);
void BecomeExplosion1(edict_t *self);
void misc_viper_use(edict_t *self, edict_t *other, edict_t *activator);
void misc_strogg_ship_use(edict_t *self, edict_t *other, edict_t *activator);
void VelocityForDamage(int damage, vec3_t &v);
void ClipGibVelocity(edict_t *ent);
constexpr spawnflags_t SPAWNFLAG_PATH_CORNER_TELEPORT = 1_spawnflag;
constexpr spawnflags_t SPAWNFLAG_POINT_COMBAT_HOLD = 1_spawnflag;
// max chars for a clock string;
// " 0:00:00" is the longest string possible
// plus null terminator.
constexpr size_t CLOCK_MESSAGE_SIZE = 9;
//
// g_ai.c
//
edict_t *AI_GetSightClient(edict_t *self);
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);
constexpr float RANGE_MELEE = 20; // bboxes basically touching
constexpr float RANGE_NEAR = 440;
constexpr float RANGE_MID = 940;
// [Paril-KEX] adjusted to return an actual distance, measured
// in a way that is consistent regardless of what is fighting what
float range_to(edict_t *self, edict_t *other);
void FoundTarget(edict_t *self);
void HuntTarget(edict_t *self, bool animate_state = true);
bool infront(edict_t *self, edict_t *other);
bool visible(edict_t *self, edict_t *other, bool through_glass = true);
bool FacingIdeal(edict_t *self);
//
// g_weapon.c
//
bool fire_hit(edict_t *self, vec3_t aim, int damage, int kick);
void fire_bullet(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int kick, int hspread,
int vspread, mod_t mod);
void fire_shotgun(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int kick, int hspread,
int vspread, int count, mod_t mod);
void blaster_touch(edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self);
void fire_blaster(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, effects_t effect,
mod_t mod);
void Grenade_Explode(edict_t *ent);
void fire_grenade(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, gtime_t timer,
float damage_radius, float right_adjust, float up_adjust, bool monster);
void fire_grenade2(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, gtime_t timer,
float damage_radius, bool held);
void rocket_touch(edict_t *ent, edict_t *other, const trace_t &tr, bool other_touching_self);
edict_t *fire_rocket(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius,
int radius_damage);
void fire_rail(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int kick);
void fire_bfg(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius);
// RAFAEL
void fire_ionripper(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, effects_t effect);
void fire_heat(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius,
int radius_damage, float turn_fraction);
void fire_blueblaster(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed,
effects_t effect);
void fire_plasma(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius,
int radius_damage);
void fire_trap(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int speed);
// RAFAEL
void fire_disintegrator(edict_t *self, const vec3_t &start, const vec3_t &dir, int speed);
vec3_t P_CurrentKickAngles(edict_t *ent);
vec3_t P_CurrentKickOrigin(edict_t *ent);
void P_AddWeaponKick(edict_t *ent, const vec3_t &origin, const vec3_t &angles);
// we won't ever pierce more than this many entities for a single trace.
constexpr size_t MAX_PIERCE = 16;
// base class for pierce args; this stores
// the stuff we are piercing.
struct pierce_args_t
{
// stuff we pierced
std::array<edict_t *, MAX_PIERCE> pierced;
std::array<solid_t, MAX_PIERCE> pierce_solidities;
size_t num_pierced = 0;
// the last trace that was done, when piercing stopped
trace_t tr;
// mark entity as pierced
inline bool mark(edict_t *ent);
// restore entities' previous solidities
inline void restore();
// we hit an entity; return false to stop the piercing.
// you can adjust the mask for the re-trace (for water, etc).
virtual bool hit(contents_t &mask, vec3_t &end) = 0;
virtual ~pierce_args_t()
{
restore();
}
};
void pierce_trace(const vec3_t &start, const vec3_t &end, edict_t *ignore, pierce_args_t &pierce, contents_t mask);
//
// g_ptrail.c
//
void PlayerTrail_Add(edict_t *player);
void PlayerTrail_Destroy(edict_t *player);
edict_t *PlayerTrail_Pick(edict_t *self, bool next);
//
// g_client.c
//
constexpr spawnflags_t SPAWNFLAG_CHANGELEVEL_CLEAR_INVENTORY = 8_spawnflag;
constexpr spawnflags_t SPAWNFLAG_CHANGELEVEL_NO_END_OF_UNIT = 16_spawnflag;
constexpr spawnflags_t SPAWNFLAG_CHANGELEVEL_FADE_OUT = 32_spawnflag;
constexpr spawnflags_t SPAWNFLAG_CHANGELEVEL_IMMEDIATE_LEAVE = 64_spawnflag;
void respawn(edict_t *ent);
void BeginIntermission(edict_t *targ);
void PutClientInServer(edict_t *ent);
void InitClientPersistant(edict_t *ent, gclient_t *client);
void InitClientResp(gclient_t *client);
void InitBodyQue();
void ClientBeginServerFrame(edict_t *ent);
void ClientUserinfoChanged(edict_t *ent, const char *userinfo);
void P_AssignClientSkinnum(edict_t *ent);
void P_ForceFogTransition(edict_t *ent, bool instant);
void P_SendLevelPOI(edict_t *ent);
unsigned int P_GetLobbyUserNum( const edict_t * player );
void G_UpdateLevelEntry();
void G_EndOfUnitMessage();
bool SelectSpawnPoint(edict_t *ent, vec3_t &origin, vec3_t &angles, bool force_spawn, bool &landmark);
struct select_spawn_result_t
{
edict_t *spot;
bool any_valid = false; // set if a spawn point was found, even if it was taken
};
select_spawn_result_t SelectDeathmatchSpawnPoint(bool farthest, bool force_spawn, bool fallback_to_ctf_or_start);
void G_PostRespawn(edict_t *self);
//
// g_player.c
//
void player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod);
//
// g_svcmds.c
//
void ServerCommand();
bool SV_FilterPacket(const char *from);
//
// p_view.c
//
void ClientEndServerFrame(edict_t *ent);
void G_LagCompensate(edict_t *from_player, const vec3_t &start, const vec3_t &dir);
void G_UnLagCompensate();
//
// p_hud.c
//
void MoveClientToIntermission(edict_t *client);
void G_SetStats(edict_t *ent);
void G_SetCoopStats(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);
void G_ReportMatchDetails(bool is_end);
//
// p_weapon.c
//
void PlayerNoise(edict_t *who, const vec3_t &where, player_noise_t type);
void P_ProjectSource(edict_t *ent, const vec3_t &angles, vec3_t distance, vec3_t &result_start, vec3_t &result_dir);
void NoAmmoWeaponChange(edict_t *ent, bool sound);
void G_RemoveAmmo(edict_t *ent);
void G_RemoveAmmo(edict_t *ent, int32_t quantity);
void Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST,
int FRAME_DEACTIVATE_LAST, const int *pause_frames, const int *fire_frames,
void (*fire)(edict_t *ent));
void Weapon_Repeating(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST,
int FRAME_DEACTIVATE_LAST, const int *pause_frames, void (*fire)(edict_t *ent));
void Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_PRIME_SOUND,
const char *prime_sound, int FRAME_THROW_HOLD, int FRAME_THROW_FIRE, const int *pause_frames,
int EXPLODE, const char *primed_sound, void (*fire)(edict_t *ent, bool held), bool extra_idle_frame);
byte P_DamageModifier(edict_t *ent);
bool G_CheckInfiniteAmmo(gitem_t *item);
void Weapon_PowerupSound(edict_t *ent);
constexpr gtime_t GRENADE_TIMER = 3_sec;
constexpr float GRENADE_MINSPEED = 400.f;
constexpr float GRENADE_MAXSPEED = 800.f;
extern bool is_quad;
// RAFAEL
extern bool is_quadfire;
// RAFAEL
extern player_muzzle_t is_silenced;
// ROGUE
extern byte damage_multiplier;
// ROGUE
//
// m_move.c
//
bool M_CheckBottom_Fast_Generic(const vec3_t &absmins, const vec3_t &absmaxs, bool ceiling);
bool M_CheckBottom_Slow_Generic(const vec3_t &origin, const vec3_t &absmins, const vec3_t &absmaxs, edict_t *ignore, contents_t mask, bool ceiling, bool allow_any_step_height);
bool M_CheckBottom(edict_t *ent);
bool SV_CloseEnough(edict_t *ent, edict_t *goal, float dist);
bool M_walkmove(edict_t *ent, float yaw, float dist);
void M_MoveToGoal(edict_t *ent, float dist);
void M_ChangeYaw(edict_t *ent);
bool ai_check_move(edict_t *self, float dist);
//
// g_phys.c
//
constexpr float sv_friction = 6;
constexpr float sv_waterfriction = 1;
void G_RunEntity(edict_t *ent);
bool SV_RunThink(edict_t *ent);
void SV_AddRotationalFriction(edict_t *ent);
void SV_AddGravity(edict_t *ent);
void SV_CheckVelocity(edict_t *ent);
void SV_FlyMove(edict_t *ent, float time, contents_t mask);
contents_t G_GetClipMask(edict_t *ent);
void G_Impact(edict_t *e1, const trace_t &trace);
//
// g_main.c
//
void SaveClientData();
void FetchClientEntData(edict_t *ent);
void EndDMLevel();
//
// g_chase.c
//
void UpdateChaseCam(edict_t *ent);
void ChaseNext(edict_t *ent);
void ChasePrev(edict_t *ent);
void GetChaseTarget(edict_t *ent);
//====================
// ROGUE PROTOTYPES
//
// g_newweap.c
//
void fire_flechette(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, int kick);
void fire_prox(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed);
void fire_nuke(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int speed);
bool fire_player_melee(edict_t *self, const vec3_t &start, const vec3_t &aim, int reach, int damage, int kick, mod_t mod);
void fire_tesla(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed);
void fire_blaster2(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int damage, int speed, effects_t effect,
bool hyper);
void fire_heatbeam(edict_t *self, const vec3_t &start, const vec3_t &aimdir, const vec3_t &offset, int damage, int kick,
bool monster);
void fire_tracker(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, edict_t *enemy);
//
// g_newai.c
//
bool blocked_checkplat(edict_t *self, float dist);
enum class blocked_jump_result_t
{
NO_JUMP,
JUMP_TURN,
JUMP_JUMP_UP,
JUMP_JUMP_DOWN
};
blocked_jump_result_t blocked_checkjump(edict_t *self, float dist);
bool monsterlost_checkhint(edict_t *self);
bool inback(edict_t *self, edict_t *other);
float realrange(edict_t *self, edict_t *other);
edict_t *SpawnBadArea(const vec3_t &mins, const vec3_t &maxs, gtime_t lifespan, edict_t *owner);
edict_t *CheckForBadArea(edict_t *ent);
bool MarkTeslaArea(edict_t *self, edict_t *tesla);
void InitHintPaths();
void PredictAim(edict_t *self, edict_t *target, const vec3_t &start, float bolt_speed, bool eye_height, float offset, vec3_t *aimdir,
vec3_t *aimpoint);
bool M_CalculatePitchToFire(edict_t *self, const vec3_t &target, const vec3_t &start, vec3_t &aim, float speed, float time_remaining, bool mortar, bool destroy_on_touch = false);
bool below(edict_t *self, edict_t *other);
void drawbbox(edict_t *self);
void M_MonsterDodge(edict_t *self, edict_t *attacker, gtime_t eta, trace_t *tr, bool gravity);
void monster_duck_down(edict_t *self);
void monster_duck_hold(edict_t *self);
void monster_duck_up(edict_t *self);
bool has_valid_enemy(edict_t *self);
void TargetTesla(edict_t *self, edict_t *tesla);
void hintpath_stop(edict_t *self);
edict_t *PickCoopTarget(edict_t *self);
int CountPlayers();
bool monster_jump_finished(edict_t *self);
void BossExplode(edict_t *self);
// g_rogue_func
void plat2_spawn_danger_area(edict_t *ent);
void plat2_kill_danger_area(edict_t *ent);
// g_rogue_spawn
edict_t *CreateMonster(const vec3_t &origin, const vec3_t &angles, const char *classname);
edict_t *CreateFlyMonster(const vec3_t &origin, const vec3_t &angles, const vec3_t &mins, const vec3_t &maxs,
const char *classname);
edict_t *CreateGroundMonster(const vec3_t &origin, const vec3_t &angles, const vec3_t &mins, const vec3_t &maxs,
const char *classname, float height);
bool FindSpawnPoint(const vec3_t &startpoint, const vec3_t &mins, const vec3_t &maxs, vec3_t &spawnpoint,
float maxMoveUp, bool drop = true);
bool CheckSpawnPoint(const vec3_t &origin, const vec3_t &mins, const vec3_t &maxs);
bool CheckGroundSpawnPoint(const vec3_t &origin, const vec3_t &entMins, const vec3_t &entMaxs, float height,
float gravity);
void SpawnGrow_Spawn(const vec3_t &startpos, float start_size, float end_size);
void Widowlegs_Spawn(const vec3_t &startpos, const vec3_t &angles);
// g_rogue_items
bool Pickup_Nuke(edict_t *ent, edict_t *other);
void Use_IR(edict_t *ent, gitem_t *item);
void Use_Double(edict_t *ent, gitem_t *item);
void Use_Nuke(edict_t *ent, gitem_t *item);
void Use_Doppleganger(edict_t *ent, gitem_t *item);
bool Pickup_Doppleganger(edict_t *ent, edict_t *other);
bool Pickup_Sphere(edict_t *ent, edict_t *other);
void Use_Defender(edict_t *ent, gitem_t *item);
void Use_Hunter(edict_t *ent, gitem_t *item);
void Use_Vengeance(edict_t *ent, gitem_t *item);
void Item_TriggeredSpawn(edict_t *self, edict_t *other, edict_t *activator);
void SetTriggeredSpawn(edict_t *ent);
//
// g_sphere.c
//
void Defender_Launch(edict_t *self);
void Vengeance_Launch(edict_t *self);
void Hunter_Launch(edict_t *self);
//
// g_newdm.c
//
void InitGameRules();
item_id_t DoRandomRespawn(edict_t *ent);
void PrecacheForRandomRespawn();
bool Tag_PickupToken(edict_t *ent, edict_t *other);
void Tag_DropToken(edict_t *ent, gitem_t *item);
void fire_doppleganger(edict_t *ent, const vec3_t &start, const vec3_t &aimdir);
//
// p_client.c
//
void RemoveAttackingPainDaemons(edict_t *self);
bool G_ShouldPlayersCollide(bool weaponry);
bool P_UseCoopInstancedItems();
constexpr spawnflags_t SPAWNFLAG_LANDMARK_KEEP_Z = 1_spawnflag;
// [Paril-KEX] convenience functions that returns true
// if the powerup should be 'active' (false to disable,
// will flash at 500ms intervals after 3 sec)
[[nodiscard]] constexpr bool G_PowerUpExpiringRelative(gtime_t left)
{
return left.milliseconds() > 3000 || (left.milliseconds() % 1000) < 500;
}
[[nodiscard]] constexpr bool G_PowerUpExpiring(gtime_t time)
{
return G_PowerUpExpiringRelative(time - level.time);
}
// ZOID
#include "ctf/g_ctf.h"
#include "ctf/p_ctf_menu.h"
// ZOID
//============================================================================
// client_t->anim_priority
enum anim_priority_t
{
ANIM_BASIC, // stand / run
ANIM_WAVE,
ANIM_JUMP,
ANIM_PAIN,
ANIM_ATTACK,
ANIM_DEATH,
// flags
ANIM_REVERSED = bit_v<8>
};
MAKE_ENUM_BITFLAGS(anim_priority_t);
// height fog data values
struct height_fog_t
{
// r g b dist
std::array<float, 4> start;
std::array<float, 4> end;
float falloff;
float density;
inline bool operator==(const height_fog_t &o) const
{
return start == o.start && end == o.end && falloff == o.falloff && density == o.density;
}
};
constexpr gtime_t SELECTED_ITEM_TIME = 3_sec;
enum bmodel_animstyle_t : int32_t
{
BMODEL_ANIM_FORWARDS,
BMODEL_ANIM_BACKWARDS,
BMODEL_ANIM_RANDOM
};
struct bmodel_anim_t
{
// range, inclusive
int32_t start, end;
bmodel_animstyle_t style;
int32_t speed; // in milliseconds
bool nowrap;
int32_t alt_start, alt_end;
bmodel_animstyle_t alt_style;
int32_t alt_speed; // in milliseconds
bool alt_nowrap;
// game-only
bool enabled;
bool alternate, currently_alternate;
gtime_t next_tick;
};
// never turn back shield on automatically; this is
// the legacy behavior.
constexpr int32_t AUTO_SHIELD_MANUAL = -1;
// when it is >= 0, the shield will turn back on
// when we have that many cells in our inventory
// if possible.
constexpr int32_t AUTO_SHIELD_AUTO = 0;
// client data that stays across multiple level loads
struct client_persistant_t
{
char userinfo[MAX_INFO_STRING];
char social_id[MAX_INFO_VALUE];
char netname[MAX_NETNAME];
handedness_t hand;
auto_switch_t autoswitch;
int32_t autoshield; // see AUTO_SHIELD_*
bool connected, spawned; // a loadgame will leave valid entities that
// just don't have a connection yet
// values saved and restored from edicts when changing levels
int32_t health;
int32_t max_health;
ent_flags_t savedFlags;
item_id_t selected_item;
gtime_t selected_item_time;
std::array<int32_t, IT_TOTAL> inventory;
// ammo capacities
std::array<int16_t, AMMO_MAX> max_ammo;
gitem_t *weapon;
gitem_t *lastweapon;
int32_t power_cubes; // used for tracking the cubes in coop games
int32_t score; // for calculating total unit score in coop games
int32_t game_help1changed, game_help2changed;
int32_t helpchanged; // flash F1 icon if non 0, play sound
// and increment only if 1, 2, or 3
gtime_t help_time;
bool spectator; // client wants to be a spectator
bool bob_skip; // [Paril-KEX] client wants no movement bob
// [Paril-KEX] fog that we want to achieve; density rgb skyfogfactor
std::array<float, 5> wanted_fog;
height_fog_t wanted_heightfog;
// relative time value, copied from last touched trigger
gtime_t fog_transition_time;
gtime_t megahealth_time; // relative megahealth time value
int32_t lives; // player lives left (1 = no respawns remaining)
uint8_t n64_crouch_warn_times;
gtime_t n64_crouch_warning;
};
// client data that stays across deathmatch respawns
struct client_respawn_t
{
client_persistant_t coop_respawn; // what to set client->pers to on a respawn
gtime_t entertime; // level.time the client entered the game
int32_t score; // frags, etc
vec3_t cmd_angles; // angles sent over in the last command
bool spectator; // client is a spectator
// ZOID
ctfteam_t ctf_team; // CTF team
int32_t ctf_state;
gtime_t ctf_lasthurtcarrier;
gtime_t ctf_lastreturnedflag;
gtime_t ctf_flagsince;
gtime_t ctf_lastfraggedcarrier;
bool id_state;
gtime_t lastidtime;
bool voted; // for elections
bool ready;
bool admin;
ghost_t *ghost; // for ghost codes
// ZOID
};
// [Paril-KEX] seconds until we are fully invisible after
// making a racket
constexpr gtime_t INVISIBILITY_TIME = 2_sec;
// max number of individual damage indicators we'll track
constexpr size_t MAX_DAMAGE_INDICATORS = 4;
struct damage_indicator_t
{
vec3_t from;
int32_t health, armor, power;
};
// time between ladder sounds
constexpr gtime_t LADDER_SOUND_TIME = 300_ms;
// time after damage that we can't respawn on a player for
constexpr gtime_t COOP_DAMAGE_RESPAWN_TIME = 2000_ms;
// this structure is cleared on each PutClientInServer(),
// except for 'client->pers'
struct gclient_t
{
// shared with server; do not touch members until the "private" section
player_state_t ps; // communicated by server to clients
int32_t 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 showeou; // end of unit screen
bool showinventory; // set layout stat
bool showhelp;
button_t buttons;
button_t oldbuttons;
button_t latched_buttons;
usercmd_t cmd; // last CMD send
// weapon cannot fire until this time is up
gtime_t weapon_fire_finished;
// time between processing individual animation frames
gtime_t weapon_think_time;
// if we latched fire between server frames but before
// the weapon fire finish has elapsed, we'll "press" it
// automatically when we have a chance
bool weapon_fire_buffered;
bool weapon_thunk;
gitem_t *newweapon;
// sum up damage over an entire frame, so
// shotgun blasts give a single big kick
int32_t damage_armor; // damage absorbed by armor
int32_t damage_parmor; // damage absorbed by power armor
int32_t damage_blood; // damage taken out of health
int32_t damage_knockback; // impact damage
vec3_t damage_from; // origin for vector calculation
damage_indicator_t damage_indicators[MAX_DAMAGE_INDICATORS];
uint8_t num_damage_indicators;
float killer_yaw; // when dead, look at killer
weaponstate_t weaponstate;
struct {
vec3_t angles, origin;
gtime_t time, total;
} kick;
gtime_t quake_time;
vec3_t kick_origin;
float v_dmg_roll, v_dmg_pitch;
gtime_t v_dmg_time; // damage kicks
gtime_t fall_time;
float fall_value; // for view drop on fall
float damage_alpha;
float bonus_alpha;
vec3_t damage_blend;
vec3_t v_angle, v_forward; // aiming direction
float bobtime; // so off-ground doesn't change it
vec3_t oldviewangles;
vec3_t oldvelocity;
edict_t *oldgroundentity; // [Paril-KEX]
gtime_t flash_time; // [Paril-KEX] for high tickrate
gtime_t next_drown_time;
water_level_t old_waterlevel;
int32_t breather_sound;
int32_t machinegun_shots; // for weapon raising
// animation vars
int32_t anim_end;
anim_priority_t anim_priority;
bool anim_duck;
bool anim_run;
gtime_t anim_time;
// powerup timers
gtime_t quad_time;
gtime_t invincible_time;
gtime_t breather_time;
gtime_t enviro_time;
gtime_t invisible_time;
bool grenade_blew_up;
gtime_t grenade_time, grenade_finished_time;
// RAFAEL
gtime_t quadfire_time;
// RAFAEL
int32_t silencer_shots;
int32_t weapon_sound;
gtime_t pickup_msg_time;
gtime_t flood_locktill; // locked from talking
gtime_t flood_when[10]; // when messages were said
int32_t flood_whenhead; // head pointer for when said
gtime_t respawn_time; // can respawn when time > this
edict_t *chase_target; // player we are chasing
bool update_chase; // need to update chase info?
//=======
// ROGUE
gtime_t double_time;
gtime_t ir_time;
gtime_t nuke_time;
gtime_t tracker_pain_time;
edict_t *owned_sphere; // this points to the player's sphere
// ROGUE
//=======
gtime_t empty_click_sound;
// ZOID
bool inmenu; // in menu
pmenuhnd_t *menu; // current menu
gtime_t menutime; // time to update menu
bool menudirty;
edict_t *ctf_grapple; // entity of grapple
int32_t ctf_grapplestate; // true if pulling
gtime_t ctf_grapplereleasetime; // time of grapple release
gtime_t ctf_regentime; // regen tech
gtime_t ctf_techsndtime;
gtime_t ctf_lasttechmsg;
// ZOID
// used for player trails.
edict_t *trail_head, *trail_tail;
// whether to use weapon chains
bool no_weapon_chains;
// seamless level transitions
bool landmark_free_fall;
const char* landmark_name;
vec3_t landmark_rel_pos; // position relative to landmark, un-rotated from landmark angle
gtime_t landmark_noise_time;
gtime_t invisibility_fade_time; // [Paril-KEX] at this time, the player will be mostly fully cloaked
gtime_t chase_msg_time; // to prevent CTF message spamming
int32_t menu_sign; // menu sign
vec3_t last_ladder_pos; // for ladder step sounds
gtime_t last_ladder_sound;
coop_respawn_t coop_respawn_state;
gtime_t last_damage_time;
// [Paril-KEX] these are now per-player, to work better in coop
edict_t *sight_entity;
gtime_t sight_entity_time;
edict_t *sound_entity;
gtime_t sound_entity_time;
edict_t *sound2_entity;
gtime_t sound2_entity_time;
// saved positions for lag compensation
uint8_t num_lag_origins; // 0 to MAX_LAG_ORIGINS, how many we can go back
uint8_t next_lag_origin; // the next one to write to
bool is_lag_compensated;
vec3_t lag_restore_origin;
// for high tickrate weapon angles
vec3_t slow_view_angles;
gtime_t slow_view_angle_time;
// not saved
bool help_draw_points;
size_t help_draw_index, help_draw_count;
gtime_t help_draw_time;
uint32_t step_frame;
int32_t help_poi_image;
vec3_t help_poi_location;
// only set temporarily
bool awaiting_respawn;
gtime_t respawn_timeout; // after this time, force a respawn
// [Paril-KEX] current active fog values; density rgb skyfogfactor
std::array<float, 5> fog;
height_fog_t heightfog;
gtime_t last_attacker_time;
};
// ==========================================
// PLAT 2
// ==========================================
enum plat2flags_t
{
PLAT2_NONE = 0,
PLAT2_CALLED = 1,
PLAT2_MOVING = 2,
PLAT2_WAITING = 4
};
MAKE_ENUM_BITFLAGS(plat2flags_t);
#include <bitset>
struct edict_t
{
edict_t() = delete;
edict_t(const edict_t &) = delete;
edict_t(edict_t &&) = delete;
// shared with server; do not touch members until the "private" section
entity_state_t s;
gclient_t *client; // nullptr if not a player
// the server expects the first part
// of gclient_t to be a player_state_t
// but the rest of it is opaque
sv_entity_t sv; // read only info about this entity for the server
bool inuse;
// world linkage data
bool linked;
int32_t linkcount;
int32_t areanum, areanum2;
svflags_t svflags;
vec3_t mins, maxs;
vec3_t absmin, absmax, size;
solid_t solid;
contents_t clipmask;
edict_t *owner;
//================================
// private to game
int32_t spawn_count; // [Paril-KEX] used to differentiate different entities that may be in the same slot
movetype_t movetype;
ent_flags_t flags;
const char *model;
gtime_t freetime; // sv.time when the object was freed
//
// only used locally in game, not by server
//
const char *message;
const char *classname;
spawnflags_t spawnflags;
gtime_t timestamp;
float angle; // set in qe3, -1 = up, -2 = down
const char *target;
const char *targetname;
const char *killtarget;
const char *team;
const char *pathtarget;
const char *deathtarget;
const char *healthtarget;
const char *itemtarget; // [Paril-KEX]
const char *combattarget;
edict_t *target_ent;
float speed, accel, decel;
vec3_t movedir;
vec3_t pos1, pos2, pos3;
vec3_t velocity;
vec3_t avelocity;
int32_t mass;
gtime_t 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;
gtime_t nextthink;
save_prethink_t prethink;
save_prethink_t postthink;
save_think_t think;
save_touch_t touch;
save_use_t use;
save_pain_t pain;
save_die_t die;
gtime_t touch_debounce_time; // are all these legit? do we need more/less of them?
gtime_t pain_debounce_time;
gtime_t damage_debounce_time;
gtime_t fly_sound_debounce_time; // move to clientinfo
gtime_t last_move_time;
int32_t health;
int32_t max_health;
int32_t gib_health;
gtime_t show_hostile;
gtime_t powerarmor_time;
const char *map; // target_changelevel
int32_t viewheight; // height above origin where eyesight is determined
bool deadflag;
bool takedamage;
int32_t dmg;
int32_t radius_dmg;
float dmg_radius;
int32_t sounds; // make this a spawntemp var?
int32_t count;
edict_t *chain;
edict_t *enemy;
edict_t *oldenemy;
edict_t *activator;
edict_t *groundentity;
int32_t groundentity_linkcount;
edict_t *teamchain;
edict_t *teammaster;
edict_t *mynoise; // can go in client only
edict_t *mynoise2;
int32_t noise_index;
int32_t noise_index2;
float volume;
float attenuation;
// timing variables
float wait;
float delay; // before firing targets
float random;
gtime_t teleport_time;
contents_t watertype;
water_level_t waterlevel;
vec3_t move_origin;
vec3_t move_angles;
int32_t style; // also used as areaportal number
gitem_t *item; // for bonus items
// common data blocks
moveinfo_t moveinfo;
monsterinfo_t monsterinfo;
//=========
// ROGUE
plat2flags_t plat2flags;
vec3_t offset;
vec3_t gravityVector;
edict_t *bad_area;
edict_t *hint_chain;
edict_t *monster_hint_chain;
edict_t *target_hint_chain;
int32_t hint_chain_id;
// ROGUE
//=========
char clock_message[CLOCK_MESSAGE_SIZE];
// Paril: we died on this frame, apply knockback even if we're dead
gtime_t dead_time;
// used for dabeam monsters
edict_t *beam, *beam2;
// proboscus for Parasite
edict_t *proboscus;
// for vooping things
edict_t *disintegrator;
gtime_t disintegrator_time;
int32_t hackflags; // n64
// fog stuff
struct {
vec3_t color;
float density;
float sky_factor;
vec3_t color_off;
float density_off;
float sky_factor_off;
} fog;
struct {
float falloff;
float density;
vec3_t start_color;
float start_dist;
vec3_t end_color;
float end_dist;
float falloff_off;
float density_off;
vec3_t start_color_off;
float start_dist_off;
vec3_t end_color_off;
float end_dist_off;
} heightfog;
// instanced coop items
std::bitset<MAX_CLIENTS> item_picked_up_by;
gtime_t slime_debounce_time;
// [Paril-KEX]
bmodel_anim_t bmodel_anim;
mod_t lastMOD;
const char *style_on, *style_off;
uint32_t crosslevel_flags;
// NOTE: if adding new elements, make sure to add them
// in g_save.cpp too!
};
//=============
// ROGUE
constexpr spawnflags_t SPHERE_DEFENDER = 0x0001_spawnflag;
constexpr spawnflags_t SPHERE_HUNTER = 0x0002_spawnflag;
constexpr spawnflags_t SPHERE_VENGEANCE = 0x0004_spawnflag;
constexpr spawnflags_t SPHERE_DOPPLEGANGER = 0x10000_spawnflag;
constexpr spawnflags_t SPHERE_TYPE = SPHERE_DEFENDER | SPHERE_HUNTER | SPHERE_VENGEANCE;
constexpr spawnflags_t SPHERE_FLAGS = SPHERE_DOPPLEGANGER;
//
// deathmatch games
//
enum
{
RDM_TAG = 2,
RDM_DEATHBALL = 3
};
struct dm_game_rt
{
void (*GameInit)();
void (*PostInitSetup)();
void (*ClientBegin)(edict_t *ent);
bool (*SelectSpawnPoint)(edict_t *ent, vec3_t &origin, vec3_t &angles, bool force_spawn);
void (*PlayerDeath)(edict_t *targ, edict_t *inflictor, edict_t *attacker);
void (*Score)(edict_t *attacker, edict_t *victim, int scoreChange, const mod_t &mod);
void (*PlayerEffects)(edict_t *ent);
void (*DogTag)(edict_t *ent, edict_t *killer, const char **pic);
void (*PlayerDisconnect)(edict_t *ent);
int (*ChangeDamage)(edict_t *targ, edict_t *attacker, int damage, mod_t mod);
int (*ChangeKnockback)(edict_t *targ, edict_t *attacker, int knockback, mod_t mod);
int (*CheckDMRules)();
};
extern dm_game_rt DMGame;
// ROGUE
//============
// [Paril-KEX]
inline void monster_footstep(edict_t *self)
{
if (self->groundentity)
self->s.event = EV_OTHER_FOOTSTEP;
}
// [Kex] helpers
// TFilter must be a type that is invokable with the
// signature bool(edict_t *); it must return true if
// the entity given is valid for the given filter
template<typename TFilter>
struct entity_iterator_t
{
using iterator_category = std::random_access_iterator_tag;
using value_type = edict_t *;
using reference = edict_t *;
using pointer = edict_t *;
using difference_type = ptrdiff_t;
private:
uint32_t index;
uint32_t end_index; // where the end index is located for this iterator
// index < globals.num_edicts are valid
TFilter filter;
// this doubles as the "end" iterator
inline bool is_out_of_range(uint32_t i) const
{
return i >= end_index;
}
inline bool is_out_of_range() const
{
return is_out_of_range(index);
}
inline void throw_if_out_of_range() const
{
if (is_out_of_range())
throw std::out_of_range("index");
}
inline difference_type clamped_index() const
{
if (is_out_of_range())
return end_index;
return index;
}
public:
// note: index is not affected by filter. it is up to
// the caller to ensure this index is filtered.
constexpr entity_iterator_t(uint32_t i, uint32_t end_index = -1) : index(i), end_index((end_index >= globals.num_edicts) ? globals.num_edicts : end_index) { }
inline reference operator*() { throw_if_out_of_range(); return &g_edicts[index]; }
inline pointer operator->() { throw_if_out_of_range(); return &g_edicts[index]; }
inline entity_iterator_t &operator++()
{
throw_if_out_of_range();
return *this = *this + 1;
}
inline entity_iterator_t &operator--()
{
throw_if_out_of_range();
return *this = *this - 1;
}
inline difference_type operator-(const entity_iterator_t &it) const
{
return clamped_index() - it.clamped_index();
}
inline entity_iterator_t operator+(const difference_type &offset) const
{
entity_iterator_t it(index + offset, end_index);
// move in the specified direction, only stopping if we
// run out of range or find a filtered entity
while (!is_out_of_range(it.index) && !filter(*it))
it.index += offset > 0 ? 1 : -1;
return it;
}
// + -1 and - 1 are the same (and - -1 & + 1)
inline entity_iterator_t operator-(const difference_type &offset) const { return *this + (-offset); }
// comparison. hopefully this won't break anything, but == and != use the
// clamped index (so -1 and num_edicts will be equal technically since they
// are the same "invalid" entity) but <= and >= will affect them properly.
inline bool operator==(const entity_iterator_t &it) const { return clamped_index() == it.clamped_index(); }
inline bool operator!=(const entity_iterator_t &it) const { return clamped_index() != it.clamped_index(); }
inline bool operator<(const entity_iterator_t &it) const { return index < it.index; }
inline bool operator>(const entity_iterator_t &it) const { return index > it.index; }
inline bool operator<=(const entity_iterator_t &it) const { return index <= it.index; }
inline bool operator>=(const entity_iterator_t &it) const { return index >= it.index; }
inline edict_t *operator[](const difference_type &offset) const { return *(*this + offset); }
};
// iterate over range of entities, with the specified filter.
// can be "open-ended" (automatically expand with num_edicts)
// by leaving the max unset.
template<typename TFilter>
struct entity_iterable_t
{
private:
uint32_t begin_index, end_index;
TFilter filter;
// find the first entity that matches the filter, from the specified index,
// in the specified direction
inline uint32_t find_matched_index(uint32_t index, int32_t direction)
{
while (index < globals.num_edicts && !filter(&g_edicts[index]))
index += direction;
return index;
}
public:
// iterate all allocated entities that match the filter,
// including ones allocated after this iterator is constructed
inline entity_iterable_t<TFilter>() : begin_index(find_matched_index(0, 1)), end_index(game.maxentities) { }
// iterate all allocated entities that match the filter from the specified begin offset
// including ones allocated after this iterator is constructed
inline entity_iterable_t<TFilter>(uint32_t start) : begin_index(find_matched_index(start, 1)), end_index(game.maxentities) { }
// iterate all allocated entities that match the filter from the specified begin offset
// to the specified INCLUSIVE end offset (or the first entity that matches before it),
// including end itself but not ones that may appear after this iterator is done
inline entity_iterable_t<TFilter>(uint32_t start, uint32_t end) :
begin_index(find_matched_index(start, 1)),
end_index(find_matched_index(end, -1) + 1)
{
}
inline entity_iterator_t<TFilter> begin() const { return entity_iterator_t<TFilter>(begin_index, end_index); }
inline entity_iterator_t<TFilter> end() const { return end_index; }
};
// inuse players that are connected; may not be spawned yet, however
struct active_players_filter_t
{
inline bool operator()(edict_t *ent) const
{
return (ent->inuse && ent->client && ent->client->pers.connected);
}
};
inline entity_iterable_t<active_players_filter_t> active_players()
{
return entity_iterable_t<active_players_filter_t> { 1u, game.maxclients };
}
struct gib_def_t
{
size_t count;
const char *gibname;
float scale;
gib_type_t type;
constexpr gib_def_t(size_t count, const char *gibname) :
count(count),
gibname(gibname),
scale(1.0f),
type(GIB_NONE)
{
}
constexpr gib_def_t(size_t count, const char *gibname, gib_type_t type) :
count(count),
gibname(gibname),
scale(1.0f),
type(type)
{
}
constexpr gib_def_t(size_t count, const char *gibname, float scale) :
count(count),
gibname(gibname),
scale(scale),
type(GIB_NONE)
{
}
constexpr gib_def_t(size_t count, const char *gibname, float scale, gib_type_t type) :
count(count),
gibname(gibname),
scale(scale),
type(type)
{
}
constexpr gib_def_t(const char *gibname, float scale, gib_type_t type) :
count(1),
gibname(gibname),
scale(scale),
type(type)
{
}
constexpr gib_def_t(const char *gibname, float scale) :
count(1),
gibname(gibname),
scale(scale),
type(GIB_NONE)
{
}
constexpr gib_def_t(const char *gibname, gib_type_t type) :
count(1),
gibname(gibname),
scale(1.0f),
type(type)
{
}
constexpr gib_def_t(const char *gibname) :
count(1),
gibname(gibname),
scale(1.0f),
type(GIB_NONE)
{
}
};
// convenience function to throw different gib types
// NOTE: always throw the head gib *last* since self's size is used
// to position the gibs!
inline void ThrowGibs(edict_t *self, int32_t damage, std::initializer_list<gib_def_t> gibs)
{
for (auto &gib : gibs)
for (size_t i = 0; i < gib.count; i++)
ThrowGib(self, gib.gibname, damage, gib.type, gib.scale * (self->s.scale ? self->s.scale : 1));
}
inline bool M_CheckGib(edict_t *self, const mod_t &mod)
{
if (self->deadflag)
{
if (mod.id == MOD_CRUSH)
return true;
}
return self->health <= self->gib_health;
}
// Fmt support for entities
template<>
struct fmt::formatter<edict_t>
{
template<typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
return ctx.begin();
}
template<typename FormatContext>
auto format(const edict_t &p, FormatContext &ctx) -> decltype(ctx.out())
{
if (p.linked)
return fmt::format_to(ctx.out(), FMT_STRING("{} @ {}"), p.classname, (p.absmax + p.absmin) * 0.5f);
return fmt::format_to(ctx.out(), FMT_STRING("{} @ {}"), p.classname, p.s.origin);
}
};
// POI tags used by this mod
enum pois_t : uint16_t
{
POI_OBJECTIVE = MAX_EDICTS, // current objective
POI_RED_FLAG, // red flag/carrier
POI_BLUE_FLAG, // blue flag/carrier
POI_PING,
POI_PING_END = POI_PING + MAX_CLIENTS - 1,
};
// implementation of pierce stuff
inline bool pierce_args_t::mark(edict_t *ent)
{
// ran out of pierces
if (num_pierced == MAX_PIERCE)
return false;
pierced[num_pierced] = ent;
pierce_solidities[num_pierced] = ent->solid;
num_pierced++;
ent->solid = SOLID_NOT;
gi.linkentity(ent);
return true;
}
// implementation of pierce stuff
inline void pierce_args_t::restore()
{
for (size_t i = 0; i < num_pierced; i++)
{
auto &ent = pierced[i];
ent->solid = pierce_solidities[i];
gi.linkentity(ent);
}
num_pierced = 0;
}