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

2315 lines
82 KiB
C++

// Copyright (c) ZeniMax Media Inc.
// Licensed under the GNU General Public License 2.0.
// game.h - game API stuff
#pragma once
#include <array>
#include <limits.h>
// compatibility with legacy float[3] stuff for engine
#ifdef GAME_INCLUDE
using gvec3_t = vec3_t;
using gvec3_ptr_t = vec3_t *;
using gvec3_cptr_t = const vec3_t *;
using gvec3_ref_t = vec3_t &;
using gvec3_cref_t = const vec3_t &;
using gvec4_t = std::array<float, 4>;
#else
using gvec3_t = float[3];
using gvec3_ptr_t = gvec3_t;
using gvec3_ref_t = gvec3_t;
using gvec3_cref_t = const gvec3_t;
using gvec3_cptr_t = const gvec3_t;
using gvec4_t = float[4];
#endif
constexpr size_t MAX_SPLIT_PLAYERS = 8;
struct rgba_t
{
uint8_t r, g, b, a;
};
struct vec2_t
{
float x, y;
};
constexpr rgba_t rgba_red { 255, 0, 0, 255 };
constexpr rgba_t rgba_blue { 0, 0, 255, 255 };
constexpr rgba_t rgba_green { 0, 255, 0, 255 };
constexpr rgba_t rgba_yellow { 255, 255, 0, 255 };
constexpr rgba_t rgba_white { 255, 255, 255, 255 };
constexpr rgba_t rgba_black { 0, 0, 0, 255 };
constexpr rgba_t rgba_cyan { 0, 255, 255, 255 };
constexpr rgba_t rgba_magenta { 255, 0, 255, 255 };
constexpr rgba_t rgba_orange { 116, 61, 50, 255 };
constexpr size_t MAX_NETNAME = 32;
constexpr float STEPSIZE = 18.0f;
// ugly hack to support bitflags on enums
// and use enable_if to prevent confusing cascading
// errors if you use the operators wrongly
#define MAKE_ENUM_BITFLAGS(T) \
constexpr T operator~(const T &v) \
{ \
return static_cast<T>(~static_cast<std::underlying_type_t<T>>(v)); \
} \
constexpr T operator|(const T &v, const T &v2) \
{ \
return static_cast<T>(static_cast<std::underlying_type_t<T>>(v) | static_cast<std::underlying_type_t<T>>(v2)); \
} \
constexpr T operator&(const T &v, const T &v2) \
{ \
return static_cast<T>(static_cast<std::underlying_type_t<T>>(v) & static_cast<std::underlying_type_t<T>>(v2)); \
} \
constexpr T operator^(const T &v, const T &v2) \
{ \
return static_cast<T>(static_cast<std::underlying_type_t<T>>(v) ^ static_cast<std::underlying_type_t<T>>(v2)); \
} \
template<typename T2 = T, typename = std::enable_if_t<std::is_same_v<T2, T>>> \
constexpr T &operator|=(T &v, const T &v2) \
{ \
v = v | v2; \
return v; \
} \
template<typename T2 = T, typename = std::enable_if_t<std::is_same_v<T2, T>>> \
constexpr T &operator&=(T &v, const T &v2) \
{ \
v = v & v2; \
return v; \
} \
template<typename T2 = T, typename = std::enable_if_t<std::is_same_v<T2, T>>> \
constexpr T &operator^=(T &v, const T &v2) \
{ \
v = v ^ v2; \
return v; \
}
using byte = uint8_t;
// bit simplification
template<size_t n>
using bit_t = std::conditional_t<n >= 32, uint64_t, uint32_t>;
// template is better for this because you can see
// it in the hover-over preview
template<size_t n>
constexpr bit_t<n> bit_v = 1ull << n;
#if defined(KEX_Q2GAME_EXPORTS)
#define Q2GAME_API extern "C" __declspec( dllexport )
#elif defined(KEX_Q2GAME_IMPORTS)
#define Q2GAME_API extern "C" __declspec( dllimport )
#else
#define Q2GAME_API
#endif
// game.h -- game dll information visible to server
// PARIL_NEW_API - value likely not used by any other Q2-esque engine in the wild
constexpr int32_t GAME_API_VERSION = 2022;
constexpr int32_t CGAME_API_VERSION = 2022;
// forward declarations
struct edict_t;
struct gclient_t;
constexpr size_t MAX_STRING_CHARS = 1024; // max length of a string passed to Cmd_TokenizeString
constexpr size_t MAX_STRING_TOKENS = 80; // max tokens resulting from Cmd_TokenizeString
constexpr size_t MAX_TOKEN_CHARS = 512; // max length of an individual token
constexpr size_t MAX_QPATH = 64; // max length of a quake game pathname
constexpr size_t MAX_OSPATH = 128; // max length of a filesystem pathname
//
// per-level limits
//
constexpr size_t MAX_CLIENTS = 256; // absolute limit
constexpr size_t MAX_EDICTS = 8192; // upper limit, due to svc_sound encoding as 15 bits
constexpr size_t MAX_LIGHTSTYLES = 256;
constexpr size_t MAX_MODELS = 8192; // these are sent over the net as shorts
constexpr size_t MAX_SOUNDS = 2048; // so they cannot be blindly increased
constexpr size_t MAX_IMAGES = 512;
constexpr size_t MAX_ITEMS = 256;
constexpr size_t MAX_GENERAL = (MAX_CLIENTS * 2); // general config strings
// [Sam-KEX]
constexpr size_t MAX_SHADOW_LIGHTS = 256;
// game print flags
enum print_type_t
{
PRINT_LOW = 0, // pickup messages
PRINT_MEDIUM = 1, // death messages
PRINT_HIGH = 2, // critical messages
PRINT_CHAT = 3, // chat messages
PRINT_TYPEWRITER = 4, // centerprint but typed out one char at a time
PRINT_CENTER = 5, // centerprint without a separate function (loc variants only)
PRINT_TTS = 6, // PRINT_HIGH but will speak for players with narration on
PRINT_BROADCAST = (1 << 3), // Bitflag, add to message to broadcast print to all clients.
PRINT_NO_NOTIFY = (1 << 4) // Bitflag, don't put on notify
};
MAKE_ENUM_BITFLAGS(print_type_t);
// [Paril-KEX] max number of arguments (not including the base) for
// localization prints
constexpr size_t MAX_LOCALIZATION_ARGS = 8;
// destination class for gi.multicast()
enum multicast_t
{
MULTICAST_ALL,
MULTICAST_PHS,
MULTICAST_PVS
};
/*
==========================================================
CVARS (console variables)
==========================================================
*/
enum cvar_flags_t : uint32_t
{
CVAR_NOFLAGS = 0,
CVAR_ARCHIVE = bit_v<0>, // set to cause it to be saved to config
CVAR_USERINFO = bit_v<1>, // added to userinfo when changed
CVAR_SERVERINFO = bit_v<2>, // added to serverinfo when changed
CVAR_NOSET = bit_v<3>, // don't allow change from console at all,
// but can be set from the command line
CVAR_LATCH = bit_v<4>, // save changes until server restart
CVAR_USER_PROFILE = bit_v<5>, // like CVAR_USERINFO but not sent to server
};
MAKE_ENUM_BITFLAGS(cvar_flags_t);
// nothing outside the Cvar_*() functions should modify these fields!
struct cvar_t
{
char *name;
char *string;
char *latched_string; // for CVAR_LATCH vars
cvar_flags_t flags;
int32_t modified_count; // changed each time the cvar is changed, but never zero
float value;
cvar_t *next;
int32_t integer; // integral value
};
// convenience function to check if the given cvar ptr has been
// modified from its previous modified value, and automatically
// assigns modified to cvar's current value
inline bool Cvar_WasModified(const cvar_t *cvar, int32_t &modified)
{
if (cvar->modified_count != modified)
{
modified = cvar->modified_count;
return true;
}
return false;
}
/*
==============================================================
COLLISION DETECTION
==============================================================
*/
// lower bits are stronger, and will eat weaker brushes completely
enum contents_t : uint32_t
{
CONTENTS_NONE = 0,
CONTENTS_SOLID = bit_v<0>, // an eye is never valid in a solid
CONTENTS_WINDOW = bit_v<1>, // translucent, but not watery
CONTENTS_AUX = bit_v<2>,
CONTENTS_LAVA = bit_v<3>,
CONTENTS_SLIME = bit_v<4>,
CONTENTS_WATER = bit_v<5>,
CONTENTS_MIST = bit_v<6>,
// remaining contents are non-visible, and don't eat brushes
CONTENTS_PROJECTILECLIP = bit_v<14>, // [Paril-KEX] projectiles will collide with this
CONTENTS_AREAPORTAL = bit_v<15>,
CONTENTS_PLAYERCLIP = bit_v<16>,
CONTENTS_MONSTERCLIP = bit_v<17>,
// currents can be added to any other contents, and may be mixed
CONTENTS_CURRENT_0 = bit_v<18>,
CONTENTS_CURRENT_90 = bit_v<19>,
CONTENTS_CURRENT_180 = bit_v<20>,
CONTENTS_CURRENT_270 = bit_v<21>,
CONTENTS_CURRENT_UP = bit_v<22>,
CONTENTS_CURRENT_DOWN = bit_v<23>,
CONTENTS_ORIGIN = bit_v<24>, // removed before bsping an entity
CONTENTS_MONSTER = bit_v<25>, // should never be on a brush, only in game
CONTENTS_DEADMONSTER = bit_v<26>,
CONTENTS_DETAIL = bit_v<27>, // brushes to be added after vis leafs
CONTENTS_TRANSLUCENT = bit_v<28>, // auto set if any surface has trans
CONTENTS_LADDER = bit_v<29>,
CONTENTS_PLAYER = bit_v<30>, // [Paril-KEX] should never be on a brush, only in game; player
CONTENTS_PROJECTILE = bit_v<31> // [Paril-KEX] should never be on a brush, only in game; projectiles.
// used to solve deadmonster collision issues.
};
MAKE_ENUM_BITFLAGS(contents_t);
constexpr contents_t LAST_VISIBLE_CONTENTS = CONTENTS_MIST;
enum surfflags_t : uint32_t
{
SURF_NONE = 0,
SURF_LIGHT = bit_v<0>, // value will hold the light strength
SURF_SLICK = bit_v<1>, // effects game physics
SURF_SKY = bit_v<2>, // don't draw, but add to skybox
SURF_WARP = bit_v<3>, // turbulent water warp
SURF_TRANS33 = bit_v<4>,
SURF_TRANS66 = bit_v<5>,
SURF_FLOWING = bit_v<6>, // scroll towards angle
SURF_NODRAW = bit_v<7>, // don't bother referencing the texture
SURF_ALPHATEST = bit_v<25>, // [Paril-KEX] alpha test using widely supported flag
SURF_N64_UV = bit_v<28>, // [Sam-KEX] Stretches texture UVs
SURF_N64_SCROLL_X = bit_v<29>, // [Sam-KEX] Texture scroll X-axis
SURF_N64_SCROLL_Y = bit_v<30>, // [Sam-KEX] Texture scroll Y-axis
SURF_N64_SCROLL_FLIP = bit_v<31> // [Sam-KEX] Flip direction of texture scroll
};
MAKE_ENUM_BITFLAGS(surfflags_t);
// content masks
constexpr contents_t MASK_ALL = static_cast<contents_t>(-1);
constexpr contents_t MASK_SOLID = (CONTENTS_SOLID | CONTENTS_WINDOW);
constexpr contents_t MASK_PLAYERSOLID = (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_PLAYER);
constexpr contents_t MASK_DEADSOLID = (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW);
constexpr contents_t MASK_MONSTERSOLID = (CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_PLAYER);
constexpr contents_t MASK_WATER = (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME);
constexpr contents_t MASK_OPAQUE = (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA);
constexpr contents_t MASK_SHOT = (CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_WINDOW | CONTENTS_DEADMONSTER);
constexpr contents_t MASK_CURRENT = (CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | CONTENTS_CURRENT_UP | CONTENTS_CURRENT_DOWN);
constexpr contents_t MASK_BLOCK_SIGHT = ( CONTENTS_SOLID | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_MONSTER | CONTENTS_PLAYER );
constexpr contents_t MASK_NAV_SOLID = ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW );
constexpr contents_t MASK_LADDER_NAV_SOLID = ( CONTENTS_SOLID | CONTENTS_WINDOW );
constexpr contents_t MASK_WALK_NAV_SOLID = ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP );
constexpr contents_t MASK_PROJECTILE = MASK_SHOT | CONTENTS_PROJECTILECLIP;
// gi.BoxEdicts() can return a list of either solid or trigger entities
// FIXME: eliminate AREA_ distinction?
enum solidity_area_t
{
AREA_SOLID = 1,
AREA_TRIGGERS = 2
};
// plane_t structure
// !!! if this is changed, it must be changed in asm code too !!!
struct cplane_t
{
gvec3_t normal;
float dist;
byte type; // for fast side tests
byte signbits; // signx + (signy<<1) + (signz<<1)
byte pad[2];
};
// [Paril-KEX]
constexpr size_t MAX_MATERIAL_NAME = 16;
struct csurface_t
{
char name[32];
surfflags_t flags;
int32_t value;
// [Paril-KEX]
uint32_t id; // unique texinfo ID, offset by 1 (0 is 'null')
char material[MAX_MATERIAL_NAME];
};
// a trace is returned when a box is swept through the world
struct trace_t
{
bool allsolid; // if true, plane is not valid
bool startsolid; // if true, the initial point was in a solid area
float fraction; // time completed, 1.0 = didn't hit anything
gvec3_t endpos; // final position
cplane_t plane; // surface normal at impact
csurface_t *surface; // surface hit
contents_t contents; // contents on other side of surface hit
edict_t *ent; // not set by CM_*() functions
// [Paril-KEX] the second-best surface hit from a trace
cplane_t plane2; // second surface normal at impact
csurface_t *surface2; // second surface hit
};
// pmove_state_t is the information necessary for client side movement
// prediction
enum pmtype_t
{
// can accelerate and turn
PM_NORMAL,
PM_GRAPPLE, // [Paril-KEX] pull towards velocity, no gravity
PM_NOCLIP,
PM_SPECTATOR, // [Paril-KEX] clip against walls, but not entities
// no acceleration or turning
PM_DEAD,
PM_GIB, // different bounding box
PM_FREEZE
};
// pmove->pm_flags
enum pmflags_t : uint16_t
{
PMF_NONE = 0,
PMF_DUCKED = bit_v<0>,
PMF_JUMP_HELD = bit_v<1>,
PMF_ON_GROUND = bit_v<2>,
PMF_TIME_WATERJUMP = bit_v<3>, // pm_time is waterjump
PMF_TIME_LAND = bit_v<4>, // pm_time is time before rejump
PMF_TIME_TELEPORT = bit_v<5>, // pm_time is non-moving time
PMF_NO_POSITIONAL_PREDICTION = bit_v<6>, // temporarily disables positional prediction (used for grappling hook)
PMF_ON_LADDER = bit_v<7>, // signal to game that we are on a ladder
PMF_NO_ANGULAR_PREDICTION = bit_v<8>, // temporary disables angular prediction
PMF_IGNORE_PLAYER_COLLISION = bit_v<9>, // don't collide with other players
PMF_TIME_TRICK = bit_v<10>, // pm_time is trick jump time
};
MAKE_ENUM_BITFLAGS(pmflags_t);
// this structure needs to be communicated bit-accurate
// from the server to the client to guarantee that
// prediction stays in sync.
// if any part of the game code modifies this struct, it
// will result in a prediction error of some degree.
struct pmove_state_t
{
pmtype_t pm_type;
vec3_t origin;
vec3_t velocity;
pmflags_t pm_flags; // ducked, jump_held, etc
uint16_t pm_time;
int16_t gravity;
gvec3_t delta_angles; // add to command angles to get view direction
// changed by spawns, rotating objects, and teleporters
int8_t viewheight; // view height, added to origin[2] + viewoffset[2], for crouching
};
//
// button bits
//
enum button_t : uint8_t
{
BUTTON_NONE = 0,
BUTTON_ATTACK = bit_v<0>,
BUTTON_USE = bit_v<1>,
BUTTON_HOLSTER = bit_v<2>, // [Paril-KEX]
BUTTON_JUMP = bit_v<3>,
BUTTON_CROUCH = bit_v<4>,
BUTTON_ANY = bit_v<7> // any key whatsoever
};
MAKE_ENUM_BITFLAGS(button_t);
// usercmd_t is sent to the server each client frame
struct usercmd_t
{
byte msec;
button_t buttons;
gvec3_t angles;
float forwardmove, sidemove;
uint32_t server_frame; // for integrity, etc
};
enum water_level_t : uint8_t
{
WATER_NONE,
WATER_FEET,
WATER_WAIST,
WATER_UNDER
};
// player_state_t->refdef flags
enum refdef_flags_t : uint8_t
{
RDF_NONE = 0,
RDF_UNDERWATER = bit_v<0>, // warp the screen as appropriate
RDF_NOWORLDMODEL = bit_v<1>, // used for player configuration screen
// ROGUE
RDF_IRGOGGLES = bit_v<2>,
RDF_UVGOGGLES = bit_v<3>,
// ROGUE
RDF_NO_WEAPON_LERP = bit_v<4>
};
constexpr size_t MAXTOUCH = 32;
struct touch_list_t
{
uint32_t num = 0;
std::array<trace_t, MAXTOUCH> traces;
};
struct pmove_t
{
// state (in / out)
pmove_state_t s;
// command (in)
usercmd_t cmd;
bool snapinitial; // if s has been changed outside pmove
// results (out)
touch_list_t touch;
gvec3_t viewangles; // clamped
gvec3_t mins, maxs; // bounding box size
edict_t *groundentity;
cplane_t groundplane;
contents_t watertype;
water_level_t waterlevel;
edict_t *player; // opaque handle
// clip against world & entities
trace_t (*trace)(gvec3_cref_t start, gvec3_cptr_t mins, gvec3_cptr_t maxs, gvec3_cref_t end, const edict_t* passent, contents_t contentmask);
// [Paril-KEX] clip against world only
trace_t (*clip)(gvec3_cref_t start, gvec3_cptr_t mins, gvec3_cptr_t maxs, gvec3_cref_t end, contents_t contentmask);
contents_t (*pointcontents)(gvec3_cref_t point);
// [KEX] variables (in)
vec3_t viewoffset; // last viewoffset (for accurate calculation of blending)
// [KEX] results (out)
gvec4_t screen_blend;
refdef_flags_t rdflags; // merged with rdflags from server
bool jump_sound; // play jump sound
float impact_delta; // impact delta, for falling damage
};
// entity_state_t->effects
// Effects are things handled on the client side (lights, particles, frame animations)
// that happen constantly on the given entity.
// An entity that has effects will be sent to the client
// even if it has a zero index model.
enum effects_t : uint64_t
{
EF_NONE = 0, // no effects
EF_ROTATE = bit_v<0>, // rotate (bonus items)
EF_GIB = bit_v<1>, // leave a trail
EF_BOB = bit_v<2>, // bob (bonus items)
EF_BLASTER = bit_v<3>, // redlight + trail
EF_ROCKET = bit_v<4>, // redlight + trail
EF_GRENADE = bit_v<5>,
EF_HYPERBLASTER = bit_v<6>,
EF_BFG = bit_v<7>,
EF_COLOR_SHELL = bit_v<8>,
EF_POWERSCREEN = bit_v<9>,
EF_ANIM01 = bit_v<10>, // automatically cycle between frames 0 and 1 at 2 hz
EF_ANIM23 = bit_v<11>, // automatically cycle between frames 2 and 3 at 2 hz
EF_ANIM_ALL = bit_v<12>, // automatically cycle through all frames at 2hz
EF_ANIM_ALLFAST = bit_v<13>, // automatically cycle through all frames at 10hz
EF_FLIES = bit_v<14>,
EF_QUAD = bit_v<15>,
EF_PENT = bit_v<16>,
EF_TELEPORTER = bit_v<17>, // particle fountain
EF_FLAG1 = bit_v<18>,
EF_FLAG2 = bit_v<19>,
// RAFAEL
EF_IONRIPPER = bit_v<20>,
EF_GREENGIB = bit_v<21>,
EF_BLUEHYPERBLASTER = bit_v<22>,
EF_SPINNINGLIGHTS = bit_v<23>,
EF_PLASMA = bit_v<24>,
EF_TRAP = bit_v<25>,
// ROGUE
EF_TRACKER = bit_v<26>,
EF_DOUBLE = bit_v<27>,
EF_SPHERETRANS = bit_v<28>,
EF_TAGTRAIL = bit_v<29>,
EF_HALF_DAMAGE = bit_v<30>,
EF_TRACKERTRAIL = bit_v<31>,
// ROGUE
EF_DUALFIRE = bit_v<32>, // [KEX] dualfire damage color shell
EF_HOLOGRAM = bit_v<33>, // [Paril-KEX] N64 hologram
EF_FLASHLIGHT = bit_v<34>, // [Paril-KEX] project flashlight, only for players
EF_BARREL_EXPLODING= bit_v<35>,
EF_TELEPORTER2 = bit_v<36>, // [Paril-KEX] n64 teleporter
EF_GRENADE_LIGHT = bit_v<37>
};
MAKE_ENUM_BITFLAGS(effects_t);
constexpr effects_t EF_FIREBALL = EF_ROCKET | EF_GIB;
// entity_state_t->renderfx flags
enum renderfx_t : uint32_t
{
RF_NONE = 0,
RF_MINLIGHT = bit_v<0>, // always have some light (viewmodel)
RF_VIEWERMODEL = bit_v<1>, // don't draw through eyes, only mirrors
RF_WEAPONMODEL = bit_v<2>, // only draw through eyes
RF_FULLBRIGHT = bit_v<3>, // always draw full intensity
RF_DEPTHHACK = bit_v<4>, // for view weapon Z crunching
RF_TRANSLUCENT = bit_v<5>,
RF_NO_ORIGIN_LERP = bit_v<6>, // no interpolation for origins
RF_BEAM = bit_v<7>,
RF_CUSTOMSKIN = bit_v<8>, // [Paril-KEX] implemented; set skinnum (or frame for RF_FLARE) to specify
// an image in CS_IMAGES to use as skin.
RF_GLOW = bit_v<9>, // pulse lighting for bonus items
RF_SHELL_RED = bit_v<10>,
RF_SHELL_GREEN = bit_v<11>,
RF_SHELL_BLUE = bit_v<12>,
RF_NOSHADOW = bit_v<13>,
RF_CASTSHADOW = bit_v<14>, // [Sam-KEX]
// ROGUE
RF_IR_VISIBLE = bit_v<15>,
RF_SHELL_DOUBLE = bit_v<16>,
RF_SHELL_HALF_DAM = bit_v<17>,
RF_USE_DISGUISE = bit_v<18>,
// ROGUE
RF_SHELL_LITE_GREEN = bit_v<19>,
RF_CUSTOM_LIGHT = bit_v<20>, // [Paril-KEX] custom point dlight that is designed to strobe/be turned off; s.frame is radius, s.skinnum is color
RF_FLARE = bit_v<21>, // [Sam-KEX]
RF_OLD_FRAME_LERP = bit_v<22>, // [Paril-KEX] force model to lerp from oldframe in entity state; otherwise it uses last frame client received
RF_DOT_SHADOW = bit_v<23>, // [Paril-KEX] draw blobby shadow
RF_LOW_PRIORITY = bit_v<24>, // [Paril-KEX] low priority object; if we can't be added to the scene, don't bother replacing entities,
// and we can be replaced if anything non-low-priority needs room
RF_NO_LOD = bit_v<25>, // [Paril-KEX] never LOD
RF_NO_STEREO = RF_WEAPONMODEL, // [Paril-KEX] this is a bit dumb, but, for looping noises if this is set there's no stereo
RF_STAIR_STEP = bit_v<26>, // [Paril-KEX] re-tuned, now used to handle stair steps for monsters
RF_FLARE_LOCK_ANGLE = RF_MINLIGHT
};
MAKE_ENUM_BITFLAGS(renderfx_t);
constexpr renderfx_t RF_BEAM_LIGHTNING = RF_BEAM | RF_GLOW; // [Paril-KEX] make a lightning bolt instead of a laser
MAKE_ENUM_BITFLAGS(refdef_flags_t);
//
// muzzle flashes / player effects
//
enum player_muzzle_t : uint8_t
{
MZ_BLASTER = 0,
MZ_MACHINEGUN = 1,
MZ_SHOTGUN = 2,
MZ_CHAINGUN1 = 3,
MZ_CHAINGUN2 = 4,
MZ_CHAINGUN3 = 5,
MZ_RAILGUN = 6,
MZ_ROCKET = 7,
MZ_GRENADE = 8,
MZ_LOGIN = 9,
MZ_LOGOUT = 10,
MZ_RESPAWN = 11,
MZ_BFG = 12,
MZ_SSHOTGUN = 13,
MZ_HYPERBLASTER = 14,
MZ_ITEMRESPAWN = 15,
// RAFAEL
MZ_IONRIPPER = 16,
MZ_BLUEHYPERBLASTER = 17,
MZ_PHALANX = 18,
MZ_BFG2 = 19,
MZ_PHALANX2 = 20,
// ROGUE
MZ_ETF_RIFLE = 30,
MZ_PROX = 31, // [Paril-KEX]
MZ_ETF_RIFLE_2 = 32, // [Paril-KEX] unused, so using it for the other barrel
MZ_HEATBEAM = 33,
MZ_BLASTER2 = 34,
MZ_TRACKER = 35,
MZ_NUKE1 = 36,
MZ_NUKE2 = 37,
MZ_NUKE4 = 38,
MZ_NUKE8 = 39,
// ROGUE
MZ_SILENCED = bit_v<7>, // bit flag ORed with one of the above numbers
MZ_NONE = 0 // "no" bitflags
};
MAKE_ENUM_BITFLAGS(player_muzzle_t);
//
// monster muzzle flashes
// NOTE: this needs to match the m_flash table!
//
enum monster_muzzleflash_id_t : uint16_t
{
MZ2_UNUSED_0,
MZ2_TANK_BLASTER_1,
MZ2_TANK_BLASTER_2,
MZ2_TANK_BLASTER_3,
MZ2_TANK_MACHINEGUN_1,
MZ2_TANK_MACHINEGUN_2,
MZ2_TANK_MACHINEGUN_3,
MZ2_TANK_MACHINEGUN_4,
MZ2_TANK_MACHINEGUN_5,
MZ2_TANK_MACHINEGUN_6,
MZ2_TANK_MACHINEGUN_7,
MZ2_TANK_MACHINEGUN_8,
MZ2_TANK_MACHINEGUN_9,
MZ2_TANK_MACHINEGUN_10,
MZ2_TANK_MACHINEGUN_11,
MZ2_TANK_MACHINEGUN_12,
MZ2_TANK_MACHINEGUN_13,
MZ2_TANK_MACHINEGUN_14,
MZ2_TANK_MACHINEGUN_15,
MZ2_TANK_MACHINEGUN_16,
MZ2_TANK_MACHINEGUN_17,
MZ2_TANK_MACHINEGUN_18,
MZ2_TANK_MACHINEGUN_19,
MZ2_TANK_ROCKET_1,
MZ2_TANK_ROCKET_2,
MZ2_TANK_ROCKET_3,
MZ2_INFANTRY_MACHINEGUN_1,
MZ2_INFANTRY_MACHINEGUN_2,
MZ2_INFANTRY_MACHINEGUN_3,
MZ2_INFANTRY_MACHINEGUN_4,
MZ2_INFANTRY_MACHINEGUN_5,
MZ2_INFANTRY_MACHINEGUN_6,
MZ2_INFANTRY_MACHINEGUN_7,
MZ2_INFANTRY_MACHINEGUN_8,
MZ2_INFANTRY_MACHINEGUN_9,
MZ2_INFANTRY_MACHINEGUN_10,
MZ2_INFANTRY_MACHINEGUN_11,
MZ2_INFANTRY_MACHINEGUN_12,
MZ2_INFANTRY_MACHINEGUN_13,
MZ2_SOLDIER_BLASTER_1,
MZ2_SOLDIER_BLASTER_2,
MZ2_SOLDIER_SHOTGUN_1,
MZ2_SOLDIER_SHOTGUN_2,
MZ2_SOLDIER_MACHINEGUN_1,
MZ2_SOLDIER_MACHINEGUN_2,
MZ2_GUNNER_MACHINEGUN_1,
MZ2_GUNNER_MACHINEGUN_2,
MZ2_GUNNER_MACHINEGUN_3,
MZ2_GUNNER_MACHINEGUN_4,
MZ2_GUNNER_MACHINEGUN_5,
MZ2_GUNNER_MACHINEGUN_6,
MZ2_GUNNER_MACHINEGUN_7,
MZ2_GUNNER_MACHINEGUN_8,
MZ2_GUNNER_GRENADE_1,
MZ2_GUNNER_GRENADE_2,
MZ2_GUNNER_GRENADE_3,
MZ2_GUNNER_GRENADE_4,
MZ2_CHICK_ROCKET_1,
MZ2_FLYER_BLASTER_1,
MZ2_FLYER_BLASTER_2,
MZ2_MEDIC_BLASTER_1,
MZ2_GLADIATOR_RAILGUN_1,
MZ2_HOVER_BLASTER_1,
MZ2_ACTOR_MACHINEGUN_1,
MZ2_SUPERTANK_MACHINEGUN_1,
MZ2_SUPERTANK_MACHINEGUN_2,
MZ2_SUPERTANK_MACHINEGUN_3,
MZ2_SUPERTANK_MACHINEGUN_4,
MZ2_SUPERTANK_MACHINEGUN_5,
MZ2_SUPERTANK_MACHINEGUN_6,
MZ2_SUPERTANK_ROCKET_1,
MZ2_SUPERTANK_ROCKET_2,
MZ2_SUPERTANK_ROCKET_3,
MZ2_BOSS2_MACHINEGUN_L1,
MZ2_BOSS2_MACHINEGUN_L2,
MZ2_BOSS2_MACHINEGUN_L3,
MZ2_BOSS2_MACHINEGUN_L4,
MZ2_BOSS2_MACHINEGUN_L5,
MZ2_BOSS2_ROCKET_1,
MZ2_BOSS2_ROCKET_2,
MZ2_BOSS2_ROCKET_3,
MZ2_BOSS2_ROCKET_4,
MZ2_FLOAT_BLASTER_1,
MZ2_SOLDIER_BLASTER_3,
MZ2_SOLDIER_SHOTGUN_3,
MZ2_SOLDIER_MACHINEGUN_3,
MZ2_SOLDIER_BLASTER_4,
MZ2_SOLDIER_SHOTGUN_4,
MZ2_SOLDIER_MACHINEGUN_4,
MZ2_SOLDIER_BLASTER_5,
MZ2_SOLDIER_SHOTGUN_5,
MZ2_SOLDIER_MACHINEGUN_5,
MZ2_SOLDIER_BLASTER_6,
MZ2_SOLDIER_SHOTGUN_6,
MZ2_SOLDIER_MACHINEGUN_6,
MZ2_SOLDIER_BLASTER_7,
MZ2_SOLDIER_SHOTGUN_7,
MZ2_SOLDIER_MACHINEGUN_7,
MZ2_SOLDIER_BLASTER_8,
MZ2_SOLDIER_SHOTGUN_8,
MZ2_SOLDIER_MACHINEGUN_8,
// --- Xian shit below ---
MZ2_MAKRON_BFG,
MZ2_MAKRON_BLASTER_1,
MZ2_MAKRON_BLASTER_2,
MZ2_MAKRON_BLASTER_3,
MZ2_MAKRON_BLASTER_4,
MZ2_MAKRON_BLASTER_5,
MZ2_MAKRON_BLASTER_6,
MZ2_MAKRON_BLASTER_7,
MZ2_MAKRON_BLASTER_8,
MZ2_MAKRON_BLASTER_9,
MZ2_MAKRON_BLASTER_10,
MZ2_MAKRON_BLASTER_11,
MZ2_MAKRON_BLASTER_12,
MZ2_MAKRON_BLASTER_13,
MZ2_MAKRON_BLASTER_14,
MZ2_MAKRON_BLASTER_15,
MZ2_MAKRON_BLASTER_16,
MZ2_MAKRON_BLASTER_17,
MZ2_MAKRON_RAILGUN_1,
MZ2_JORG_MACHINEGUN_L1,
MZ2_JORG_MACHINEGUN_L2,
MZ2_JORG_MACHINEGUN_L3,
MZ2_JORG_MACHINEGUN_L4,
MZ2_JORG_MACHINEGUN_L5,
MZ2_JORG_MACHINEGUN_L6,
MZ2_JORG_MACHINEGUN_R1,
MZ2_JORG_MACHINEGUN_R2,
MZ2_JORG_MACHINEGUN_R3,
MZ2_JORG_MACHINEGUN_R4,
MZ2_JORG_MACHINEGUN_R5,
MZ2_JORG_MACHINEGUN_R6,
MZ2_JORG_BFG_1,
MZ2_BOSS2_MACHINEGUN_R1,
MZ2_BOSS2_MACHINEGUN_R2,
MZ2_BOSS2_MACHINEGUN_R3,
MZ2_BOSS2_MACHINEGUN_R4,
MZ2_BOSS2_MACHINEGUN_R5,
// ROGUE
MZ2_CARRIER_MACHINEGUN_L1,
MZ2_CARRIER_MACHINEGUN_R1,
MZ2_CARRIER_GRENADE,
MZ2_TURRET_MACHINEGUN,
MZ2_TURRET_ROCKET,
MZ2_TURRET_BLASTER,
MZ2_STALKER_BLASTER,
MZ2_DAEDALUS_BLASTER,
MZ2_MEDIC_BLASTER_2,
MZ2_CARRIER_RAILGUN,
MZ2_WIDOW_DISRUPTOR,
MZ2_WIDOW_BLASTER,
MZ2_WIDOW_RAIL,
MZ2_WIDOW_PLASMABEAM, // PMM - not used
MZ2_CARRIER_MACHINEGUN_L2,
MZ2_CARRIER_MACHINEGUN_R2,
MZ2_WIDOW_RAIL_LEFT,
MZ2_WIDOW_RAIL_RIGHT,
MZ2_WIDOW_BLASTER_SWEEP1,
MZ2_WIDOW_BLASTER_SWEEP2,
MZ2_WIDOW_BLASTER_SWEEP3,
MZ2_WIDOW_BLASTER_SWEEP4,
MZ2_WIDOW_BLASTER_SWEEP5,
MZ2_WIDOW_BLASTER_SWEEP6,
MZ2_WIDOW_BLASTER_SWEEP7,
MZ2_WIDOW_BLASTER_SWEEP8,
MZ2_WIDOW_BLASTER_SWEEP9,
MZ2_WIDOW_BLASTER_100,
MZ2_WIDOW_BLASTER_90,
MZ2_WIDOW_BLASTER_80,
MZ2_WIDOW_BLASTER_70,
MZ2_WIDOW_BLASTER_60,
MZ2_WIDOW_BLASTER_50,
MZ2_WIDOW_BLASTER_40,
MZ2_WIDOW_BLASTER_30,
MZ2_WIDOW_BLASTER_20,
MZ2_WIDOW_BLASTER_10,
MZ2_WIDOW_BLASTER_0,
MZ2_WIDOW_BLASTER_10L,
MZ2_WIDOW_BLASTER_20L,
MZ2_WIDOW_BLASTER_30L,
MZ2_WIDOW_BLASTER_40L,
MZ2_WIDOW_BLASTER_50L,
MZ2_WIDOW_BLASTER_60L,
MZ2_WIDOW_BLASTER_70L,
MZ2_WIDOW_RUN_1,
MZ2_WIDOW_RUN_2,
MZ2_WIDOW_RUN_3,
MZ2_WIDOW_RUN_4,
MZ2_WIDOW_RUN_5,
MZ2_WIDOW_RUN_6,
MZ2_WIDOW_RUN_7,
MZ2_WIDOW_RUN_8,
MZ2_CARRIER_ROCKET_1,
MZ2_CARRIER_ROCKET_2,
MZ2_CARRIER_ROCKET_3,
MZ2_CARRIER_ROCKET_4,
MZ2_WIDOW2_BEAMER_1,
MZ2_WIDOW2_BEAMER_2,
MZ2_WIDOW2_BEAMER_3,
MZ2_WIDOW2_BEAMER_4,
MZ2_WIDOW2_BEAMER_5,
MZ2_WIDOW2_BEAM_SWEEP_1,
MZ2_WIDOW2_BEAM_SWEEP_2,
MZ2_WIDOW2_BEAM_SWEEP_3,
MZ2_WIDOW2_BEAM_SWEEP_4,
MZ2_WIDOW2_BEAM_SWEEP_5,
MZ2_WIDOW2_BEAM_SWEEP_6,
MZ2_WIDOW2_BEAM_SWEEP_7,
MZ2_WIDOW2_BEAM_SWEEP_8,
MZ2_WIDOW2_BEAM_SWEEP_9,
MZ2_WIDOW2_BEAM_SWEEP_10,
MZ2_WIDOW2_BEAM_SWEEP_11,
// ROGUE
// [Paril-KEX]
MZ2_SOLDIER_RIPPER_1,
MZ2_SOLDIER_RIPPER_2,
MZ2_SOLDIER_RIPPER_3,
MZ2_SOLDIER_RIPPER_4,
MZ2_SOLDIER_RIPPER_5,
MZ2_SOLDIER_RIPPER_6,
MZ2_SOLDIER_RIPPER_7,
MZ2_SOLDIER_RIPPER_8,
MZ2_SOLDIER_HYPERGUN_1,
MZ2_SOLDIER_HYPERGUN_2,
MZ2_SOLDIER_HYPERGUN_3,
MZ2_SOLDIER_HYPERGUN_4,
MZ2_SOLDIER_HYPERGUN_5,
MZ2_SOLDIER_HYPERGUN_6,
MZ2_SOLDIER_HYPERGUN_7,
MZ2_SOLDIER_HYPERGUN_8,
MZ2_GUARDIAN_BLASTER,
MZ2_ARACHNID_RAIL1,
MZ2_ARACHNID_RAIL2,
MZ2_ARACHNID_RAIL_UP1,
MZ2_ARACHNID_RAIL_UP2,
MZ2_INFANTRY_MACHINEGUN_14, // run-attack
MZ2_INFANTRY_MACHINEGUN_15, // run-attack
MZ2_INFANTRY_MACHINEGUN_16, // run-attack
MZ2_INFANTRY_MACHINEGUN_17, // run-attack
MZ2_INFANTRY_MACHINEGUN_18, // run-attack
MZ2_INFANTRY_MACHINEGUN_19, // run-attack
MZ2_INFANTRY_MACHINEGUN_20, // run-attack
MZ2_INFANTRY_MACHINEGUN_21, // run-attack
MZ2_GUNCMDR_CHAINGUN_1, // straight
MZ2_GUNCMDR_CHAINGUN_2, // dodging
MZ2_GUNCMDR_GRENADE_MORTAR_1,
MZ2_GUNCMDR_GRENADE_MORTAR_2,
MZ2_GUNCMDR_GRENADE_MORTAR_3,
MZ2_GUNCMDR_GRENADE_FRONT_1,
MZ2_GUNCMDR_GRENADE_FRONT_2,
MZ2_GUNCMDR_GRENADE_FRONT_3,
MZ2_GUNCMDR_GRENADE_CROUCH_1,
MZ2_GUNCMDR_GRENADE_CROUCH_2,
MZ2_GUNCMDR_GRENADE_CROUCH_3,
// prone
MZ2_SOLDIER_BLASTER_9,
MZ2_SOLDIER_SHOTGUN_9,
MZ2_SOLDIER_MACHINEGUN_9,
MZ2_SOLDIER_RIPPER_9,
MZ2_SOLDIER_HYPERGUN_9,
// alternate frontwards grenades
MZ2_GUNNER_GRENADE2_1,
MZ2_GUNNER_GRENADE2_2,
MZ2_GUNNER_GRENADE2_3,
MZ2_GUNNER_GRENADE2_4,
MZ2_INFANTRY_MACHINEGUN_22,
// supertonk
MZ2_SUPERTANK_GRENADE_1,
MZ2_SUPERTANK_GRENADE_2,
// hover & daedalus other side
MZ2_HOVER_BLASTER_2,
MZ2_DAEDALUS_BLASTER_2,
// medic (commander) sweeps
MZ2_MEDIC_HYPERBLASTER1_1,
MZ2_MEDIC_HYPERBLASTER1_2,
MZ2_MEDIC_HYPERBLASTER1_3,
MZ2_MEDIC_HYPERBLASTER1_4,
MZ2_MEDIC_HYPERBLASTER1_5,
MZ2_MEDIC_HYPERBLASTER1_6,
MZ2_MEDIC_HYPERBLASTER1_7,
MZ2_MEDIC_HYPERBLASTER1_8,
MZ2_MEDIC_HYPERBLASTER1_9,
MZ2_MEDIC_HYPERBLASTER1_10,
MZ2_MEDIC_HYPERBLASTER1_11,
MZ2_MEDIC_HYPERBLASTER1_12,
MZ2_MEDIC_HYPERBLASTER2_1,
MZ2_MEDIC_HYPERBLASTER2_2,
MZ2_MEDIC_HYPERBLASTER2_3,
MZ2_MEDIC_HYPERBLASTER2_4,
MZ2_MEDIC_HYPERBLASTER2_5,
MZ2_MEDIC_HYPERBLASTER2_6,
MZ2_MEDIC_HYPERBLASTER2_7,
MZ2_MEDIC_HYPERBLASTER2_8,
MZ2_MEDIC_HYPERBLASTER2_9,
MZ2_MEDIC_HYPERBLASTER2_10,
MZ2_MEDIC_HYPERBLASTER2_11,
MZ2_MEDIC_HYPERBLASTER2_12,
// only used for compile time checks
MZ2_LAST
};
// temp entity events
//
// Temp entity events are for things that happen
// at a location seperate from any existing entity.
// Temporary entity messages are explicitly constructed
// and broadcast.
enum temp_event_t : uint8_t
{
TE_GUNSHOT,
TE_BLOOD,
TE_BLASTER,
TE_RAILTRAIL,
TE_SHOTGUN,
TE_EXPLOSION1,
TE_EXPLOSION2,
TE_ROCKET_EXPLOSION,
TE_GRENADE_EXPLOSION,
TE_SPARKS,
TE_SPLASH,
TE_BUBBLETRAIL,
TE_SCREEN_SPARKS,
TE_SHIELD_SPARKS,
TE_BULLET_SPARKS,
TE_LASER_SPARKS,
TE_PARASITE_ATTACK,
TE_ROCKET_EXPLOSION_WATER,
TE_GRENADE_EXPLOSION_WATER,
TE_MEDIC_CABLE_ATTACK,
TE_BFG_EXPLOSION,
TE_BFG_BIGEXPLOSION,
TE_BOSSTPORT, // used as '22' in a map, so DON'T RENUMBER!!!
TE_BFG_LASER,
TE_GRAPPLE_CABLE,
TE_WELDING_SPARKS,
TE_GREENBLOOD,
TE_BLUEHYPERBLASTER_DUMMY, // [Paril-KEX] leaving for compatibility, do not use; use TE_BLUEHYPERBLASTER
TE_PLASMA_EXPLOSION,
TE_TUNNEL_SPARKS,
// ROGUE
TE_BLASTER2,
TE_RAILTRAIL2,
TE_FLAME,
TE_LIGHTNING,
TE_DEBUGTRAIL,
TE_PLAIN_EXPLOSION,
TE_FLASHLIGHT,
TE_FORCEWALL,
TE_HEATBEAM,
TE_MONSTER_HEATBEAM,
TE_STEAM,
TE_BUBBLETRAIL2,
TE_MOREBLOOD,
TE_HEATBEAM_SPARKS,
TE_HEATBEAM_STEAM,
TE_CHAINFIST_SMOKE,
TE_ELECTRIC_SPARKS,
TE_TRACKER_EXPLOSION,
TE_TELEPORT_EFFECT,
TE_DBALL_GOAL,
TE_WIDOWBEAMOUT,
TE_NUKEBLAST,
TE_WIDOWSPLASH,
TE_EXPLOSION1_BIG,
TE_EXPLOSION1_NP,
TE_FLECHETTE,
// ROGUE
// [Paril-KEX]
TE_BLUEHYPERBLASTER,
TE_BFG_ZAP,
TE_BERSERK_SLAM,
TE_GRAPPLE_CABLE_2,
TE_POWER_SPLASH,
TE_LIGHTNING_BEAM,
TE_EXPLOSION1_NL,
TE_EXPLOSION2_NL,
};
enum splash_color_t : uint8_t
{
SPLASH_UNKNOWN = 0,
SPLASH_SPARKS = 1,
SPLASH_BLUE_WATER = 2,
SPLASH_BROWN_WATER = 3,
SPLASH_SLIME = 4,
SPLASH_LAVA = 5,
SPLASH_BLOOD = 6,
// [Paril-KEX] N64 electric sparks that go zap
SPLASH_ELECTRIC = 7
};
// sound channels
// channel 0 never willingly overrides
// other channels (1-7) always override a playing sound on that channel
enum soundchan_t : uint8_t
{
CHAN_AUTO = 0,
CHAN_WEAPON = 1,
CHAN_VOICE = 2,
CHAN_ITEM = 3,
CHAN_BODY = 4,
CHAN_AUX = 5,
CHAN_FOOTSTEP = 6,
CHAN_AUX3 = 7,
// modifier flags
CHAN_NO_PHS_ADD = bit_v<3>, // send to all clients, not just ones in PHS (ATTN 0 will also do this)
CHAN_RELIABLE = bit_v<4>, // send by reliable message, not datagram
CHAN_FORCE_POS = bit_v<5>, // always use position sent in packet
};
MAKE_ENUM_BITFLAGS(soundchan_t);
// sound attenuation values
constexpr float ATTN_LOOP_NONE = -1; // full volume the entire level, for loop only
constexpr float ATTN_NONE = 0; // full volume the entire level, for sounds only
constexpr float ATTN_NORM = 1;
constexpr float ATTN_IDLE = 2;
constexpr float ATTN_STATIC = 3; // diminish very rapidly with distance
// total stat count
constexpr size_t MAX_STATS = 64;
/*
ROGUE - VERSIONS
1234 08/13/1998 Activision
1235 08/14/1998 Id Software
1236 08/15/1998 Steve Tietze
1237 08/15/1998 Phil Dobranski
1238 08/15/1998 John Sheley
1239 08/17/1998 Barrett Alexander
1230 08/17/1998 Brandon Fish
1245 08/17/1998 Don MacAskill
1246 08/17/1998 David "Zoid" Kirsch
1247 08/17/1998 Manu Smith
1248 08/17/1998 Geoff Scully
1249 08/17/1998 Andy Van Fossen
1240 08/20/1998 Activision Build 2
1256 08/20/1998 Ranger Clan
1257 08/20/1998 Ensemble Studios
1258 08/21/1998 Robert Duffy
1259 08/21/1998 Stephen Seachord
1250 08/21/1998 Stephen Heaslip
1267 08/21/1998 Samir Sandesara
1268 08/21/1998 Oliver Wyman
1269 08/21/1998 Steven Marchegiano
1260 08/21/1998 Build #2 for Nihilistic
1278 08/21/1998 Build #2 for Ensemble
1279 08/26/1998 Build for Ron Solo - DEFUNCT
1270 08/26/1998 Build #3 for Activision
1289 08/26/1998 Build for Don MacAskill
1280 08/26/1998 Build for Robert Duffy
1290 08/26/1998 Build #2 for Rangers
1345 08/28/1998 Build #4 for Activision
2345 08/26/1998 Build for Zoid
9999 08/20/1998 Internal Use
*/
// ROGUE
/*
==========================================================
ELEMENTS COMMUNICATED ACROSS THE NET
==========================================================
*/
//=============================================
// INFO STRINGS
//
// NB: the Q2 protocol does not dictate the type
// of strings being used, so it's kind of a crapshoot.
// Kex's protocol assumes info strings are always UTF8.
//=============================================
//
// key / value info strings
//
constexpr size_t MAX_INFO_KEY = 64;
constexpr size_t MAX_INFO_VALUE = 256;
constexpr size_t MAX_INFO_STRING = 2048;
// CONFIG STRINGS
// bound by number of things we can fit in two stats
constexpr size_t MAX_WHEEL_ITEMS = 32;
// CS_WHEEL_xxx are special configstrings that
// map individual weapon and ammo ids to each other, separated by a pipe |
// the format for CS_WHEEL_WEAPONS is:
// <CS_ITEMS INDEX>|<CS_IMAGES INDEX>|<CS_WHEEL_AMMO INDEX>|<min ammo>|<on powerup wheel>|<sort id>|<warn quantity>|<droppable>
// if the weapon does not take ammo, the index will be -1
// the format for CS_WHEEL_AMMO is:
// <CS_ITEMS INDEX>|<CS_IMAGES INDEX>
// the indices here are not related to the IT_ or AMMO_
// indices, and are just as they appear in the configstrings.
// the format for CS_WHEEL_POWERUP is:
// <CS_ITEMS INDEX>|<CS_IMAGES INDEX>|<USE ON/OFF INSTEAD OF COUNT>|<SORT_ID>|<DROPPABLE>|<AMMO, IF APPLICABLE>
enum game_style_t : uint8_t
{
GAME_STYLE_PVE,
GAME_STYLE_FFA,
GAME_STYLE_TDM
};
//
// config strings are a general means of communication from
// the server to all connected clients.
// Each config string can be at most CS_MAX_STRING_LENGTH characters.
//
enum
{
CS_NAME,
CS_CDTRACK,
CS_SKY,
CS_SKYAXIS, // %f %f %f format
CS_SKYROTATE,
CS_STATUSBAR, // display program string
CS_AIRACCEL = 59, // air acceleration control
CS_MAXCLIENTS,
CS_MAPCHECKSUM, // for catching cheater maps
CS_MODELS,
CS_SOUNDS = (CS_MODELS + MAX_MODELS),
CS_IMAGES = (CS_SOUNDS + MAX_SOUNDS),
CS_LIGHTS = (CS_IMAGES + MAX_IMAGES),
CS_SHADOWLIGHTS = (CS_LIGHTS + MAX_LIGHTSTYLES), // [Sam-KEX]
CS_ITEMS = (CS_SHADOWLIGHTS + MAX_SHADOW_LIGHTS),
CS_PLAYERSKINS = (CS_ITEMS + MAX_ITEMS),
CS_GENERAL = (CS_PLAYERSKINS + MAX_CLIENTS),
CS_WHEEL_WEAPONS = (CS_GENERAL + MAX_GENERAL), // [Paril-KEX] see MAX_WHEEL_ITEMS
CS_WHEEL_AMMO = (CS_WHEEL_WEAPONS + MAX_WHEEL_ITEMS), // [Paril-KEX] see MAX_WHEEL_ITEMS
CS_WHEEL_POWERUPS = (CS_WHEEL_AMMO + MAX_WHEEL_ITEMS), // [Paril-KEX] see MAX_WHEEL_ITEMS
CS_CD_LOOP_COUNT = (CS_WHEEL_POWERUPS + MAX_WHEEL_ITEMS), // [Paril-KEX] override default loop count
CS_GAME_STYLE, // [Paril-KEX] see game_style_t
MAX_CONFIGSTRINGS
};
static_assert(MAX_CONFIGSTRINGS <= 0x7FFF, "configstrings too big");
// [Sam-KEX] New define for max config string length
constexpr size_t CS_MAX_STRING_LENGTH = 96;
constexpr size_t CS_MAX_STRING_LENGTH_OLD = 64;
// certain configstrings are allowed to be larger
// than CS_MAX_STRING_LENGTH; this gets the absolute size
// for the given configstring at the specified id
// since vanilla didn't do a very good job of size checking
constexpr size_t CS_SIZE(int32_t in)
{
if (in >= CS_STATUSBAR && in < CS_AIRACCEL)
return CS_MAX_STRING_LENGTH * (CS_AIRACCEL - in);
else if (in >= CS_GENERAL && in < CS_WHEEL_WEAPONS)
return CS_MAX_STRING_LENGTH * (MAX_CONFIGSTRINGS - in);
return CS_MAX_STRING_LENGTH;
}
constexpr size_t MAX_MODELS_OLD = 256, MAX_SOUNDS_OLD = 256, MAX_IMAGES_OLD = 256;
enum
{
CS_NAME_OLD,
CS_CDTRACK_OLD,
CS_SKY_OLD,
CS_SKYAXIS_OLD, // %f %f %f format
CS_SKYROTATE_OLD,
CS_STATUSBAR_OLD, // display program string
CS_AIRACCEL_OLD = 29, // air acceleration control
CS_MAXCLIENTS_OLD,
CS_MAPCHECKSUM_OLD, // for catching cheater maps
CS_MODELS_OLD,
CS_SOUNDS_OLD = (CS_MODELS_OLD + MAX_MODELS_OLD),
CS_IMAGES_OLD = (CS_SOUNDS_OLD + MAX_SOUNDS_OLD),
CS_LIGHTS_OLD = (CS_IMAGES_OLD + MAX_IMAGES_OLD),
CS_ITEMS_OLD = (CS_LIGHTS_OLD + MAX_LIGHTSTYLES),
CS_PLAYERSKINS_OLD = (CS_ITEMS_OLD + MAX_ITEMS),
CS_GENERAL_OLD = (CS_PLAYERSKINS_OLD + MAX_CLIENTS),
MAX_CONFIGSTRINGS_OLD = (CS_GENERAL_OLD + MAX_GENERAL)
};
// remaps old configstring IDs to new ones
// for old DLL & demo support
struct configstring_remap_t
{
// start position in the configstring list
// to write into
size_t start;
// max length to write into; [start+length-1] should always
// be set to '\0'
size_t length;
};
constexpr configstring_remap_t CS_REMAP(int32_t id)
{
// direct mapping
if (id < CS_STATUSBAR_OLD)
return { id * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
// statusbar needs a bit of special handling, since we have a different
// max configstring length and these are just segments of a longer string
else if (id < CS_AIRACCEL_OLD)
return { (CS_STATUSBAR * CS_MAX_STRING_LENGTH) + ((id - CS_STATUSBAR_OLD) * CS_MAX_STRING_LENGTH_OLD), (CS_AIRACCEL - CS_STATUSBAR) * CS_MAX_STRING_LENGTH };
// offset
else if (id < CS_MODELS_OLD)
return { (id + (CS_AIRACCEL - CS_AIRACCEL_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_SOUNDS_OLD)
return { (id + (CS_MODELS - CS_MODELS_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_IMAGES_OLD)
return { (id + (CS_SOUNDS - CS_SOUNDS_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_LIGHTS_OLD)
return { (id + (CS_IMAGES - CS_IMAGES_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_ITEMS_OLD)
return { (id + (CS_LIGHTS - CS_LIGHTS_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_PLAYERSKINS_OLD)
return { (id + (CS_ITEMS - CS_ITEMS_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
else if (id < CS_GENERAL_OLD)
return { (id + (CS_PLAYERSKINS - CS_PLAYERSKINS_OLD)) * CS_MAX_STRING_LENGTH, CS_MAX_STRING_LENGTH };
// general also needs some special handling because it's both
// offset *and* allowed to overflow
return { (id + (CS_GENERAL - CS_GENERAL_OLD)) * CS_MAX_STRING_LENGTH_OLD, (MAX_CONFIGSTRINGS - CS_GENERAL) * CS_MAX_STRING_LENGTH };
}
static_assert(CS_REMAP(CS_MODELS_OLD).start == (CS_MODELS * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_SOUNDS_OLD).start == (CS_SOUNDS * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_IMAGES_OLD).start == (CS_IMAGES * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_LIGHTS_OLD).start == (CS_LIGHTS * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_PLAYERSKINS_OLD).start == (CS_PLAYERSKINS * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_ITEMS_OLD).start == (CS_ITEMS * 96), "check CS_REMAP");
static_assert(CS_REMAP(CS_GENERAL_OLD).start == (CS_GENERAL * 64), "check CS_REMAP");
static_assert(CS_REMAP(CS_AIRACCEL_OLD).start == (CS_AIRACCEL * 96), "check CS_REMAP");
//==============================================
// entity_state_t->event values
// ertity events are for effects that take place reletive
// to an existing entities origin. Very network efficient.
// All muzzle flashes really should be converted to events...
enum entity_event_t : uint8_t
{
EV_NONE,
EV_ITEM_RESPAWN,
EV_FOOTSTEP,
EV_FALLSHORT,
EV_FALL,
EV_FALLFAR,
EV_PLAYER_TELEPORT,
EV_OTHER_TELEPORT,
// [Paril-KEX]
EV_OTHER_FOOTSTEP,
EV_LADDER_STEP,
};
// [Paril-KEX] player s.skinnum's encode additional data
union player_skinnum_t
{
int32_t skinnum;
struct {
uint8_t client_num; // client index
uint8_t vwep_index; // vwep index
int8_t viewheight; // viewheight
uint8_t team_index : 4; // team #; note that teams are 1-indexed here, with 0 meaning no team
// (spectators in CTF would be 0, for instance)
uint8_t poi_icon : 4; // poi icon; 0 default friendly, 1 dead, others unused
};
};
// entity_state_t is the information conveyed from the server
// in an update message about entities that the client will
// need to render in some way
struct entity_state_t
{
uint32_t number; // edict index
gvec3_t origin;
gvec3_t angles;
gvec3_t old_origin; // for lerping
int32_t modelindex;
int32_t modelindex2, modelindex3, modelindex4; // weapons, CTF flags, etc
int32_t frame;
int32_t skinnum;
effects_t effects; // PGM - we're filling it, so it needs to be unsigned
renderfx_t renderfx;
uint32_t solid; // for client side prediction
int32_t sound; // for looping sounds, to guarantee shutoff
entity_event_t event; // impulse events -- muzzle flashes, footsteps, etc
// events only go out for a single frame, they
// are automatically cleared each frame
float alpha; // [Paril-KEX] alpha scalar; 0 is a "default" value, which will respect other
// settings (default 1.0 for most things, EF_TRANSLUCENT will default this
// to 0.3, etc)
float scale; // [Paril-KEX] model scale scalar; 0 is a "default" value, like with alpha.
uint8_t instance_bits; // [Paril-KEX] players that *can't* see this entity will have a bit of 1. handled by
// the server, do not set directly.
// [Paril-KEX] allow specifying volume/attn for looping noises; note that
// zero will be defaults (1.0 and 3.0 respectively); -1 attenuation is used
// for "none" (similar to target_speaker) for no phs/pvs looping noises
float loop_volume;
float loop_attenuation;
// [Paril-KEX] for proper client-side owner collision skipping
int32_t owner;
// [Paril-KEX] for custom interpolation stuff
int32_t old_frame;
};
//==============================================
// player_state_t is the information needed in addition to pmove_state_t
// to rendered a view. There will only be 10 player_state_t sent each second,
// but the number of pmove_state_t changes will be relative to client
// frame rates
struct player_state_t
{
pmove_state_t pmove; // for prediction
// these fields do not need to be communicated bit-precise
gvec3_t viewangles; // for fixed views
gvec3_t viewoffset; // add to pmovestate->origin
gvec3_t kick_angles; // add to view direction to get render angles
// set by weapon kicks, pain effects, etc
gvec3_t gunangles;
gvec3_t gunoffset;
int32_t gunindex;
int32_t gunskin; // [Paril-KEX] gun skin #
int32_t gunframe;
int32_t gunrate; // [Paril-KEX] tickrate of gun animations; 0 and 10 are equivalent
std::array<float, 4> screen_blend; // rgba full screen effect
std::array<float, 4> damage_blend; // [Paril-KEX] rgba damage blend effect
float fov; // horizontal field of view
refdef_flags_t rdflags; // refdef flags
std::array<int16_t, MAX_STATS> stats; // fast status bar updates
uint8_t team_id; // team identifier
};
// protocol bytes that can be directly added to messages
enum server_command_t : uint8_t
{
svc_bad,
svc_muzzleflash,
svc_muzzleflash2,
svc_temp_entity,
svc_layout,
svc_inventory,
svc_nop,
svc_disconnect,
svc_reconnect,
svc_sound, // <see code>
svc_print, // [byte] id [string] null terminated string
svc_stufftext, // [string] stuffed into client's console buffer, should be \n terminated
svc_serverdata, // [long] protocol ...
svc_configstring, // [short] [string]
svc_spawnbaseline,
svc_centerprint, // [string] to put in center of the screen
svc_download, // [short] size [size bytes]
svc_playerinfo, // variable
svc_packetentities, // [...]
svc_deltapacketentities, // [...]
svc_frame,
svc_splitclient,
svc_configblast, // [Kex] A compressed version of svc_configstring
svc_spawnbaselineblast, // [Kex] A compressed version of svc_spawnbaseline
svc_level_restart, // [Paril-KEX] level was soft-rebooted
svc_damage, // [Paril-KEX] damage indicators
svc_locprint, // [Kex] localized + libfmt version of print
svc_fog, // [Paril-KEX] change current fog values
svc_waitingforplayers, // [Kex-Edward] Inform clients that the server is waiting for remaining players
svc_bot_chat, // [Kex] bot specific chat
svc_poi, // [Paril-KEX] point of interest
svc_help_path, // [Paril-KEX] help path
svc_muzzleflash3, // [Paril-KEX] muzzleflashes, but ushort id
svc_achievement, // [Paril-KEX]
svc_last // only for checks
};
enum svc_poi_flags
{
POI_FLAG_NONE = 0,
POI_FLAG_HIDE_ON_AIM = 1, // hide the POI if we get close to it with our aim
};
// data for svc_fog
struct svc_fog_data_t
{
enum bits_t : uint16_t
{
// global fog
BIT_DENSITY = bit_v<0>,
BIT_R = bit_v<1>,
BIT_G = bit_v<2>,
BIT_B = bit_v<3>,
BIT_TIME = bit_v<4>, // if set, the transition takes place over N milliseconds
// height fog
BIT_HEIGHTFOG_FALLOFF = bit_v<5>,
BIT_HEIGHTFOG_DENSITY = bit_v<6>,
BIT_MORE_BITS = bit_v<7>, // read additional bit
BIT_HEIGHTFOG_START_R = bit_v<8>,
BIT_HEIGHTFOG_START_G = bit_v<9>,
BIT_HEIGHTFOG_START_B = bit_v<10>,
BIT_HEIGHTFOG_START_DIST= bit_v<11>,
BIT_HEIGHTFOG_END_R = bit_v<12>,
BIT_HEIGHTFOG_END_G = bit_v<13>,
BIT_HEIGHTFOG_END_B = bit_v<14>,
BIT_HEIGHTFOG_END_DIST = bit_v<15>
};
bits_t bits;
float density; // bits & BIT_DENSITY
uint8_t skyfactor; // bits & BIT_DENSITY
uint8_t red; // bits & BIT_R
uint8_t green; // bits & BIT_G
uint8_t blue; // bits & BIT_B
uint16_t time; // bits & BIT_TIME
float hf_falloff; // bits & BIT_HEIGHTFOG_FALLOFF
float hf_density; // bits & BIT_HEIGHTFOG_DENSITY
uint8_t hf_start_r; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_START_R)
uint8_t hf_start_g; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_START_G)
uint8_t hf_start_b; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_START_B)
int32_t hf_start_dist; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_START_DIST)
uint8_t hf_end_r; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_END_R)
uint8_t hf_end_g; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_END_G)
uint8_t hf_end_b; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_END_B)
int32_t hf_end_dist; // bits & (BIT_MORE_BITS | BIT_HEIGHTFOG_END_DIST)
};
MAKE_ENUM_BITFLAGS(svc_fog_data_t::bits_t);
// bit masks
static constexpr svc_fog_data_t::bits_t BITS_GLOBAL_FOG = (svc_fog_data_t::BIT_DENSITY | svc_fog_data_t::BIT_R | svc_fog_data_t::BIT_G | svc_fog_data_t::BIT_B);
static constexpr svc_fog_data_t::bits_t BITS_HEIGHTFOG = (svc_fog_data_t::BIT_HEIGHTFOG_FALLOFF | svc_fog_data_t::BIT_HEIGHTFOG_DENSITY | svc_fog_data_t::BIT_HEIGHTFOG_START_R | svc_fog_data_t::BIT_HEIGHTFOG_START_G |
svc_fog_data_t::BIT_HEIGHTFOG_START_B | svc_fog_data_t::BIT_HEIGHTFOG_START_DIST | svc_fog_data_t::BIT_HEIGHTFOG_END_R | svc_fog_data_t::BIT_HEIGHTFOG_END_G |
svc_fog_data_t::BIT_HEIGHTFOG_END_B | svc_fog_data_t::BIT_HEIGHTFOG_END_DIST);
// edict->svflags
enum svflags_t : uint32_t
{
SVF_NONE = 0, // no serverflags
SVF_NOCLIENT = bit_v<0>, // don't send entity to clients, even if it has effects
SVF_DEADMONSTER = bit_v<1>, // treat as CONTENTS_DEADMONSTER for collision
SVF_MONSTER = bit_v<2>, // treat as CONTENTS_MONSTER for collision
SVF_PLAYER = bit_v<3>, // [Paril-KEX] treat as CONTENTS_PLAYER for collision
SVF_BOT = bit_v<4>, // entity is controlled by a bot AI.
SVF_NOBOTS = bit_v<5>, // don't allow bots to use/interact with entity
SVF_RESPAWNING = bit_v<6>, // entity will respawn on it's next think.
SVF_PROJECTILE = bit_v<7>, // treat as CONTENTS_PROJECTILE for collision
SVF_INSTANCED = bit_v<8>, // entity has different visibility per player
SVF_DOOR = bit_v<9>, // entity is a door of some kind
SVF_NOCULL = bit_v<10>, // always send, even if we normally wouldn't
SVF_HULL = bit_v<11> // always use hull when appropriate (triggers, etc; for gi.clip)
};
MAKE_ENUM_BITFLAGS(svflags_t);
// edict->solid values
enum solid_t : uint8_t
{
SOLID_NOT, // no interaction with other objects
SOLID_TRIGGER, // only touch when inside, after moving
SOLID_BBOX, // touch on edge
SOLID_BSP // bsp clip, touch on edge
};
// bitflags for STAT_LAYOUTS
enum layout_flags_t : int16_t
{
LAYOUTS_LAYOUT = bit_v<0>, // svc_layout is active; escape remapped to putaway
LAYOUTS_INVENTORY = bit_v<1>, // inventory is active; escape remapped to putaway
LAYOUTS_HIDE_HUD = bit_v<2>, // hide entire hud, for cameras, etc
LAYOUTS_INTERMISSION = bit_v<3>, // intermission is being drawn; collapse splitscreen into 1 view
LAYOUTS_HELP = bit_v<4>, // help is active; escape remapped to putaway
LAYOUTS_HIDE_CROSSHAIR = bit_v<5> // hide crosshair only
};
MAKE_ENUM_BITFLAGS(layout_flags_t);
enum GoalReturnCode {
Error = 0,
Started,
InProgress,
Finished
};
enum gesture_type {
GESTURE_NONE = -1,
GESTURE_FLIP_OFF,
GESTURE_SALUTE,
GESTURE_TAUNT,
GESTURE_WAVE,
GESTURE_POINT,
GESTURE_POINT_NO_PING,
GESTURE_MAX
};
enum class PathReturnCode {
ReachedGoal = 0, // we're at our destination
ReachedPathEnd, // we're as close to the goal as we can get with a path
TraversalPending, // the upcoming path segment is a traversal
RawPathFound, // user wanted ( and got ) just a raw path ( no processing )
InProgress, // pathing in progress
StartPathErrors, // any code after this one indicates an error of some kind.
InvalidStart, // start position is invalid.
InvalidGoal, // goal position is invalid.
NoNavAvailable, // no nav file available for this map.
NoStartNode, // can't find a nav node near the start position
NoGoalNode, // can't find a nav node near the goal position
NoPathFound, // can't find a path from the start to the goal
MissingWalkOrSwimFlag // MUST have at least Walk or Water path flags set!
};
enum class PathLinkType {
Walk, // can walk between the path points
WalkOffLedge, // will walk off a ledge going between path points
LongJump, // will need to perform a long jump between path points
BarrierJump, // will need to jump over a low barrier between path points
Elevator // will need to use an elevator between path points
};
enum PathFlags : uint32_t {
All = static_cast<uint32_t>( -1 ),
Water = bit_v<0>, // swim to your goal ( useful for fish/gekk/etc. )
Walk = bit_v<1>, // walk to your goal
WalkOffLedge = bit_v<2>, // allow walking over ledges
LongJump = bit_v<3>, // allow jumping over gaps
BarrierJump = bit_v<4>, // allow jumping over low barriers
Elevator = bit_v<5> // allow using elevators
};
MAKE_ENUM_BITFLAGS(PathFlags);
struct PathRequest {
gvec3_t start = { 0.0f, 0.0f, 0.0f };
gvec3_t goal = { 0.0f, 0.0f, 0.0f };
PathFlags pathFlags = PathFlags::Walk;
float moveDist = 0.0f;
struct DebugSettings {
float drawTime = 0.0f; // if > 0, how long ( in seconds ) to draw path in world
} debugging;
struct NodeSettings {
bool ignoreNodeFlags = false; // true = ignore node flags when considering nodes
float minHeight = 0.0f; // 0 <= use default values
float maxHeight = 0.0f; // 0 <= use default values
float radius = 0.0f; // 0 <= use default values
} nodeSearch;
struct TraversalSettings {
float dropHeight = 0.0f; // 0 = don't drop down
float jumpHeight = 0.0f; // 0 = don't jump up
} traversals;
struct PathArray {
mutable gvec3_t * array = nullptr; // array to store raw path points
int64_t count = 0; // number of elements in array
} pathPoints;
};
struct PathInfo {
int32_t numPathPoints = 0;
float pathDistSqr = 0.0f;
gvec3_t firstMovePoint = { 0.0f, 0.0f, 0.0f };
gvec3_t secondMovePoint = { 0.0f, 0.0f, 0.0f };
PathLinkType pathLinkType = PathLinkType::Walk;
PathReturnCode returnCode = PathReturnCode::StartPathErrors;
};
//===============================================================
constexpr int32_t MODELINDEX_WORLD = 1; // special index for world
constexpr int32_t MODELINDEX_PLAYER = MAX_MODELS_OLD - 1; // special index for player models
// short stubs only used by the engine; the game DLL's version
// must be compatible with this.
#ifndef GAME_INCLUDE
struct gclient_t
#else
struct gclient_shared_t
#endif
{
player_state_t ps; // communicated by server to clients
int32_t ping;
// the game dll can add anything it wants after
// this point in the structure
};
static constexpr int32_t Team_None = 0;
static constexpr int32_t Item_UnknownRespawnTime = INT_MAX;
static constexpr int32_t Item_Invalid = -1;
static constexpr int32_t Item_Null = 0;
enum sv_ent_flags_t : uint64_t {
SVFL_NONE = 0, // no flags
SVFL_ONGROUND = bit_v< 0 >,
SVFL_HAS_DMG_BOOST = bit_v< 1 >,
SVFL_HAS_PROTECTION = bit_v< 2 >,
SVFL_HAS_INVISIBILITY = bit_v< 3 >,
SVFL_IS_JUMPING = bit_v< 4 >,
SVFL_IS_CROUCHING = bit_v< 5 >,
SVFL_IS_ITEM = bit_v< 6 >,
SVFL_IS_OBJECTIVE = bit_v< 7 >,
SVFL_HAS_TELEPORTED = bit_v< 8 >,
SVFL_TAKES_DAMAGE = bit_v< 9 >,
SVFL_IS_HIDDEN = bit_v< 10 >,
SVFL_IS_NOCLIP = bit_v< 11 >,
SVFL_IN_WATER = bit_v< 12 >,
SVFL_NO_TARGET = bit_v< 13 >,
SVFL_GOD_MODE = bit_v< 14 >,
SVFL_IS_FLIPPING_OFF = bit_v< 15 >,
SVFL_IS_SALUTING = bit_v< 16 >,
SVFL_IS_TAUNTING = bit_v< 17 >,
SVFL_IS_WAVING = bit_v< 18 >,
SVFL_IS_POINTING = bit_v< 19 >,
SVFL_ON_LADDER = bit_v< 20 >,
SVFL_MOVESTATE_TOP = bit_v< 21 >,
SVFL_MOVESTATE_BOTTOM = bit_v< 22 >,
SVFL_MOVESTATE_MOVING = bit_v< 23 >,
SVFL_IS_LOCKED_DOOR = bit_v< 24 >,
SVFL_CAN_GESTURE = bit_v< 25 >,
SVFL_WAS_TELEFRAGGED = bit_v< 26 >,
SVFL_TRAP_DANGER = bit_v< 27 >,
SVFL_ACTIVE = bit_v< 28 >,
SVFL_IS_SPECTATOR = bit_v< 29 >,
SVFL_IN_TEAM = bit_v< 30 >
};
MAKE_ENUM_BITFLAGS( sv_ent_flags_t );
#include <bitset>
static constexpr int Max_Armor_Types = 3;
struct armorInfo_t {
int32_t item_id = Item_Null;
int32_t max_count = 0;
};
// Used by AI/Tools on the engine side...
struct sv_entity_t {
bool init;
sv_ent_flags_t ent_flags;
button_t buttons;
uint32_t spawnflags;
int32_t item_id;
int32_t armor_type;
int32_t armor_value;
int32_t health;
int32_t max_health;
int32_t starting_health;
int32_t weapon;
int32_t team;
int32_t lobby_usernum;
int32_t respawntime;
int32_t viewheight;
int32_t last_attackertime;
water_level_t waterlevel;
gvec3_t viewangles;
gvec3_t viewforward;
gvec3_t velocity;
gvec3_t start_origin;
gvec3_t end_origin;
edict_t * enemy;
edict_t * ground_entity;
const char * classname;
const char * targetname;
char netname[ MAX_NETNAME ];
int32_t inventory[ MAX_ITEMS ] = { 0 };
armorInfo_t armor_info[ Max_Armor_Types ];
std::bitset<MAX_CLIENTS> pickedup_list;
};
#ifndef GAME_INCLUDE
struct edict_t
#else
struct edict_shared_t
#endif
{
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;
};
#define CHECK_INTEGRITY(from_type, to_type, member) \
static_assert(offsetof(from_type, member) == offsetof(to_type, member) && \
sizeof(from_type::member) == sizeof(to_type::member), \
"structure malformed; not compatible with server: check member \"" #member "\"")
#define CHECK_GCLIENT_INTEGRITY \
CHECK_INTEGRITY(gclient_t, gclient_shared_t, ps); \
CHECK_INTEGRITY(gclient_t, gclient_shared_t, ping)
#define CHECK_EDICT_INTEGRITY \
CHECK_INTEGRITY(edict_t, edict_shared_t, s); \
CHECK_INTEGRITY(edict_t, edict_shared_t, client); \
CHECK_INTEGRITY(edict_t, edict_shared_t, sv); \
CHECK_INTEGRITY(edict_t, edict_shared_t, inuse); \
CHECK_INTEGRITY(edict_t, edict_shared_t, linked); \
CHECK_INTEGRITY(edict_t, edict_shared_t, linkcount); \
CHECK_INTEGRITY(edict_t, edict_shared_t, areanum); \
CHECK_INTEGRITY(edict_t, edict_shared_t, areanum2); \
CHECK_INTEGRITY(edict_t, edict_shared_t, svflags); \
CHECK_INTEGRITY(edict_t, edict_shared_t, mins); \
CHECK_INTEGRITY(edict_t, edict_shared_t, maxs); \
CHECK_INTEGRITY(edict_t, edict_shared_t, absmin); \
CHECK_INTEGRITY(edict_t, edict_shared_t, absmax); \
CHECK_INTEGRITY(edict_t, edict_shared_t, size); \
CHECK_INTEGRITY(edict_t, edict_shared_t, solid); \
CHECK_INTEGRITY(edict_t, edict_shared_t, clipmask); \
CHECK_INTEGRITY(edict_t, edict_shared_t, owner)
//===============================================================
// file system stuff
using fs_handle_t = uint64_t;
enum fs_search_flags_t
{
FS_SEARCH_NONE = 0,
// flags for individual file filtering; note that if none
// of these are set, they will all apply.
FS_SEARCH_FOR_DIRECTORIES = bit_v<0>, // only get directories
FS_SEARCH_FOR_FILES = bit_v<1> // only get files
};
MAKE_ENUM_BITFLAGS(fs_search_flags_t);
enum class BoxEdictsResult_t
{
Keep, // keep the given entity in the result and keep looping
Skip, // skip the given entity
End = 64, // stop searching any further
Flags = End
};
MAKE_ENUM_BITFLAGS(BoxEdictsResult_t);
using BoxEdictsFilter_t = BoxEdictsResult_t (*)(edict_t *, void *);
//
// functions provided by the main engine
//
struct game_import_t
{
uint32_t tick_rate;
float frame_time_s;
uint32_t frame_time_ms;
// broadcast to all clients
void (*Broadcast_Print)(print_type_t printlevel, const char *message);
// print to appropriate places (console, log file, etc)
void (*Com_Print)(const char *msg);
// print directly to a single client (or nullptr for server console)
void (*Client_Print)(edict_t *ent, print_type_t printlevel, const char *message);
// center-print to player (legacy function)
void (*Center_Print)(edict_t *ent, const char *message);
void (*sound)(edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs);
void (*positioned_sound)(gvec3_cref_t origin, edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs);
// [Paril-KEX] like sound, but only send to the player indicated by the parameter;
// this is mainly to handle split screen properly
void (*local_sound)(edict_t *target, gvec3_cptr_t origin, edict_t *ent, soundchan_t channel, int soundindex, float volume, float attenuation, float timeofs, uint32_t dupe_key);
// config strings hold all the index strings, the lightstyles,
// and misc data like the sky definition and cdtrack.
// All of the current configstrings are sent to clients when
// they connect, and changes are sent to all connected clients.
void (*configstring)(int num, const char *string);
const char *(*get_configstring)(int num);
void (*Com_Error)(const char *message);
// the *index functions create configstrings and some internal server state
int (*modelindex)(const char *name);
int (*soundindex)(const char *name);
// [Paril-KEX] imageindex can precache both pics for the HUD and
// textures used for RF_CUSTOMSKIN; to register an image as a texture,
// the path must be relative to the mod dir and end in an extension
// ie models/my_model/skin.tga
int (*imageindex)(const char *name);
void (*setmodel)(edict_t *ent, const char *name);
// collision detection
trace_t (*trace)(gvec3_cref_t start, gvec3_cptr_t mins, gvec3_cptr_t maxs, gvec3_cref_t end, const edict_t *passent, contents_t contentmask);
// [Paril-KEX] clip the box against the specified entity
trace_t (*clip)(edict_t *entity, gvec3_cref_t start, gvec3_cptr_t mins, gvec3_cptr_t maxs, gvec3_cref_t end, contents_t contentmask);
contents_t (*pointcontents)(gvec3_cref_t point);
bool (*inPVS)(gvec3_cref_t p1, gvec3_cref_t p2, bool portals);
bool (*inPHS)(gvec3_cref_t p1, gvec3_cref_t p2, bool portals);
void (*SetAreaPortalState)(int portalnum, bool open);
bool (*AreasConnected)(int area1, int area2);
// an entity will never be sent to a client or used for collision
// if it is not passed to linkentity. If the size, position, or
// solidity changes, it must be relinked.
void (*linkentity)(edict_t *ent);
void (*unlinkentity)(edict_t *ent); // call before removing an interactive edict
// return a list of entities that touch the input absmin/absmax.
// if maxcount is 0, it will return a count but not attempt to fill "list".
// if maxcount > 0, once it reaches maxcount, it will keep going but not fill
// any more of list (the return count will cap at maxcount).
// the filter function can remove unnecessary entities from the final list; it is illegal
// to modify world links in this callback.
size_t (*BoxEdicts)(gvec3_cref_t mins, gvec3_cref_t maxs, edict_t **list, size_t maxcount, solidity_area_t areatype, BoxEdictsFilter_t filter, void *filter_data);
// network messaging
void (*multicast)(gvec3_cref_t origin, multicast_t to, bool reliable);
// [Paril-KEX] `dupe_key` is a key unique to a group of calls to unicast
// that will prevent sending the message on this frame with the same key
// to the same player (for splitscreen players).
void (*unicast)(edict_t *ent, bool reliable, uint32_t dupe_key);
void (*WriteChar)(int c);
void (*WriteByte)(int c);
void (*WriteShort)(int c);
void (*WriteLong)(int c);
void (*WriteFloat)(float f);
void (*WriteString)(const char *s);
void (*WritePosition)(gvec3_cref_t pos);
void (*WriteDir)(gvec3_cref_t pos); // single byte encoded, very coarse
void (*WriteAngle)(float f); // legacy 8-bit angle
void (*WriteEntity)(const edict_t *e);
// managed memory allocation
void *(*TagMalloc)(size_t size, int tag);
void (*TagFree)(void *block);
void (*FreeTags)(int tag);
// console variable interaction
cvar_t *(*cvar)(const char *var_name, const char *value, cvar_flags_t flags);
cvar_t *(*cvar_set)(const char *var_name, const char *value);
cvar_t *(*cvar_forceset)(const char *var_name, const char *value);
// ClientCommand and ServerCommand parameter access
int (*argc)();
const char *(*argv)(int n);
const char *(*args)(); // concatenation of all argv >= 1
// add commands to the server console as if they were typed in
// for map changing, etc
void (*AddCommandString)(const char *text);
void (*DebugGraph)(float value, int color);
// Fetch named extension from engine.
void *(*GetExtension)(const char *name);
// === [KEX] Additional APIs ===
// bots
void (*Bot_RegisterEdict)(const edict_t * edict);
void (*Bot_UnRegisterEdict)(const edict_t * edict);
GoalReturnCode (*Bot_MoveToPoint)(const edict_t * bot, gvec3_cref_t point, const float moveTolerance);
GoalReturnCode (*Bot_FollowActor)(const edict_t * bot, const edict_t * actor);
// pathfinding - returns true if a path was found
bool (*GetPathToGoal)(const PathRequest & request, PathInfo & info);
// localization
void (*Loc_Print)(edict_t* ent, print_type_t level, const char* base, const char** args, size_t num_args);
// drawing
void (*Draw_Line)(gvec3_cref_t start, gvec3_cref_t end, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_Point)(gvec3_cref_t point, const float size, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_Circle)(gvec3_cref_t origin, const float radius, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_Bounds)(gvec3_cref_t mins, gvec3_cref_t maxs, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_Sphere)(gvec3_cref_t origin, const float radius, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_OrientedWorldText)(gvec3_cref_t origin, const char * text, const rgba_t &color, const float size, const float lifeTime, const bool depthTest);
void (*Draw_StaticWorldText )(gvec3_cref_t origin, gvec3_cref_t angles, const char * text, const rgba_t & color, const float size, const float lifeTime, const bool depthTest);
void (*Draw_Cylinder)(gvec3_cref_t origin, const float halfHeight, const float radius, const rgba_t &color, const float lifeTime, const bool depthTest);
void (*Draw_Ray)(gvec3_cref_t origin, gvec3_cref_t direction, const float length, const float size, const rgba_t &color, const float lifeTime, const bool depthTest);
// scoreboard
void (*ReportMatchDetails_Multicast)(bool is_end);
// get server frame #
uint32_t (*ServerFrame)();
// misc utils
void (*SendToClipBoard)(const char * text);
// info string stuff
size_t (*Info_ValueForKey) (const char *s, const char *key, char *buffer, size_t buffer_len);
bool (*Info_RemoveKey) (char *s, const char *key);
bool (*Info_SetValueForKey) (char *s, const char *key, const char *value);
};
enum class shadow_light_type_t
{
point,
cone
};
struct shadow_light_data_t
{
shadow_light_type_t lighttype;
float radius;
int resolution;
float intensity = 1;
float fade_start;
float fade_end;
int lightstyle = -1;
float coneangle = 45;
vec3_t conedirection;
};
enum server_flags_t
{
SERVER_FLAGS_NONE = 0,
SERVER_FLAG_SLOW_TIME = bit_v<0>,
SERVER_FLAG_INTERMISSION = bit_v<1>,
SERVER_FLAG_LOADING = bit_v<2>
};
MAKE_ENUM_BITFLAGS(server_flags_t);
//
// functions exported by the game subsystem
//
struct game_export_t
{
int apiversion;
// the init function will only be called when a game starts,
// not each time a level is loaded. Persistant data for clients
// and the server can be allocated in init
void (*PreInit)(); // [Paril-KEX] called before InitGame, to potentially change maxclients
void (*Init)();
void (*Shutdown)();
// each new level entered will cause a call to SpawnEntities
void (*SpawnEntities)(const char *mapname, const char *entstring, const char *spawnpoint);
// Read/Write Game is for storing persistant cross level information
// about the world state and the clients.
// WriteGame is called every time a level is exited.
// ReadGame is called on a loadgame.
// returns pointer to tagmalloc'd allocated string.
// tagfree after use
char *(*WriteGameJson)(bool autosave, size_t *out_size);
void (*ReadGameJson)(const char *json);
// ReadLevel is called after the default map information has been
// loaded with SpawnEntities
// returns pointer to tagmalloc'd allocated string.
// tagfree after use
char *(*WriteLevelJson)(bool transition, size_t *out_size);
void (*ReadLevelJson)(const char *json);
// [Paril-KEX] game can tell the server whether a save is allowed
// currently or not.
bool (*CanSave)();
// [Paril-KEX] choose a free gclient_t slot for the given social ID; for
// coop slot re-use. Return nullptr if none is available. You can not
// return a slot that is currently in use by another client; that must
// throw a fatal error.
edict_t *(*ClientChooseSlot) (const char *userinfo, const char *social_id, bool isBot, edict_t **ignore, size_t num_ignore, bool cinematic);
bool (*ClientConnect)(edict_t *ent, char *userinfo, const char *social_id, bool isBot);
void (*ClientBegin)(edict_t *ent);
void (*ClientUserinfoChanged)(edict_t *ent, const char *userinfo);
void (*ClientDisconnect)(edict_t *ent);
void (*ClientCommand)(edict_t *ent);
void (*ClientThink)(edict_t *ent, usercmd_t *cmd);
void (*RunFrame)(bool main_loop);
// [Paril-KEX] allow the game DLL to clear per-frame stuff
void (*PrepFrame)();
// ServerCommand will be called when an "sv <command>" command is issued on the
// server console.
// The game can issue gi.argc() / gi.argv() commands to get the rest
// of the parameters
void (*ServerCommand)();
//
// global variables shared between game and server
//
// The edict array is allocated in the game dll so it
// can vary in size from one game to another.
//
// The size will be fixed when ge->Init() is called
edict_t *edicts;
size_t edict_size;
uint32_t num_edicts; // current number, <= max_edicts
uint32_t max_edicts;
// [Paril-KEX] special flags to indicate something to the server
server_flags_t server_flags;
// [KEX]: Pmove as export
void (*Pmove)(pmove_t *pmove); // player movement code called by server & client
// Fetch named extension from game DLL.
void *(*GetExtension)(const char *name);
void (*Bot_SetWeapon)(edict_t * botEdict, const int weaponIndex, const bool instantSwitch);
void (*Bot_TriggerEdict)(edict_t * botEdict, edict_t * edict);
void (*Bot_UseItem)(edict_t * botEdict, const int32_t itemID);
int32_t (*Bot_GetItemID)(const char * classname);
// [KEX]: Checks entity visibility instancing
bool (*Entity_IsVisibleToPlayer)(edict_t* ent, edict_t* player);
// Fetch info from the shadow light, for culling
const shadow_light_data_t *(*GetShadowLightData)(int32_t entity_number);
};
// generic rectangle
struct vrect_t
{
int32_t x, y, width, height;
};
enum class text_align_t
{
LEFT,
CENTER,
RIGHT
};
// transient data from server
struct cg_server_data_t
{
char layout[1024];
std::array<int16_t, MAX_ITEMS> inventory;
};
constexpr int32_t PROTOCOL_VERSION_3XX = 34;
constexpr int32_t PROTOCOL_VERSION_DEMOS = 2022;
constexpr int32_t PROTOCOL_VERSION = 2023;
//
// functions provided by main engine for client
//
struct cgame_import_t
{
uint32_t tick_rate;
float frame_time_s;
uint32_t frame_time_ms;
// print to appropriate places (console, log file, etc)
void (*Com_Print)(const char *msg);
// config strings hold all the index strings, the lightstyles,
// and misc data like the sky definition and cdtrack.
// All of the current configstrings are sent to clients when
// they connect, and changes are sent to all connected clients.
const char *(*get_configstring)(int num);
void (*Com_Error)(const char *message);
// managed memory allocation
void *(*TagMalloc)(size_t size, int tag);
void (*TagFree)(void *block);
void (*FreeTags)(int tag);
// console variable interaction
cvar_t *(*cvar)(const char *var_name, const char *value, cvar_flags_t flags);
cvar_t *(*cvar_set)(const char *var_name, const char *value);
cvar_t *(*cvar_forceset)(const char *var_name, const char *value);
// add commands to the server console as if they were typed in
// for map changing, etc
void (*AddCommandString)(const char *text);
// Fetch named extension from engine.
void *(*GetExtension)(const char *name);
// Check whether current frame is valid
bool (*CL_FrameValid) ();
// Get client frame time delta
float (*CL_FrameTime) ();
// [Paril-KEX] cgame-specific stuff
uint64_t (*CL_ClientTime) ();
uint64_t (*CL_ClientRealTime) ();
int32_t (*CL_ServerFrame) ();
int32_t (*CL_ServerProtocol) ();
const char *(*CL_GetClientName) (int32_t index);
const char *(*CL_GetClientPic) (int32_t index);
const char *(*CL_GetClientDogtag) (int32_t index);
const char *(*CL_GetKeyBinding) (const char *binding); // fetch key bind for key, or empty string
bool (*Draw_RegisterPic) (const char *name);
void (*Draw_GetPicSize) (int *w, int *h, const char *name); // will return 0 0 if not found
void (*SCR_DrawChar)(int x, int y, int scale, int num, bool shadow);
void (*SCR_DrawPic) (int x, int y, int w, int h, const char *name);
void (*SCR_DrawColorPic)(int x, int y, int w, int h, const char* name, const rgba_t &color);
// [Paril-KEX] kfont stuff
void(*SCR_SetAltTypeface)(bool enabled);
void (*SCR_DrawFontString)(const char *str, int x, int y, int scale, const rgba_t &color, bool shadow, text_align_t align);
vec2_t (*SCR_MeasureFontString)(const char *str, int scale);
float (*SCR_FontLineHeight)(int scale);
// [Paril-KEX] for legacy text input (not used in lobbies)
bool (*CL_GetTextInput)(const char **msg, bool *is_team);
// [Paril-KEX] FIXME this probably should be an export instead...
int32_t (*CL_GetWarnAmmoCount)(int32_t weapon_id);
// === [KEX] Additional APIs ===
// returns a *temporary string* ptr to a localized input
const char* (*Localize) (const char *base, const char **args, size_t num_args);
// [Paril-KEX] Draw binding, for centerprint; returns y offset
int32_t (*SCR_DrawBind) (int32_t isplit, const char *binding, const char *purpose, int x, int y, int scale);
// [Paril-KEX]
bool (*CL_InAutoDemoLoop) ();
};
//
// functions exported for client by game subsystem
//
struct cgame_export_t
{
int apiversion;
// the init/shutdown functions will be called between levels/connections
// (cgame does not run in menus)
void (*Init)();
void (*Shutdown)();
// [Paril-KEX] hud drawing
void (*DrawHUD) (int32_t isplit, const cg_server_data_t *data, vrect_t hud_vrect, vrect_t hud_safe, int32_t scale, int32_t playernum, const player_state_t *ps);
// [Paril-KEX] precache special pics used by hud
void (*TouchPics) ();
// [Paril-KEX] layout flags; see layout_flags_t
layout_flags_t (*LayoutFlags) (const player_state_t *ps);
// [Paril-KEX] fetch the current wheel weapon ID in use
int32_t (*GetActiveWeaponWheelWeapon) (const player_state_t *ps);
// [Paril-KEX] fetch owned weapon IDs
uint32_t (*GetOwnedWeaponWheelWeapons) (const player_state_t *ps);
// [Paril-KEX] fetch ammo count for given ammo id
int16_t (*GetWeaponWheelAmmoCount)(const player_state_t *ps, int32_t ammo_id);
// [Paril-KEX] fetch powerup count for given powerup id
int16_t (*GetPowerupWheelCount)(const player_state_t *ps, int32_t powerup_id);
// [Paril-KEX] fetch how much damage was registered by these stats
int16_t (*GetHitMarkerDamage)(const player_state_t *ps);
// [KEX]: Pmove as export
void (*Pmove)(pmove_t *pmove); // player movement code called by server & client
// [Paril-KEX] allow cgame to react to configstring changes
void (*ParseConfigString)(int32_t i, const char *s);
// [Paril-KEX] parse centerprint-like messages
void (*ParseCenterPrint)(const char *str, int isplit, bool instant);
// [Paril-KEX] tell the cgame to clear notify stuff
void (*ClearNotify)(int32_t isplit);
// [Paril-KEX] tell the cgame to clear centerprint state
void (*ClearCenterprint)(int32_t isplit);
// [Paril-KEX] be notified by the game DLL of a message of some sort
void (*NotifyMessage)(int32_t isplit, const char *msg, bool is_chat);
// [Paril-KEX]
void (*GetMonsterFlashOffset)(monster_muzzleflash_id_t id, gvec3_ref_t offset);
// Fetch named extension from cgame DLL.
void *(*GetExtension)(const char *name);
};
// EOF