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