1 #ifndef __GAME_H__
2 #define __GAME_H__
3 
4 #include "engine.h"
5 
6 #define GAMEID				"bfa"
7 #define GAMEVERSION			161
8 #define DEMO_VERSION		GAMEVERSION
9 
10 #define MAXAI 256
11 #define MAXPLAYERS (MAXCLIENTS + MAXAI)
12 
13 // network quantization scale
14 #define DMF 16.0f			// for world locations
15 #define DNF 100.0f			// for normalized vectors
16 #define DVELF 1.0f			// for playerspeed based velocity vectors
17 
18 #ifdef GAMESERVER
19 #define GVAR(name)			(sv_##name)
20 #else
21 #define GVAR(name)			(name)
22 #ifdef GAMEPHYSICS
23 #define FWV(name)			(force##name >= 0 ? force##name : name)
24 #else
25 #define FWV(name)			(force##name >= 0 ? force##name : physics::name)
26 #endif
27 #endif
28 enum
29 {
30 	S_JUMP = S_GAMESPECIFIC, S_IMPULSE, S_LAND, S_PAIN1, S_PAIN2, S_PAIN3, S_PAIN4, S_PAIN5, S_PAIN6, S_DIE1, S_DIE2, S_SPLASH1, S_SPLASH2, S_UNDERWATER,
31 	S_SPLAT, S_SPLOSH, S_DEBRIS, S_TINK, S_RICOCHET, S_WHIZZ, S_WHIRR, S_BEEP, S_EXPLODE, S_ENERGY, S_HUM, S_BURN, S_BURNING, S_BURNFIRE, S_EXTINGUISH, S_BZAP, S_BZZT,
32 	S_RELOAD, S_SWITCH, S_MELEE, S_MELEE2, S_PISTOL, S_PISTOL2, S_SHOTGUN, S_SHOTGUN2, S_SMG, S_SMG2, S_GRENADE, S_GRENADE2, S_FLAMER, S_FLAMER2, S_PLASMA, S_PLASMA2, S_RIFLE, S_RIFLE2,
33 	S_ITEMPICKUP, S_ITEMSPAWN, S_REGEN, S_DAMAGE1, S_DAMAGE2, S_DAMAGE3, S_DAMAGE4, S_DAMAGE5, S_DAMAGE6, S_DAMAGE7, S_DAMAGE8, S_BURNDAMAGE,
34 	S_RESPAWN, S_CHAT, S_ERROR, S_ALARM, S_V_FLAGSECURED, S_V_FLAGOVERTHROWN, S_V_FLAGPICKUP, S_V_FLAGDROP, S_V_FLAGRETURN, S_V_FLAGSCORE, S_V_FLAGRESET,
35 	S_V_FIGHT, S_V_CHECKPOINT, S_V_ONEMINUTE, S_V_HEADSHOT, S_V_SPREE1, S_V_SPREE2, S_V_SPREE3, S_V_SPREE4, S_V_SPREE5, S_V_SPREE6, S_V_MKILL1, S_V_MKILL2, S_V_MKILL3,
36 	S_V_REVENGE, S_V_DOMINATE, S_V_YOUWIN, S_V_YOULOSE, S_V_MCOMPLETE, S_V_FRAGGED, S_V_OWNED, S_V_DENIED,
37 	S_MAX
38 };
39 
40 enum								// entity types
41 {
42 	NOTUSED = ET_EMPTY, LIGHT = ET_LIGHT, MAPMODEL = ET_MAPMODEL, PLAYERSTART = ET_PLAYERSTART, ENVMAP = ET_ENVMAP, PARTICLES = ET_PARTICLES,
43 	MAPSOUND = ET_SOUND, LIGHTFX = ET_LIGHTFX, SUNLIGHT = ET_SUNLIGHT, WEAPON = ET_GAMESPECIFIC,
44 	TELEPORT, ACTOR, TRIGGER, PUSHER, FLAG, CHECKPOINT, CAMERA, WAYPOINT, MAXENTTYPES
45 };
46 
47 enum { EU_NONE = 0, EU_ITEM, EU_AUTO, EU_ACT, EU_MAX };
48 
49 enum { TR_TOGGLE = 0, TR_LINK, TR_SCRIPT, TR_ONCE, TR_EXIT, TR_MAX };
50 enum { TA_MANUAL = 0, TA_AUTO, TA_ACTION, TA_MAX };
51 #define TRIGGERIDS		16
52 #define TRIGSTATE(a,b)	(b%2 ? !a : a)
53 
54 enum { CP_RESPAWN = 0, CP_START, CP_FINISH, CP_LAST, CP_MAX };
55 enum { WP_COMMON = 0, WP_PLAYER, WP_ENEMY, WP_LINKED, WP_CAMERA, WP_MAX };
56 enum { WP_S_NONE = 0, WP_S_DEFEND, WP_S_PROJECT, WP_S_MAX };
57 
58 struct enttypes
59 {
60 	int	type,			priority, links,	radius,	usetype,	numattrs,
61 			canlink,
62 			reclink;
63 	bool	noisy;	const char *name,			*attrs[6];
64 };
65 #ifdef GAMESERVER
66 enttypes enttype[] = {
67 	{
68 		NOTUSED,		-1,  0,		0,		EU_NONE,	0,
69 			0,
70 			0,
71 			true,				"none",			{ "",		"",			"",			"",			"",			"" }
72 	},
73 	{
74 		LIGHT,			1,  59,		0,		EU_NONE,	4,
75 			(1<<LIGHTFX),
76 			(1<<LIGHTFX),
77 			false,				"light",		{ "radius",	"red",		"green",	"blue",		"",			"" }
78 	},
79 	{
80 		MAPMODEL,		1,  58,		0,		EU_NONE,	6,
81 			(1<<TRIGGER),
82 			(1<<TRIGGER),
83 			false,				"mapmodel",		{ "type",	"yaw",		"rot",		"blend",	"scale",	"flags" }
84 	},
85 	{
86 		PLAYERSTART,	1,  59,		0,		EU_NONE,	5,
87 			0,
88 			0,
89 			false,				"playerstart",	{ "team",	"yaw",		"pitch",	"mode",		"id",		"" }
90 	},
91 	{
92 		ENVMAP,			1,  0,		0,		EU_NONE,	1,
93 			0,
94 			0,
95 			false,				"envmap",		{ "radius",	"",			"",			"",			"",			"" }
96 	},
97 	{
98 		PARTICLES,		1,  59,		0,		EU_NONE,	5,
99 			(1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER),
100 			(1<<TRIGGER)|(1<<PUSHER),
101 			false,				"particles",	{ "type",	"a",		"b",		"c",		"d",		"" }
102 	},
103 	{
104 		MAPSOUND,		1,  58,		0,		EU_NONE,	5,
105 			(1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER),
106 			(1<<TRIGGER)|(1<<PUSHER),
107 			false,				"sound",		{ "type",	"maxrad",	"minrad",	"volume",	"flags",	"" }
108 	},
109 	{
110 		LIGHTFX,		1,  1,		0,		EU_NONE,	5,
111 			(1<<LIGHT)|(1<<TELEPORT)|(1<<TRIGGER)|(1<<PUSHER),
112 			(1<<LIGHT)|(1<<TRIGGER)|(1<<PUSHER),
113 			false,				"lightfx",		{ "type",	"mod",		"min",		"max",		"flags",	"" }
114 	},
115 	{
116 		SUNLIGHT,		1,  160,	0,		EU_NONE,	6,
117 			0,
118 			0,
119 			false,				"sunlight",		{ "yaw",	"pitch",	"red",		"green",	"blue",		"offset" }
120 	},
121 	{
122 		WEAPON,			2,  59,		16,		EU_ITEM,	4,
123 			0,
124 			0,
125 			false,				"weapon",		{ "type",	"flags",	"mode",		"id",		"",			"" }
126 	},
127 	{
128 		TELEPORT,		1,  50,		12,		EU_AUTO,	5,
129 			(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX)|(1<<TELEPORT),
130 			(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
131 			false,				"teleport",		{ "yaw",	"pitch",	"push",		"radius",	"colour",	"" }
132 	},
133 	{
134 		ACTOR,			1,  59,		0,		EU_NONE,	5,
135 			(1<<FLAG)|(1<<WAYPOINT),
136 			0,
137 			false,				"actor",		{ "type",	"yaw",		"pitch",	"mode",		"id",		"" }
138 	},
139 	{
140 		TRIGGER,		1,  58,		16,		EU_AUTO,	5,
141 			(1<<MAPMODEL)|(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
142 			(1<<MAPMODEL)|(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
143 			false,				"trigger",		{ "id",		"type",		"action",	"radius",	"state",	"" }
144 	},
145 	{
146 		PUSHER,			1,  58,		12,		EU_AUTO,	5,
147 			(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
148 			(1<<MAPSOUND)|(1<<PARTICLES)|(1<<LIGHTFX),
149 			false,				"pusher",		{ "zpush",	"ypush",	"xpush",	"radius",	"min",		"" }
150 	},
151 	{
152 		FLAG,			1,  48,		36,		EU_NONE,	5,
153 			(1<<FLAG),
154 			0,
155 			false,				"flag",			{ "team",	"yaw",		"pitch",	"mode",		"id",		"" }
156 	},
157 	{
158 		CHECKPOINT,		1,  48,		16,		EU_AUTO,	6,
159 			0,
160 			0,
161 			false,				"checkpoint",	{ "radius",	"yaw",		"pitch",	"mode",		"id",		"type" }
162 	},
163 	{
164 		CAMERA,			1,  48,		0,		EU_NONE,	3,
165 			(1<<CAMERA),
166 			0,
167 			false,				"camera",		{ "type",	"mindist",	"maxdist",	"",			"",			"" }
168 	},
169 	{
170 		WAYPOINT,		0,  1,		16,		EU_NONE,	5,
171 			(1<<WAYPOINT),
172 			0,
173 			true,				"waypoint",		{ "type",	"state",	"id",		"radius",	"flags",	"" }
174 	}
175 };
176 #else
177 extern enttypes enttype[];
178 #endif
179 
180 enum
181 {
182 	ANIM_PAIN = ANIM_GAMESPECIFIC, ANIM_JUMP,
183 	ANIM_IMPULSE_FORWARD, ANIM_IMPULSE_BACKWARD, ANIM_IMPULSE_LEFT, ANIM_IMPULSE_RIGHT, ANIM_IMPULSE_DASH,
184 	ANIM_SINK, ANIM_EDIT, ANIM_LAG, ANIM_SWITCH, ANIM_PICKUP, ANIM_WIN, ANIM_LOSE,
185 	ANIM_CROUCH, ANIM_CRAWL_FORWARD, ANIM_CRAWL_BACKWARD, ANIM_CRAWL_LEFT, ANIM_CRAWL_RIGHT,
186 	ANIM_MELEE, ANIM_MELEE_ATTACK,
187 	ANIM_PISTOL, ANIM_PISTOL_SHOOT, ANIM_PISTOL_RELOAD,
188 	ANIM_SHOTGUN, ANIM_SHOTGUN_SHOOT, ANIM_SHOTGUN_RELOAD,
189 	ANIM_SMG, ANIM_SMG_SHOOT, ANIM_SMG_RELOAD,
190 	ANIM_GRENADE, ANIM_GRENADE_THROW, ANIM_GREANDES_RELOAD, ANIM_GRENADE_POWER,
191 	ANIM_FLAMER, ANIM_FLAMER_SHOOT, ANIM_FLAMER_RELOAD,
192 	ANIM_PLASMA, ANIM_PLASMA_SHOOT, ANIM_PLASMA_RELOAD,
193 	ANIM_RIFLE, ANIM_RIFLE_SHOOT, ANIM_RIFLE_RELOAD,
194 	ANIM_VWEP, ANIM_SHIELD, ANIM_POWERUP,
195 	ANIM_MAX
196 };
197 
198 #define WEAPSWITCHDELAY	PHYSMILLIS*2
199 #define WEAPPICKUPDELAY	PHYSMILLIS*2
200 #define EXPLOSIONSCALE	16.f
201 
202 enum
203 {
204 	WEAP_MELEE = 0, WEAP_PISTOL, WEAP_OFFSET, // end of unselectable weapon set
205 	WEAP_SHOTGUN = WEAP_OFFSET, WEAP_SMG, WEAP_FLAMER, WEAP_PLASMA, WEAP_RIFLE, WEAP_GRENADE, WEAP_SUPER, // end of item weapon set
206 	WEAP_INSTA = WEAP_SUPER, WEAP_TOTAL, // end of selectable weapon set
207 	WEAP_GIBS = WEAP_TOTAL, WEAP_MAX
208 };
209 #define isweap(a)		(a > -1 && a < WEAP_MAX)
210 
211 enum { WEAP_F_NONE = 0, WEAP_F_FORCED = 1<<0 };
212 enum { WEAP_S_IDLE = 0, WEAP_S_SHOOT, WEAP_S_RELOAD, WEAP_S_POWER, WEAP_S_SWITCH, WEAP_S_PICKUP, WEAP_S_WAIT };
213 
214 enum
215 {
216 	IMPACT_GEOM = 1<<0, BOUNCE_GEOM = 1<<1, IMPACT_PLAYER = 1<<2, BOUNCE_PLAYER = 1<<3,
217 	COLLIDE_TRACE = 1<<4, COLLIDE_OWNER = 1<<5, COLLIDE_CONT = 1<<6, COLLIDE_STICK = 1<<7,
218 	COLLIDE_GEOM = IMPACT_GEOM | BOUNCE_GEOM, COLLIDE_PLAYER = IMPACT_PLAYER | BOUNCE_PLAYER,
219 };
220 
221 struct weaptypes
222 {
223 	int	info, 				anim,				colour,			sound, 		esound, 	fsound,		rsound,
224 			add,	max,	sub[2],		adelay[2],		rdelay,	damage[2],		speed[2],			power,		time[2],
225 			delay,	explode[2],	rays[2],		spread[2],		zdiv[2],		aiskew[2],
226 			collide[2];
227 	bool	taper[2],			extinguish[2],		radial[2],			burns[2],		follows[2],			reloads,	zooms,	fullauto[2],		muzzle;
228 	float	elasticity[2],		reflectivity[2],	relativity[2],			waterfric[2],	weight[2],		partsize[2],		partlen[2],
229 			radius[2],		kickpush[2],		hitpush[2],			maxdist[2],			thrown[2],				halo;
230 	const char
231 			*name, 		*text,	*item,						*vwep,						*proj;
232 };
233 #ifdef GAMESERVER
234 weaptypes weaptype[WEAP_MAX] =
235 {
236 	{
237 		WEAP_MELEE,			ANIM_MELEE,			0xFFFFFF,		S_MELEE,	S_RICOCHET,	-1,			-1,
238 			1,		1,		{ 0, 0 },	{ 300, 300, },	0,		{ 50, 25 },		{ 150, 150 },	0,				{ 100, 100 },
239 			1,		{ 0, 0 },	{ 1, 1 },		{ 1, 1 },		{ 1, 1 },		{ 0, 0 },
240 			{ IMPACT_PLAYER, IMPACT_PLAYER },
241 			{ true, true },	{ false, false },	{ false, false },	{ false, false, },	{ false, false },	false,		false,	{ true, true },		false,
242 			{ 0, 0 },			{ 0, 0 },			{ 1, 1 },				{ 0, 0 },		{ 0, 0 },		{ 0.75f, 0.75f },	{ 0, 0 },
243 			{ 6, 6 },		{ 2, 4 },			{ 100, 500 },		{ 25, 25 },			{ 0, 0 },				1,
244 			"melee",	"\fd",	"",							"",							""
245 	},
246 	{
247 		WEAP_PISTOL,		ANIM_PISTOL,		0x888888,		S_PISTOL,	S_BZAP,		S_WHIZZ,	-1,
248 			10,		10,		{ 1, 1 },	{ 100, 200, },	1000,	{ 35, 35 },		{ 3000, 3000 },		0,			{ 2000, 2000 },
249 			0,		{ 0, 0 },	{ 1, 1 },		{ 1, 1 },		{ 1, 1 },		{ 16, 16 },
250 			{ IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE },
251 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ true, true },		true,	false,	{ false, true },	true,
252 			{ 0, 0 },			{ 0, 0 },			{ 0.05f, 0.05f },		{ 2, 2 },		{ 0, 0 },		{ 0.5f, 0.5f },		{ 10, 10 },
253 			{ 1, 1 },		{ 2, 2 },			{ 150, 150 },		{ 300, 300 },		{ 0, 0 },				4,
254 			"pistol",	"\fa",	"weapons/pistol/item",		"weapons/pistol/vwep",		""
255 	},
256 	{
257 		WEAP_SHOTGUN,		ANIM_SHOTGUN,		0xFFFF22,		S_SHOTGUN,	S_BZAP,		S_WHIZZ,	S_RICOCHET,
258 			1,		8,		{ 1, 2 },	{ 500, 750 },	1000,	{ 20, 16 },		{ 2500, 2000 },		0,			{ 300, 100 },
259 			0,		{ 0, 0 },	{ 20, 40 },		{ 25, 20 },		{ 1, 2 },		{ 2, 2 },
260 			{ BOUNCE_GEOM|IMPACT_PLAYER|COLLIDE_TRACE|COLLIDE_OWNER, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE },
261 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ true, true },		true,	false,	{ false, false },	true,
262 			{ 0.5f, 0.35f },		{ 50, 50 },			{ 0.05f, 0.05f },		{ 2, 2 },		{ 25, 25 },		{ 0.75f, 0.75f },{ 50, 50 },
263 			{ 1, 1 },		{ 15, 15 },			{ 20, 40 },			{ 150, 300 },			{ 0, 0 },			6,
264 			"shotgun",	"\fy",	"weapons/shotgun/item",		"weapons/shotgun/vwep",		""
265 	},
266 	{
267 		WEAP_SMG,			ANIM_SMG,			0xFF8822,		S_SMG,		S_BZAP,		S_WHIZZ,	S_RICOCHET,
268 			40,		40,		{ 1, 5 },	{ 75, 300 },	1500,	{ 30, 20 },		{ 2000, 2000 },		0,			{ 500, 500 },
269 			0,		{ 0, 0 },	{ 1, 5 },		{ 5, 5 },		{ 4, 2 },		{ 4, 4 },
270 			{ BOUNCE_GEOM|IMPACT_PLAYER|COLLIDE_TRACE|COLLIDE_OWNER, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE },
271 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ true, true },		true,	false,	{ true, true },		true,
272 			{ 0.75f, 0.5f },	{ 30, 30 },			{ 0.05f, 0.05f },		{ 2, 2 },		{ 0, 0 },		{ 0.5f, 0.5f },		{ 40, 40 },
273 			{ 1, 1 },		{ 0.5f, 3 },		{ 100, 120 },		{ 300, 400 },		{ 0, 0 },				5,
274 			"smg",		"\fo",	"weapons/smg/item",			"weapons/smg/vwep",			""
275 	},
276 	{
277 		WEAP_FLAMER,		ANIM_FLAMER,		0xFF2222,		S_FLAMER,	S_BURN,		S_BURNING,	-1,
278 			50,		50,		{ 1, 5 },	{ 100, 750 }, 	2000,	{ 8, 4 },		{ 150, 200 },		0,			{ 400, 600 },
279 			0,		{ 20, 24 },	{ 1, 5 },		{ 40, 20 },		{ 0, 0 },		{ 1, 2 },
280 			{ BOUNCE_GEOM|IMPACT_PLAYER, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_OWNER },
281 			{ false, false },	{ true, true },		{ true, true },		{ true, true, },	{ true, true },		true,	false,	{ true, true },		true,
282 			{ 0.15f, 0 },		{ 45, 0 },			{ 0.95f, 0.5f },	{ 1, 1 },		{ -300, 50 },		{ 22, 26 },			{ 0, 0 },
283 			{ 1, 1 },		{ 0.25f, 1 },		{ 20, 40 },			{ 40, 80 },			{ 0, 0 },				7,
284 			"flamer",	"\fr",	"weapons/flamer/item",		"weapons/flamer/vwep",		""
285 	},
286 	{
287 		WEAP_PLASMA,		ANIM_PLASMA,		0x22FFFF,		S_PLASMA,	S_ENERGY,	S_HUM,		-1,
288 			20,		20,		{ 1, 20 },	{ 500, 2000 },	3000,	{ 25, 25 },		{ 1500,	35 },		0,			{ 750, 5000 },
289 			0,		{ 16, 40 },	{ 1, 1 },		{ 5, 5 },		{ 0, 0 },		{ 6, 2 },
290 			{ IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_OWNER, IMPACT_GEOM|COLLIDE_OWNER|COLLIDE_STICK },
291 			{ true, true },		{ true, false },	{ true, true },		{ false, false, },	{ true, true },		true,	false,	{ true, false },	true,
292 			{ 0, 0 },			{ 0, 0 },			{ 0.125f, 0.175f },			{ 1, 1 },		{ 0, 0 },	{ 16, 42 },			{ 0, 0 },
293 			{ 1, 1 },		{ 3, 6 },			{ 50, 200 },		{ 200, 50 },		{ 0, 0 },				5,
294 			"plasma",	"\fc",	"weapons/plasma/item",		"weapons/plasma/vwep",		""
295 	},
296 	{
297 		WEAP_RIFLE,			ANIM_RIFLE,			0xAA66FF,		S_RIFLE,	S_ENERGY,	S_BZZT,		-1,
298 			5,		5,		{ 1, 1 },	{ 750, 750 },	2000,	{ 50, 125 },	{ 5000, 50000 },		0,		{ 5000, 5000 },
299 			0,		{ 16, 0 },	{ 1, 1 },		{ 1, 0 },		{ 0, 0 },		{ 2, 1 },
300 			{ IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_OWNER|COLLIDE_TRACE, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE|COLLIDE_CONT },
301 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ false, false },	true,	true,	{ false, false },	true,
302 			{ 0, 0 },			{ 0, 0 },			{ 1, 0 },				{ 2, 2 },		{ 0, 0 },		{ 0.65f, 1.5f },	{ 1024, 4096 },
303 			{ 1, 1 },		{ 5, 0 },		{ 100, 200 },			{ 600, 0 },			{ 0, 0 },				7,
304 			"rifle",	"\fv",	"weapons/rifle/item",		"weapons/rifle/vwep",		""
305 	},
306 	{
307 		WEAP_GRENADE,		ANIM_GRENADE,		0x22FF22,		S_GRENADE,	S_EXPLODE,	S_BEEP,	S_TINK,
308 			1,		2,		{ 1, 1 },	{ 1500, 1500 },	6000,	{ 300, 300 },	{ 250, 250 },			3000,	{ 3000, 3000 },
309 			100,	{ 60, 60 },	{ 1, 1 },		{ 0, 0 },		{ 0, 0 },		{ 1, 1 },
310 			{ BOUNCE_GEOM|BOUNCE_PLAYER|COLLIDE_OWNER, IMPACT_GEOM|COLLIDE_OWNER|COLLIDE_STICK },
311 			{ false, false },	{ false, false },	{ false, false },	{ true, true, },	{ true, true },		false,	false,	{ false, false },	false,
312 			{ 0.5f, 0 },		{ 0, 0 },			{ 1, 1 },				{ 2, 2 },		{ 64, 64 },		{ 2, 2 },			{ 0, 0 },
313 			{ 1, 1 },		{ 5, 5 },		{ 1000, 1000 },		{ 400, 400 },			{ 0.0625f, 0.0625f },	3,
314 			"grenade",	"\fg",	"weapons/grenade/item",		"weapons/grenade/vwep",		"weapons/grenade/proj"
315 	},
316 	{
317 		WEAP_INSTA,			ANIM_RIFLE,			0xAA66FF,		S_RIFLE,	S_ENERGY,	S_BZZT,		-1,
318 			5,		5,		{ 1, 1 },	{ 750, 750 },	2000,	{ 100, 100 },	{ 10000, 50000 },		0,		{ 5000, 5000 },
319 			0,		{ 0, 0 },	{ 1, 1 },		{ 0, 0 },		{ 0, 0 },		{ 1, 1 },
320 			{ IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_TRACE|COLLIDE_CONT },
321 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ false, false },	true,	true,	{ false, false },	true,
322 			{ 0, 0 },			{ 0, 0 },			{ 1, 0 },				{ 2, 2 },		{ 0, 0 },		{ 0.65f, 1.5f },	{ 1024, 4096 },
323 			{ 1, 1 },		{ 5, 0 },		{ 100, 200 },		{ 0, 0 },				{ 0, 0 },				7,
324 			"rifle",	"\fv",	"weapons/rifle/item",		"weapons/rifle/vwep",		""
325 	},
326 	{
327 		WEAP_GIBS,			ANIM_GRENADE,		0x660000,		S_SPLOSH,	S_SPLAT,	S_WHIRR,	S_SPLAT,
328 			2,		2,		{ 1, 1 },	{ 500, 500 },	500,	{ 25, 25 },		{ 250, 250 },			0,		{ 1500, 1500 },
329 			100,	{ 0, 0 },	{ 1, 1 },		{ 0, 0 },		{ 0, 0 },		{ 1, 1 },
330 			{ IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_OWNER, IMPACT_GEOM|IMPACT_PLAYER|COLLIDE_OWNER },
331 			{ false, false },	{ false, false },	{ false, false },	{ false, false, },	{ true, true },		true,	false,	{ false, false },	false,
332 			{ 0.35f, 0.35f },	{ 0, 0 },			{ 1, 1 },				{ 2, 2 },		{ 35, 35 },		{ 2, 2 },			{ 0, 0 },
333 			{ 2, 2 },		{ 5, 5 },		{ 100, 100 },		{ 200, 200 },			{ 0.125f, 0.125f },		4,
334 			"gibs",		"\fw",	"gibs/gibc",				"gibs/gibc",				"gibs/gibc"
335 	},
336 };
337 #else
338 extern weaptypes weaptype[];
339 #define FIRECOLOURS 8
340 const int firecols[FIRECOLOURS] = { 0xFF5808, 0x981808, 0x782808, 0x481808, 0x983818, 0x601808, 0xFF1808, 0x381808 };
341 #endif
342 
343 enum
344 {
345 	HIT_NONE = 0, HIT_ALT = 1<<0, HIT_LEGS = 1<<1, HIT_TORSO = 1<<2, HIT_HEAD = 1<<3, HIT_FULL = 1<<4, HIT_PROJ = 1<<5,
346 	HIT_EXPLODE = 1<<6, HIT_BURN = 1<<7, HIT_MELT = 1<<8, HIT_DEATH = 1<<9, HIT_WATER = 1<<10, HIT_WAVE = 1<<11, HIT_SPAWN = 1<<12,
347 	HIT_LOST = 1<<13, HIT_KILL = 1<<14, HIT_SFLAGS = HIT_KILL
348 };
349 
350 #define hithurts(x) 	(x&HIT_BURN || x&HIT_EXPLODE || x&HIT_PROJ || x&HIT_MELT || x&HIT_DEATH || x&HIT_WATER)
351 #define doesburn(x,y)	(isweap(x) && weaptype[x].burns[y&HIT_ALT ? 1 : 0] && y&HIT_FULL)
352 enum
353 {
354 	FRAG_NONE = 0, FRAG_HEADSHOT = 1<<1, FRAG_OBLITERATE = 1<<2,
355 	FRAG_SPREE1 = 1<<3, FRAG_SPREE2 = 1<<4, FRAG_SPREE3 = 1<<5, FRAG_SPREE4 = 1<<6, FRAG_SPREE5 = 1<<7, FRAG_SPREE6 = 1<<8,
356 	FRAG_MKILL1 = 1<<9, FRAG_MKILL2 = 1<<10, FRAG_MKILL3 = 1<<11, FRAG_REVENGE = 1<<12, FRAG_DOMINATE = 1<<13,
357 	FRAG_SPREES = 6, FRAG_SPREE = 3, FRAG_MKILL = 9,
358 	FRAG_CHECK = FRAG_SPREE1|FRAG_SPREE2|FRAG_SPREE3|FRAG_SPREE4|FRAG_SPREE5|FRAG_SPREE6,
359 	FRAG_MULTI = FRAG_MKILL1|FRAG_MKILL2|FRAG_MKILL3,
360 };
361 
362 enum
363 {
364 	G_DEMO = 0, G_LOBBY, G_EDITMODE, G_STORY, G_DEATHMATCH, G_STF, G_CTF, G_TRIAL, G_MAX,
365 	G_START = G_LOBBY, G_PLAY = G_STORY, G_FIGHT = G_DEATHMATCH, G_RAND = G_CTF-G_DEATHMATCH+1
366 };
367 enum
368 {
369 	G_M_NONE = 0, G_M_MULTI = 1<<0, G_M_TEAM = 1<<1, G_M_INSTA = 1<<2, G_M_DUEL = 1<<3, G_M_SURVIVOR= 1<<4, G_M_ARENA = 1<<5,
370 	G_M_ALL = G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA, G_M_SOME = G_M_INSTA|G_M_ARENA,
371 	G_M_NUM = 6
372 };
373 
374 struct gametypes
375 {
376 	int	type,			mutators,															implied;		const char *name;
377 };
378 #ifdef GAMESERVER
379 gametypes gametype[] = {
380 	{ G_DEMO,			G_M_NONE,															G_M_NONE,				"demo" },
381 	{ G_LOBBY,			G_M_SOME,															G_M_NONE,				"lobby" },
382 	{ G_EDITMODE,		G_M_SOME,															G_M_NONE,				"editing" },
383 	{ G_STORY,			G_M_TEAM|G_M_SOME,													G_M_TEAM|G_M_ARENA,		"story" },
384 	{ G_DEATHMATCH,		G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA,		G_M_NONE,				"deathmatch" },
385 	{ G_STF,			G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_ARENA,								G_M_TEAM,				"secure-the-flag" },
386 	{ G_CTF,			G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_ARENA,								G_M_TEAM,				"capture-the-flag" },
387 	{ G_TRIAL,			G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_ARENA,								G_M_NONE,				"time-trial" },
388 }, mutstype[] = {
389 	{ G_M_MULTI,		G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA,		G_M_TEAM|G_M_MULTI,		"multi" },
390 	{ G_M_TEAM,			G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA,		G_M_TEAM,				"team" },
391 	{ G_M_INSTA,		G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA,		G_M_INSTA,				"insta" },
392 	{ G_M_DUEL,			G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_ARENA,					G_M_DUEL,				"duel" },
393 	{ G_M_SURVIVOR,		G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_SURVIVOR|G_M_ARENA,				G_M_SURVIVOR,			"survivor" },
394 	{ G_M_ARENA,		G_M_MULTI|G_M_TEAM|G_M_INSTA|G_M_DUEL|G_M_SURVIVOR|G_M_ARENA,		G_M_ARENA,				"arena" },
395 };
396 #else
397 extern gametypes gametype[], mutstype[];
398 #endif
399 
400 #define m_game(a)			(a > -1 && a < G_MAX)
401 #define m_check(a,b)		(!a || (a < 0 ? -a != b : a == b))
402 
403 #define m_demo(a)			(a == G_DEMO)
404 #define m_lobby(a)			(a == G_LOBBY)
405 #define m_edit(a)			(a == G_EDITMODE)
406 #define m_story(a)			(a == G_STORY)
407 #define m_dm(a)				(a == G_DEATHMATCH)
408 #define m_stf(a)			(a == G_STF)
409 #define m_ctf(a)			(a == G_CTF)
410 #define m_trial(a)			(a == G_TRIAL)
411 
412 #define m_play(a)			(a >= G_PLAY)
413 #define m_flag(a)			(m_stf(a) || m_ctf(a))
414 #define m_fight(a)			(a >= G_FIGHT)
415 
416 #define m_multi(a,b)		((b & G_M_MULTI) || (gametype[a].implied & G_M_MULTI))
417 #define m_team(a,b)			((b & G_M_TEAM) || (gametype[a].implied & G_M_TEAM))
418 #define m_insta(a,b)		((b & G_M_INSTA) || (gametype[a].implied & G_M_INSTA))
419 #define m_duel(a,b)			((b & G_M_DUEL) || (gametype[a].implied & G_M_DUEL))
420 #define m_survivor(a,b)		((b & G_M_SURVIVOR) || (gametype[a].implied & G_M_SURVIVOR))
421 #define m_arena(a,b)		((b & G_M_ARENA) || (gametype[a].implied & G_M_ARENA))
422 
423 #define m_duke(a,b)			(m_duel(a, b) || m_survivor(a, b))
424 #define m_regen(a,b)		(!m_duke(a,b) && !m_insta(a,b))
425 
426 #define m_weapon(a,b)		(m_arena(a,b) ? -1 : (m_edit(a) || m_trial(a) ? GVAR(trialweapon) : (m_insta(a,b) ? GVAR(instaweapon) : GVAR(spawnweapon))))
427 #define m_delay(a,b)		(m_play(a) && !m_duke(a,b) ? (m_trial(a) ? GVAR(trialdelay) : ((m_insta(a, b) || m_arena(a, b) ? GVAR(instadelay) : GVAR(spawndelay)))) : 0)
428 #define m_protect(a,b)		(m_insta(a, b) || m_arena(a, b) ? GVAR(instaprotect) : GVAR(spawnprotect))
429 #define m_noitems(a,b)		(GVAR(itemsallowed) < (m_insta(a,b) ? (m_arena(a,b) ? 2 : 3) : (m_trial(a) ? 3 : 1)))
430 #define m_health(a,b)		(m_insta(a,b) ? 1 : GVAR(maxhealth))
431 
432 #define w_reload(a,b)		(a != WEAP_MELEE && (a == (isweap(b) ? b : WEAP_PISTOL) || weaptype[a].reloads))
433 #define w_carry(a,b)		(a != WEAP_MELEE && a != (isweap(b) ? b : WEAP_PISTOL) && weaptype[a].reloads)
434 #define w_attr(g,a,b)		(m_edit(g) || (a >= WEAP_OFFSET && a != (isweap(b) ? b : WEAP_PISTOL)) ? a : WEAP_GRENADE)
435 
436 // network messages codes, c2s, c2c, s2c
437 enum
438 {
439 	SV_CONNECT = 0, SV_SERVERINIT, SV_WELCOME, SV_CLIENTINIT, SV_POS, SV_PHYS, SV_TEXT, SV_COMMAND, SV_ANNOUNCE, SV_DISCONNECT,
440 	SV_SHOOT, SV_DESTROY, SV_SUICIDE, SV_DIED, SV_POINTS, SV_DAMAGE, SV_SHOTFX,
441 	SV_ARENAWEAP, SV_TRYSPAWN, SV_SPAWNSTATE, SV_SPAWN,
442 	SV_DROP, SV_WEAPSELECT,
443 	SV_MAPCHANGE, SV_MAPVOTE, SV_CHECKPOINT, SV_ITEMSPAWN, SV_ITEMUSE, SV_TRIGGER, SV_EXECLINK,
444 	SV_PING, SV_PONG, SV_CLIENTPING,
445 	SV_TIMEUP, SV_NEWGAME, SV_ITEMACC,
446 	SV_SERVMSG, SV_GAMEINFO, SV_RESUME,
447 	SV_EDITMODE, SV_EDITENT, SV_EDITLINK, SV_EDITVAR, SV_EDITF, SV_EDITT, SV_EDITM, SV_FLIP, SV_COPY, SV_PASTE, SV_ROTATE, SV_REPLACE, SV_DELCUBE, SV_REMIP, SV_NEWMAP,
448 	SV_GETMAP, SV_SENDMAP, SV_SENDMAPFILE, SV_SENDMAPSHOT, SV_SENDMAPCONFIG,
449 	SV_MASTERMODE, SV_KICK, SV_CLEARBANS, SV_CURRENTMASTER, SV_SPECTATOR, SV_WAITING, SV_SETMASTER, SV_SETTEAM,
450 	SV_FLAGS, SV_FLAGINFO,
451 	SV_TAKEFLAG, SV_RETURNFLAG, SV_RESETFLAG, SV_DROPFLAG, SV_SCOREFLAG, SV_INITFLAGS, SV_SCORE,
452 	SV_LISTDEMOS, SV_SENDDEMOLIST, SV_GETDEMO, SV_SENDDEMO,
453 	SV_DEMOPLAYBACK, SV_RECORDDEMO, SV_STOPDEMO, SV_CLEARDEMOS,
454 	SV_CLIENT, SV_RELOAD, SV_REGEN,
455 	SV_ADDBOT, SV_DELBOT, SV_INITAI,
456     SV_MAPCRC, SV_CHECKMAPS,
457 	SV_SWITCHNAME, SV_SWITCHTEAM,
458 	SV_AUTHTRY, SV_AUTHCHAL, SV_AUTHANS,
459     NUMSV
460 };
461 
462 #ifdef GAMESERVER
msgsizelookup(int msg)463 char msgsizelookup(int msg)
464 {
465 	static const int msgsizes[] =				// size inclusive message token, 0 for variable or not-checked sizes
466 	{
467 		SV_CONNECT, 0, SV_SERVERINIT, 5, SV_WELCOME, 1, SV_CLIENTINIT, 0, SV_POS, 0, SV_PHYS, 0, SV_TEXT, 0, SV_COMMAND, 0,
468 		SV_ANNOUNCE, 0, SV_DISCONNECT, 2,
469 		SV_SHOOT, 0, SV_DESTROY, 0, SV_SUICIDE, 3, SV_DIED, 8, SV_POINTS, 4, SV_DAMAGE, 10, SV_SHOTFX, 10,
470 		SV_ARENAWEAP, 0, SV_TRYSPAWN, 2, SV_SPAWNSTATE, 0, SV_SPAWN, 0,
471 		SV_DROP, 0, SV_WEAPSELECT, 0,
472 		SV_MAPCHANGE, 0, SV_MAPVOTE, 0, SV_CHECKPOINT, 0, SV_ITEMSPAWN, 2, SV_ITEMUSE, 0, SV_TRIGGER, 0, SV_EXECLINK, 3,
473 		SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2,
474 		SV_TIMEUP, 2, SV_NEWGAME, 1, SV_ITEMACC, 0,
475 		SV_SERVMSG, 0, SV_GAMEINFO, 0, SV_RESUME, 0,
476 		SV_EDITMODE, 2, SV_EDITENT, 0, SV_EDITLINK, 4, SV_EDITVAR, 0, SV_EDITF, 16, SV_EDITT, 16, SV_EDITM, 15, SV_FLIP, 14, SV_COPY, 14, SV_PASTE, 14, SV_ROTATE, 15, SV_REPLACE, 16, SV_DELCUBE, 14, SV_REMIP, 1, SV_NEWMAP, 2,
477 		SV_GETMAP, 0, SV_SENDMAP, 0, SV_SENDMAPFILE, 0, SV_SENDMAPSHOT, 0, SV_SENDMAPCONFIG, 0,
478 		SV_MASTERMODE, 2, SV_KICK, 2, SV_CLEARBANS, 1, SV_CURRENTMASTER, 3, SV_SPECTATOR, 3, SV_WAITING, 2, SV_SETMASTER, 0, SV_SETTEAM, 0,
479 		SV_FLAGS, 0, SV_FLAGINFO, 0,
480 		SV_DROPFLAG, 0, SV_SCOREFLAG, 5, SV_RETURNFLAG, 3, SV_TAKEFLAG, 3, SV_RESETFLAG, 2, SV_INITFLAGS, 0, SV_SCORE, 0,
481 		SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0,
482 		SV_DEMOPLAYBACK, 3, SV_RECORDDEMO, 2, SV_STOPDEMO, 1, SV_CLEARDEMOS, 2,
483 		SV_CLIENT, 0, SV_RELOAD, 0, SV_REGEN, 0,
484 		SV_ADDBOT, 0, SV_DELBOT, 0, SV_INITAI, 0,
485         SV_MAPCRC, 0, SV_CHECKMAPS, 1,
486 		SV_SWITCHNAME, 0, SV_SWITCHTEAM, 0,
487 		SV_AUTHTRY, 0, SV_AUTHCHAL, 0, SV_AUTHANS, 0,
488 		-1
489 	};
490 	static int sizetable[NUMSV] = { -1 };
491 	if(sizetable[0] < 0)
492 	{
493 		memset(sizetable, -1, sizeof(sizetable));
494 		for(const int *p = msgsizes; *p >= 0; p += 2) sizetable[p[0]] = p[1];
495 	}
496 	return msg >= 0 && msg < NUMSV ? sizetable[msg] : -1;
497 }
498 #else
499 extern char msgsizelookup(int msg);
500 #endif
501 enum { SPHY_NONE = 0, SPHY_JUMP, SPHY_IMPULSE, SPHY_POWER, SPHY_EXTINGUISH, SPHY_MAX };
502 enum { CON_CHAT = CON_GAMESPECIFIC, CON_EVENT, CON_MAX, CON_LO = CON_MESG, CON_HI = CON_SELF, CON_IMPORTANT = CON_SELF };
503 
504 #define DEMO_MAGIC "BFDZ"
505 struct demoheader
506 {
507 	char magic[16];
508 	int version, gamever;
509 };
510 
511 enum
512 {
513 	TEAM_NEUTRAL = 0, TEAM_ALPHA, TEAM_BETA, TEAM_GAMMA, TEAM_DELTA, TEAM_ENEMY, TEAM_MAX,
514 	TEAM_FIRST = TEAM_ALPHA, TEAM_LAST = TEAM_DELTA, TEAM_NUM = TEAM_LAST-(TEAM_FIRST-1)
515 };
516 struct teamtypes
517 {
518 	int	type,			colour;	const char	*name,
519 		*tpmdl,								*fpmdl,
520 		*flag,			*icon,				*chat,		*colname;
521 };
522 #ifdef GAMESERVER
523 teamtypes teamtype[] = {
524 	{
525 		TEAM_NEUTRAL,	0x666666,			"neutral",
526 		"actors/player",					"actors/player/vwep",
527 		"flag",			"team",				"\fa",		"grey"
528 	},
529 	{
530 		TEAM_ALPHA,		0x2222AA,			"alpha",
531 		"actors/player/alpha",				"actors/player/alpha/vwep",
532 		"flag/alpha",	"teamalpha",		"\fb",		"blue"
533 	},
534 	{
535 		TEAM_BETA,		0xAA2222,			"beta",
536 		"actors/player/beta",				"actors/player/beta/vwep",
537 		"flag/beta",	"teambeta",			"\fr",		"red"
538 	},
539 	{
540 		TEAM_GAMMA,		0x22AA22,			"gamma",
541 		"actors/player/gamma",				"actors/player/gamma/vwep",
542 		"flag/gamma",	"teamgamma",		"\fg",		"green"
543 	},
544 	{
545 		TEAM_DELTA,		0xAAAA22,			"delta",
546 		"actors/player/delta",				"actors/player/delta/vwep",
547 		"flag/delta",	"teamdelta",		"\fy",		"yellow"
548 	},
549 	{
550 		TEAM_ENEMY,		0x222222,			"enemy",
551 		"actors/player",					"actors/player/vwep",
552 		"flag",			"team",				"\fd",		"dark grey"
553 	}
554 };
555 #else
556 extern teamtypes teamtype[];
557 #endif
558 enum { BASE_NONE = 0, BASE_HOME = 1<<0, BASE_FLAG = 1<<1, BASE_BOTH = BASE_HOME|BASE_FLAG };
559 
560 #define numteams(a,b)	(m_fight(a) && m_team(a,b) ? (m_multi(a,b) ? TEAM_NUM : TEAM_NUM/2) : 1)
561 #define isteam(a,b,c,d)	(m_fight(a) && m_team(a,b) ? (c >= d && c <= numteams(a,b)) : c == TEAM_NEUTRAL)
562 #define valteam(a,b)	(a >= b && a <= TEAM_NUM)
563 #define adjustscaled(t,n,s) { \
564 	if(n > 0) { n = (t)(n/(1.f+sqrtf((float)curtime)/float(s))); if(n <= 0) n = (t)0; } \
565 	else if(n < 0) { n = (t)(n/(1.f+sqrtf((float)curtime)/float(s))); if(n >= 0) n = (t)0; } \
566 }
567 
568 #define MAXNAMELEN 24
569 enum { SAY_NONE	= 0, SAY_ACTION = 1<<0, SAY_TEAM = 1<<1, SAY_NUM = 2 };
570 enum { PRIV_NONE = 0, PRIV_MASTER, PRIV_ADMIN, PRIV_MAX };
571 
572 #define MM_MODE 0xF
573 #define MM_AUTOAPPROVE 0x1000
574 #define MM_PRIVSERV (MM_MODE | MM_AUTOAPPROVE)
575 #define MM_PUBSERV ((1<<MM_OPEN) | (1<<MM_VETO))
576 #define MM_COOPSERV (MM_AUTOAPPROVE | MM_PUBSERV | (1<<MM_LOCKED))
577 
578 enum { MM_OPEN = 0, MM_VETO, MM_LOCKED, MM_PRIVATE, MM_PASSWORD };
579 enum { CAMERA_NONE = 0, CAMERA_PLAYER, CAMERA_FOLLOW, CAMERA_ENTITY, CAMERA_MAX };
580 enum { SINFO_STATUS = 0, SINFO_DESC, SINFO_PING, SINFO_PLAYERS, SINFO_MAXCLIENTS, SINFO_GAME, SINFO_MAP, SINFO_TIME, SINFO_MAX };
581 enum { SSTAT_OPEN = 0, SSTAT_LOCKED, SSTAT_PRIVATE, SSTAT_FULL, SSTAT_UNKNOWN, SSTAT_MAX };
582 
583 enum { AC_ATTACK = 0, AC_ALTERNATE, AC_RELOAD, AC_USE, AC_JUMP, AC_IMPULSE, AC_CROUCH, AC_SPECIAL, AC_TOTAL, AC_DASH = AC_TOTAL, AC_MAX };
584 enum { IM_METER = 0, IM_TYPE, IM_TIME, IM_COUNT, IM_MAX };
585 enum { IM_T_NONE = 0, IM_T_BOOST, IM_T_DASH, IM_T_KICK, IM_T_SKATE, IM_T_MAX, IM_T_WALL = IM_T_KICK };
586 #define CROUCHHEIGHT 0.7f
587 #define PHYSMILLIS 250
588 
589 #include "ai.h"
590 
591 // inherited by gameent and server clients
592 struct gamestate
593 {
594 	int health, ammo[WEAP_MAX], entid[WEAP_MAX];
595 	int lastweap, arenaweap, weapselect, weapload[WEAP_MAX], weapshot[WEAP_MAX], weapstate[WEAP_MAX], weapwait[WEAP_MAX], weaplast[WEAP_MAX];
596 	int lastdeath, lastspawn, lastrespawn, lastpain, lastregen, lastfire;
597 	int aitype, aientity, ownernum, skill, points, cpmillis, cptime;
598 
gamestategamestate599 	gamestate() : arenaweap(-1), lastdeath(0), lastspawn(0), lastrespawn(0), lastpain(0), lastregen(0), lastfire(0),
600 		aitype(-1), aientity(-1), ownernum(-1), skill(0), points(0), cpmillis(0), cptime(0) {}
~gamestategamestate601 	~gamestate() {}
602 
603 	int hasweap(int weap, int sweap, int level = 0, int exclude = -1)
604 	{
605 		if(isweap(weap) && weap != exclude)
606 		{
607 			if(ammo[weap] > 0 || (w_reload(weap, sweap) && !ammo[weap])) switch(level)
608 			{
609 				case 0: default: return true; break; // has weap at all
610 				case 1: if(w_carry(weap, sweap)) return true; break; // only carriable
611 				case 2: if(ammo[weap] > 0) return true; break; // only with actual ammo
612 				case 3: if(ammo[weap] > 0 && w_reload(weap, sweap)) return true; break; // only reloadable with actual ammo
613 				case 4: if(ammo[weap] >= (w_reload(weap, sweap) ? 0 : weaptype[weap].max)) return true; break; // only reloadable or those with < max
614 				case 5: if(w_carry(weap, sweap) || (!w_reload(weap, sweap) && weap >= WEAP_OFFSET)) return true; break; // special case for usable weapons
615 			}
616 		}
617 		return false;
618 	}
619 
620 	int bestweap(int sweap, bool last = false)
621 	{
622 		if(last && hasweap(lastweap, sweap)) return lastweap;
623 		loopirev(WEAP_MAX) if(hasweap(i, sweap, 3)) return i; // reloadable first
624 		loopirev(WEAP_MAX) if(hasweap(i, sweap, 1)) return i; // carriable second
625 		loopirev(WEAP_MAX) if(hasweap(i, sweap, 0)) return i; // any just to bail us out
626 		return weapselect;
627 	}
628 
629 	int carry(int sweap, int level = 1, int exclude = -1)
630 	{
631 		int carry = 0;
632 		loopi(WEAP_MAX) if(hasweap(i, sweap, level, exclude)) carry++;
633 		return carry;
634 	}
635 
636 	int drop(int sweap, int exclude = -1)
637 	{
638 		int weap = -1;
639 		if(hasweap(weapselect, sweap, 1, exclude)) weap = weapselect;
640 		else loopi(WEAP_MAX) if(hasweap(i, sweap, 1, exclude))
641 		{
642 			weap = i;
643 			break;
644 		}
645 		return weap;
646 	}
647 
648 	void weapreset(bool full = false)
649 	{
loopigamestate650 		loopi(WEAP_MAX)
651 		{
652 			weapstate[i] = WEAP_S_IDLE;
653 			weapwait[i] = weaplast[i] = weapload[i] = weapshot[i] = 0;
654 			if(full) ammo[i] = -1;
655 			entid[i] = -1;
656 		}
657 		if(full) lastweap = weapselect = -1;
658 	}
659 
setweapstategamestate660 	void setweapstate(int weap, int state, int delay, int millis)
661 	{
662 		weapstate[weap] = state;
663 		weapwait[weap] = delay;
664 		weaplast[weap] = millis;
665 	}
666 
667 	void weapswitch(int weap, int millis, int state = WEAP_S_SWITCH)
668 	{
669 		int delay = state == WEAP_S_PICKUP ? WEAPPICKUPDELAY : WEAPSWITCHDELAY;
670 		lastweap = weapselect;
671 		setweapstate(lastweap, state, delay, millis);
672 		weapselect = weap;
673 		setweapstate(weap, state, delay, millis);
674 	}
675 
676 	bool weapwaited(int weap, int millis, int skip = 0)
677 	{
678 		if(!weapwait[weap] || weapstate[weap] == WEAP_S_IDLE || weapstate[weap] == WEAP_S_POWER || (skip && skip&(1<<weapstate[weap]))) return true;
679 		return millis-weaplast[weap] >= weapwait[weap];
680 	}
681 
682 	int skipwait(int weap, int flags, int millis, int skip, bool override = false)
683 	{
684 		int skipstate = skip;
685 		if(weaptype[weap].sub[flags&HIT_ALT ? 1 : 0] && (skip&(1<<WEAP_S_RELOAD)) && weapstate[weap] == WEAP_S_RELOAD && millis-weaplast[weap] < weapwait[weap])
686 		{
687 			if(!override && ammo[weap]-weapload[weap] < weaptype[weap].sub[flags&HIT_ALT ? 1 : 0])
688 				skipstate &= ~(1<<WEAP_S_RELOAD);
689 		}
690 		return skipstate;
691 	}
692 
693 	bool canswitch(int weap, int sweap, int millis, int skip = 0)
694 	{
695 		if(weap != weapselect && weapwaited(weapselect, millis, skipwait(weapselect, 0, millis, skip, true)) && hasweap(weap, sweap) && weapwaited(weap, millis, skipwait(weap, 0, millis, skip, true)))
696 			return true;
697 		return false;
698 	}
699 
700 	bool canshoot(int weap, int flags, int sweap, int millis, int skip = 0)
701 	{
702 		if(hasweap(weap, sweap) && ammo[weap] >= weaptype[weap].sub[flags&HIT_ALT ? 1 : 0] && weapwaited(weap, millis, skipwait(weap, flags, millis, skip)))
703 			return true;
704 		return false;
705 	}
706 
canreloadgamestate707 	bool canreload(int weap, int sweap, int millis)
708 	{
709 		if(weap == weapselect && w_reload(weap, sweap) && hasweap(weap, sweap) && ammo[weap] < weaptype[weap].max && weapwaited(weap, millis))
710 			return true;
711 		return false;
712 	}
713 
714 	bool canuse(int type, int attr, vector<int> &attrs, int sweap, int millis, int skip = 0)
715 	{
716 		//if((type != TRIGGER || attrs[2] == TA_AUTO) && enttype[type].usetype == EU_AUTO) return true;
717 		//if(weapwaited(weapselect, millis, skipwait(weapselect, 0, millis, skip)))
718 		switch(enttype[type].usetype)
719 		{
720 			case EU_AUTO: case EU_ACT: return true; break;
721 			case EU_ITEM:
722 			{ // can't use when reloading or firing
723 				if(isweap(attr) && !hasweap(attr, sweap, 4) && weapwaited(weapselect, millis, skipwait(weapselect, 0, millis, skip, true)))
724 					return true;
725 				break;
726 			}
727 			default: break;
728 		}
729 		return false;
730 	}
731 
useitemgamestate732 	void useitem(int id, int type, int attr, vector<int> &attrs, int sweap, int millis)
733 	{
734 		switch(type)
735 		{
736 			case TRIGGER: break;
737 			case WEAPON:
738 			{
739 				weapswitch(attr, millis, hasweap(attr, sweap) ? (weapselect != attr ? WEAP_S_SWITCH : WEAP_S_RELOAD) : WEAP_S_PICKUP);
740 				ammo[attr] = clamp((ammo[attr] > 0 ? ammo[attr] : 0)+weaptype[attr].add, 1, weaptype[attr].max);
741 				entid[attr] = id;
742 				break;
743 			}
744 			default: break;
745 		}
746 	}
747 
clearstategamestate748 	void clearstate()
749 	{
750 		lastdeath = lastpain = lastregen = lastfire = 0;
751 		lastrespawn = -1;
752 	}
753 
mapchangegamestate754 	void mapchange()
755 	{
756 		points = cpmillis = cptime = 0;
757 	}
758 
respawngamestate759 	void respawn(int millis, int heal)
760 	{
761 		health = heal;
762 		lastspawn = millis;
763 		clearstate();
764 		weapreset(true);
765 	}
766 
767 	void spawnstate(int sweap, int heal, bool arena = false, bool grenades = false)
768 	{
769 		health = heal;
770 		weapreset(true);
771 		if(!isweap(sweap)) sweap = WEAP_PISTOL;
772 		ammo[sweap] = weaptype[sweap].reloads ? weaptype[sweap].add : weaptype[sweap].max;
773 		if(arena)
774 		{
775 			int aweap = arenaweap;
776 			while(aweap < WEAP_OFFSET || aweap >= WEAP_SUPER || aweap == WEAP_GRENADE) aweap = rnd(WEAP_SUPER-WEAP_OFFSET)+WEAP_OFFSET; // pistol = random
777 			ammo[aweap] = weaptype[aweap].reloads ? weaptype[aweap].add : weaptype[aweap].max;
778 			lastweap = weapselect = aweap;
779 		}
780 		else
781 		{
782 			arenaweap = -1;
783 			lastweap = weapselect = sweap;
784 		}
785 		if(sweap != WEAP_MELEE) ammo[WEAP_MELEE] = weaptype[WEAP_MELEE].max;
786 		if(grenades && weapselect != WEAP_GRENADE) ammo[WEAP_GRENADE] = weaptype[WEAP_GRENADE].max;
787 	}
788 
789 	void editspawn(int millis, int sweap, int heal, bool arena = false, bool grenades = false)
790 	{
791 		clearstate();
792 		spawnstate(sweap, heal, arena, grenades);
793 	}
794 
respawnwaitgamestate795 	int respawnwait(int millis, int delay)
796 	{
797 		return lastdeath ? max(0, delay-(millis-lastdeath)) : 0;
798 	}
799 
800 	// just subtract damage here, can set death, etc. later in code calling this
dodamagegamestate801 	void dodamage(int heal)
802 	{
803 		lastregen = 0;
804 		health = heal;
805 	}
806 
protectgamestate807 	int protect(int millis, int delay)
808 	{
809 		int amt = 0;
810 		if(lastspawn && delay && millis-lastspawn <= delay) amt = delay-(millis-lastspawn);
811 		return amt;
812 	}
813 };
814 
815 #if !defined(GAMESERVER) && !defined(STANDALONE)
816 struct gameentity : extentity
817 {
818 	int schan;
819 	int lastuse, lastspawn;
820 	int mark;
821 	vector<int> kin;
822 
gameentitygameentity823 	gameentity() : schan(-1), lastuse(0), lastspawn(0), mark(0) { kin.setsize(0); }
~gameentitygameentity824 	~gameentity()
825 	{
826 		if(issound(schan)) removesound(schan);
827 		schan = -1;
828 		kin.setsize(0);
829 	}
830 };
831 
832 enum
833 {
834 	ST_NONE		= 0,
835 	ST_CAMERA	= 1<<0,
836 	ST_CURSOR	= 1<<1,
837 	ST_GAME		= 1<<2,
838 	ST_SPAWNS	= 1<<3,
839 	ST_DEFAULT	= ST_CAMERA|ST_CURSOR|ST_GAME,
840 	ST_VIEW		= ST_CURSOR|ST_CAMERA,
841 	ST_ALL		= ST_CAMERA|ST_CURSOR|ST_GAME|ST_SPAWNS,
842 };
843 
844 enum { ITEM_ENT = 0, ITEM_PROJ, ITEM_MAX };
845 struct actitem
846 {
847 	int type, target;
848 	float score;
849 
actitemactitem850 	actitem() : type(ITEM_ENT), target(-1), score(0.f) {}
~actitemactitem851 	~actitem() {}
852 };
853 #ifdef GAMEWORLD
854 const char *animnames[] =
855 {
856 	"idle", "forward", "backward", "left", "right", "dead", "dying", "swim",
857 	"mapmodel", "trigger on", "trigger off", "pain", "jump",
858 	"impulse forward", "impulse backward", "impulse left", "impulse right", "impulse dash",
859 	"sink", "edit", "lag", "switch", "pickup", "win", "lose",
860 	"crouch", "crawl forward", "crawl backward", "crawl left", "crawl right",
861 	"melee", "melee attack",
862 	"pistol", "pistol shoot", "pistol reload",
863 	"shotgun", "shotgun shoot", "shotgun reload",
864 	"smg", "smg shoot", "smg reload",
865 	"grenade", "grenade throw", "grenade reload", "grenade power",
866 	"flamer", "flamer shoot", "flamer reload",
867 	"plasma", "plasma shoot", "plasma reload",
868 	"rifle", "rifle shoot", "rifle reload",
869 	"vwep", "shield", "powerup",
870 	""
871 };
872 #else
873 extern const char *animnames[];
874 #endif
875 struct serverstatuses
876 {
877 	int type,				colour;		const char *icon;
878 };
879 #ifdef GAMEWORLD
880 serverstatuses serverstatus[] = {
881 	{ SSTAT_OPEN,			0xDDDDDD,	"server" },
882 	{ SSTAT_LOCKED,			0xDD8800,	"serverlock" },
883 	{ SSTAT_PRIVATE,		0x8888DD,	"serverpriv" },
884 	{ SSTAT_FULL,			0xDD8888,	"serverfull" },
885 	{ SSTAT_UNKNOWN,		0x888888,	"serverunk" }
886 };
887 const char *serverinfotypes[] = {
888 	"",	"desc", "ping", "pl", "max", "game", "map", "time"
889 };
890 #else
891 extern serverstatuses serverstatus[];
892 extern const char *serverinfotypes[];
893 extern const char *serverinfotypes[];
894 #endif
895 
896 struct gameent : dynent, gamestate
897 {
898 	editinfo *edit; ai::aiinfo *ai;
899 	int team, clientnum, privilege, lastnode, checkpoint, cplast, respawned, suicided, lastupdate, lastpredict, plag, ping, lastflag, frags, deaths, totaldamage, totalshots,
900 		actiontime[AC_MAX], impulse[IM_MAX], smoothmillis, turnmillis, turnside, aschan, vschan, wschan, fschan, lasthit, lastkill, lastattacker, lastpoints, quake, lastpush;
901 	float deltayaw, deltapitch, newyaw, newpitch, deltaaimyaw, deltaaimpitch, newaimyaw, newaimpitch, turnyaw, turnroll;
902 	vec head, torso, muzzle, melee, waist, lfoot, rfoot, legs, hrad, trad, lrad;
903 	bool action[AC_MAX], conopen, dominating, dominated, k_up, k_down, k_left, k_right;
904 	string name, info, obit;
905 	vector<int> airnodes;
906 
gameentgameent907 	gameent() : edit(NULL), ai(NULL), team(TEAM_NEUTRAL), clientnum(-1), privilege(PRIV_NONE), checkpoint(-1), cplast(0), lastupdate(0), lastpredict(0), plag(0), ping(0),
908 		frags(0), deaths(0), totaldamage(0), totalshots(0), smoothmillis(-1), turnmillis(0), aschan(-1), vschan(-1), wschan(-1), fschan(-1),
909 		lastattacker(-1), lastpoints(0), quake(0), lastpush(0),
910 		head(-1, -1, -1), torso(-1, -1, -1), muzzle(-1, -1, -1), melee(-1, -1, -1), waist(-1, -1, -1),
911 		lfoot(-1, -1, -1), rfoot(-1, -1, -1), legs(-1, -1, -1), hrad(-1, -1, -1), trad(-1, -1, -1), lrad(-1, -1, -1),
912 		conopen(false), dominating(false), dominated(false), k_up(false), k_down(false), k_left(false), k_right(false)
913 	{
914 		name[0] = info[0] = obit[0] = 0;
915 		weight = 200; // so we can control the 'gravity' feel
916 		maxspeed = 50; // ditto for movement
917 		checktags();
918 		respawn(-1, 100);
919 	}
~gameentgameent920 	~gameent()
921 	{
922 		removesounds();
923 		freeeditinfo(edit);
924 		if(ai) delete ai;
925 		removetrackedparticles(this);
926 		removetrackedsounds(this);
927 	}
928 
removesoundsgameent929 	void removesounds()
930 	{
931 		if(issound(aschan)) removesound(aschan);
932 		if(issound(vschan)) removesound(vschan);
933 		if(issound(wschan)) removesound(wschan);
934 		if(issound(fschan)) removesound(fschan);
935 		aschan = vschan = wschan = fschan = -1;
936 	}
937 
stopmovinggameent938 	void stopmoving(bool full)
939 	{
940 		if(full) move = strafe = 0;
941 		loopi(AC_MAX)
942 		{
943 			action[i] = false;
944 			actiontime[i] = 0;
945 		}
946 	}
947 
clearstategameent948 	void clearstate()
949 	{
950 		loopi(IM_MAX) impulse[i] = 0;
951 		cplast = lasthit = lastkill = quake = lastpush = turnmillis = turnside = 0;
952 		turnroll = turnyaw = 0;
953 		lastflag = respawned = suicided = lastnode = -1;
954 		obit[0] = 0;
955 	}
956 
respawngameent957 	void respawn(int millis, int heal)
958 	{
959 		stopmoving(true);
960 		removesounds();
961 		clearstate();
962 		physent::reset();
963 		gamestate::respawn(millis, heal);
964 		airnodes.setsizenodelete(0);
965 	}
966 
967 	void editspawn(int millis, int sweap, int heal, bool arena = false, bool grenades = false)
968 	{
969 		stopmoving(true);
970 		clearstate();
971 		inmaterial = timeinair = 0;
972 		inliquid = onladder = false;
973 		strafe = move = 0;
974 		physstate = PHYS_FALL;
975 		vel = falling = vec(0, 0, 0);
976 		floor = vec(0, 0, 1);
977 		resetinterp();
978 		gamestate::editspawn(millis, sweap, heal, arena, grenades);
979 		airnodes.setsizenodelete(0);
980 	}
981 
resetstategameent982 	void resetstate(int millis, int heal)
983 	{
984 		respawn(millis, heal);
985 		frags = deaths = totaldamage = totalshots = 0;
986 	}
987 
mapchangegameent988 	void mapchange(int millis, int heal)
989 	{
990 		checkpoint = -1;
991 		dominating = dominated = false;
992 		resetstate(millis, heal);
993 		gamestate::mapchange();
994 	}
995 
cleartagsgameent996 	void cleartags() { head = torso = muzzle = melee = waist = lfoot = rfoot = vec(-1, -1, -1); }
997 
checkmeleeposgameent998 	void checkmeleepos()
999 	{
1000 		if(melee == vec(-1, -1, -1))
1001 		{
1002 			vec dir; vecfromyawpitch(yaw, pitch, 1, 0, dir);
1003 			dir.mul(radius); dir.z -= height*0.0625f;
1004 			melee = vec(o).add(dir);
1005 		}
1006 	}
checkmuzzleposgameent1007 	void checkmuzzlepos()
1008 	{
1009 		if(muzzle == vec(-1, -1, -1))
1010 		{
1011 			vec dir, right; vecfromyawpitch(yaw, pitch, 1, 0, dir); vecfromyawpitch(yaw, pitch, 0, -1, right);
1012 			dir.mul(radius); right.mul(radius); dir.z -= height*0.0625f;
1013 			muzzle = vec(o).add(dir).add(right);
1014 		}
1015 	}
checktagsgameent1016 	void checktags()
1017 	{
1018 		checkmeleepos();
1019 		if(type == ENT_PLAYER || (type == ENT_AI && (!isaitype(aitype) || aistyle[aitype].maxspeed)))
1020 		{
1021 			float hsize = max(xradius*0.45f, yradius*0.45f); if(head == vec(-1, -1, -1)) { torso = head; head = o; head.z -= hsize; }
1022 			vec dir; vecfromyawpitch(yaw, pitch+90, 1, 0, dir); dir.mul(hsize); head.add(dir); hrad = vec(xradius*0.45f, yradius*0.45f, hsize);
1023 			if(torso == vec(-1, -1, -1)) { torso = o; torso.z -= height*0.5f; } torso.z += hsize*0.5f;
1024 			float tsize = (head.z-hrad.z)-torso.z; trad = vec(xradius, yradius, tsize);
1025 			float lsize = ((torso.z-trad.z)-(o.z-height))*0.5f; legs = torso; legs.z -= trad.z+lsize; lrad = vec(xradius*0.8f, yradius*0.8f, lsize);
1026 			if(waist == vec(-1, -1, -1))
1027 			{
1028 				vecfromyawpitch(yaw, 0, -1, 0, dir); dir.mul(radius*1.15f); dir.z -= height*0.5f;
1029 				waist = vec(o).add(dir);
1030 			}
1031 			if(lfoot == vec(-1, -1, -1))
1032 			{
1033 				vecfromyawpitch(yaw, 0, 0, -1, dir); dir.mul(radius); dir.z -= height;
1034 				lfoot = vec(o).add(dir);
1035 			}
1036 			if(rfoot == vec(-1, -1, -1))
1037 			{
1038 				vecfromyawpitch(yaw, 0, 0, 1, dir); dir.mul(radius); dir.z -= height;
1039 				rfoot = vec(o).add(dir);
1040 			}
1041 		}
1042 	}
1043 
muzzleposgameent1044 	vec &muzzlepos(int weap)
1045 	{
1046 		if(isweap(weap) && weaptype[weap].muzzle)
1047 		{
1048 			checkmuzzlepos();
1049 			return muzzle;
1050 		}
1051 		checkmeleepos();
1052 		return melee;
1053 	}
1054 
calcrollgameent1055 	float calcroll(bool crouch)
1056 	{
1057 		float c = roll;
1058 		if(quake > 0)
1059 		{
1060 			float wobble = float(rnd(15)-7)*(float(min(quake, 100))/100.f);
1061 			switch(state)
1062 			{
1063 				case CS_SPECTATOR: case CS_WAITING: c = 0; wobble *= 0.5f; break;
1064 				case CS_ALIVE: if(crouch) wobble *= 0.5f; break;
1065 				case CS_DEAD: c = 0; break;
1066 				default: c = wobble = 0; break;
1067 			}
1068 			c += wobble;
1069 		}
1070 		return c;
1071 	}
1072 
doimpulsegameent1073 	void doimpulse(int cost, int type, int millis)
1074 	{
1075 		impulse[IM_METER] += cost;
1076 		impulse[IM_TYPE] = type;
1077 		impulse[IM_TIME] = millis;
1078 		impulse[IM_COUNT]++;
1079 		resetphys();
1080 	}
1081 
dojumpresetgameent1082 	void dojumpreset()
1083 	{
1084 		timeinair = turnside = impulse[IM_COUNT] = impulse[IM_TYPE] = 0;
1085 	}
1086 };
1087 
1088 enum { PRJ_SHOT = 0, PRJ_GIBS, PRJ_DEBRIS, PRJ_ENT };
1089 
1090 struct projent : dynent
1091 {
1092 	vec from, to, norm;
1093 	int addtime, lifetime, lifemillis, waittime, spawntime, lastradial, lasteffect, lastbounce;
1094 	float movement, roll, lifespan, lifesize;
1095 	bool local, beenused, extinguish, limited, stuck, escaped;
1096 	int projtype, projcollide;
1097 	float elasticity, reflectivity, relativity, waterfric;
1098 	int schan, id, weap, flags, colour, hitflags;
1099 	entitylight light;
1100 	gameent *owner;
1101 	physent *hit;
1102 	const char *mdl;
1103 
projentprojent1104 	projent() : norm(0, 0, 1), projtype(PRJ_SHOT), id(-1), hitflags(HITFLAG_NONE), owner(NULL), hit(NULL), mdl(NULL) { reset(); }
~projentprojent1105 	~projent()
1106 	{
1107 		removetrackedparticles(this);
1108 		removetrackedsounds(this);
1109 		if(issound(schan)) removesound(schan);
1110 		schan = -1;
1111 	}
1112 
resetprojent1113 	void reset()
1114 	{
1115 		physent::reset();
1116 		type = ENT_PROJ;
1117 		state = CS_ALIVE;
1118 		norm = vec(0, 0, 1);
1119 		addtime = lifetime = lifemillis = waittime = spawntime = lastradial = lasteffect = lastbounce = flags = 0;
1120 		schan = id = weap = -1;
1121 		movement = roll = lifespan = lifesize = 0.f;
1122 		colour = 0xFFFFFF;
1123 		beenused = extinguish = limited = stuck = false;
1124 		escaped = true;
1125 		projcollide = BOUNCE_GEOM|BOUNCE_PLAYER;
1126 	}
1127 
readyprojent1128 	bool ready()
1129 	{
1130 		if(owner && !beenused && waittime <= 0 && state != CS_DEAD)
1131 			return true;
1132 		return false;
1133 	}
1134 };
1135 
1136 namespace server
1137 {
1138 	extern void stopdemo();
1139 	extern void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen = MAXSTRLEN);
1140 }
1141 
1142 namespace client
1143 {
1144 	struct mapvote
1145 	{
1146 		gameent *player;
1147 		string map;
1148 		int mode, muts;
1149 	};
1150 	extern vector<mapvote> mapvotes;
1151 	extern bool demoplayback, sendinfo, sendcrc;
1152 	extern void addmsg(int type, const char *fmt = NULL, ...);
1153 	extern void c2sinfo();
1154 }
1155 
1156 namespace physics
1157 {
1158 	extern float gravity, jumpspeed, movespeed, movecrawl, impulsespeed, impulseregen, liquidspeed, liquidcurb, floorcurb, aircurb;
1159 	extern int impulsestyle, impulsemeter, impulsecost, impulsecount, impulseskate;
1160 	extern int smoothmove, smoothdist;
1161 	extern bool canimpulse(physent *d, int cost = 0);
1162 	extern bool movecamera(physent *pl, const vec &dir, float dist, float stepdist);
1163 	extern void smoothplayer(gameent *d, int res, bool local);
1164 	extern void update();
1165 }
1166 
1167 namespace projs
1168 {
1169 	extern vector<projent *> projs;
1170 
1171 	extern void reset();
1172 	extern void update();
1173 	extern void create(vec &from, vec &to, bool local, gameent *d, int type, int lifetime, int lifemillis, int waittime, int speed, int id = 0, int weap = -1, int flags = 0);
1174 	extern void preload();
1175 	extern void remove(gameent *owner);
1176 	extern void shootv(int weap, int flags, int power, vec &from, vector<vec> &locs, gameent *d, bool local);
1177 	extern void drop(gameent *d, int g, int n, bool local = true);
1178 	extern void adddynlights();
1179 	extern void render();
1180 }
1181 
1182 namespace weapons
1183 {
1184 	extern int autoreloading;
1185 	extern bool weapselect(gameent *d, int weap, bool local = true);
1186 	extern bool weapreload(gameent *d, int weap, int load = -1, int ammo = -1, bool local = true);
1187 	extern void reload(gameent *d);
1188 	extern void shoot(gameent *d, vec &targ, int force = 0);
1189 	extern void preload(int weap = -1);
1190 }
1191 
1192 namespace hud
1193 {
1194 	extern char *conopentex, *playertex, *deadtex, *dominatingtex, *dominatedtex, *inputtex, *bliptex, *cardtex, *flagtex, *arrowtex;
1195 	extern int hudwidth, hudsize, lastteam, damageresidue, damageresiduefade, shownotices, radarflagnames, inventorygame, teamkillnum;
1196 	extern float noticescale, inventoryblend, inventoryskew, inventorygrow, radarflagblend, radarblipblend, radarflagsize;
1197 	extern vector<int> teamkills;
1198 	extern bool chkcond(int val, bool cond);
1199 	extern void drawquad(float x, float y, float w, float h, float tx1 = 0, float ty1 = 0, float tx2 = 1, float ty2 = 1);
1200 	extern void drawtex(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1);
1201 	extern void drawsized(float x, float y, float s);
1202 	extern void colourskew(float &r, float &g, float &b, float skew = 1.f);
1203 	extern void healthskew(int &s, float &r, float &g, float &b, float &fade, float ss, bool throb = 1.f);
1204 	extern void skewcolour(float &r, float &g, float &b, bool t = false);
1205 	extern void skewcolour(int &r, int &g, int &b, bool t = false);
1206 	extern void drawindicator(int weap, int x, int y, int s);
1207 	extern void drawclip(int weap, int x, int y, float s);
1208 	extern void drawpointerindex(int index, int x, int y, int s, float r = 1.f, float g = 1.f, float b = 1.f, float fade = 1.f);
1209 	extern void drawpointer(int w, int h, int index);
1210 	extern int numteamkills();
1211 	extern float radarrange();
1212 	extern void drawblip(const char *tex, float area, int w, int h, float s, float blend, vec &dir, float r = 1.f, float g = 1.f, float b = 1.f, const char *font = "sub", const char *text = NULL, ...);
1213 	extern int drawprogress(int x, int y, float start, float length, float size, bool left, float r = 1.f, float g = 1.f, float b = 1.f, float fade = 1.f, float skew = 1.f, const char *font = NULL, const char *text = NULL, ...);
1214 	extern int drawitem(const char *tex, int x, int y, float size, bool left = false, float r = 1.f, float g = 1.f, float b = 1.f, float fade = 1.f, float skew = 1.f, const char *font = NULL, const char *text = NULL, ...);
1215 	extern int drawitemsubtext(int x, int y, float size, int align = TEXT_RIGHT_UP, float skew = 1.f, const char *font = NULL, float blend = 1.f, const char *text = NULL, ...);
1216 	extern int drawweapons(int x, int y, int s, float blend = 1.f);
1217 	extern int drawhealth(int x, int y, int s, float blend = 1.f);
1218 	extern void drawinventory(int w, int h, int edge, float blend = 1.f);
1219 	extern void damage(int n, const vec &loc, gameent *actor, int weap);
1220 	extern const char *teamtex(int team = TEAM_NEUTRAL);
1221 	extern const char *itemtex(int type, int stype);
1222 }
1223 
1224 namespace game
1225 {
1226 	extern int numplayers, gamemode, mutators, nextmode, nextmuts, minremain, maptime,
1227 			zoomtime, lastzoom, lasttvcam, lasttvchg, spectvtime, waittvtime, showplayerinfo,
1228 				bloodfade, gibfade, fogdist, aboveheadfade, announcefilter;
1229 	extern float bloodscale, gibscale;
1230 	extern bool intermission, zooming;
1231 	extern vec swaypush, swaydir;
1232     extern string clientmap;
1233 
1234 	extern gameent *player1;
1235 	extern vector<gameent *> players;
1236 
1237 	extern gameent *newclient(int cn);
1238 	extern gameent *getclient(int cn);
1239 	extern gameent *intersectclosest(vec &from, vec &to, gameent *at);
1240 	extern void clientdisconnected(int cn);
1241 	extern char *colorname(gameent *d, char *name = NULL, const char *prefix = "", bool team = true, bool dupname = true);
1242 	extern void announce(int idx, int targ, gameent *d, const char *msg, ...);
1243 	extern void respawn(gameent *d);
1244 	extern void spawneffect(int type, const vec &o, int colour = 0xFFFFFF, int radius = 4, float vel = 10.f, int fade = 250, float size = 1.5f, float blend = 1);
1245 	extern void impulseeffect(gameent *d, bool effect);
1246 	extern void suicide(gameent *d, int flags);
1247 	extern void fixrange(float &yaw, float &pitch);
1248 	extern void fixfullrange(float &yaw, float &pitch, float &roll, bool full);
1249 	extern void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch);
1250 	extern void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float frame = 1.f, float scale = 1.f);
1251 	extern bool allowmove(physent *d);
1252 	extern int mousestyle();
1253 	extern int deadzone();
1254 	extern int panspeed();
1255 	extern void checkzoom();
1256 	extern bool inzoom();
1257 	extern bool inzoomswitch();
1258 	extern void zoomview(bool down);
1259 	extern bool tvmode();
1260 	extern void resetcamera();
1261 	extern void resetworld();
1262 	extern void resetstate();
1263 	extern void quake(const vec &o, int damage, int radius);
1264 	extern void hiteffect(int weap, int flags, int damage, gameent *d, gameent *actor, vec &dir, bool local = false);
1265 	extern void damaged(int weap, int flags, int damage, int health, gameent *d, gameent *actor, int millis, vec &dir);
1266 	extern void killed(int weap, int flags, int damage, gameent *d, gameent *actor, int style);
1267 	extern void timeupdate(int timeremain);
1268 }
1269 
1270 namespace entities
1271 {
1272 	extern int showentdescs;
1273 	extern vector<extentity *> ents;
1274 	extern int lastenttype[MAXENTTYPES], lastusetype[EU_MAX];
1275 	extern void clearentcache();
1276 	extern int closestent(int type, const vec &pos, float mindist, bool links = false, gameent *d = NULL);
1277 	extern bool collateitems(gameent *d, vector<actitem> &actitems);
1278 	extern void checkitems(gameent *d);
1279 	extern void putitems(packetbuf &p);
1280 	extern void execlink(gameent *d, int index, bool local, int ignore = -1);
1281 	extern void setspawn(int n, int m);
1282 	extern bool tryspawn(dynent *d, const vec &o, short yaw = 0, short pitch = 0);
1283 	extern void spawnplayer(gameent *d, int ent = -1, bool suicide = false);
1284 	extern const char *entinfo(int type, vector<int> &attr, bool full = false);
1285 	extern void useeffects(gameent *d, int n, bool s, int g, int r);
1286 	extern const char *entmdlname(int type, vector<int> &attr);
1287 	extern bool clipped(const vec &o, bool aiclip = false);
1288 	extern void preload();
1289 	extern void edittoggled(bool edit);
1290 	extern const char *findname(int type);
1291 	extern void adddynlights();
1292 	extern void render();
1293 	extern void update();
1294 	struct avoidset
1295 	{
1296 		struct obstacle
1297 		{
1298 			dynent *ent;
1299 			int numentities;
1300 			float above;
1301 
obstacleavoidset::obstacle1302 			obstacle(dynent *ent) : ent(ent), numentities(0), above(-1) {}
1303 		};
1304 
1305 		vector<obstacle> obstacles;
1306 		vector<int> entities;
1307 
clearavoidset1308 		void clear()
1309 		{
1310 			obstacles.setsizenodelete(0);
1311 			entities.setsizenodelete(0);
1312 		}
1313 
addavoidset1314 		void add(dynent *ent)
1315 		{
1316 			obstacle &ob = obstacles.add(obstacle(ent));
1317 			if(!ent) ob.above = enttype[WAYPOINT].radius;
1318 			else switch(ent->type)
1319 			{
1320 				case ENT_PLAYER: case ENT_AI:
1321 				{
1322 					gameent *e = (gameent *)ent;
1323 					ob.above = e->abovehead().z;
1324 					break;
1325 				}
1326 				case ENT_PROJ:
1327 				{
1328 					projent *p = (projent *)ob.ent;
1329 					if(p->projtype == PRJ_SHOT && weaptype[p->weap].explode[p->flags&HIT_ALT ? 1 : 0])
1330 						ob.above = p->o.z+(weaptype[p->weap].explode[p->flags&HIT_ALT ? 1 : 0]*p->lifesize)+1.f;
1331 					break;
1332 				}
1333 			}
1334 		}
1335 
addavoidset1336 		void add(dynent *ent, int entity)
1337 		{
1338 			if(obstacles.empty() || ent!=obstacles.last().ent) add(ent);
1339 			obstacles.last().numentities++;
1340 			entities.add(entity);
1341 		}
1342 
1343 		void avoidnear(dynent *d, const vec &pos, float limit);
1344 
1345 		#define loopavoid(v, d, body) \
1346 			if(!(v).obstacles.empty()) \
1347 			{ \
1348 				int cur = 0; \
1349 				loopv((v).obstacles) \
1350 				{ \
1351 					const entities::avoidset::obstacle &ob = (v).obstacles[i]; \
1352 					int next = cur + ob.numentities; \
1353 					if(!ob.ent || ob.ent != (d)) \
1354 					{ \
1355 						for(; cur < next; cur++) \
1356 						{ \
1357 							int ent = (v).entities[cur]; \
1358 							body; \
1359 						} \
1360 					} \
1361 					cur = next; \
1362 				} \
1363 			}
1364 
findavoidset1365 		bool find(int entity, gameent *d) const
1366 		{
1367 			loopavoid(*this, d, { if(ent == entity) return true; });
1368 			return false;
1369 		}
1370 
remapavoidset1371 		int remap(gameent *d, int n, vec &pos)
1372 		{
1373 			if(!obstacles.empty())
1374 			{
1375 				int cur = 0;
1376 				loopv(obstacles)
1377 				{
1378 					obstacle &ob = obstacles[i];
1379 					int next = cur + ob.numentities;
1380 					if(!ob.ent || ob.ent != d)
1381 					{
1382 						for(; cur < next; cur++) if(entities[cur] == n)
1383 						{
1384 							if(ob.above < 0) return -1;
1385 							vec above(pos.x, pos.y, ob.above);
1386 							if(above.z-d->o.z >= ai::JUMPMAX)
1387 								return -1; // too much scotty
1388 							int node = closestent(WAYPOINT, above, ai::NEARDIST, true, d);
1389 							if(ents.inrange(node) && node != n)
1390 							{ // try to reroute above their head?
1391 								if(!find(node, d))
1392 								{
1393 									pos = ents[node]->o;
1394 									return node;
1395 								}
1396 								else return -1;
1397 							}
1398 							else
1399 							{
1400 								vec old = d->o;
1401 								d->o = vec(above).add(vec(0, 0, d->height));
1402 								bool col = collide(d, vec(0, 0, 1));
1403 								d->o = old;
1404 								if(col)
1405 								{
1406 									pos = above;
1407 									return n;
1408 								}
1409 								else return -1;
1410 							}
1411 						}
1412 					}
1413 					cur = next;
1414 				}
1415 			}
1416 			return n;
1417 		}
1418 	};
1419 	extern void findentswithin(int type, const vec &pos, float mindist, float maxdist, vector<int> &results);
1420 	extern float route(int node, int goal, vector<int> &route, const avoidset &obstacles, gameent *d = NULL, bool check = true);
1421 }
1422 #elif defined(GAMESERVER)
1423 namespace client
1424 {
1425 	extern const char *getname();
1426 }
1427 #endif
1428 #include "ctf.h"
1429 #include "stf.h"
1430 #include "vars.h"
1431 #ifndef GAMESERVER
1432 #include "scoreboard.h"
1433 #endif
1434 
1435 #endif
1436 
1437