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