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