1 #ifndef __GAME_H__
2 #define __GAME_H__
3 
4 #include "cube.h"
5 
6 // console message types
7 
8 enum
9 {
10     CON_CHAT       = 1<<8,
11     CON_TEAMCHAT   = 1<<9,
12     CON_GAMEINFO   = 1<<10,
13     CON_FRAG_SELF  = 1<<11,
14     CON_FRAG_OTHER = 1<<12,
15     CON_TEAMKILL   = 1<<13
16 };
17 
18 // network quantization scale
19 #define DMF 16.0f                // for world locations
20 #define DNF 100.0f              // for normalized vectors
21 #define DVELF 1.0f              // for playerspeed based velocity vectors
22 
23 enum                            // static entity types
24 {
25     NOTUSED = ET_EMPTY,         // entity slot not in use in map
26     LIGHT = ET_LIGHT,           // lightsource, attr1 = radius, attr2 = intensity
27     MAPMODEL = ET_MAPMODEL,     // attr1 = angle, attr2 = idx
28     PLAYERSTART,                // attr1 = angle, attr2 = team
29     ENVMAP = ET_ENVMAP,         // attr1 = radius
30     PARTICLES = ET_PARTICLES,
31     MAPSOUND = ET_SOUND,
32     SPOTLIGHT = ET_SPOTLIGHT,
33     I_SHELLS, I_BULLETS, I_ROCKETS, I_ROUNDS, I_GRENADES, I_CARTRIDGES,
34     I_HEALTH, I_BOOST,
35     I_GREENARMOUR, I_YELLOWARMOUR,
36     I_QUAD,
37     TELEPORT,                   // attr1 = idx, attr2 = model, attr3 = tag
38     TELEDEST,                   // attr1 = angle, attr2 = idx
39     MONSTER,                    // attr1 = angle, attr2 = monstertype
40     CARROT,                     // attr1 = tag, attr2 = type
41     JUMPPAD,                    // attr1 = zpush, attr2 = ypush, attr3 = xpush
42     BASE,
43     RESPAWNPOINT,
44     BOX,                        // attr1 = angle, attr2 = idx, attr3 = weight
45     BARREL,                     // attr1 = angle, attr2 = idx, attr3 = weight, attr4 = health
46     PLATFORM,                   // attr1 = angle, attr2 = idx, attr3 = tag, attr4 = speed
47     ELEVATOR,                   // attr1 = angle, attr2 = idx, attr3 = tag, attr4 = speed
48     FLAG,                       // attr1 = angle, attr2 = team
49     MAXENTTYPES
50 };
51 
52 enum
53 {
54     TRIGGER_RESET = 0,
55     TRIGGERING,
56     TRIGGERED,
57     TRIGGER_RESETTING,
58     TRIGGER_DISAPPEARED
59 };
60 
61 struct fpsentity : extentity
62 {
63     int triggerstate, lasttrigger;
64 
fpsentityfpsentity65     fpsentity() : triggerstate(TRIGGER_RESET), lasttrigger(0) {}
66 };
67 
68 enum { GUN_FIST = 0, GUN_SG, GUN_CG, GUN_RL, GUN_RIFLE, GUN_GL, GUN_PISTOL, GUN_FIREBALL, GUN_ICEBALL, GUN_SLIMEBALL, GUN_BITE, GUN_BARREL, NUMGUNS };
69 enum { A_BLUE, A_GREEN, A_YELLOW };     // armour types... take 20/40/60 % off
70 enum { M_NONE = 0, M_SEARCH, M_HOME, M_ATTACKING, M_PAIN, M_SLEEP, M_AIMING };  // monster states
71 
72 enum
73 {
74     M_TEAM       = 1<<0,
75     M_NOITEMS    = 1<<1,
76     M_NOAMMO     = 1<<2,
77     M_INSTA      = 1<<3,
78     M_EFFICIENCY = 1<<4,
79     M_TACTICS    = 1<<5,
80     M_CAPTURE    = 1<<6,
81     M_REGEN      = 1<<7,
82     M_CTF        = 1<<8,
83     M_PROTECT    = 1<<9,
84     M_HOLD       = 1<<10,
85     M_EDIT       = 1<<12,
86     M_DEMO       = 1<<13,
87     M_LOCAL      = 1<<14,
88     M_LOBBY      = 1<<15,
89     M_DMSP       = 1<<16,
90     M_CLASSICSP  = 1<<17,
91     M_SLOWMO     = 1<<18,
92     M_COLLECT    = 1<<19
93 };
94 
95 static struct gamemodeinfo
96 {
97     const char *name;
98     int flags;
99     const char *info;
100 } gamemodes[] =
101 {
102     { "SP", M_LOCAL | M_CLASSICSP, NULL },
103     { "DMSP", M_LOCAL | M_DMSP, NULL },
104     { "demo", M_DEMO | M_LOCAL, NULL},
105     { "ffa", M_LOBBY, "Free For All: Collect items for ammo. Frag everyone to score points." },
106     { "coop edit", M_EDIT, "Cooperative Editing: Edit maps with multiple players simultaneously." },
107     { "teamplay", M_TEAM, "Teamplay: Collect items for ammo. Frag \fs\f3the enemy team\fr to score points for \fs\f1your team\fr." },
108     { "instagib", M_NOITEMS | M_INSTA, "Instagib: You spawn with full rifle ammo and die instantly from one shot. There are no items. Frag everyone to score points." },
109     { "insta team", M_NOITEMS | M_INSTA | M_TEAM, "Instagib Team: You spawn with full rifle ammo and die instantly from one shot. There are no items. Frag \fs\f3the enemy team\fr to score points for \fs\f1your team\fr." },
110     { "efficiency", M_NOITEMS | M_EFFICIENCY, "Efficiency: You spawn with all weapons and armour. There are no items. Frag everyone to score points." },
111     { "effic team", M_NOITEMS | M_EFFICIENCY | M_TEAM, "Efficiency Team: You spawn with all weapons and armour. There are no items. Frag \fs\f3the enemy team\fr to score points for \fs\f1your team\fr." },
112     { "tactics", M_NOITEMS | M_TACTICS, "Tactics: You spawn with two random weapons and armour. There are no items. Frag everyone to score points." },
113     { "tac team", M_NOITEMS | M_TACTICS | M_TEAM, "Tactics Team: You spawn with two random weapons and armour. There are no items. Frag \fs\f3the enemy team\fr to score points for \fs\f1your team\fr." },
114     { "capture", M_NOAMMO | M_TACTICS | M_CAPTURE | M_TEAM, "Capture: Capture neutral bases or steal \fs\f3enemy bases\fr by standing next to them.  \fs\f1Your team\fr scores points for every 10 seconds it holds a base. You spawn with two random weapons and armour. Collect extra ammo that spawns at \fs\f1your bases\fr. There are no ammo items." },
115     { "regen capture", M_NOITEMS | M_CAPTURE | M_REGEN | M_TEAM, "Regen Capture: Capture neutral bases or steal \fs\f3enemy bases\fr by standing next to them. \fs\f1Your team\fr scores points for every 10 seconds it holds a base. Regenerate health and ammo by standing next to \fs\f1your bases\fr. There are no items." },
116     { "ctf", M_CTF | M_TEAM, "Capture The Flag: Capture \fs\f3the enemy flag\fr and bring it back to \fs\f1your flag\fr to score points for \fs\f1your team\fr. Collect items for ammo." },
117     { "insta ctf", M_NOITEMS | M_INSTA | M_CTF | M_TEAM, "Instagib Capture The Flag: Capture \fs\f3the enemy flag\fr and bring it back to \fs\f1your flag\fr to score points for \fs\f1your team\fr. You spawn with full rifle ammo and die instantly from one shot. There are no items." },
118     { "protect", M_CTF | M_PROTECT | M_TEAM, "Protect The Flag: Touch \fs\f3the enemy flag\fr to score points for \fs\f1your team\fr. Pick up \fs\f1your flag\fr to protect it. \fs\f1Your team\fr loses points if a dropped flag resets. Collect items for ammo." },
119     { "insta protect", M_NOITEMS | M_INSTA | M_CTF | M_PROTECT | M_TEAM, "Instagib Protect The Flag: Touch \fs\f3the enemy flag\fr to score points for \fs\f1your team\fr. Pick up \fs\f1your flag\fr to protect it. \fs\f1Your team\fr loses points if a dropped flag resets. You spawn with full rifle ammo and die instantly from one shot. There are no items." },
120     { "hold", M_CTF | M_HOLD | M_TEAM, "Hold The Flag: Hold \fs\f7the flag\fr for 20 seconds to score points for \fs\f1your team\fr. Collect items for ammo." },
121     { "insta hold", M_NOITEMS | M_INSTA | M_CTF | M_HOLD | M_TEAM, "Instagib Hold The Flag: Hold \fs\f7the flag\fr for 20 seconds to score points for \fs\f1your team\fr. You spawn with full rifle ammo and die instantly from one shot. There are no items." },
122     { "effic ctf", M_NOITEMS | M_EFFICIENCY | M_CTF | M_TEAM, "Efficiency Capture The Flag: Capture \fs\f3the enemy flag\fr and bring it back to \fs\f1your flag\fr to score points for \fs\f1your team\fr. You spawn with all weapons and armour. There are no items." },
123     { "effic protect", M_NOITEMS | M_EFFICIENCY | M_CTF | M_PROTECT | M_TEAM, "Efficiency Protect The Flag: Touch \fs\f3the enemy flag\fr to score points for \fs\f1your team\fr. Pick up \fs\f1your flag\fr to protect it. \fs\f1Your team\fr loses points if a dropped flag resets. You spawn with all weapons and armour. There are no items." },
124     { "effic hold", M_NOITEMS | M_EFFICIENCY | M_CTF | M_HOLD | M_TEAM, "Efficiency Hold The Flag: Hold \fs\f7the flag\fr for 20 seconds to score points for \fs\f1your team\fr. You spawn with all weapons and armour. There are no items." },
125     { "collect", M_COLLECT | M_TEAM, "Skull Collector: Frag \fs\f3the enemy team\fr to drop \fs\f3skulls\fr. Collect them and bring them to \fs\f3the enemy base\fr to score points for \fs\f1your team\fr or steal back \fs\f1your skulls\fr. Collect items for ammo." },
126     { "insta collect", M_NOITEMS | M_INSTA | M_COLLECT | M_TEAM, "Instagib Skull Collector: Frag \fs\f3the enemy team\fr to drop \fs\f3skulls\fr. Collect them and bring them to \fs\f3the enemy base\fr to score points for \fs\f1your team\fr or steal back \fs\f1your skulls\fr. You spawn with full rifle ammo and die instantly from one shot. There are no items." },
127     { "effic collect", M_NOITEMS | M_EFFICIENCY | M_COLLECT | M_TEAM, "Efficiency Skull Collector: Frag \fs\f3the enemy team\fr to drop \fs\f3skulls\fr. Collect them and bring them to \fs\f3the enemy base\fr to score points for \fs\f1your team\fr or steal back \fs\f1your skulls\fr. You spawn with all weapons and armour. There are no items." }
128 };
129 
130 #define STARTGAMEMODE (-3)
131 #define NUMGAMEMODES ((int)(sizeof(gamemodes)/sizeof(gamemodes[0])))
132 
133 #define m_valid(mode)          ((mode) >= STARTGAMEMODE && (mode) < STARTGAMEMODE + NUMGAMEMODES)
134 #define m_check(mode, flag)    (m_valid(mode) && gamemodes[(mode) - STARTGAMEMODE].flags&(flag))
135 #define m_checknot(mode, flag) (m_valid(mode) && !(gamemodes[(mode) - STARTGAMEMODE].flags&(flag)))
136 #define m_checkall(mode, flag) (m_valid(mode) && (gamemodes[(mode) - STARTGAMEMODE].flags&(flag)) == (flag))
137 #define m_checkonly(mode, flag, exclude) (m_valid(mode) && (gamemodes[(mode) - STARTGAMEMODE].flags&((flag)|(exclude))) == (flag))
138 
139 #define m_noitems      (m_check(gamemode, M_NOITEMS))
140 #define m_noammo       (m_check(gamemode, M_NOAMMO|M_NOITEMS))
141 #define m_insta        (m_check(gamemode, M_INSTA))
142 #define m_tactics      (m_check(gamemode, M_TACTICS))
143 #define m_efficiency   (m_check(gamemode, M_EFFICIENCY))
144 #define m_capture      (m_check(gamemode, M_CAPTURE))
145 #define m_capture_only (m_checkonly(gamemode, M_CAPTURE, M_REGEN))
146 #define m_regencapture (m_checkall(gamemode, M_CAPTURE | M_REGEN))
147 #define m_ctf          (m_check(gamemode, M_CTF))
148 #define m_ctf_only     (m_checkonly(gamemode, M_CTF, M_PROTECT | M_HOLD))
149 #define m_protect      (m_checkall(gamemode, M_CTF | M_PROTECT))
150 #define m_hold         (m_checkall(gamemode, M_CTF | M_HOLD))
151 #define m_collect      (m_check(gamemode, M_COLLECT))
152 #define m_teammode     (m_check(gamemode, M_TEAM))
153 #define isteam(a,b)    (m_teammode && strcmp(a, b)==0)
154 
155 #define m_demo         (m_check(gamemode, M_DEMO))
156 #define m_edit         (m_check(gamemode, M_EDIT))
157 #define m_lobby        (m_check(gamemode, M_LOBBY))
158 #define m_timed        (m_checknot(gamemode, M_DEMO|M_EDIT|M_LOCAL))
159 #define m_botmode      (m_checknot(gamemode, M_DEMO|M_LOCAL))
160 #define m_mp(mode)     (m_checknot(mode, M_LOCAL))
161 
162 #define m_sp           (m_check(gamemode, M_DMSP | M_CLASSICSP))
163 #define m_dmsp         (m_check(gamemode, M_DMSP))
164 #define m_classicsp    (m_check(gamemode, M_CLASSICSP))
165 
166 enum { MM_AUTH = -1, MM_OPEN = 0, MM_VETO, MM_LOCKED, MM_PRIVATE, MM_PASSWORD, MM_START = MM_AUTH };
167 
168 static const char * const mastermodenames[] =  { "auth",   "open",   "veto",       "locked",     "private",    "password" };
169 static const char * const mastermodecolors[] = { "",       "\f0",    "\f2",        "\f2",        "\f3",        "\f3" };
170 static const char * const mastermodeicons[] =  { "server", "server", "serverlock", "serverlock", "serverpriv", "serverpriv" };
171 
172 // hardcoded sounds, defined in sounds.cfg
173 enum
174 {
175     S_JUMP = 0, S_LAND, S_RIFLE, S_PUNCH1, S_SG, S_CG,
176     S_RLFIRE, S_RLHIT, S_WEAPLOAD, S_ITEMAMMO, S_ITEMHEALTH,
177     S_ITEMARMOUR, S_ITEMPUP, S_ITEMSPAWN, S_TELEPORT, S_NOAMMO, S_PUPOUT,
178     S_PAIN1, S_PAIN2, S_PAIN3, S_PAIN4, S_PAIN5, S_PAIN6,
179     S_DIE1, S_DIE2,
180     S_FLAUNCH, S_FEXPLODE,
181     S_SPLASH1, S_SPLASH2,
182     S_GRUNT1, S_GRUNT2, S_RUMBLE,
183     S_PAINO,
184     S_PAINR, S_DEATHR,
185     S_PAINE, S_DEATHE,
186     S_PAINS, S_DEATHS,
187     S_PAINB, S_DEATHB,
188     S_PAINP, S_PIGGR2,
189     S_PAINH, S_DEATHH,
190     S_PAIND, S_DEATHD,
191     S_PIGR1, S_ICEBALL, S_SLIMEBALL,
192     S_JUMPPAD, S_PISTOL,
193 
194     S_V_BASECAP, S_V_BASELOST,
195     S_V_FIGHT,
196     S_V_BOOST, S_V_BOOST10,
197     S_V_QUAD, S_V_QUAD10,
198     S_V_RESPAWNPOINT,
199 
200     S_FLAGPICKUP,
201     S_FLAGDROP,
202     S_FLAGRETURN,
203     S_FLAGSCORE,
204     S_FLAGRESET,
205 
206     S_BURN,
207     S_CHAINSAW_ATTACK,
208     S_CHAINSAW_IDLE,
209 
210     S_HIT,
211 
212     S_FLAGFAIL
213 };
214 
215 // network messages codes, c2s, c2c, s2c
216 
217 enum { PRIV_NONE = 0, PRIV_MASTER, PRIV_AUTH, PRIV_ADMIN };
218 
219 enum
220 {
221     N_CONNECT = 0, N_SERVINFO, N_WELCOME, N_INITCLIENT, N_POS, N_TEXT, N_SOUND, N_CDIS,
222     N_SHOOT, N_EXPLODE, N_SUICIDE,
223     N_DIED, N_DAMAGE, N_HITPUSH, N_SHOTFX, N_EXPLODEFX,
224     N_TRYSPAWN, N_SPAWNSTATE, N_SPAWN, N_FORCEDEATH,
225     N_GUNSELECT, N_TAUNT,
226     N_MAPCHANGE, N_MAPVOTE, N_TEAMINFO, N_ITEMSPAWN, N_ITEMPICKUP, N_ITEMACC, N_TELEPORT, N_JUMPPAD,
227     N_PING, N_PONG, N_CLIENTPING,
228     N_TIMEUP, N_FORCEINTERMISSION,
229     N_SERVMSG, N_ITEMLIST, N_RESUME,
230     N_EDITMODE, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_REMIP, N_EDITVSLOT, N_UNDO, N_REDO, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, N_EDITVAR,
231     N_MASTERMODE, N_KICK, N_CLEARBANS, N_CURRENTMASTER, N_SPECTATOR, N_SETMASTER, N_SETTEAM,
232     N_BASES, N_BASEINFO, N_BASESCORE, N_REPAMMO, N_BASEREGEN, N_ANNOUNCE,
233     N_LISTDEMOS, N_SENDDEMOLIST, N_GETDEMO, N_SENDDEMO,
234     N_DEMOPLAYBACK, N_RECORDDEMO, N_STOPDEMO, N_CLEARDEMOS,
235     N_TAKEFLAG, N_RETURNFLAG, N_RESETFLAG, N_INVISFLAG, N_TRYDROPFLAG, N_DROPFLAG, N_SCOREFLAG, N_INITFLAGS,
236     N_SAYTEAM,
237     N_CLIENT,
238     N_AUTHTRY, N_AUTHKICK, N_AUTHCHAL, N_AUTHANS, N_REQAUTH,
239     N_PAUSEGAME, N_GAMESPEED,
240     N_ADDBOT, N_DELBOT, N_INITAI, N_FROMAI, N_BOTLIMIT, N_BOTBALANCE,
241     N_MAPCRC, N_CHECKMAPS,
242     N_SWITCHNAME, N_SWITCHMODEL, N_SWITCHTEAM,
243     N_INITTOKENS, N_TAKETOKEN, N_EXPIRETOKENS, N_DROPTOKENS, N_DEPOSITTOKENS, N_STEALTOKENS,
244     N_SERVCMD,
245     N_DEMOPACKET,
246     NUMMSG
247 };
248 
249 static const int msgsizes[] =               // size inclusive message token, 0 for variable or not-checked sizes
250 {
251     N_CONNECT, 0, N_SERVINFO, 0, N_WELCOME, 1, N_INITCLIENT, 0, N_POS, 0, N_TEXT, 0, N_SOUND, 2, N_CDIS, 2,
252     N_SHOOT, 0, N_EXPLODE, 0, N_SUICIDE, 1,
253     N_DIED, 5, N_DAMAGE, 6, N_HITPUSH, 7, N_SHOTFX, 10, N_EXPLODEFX, 4,
254     N_TRYSPAWN, 1, N_SPAWNSTATE, 14, N_SPAWN, 3, N_FORCEDEATH, 2,
255     N_GUNSELECT, 2, N_TAUNT, 1,
256     N_MAPCHANGE, 0, N_MAPVOTE, 0, N_TEAMINFO, 0, N_ITEMSPAWN, 2, N_ITEMPICKUP, 2, N_ITEMACC, 3,
257     N_PING, 2, N_PONG, 2, N_CLIENTPING, 2,
258     N_TIMEUP, 2, N_FORCEINTERMISSION, 1,
259     N_SERVMSG, 0, N_ITEMLIST, 0, N_RESUME, 0,
260     N_EDITMODE, 2, N_EDITENT, 11, N_EDITF, 16, N_EDITT, 16, N_EDITM, 16, N_FLIP, 14, N_COPY, 14, N_PASTE, 14, N_ROTATE, 15, N_REPLACE, 17, N_DELCUBE, 14, N_REMIP, 1, N_EDITVSLOT, 16, N_UNDO, 0, N_REDO, 0, N_NEWMAP, 2, N_GETMAP, 1, N_SENDMAP, 0, N_EDITVAR, 0,
261     N_MASTERMODE, 2, N_KICK, 0, N_CLEARBANS, 1, N_CURRENTMASTER, 0, N_SPECTATOR, 3, N_SETMASTER, 0, N_SETTEAM, 0,
262     N_BASES, 0, N_BASEINFO, 0, N_BASESCORE, 0, N_REPAMMO, 1, N_BASEREGEN, 6, N_ANNOUNCE, 2,
263     N_LISTDEMOS, 1, N_SENDDEMOLIST, 0, N_GETDEMO, 3, N_SENDDEMO, 0,
264     N_DEMOPLAYBACK, 3, N_RECORDDEMO, 2, N_STOPDEMO, 1, N_CLEARDEMOS, 2,
265     N_TAKEFLAG, 3, N_RETURNFLAG, 4, N_RESETFLAG, 6, N_INVISFLAG, 3, N_TRYDROPFLAG, 1, N_DROPFLAG, 7, N_SCOREFLAG, 10, N_INITFLAGS, 0,
266     N_SAYTEAM, 0,
267     N_CLIENT, 0,
268     N_AUTHTRY, 0, N_AUTHKICK, 0, N_AUTHCHAL, 0, N_AUTHANS, 0, N_REQAUTH, 0,
269     N_PAUSEGAME, 0, N_GAMESPEED, 0,
270     N_ADDBOT, 2, N_DELBOT, 1, N_INITAI, 0, N_FROMAI, 2, N_BOTLIMIT, 2, N_BOTBALANCE, 2,
271     N_MAPCRC, 0, N_CHECKMAPS, 1,
272     N_SWITCHNAME, 0, N_SWITCHMODEL, 2, N_SWITCHTEAM, 0,
273     N_INITTOKENS, 0, N_TAKETOKEN, 2, N_EXPIRETOKENS, 0, N_DROPTOKENS, 0, N_DEPOSITTOKENS, 2, N_STEALTOKENS, 0,
274     N_SERVCMD, 0,
275     N_DEMOPACKET, 0,
276     -1
277 };
278 
279 #define SAUERBRATEN_LANINFO_PORT 28784
280 #define SAUERBRATEN_SERVER_PORT 28785
281 #define SAUERBRATEN_SERVINFO_PORT 28786
282 #define SAUERBRATEN_MASTER_PORT 28787
283 #define PROTOCOL_VERSION 260            // bump when protocol changes
284 #define DEMO_VERSION 1                  // bump when demo format changes
285 #define DEMO_MAGIC "SAUERBRATEN_DEMO"
286 
287 struct demoheader
288 {
289     char magic[16];
290     int version, protocol;
291 };
292 
293 #define MAXNAMELEN 15
294 #define MAXTEAMLEN 4
295 
296 enum
297 {
298     HICON_BLUE_ARMOUR = 0,
299     HICON_GREEN_ARMOUR,
300     HICON_YELLOW_ARMOUR,
301 
302     HICON_HEALTH,
303 
304     HICON_FIST,
305     HICON_SG,
306     HICON_CG,
307     HICON_RL,
308     HICON_RIFLE,
309     HICON_GL,
310     HICON_PISTOL,
311 
312     HICON_QUAD,
313 
314     HICON_RED_FLAG,
315     HICON_BLUE_FLAG,
316     HICON_NEUTRAL_FLAG,
317 
318     HICON_TOKEN,
319 
320     HICON_X       = 20,
321     HICON_Y       = 1650,
322     HICON_TEXTY   = 1644,
323     HICON_STEP    = 490,
324     HICON_SIZE    = 120,
325     HICON_SPACE   = 40
326 };
327 
328 static struct itemstat { int add, max, sound; const char *name; int icon, info; } itemstats[] =
329 {
330     {10,    30,    S_ITEMAMMO,   "SG", HICON_SG, GUN_SG},
331     {20,    60,    S_ITEMAMMO,   "CG", HICON_CG, GUN_CG},
332     {5,     15,    S_ITEMAMMO,   "RL", HICON_RL, GUN_RL},
333     {5,     15,    S_ITEMAMMO,   "RI", HICON_RIFLE, GUN_RIFLE},
334     {10,    30,    S_ITEMAMMO,   "GL", HICON_GL, GUN_GL},
335     {30,    120,   S_ITEMAMMO,   "PI", HICON_PISTOL, GUN_PISTOL},
336     {25,    100,   S_ITEMHEALTH, "H",  HICON_HEALTH, -1},
337     {100,   200,   S_ITEMHEALTH, "MH", HICON_HEALTH, 50},
338     {100,   100,   S_ITEMARMOUR, "GA", HICON_GREEN_ARMOUR, A_GREEN},
339     {200,   200,   S_ITEMARMOUR, "YA", HICON_YELLOW_ARMOUR, A_YELLOW},
340     {20000, 30000, S_ITEMPUP,    "Q",  HICON_QUAD, -1},
341 };
342 
343 #define MAXRAYS 20
344 #define EXP_SELFDAMDIV 2
345 #define EXP_SELFPUSH 2.5f
346 #define EXP_DISTSCALE 1.5f
347 
348 static const struct guninfo { int sound, attackdelay, damage, spread, projspeed, kickamount, range, rays, hitpush, exprad, ttl; const char *name, *file; short part; } guns[NUMGUNS] =
349 {
350     { S_PUNCH1,    250,  50,   0,   0,  0,   14,  1,  80,  0,    0, "fist",            "fist",   0 },
351     { S_SG,       1400,  10, 400,   0, 20, 1024, 20,  80,  0,    0, "shotgun",         "shotg",  0 },
352     { S_CG,        100,  30, 100,   0,  7, 1024,  1,  80,  0,    0, "chaingun",        "chaing", 0 },
353     { S_RLFIRE,    800, 120,   0, 320, 10, 1024,  1, 160, 40,    0, "rocketlauncher",  "rocket", 0 },
354     { S_RIFLE,    1500, 100,   0,   0, 30, 2048,  1,  80,  0,    0, "rifle",           "rifle",  0 },
355     { S_FLAUNCH,   600,  90,   0, 200, 10, 1024,  1, 250, 45, 1500, "grenadelauncher", "gl",     0 },
356     { S_PISTOL,    500,  35,  50,   0,  7, 1024,  1,  80,  0,    0, "pistol",          "pistol", 0 },
357     { S_FLAUNCH,   200,  20,   0, 200,  1, 1024,  1,  80, 40,    0, "fireball",        NULL,     PART_FIREBALL1 },
358     { S_ICEBALL,   200,  40,   0, 120,  1, 1024,  1,  80, 40,    0, "iceball",         NULL,     PART_FIREBALL2 },
359     { S_SLIMEBALL, 200,  30,   0, 640,  1, 1024,  1,  80, 40,    0, "slimeball",       NULL,     PART_FIREBALL3 },
360     { S_PIGR1,     250,  50,   0,   0,  1,   12,  1,  80,  0,    0, "bite",            NULL,     0 },
361     { -1,            0, 120,   0,   0,  0,    0,  1,  80, 40,    0, "barrel",          NULL,     0 }
362 };
363 
364 #include "ai.h"
365 
366 // inherited by fpsent and server clients
367 struct fpsstate
368 {
369     int health, maxhealth;
370     int armour, armourtype;
371     int quadmillis;
372     int gunselect, gunwait;
373     int ammo[NUMGUNS];
374     int aitype, skill;
375 
fpsstatefpsstate376     fpsstate() : maxhealth(100), aitype(AI_NONE), skill(0) {}
377 
378     void baseammo(int gun, int k = 2, int scale = 1)
379     {
380         ammo[gun] = (itemstats[gun-GUN_SG].add*k)/scale;
381     }
382 
383     void addammo(int gun, int k = 1, int scale = 1)
384     {
385         itemstat &is = itemstats[gun-GUN_SG];
386         ammo[gun] = min(ammo[gun] + (is.add*k)/scale, is.max);
387     }
388 
hasmaxammofpsstate389     bool hasmaxammo(int type)
390     {
391        const itemstat &is = itemstats[type-I_SHELLS];
392        return ammo[type-I_SHELLS+GUN_SG]>=is.max;
393     }
394 
canpickupfpsstate395     bool canpickup(int type)
396     {
397         if(type<I_SHELLS || type>I_QUAD) return false;
398         itemstat &is = itemstats[type-I_SHELLS];
399         switch(type)
400         {
401             case I_BOOST: return maxhealth<is.max || health<maxhealth;
402             case I_HEALTH: return health<maxhealth;
403             case I_GREENARMOUR:
404                 // (100h/100g only absorbs 200 damage)
405                 if(armourtype==A_YELLOW && armour>=100) return false;
406             case I_YELLOWARMOUR: return !armourtype || armour<is.max;
407             case I_QUAD: return quadmillis<is.max;
408             default: return ammo[is.info]<is.max;
409         }
410     }
411 
pickupfpsstate412     void pickup(int type)
413     {
414         if(type<I_SHELLS || type>I_QUAD) return;
415         itemstat &is = itemstats[type-I_SHELLS];
416         switch(type)
417         {
418             case I_BOOST:
419                 maxhealth = min(maxhealth+is.info, is.max);
420             case I_HEALTH: // boost also adds to health
421                 health = min(health+is.add, maxhealth);
422                 break;
423             case I_GREENARMOUR:
424             case I_YELLOWARMOUR:
425                 armour = min(armour+is.add, is.max);
426                 armourtype = is.info;
427                 break;
428             case I_QUAD:
429                 quadmillis = min(quadmillis+is.add, is.max);
430                 break;
431             default:
432                 ammo[is.info] = min(ammo[is.info]+is.add, is.max);
433                 break;
434         }
435     }
436 
respawnfpsstate437     void respawn()
438     {
439         maxhealth = 100;
440         health = maxhealth;
441         armour = 0;
442         armourtype = A_BLUE;
443         quadmillis = 0;
444         gunselect = GUN_PISTOL;
445         gunwait = 0;
446         loopi(NUMGUNS) ammo[i] = 0;
447         ammo[GUN_FIST] = 1;
448     }
449 
spawnstatefpsstate450     void spawnstate(int gamemode)
451     {
452         if(m_demo)
453         {
454             gunselect = GUN_FIST;
455         }
456         else if(m_insta)
457         {
458             armour = 0;
459             health = 1;
460             gunselect = GUN_RIFLE;
461             ammo[GUN_RIFLE] = 100;
462         }
463         else if(m_regencapture)
464         {
465             extern int regenbluearmour;
466             if(regenbluearmour)
467             {
468                 armourtype = A_BLUE;
469                 armour = 25;
470             }
471             gunselect = GUN_PISTOL;
472             ammo[GUN_PISTOL] = 40;
473             ammo[GUN_GL] = 1;
474         }
475         else if(m_tactics)
476         {
477             armourtype = A_GREEN;
478             armour = 100;
479             ammo[GUN_PISTOL] = 40;
480             int spawngun1 = rnd(5)+1, spawngun2;
481             gunselect = spawngun1;
482             baseammo(spawngun1, m_noitems ? 2 : 1);
483             do spawngun2 = rnd(5)+1; while(spawngun1==spawngun2);
484             baseammo(spawngun2, m_noitems ? 2 : 1);
485             if(m_noitems) ammo[GUN_GL] += 1;
486         }
487         else if(m_efficiency)
488         {
489             armourtype = A_GREEN;
490             armour = 100;
491             loopi(5) baseammo(i+1);
492             gunselect = GUN_CG;
493             ammo[GUN_CG] /= 2;
494         }
495         else if(m_ctf || m_collect)
496         {
497             armourtype = A_BLUE;
498             armour = 50;
499             ammo[GUN_PISTOL] = 40;
500             ammo[GUN_GL] = 1;
501         }
502         else if(m_sp)
503         {
504             if(m_dmsp)
505             {
506                 armourtype = A_BLUE;
507                 armour = 25;
508             }
509             ammo[GUN_PISTOL] = 80;
510             ammo[GUN_GL] = 1;
511         }
512         else
513         {
514             armourtype = A_BLUE;
515             armour = 25;
516             ammo[GUN_PISTOL] = 40;
517             ammo[GUN_GL] = 1;
518         }
519     }
520 
521     // just subtract damage here, can set death, etc. later in code calling this
dodamagefpsstate522     int dodamage(int damage)
523     {
524         int ad = (damage*(armourtype+1)*25)/100; // let armour absorb when possible
525         if(ad>armour) ad = armour;
526         armour -= ad;
527         damage -= ad;
528         health -= damage;
529         return damage;
530     }
531 
532     int hasammo(int gun, int exclude = -1)
533     {
534         return gun >= 0 && gun <= NUMGUNS && gun != exclude && ammo[gun] > 0;
535     }
536 };
537 
538 struct fpsent : dynent, fpsstate
539 {
540     int weight;                         // affects the effectiveness of hitpush
541     int clientnum, privilege, lastupdate, plag, ping;
542     int lifesequence;                   // sequence id for each respawn, used in damage test
543     int respawned, suicided;
544     int lastpain;
545     int lastaction, lastattackgun;
546     bool attacking;
547     int attacksound, attackchan, idlesound, idlechan;
548     int lasttaunt;
549     int lastpickup, lastpickupmillis, lastbase, lastrepammo, flagpickup, tokens;
550     vec lastcollect;
551     int frags, flags, deaths, totaldamage, totalshots;
552     editinfo *edit;
553     float deltayaw, deltapitch, deltaroll, newyaw, newpitch, newroll;
554     int smoothmillis;
555 
556     string name, team, info;
557     int playermodel;
558     ai::aiinfo *ai;
559     int ownernum, lastnode;
560 
561     vec muzzle;
562 
fpsentfpsent563     fpsent() : weight(100), clientnum(-1), privilege(PRIV_NONE), lastupdate(0), plag(0), ping(0), lifesequence(0), respawned(-1), suicided(-1), lastpain(0), attacksound(-1), attackchan(-1), idlesound(-1), idlechan(-1), frags(0), flags(0), deaths(0), totaldamage(0), totalshots(0), edit(NULL), smoothmillis(-1), playermodel(-1), ai(NULL), ownernum(-1), muzzle(-1, -1, -1)
564     {
565         name[0] = team[0] = info[0] = 0;
566         respawn();
567     }
~fpsentfpsent568     ~fpsent()
569     {
570         freeeditinfo(edit);
571         if(attackchan >= 0) stopsound(attacksound, attackchan);
572         if(idlechan >= 0) stopsound(idlesound, idlechan);
573         if(ai) delete ai;
574     }
575 
hitpushfpsent576     void hitpush(int damage, const vec &dir, fpsent *actor, int gun)
577     {
578         vec push(dir);
579         push.mul((actor==this && guns[gun].exprad ? EXP_SELFPUSH : 1.0f)*guns[gun].hitpush*damage/weight);
580         vel.add(push);
581     }
582 
stopattacksoundfpsent583     void stopattacksound()
584     {
585         if(attackchan >= 0) stopsound(attacksound, attackchan, 250);
586         attacksound = attackchan = -1;
587     }
588 
stopidlesoundfpsent589     void stopidlesound()
590     {
591         if(idlechan >= 0) stopsound(idlesound, idlechan, 100);
592         idlesound = idlechan = -1;
593     }
594 
respawnfpsent595     void respawn()
596     {
597         dynent::reset();
598         fpsstate::respawn();
599         respawned = suicided = -1;
600         lastaction = 0;
601         lastattackgun = gunselect;
602         attacking = false;
603         lasttaunt = 0;
604         lastpickup = -1;
605         lastpickupmillis = 0;
606         lastbase = lastrepammo = -1;
607         flagpickup = 0;
608         tokens = 0;
609         lastcollect = vec(-1e10f, -1e10f, -1e10f);
610         stopattacksound();
611         lastnode = -1;
612     }
613 
614     int respawnwait(int secs, int delay = 0)
615     {
616         return max(0, secs - (::lastmillis - lastpain - delay)/1000);
617     }
618 };
619 
620 struct teamscore
621 {
622     const char *team;
623     int score;
teamscoreteamscore624     teamscore() {}
teamscoreteamscore625     teamscore(const char *s, int n) : team(s), score(n) {}
626 
compareteamscore627     static bool compare(const teamscore &x, const teamscore &y)
628     {
629         if(x.score > y.score) return true;
630         if(x.score < y.score) return false;
631         return strcmp(x.team, y.team) < 0;
632     }
633 };
634 
hthash(const teamscore & t)635 static inline uint hthash(const teamscore &t) { return hthash(t.team); }
htcmp(const char * key,const teamscore & t)636 static inline bool htcmp(const char *key, const teamscore &t) { return htcmp(key, t.team); }
637 
638 #define MAXTEAMS 128
639 
640 struct teaminfo
641 {
642     char team[MAXTEAMLEN+1];
643     int frags;
644 };
645 
hthash(const teaminfo & t)646 static inline uint hthash(const teaminfo &t) { return hthash(t.team); }
htcmp(const char * team,const teaminfo & t)647 static inline bool htcmp(const char *team, const teaminfo &t) { return !strcmp(team, t.team); }
648 
649 namespace entities
650 {
651     extern vector<extentity *> ents;
652 
653     extern const char *entmdlname(int type);
654     extern const char *itemname(int i);
655     extern int itemicon(int i);
656 
657     extern void preloadentities();
658     extern void renderentities();
659     extern void resettriggers();
660     extern void checktriggers();
661     extern void checkitems(fpsent *d);
662     extern void checkquad(int time, fpsent *d);
663     extern void resetspawns();
664     extern void spawnitems(bool force = false);
665     extern void putitems(packetbuf &p);
666     extern void setspawn(int i, bool on);
667     extern void teleport(int n, fpsent *d);
668     extern void pickupeffects(int n, fpsent *d);
669     extern void teleporteffects(fpsent *d, int tp, int td, bool local = true);
670     extern void jumppadeffects(fpsent *d, int jp, bool local = true);
671 
672     extern void repammo(fpsent *d, int type, bool local = true);
673 }
674 
675 namespace game
676 {
677     struct clientmode
678     {
~clientmodeclientmode679         virtual ~clientmode() {}
680 
preloadclientmode681         virtual void preload() {}
clipconsoleclientmode682         virtual int clipconsole(int w, int h) { return 0; }
drawhudclientmode683         virtual void drawhud(fpsent *d, int w, int h) {}
rendergameclientmode684         virtual void rendergame() {}
respawnedclientmode685         virtual void respawned(fpsent *d) {}
setupclientmode686         virtual void setup() {}
checkitemsclientmode687         virtual void checkitems(fpsent *d) {}
688         virtual int respawnwait(fpsent *d, int delay = 0) { return 0; }
getspawngroupclientmode689         virtual int getspawngroup(fpsent *d) { return 0; }
ratespawnclientmode690         virtual float ratespawn(fpsent *d, const extentity &e) { return 1.0f; }
senditemsclientmode691         virtual void senditems(packetbuf &p) {}
removeplayerclientmode692         virtual void removeplayer(fpsent *d) {}
diedclientmode693         virtual void died(fpsent *victim, fpsent *actor) {}
gameoverclientmode694         virtual void gameover() {}
hidefragsclientmode695         virtual bool hidefrags() { return false; }
getteamscoreclientmode696         virtual int getteamscore(const char *team) { return 0; }
getteamscoresclientmode697         virtual void getteamscores(vector<teamscore> &scores) {}
aifindclientmode698         virtual void aifind(fpsent *d, ai::aistate &b, vector<ai::interest> &interests) {}
aicheckclientmode699         virtual bool aicheck(fpsent *d, ai::aistate &b) { return false; }
aidefendclientmode700         virtual bool aidefend(fpsent *d, ai::aistate &b) { return false; }
aipursueclientmode701         virtual bool aipursue(fpsent *d, ai::aistate &b) { return false; }
702     };
703 
704     extern clientmode *cmode;
705     extern void setclientmode();
706 
707     // fps
708     extern int gamemode, nextmode;
709     extern string clientmap;
710     extern bool intermission;
711     extern int maptime, maprealtime, maplimit;
712     extern fpsent *player1;
713     extern vector<fpsent *> players, clients;
714     extern int lastspawnattempt;
715     extern int lasthit;
716     extern int respawnent;
717     extern int following;
718     extern int smoothmove, smoothdist;
719 
720     extern bool clientoption(const char *arg);
721     extern fpsent *getclient(int cn);
722     extern fpsent *newclient(int cn);
723     extern const char *colorname(fpsent *d, const char *name = NULL, const char *prefix = "", const char *suffix = "", const char *alt = NULL);
724     extern const char *teamcolorname(fpsent *d, const char *alt = "you");
725     extern const char *teamcolor(const char *name, bool sameteam, const char *alt = NULL);
726     extern const char *teamcolor(const char *name, const char *team, const char *alt = NULL);
727     extern void teamsound(bool sameteam, int n, const vec *loc = NULL);
728     extern void teamsound(fpsent *d, int n, const vec *loc = NULL);
729     extern fpsent *pointatplayer();
730     extern fpsent *hudplayer();
731     extern fpsent *followingplayer(fpsent *fallback = NULL);
732     extern void stopfollowing();
733     extern void clientdisconnected(int cn, bool notify = true);
734     extern void clearclients(bool notify = true);
735     extern void startgame();
736     extern float proximityscore(float x, float lower, float upper);
737     extern void pickgamespawn(fpsent *d);
738     extern void spawnplayer(fpsent *d);
739     extern void deathstate(fpsent *d, bool restore = false);
740     extern void damaged(int damage, fpsent *d, fpsent *actor, bool local = true);
741     extern void killed(fpsent *d, fpsent *actor);
742     extern void timeupdate(int timeremain);
743     extern void msgsound(int n, physent *d = NULL);
744     extern void drawicon(int icon, float x, float y, float sz = 120);
745     const char *mastermodecolor(int n, const char *unknown);
746     const char *mastermodeicon(int n, const char *unknown);
747 
748     // client
749     extern bool connected, remote, demoplayback;
750     extern string servinfo;
751     extern vector<uchar> messages;
752 
753     extern int parseplayer(const char *arg);
754     extern void ignore(int cn);
755     extern void unignore(int cn);
756     extern bool isignored(int cn);
757     extern bool addmsg(int type, const char *fmt = NULL, ...);
758     extern void switchname(const char *name);
759     extern void switchteam(const char *name);
760     extern void switchplayermodel(int playermodel);
761     extern void sendmapinfo();
762     extern void stopdemo();
763     extern void changemap(const char *name, int mode);
764     extern void forceintermission();
765     extern void c2sinfo(bool force = false);
766     extern void sendposition(fpsent *d, bool reliable = false);
767 
768     // monster
769     struct monster;
770     extern vector<monster *> monsters;
771 
772     extern void clearmonsters();
773     extern void preloadmonsters();
774     extern void stackmonster(monster *d, physent *o);
775     extern void updatemonsters(int curtime);
776     extern void rendermonsters();
777     extern void suicidemonster(monster *m);
778     extern void hitmonster(int damage, monster *m, fpsent *at, const vec &vel, int gun);
779     extern void monsterkilled();
780     extern void endsp(bool allkilled);
781     extern void spsummary(int accuracy);
782 
783     // movable
784     struct movable;
785     extern vector<movable *> movables;
786 
787     extern void clearmovables();
788     extern void stackmovable(movable *d, physent *o);
789     extern void updatemovables(int curtime);
790     extern void rendermovables();
791     extern void suicidemovable(movable *m);
792     extern void hitmovable(int damage, movable *m, fpsent *at, const vec &vel, int gun);
793 
794     // weapon
795     extern int getweapon(const char *name);
796     extern void shoot(fpsent *d, const vec &targ);
797     extern void shoteffects(int gun, const vec &from, const vec &to, fpsent *d, bool local, int id, int prevaction);
798     extern void explode(bool local, fpsent *owner, const vec &v, dynent *safe, int dam, int gun);
799     extern void explodeeffects(int gun, fpsent *d, bool local, int id = 0);
800     extern void damageeffect(int damage, fpsent *d, bool thirdperson = true);
801     extern void gibeffect(int damage, const vec &vel, fpsent *d);
802     extern float intersectdist;
803     extern bool intersect(dynent *d, const vec &from, const vec &to, float &dist = intersectdist);
804     extern dynent *intersectclosest(const vec &from, const vec &to, fpsent *at, float &dist = intersectdist);
805     extern void clearbouncers();
806     extern void updatebouncers(int curtime);
807     extern void removebouncers(fpsent *owner);
808     extern void renderbouncers();
809     extern void clearprojectiles();
810     extern void updateprojectiles(int curtime);
811     extern void removeprojectiles(fpsent *owner);
812     extern void renderprojectiles();
813     extern void preloadbouncers();
814     extern void removeweapons(fpsent *owner);
815     extern void updateweapons(int curtime);
816     extern void gunselect(int gun, fpsent *d);
817     extern void weaponswitch(fpsent *d);
818     extern void avoidweapons(ai::avoidset &obstacles, float radius);
819 
820     // scoreboard
821     extern void showscores(bool on);
822     extern void getbestplayers(vector<fpsent *> &best);
823     extern void getbestteams(vector<const char *> &best);
824     extern void clearteaminfo();
825     extern void setteaminfo(const char *team, int frags);
826     extern int statuscolor(fpsent *d, int color);
827 
828     // render
829     struct playermodelinfo
830     {
831         const char *ffa, *blueteam, *redteam, *hudguns,
832                    *vwep, *quad, *armour[3],
833                    *ffaicon, *blueicon, *redicon;
834         bool ragdoll;
835     };
836 
837     extern int playermodel, teamskins, testteam;
838 
839     extern void saveragdoll(fpsent *d);
840     extern void clearragdolls();
841     extern void moveragdolls();
842     extern void changedplayermodel();
843     extern const playermodelinfo &getplayermodelinfo(fpsent *d);
844     extern int chooserandomplayermodel(int seed);
845     extern void swayhudgun(int curtime);
846     extern vec hudgunorigin(int gun, const vec &from, const vec &to, fpsent *d);
847 }
848 
849 namespace server
850 {
851     extern const char *modename(int n, const char *unknown = "unknown");
852     extern const char *mastermodename(int n, const char *unknown = "unknown");
853     extern void startintermission();
854     extern void stopdemo();
855     extern void timeupdate(int secs);
856     extern const char *getdemofile(const char *file, bool init);
857     extern void forcemap(const char *map, int mode);
858     extern void forcepaused(bool paused);
859     extern void forcegamespeed(int speed);
860     extern void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen = MAXSTRLEN);
861     extern int msgsizelookup(int msg);
862     extern bool serveroption(const char *arg);
863     extern bool delayspawn(int type);
864 }
865 
866 #endif
867 
868