1 #ifndef __GAME_H__
2 #define __GAME_H__
3 
4 #include "engine.h"
5 
6 #define VERSION_GAMEID "fps"
7 #define VERSION_GAME 245
8 #define VERSION_DEMOMAGIC "RED_ECLIPSE_DEMO"
9 
10 #define MAXAI 256
11 #define MAXPLAYERS (MAXCLIENTS+MAXAI*2)
12 #define MAXPARAMS 256
13 
14 // network quantization scale
15 #define DMF 16.0f // for world locations
16 #define DNF 1000.0f // for normalized vectors
17 #define DVELF 1.0f // for playerspeed based velocity vectors
18 
19 #define FOOTSTEP_DIST 1.5f // Threshold, below which a footstep will be registered
20 
21 enum
22 {
23     S_JUMP = S_GAMESPECIFIC, S_IMPULSE, S_LAND, S_FOOTSTEP, S_SWIMSTEP, S_PAIN, S_DEATH,
24     S_SPLASH1, S_SPLASH2, S_SPLOSH, S_DEBRIS, S_BURNLAVA,
25     S_EXTINGUISH, S_SHELL, S_ITEMUSE, S_ITEMSPAWN,
26     S_REGEN, S_CRITICAL, S_DAMAGE, S_DAMAGE2, S_DAMAGE3, S_DAMAGE4, S_DAMAGE5, S_DAMAGE6, S_DAMAGE7, S_DAMAGE8,
27     S_BURNED, S_BLEED, S_SHOCK, S_RESPAWN, S_CHAT, S_ERROR, S_ALARM, S_CATCH, S_BOUNCE,
28     S_V_FLAGSECURED, S_V_FLAGOVERTHROWN, S_V_FLAGPICKUP, S_V_FLAGDROP, S_V_FLAGRETURN, S_V_FLAGSCORE, S_V_FLAGRESET,
29     S_V_BOMBSTART, S_V_BOMBDUEL, S_V_BOMBPICKUP, S_V_BOMBSCORE, S_V_BOMBRESET,
30     S_V_NOTIFY, S_V_FIGHT, S_V_START, S_V_CHECKPOINT, S_V_COMPLETE, S_V_OVERTIME, S_V_ONEMINUTE, S_V_HEADSHOT,
31     S_V_SPREE, S_V_SPREE2, S_V_SPREE3, S_V_SPREE4, S_V_MULTI, S_V_MULTI2, S_V_MULTI3,
32     S_V_REVENGE, S_V_DOMINATE, S_V_FIRSTBLOOD, S_V_BREAKER,
33     S_V_YOUWIN, S_V_YOULOSE, S_V_DRAW, S_V_FRAGGED, S_V_BALWARN, S_V_BALALERT,
34     S_GAME
35 };
36 
37 enum                                // entity types
38 {
39     NOTUSED = ET_EMPTY, LIGHT = ET_LIGHT, MAPMODEL = ET_MAPMODEL, PLAYERSTART = ET_PLAYERSTART, ENVMAP = ET_ENVMAP, PARTICLES = ET_PARTICLES,
40     MAPSOUND = ET_SOUND, LIGHTFX = ET_LIGHTFX, DECAL = ET_DECAL, WIND = ET_WIND, OUTLINE = ET_OUTLINE, WEAPON = ET_GAMESPECIFIC,
41     TELEPORT, ACTOR, TRIGGER, PUSHER, AFFINITY, CHECKPOINT,
42     ROUTE, UNUSEDENT,
43     MAXENTTYPES
44 };
45 
46 enum { EU_NONE = 0, EU_ITEM, EU_AUTO, EU_ACT, EU_MAX };
47 
48 enum { TR_TOGGLE = 0, TR_LINK, TR_SCRIPT, TR_ONCE, TR_EXIT, TR_MAX };
49 enum { TA_MANUAL = 0, TA_AUTO, TA_ACTION, TA_MAX };
50 
51 #define TRIGGERIDS      16
52 #define TRIGSTATE(a,b)  (b%2 ? !a : a)
53 #define TRIGGERTIME     1000
54 #define TRIGGERDELAY    250
55 
56 enum { CP_RESPAWN = 0, CP_START, CP_FINISH, CP_LAST, CP_MAX, CP_ALL = (1<<CP_RESPAWN)|(1<<CP_START)|(1<<CP_FINISH)|(1<<CP_LAST) };
57 
58 enum { TELE_NOAFFIN = 0, TELE_MAX };
59 enum { MDLF_HIDE = 0, MDLF_NOCLIP, MDLF_NOSHADOW, MDLF_MAX };
60 enum { LIGHT_NOSHADOW = 0, LIGHT_STATIC, LIGHT_VOLUMETRIC, LIGHT_NOSPEC };
61 
62 struct enttypes
63 {
64     int type,           priority, links,    radius, usetype,    numattrs,   modesattr,  idattr, mvattr, fxattr,
65             canlink, reclink, canuse;
66     bool    noisy,  syncs,  resyncs,    syncpos,    synckin;
67     const char *name,           *attrs[16];
68 };
69 #ifdef CPP_GAME_SERVER
70 extern const enttypes enttype[] = {
71     {
72         NOTUSED,        -1,         0,      0,      EU_NONE,    0,          -1,         -1,     -1,     -1,
73             0, 0, 0,
74             true,   false,  false,      false,      false,
75                 "none",         { "" }
76     },
77     {
78         LIGHT,          1,          59,     0,      EU_NONE,    11,         -1,         -1,     9,      10,
79             (1<<LIGHTFX), (1<<LIGHTFX), 0,
80             false,  false,  false,      false,      false,
81                 "light",        { "radius", "red",      "green",    "blue",     "flare",    "flarescale", "flags",  "palette",  "palindex", "variant", "fxlevel"  }
82     },
83     {
84         MAPMODEL,       1,          58,     0,      EU_NONE,    15,         -1,         -1,     13,     14,
85             (1<<TRIGGER), (1<<TRIGGER), 0,
86             false,  false,  false,      false,      false,
87                 "mapmodel",     { "type",   "yaw",      "pitch",    "roll",     "blend",    "scale",    "flags",    "colour",   "palette",  "palindex", "spinyaw",  "spinpitch", "spinroll",    "variant",  "fxlevel" }
88     },
89     {
90         PLAYERSTART,    1,          59,     0,      EU_NONE,    7,          3,          5,      6,      -1,
91             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
92             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
93             0,
94             false,  true,  false,      false,      false,
95                 "playerstart",  { "team",   "yaw",      "pitch",    "modes",    "muts",     "id",       "variant" }
96     },
97     {
98         ENVMAP,         1,          0,      0,      EU_NONE,    3,          -1,         -1,     -1,     -1,
99             0, 0, 0,
100             false,  false,  false,      false,      false,
101                 "envmap",       { "radius", "size", "blur" }
102     },
103     {
104         PARTICLES,      1,          59,     0,      EU_NONE,    14,         -1,         -1,     12,     13,
105             (1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT),
106             (1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT),
107             0,
108             false,  false,  false,      false,      false,
109                 "particles",    { "type",   "1",        "2",        "3",        "4",        "5",        "6",        "7",        "8",        "9",        "10",   "millis",   "variant",  "fxlevel" }
110     },
111     {
112         MAPSOUND,       1,          58,     0,      EU_NONE,    6,          -1,         -1,     5,      -1,
113             (1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT)|(1<<WIND),
114             (1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT)|(1<<WIND),
115             0,
116             false,  false,  false,      false,      false,
117                 "sound",        { "type",   "maxrad",   "minrad",   "volume",   "flags",    "variant" }
118     },
119     {
120         LIGHTFX,        1,          1,      0,      EU_NONE,    7,          -1,         -1,     5,      6,
121             (1<<LIGHT)|(1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT),
122             (1<<LIGHT)|(1<<TRIGGER)|(1<<PUSHER)|(1<<PLAYERSTART)|(1<<CHECKPOINT),
123             0,
124             false,  false,  false,      false,      false,
125                 "lightfx",      { "type",   "mod",      "min",      "max",      "flags",    "variant",  "fxlevel" }
126     },
127     {
128         DECAL,          1,          0,      0,      EU_NONE,    11,         -1,         -1,     9,      10,
129             0, 0, 0,
130             false,  false,  false,      false,      false,
131                 "decal",        { "type",   "yaw",      "pitch",    "roll",     "scale",    "blend",    "colour",   "palette",  "palindex", "variant",  "fxlevel" }
132     },
133     {
134         WIND,           1,          239,    0,      EU_NONE,    9,          -1,         -1,     7,      8,
135             (1<<MAPSOUND),
136             (1<<MAPSOUND),
137             0,
138             false,  false,  false,      false,      false,
139                 "wind",        { "mode",   "yaw",      "speed",    "radius",   "atten",    "interval",  "implen",   "variant",  "fxlevel" }
140     },
141     {
142         OUTLINE,        1,          241,    0,      EU_NONE,    0,          -1,         -1,     -1,     -1,
143             (1<<OUTLINE),
144             (1<<OUTLINE),
145             0,
146             false,  false,  false,      false,      false,
147                 "outline",      { "" }
148     },
149     {
150         WEAPON,         2,          59,     24,     EU_ITEM,    6,          2,          4,      5,      -1,
151             0, 0,
152             (1<<ENT_PLAYER)|(1<<ENT_AI),
153             false,  true,   true,      false,      false,
154                 "weapon",       { "type",   "flags",    "modes",    "muts",     "id",       "variant" }
155     },
156     {
157         TELEPORT,       1,          50,     12,     EU_AUTO,    10,         -1,         -1,     9,      -1,
158             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX)|(1<<TELEPORT),
159             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
160             (1<<ENT_PLAYER)|(1<<ENT_AI)|(1<<ENT_PROJ),
161             false,  false,  false,      false,      false,
162                 "teleport",     { "yaw",    "pitch",    "push",     "radius",   "colour",   "type",     "palette",  "palindex", "flags",    "variant" }
163     },
164     {
165         ACTOR,          1,          59,     0,      EU_NONE,    11,         3,          5,      10,     -1,
166             0, 0, 0,
167             false,  true,   false,      true,       false,
168                 "actor",        { "type",   "yaw",      "pitch",    "modes",    "muts",     "id",       "weap",     "health",   "speed",    "scale",    "variant" }
169     },
170     {
171         TRIGGER,        1,          58,     16,     EU_AUTO,    8,          6,          -1,     7,      -1,
172             (1<<MAPMODEL)|(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
173             (1<<MAPMODEL)|(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
174             (1<<ENT_PLAYER)|(1<<ENT_AI),
175             false,  true,   true,       false,      true,
176                 "trigger",      { "id",     "type",     "action",   "radius",   "state",    "modes",    "muts",     "variant" }
177     },
178     {
179         PUSHER,         1,          58,     12,     EU_AUTO,    11,         -1,         -1,     9,      -1,
180             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
181             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
182             (1<<ENT_PLAYER)|(1<<ENT_AI)|(1<<ENT_PROJ),
183             false,  false,  false,      false,      false,
184                 "pusher",       { "yaw",    "pitch",    "force",    "maxrad",   "minrad",   "type",     "modes",    "muts",     "id",       "variant",  "sdelay" }
185     },
186     {
187         AFFINITY,       1,          48,     48,     EU_NONE,    7,          3,          5,      6,      -1,
188             0, 0, 0,
189             false,  false,  false,      false,      false,
190                 "affinity",     { "team",   "yaw",      "pitch",    "modes",    "muts",     "id",       "variant" }
191     },
192     {
193         CHECKPOINT,     1,          48,     16,     EU_AUTO,    8,          3,          5,      7,      -1,
194             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
195             (1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
196             (1<<ENT_PLAYER)|(1<<ENT_AI),
197             false,  true,   false,      true,       false,
198                 "checkpoint",   { "radius", "yaw",      "pitch",    "modes",    "muts",     "id",       "type",     "variant" }
199     },
200     {
201         ROUTE,          1,         224,      16,    EU_NONE,    6,          -1,         -1,     -1,     -1,
202             (1<<ROUTE), 0, 0,
203             false,   false,  false,      false,      false,
204                 "route",         { "num",   "yaw",      "pitch",    "move",     "strafe",   "action" }
205     },
206     {
207         UNUSEDENT,      -1,          0,      0,     EU_NONE,    0,          -1,         -1,     -1,     -1,
208             0, 0, 0,
209             true,   false,  false,      false,      false,
210                 "unused",         { "" }
211     }
212 };
213 #else
214 extern const enttypes enttype[];
215 namespace entities
216 {
217     extern vector<extentity *> ents;
218 }
219 #endif
220 #define isent(a) (a >= NOTUSED && a < MAXENTTYPES)
221 
222 #define MAXNAMELEN 24
223 enum { SAY_NONE = 0, SAY_ACTION = 1<<0, SAY_TEAM = 1<<1, SAY_WHISPER = 1<<2, SAY_NUM = 3 };
224 
225 enum
226 {
227     PRIV_NONE = 0, PRIV_PLAYER, PRIV_SUPPORTER, PRIV_MODERATOR, PRIV_ADMINISTRATOR, PRIV_DEVELOPER, PRIV_CREATOR, PRIV_MAX,
228     PRIV_START = PRIV_PLAYER, PRIV_ELEVATED = PRIV_MODERATOR, PRIV_LAST = PRIV_CREATOR, PRIV_TYPE = 0xFF, PRIV_LOCAL = 1<<8
229 };
230 
231 #define MM_MODE 0xF
232 #define MM_AUTOAPPROVE 0x1000
233 #define MM_FREESERV (MM_AUTOAPPROVE|MM_MODE)
234 #define MM_VETOSERV ((1<<MM_OPEN)|(1<<MM_VETO))
235 #define MM_COOPSERV (MM_AUTOAPPROVE|MM_VETOSERV|(1<<MM_LOCKED))
236 #define MM_OPENSERV (MM_AUTOAPPROVE|(1<<MM_OPEN))
237 
238 enum { MM_OPEN = 0, MM_VETO, MM_LOCKED, MM_PRIVATE, MM_PASSWORD };
239 enum { SINFO_NONE = 0, SINFO_STATUS, SINFO_NAME, SINFO_PORT, SINFO_QPORT, SINFO_DESC, SINFO_MODE, SINFO_MUTS, SINFO_MAP, SINFO_TIME, SINFO_NUMPLRS, SINFO_MAXPLRS, SINFO_PING, SINFO_PRIO, SINFO_MAX };
240 enum { SSTAT_OPEN = 0, SSTAT_LOCKED, SSTAT_PRIVATE, SSTAT_FULL, SSTAT_UNKNOWN, SSTAT_MAX };
241 
242 enum
243 {
244     AC_PRIMARY = 0, AC_SECONDARY, AC_RELOAD, AC_USE, AC_JUMP, AC_WALK, AC_CROUCH, AC_SPECIAL, AC_DROP, AC_AFFINITY, AC_MAX,
245     AC_ALL = (1<<AC_PRIMARY)|(1<<AC_SECONDARY)|(1<<AC_RELOAD)|(1<<AC_USE)|(1<<AC_JUMP)|(1<<AC_WALK)|(1<<AC_CROUCH)|(1<<AC_SPECIAL)|(1<<AC_DROP)|(1<<AC_AFFINITY)
246 };
247 enum { IM_METER = 0, IM_TYPE, IM_REGEN, IM_COUNT, IM_COLLECT, IM_SLIP, IM_MAX };
248 enum { IM_T_JUMP = 0, IM_T_BOOST, IM_T_POUND, IM_T_SLIDE, IM_T_MELEE, IM_T_KICK, IM_T_GRAB, IM_T_PARKOUR, IM_T_AFTER, IM_T_PUSHER, IM_T_MAX, IM_T_TOUCH = IM_T_MELEE };
249 enum
250 {
251     SPHY_JUMP = 0, SPHY_BOOST, SPHY_POUND, SPHY_SLIDE, SPHY_MELEE, SPHY_KICK, SPHY_GRAB, SPHY_PARKOUR, SPHY_AFTER, SPHY_MATERIAL,
252     SPHY_SERVER, SPHY_EXTINGUISH = SPHY_SERVER, SPHY_BUFF,
253     SPHY_MAX
254 };
255 
256 #define CROUCHLOW 0.7f
257 #define CROUCHHIGH 0.85f
258 #define GUARDRADIUS 3.f
259 #define PHYSMILLIS 250
260 #define DEATHMILLIS 300
261 
262 enum
263 {
264     ANIM_PAIN = ANIM_GAMESPECIFIC,
265     ANIM_JUMP_FORWARD, ANIM_JUMP_BACKWARD, ANIM_JUMP_LEFT, ANIM_JUMP_RIGHT, ANIM_JUMP,
266     ANIM_RUN_FORWARD, ANIM_RUN_BACKWARD, ANIM_RUN_LEFT, ANIM_RUN_RIGHT,
267     ANIM_BOOST_FORWARD, ANIM_BOOST_BACKWARD, ANIM_BOOST_LEFT, ANIM_BOOST_RIGHT, ANIM_BOOST_UP,
268     ANIM_PARKOUR_LEFT, ANIM_PARKOUR_RIGHT, ANIM_PARKOUR_UP, ANIM_PARKOUR_JUMP, ANIM_POWERSLIDE, ANIM_FLYKICK,
269     ANIM_SINK, ANIM_EDIT, ANIM_WIN, ANIM_LOSE,
270     ANIM_CROUCH, ANIM_CRAWL_FORWARD, ANIM_CRAWL_BACKWARD, ANIM_CRAWL_LEFT, ANIM_CRAWL_RIGHT,
271     ANIM_CROUCH_JUMP_FORWARD, ANIM_CROUCH_JUMP_BACKWARD, ANIM_CROUCH_JUMP_LEFT, ANIM_CROUCH_JUMP_RIGHT, ANIM_CROUCH_JUMP,
272     ANIM_CLAW, ANIM_CLAW_PRIMARY, ANIM_CLAW_SECONDARY, ANIM_CLAW_RELOAD, ANIM_CLAW_POWER, ANIM_CLAW_ZOOM,
273     ANIM_PISTOL, ANIM_PISTOL_PRIMARY, ANIM_PISTOL_SECONDARY, ANIM_PISTOL_RELOAD, ANIM_PISTOL_POWER, ANIM_PISTOL_ZOOM,
274     ANIM_SWORD, ANIM_SWORD_PRIMARY, ANIM_SWORD_SECONDARY, ANIM_SWORD_POWER, ANIM_SWORD_ZOOM,
275     ANIM_SHOTGUN, ANIM_SHOTGUN_PRIMARY, ANIM_SHOTGUN_SECONDARY, ANIM_SHOTGUN_RELOAD, ANIM_SHOTGUN_POWER, ANIM_SHOTGUN_ZOOM,
276     ANIM_SMG, ANIM_SMG_PRIMARY, ANIM_SMG_SECONDARY, ANIM_SMG_RELOAD, ANIM_SMG_POWER, ANIM_SMG_ZOOM,
277     ANIM_FLAMER, ANIM_FLAMER_PRIMARY, ANIM_FLAMER_SECONDARY, ANIM_FLAMER_RELOAD, ANIM_FLAMER_POWER, ANIM_FLAMER_ZOOM,
278     ANIM_PLASMA, ANIM_PLASMA_PRIMARY, ANIM_PLASMA_SECONDARY, ANIM_PLASMA_RELOAD, ANIM_PLASMA_POWER, ANIM_PLASMA_ZOOM,
279     ANIM_ZAPPER, ANIM_ZAPPER_PRIMARY, ANIM_ZAPPER_SECONDARY, ANIM_ZAPPER_RELOAD, ANIM_ZAPPER_POWER, ANIM_ZAPPER_ZOOM,
280     ANIM_RIFLE, ANIM_RIFLE_PRIMARY, ANIM_RIFLE_SECONDARY, ANIM_RIFLE_RELOAD, ANIM_RIFLE_POWER, ANIM_RIFLE_ZOOM,
281     ANIM_GRENADE, ANIM_GRENADE_PRIMARY, ANIM_GRENADE_SECONDARY, ANIM_GRENADE_RELOAD, ANIM_GRENADE_POWER, ANIM_GRENADE_ZOOM,
282     ANIM_MINE, ANIM_MINE_PRIMARY, ANIM_MINE_SECONDARY, ANIM_MINE_RELOAD, ANIM_MINE_POWER, ANIM_MINE_ZOOM,
283     ANIM_ROCKET, ANIM_ROCKET_PRIMARY, ANIM_ROCKET_SECONDARY, ANIM_ROCKET_RELOAD, ANIM_ROCKET_POWER, ANIM_ROCKET_ZOOM,
284     ANIM_SWITCH, ANIM_USE,
285     ANIM_MAX
286 };
287 
288 enum { PULSE_FIRE = 0, PULSE_BURN, PULSE_DISCO, PULSE_SHOCK, PULSE_BLEED, PULSE_BUFF, PULSE_WARN, PULSE_MAX, PULSE_LAST = PULSE_MAX-1 };
289 #define PULSECOLOURS 8
290 #define PULSE(x) (PULSE_##x)
291 #define INVPULSE(x) (-1-(x))
292 #define PC(x) (INVPULSE(PULSE(x)))
293 #ifdef CPP_GAME_SERVER
294 extern const int pulsecols[PULSE_MAX][PULSECOLOURS] = {
295     { 0xFF5808, 0x981808, 0x782808, 0x481808, 0x983818, 0x681808, 0xC81808, 0x381808 },
296     { 0xFFC848, 0xF86838, 0xA85828, 0xA84838, 0xF8A858, 0xC84828, 0xF86848, 0xA89858 },
297     { 0xFF8888, 0xFFAA88, 0xFFFF88, 0x88FF88, 0x88FFFF, 0x8888FF, 0xFF88FF, 0xFFFFFF },
298     { 0xAA88FF, 0xAA88FF, 0xAAAAFF, 0x44AAFF, 0x88AAFF, 0x4444FF, 0xAA44FF, 0xFFFFFF },
299     { 0xFF0000, 0xFF2222, 0xFF0022, 0xFF2200, 0x880000, 0x882222, 0x880022, 0x882200 },
300     { 0xFFFFFF, 0xAAFFAA, 0x66FF66, 0x00FF00, 0x66FF00, 0xAAFF00, 0xFFFF00, 0xFFFF88 },
301     { 0xFF0000, 0xFF2222, 0xFF4444, 0xFF8888, 0xFFAAAA, 0xFF8888, 0xFF4444, 0xFF2222 }
302 };
303 SVAR(IDF_READONLY, pulsenames, "fire burn disco shock bleed buff warn");
304 VAR(IDF_READONLY, pulseidxfire, 1, PULSE_FIRE, -1);
305 VAR(IDF_READONLY, pulseidxburn, 1, PULSE_BURN, -1);
306 VAR(IDF_READONLY, pulseidxdisco, 1, PULSE_DISCO, -1);
307 VAR(IDF_READONLY, pulseidxshock, 1, PULSE_SHOCK, -1);
308 VAR(IDF_READONLY, pulseidxbleed, 1, PULSE_BLEED, -1);
309 VAR(IDF_READONLY, pulseidxbuff, 1, PULSE_BUFF, -1);
310 VAR(IDF_READONLY, pulseidxwarn, 1, PULSE_WARN, -1);
311 VAR(IDF_READONLY, pulseidxmax, 1, PULSE_MAX, -1);
312 VAR(IDF_READONLY, pulseidxlast, 1, PULSE_LAST, -1);
313 #else
314 extern const int pulsecols[PULSE_MAX][PULSECOLOURS];
315 #endif
316 
317 #define RESIDUALS \
318     RESIDUAL(burn, BURN, BURN); \
319     RESIDUAL(bleed, BLEED, BLEED); \
320     RESIDUAL(shock, SHOCK, SHOCK);
321 #define RESIDUALSF \
322     RESIDUAL(burn, BURN, FIRE); \
323     RESIDUAL(bleed, BLEED, BLEED); \
324     RESIDUAL(shock, SHOCK, SHOCK);
325 
326 enum
327 {
328     FRAG_NONE = 0, FRAG_HEADSHOT = 1<<1, FRAG_OBLITERATE = 1<<2,
329     FRAG_SPREE1 = 1<<3, FRAG_SPREE2 = 1<<4, FRAG_SPREE3 = 1<<5, FRAG_SPREE4 = 1<<6,
330     FRAG_MKILL1 = 1<<7, FRAG_MKILL2 = 1<<8, FRAG_MKILL3 = 1<<9,
331     FRAG_REVENGE = 1<<10, FRAG_DOMINATE = 1<<11, FRAG_FIRSTBLOOD = 1<<12, FRAG_BREAKER = 1<<13,
332     FRAG_SPREES = 4, FRAG_SPREE = 3, FRAG_MKILL = 7,
333     FRAG_CHECK = FRAG_SPREE1|FRAG_SPREE2|FRAG_SPREE3|FRAG_SPREE4,
334     FRAG_MULTI = FRAG_MKILL1|FRAG_MKILL2|FRAG_MKILL3,
335 };
336 
337 enum
338 {
339     SENDMAP_MPZ = 0, SENDMAP_CFG, SENDMAP_PNG, SENDMAP_TXT, SENDMAP_GAME, SENDMAP_WPT = SENDMAP_GAME, SENDMAP_MAX,
340     SENDMAP_MIN = SENDMAP_PNG, SENDMAP_HAS = SENDMAP_MIN+1, SENDMAP_EDIT = SENDMAP_CFG+1, SENDMAP_ALL = SENDMAP_MAX-1
341 };
342 #ifdef CPP_GAME_SERVER
343 extern const char * const sendmaptypes[SENDMAP_MAX] = { "mpz", "cfg", "png", "txt", "wpt" };
344 #else
345 extern const char * const sendmaptypes[SENDMAP_MAX];
346 #define CLCOMMANDK(name, body, fail) \
347     ICOMMAND(IDF_NAMECOMPLETE, getclient##name, "s", (char *who), \
348     { \
349         gameent *d = game::getclient(client::parseplayer(who)); \
350         if(!d) { fail; return; } \
351         body; \
352     });
353 #define CLCOMMAND(name, body) CLCOMMANDK(name, body,)
354 #define CLCOMMANDMK(name, fmt, args, body, fail) \
355     ICOMMAND(IDF_NAMECOMPLETE, getclient##name, fmt, args, \
356     { \
357         gameent *d = game::getclient(client::parseplayer(who)); \
358         if(!d) { fail; return; } \
359         body; \
360     });
361 #define CLCOMMANDM(name, fmt, args, body) CLCOMMANDMK(name, fmt, args, body,)
362 #endif
363 
364 #include "gamemode.h"
365 #include "weapons.h"
366 
367 // network messages codes, c2s, c2c, s2c
368 enum
369 {
370     N_CONNECT = 0, N_SERVERINIT, N_WELCOME, N_CLIENTINIT, N_POS, N_SPHY, N_TEXT, N_COMMAND, N_ANNOUNCE, N_DISCONNECT,
371     N_SHOOT, N_DESTROY, N_STICKY, N_SUICIDE, N_DIED, N_POINTS, N_TOTALS, N_AVGPOS, N_DAMAGE, N_BURNRES, N_BLEEDRES, N_SHOCKRES, N_SHOTFX,
372     N_LOADOUT, N_TRYSPAWN, N_SPAWNSTATE, N_SPAWN, N_WEAPDROP, N_WEAPSELECT, N_WEAPCOOK,
373     N_MAPCHANGE, N_MAPVOTE, N_CLEARVOTE, N_CHECKPOINT, N_ITEMSPAWN, N_ITEMUSE, N_TRIGGER, N_EXECLINK,
374     N_PING, N_PONG, N_CLIENTPING, N_TICK, N_ITEMACC, N_SERVMSG, N_GETGAMEINFO, N_GAMEINFO, N_ATTRMAP, N_RESUME,
375     N_EDITMODE, N_EDITENT, N_EDITLINK, N_EDITVAR, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE,
376     N_CALCLIGHT, N_REMIP, N_EDITVSLOT, N_UNDO, N_REDO, N_CLIPBOARD, N_NEWMAP,
377     N_GETMAP, N_SENDMAP, N_FAILMAP, N_SENDMAPFILE,
378     N_MASTERMODE, N_ADDCONTROL, N_CLRCONTROL, N_CURRENTPRIV, N_SPECTATOR, N_WAITING, N_SETPRIV, N_SETTEAM, N_ADDPRIV,
379     N_SETUPAFFIN, N_INFOAFFIN, N_MOVEAFFIN,
380     N_TAKEAFFIN, N_RETURNAFFIN, N_RESETAFFIN, N_DROPAFFIN, N_SCOREAFFIN, N_INITAFFIN, N_SCORE, N_DUELEND,
381     N_LISTDEMOS, N_SENDDEMOLIST, N_GETDEMO, N_SENDDEMO, N_DEMOREADY,
382     N_DEMOPLAYBACK, N_RECORDDEMO, N_STOPDEMO, N_CLEARDEMOS,
383     N_CLIENT, N_RELOAD, N_REGEN, N_INITAI, N_MAPCRC,
384     N_SETPLAYERINFO, N_SWITCHTEAM, N_AUTHTRY, N_AUTHCHAL, N_AUTHANS, N_QUEUEPOS,
385     N_STEAMCHAL, N_STEAMANS, N_STEAMFAIL,
386     NUMMSG
387 };
388 
389 enum
390 {
391     SS_F_NONE = 0,
392     SS_F_STEAMAUTH = 1<<0,
393     SS_F_ALL = SS_F_STEAMAUTH
394 };
395 
396 #ifdef CPP_GAME_SERVER
msgsizelookup(int msg)397 char msgsizelookup(int msg)
398 {
399     static const int msgsizes[] =               // size inclusive message token, 0 for variable or not-checked sizes
400     {
401         N_CONNECT, 0, N_SERVERINIT, 5, N_WELCOME, 2, N_CLIENTINIT, 0, N_POS, 0, N_SPHY, 0, N_TEXT, 0, N_COMMAND, 0, N_ANNOUNCE, 0, N_DISCONNECT, 3,
402         N_SHOOT, 0, N_DESTROY, 0, N_STICKY, 0, N_SUICIDE, 4, N_DIED, 0, N_POINTS, 5, N_TOTALS, 0, N_AVGPOS, 0, N_DAMAGE, 14, N_SHOTFX, 0,
403         N_LOADOUT, 0, N_TRYSPAWN, 2, N_SPAWNSTATE, 0, N_SPAWN, 0, N_WEAPDROP, 0, N_WEAPSELECT, 0, N_WEAPCOOK, 0,
404         N_MAPCHANGE, 0, N_MAPVOTE, 0, N_CLEARVOTE, 0, N_CHECKPOINT, 0, N_ITEMSPAWN, 3, N_ITEMUSE, 0, N_TRIGGER, 0, N_EXECLINK, 3,
405         N_PING, 2, N_PONG, 2, N_CLIENTPING, 2, N_TICK, 3, N_ITEMACC, 0, N_SERVMSG, 0, N_GETGAMEINFO, 0, N_GAMEINFO, 0, N_ATTRMAP, 0, N_RESUME, 0,
406         N_EDITMODE, 2, N_EDITENT, 0, N_EDITLINK, 4, N_EDITVAR, 0, N_EDITF, 16, N_EDITT, 16, N_EDITM, 17, N_FLIP, 14,
407         N_COPY, 14, N_PASTE, 14, N_ROTATE, 15, N_REPLACE, 17, N_DELCUBE, 14,
408         N_CALCLIGHT, 1, N_REMIP, 1, N_EDITVSLOT, 16, N_UNDO, 0, N_REDO, 0, N_NEWMAP, 0,
409         N_GETMAP, 0, N_SENDMAP, 0, N_FAILMAP, 0, N_SENDMAPFILE, 0,
410         N_MASTERMODE, 0, N_ADDCONTROL, 0, N_CLRCONTROL, 2, N_CURRENTPRIV, 3, N_SPECTATOR, 3, N_WAITING, 2, N_SETPRIV, 0, N_SETTEAM, 0, N_ADDPRIV, 0,
411         N_SETUPAFFIN, 0, N_INFOAFFIN, 0, N_MOVEAFFIN, 0,
412         N_DROPAFFIN, 0, N_SCOREAFFIN, 0, N_RETURNAFFIN, 0, N_TAKEAFFIN, 0, N_RESETAFFIN, 0, N_INITAFFIN, 0, N_SCORE, 0, N_DUELEND, 0,
413         N_LISTDEMOS, 1, N_SENDDEMOLIST, 0, N_GETDEMO, 3, N_SENDDEMO, 0, N_DEMOREADY, 0,
414         N_DEMOPLAYBACK, 3, N_RECORDDEMO, 2, N_STOPDEMO, 1, N_CLEARDEMOS, 2,
415         N_CLIENT, 0, N_RELOAD, 0, N_REGEN, 0, N_INITAI, 0, N_MAPCRC, 0,
416         N_SETPLAYERINFO, 0, N_SWITCHTEAM, 0, N_AUTHTRY, 0, N_AUTHCHAL, 0, N_AUTHANS, 0, N_QUEUEPOS, 0,
417         N_STEAMCHAL, 0, N_STEAMANS, 0, N_STEAMFAIL, 0,
418         -1
419     };
420     static int sizetable[NUMMSG] = { -1 };
421     if(sizetable[0] < 0)
422     {
423         memset(sizetable, -1, sizeof(sizetable));
424         for(const int *p = msgsizes; *p >= 0; p += 2) sizetable[p[0]] = p[1];
425     }
426     return msg >= 0 && msg < NUMMSG ? sizetable[msg] : -1;
427 }
428 #else
429 extern char msgsizelookup(int msg);
430 #endif
431 
432 struct demoheader
433 {
434     char magic[16];
435     int gamever, gamemode, mutators, starttime;
436     string mapname;
437 };
438 #include "player.h"
439 #include "vars.h"
440 #ifndef CPP_GAME_SERVER
441 #include "ai.h"
442 #endif
443 
444 template<class T>
adjustscaled(T & n,int s)445 static inline void adjustscaled(T &n, int s)
446 {
447     T o = n;
448     n = (T)(n/(1.f+sqrtf((float)curtime)/float(s)));
449     if((o > 0 && n < 0) || (o < 0 && n > 0)) n = (T)0;
450 }
451 
452 static inline void modecheck(int &mode, int &muts, int trying = 0)
453 {
454     if(!m_game(mode)) mode = G_DEATHMATCH;
455     if(gametype[mode].implied) muts |= gametype[mode].implied;
456     int retries = G_M_NUM*G_M_NUM;
loop(r,retries)457     loop(r, retries)
458     {
459         if(!muts) break; // nothing to do then
460         bool changed = false;
461         int mutsidx = gametype[mode].mutators[0];
462         loopj(G_M_GSN) if(gametype[mode].mutators[j+1])
463         {
464             int m = 1<<(j+G_M_GSP);
465             if(!(muts&m)) continue;
466             if(trying&m) loopi(G_M_GSN) if(i != j && gametype[mode].mutators[i+1])
467             {
468                 int n = 1<<(i+G_M_GSP);
469                 if(!(muts&n)) continue;
470                 if(!(gametype[mode].mutators[i+1]&m))
471                 {
472                     muts &= ~n;
473                     trying &= ~n;
474                     changed = true;
475                     break;
476                 }
477             }
478             if(changed) break;
479             if(gametype[mode].flags&GF(GSP))
480             {
481                 //trying |= m; // game specific mutator exclusively provides allowed bits
482                 mutsidx = gametype[mode].mutators[j+1];
483             }
484         }
485         if(changed) continue;
486         loop(s, G_M_NUM)
487         {
488             if(!(mutsidx&(1<<mutstype[s].type)) && (muts&(1<<mutstype[s].type)))
489             {
490                 muts &= ~(1<<mutstype[s].type);
491                 trying &= ~(1<<mutstype[s].type);
492                 changed = true;
493                 break;
494             }
495             if(muts&(1<<mutstype[s].type)) loopj(G_M_NUM)
496             {
497                 if(!(mutstype[s].mutators&(1<<mutstype[j].type)) && (muts&(1<<mutstype[j].type)))
498                 {
499                     if(trying && (trying&(1<<mutstype[j].type)) && !(gametype[mode].implied&(1<<mutstype[s].type)))
500                     {
501                         muts &= ~(1<<mutstype[s].type);
502                         trying &= ~(1<<mutstype[s].type);
503                     }
504                     else
505                     {
506                         muts &= ~(1<<mutstype[j].type);
507                         trying &= ~(1<<mutstype[j].type);
508                     }
509                     changed = true;
510                     break;
511                 }
512                 int implying = gametype[mode].implied|mutstype[s].implied;
513                 if(implying && (implying&(1<<mutstype[j].type)) && !(muts&(1<<mutstype[j].type)))
514                 {
515                     muts |= (1<<mutstype[j].type);
516                     changed = true;
517                     break;
518                 }
519             }
520             if(changed) break;
521         }
522         if(!changed) break;
523     }
524 }
525 
mastermodename(int type)526 static inline const char *mastermodename(int type)
527 {
528     switch(type)
529     {
530         case MM_OPEN: return "open";
531         case MM_VETO: return "veto";
532         case MM_LOCKED: return "locked";
533         case MM_PRIVATE: return "private";
534         case MM_PASSWORD: return "password";
535         default: return "unknown";
536     }
537 }
538 
539 struct verinfo
540 {
541     int type, flag, version, major, minor, patch, game, build, platform, arch, gpuglver, gpuglslver, crc;
542     char *branch, *revision, *gpuvendor, *gpurenderer, *gpuversion;
543 
verinfoverinfo544     verinfo() : branch(NULL), revision(NULL), gpuvendor(NULL), gpurenderer(NULL), gpuversion(NULL) { reset(); }
~verinfoverinfo545     ~verinfo() { reset(); }
546 
resetverinfo547     void reset()
548     {
549         if(branch) delete[] branch;
550         if(revision) delete[] revision;
551         if(gpuvendor) delete[] gpuvendor;
552         if(gpurenderer) delete[] gpurenderer;
553         if(gpuversion) delete[] gpuversion;
554         branch = revision = gpuvendor = gpurenderer = gpuversion = NULL;
555         type = flag = version = major = minor = patch = game = arch = gpuglver = gpuglslver = crc = build = 0;
556         platform = -1;
557     }
558 
559     template <class T>
getverinfo560     void get(T &p)
561     {
562         string text;
563         major = getint(p);
564         minor = getint(p);
565         patch = getint(p);
566         game = getint(p);
567         build = getint(p);
568         platform = getint(p);
569         arch = getint(p);
570         gpuglver = getint(p);
571         gpuglslver = getint(p);
572         crc = getint(p);
573         if(branch) delete[] branch;
574         getstring(text, p); branch = newstring(text, MAXBRANCHLEN);
575         if(revision) delete[] revision;
576         getstring(text, p); revision = newstring(text, MAXREVISIONLEN);
577         if(gpuvendor) delete[] gpuvendor;
578         getstring(text, p); gpuvendor = newstring(text);
579         if(gpurenderer) delete[] gpurenderer;
580         getstring(text, p); gpurenderer = newstring(text);
581         if(gpuversion) delete[] gpuversion;
582         getstring(text, p); gpuversion = newstring(text);
583     }
584 
585     template <class T>
putverinfo586     void put(T &p)
587     {
588         putint(p, major);
589         putint(p, minor);
590         putint(p, patch);
591         putint(p, game);
592         putint(p, build);
593         putint(p, platform);
594         putint(p, arch);
595         putint(p, gpuglver);
596         putint(p, gpuglslver);
597         putint(p, crc);
598         sendstring(branch ? branch : "", p);
599         sendstring(revision ? revision : "", p);
600         sendstring(gpuvendor ? gpuvendor : "", p);
601         sendstring(gpurenderer ? gpurenderer : "", p);
602         sendstring(gpuversion ? gpuversion : "", p);
603     }
604 
grabverinfo605     void grab(verinfo &v)
606     {
607         major = v.major;
608         minor = v.minor;
609         patch = v.patch;
610         game = v.game;
611         build = v.build;
612         platform = v.platform;
613         arch = v.arch;
614         gpuglver = v.gpuglver;
615         gpuglslver = v.gpuglslver;
616         crc = v.crc;
617         if(branch) delete[] branch;
618         branch = newstring(v.branch ? v.branch : "", MAXBRANCHLEN);
619         if(revision) delete[] revision;
620         revision = newstring(v.revision ? v.revision : "", MAXREVISIONLEN);
621         if(gpuvendor) delete[] gpuvendor;
622         gpuvendor = newstring(v.gpuvendor ? v.gpuvendor : "");
623         if(gpurenderer) delete[] gpurenderer;
624         gpurenderer = newstring(v.gpurenderer ? v.gpurenderer : "");
625         if(gpuversion) delete[] gpuversion;
626         gpuversion = newstring(v.gpuversion ? v.gpuversion : "");
627     }
628 };
629 
630 // inherited by gameent and server clients
631 struct clientstate
632 {
633     int health, colour, model, pattern, checkpointspawn;
634     int weapselect, weapammo[W_MAX][W_A_MAX], weapload[W_MAX][W_A_MAX], weapent[W_MAX], weapshot[W_MAX], weapstate[W_MAX], weapwait[W_MAX], weaptime[W_MAX], prevstate[W_MAX], prevtime[W_MAX];
635     int lastdeath, lastspawn, lastpain, lastregen, lastregenamt, lastbuff, lastshoot, lastcook, lastaffinity, lastres[W_R_MAX], lastrestime[W_R_MAX];
636     int burntime, burndelay, burndamage, bleedtime, bleeddelay, bleeddamage, shocktime, shockdelay, shockdamage, shockstun, shockstuntime;
637     float shockstunscale, shockstunfall;
638     int actortype, spawnpoint, ownernum, skill, points, frags, deaths, totalpoints, totalfrags, totaldeaths, spree, lasttimeplayed, timeplayed, cpmillis, cptime, queuepos;
639     float totalavgpos;
640     bool quarantine;
641     string vanity;
642     vector<int> loadweap, lastweap, randweap;
643     verinfo version;
644 
clientstateclientstate645     clientstate() : colour(0), model(0), pattern(0), checkpointspawn(1), weapselect(W_CLAW), lastdeath(0), lastspawn(0), lastpain(0), lastregen(0), lastregenamt(0), lastbuff(0), lastshoot(0), lastcook(0), lastaffinity(0),
646         actortype(A_PLAYER), spawnpoint(-1), ownernum(-1), skill(0), points(0), frags(0), deaths(0), totalpoints(0), totalfrags(0), totaldeaths(0), spree(0), lasttimeplayed(0), timeplayed(0),
647         cpmillis(0), cptime(0), queuepos(-1), totalavgpos(0), quarantine(false)
648     {
649         vanity[0] = '\0';
650         loadweap.shrink(0);
651         lastweap.shrink(0);
652         randweap.shrink(0);
653         resetresidual();
654     }
~clientstateclientstate655     ~clientstate() {}
656 
657     int gethealth(int gamemode, int mutators, bool full = false)
658     {
659         if(m_insta(gamemode, mutators)) return 1;
660         int hp = AA(actortype, health), sweap = m_weapon(actortype, gamemode, mutators);
661         loopi(W_MAX) if(hasweap(i, sweap))
662         {
663             hp += W(i, modhealth)+(getammo(i, 0, true)*W(i, modhealthammo));
664             if(i == weapselect) hp += W(i, modhealthequip);
665         }
666         hp = hp*(m_hard(gamemode, mutators) ? G(healthscalehard) : G(healthscale));
667         if(full) hp = hp*(m_vampire(gamemode, mutators) ? G(maxhealthvampire) : G(maxhealth));
668         return max(hp, 1);
669     }
670 
setvanityclientstate671     bool setvanity(const char *v)
672     {
673         bool changed = strcmp(v, vanity);
674         if(changed) copystring(vanity, v);
675         return changed;
676     }
677 
678     int getammo(int weap, int millis = 0, bool store = false)
679     {
680         if(!isweap(weap) || weapammo[weap][W_A_CLIP] < 0) return -1;
681         int a = weapammo[weap][W_A_CLIP] > 0 ? weapammo[weap][W_A_CLIP] : 0;
682         if(millis && weapstate[weap] == W_S_RELOAD && millis-weaptime[weap] < weapwait[weap] && weapload[weap][W_A_CLIP] > 0)
683             a -= weapload[weap][W_A_CLIP];
684         if(store) a += weapammo[weap][W_A_STORE];
685         return a;
686     }
687 
688     bool hasweap(int weap, int sweap, int level = 0, int exclude = -1)
689     {
690         if(isweap(weap) && weap != exclude)
691         {
692             int ammo = getammo(weap, 0, true);
693             if(ammo >= 0) switch(level)
694             {
695                 case 0: default: return true; break; // has weap at all
696                 case 1: if(w_carry(weap, sweap)) return true; break; // only carriable
697                 case 2: if(ammo > 0) return true; break; // only with actual ammo
698                 case 3: if(ammo > 0 || canreload(weap, sweap)) return true; break; // only reloadable or has ammo
699                 case 4: if(ammo >= (canreload(weap, sweap) ? 0 : W(weap, ammoclip))) return true; break; // only reloadable or those with < clipsize
700                 case 5: case 6: // special case to determine drop in classic games
701                     if(weap == sweap || (level == 6 && weap < W_ITEM && weap >= W_OFFSET) || ammo > 0 || canreload(weap, sweap)) return true;
702                     break;
703             }
704         }
705         return false;
706     }
707 
holdweapclientstate708     bool holdweap(int weap, int sweap, int millis)
709     {
710         return weap == weapselect || millis-weaptime[weap] < weapwait[weap] || hasweap(weap, sweap);
711     }
712 
holdweapcountclientstate713     int holdweapcount(int sweap, int millis)
714     {
715         int n = 0;
716         loopi(W_ALL) if(holdweap(i, sweap, millis)) n++;
717         return n;
718     }
719 
addlastweapclientstate720     void addlastweap(int weap)
721     {
722         loopvrev(lastweap) if(lastweap[i] == weap) lastweap.remove(i);
723         lastweap.add(weap);
724     }
725 
726     int getlastweap(int sweap, int exclude = -1)
727     {
loopvrevclientstate728         loopvrev(lastweap)
729         {
730             if(lastweap[i] == exclude) continue;
731             if(hasweap(lastweap[i], sweap)) return lastweap[i];
732         }
733         return -1;
734     }
735 
736     int bestweapcheck(int sweap, int level, int num = W_ALL, int exclude = -1)
737     {
738         loopvrev(lastweap) if(lastweap[i] < num && hasweap(lastweap[i], sweap, level, exclude)) return lastweap[i];
739         loopirev(num) if(hasweap(i, sweap, level, exclude)) return i;
740         return -1;
741     }
742 
743     int bestweap(int sweap, bool ammo, bool pick = false, int exclude = -1)
744     {
745         int n = bestweapcheck(sweap, 3, W_ALL, exclude);
746         if(isweap(n)) return n;
747         if(ammo)
748         {
749             n = bestweapcheck(sweap, 2, W_ALL, exclude);
750             if(isweap(n)) return n;
751         }
752         if(pick)
753         {
754             n = bestweapcheck(sweap, 0, W_ITEM, exclude);
755             if(isweap(n)) return n;
756         }
757         return weapselect;
758     }
759 
760     int carry(int sweap, int level = 1, int exclude = -1)
761     {
762         int carry = 0;
763         loopi(W_ALL) if(hasweap(i, sweap, level, exclude)) carry++;
764         return carry;
765     }
766 
dropclientstate767     int drop(int sweap)
768     {
769         if(hasweap(weapselect, sweap, 1)) return weapselect;
770         int w = getlastweap(sweap, weapselect);
771         if(hasweap(w, sweap, 1)) return w;
772         loopi(W_ALL) if(hasweap(i, sweap, 1)) return i;
773         return -1;
774     }
775 
776     void weapreset(bool full = false)
777     {
loopiclientstate778         loopi(W_MAX)
779         {
780             weapstate[i] = prevstate[i] = W_S_IDLE;
781             weapwait[i] = weaptime[i] = weapshot[i] = prevtime[0] = 0;
782             loopj(W_A_MAX) weapload[i][j] = 0;
783             if(full)
784             {
785                 weapammo[i][W_A_CLIP] = weapent[i] = -1;
786                 weapammo[i][W_A_STORE] = 0;
787             }
788         }
789         if(full) lastweap.shrink(0);
790     }
791 
792     void setweapstate(int weap, int state, int delay, int millis, int offtime = 0, bool blank = false)
793     {
794         if(blank || (state >= W_S_RELOAD && state <= W_S_USE))
795         {
796             prevstate[weap] = W_S_IDLE;
797             prevtime[weap] = millis;
798         }
799         else if(weapstate[weap] == W_S_ZOOM || weapstate[weap] == W_S_POWER)
800         {
801             prevstate[weap] = weapstate[weap];
802             prevtime[weap] = weaptime[weap];
803         }
804         weapstate[weap] = state;
805         weapwait[weap] = delay;
806         weaptime[weap] = millis-offtime;
807     }
808 
809     bool weapswitch(int weap, int millis, int delay = 0, int state = W_S_SWITCH)
810     {
811         if(isweap(weap) && weap < W_ALL)
812         {
813             if(isweap(weapselect))
814             {
815                 addlastweap(weapselect);
816                 setweapstate(weapselect, W_S_SWITCH, delay, millis);
817             }
818             weapselect = weap;
819             setweapstate(weap, state, delay, millis);
820             return true;
821         }
822         return false;
823     }
824 
825     bool weapwaited(int weap, int millis, int skip = 0)
826     {
827         if(weap != weapselect) skip &= ~(1<<W_S_RELOAD);
828         if(!weapwait[weap] || weapstate[weap] == W_S_IDLE || (skip && skip&(1<<weapstate[weap]))) return true;
829         int wait = weapwait[weap];
830         if(W_S_INTERRUPT&(1<<weapstate[weap]))
831         {
832             if(W(weap, cookinterrupt)) return true;
833             wait += 50;
834         }
835         return millis-weaptime[weap] >= wait;
836     }
837 
838     bool candrop(int weap, int sweap, int millis, bool classic, int skip = 0)
839     {
840         if(weap < W_ALL && weap != sweap && (classic ? weap >= W_OFFSET && W2(weap, ammosub, false) && W2(weap, ammosub, true) : weap >= W_ITEM)
841             && hasweap(weap, sweap) && weapwaited(weap, millis, skip) && weapwaited(weapselect, millis, skip))
842                 return true;
843         return false;
844     }
845 
846     bool canswitch(int weap, int sweap, int millis, int skip = 0)
847     {
848         if(!isweap(weap) || weap >= W_ALL) return false;
849         if(weap != weapselect && weapwaited(weapselect, millis, skip) && hasweap(weap, sweap) && weapwaited(weap, millis, skip))
850             return true;
851         return false;
852     }
853 
854     bool canshoot(int weap, int flags, int sweap, int millis, int skip = 0)
855     {
856         if(!(AA(actortype, abilities)&(1<<(WS(flags) ? A_A_SECONDARY : A_A_PRIMARY)))) return false;
857         if(weap == weapselect || weap == W_MELEE)
858             if(hasweap(weap, sweap) && getammo(weap, millis) >= (W2(weap, cooktime, WS(flags)) ? 1 : W2(weap, ammosub, WS(flags))) && weapwaited(weap, millis, W_S_INTERRUPT|skip))
859                 return true;
860         return false;
861     }
862 
863     bool canreload(int weap, int sweap, bool check = false, int millis = 0, int skip = 0)
864     {
865         if(actortype >= A_ENEMY ||
866             ((W(weap, ammostore) < 0 || weapammo[weap][W_A_STORE] > 0)
867                 && (!check || (weap == weapselect && hasweap(weap, sweap) && weapammo[weap][W_A_CLIP] < W(weap, ammoclip) && weapstate[weap] != W_S_ZOOM && weapwaited(weap, millis, skip)))))
868             return true;
869         return false;
870     }
871 
872     bool canuseweap(int gamemode, int mutators, int attr, int sweap, int millis, int skip = 0, bool full = true)
873     {
874         if(!m_classic(gamemode, mutators) && attr < W_ITEM && !hasweap(attr, sweap)) return false;
875         if(full)
876         {
877             int ammo = getammo(attr, 0, true), total = W(attr, ammoclip);
878             if(W(attr, ammostore) > 0) total += W(attr, ammostore);
879             if(ammo >= total) return false;
880         }
881         return true;
882     }
883 
884     bool canuse(int gamemode, int mutators, int type, int attr, attrvector &attrs, int sweap, int millis, int skip = 0, bool full = true)
885     {
886         switch(enttype[type].usetype)
887         {
888             case EU_AUTO: case EU_ACT: return true; break;
889             case EU_ITEM:
890             { // can't use when reloading or firing
891                 if(type != WEAPON || !isweap(attr) || !m_maxcarry(actortype, gamemode, mutators)) return false;
892                 if(!weapwaited(weapselect, millis, skip)) return false;
893                 return canuseweap(gamemode, mutators, attr, sweap, millis, skip, full);
894             }
895             default: break;
896         }
897         return false;
898     }
899 
useitemclientstate900     void useitem(int id, int type, int attr, int ammoamt, int sweap, int millis, int delay)
901     {
902         if(type != WEAPON || !isweap(attr)) return;
903         int prevclip = max(weapammo[attr][W_A_CLIP], 0), prevstore = max(weapammo[attr][W_A_STORE], 0);
904         weapswitch(attr, millis, delay, W_S_USE);
905         weapammo[attr][W_A_CLIP] = W(attr, ammostore) <= 0 || !hasweap(attr, sweap) ? clamp(prevclip+ammoamt, 0, W(attr, ammoclip)) : prevclip;
906         int diffclip = max(weapammo[attr][W_A_CLIP], 0)-prevclip, store = ammoamt-diffclip;
907         weapammo[attr][W_A_STORE] = W(attr, ammostore) > 0 ? clamp(weapammo[attr][W_A_STORE]+store, 0, W(attr, ammostore)) : 0;
908         weapload[attr][W_A_CLIP] = diffclip;
909         weapload[attr][W_A_STORE] = max(weapammo[attr][W_A_STORE], 0)-prevstore;
910         weapent[attr] = id;
911     }
912 
913     void resetresidual(int n = -1)
914     {
915         if(n < 0 || n == W_R_BURN) lastres[W_R_BURN] = lastrestime[W_R_BURN] = burntime = burndelay = burndamage = 0;
916         if(n < 0 || n == W_R_BLEED) lastres[W_R_BLEED] = lastrestime[W_R_BLEED] = bleedtime = bleeddelay = bleeddamage = 0;
917         if(n < 0 || n == W_R_SHOCK)
918         {
919             lastres[W_R_SHOCK] = lastrestime[W_R_SHOCK] = shocktime = shockdelay = shockdamage = shockstun = shockstuntime = 0;
920             shockstunscale = shockstunfall = 0.f;
921         }
922     }
923 
clearstateclientstate924     void clearstate()
925     {
926         spree = lastdeath = lastpain = lastregen = lastregenamt = lastbuff = lastshoot = lastcook = lastaffinity = 0;
927         queuepos = -1;
928         resetresidual();
929     }
930 
931     void mapchange(bool change = false)
932     {
933         points = frags = deaths = cpmillis = cptime = spree = 0;
934     }
935 
respawnclientstate936     void respawn(int millis)
937     {
938         lastspawn = millis;
939         clearstate();
940         weapreset(true);
941     }
942 
updatetimeplayedclientstate943     int updatetimeplayed()
944     {
945         if(lasttimeplayed)
946         {
947             int millis = totalmillis-lasttimeplayed, secs = millis/1000;
948             timeplayed += secs;
949             lasttimeplayed = totalmillis+(secs*1000)-millis;
950         }
951         else lasttimeplayed = totalmillis ? totalmillis : 1;
952         return timeplayed;
953     }
954 
955     float scoretime(bool update = true)
956     {
957         if(update) updatetimeplayed();
958         return totalpoints/float(max(timeplayed, 1));
959     }
960 
961     float kdratio(bool total = true)
962     {
963         if(total) return totalfrags >= totaldeaths ? (totalfrags/float(max(totaldeaths, 1))) : -(totaldeaths/float(max(totalfrags, 1)));
964         return frags >= deaths ? (frags/float(max(deaths, 1))) : -(deaths/float(max(frags, 1)));
965     }
966 
combinedkdratioclientstate967     float combinedkdratio()
968     {
969         return ((totalfrags / float(max(totaldeaths, 1))) + (frags / float(max(deaths, 1)))) / ((frags || deaths) ? 2.0f : 1.0f);
970     }
971 
972     float balancescore(float none = 0.0f)
973     {
974         switch(G(teambalancestyle))
975         {
976             case 1: return timeplayed;
977             case 2: return totalpoints;
978             case 3: return totalfrags;
979             case 4: return scoretime();
980             case 5: return kdratio();
981             case 6: return combinedkdratio();
982             case 7: return totalavgpos;
983             case 0: default: return none;
984         }
985     }
986 
canrandweapclientstate987     bool canrandweap(int weap)
988     {
989         int cweap = weap-W_OFFSET;
990         if(!randweap.inrange(cweap)) return true;
991         return randweap[cweap];
992     }
993 
spawnstateclientstate994     void spawnstate(int gamemode, int mutators, int sweap, int heal)
995     {
996         weapreset(true);
997         health = heal > 0 ? heal : gethealth(gamemode, mutators);
998         int s = sweap;
999         if(!isweap(s)) s = m_weapon(actortype, gamemode, mutators);
1000         if(!isweap(s) || s >= W_ALL) s = W_CLAW;
1001         if(isweap(s))
1002         {
1003             weapammo[s][W_A_CLIP] = W(s, ammospawn);
1004             weapselect = s;
1005         }
1006         if(s != W_CLAW && m_edit(gamemode) && !W(W_CLAW, disabled)) weapammo[W_CLAW][W_A_CLIP] = W(W_CLAW, ammospawn); // give SniperGoth his claw in edit mode
1007         if(s != W_MELEE && AA(actortype, abilities)&(1<<A_A_MELEE) && !W(W_MELEE, disabled)) weapammo[W_MELEE][W_A_CLIP] = W(W_MELEE, ammospawn);
1008         if(actortype < A_ENEMY)
1009         {
1010             if(m_kaboom(gamemode, mutators) && !W(W_MINE, disabled)) weapammo[W_MINE][W_A_CLIP] = W(W_MINE, ammospawn);
1011             else if(!m_race(gamemode) || m_ra_gauntlet(gamemode, mutators))
1012             {
1013                 if(s != W_GRENADE && AA(actortype, spawngrenades) >= (m_insta(gamemode, mutators) ? 2 : 1) && !W(W_GRENADE, disabled))
1014                     weapammo[W_GRENADE][W_A_CLIP] = W(W_GRENADE, ammospawn);
1015                 if(s != W_MINE && AA(actortype, spawnmines) >= (m_insta(gamemode, mutators) ? 2 : 1) && !W(W_MINE, disabled))
1016                     weapammo[W_MINE][W_A_CLIP] = W(W_MINE, ammospawn);
1017             }
1018         }
1019         if(m_maxcarry(actortype, gamemode, mutators) && m_loadout(gamemode, mutators))
1020         {
1021             vector<int> aweap;
1022             loopj(W_LOADOUT)
1023             {
1024                 if(loadweap.inrange(j) && isweap(loadweap[j]) && !hasweap(loadweap[j], sweap) && m_check(W(loadweap[j], modes), W(loadweap[j], muts), gamemode, mutators) && !W(loadweap[j], disabled))
1025                     aweap.add(loadweap[j]);
1026                 else aweap.add(0);
1027             }
1028             vector<int> rand, forcerand;
1029             for(int t = W_OFFSET; t < W_ITEM; t++)
1030                 if(!hasweap(t, sweap) && m_check(W(t, modes), W(t, muts), gamemode, mutators) && !W(t, disabled) && aweap.find(t) < 0)
1031                     (canrandweap(t) ? rand : forcerand).add(t);
1032             int count = 0;
1033             loopj(W_LOADOUT)
1034             {
1035                 if(aweap[j] <= 0) // specifically asking for random
1036                 {
1037                     vector<int> &randsrc = rand.empty() ? forcerand : rand;
1038                     if(!randsrc.empty())
1039                     {
1040                         int n = rnd(randsrc.length());
1041                         aweap[j] = randsrc.remove(n);
1042                     }
1043                     else continue;
1044                 }
1045                 if(!isweap(aweap[j])) continue;
1046                 weapammo[aweap[j]][W_A_CLIP] = W(aweap[j], ammospawn);
1047                 count++;
1048                 if(count >= m_maxcarry(actortype, gamemode, mutators)) break;
1049             }
1050             loopj(2) if(isweap(aweap[j])) { weapselect = aweap[j]; break; }
1051         }
1052         loopj(W_MAX) if(weapammo[j][W_A_CLIP] > W(j, ammoclip))
1053         {
1054             if(W(j, ammostore) > 0) weapammo[j][W_A_STORE] = clamp(weapammo[j][W_A_CLIP]-W(j, ammoclip), 0, W(j, ammostore));
1055             weapammo[j][W_A_CLIP] = W(j, ammoclip);
1056         }
1057     }
1058 
editspawnclientstate1059     void editspawn(int gamemode, int mutators)
1060     {
1061         clearstate();
1062         spawnstate(gamemode, mutators, m_weapon(actortype, gamemode, mutators), gethealth(gamemode, mutators));
1063     }
1064 
respawnwaitclientstate1065     int respawnwait(int millis, int delay)
1066     {
1067         return lastdeath ? max(0, delay-(millis-lastdeath)) : 0;
1068     }
1069 
protectclientstate1070     int protect(int millis, int delay)
1071     {
1072         if(actortype >= A_ENEMY || !lastspawn || !delay) return 0;
1073         if(G(protectbreakshoot) && lastshoot) return 0;
1074         if(G(protectbreakcook) && lastcook) return 0;
1075         if(G(protectbreakaffinity) && lastaffinity) return 0;
1076         int len = millis-lastspawn;
1077         if(len > delay) return 0;
1078         return delay-len;
1079     }
1080 
1081     #define RESIDUAL(name, type, pulse) bool name##ing(int millis, int len) { return len && lastres[W_R_##type] && millis-lastres[W_R_##type] <= len; }
1082     RESIDUALS
1083     #undef RESIDUAL
1084 };
1085 
1086 enum { PRJ_SHOT = 0, PRJ_GIBS, PRJ_DEBRIS, PRJ_EJECT, PRJ_ENT, PRJ_AFFINITY, PRJ_VANITY, PRJ_MAX };
1087 
1088 namespace server
1089 {
1090     extern void stopdemo();
1091     extern void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen = MAXSTRLEN);
1092     extern bool servcmd(int nargs, const char *cmd, const char *arg);
1093     extern const char *privname(int priv = PRIV_NONE, int actortype = A_PLAYER);
1094     extern const char *privnamex(int priv = PRIV_NONE, int actortype = A_PLAYER, bool local = false);
1095 #ifdef CPP_GAME_SERVER
1096     struct clientinfo;
1097     extern void waiting(clientinfo *ci, int drop = 0, bool doteam = true, bool exclude = false);
1098     extern void setteam(clientinfo *ci, int team, int flags = TT_RESET, bool swaps = true);
1099     extern int chooseteam(clientinfo *ci, int suggest = -1, bool wantbal = false);
1100 #endif
1101 }
1102 
1103 #if !defined(CPP_GAME_SERVER) && !defined(STANDALONE)
flashcolour(T & r,T & g,T & b,T br,T bg,T bb,float amt)1104 template<class T> inline void flashcolour(T &r, T &g, T &b, T br, T bg, T bb, float amt)
1105 {
1106     r += (br-r)*amt;
1107     g += (bg-g)*amt;
1108     b += (bb-b)*amt;
1109 }
1110 
flashcolourf(T & r,T & g,T & b,T & f,T br,T bg,T bb,T bf,float amt)1111 template<class T> inline void flashcolourf(T &r, T &g, T &b, T &f, T br, T bg, T bb, T bf, float amt)
1112 {
1113     r += (br-r)*amt;
1114     g += (bg-g)*amt;
1115     b += (bb-b)*amt;
1116     f += (bf-f)*amt;
1117 }
1118 
1119 struct gameentity : extentity
1120 {
1121     int schan;
1122     int lastspawn, nextemit;
1123     linkvector kin;
1124 
gameentitygameentity1125     gameentity() : schan(-1), lastspawn(0), nextemit(0) {}
~gameentitygameentity1126     ~gameentity()
1127     {
1128         if(issound(schan)) removesound(schan);
1129         schan = -1;
1130     }
1131 };
1132 
1133 struct actitem
1134 {
1135     enum { ENT = 0, PROJ };
1136     int type, target;
1137     float score;
1138 
actitemactitem1139     actitem() : type(ENT), target(-1), score(0) {}
~actitemactitem1140     ~actitem() {}
1141 };
1142 #ifdef CPP_GAME_MAIN
1143 const char * const animnames[] =
1144 {
1145     "idle", "walk forward", "walk backward", "walk left", "walk right", "dead", "dying", "swim",
1146     "mapmodel", "trigger on", "trigger off", "pain",
1147     "jump forward", "jump backward", "jump left", "jump right", "jump",
1148     "run forward", "run backward", "run left", "run right",
1149     "boost forward", "boost backward", "boost left", "boost right", "boost up",
1150     "parkour left", "parkour right", "parkour up", "parkour jump", "power slide", "fly kick",
1151     "sink", "edit", "win", "lose",
1152     "crouch", "crawl forward", "crawl backward", "crawl left", "crawl right",
1153     "crouch jump forward", "crouch jump backward", "crouch jump left", "crouch jump right", "crouch jump",
1154     "claw", "claw primary", "claw secondary", "claw reload", "claw power", "claw zoom",
1155     "pistol", "pistol primary", "pistol secondary", "pistol reload", "pistol power", "pistol zoom",
1156     "sword", "sword primary", "sword secondary", "sword power", "sword zoom",
1157     "shotgun", "shotgun primary", "shotgun secondary", "shotgun reload", "shotgun power", "shotgun zoom",
1158     "smg", "smg primary", "smg secondary", "smg reload", "smg power", "smg zoom",
1159     "flamer", "flamer primary", "flamer secondary", "flamer reload", "flamer power", "flamer zoom",
1160     "plasma", "plasma primary", "plasma secondary", "plasma reload", "plasma power", "plasma zoom",
1161     "zapper", "zapper primary", "zapper secondary", "zapper reload", "zapper power", "zapper zoom",
1162     "rifle", "rifle primary", "rifle secondary", "rifle reload", "rifle power", "rifle zoom",
1163     "grenade", "grenade primary", "grenade secondary", "grenade reload", "grenade power", "grenade zoom",
1164     "mine", "mine primary", "mine secondary", "mine reload", "mine power", "mine zoom",
1165     "rocket", "rocket primary", "rocket secondary", "rocket reload", "rocket power", "rocket zoom",
1166     "switch", "use",
1167     ""
1168 };
1169 #else
1170 extern const char * const animnames[];
1171 #endif
1172 
1173 struct eventicon
1174 {
1175     enum { SPREE = 0, MULTIKILL, HEADSHOT, DOMINATE, REVENGE, FIRSTBLOOD, BREAKER, WEAPON, AFFINITY, TOTAL, SORTED = WEAPON, VERBOSE = WEAPON };
1176     int type, millis, fade, length, value;
1177 };
1178 
1179 struct stunevent
1180 {
1181     int weap, millis, delay;
1182     float scale, gravity;
1183 };
1184 
1185 struct jitterevent
1186 {
1187     int weap, millis, delay, last;
1188     float yaw, pitch;
1189 };
1190 
1191 enum
1192 {
1193     TAG_HEAD, TAG_R_HEAD, TAG_TORSO, TAG_R_TORSO, TAG_LIMBS, TAG_R_LIMBS, TAG_WAIST,
1194     TAG_MUZZLE, TAG_ORIGIN, TAG_EJECT1, TAG_EJECT2, TAG_JET_LEFT, TAG_JET_RIGHT, TAG_JET_BACK, TAG_TOE_LEFT, TAG_TOE_RIGHT,
1195     TAG_MAX, TAG_EJECT = TAG_EJECT1, TAG_N_EJECT = 2, TAG_JET = TAG_JET_LEFT, TAG_N_JET = 3, TAG_TOE = TAG_TOE_LEFT, TAG_N_TOE = 2
1196 };
1197 
1198 enum
1199 {
1200     WS_BEGIN_CHAN = 0,
1201     WS_MAIN_CHAN,
1202     WS_END_CHAN,
1203     WS_OTHER_CHAN,
1204 
1205     WS_CHANS
1206 };
1207 
1208 struct gameent : dynent, clientstate
1209 {
1210     editinfo *edit;
1211     ai::aiinfo *ai;
1212     int team, clientnum, privilege, projid, lastnode, checkpoint, cplast, respawned, suicided, lastupdate, lastpredict, plag, ping, lastflag, totaldamage,
1213         actiontime[AC_MAX], impulse[IM_MAX], impulsetime[IM_T_MAX], smoothmillis, turnside, aschan, cschan, vschan, wschan[WS_CHANS], pschan, sschan[2],
1214         lasthit, lastteamhit, lastkill, lastattacker, lastpoints, quake, wasfiring, lastfoot, lastimpulsecollect;
1215     float deltayaw, deltapitch, newyaw, newpitch, stunscale, stungravity;
1216     bool action[AC_MAX], conopen, k_up, k_down, k_left, k_right, obliterated, headless;
1217     vec tag[TAG_MAX];
1218     string hostip, name, handle, steamid, info, obit;
1219     vector<gameent *> dominating, dominated;
1220     vector<eventicon> icons;
1221     vector<stunevent> stuns;
1222     vector<jitterevent> jitters;
1223     vector<int> vitems;
1224 
gameentgameent1225     gameent() : edit(NULL), ai(NULL), team(T_NEUTRAL), clientnum(-1), privilege(PRIV_NONE), projid(0), checkpoint(-1), cplast(0), lastupdate(0), lastpredict(0), plag(0), ping(0),
1226         totaldamage(0), smoothmillis(-1), lastattacker(-1), lastpoints(0), quake(0), wasfiring(-1), conopen(false), k_up(false), k_down(false), k_left(false), k_right(false), obliterated(false)
1227     {
1228         state = CS_DEAD;
1229         type = ENT_PLAYER;
1230         copystring(hostip, "0.0.0.0");
1231         name[0] = handle[0] = steamid[0] = info[0] = obit[0] = '\0';
1232         removesounds();
1233         respawn(-1, 0, 0);
1234     }
~gameentgameent1235     ~gameent()
1236     {
1237         removesounds();
1238         freeeditinfo(edit);
1239         if(ai) delete ai;
1240         removetrackedparticles(this);
1241         removetrackedsounds(this);
1242     }
1243 
isgameent1244     static bool is(int t) { return t == ENT_PLAYER || t == ENT_AI; }
isgameent1245     static bool is(physent *d) { return d->type == ENT_PLAYER || d->type == ENT_AI; }
1246 
1247 
addstungameent1248     void addstun(int weap, int millis, int delay, float scale, float gravity)
1249     {
1250         if(delay <= 0 || (scale == 0 && gravity == 0)) return;
1251         stunevent &s = stuns.add();
1252         s.weap = weap;
1253         s.millis = millis;
1254         s.delay = delay;
1255         s.scale = scale;
1256         s.gravity = gravity;
1257     }
1258 
1259     float stunned(int millis, bool gravity = false)
1260     {
1261         float stun = 0;
loopvrevgameent1262         loopvrev(stuns)
1263         {
1264             stunevent &s = stuns[i];
1265             if(!s.delay)
1266             {
1267                 stuns.remove(i);
1268                 continue;
1269             }
1270             int etime = s.millis+s.delay, len = etime-clamp(millis, s.millis, etime);
1271             if(len > 0) stun += (gravity ? s.gravity : s.scale)*(len/float(s.delay));
1272             if(gravity && millis >= etime) stuns.remove(i);
1273         }
1274         return 1.f-clamp(stun, 0.f, 1.f);
1275     }
1276 
1277     void addjitter(int weap, int millis, int delay, float yawmin, float yawmax, float pitchmin, float pitchmax, int dir = 0)
1278     {
1279         if(delay <= 0) return;
1280         jitterevent &s = jitters.add();
1281         s.weap = weap;
1282         s.millis = s.last = millis;
1283         s.delay = delay;
1284         float yaw1 = min(yawmin, yawmax), yaw2 = max(yawmin, yawmax), yawv = yaw1,
1285               pitch1 = min(pitchmin, pitchmax), pitch2 = max(pitchmin, pitchmax), pitchv = pitch1;
1286         if(yaw2 > yaw1) yawv += (yaw2-yaw1)*(rnd(10000)+1)/10000.f;
1287         if(pitch2 > pitch1) pitchv += (pitch2-pitch1)*(rnd(10000)+1)/10000.f;
1288         if(dir)
1289         {
1290             int value = rnd((dir > 0 ? dir : -dir)+1);
1291             if(dir > 0 ? value == 0 : value != 0) pitchv = -pitchv;
1292         }
1293         s.yaw = yawv;
1294         s.pitch = pitchv;
1295     }
1296 
jittergameent1297     void jitter(int millis)
1298     {
1299         loopvrev(jitters)
1300         {
1301             jitterevent &s = jitters[i];
1302             if(!s.delay)
1303             {
1304                 jitters.remove(i);
1305                 continue;
1306             }
1307             int etime = s.millis+s.delay, len = clamp(millis, s.millis, etime)-s.last;
1308             if(len > 0)
1309             {
1310                 float scale = len/float(s.delay);
1311                 yaw += s.yaw*scale;
1312                 while(yaw < 0.0f) yaw += 360.0f;
1313                 while(yaw >= 360.0f) yaw -= 360.0f;
1314                 pitch += s.pitch*scale;
1315                 if(pitch > 89.9f) pitch = 89.9f;
1316                 if(pitch < -89.9f) pitch = -89.9f;
1317                 s.last = millis;
1318             }
1319             if(millis >= etime) jitters.remove(i);
1320         }
1321     }
1322 
1323     void configure(int millis, int gamemode, int mutators, int affinities = 0, int cur = 0)
1324     {
1325         #define MODPHYSL \
1326             MODPHYS(speed, float, speedscale); \
1327             MODPHYS(jumpspeed, float, speedscale); \
1328             MODPHYS(impulsespeed, float, speedscale); \
1329             MODPHYS(weight, float, curscale);
1330 
1331         float scale = AA(actortype, scale), speedscale = 1;
1332 
1333         if(actortype >= A_ENEMY && entities::ents.inrange(spawnpoint) && entities::ents[spawnpoint]->type == ACTOR)
1334         {
1335             if(entities::ents[spawnpoint]->attrs[8] > 0) speedscale *= entities::ents[spawnpoint]->attrs[8]/100.f;
1336             if(entities::ents[spawnpoint]->attrs[9] > 0) scale *= (entities::ents[spawnpoint]->attrs[9]/100.f);
1337         }
1338 
1339         if(m_resize(gamemode, mutators))
1340         {
1341             float minscale = 1, amtscale = m_insta(gamemode, mutators) ? 1+(spree*G(instaresizeamt)) : max(health, 1)/float(max(gethealth(gamemode, mutators), 1));
1342             if(m_resize(gamemode, mutators))
1343             {
1344                 minscale = G(minresizescale);
1345                 if(amtscale < 1) amtscale = (amtscale*(1-minscale))+minscale;
1346             }
1347             scale *= clamp(amtscale, minscale, G(maxresizescale));
1348         }
1349 
1350         if(scale != curscale)
1351         {
1352             if(cur && state == CS_ALIVE)
1353                 curscale = scale > curscale ? min(curscale+cur/2000.f, scale) : max(curscale-cur/2000.f, scale);
1354             else curscale = scale;
1355         }
1356 
1357         loopi(W_MAX) if(weapstate[i] != W_S_IDLE && (weapselect != i || (weapstate[i] != W_S_POWER && weapstate[i] != W_S_ZOOM)) && millis-weaptime[i] >= weapwait[i]+100)
1358             setweapstate(i, W_S_IDLE, 0, millis);
1359 
1360         xradius = yradius = actors[actortype].radius*curscale;
1361         zradius = actors[actortype].height*curscale;
1362         if(!cur) height = zradius;
1363 
1364         #define MODPHYS(a,b,c) a = AA(actortype, a)*c;
1365         MODPHYSL;
1366         #undef MODPHYS
1367         if(m_single(gamemode, mutators))
1368         {
1369             #define MODPHYS(a,b,c) a += AA(actortype, a##extra)*c;
1370             MODPHYSL;
1371             #undef MODPHYS
1372         }
1373         if(affinities > 0)
1374         {
1375             if(m_capture(gamemode))
1376             {
1377                 #define MODPHYS(a,b,c) a += G(capturecarry##a)+(affinities*G(capturecarry##a##each));
1378                 MODPHYSL;
1379                 #undef MODPHYS
1380             }
1381             else if(m_bomber(gamemode))
1382             {
1383                 #define MODPHYS(a,b,c) a += G(bombercarry##a);
1384                 MODPHYSL;
1385                 #undef MODPHYS
1386             }
1387         }
1388         int sweap = m_weapon(actortype, gamemode, mutators);
1389         loopi(W_MAX) if(hasweap(i, sweap))
1390         {
1391             int numammo = getammo(i, 0, true);
1392             #define MODCARRY(a) (m_arena(gamemode, mutators) ? (a)*WEAPCARRY/W_LOADOUT : a)
1393             #define MODPHYS(a,b,c) a += MODCARRY(W(i, mod##a)+(numammo*W(i, mod##a##ammo)));
1394             MODPHYSL;
1395             #undef MODPHYS
1396             if(i != weapselect) continue;
1397             #define MODPHYS(a,b,c) a += W(i, mod##a##equip);
1398             MODPHYSL;
1399             #undef MODPHYS
1400             switch(weapstate[i])
1401             {
1402                 case W_S_PRIMARY: case W_S_SECONDARY:
1403                 {
1404                     #define MODPHYS(a,b,c) a += W2(i, mod##a##attack, weapstate[i] == W_S_SECONDARY);
1405                     MODPHYSL;
1406                     #undef MODPHYS
1407                     break;
1408                 }
1409                 case W_S_RELOAD:
1410                 {
1411                     #define MODPHYS(a,b,c) a += W(i, mod##a##reload);
1412                     MODPHYSL;
1413                     #undef MODPHYS
1414                     break;
1415                 }
1416                 case W_S_SWITCH:
1417                 {
1418                     #define MODPHYS(a,b,c) a += W(i, mod##a##switch);
1419                     MODPHYSL;
1420                     #undef MODPHYS
1421                     break;
1422                 }
1423                 case W_S_USE:
1424                 {
1425                     #define MODPHYS(a,b,c) a += W(i, mod##a##use);
1426                     MODPHYSL;
1427                     #undef MODPHYS
1428                     break;
1429                 }
1430                 case W_S_POWER:
1431                 {
1432                     #define MODPHYS(a,b,c) a += W(i, mod##a##power);
1433                     MODPHYSL;
1434                     #undef MODPHYS
1435                     break;
1436                 }
1437                 case W_S_ZOOM:
1438                 {
1439                     #define MODPHYS(a,b,c) a += W(i, mod##a##zoom);
1440                     MODPHYSL;
1441                     #undef MODPHYS
1442                     break;
1443                 }
1444             }
1445         }
1446 
1447         #define MODPHYS(a,b,c) a = max(a, b(0));
1448         MODPHYSL;
1449         #undef MODPHYS
1450 
1451         radius = max(xradius, yradius);
1452         aboveeye = curscale;
1453 
1454         #undef MODPHYSL
1455         if(cur)
1456         {
1457             jitter(millis);
1458             stunscale = stunned(millis, false);
1459             stungravity = stunned(millis, true);
1460         }
1461     }
1462 
getprojidgameent1463     int getprojid()
1464     {
1465         projid++;
1466         if(projid < 2) projid = 2;
1467         return projid;
1468     }
1469 
removesoundsgameent1470     void removesounds()
1471     {
1472         if(issound(aschan)) removesound(aschan);
1473         if(issound(cschan)) removesound(cschan);
1474         if(issound(vschan)) removesound(vschan);
1475         if(issound(pschan)) removesound(pschan);
1476         aschan = cschan = vschan = pschan = -1;
1477         loopi(WS_CHANS)
1478         {
1479             if(issound(wschan[i])) removesound(wschan[i]);
1480             wschan[i] = -1;
1481         }
1482         loopi(2)
1483         {
1484             if(issound(sschan[i])) removesound(sschan[i]);
1485             sschan[i] = -1;
1486         }
1487     }
1488 
stopmovinggameent1489     void stopmoving(bool full)
1490     {
1491         if(full) move = strafe = 0;
1492         loopi(AC_MAX)
1493         {
1494             action[i] = false;
1495             actiontime[i] = 0;
1496         }
1497     }
1498 
clearstategameent1499     void clearstate(int millis, int gamemode, int mutators)
1500     {
1501         loopi(IM_MAX) impulse[i] = 0;
1502         loopi(IM_T_MAX) impulsetime[i] = 0;
1503         lasthit = lastkill = quake = turnside = lastimpulsecollect = 0;
1504         lastteamhit = lastflag = respawned = suicided = lastnode = lastfoot = wasfiring = -1;
1505         obit[0] = '\0';
1506         obliterated = headless = false;
1507         icons.shrink(0);
1508         stuns.shrink(0);
1509         jitters.shrink(0);
1510         used.shrink(0);
1511         cleartags();
1512     }
1513 
respawngameent1514     void respawn(int millis, int gamemode, int mutators)
1515     {
1516         stopmoving(true);
1517         removesounds();
1518         physent::reset();
1519         clearstate(millis, gamemode, mutators);
1520         clientstate::respawn(millis);
1521         configure(millis, gamemode, mutators);
1522     }
1523 
editspawngameent1524     void editspawn(int gamemode, int mutators)
1525     {
1526         stopmoving(true);
1527         clearstate(lastmillis, gamemode, mutators);
1528         inmaterial = airmillis = floormillis = 0;
1529         inliquid = onladder = forcepos = false;
1530         strafe = move = 0;
1531         physstate = PHYS_FALL;
1532         vel = falling = vec(0, 0, 0);
1533         floor = vec(0, 0, 1);
1534         resetinterp();
1535         clientstate::editspawn(gamemode, mutators);
1536     }
1537 
resetstategameent1538     void resetstate(int millis, int gamemode, int mutators)
1539     {
1540         respawn(millis, gamemode, mutators);
1541         checkpoint = -1;
1542         frags = deaths = totaldamage = cplast = 0;
1543     }
1544 
mapchangegameent1545     void mapchange(int millis, int gamemode, int mutators)
1546     {
1547         dominating.shrink(0);
1548         dominated.shrink(0);
1549         icons.shrink(0);
1550         resetstate(millis, gamemode, mutators);
1551         clientstate::mapchange();
1552     }
1553 
cleartagsgameent1554     void cleartags()
1555     {
1556         loopi(TAG_MAX) tag[i] = vec(-1, -1, -1);
1557     }
1558 
headsizegameent1559     float headsize()
1560     {
1561         return max(xradius*0.45f, yradius*0.45f);
1562     }
1563 
headtaggameent1564     vec &headtag()
1565     {
1566         if(tag[TAG_HEAD] == vec(-1, -1, -1))
1567         {
1568             tag[TAG_HEAD] = o;
1569             tag[TAG_HEAD].z -= headsize()*0.375f;
1570         }
1571         return tag[TAG_HEAD];
1572     }
1573 
headboxgameent1574     vec &headbox()
1575     {
1576         if(tag[TAG_R_HEAD] == vec(-1, -1, -1))
1577             tag[TAG_R_HEAD] = vec(xradius*0.5f, yradius*0.5f, headsize());
1578         return tag[TAG_R_HEAD];
1579     }
1580 
torsosizegameent1581     float torsosize()
1582     {
1583         return (headtag().z-headbox().z)-torsotag().z;
1584     }
1585 
torsotaggameent1586     vec &torsotag()
1587     {
1588         if(tag[TAG_TORSO] == vec(-1, -1, -1))
1589         {
1590             tag[TAG_TORSO] = o;
1591             tag[TAG_TORSO].z -= height*0.45f;
1592         }
1593         return tag[TAG_TORSO];
1594     }
1595 
torsoboxgameent1596     vec &torsobox()
1597     {
1598         if(tag[TAG_R_TORSO] == vec(-1, -1, -1))
1599             tag[TAG_R_TORSO] = vec(xradius, yradius, torsosize());
1600         return tag[TAG_R_TORSO];
1601     }
1602 
limbsizegameent1603     float limbsize()
1604     {
1605         return ((torsotag().z-torsobox().z)-(o.z-height))*0.5f;
1606     }
1607 
limbstaggameent1608     vec &limbstag()
1609     {
1610         if(tag[TAG_LIMBS] == vec(-1, -1, -1))
1611         {
1612             tag[TAG_LIMBS] = torsotag();
1613             tag[TAG_LIMBS].z -= torsobox().z+limbsize();
1614         }
1615         return tag[TAG_LIMBS];
1616     }
1617 
limbsboxgameent1618     vec &limbsbox()
1619     {
1620         if(tag[TAG_R_LIMBS] == vec(-1, -1, -1))
1621             tag[TAG_R_LIMBS] = vec(xradius*0.85f, yradius*0.85f, limbsize());
1622         return tag[TAG_R_LIMBS];
1623     }
1624 
1625     vec &origintag(int weap = -1)
1626     {
1627         if(!isweap(weap)) weap = weapselect;
1628         if(tag[TAG_ORIGIN] == vec(-1, -1, -1))
1629         {
1630             if(weap == W_MELEE) tag[TAG_ORIGIN] = feetpos();
1631             else
1632             {
1633                 vec dir, right;
1634                 vecfromyawpitch(yaw, pitch, 1, 0, dir);
1635                 dir.mul(radius*3);
1636                 vecfromyawpitch(yaw, pitch, 0, -1, right);
1637                 right.mul(radius*1.5f);
1638                 tag[TAG_ORIGIN] = vec(headpos(-height/6)).add(right).add(dir);
1639             }
1640         }
1641         return tag[TAG_ORIGIN];
1642     }
1643 
1644     vec &muzzletag(int weap = -1)
1645     {
1646         if(!isweap(weap)) weap = weapselect;
1647         if(tag[TAG_MUZZLE] == vec(-1, -1, -1))
1648         {
1649             if(weap == W_SWORD && ((weapstate[weap] == W_S_PRIMARY) || (weapstate[weap] == W_S_SECONDARY)))
1650             {
1651                 float frac = (lastmillis-weaptime[weap])/float(weapwait[weap]), yx = yaw, px = pitch;
1652                 if(weapstate[weap] == W_S_PRIMARY)
1653                 {
1654                     yx -= 90;
1655                     yx += frac*180;
1656                     if(yx >= 360) yx -= 360;
1657                     if(yx < 0) yx += 360;
1658                 }
1659                 else
1660                 {
1661                     px += 90;
1662                     px -= frac*180;
1663                     if(px >= 180) px -= 360;
1664                     if(px < -180) px += 360;
1665                 }
1666                 tag[TAG_MUZZLE] = vec(origintag(weap)).add(vec(yx*RAD, px*RAD).mul(8));
1667             }
1668             else
1669             {
1670                 vec dir(yaw*RAD, pitch*RAD);
1671                 if(weap != W_CLAW)
1672                 {
1673                     vec right;
1674                     vecfromyawpitch(yaw, pitch, 0, -1, right);
1675                     tag[TAG_MUZZLE] = vec(origintag(weap)).add(dir.mul(radius*0.75f)).add(right.mul(radius*0.6f));
1676                 }
1677                 else tag[TAG_MUZZLE] = vec(origintag(weap)).add(dir.mul(radius*2));
1678             }
1679         }
1680         return tag[TAG_MUZZLE];
1681     }
1682 
1683     vec &ejecttag(int weap = -1, int idx = 0)
1684     {
1685         if(!isweap(weap)) weap = weapselect;
1686         if(idx < 0 || idx >= TAG_N_EJECT) idx = 0;
1687         int tnum = TAG_EJECT+idx;
1688         if(tag[tnum] == vec(-1, -1, -1)) tag[tnum] = idx ? origintag(weap) : muzzletag(weap);
1689         return tag[tnum];
1690     }
1691 
waisttaggameent1692     vec &waisttag()
1693     {
1694         if(tag[TAG_WAIST] == vec(-1, -1, -1))
1695         {
1696             vec dir;
1697             vecfromyawpitch(yaw, 0, -1, 0, dir);
1698             dir.mul(radius*1.6f);
1699             dir.z -= height*0.5f;
1700             tag[TAG_WAIST] = vec(o).add(dir);
1701         }
1702         return tag[TAG_WAIST];
1703     }
1704 
jetlefttaggameent1705     vec &jetlefttag()
1706     {
1707         if(tag[TAG_JET_LEFT] == vec(-1, -1, -1))
1708         {
1709             vec dir;
1710             vecfromyawpitch(yaw, 0, -1, -1, dir);
1711             dir.mul(radius);
1712             dir.z -= height;
1713             tag[TAG_JET_LEFT] = vec(o).add(dir);
1714         }
1715         return tag[TAG_JET_LEFT];
1716     }
1717 
jetrighttaggameent1718     vec &jetrighttag()
1719     {
1720         if(tag[TAG_JET_RIGHT] == vec(-1, -1, -1))
1721         {
1722             vec dir;
1723             vecfromyawpitch(yaw, 0, -1, 1, dir);
1724             dir.mul(radius);
1725             dir.z -= height;
1726             tag[TAG_JET_RIGHT] = vec(o).add(dir);
1727         }
1728         return tag[TAG_JET_RIGHT];
1729     }
1730 
jetbacktaggameent1731     vec &jetbacktag()
1732     {
1733         if(tag[TAG_JET_BACK] == vec(-1, -1, -1))
1734         {
1735             vec dir;
1736             vecfromyawpitch(yaw, 0, -1, 0, dir);
1737             dir.mul(radius*1.25f);
1738             dir.z -= height*0.35f;
1739             tag[TAG_JET_BACK] = vec(o).add(dir);
1740         }
1741         return tag[TAG_JET_BACK];
1742     }
1743 
jettaggameent1744     vec &jettag(int idx)
1745     {
1746         switch(idx)
1747         {
1748             case 2: return jetbacktag();
1749             case 1: return jetrighttag();
1750             case 0: return jetlefttag();
1751             default: break;
1752         }
1753         return jetbacktag();
1754     }
1755 
1756     vec &foottag(int idx = 0)
1757     {
1758         if(idx < 0 || idx > 1) idx = 0;
1759         int tnum = TAG_TOE+idx;
1760         if(tag[tnum] == vec(-1, -1, -1))
1761         {
1762             int millis = lastmillis%500;
1763             float amt = millis > 250 ? (500-millis)/250.f : millis/250.f;
1764             vec dir, right;
1765             vecfromyawpitch(yaw, pitch, 1, 0, dir);
1766             vecfromyawpitch(yaw, pitch, 0, idx ? 1 : -1, right);
1767             dir.mul(radius*0.5f);
1768             right.mul(radius*(!move && strafe ? amt-0.5f : 0.5f));
1769             dir.z -= height*0.6f+(height*0.4f*(idx ? 1-amt : amt));
1770             tag[tnum] = vec(o).add(dir).add(right);
1771         }
1772         return tag[tnum];
1773     }
1774 
toetaggameent1775     vec &toetag(int idx)
1776     {
1777         switch(idx)
1778         {
1779             case 1: return foottag(0);
1780             case 0: return foottag(1);
1781             default: break;
1782         }
1783         return foottag(0);
1784     }
1785 
1786     void resetjump(bool wait = false)
1787     {
1788         airmillis = turnside = impulse[IM_COUNT] = 0;
1789         impulsetime[IM_T_JUMP] = impulsetime[IM_T_BOOST] = impulsetime[IM_T_POUND] = 0;
1790         if(!wait)
1791         {
1792             impulse[IM_TYPE] = IM_T_JUMP;
1793             impulsetime[IM_T_PUSHER] = 0;
1794         }
1795     }
1796 
1797     void resetair(bool wait = false)
1798     {
1799         resetphys();
1800         resetjump(wait);
1801     }
1802 
1803     void doimpulse(int type, int millis, int cost = 0, int side = 0)
1804     {
1805         if(type < 0 || type >= IM_T_MAX) return;
1806         if(cost) impulse[IM_METER] += cost;
1807         impulsetime[type] = millis;
1808         if(type != IM_T_KICK) impulse[IM_SLIP] = millis;
1809         impulse[IM_TYPE] = type;
1810         if(type != IM_T_JUMP)
1811         {
1812             if(!impulsetime[IM_T_JUMP]) impulsetime[IM_T_JUMP] = millis;
1813             if(type != IM_T_AFTER) impulse[IM_COUNT]++;
1814         }
1815         if(type != IM_T_AFTER)
1816         {
1817             impulse[IM_REGEN] = millis;
1818             if(type != IM_T_PUSHER) resetphys(type > IM_T_JUMP && type < IM_T_TOUCH);
1819             else resetair(true);
1820         }
1821         turnside = side;
1822     }
1823 
1824     void addicon(int type, int millis, int fade, int value = 0)
1825     {
1826         int pos = -1;
loopvgameent1827         loopv(icons)
1828         {
1829             if(icons[i].type == type)
1830             {
1831                 switch(icons[i].type)
1832                 {
1833                     case eventicon::WEAPON:
1834                     {
1835                         if(icons[i].value != value) continue;
1836                         break;
1837                     }
1838                     default: break;
1839                 }
1840                 icons[i].length = max(icons[i].fade, fade);
1841                 icons[i].fade = millis-icons[i].millis+fade;
1842                 icons[i].value = value;
1843                 return;
1844             }
1845             if(pos < 0 && type >= eventicon::SORTED && icons[i].type > type) pos = i;
1846         }
1847         eventicon e;
1848         e.type = type;
1849         e.millis = millis;
1850         e.fade = e.length = fade;
1851         e.value = value;
1852         if(pos < 0) icons.add(e);
1853         else icons.insert(pos, e);
1854     }
1855 
setnamegameent1856     void setname(const char *n)
1857     {
1858         if(n && *n) copystring(name, n, MAXNAMELEN+1);
1859         else name[0] = '\0';
1860     }
1861 
setvanitygameent1862     bool setvanity(const char *v)
1863     {
1864         if(clientstate::setvanity(v))
1865         {
1866             vitems.shrink(0);
1867             return true;
1868         }
1869         return false;
1870     }
1871 
setinfogameent1872     void setinfo(const char *n, int c, int m, int p, const char *v, vector<int> &w, vector<int> &r)
1873     {
1874         setname(n);
1875         colour = c;
1876         model = m;
1877         pattern = p;
1878         setvanity(v);
1879         loadweap.shrink(0);
1880         loopv(w) loadweap.add(w[i]);
1881         randweap.shrink(0);
1882         loopv(r) randweap.add(r[i]);
1883     }
1884 
1885     bool hasmelee(int millis, bool check = true)
1886     {
1887         if(!(AA(actortype, abilities)&(1<<A_A_MELEE))) return false;
1888         if(check && ((weapstate[W_MELEE] != W_S_PRIMARY && weapstate[W_MELEE] != W_S_SECONDARY) || millis-weaptime[W_MELEE] >= weapwait[W_MELEE])) return false;
1889         return true;
1890     }
1891 
1892     bool canmelee(int sweap, int millis, bool alt = false)
1893     {
1894         if(!hasmelee(millis, false)) return false;
1895         if(!canshoot(W_MELEE, alt ? HIT(ALT) : 0, sweap, millis, (1<<W_S_RELOAD))) return false;
1896         return true;
1897     }
1898 
curfootgameent1899     int curfoot()
1900     {
1901         int foot = -1;
1902         vec fp = feetpos();
1903 
1904         float d0 = fabs(foottag(0).z - fp.z),
1905               d1 = fabs(foottag(1).z - fp.z);
1906 
1907         if(d0 < FOOTSTEP_DIST * curscale) foot = 0;
1908         else if(d1 < FOOTSTEP_DIST * curscale) foot = 1;
1909 
1910         return foot;
1911     }
1912 
1913     bool crouching(bool limit = false)
1914     {
1915         if(!(AA(actortype, abilities)&(1<<A_A_CROUCH))) return false;
1916         return action[AC_CROUCH] || (!limit && zradius > height);
1917     }
1918 
1919     bool running(float minspeed = 0)
1920     {
1921         if(minspeed != 0 && vel.magnitude() >= speed*0.5f*minspeed) return true;
1922         return sliding(true) || (!action[AC_WALK] && !crouching());
1923     }
1924 
1925     bool sliding(bool power = false)
1926     {
1927         if(G(impulseslidelen) && impulsetime[IM_T_SLIDE] && lastmillis-impulsetime[IM_T_SLIDE] <= G(impulseslidelen)) return true;
1928         if(!power && G(impulsesliplen) && impulse[IM_SLIP] && lastmillis-impulse[IM_SLIP] <= G(impulsesliplen)) return true;
1929         return false;
1930     }
1931 
zoominggameent1932     int zooming()
1933     {
1934         if(isweap(weapselect) && W2(weapselect, cooked, true)&W_C_ZOOM)
1935         {
1936             if(weapstate[weapselect] == W_S_ZOOM) return weaptime[weapselect];
1937             if(W2(weapselect, cooked, true)&W_C_KEEP && prevstate[weapselect] == W_S_ZOOM && action[AC_SECONDARY])
1938                 return prevtime[weapselect];
1939         }
1940         return 0;
1941     }
1942 };
1943 
1944 struct projent : dynent
1945 {
1946     vec from, dest, norm, inertia, sticknrm, stickpos, effectpos, trailpos, lastgood;
1947     int addtime, lifetime, lifemillis, waittime, spawntime, fadetime, lastradial, lasteffect, lastbounce, beenused, extinguish, stuck;
1948     float movement, distance, lifespan, lifesize, speedmin, speedmax;
1949     bool local, limited, escaped, child, bounced;
1950     int projtype, projcollide, interacts;
1951     float elasticity, reflectivity, relativity, liquidcoast;
1952     int schan, id, weap, fromweap, fromflags, value, flags, collidezones;
1953     //entitylight light;
1954     gameent *owner, *target, *stick;
1955     physent *hit;
1956     const char *mdlname;
1957     bvec material;
1958 
projentprojent1959     projent() : projtype(PRJ_SHOT), id(-1), collidezones(CLZ_NONE), owner(NULL), target(NULL), stick(NULL), hit(NULL), mdlname(NULL) { reset(); }
~projentprojent1960     ~projent()
1961     {
1962         removetrackedparticles(this);
1963         removetrackedsounds(this);
1964         if(issound(schan)) removesound(schan);
1965         schan = -1;
1966     }
1967 
isprojent1968     static bool is(int t) { return t == ENT_PROJ; }
isprojent1969     static bool is(physent *d) { return d->type == ENT_PROJ; }
shotprojent1970     static bool shot(int t, int w) { return t == ENT_PROJ && w == PRJ_SHOT; }
shotprojent1971     static bool shot(physent *d) { return d->type == ENT_PROJ && ((projent*)d)->projtype == PRJ_SHOT; }
1972 
resetprojent1973     void reset()
1974     {
1975         physent::reset();
1976         type = ENT_PROJ;
1977         state = CS_ALIVE;
1978         norm = vec(0, 0, 1);
1979         inertia = sticknrm = stickpos = lastgood = vec(0, 0, 0);
1980         effectpos = vec(-1e16f, -1e16f, -1e16f);
1981         addtime = lifetime = lifemillis = waittime = spawntime = fadetime = lastradial = lasteffect = lastbounce = beenused = flags = 0;
1982         schan = id = weap = fromweap = fromflags = value = -1;
1983         movement = distance = lifespan = speedmin = speedmax = 0;
1984         curscale = lifesize = 1;
1985         extinguish = stuck = interacts = 0;
1986         limited = escaped = child = bounced = false;
1987         projcollide = BOUNCE_GEOM|BOUNCE_PLAYER;
1988         material = bvec(255, 255, 255);
1989     }
1990 
1991     bool ready(bool used = true)
1992     {
1993         if(owner && (!used || projtype == PRJ_SHOT || !beenused) && waittime <= 0 && state != CS_DEAD)
1994             return true;
1995         return false;
1996     }
1997 
usestuckprojent1998     bool usestuck()
1999     {
2000         if(projcollide&COLLIDE_SCAN) return false;
2001         return stuck != 0;
2002     }
2003 };
2004 
2005 struct cament
2006 {
2007     enum { ENTITY = 0, WAYPOINT, PLAYER, AFFINITY, MAX };
2008 
2009     int cn, type, id, inview[MAX], lastinview[MAX], lastyawtime, lastpitchtime;
2010     vec o, dir;
2011     float dist, lastyaw, lastpitch;
2012     gameent *player;
2013     bool ignore;
2014     cament *moveto;
2015     vector<cament *> links;
2016 
camentcament2017     cament(int p, int t) : cn(p), type(t), id(-1), player(NULL), ignore(false), moveto(NULL)
2018     {
2019         reset();
2020         resetlast();
2021     }
camentcament2022     cament(int p, int t, int n) : cn(p), type(t), id(n), player(NULL), ignore(false), moveto(NULL)
2023     {
2024         reset();
2025         resetlast();
2026     }
camentcament2027     cament(int p, int t, int n, vec &d) : cn(p), type(t), id(n), player(NULL), ignore(false), moveto(NULL)
2028     {
2029         reset();
2030         resetlast();
2031         o = d;
2032     }
camentcament2033     cament(int p, int t, int n, vec &c, gameent *d) : cn(p), type(t), id(n), player(d), ignore(false), moveto(NULL)
2034     {
2035         reset();
2036         resetlast();
2037         o = c;
2038     }
~camentcament2039     ~cament() {}
2040 
resetcament2041     void reset()
2042     {
2043         loopi(MAX) inview[i] = lastinview[i] = 0;
2044         if(dir.iszero()) dir = vec(float(rnd(360)), float(rnd(91)-45));
2045         links.setsize(0);
2046     }
2047 
resetlastcament2048     void resetlast()
2049     {
2050         lastyawtime = lastpitchtime = totalmillis;
2051         lastyaw = lastpitch = 0;
2052     }
2053 
comparecament2054     static bool compare(const cament *a, const cament *b)
2055     {
2056         if(!a->ignore && b->ignore) return true;
2057         if(a->ignore && !b->ignore) return false;
2058         if(a->inview[cament::PLAYER] > b->inview[cament::PLAYER]) return true;
2059         if(a->inview[cament::PLAYER] < b->inview[cament::PLAYER]) return false;
2060         if(a->inview[cament::AFFINITY] > b->inview[cament::AFFINITY]) return true;
2061         if(a->inview[cament::AFFINITY] < b->inview[cament::AFFINITY]) return false;
2062         return !rnd(2);
2063     }
2064 
2065     vec pos(float amt = 0)
2066     {
2067         if(amt > 0 && moveto) return vec(o).add(vec(moveto->o).sub(o).mul(min(amt, 1.f)));
2068         return o;
2069     }
2070 };
2071 
2072 namespace client
2073 {
2074     extern int showpresence, showpresencehostinfo, showteamchange, needsmap, gettingmap;
2075     extern bool demoplayback, isready, loadedmap;
2076     extern vector<uchar> messages;
2077     extern void clearvotes(gameent *d, bool msg = false);
2078     extern void ignore(int cn);
2079     extern void unignore(int cn);
2080     extern bool isignored(int cn);
2081     extern bool addmsg(int type, const char *fmt = NULL, ...);
2082     extern void saytext(gameent *f, gameent *t, int flags, char *text);
2083     extern void c2sinfo(bool force = false);
2084     extern bool haspriv(gameent *d, int priv = PRIV_NONE);
2085 }
2086 
2087 namespace physics
2088 {
2089     extern int smoothmove, smoothdist, physframetime, grabstyle, grabplayerstyle;
2090     extern bool isghost(gameent *d, gameent *e, bool proj = false);
2091     extern int carryaffinity(gameent *d);
2092     extern bool secondaryweap(gameent *d);
2093     extern bool allowimpulse(physent *d, int level = 0);
2094     extern bool canimpulse(physent *d, int level = 0, bool touch = false);
2095     extern float impulsevelocity(physent *d, float amt, int &cost, int type, float redir, vec &keep);
2096     extern bool movecamera(physent *pl, const vec &dir, float dist, float stepdist);
2097     extern void smoothplayer(gameent *d, int res, bool local);
2098     extern void update();
2099     extern void reset();
2100 }
2101 
2102 namespace projs
2103 {
2104     extern vector<projent *> projs, collideprojs;
2105 
2106     extern void reset();
2107     extern void update();
2108     extern projent *create(const vec &from, const vec &to, bool local, gameent *d, int type, int fromweap, int fromflags, int lifetime, int lifemillis, int waittime, int speed, int id = 0, int weap = -1, int value = -1, int flags = 0, float scale = 1, bool child = false, gameent *target = NULL);
2109     extern void preload();
2110     extern void removeplayer(gameent *d);
2111     extern void destruct(gameent *d, int targ, int id, bool all = false);
2112     extern void sticky(gameent *d, int id, vec &norm, vec &pos, gameent *f = NULL);
2113     extern void shootv(int weap, int flags, int sub, int offset, float scale, vec &from, vec &dest, vector<shotmsg> &shots, gameent *d, bool local, gameent *v = NULL);
2114     extern void drop(gameent *d, int weap, int ent, int ammo = -1, bool local = true, int index = 0, int targ = -1);
2115     extern void adddynlights();
2116     extern void render();
2117 }
2118 
2119 namespace weapons
2120 {
2121     extern int slot(gameent *d, int n, bool back = false);
2122     extern bool weapselect(gameent *d, int weap, int filter, bool local = true);
2123     extern bool weapreload(gameent *d, int weap, int load = -1, int ammo = -1, int store = 0, bool local = true);
2124     extern void weapdrop(gameent *d, int w = -1);
2125     extern void checkweapons(gameent *d);
2126     extern float accmodspread(gameent *d, int weap, bool secondary, bool zooming);
2127     extern bool doshot(gameent *d, vec &targ, int weap, bool pressed = false, bool secondary = false, int force = 0, gameent *v = NULL);
2128     extern void shoot(gameent *d, vec &targ, int force = 0);
2129     extern void preload();
2130     extern bool canuse(int weap);
2131 }
2132 
2133 namespace hud
2134 {
2135     extern char *chattex, *playertex, *deadtex, *waitingtex, *spectatortex, *editingtex, *dominatingtex, *dominatedtex, *inputtex,
2136         *bliptex, *playerbliptex, *pointtex, *flagtex, *bombtex, *arrowtex, *arrowrighttex, *arrowdowntex, *arrowlefttex, *alerttex, *questiontex, *flagdroptex,
2137         *flagtakentex, *bombdroptex, *bombtakentex, *attacktex, *warningtex, *indicatortex, *crosshairtex, *hithairtex,
2138         *spree1tex, *spree2tex, *spree3tex, *spree4tex, *multi1tex, *multi2tex, *multi3tex, *headshottex, *dominatetex, *revengetex,
2139         *firstbloodtex, *breakertex;
2140     extern int hudwidth, hudheight, hudsize, lastteam, damageresidue, damageresiduefade, shownotices, showevents, radaraffinitynames, teamhurthud, teamhurttime, teamhurtdist;
2141     extern float noticescale, eventscale, radaraffinityblend, radarblipblend, radaraffinitysize, noticepadx, noticepady, eventpadx, eventpady;
2142     extern bool scoreson, scoresoff, shownscores;
2143     extern vector<int> teamkills;
2144     extern const char *icontex(int type, int value);
2145     extern void drawindicator(int weap, int x, int y, int s);
2146     extern void drawclip(int weap, int x, int y, float s);
2147     extern void drawpointertex(const char *tex, int x, int y, int s, float r = 1, float g = 1, float b = 1, float fade = 1);
2148     extern void drawpointer(int w, int h, int index);
2149     extern int numteamkills();
2150     extern void damage(int n, const vec &loc, gameent *v, int weap, int flags);
2151     extern void hit(int n, const vec &loc, gameent *v, int weap, int flags);
2152     extern void removeplayer(gameent *d);
2153     extern const char *teamtexname(int team = T_NEUTRAL);
2154     extern const char *itemtex(int type, int stype);
2155     extern const char *privtex(int priv = PRIV_NONE, int actortype = A_PLAYER);
2156     extern bool canshowscores();
2157     extern void showscores(bool on, bool interm = false, bool onauto = true, bool ispress = false);
2158     extern score &teamscore(int team);
2159     extern void resetscores();
2160     extern void cleanup();
2161 }
2162 
2163 enum { CTONE_TEAM = 0, CTONE_TONE, CTONE_TEAMED, CTONE_ALONE, CTONE_MIXED, CTONE_TMIX, CTONE_AMIX, CTONE_MAX };
2164 namespace game
2165 {
2166     extern int gamestate, gamemode, mutators, nextmode, nextmuts, timeremaining, lasttimeremain, maptime, mapstart, lastzoom, lasttvcam, lasttvchg, spectvtime, waittvtime,
2167             bloodfade, bloodsize, bloodsparks, debrisfade, eventiconfade, eventiconshort, damageinteger,
2168             announcefilter, dynlighteffects, aboveheadaffinity, aboveheadnames, followthirdperson, nogore, forceplayermodel, forceplayerpattern,
2169             playerovertone, playerundertone, playerdisplaytone, playereffecttone, playerteamtone, follow, specmode, spectvfollow, spectvfollowing, clientcrc, affinityhint;
2170     extern float bloodscale, debrisscale, aboveitemiconsize, aboveheadnamessize, playerovertonelevel, playerundertonelevel, playerdisplaytonelevel, playereffecttonelevel, playerteamtonelevel,
2171     affinityhintblend, affinityhintsize, affinityhintfadeat, affinityhintfadecut, affinityfollowblend, affinitythirdblend, bombertargetnamefadeat, bombertargetnamefadecut, damagedivisor;
2172     extern bool zooming, wantsloadoutmenu;
2173     extern vec swaypush, swaydir;
2174     extern string clientmap;
2175     extern int attrmap[W_MAX];
2176 
2177     extern gameent *player1, *focus;
2178     extern vector<gameent *> players, waiting;
2179 
2180     struct avatarent : dynent
2181     {
avatarentavatarent2182         avatarent() { type = ENT_CAMERA; }
2183     };
2184     extern avatarent avatarmodel, bodymodel;
2185 
2186     extern bool needname(gameent *d);
2187     extern void vanityreset();
2188     extern void vanitybuild(gameent *d);
2189     extern const char *vanityfname(gameent *d, int n, bool proj = false);
2190     extern void followswitch(int n, bool other = false);
2191     extern vector<cament *> cameras;
2192     extern gameent *newclient(int cn);
2193     extern gameent *getclient(int cn);
2194     extern gameent *intersectclosest(vec &from, vec &to, gameent *at);
2195     extern void clientdisconnected(int cn, int reason = DISC_NONE);
2196     extern const char *colourname(gameent *d, char *name = NULL, bool icon = true, bool dupname = true, int colour = 3);
2197     extern const char *colourteam(int team, const char *icon = "");
2198     extern int findcolour(gameent *d, bool tone = true, bool mix = false, float level = 1);
2199     extern int getcolour(gameent *d, int type = 0, float level = 1.f);
2200     extern void errorsnd(gameent *d);
2201     extern void announce(int idx, gameent *d = NULL, bool forced = false);
2202     extern void announcef(int idx, int targ, gameent *d, bool forced, const char *msg, ...);
2203     extern void specreset(gameent *d = NULL, bool clear = false);
2204     extern float opacity(gameent *d);
2205     extern void respawn(gameent *d);
2206     extern void respawned(gameent *d, bool local, int ent = -1);
2207     extern vec pulsecolour(physent *d, int i = 0, int cycle = 50);
2208     extern int pulsehexcol(physent *d, int i = 0, int cycle = 50);
2209     extern void spawneffect(int type, const vec &pos, float radius, int colour, float size);
2210     extern void impulseeffect(gameent *d, int effect = 0);
2211     extern void suicide(gameent *d, int flags = 0);
2212     extern void fixrange(float &yaw, float &pitch);
2213     extern void fixfullrange(float &yaw, float &pitch, float &roll, bool full = false);
2214     extern void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch);
2215     extern void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float yawspeed = 1, float pitchspeed = 1, float rotate = 0);
2216     extern bool allowmove(physent *d);
2217     extern void checkzoom();
2218     extern bool inzoom();
2219     extern bool tvmode(bool check = true, bool force = true);
2220     extern void resetcamera(bool cam = true, bool input = true);
2221     extern void resetsway();
2222     extern void resetworld();
2223     extern void resetstate();
2224     extern void hiteffect(int weap, int flags, int damage, gameent *d, gameent *v, vec &dir, vec &vel, float dist, bool local = false);
2225     extern void damaged(int weap, int flags, int damage, int health, gameent *d, gameent *v, int millis, vec &dir, vec &vel, float dist);
2226     extern void killed(int weap, int flags, int damage, gameent *d, gameent *v, vector<gameent*> &log, int style, int material);
2227     extern void timeupdate(int state, int remain);
2228     extern void footstep(gameent *d, int curfoot = -1);
2229     extern bool canregenimpulse(gameent *d);
2230     #define RESIDUAL(name, type, pulse) extern void get##name##effect(physent *d, modelstate &mdl, int length, int millis, int delay);
2231     RESIDUALS
2232     #undef RESIDUAL
2233     extern void getplayermaterials(gameent *d, modelstate &mdl);
2234     extern void getplayereffects(gameent *d, modelstate &mdl);
2235     extern const char *getplayerstate(gameent *d, modelstate &mdl, int third = 1, float size = 1, int flags = 0, modelattach *mdlattach = NULL, int *lastoffset = NULL);
2236 }
2237 
2238 namespace entities
2239 {
2240     extern int showentdescs, showentfull, showentweapons, simpleitems;
2241     extern float showentavailable, showentunavailable;
2242     extern bool execitem(int n, int cn, dynent *d, vec &pos, float dist);
2243     extern bool collateitems(dynent *d, vec &pos, float radius, vector<actitem> &actitems);
2244     extern void checkitems(dynent *d);
2245     extern void putitems(packetbuf &p);
2246     extern void setspawn(int n, int m);
2247     extern bool tryspawn(dynent *d, const vec &o, float yaw = 0, float pitch = 0);
2248     extern void spawnplayer(gameent *d, int ent = -1, bool suicide = false);
2249     extern void useeffects(gameent *d, int cn, int ent, int ammoamt, bool spawn, int weap, int drop, int ammo = -1);
2250     extern void adddynlights();
2251     extern void render();
2252     extern void update();
2253 }
2254 #endif
2255 #include "capture.h"
2256 #include "defend.h"
2257 #include "bomber.h"
2258 
2259 #endif
2260