1 #define GAMEWORLD 1
2 #include "game.h"
3 namespace game
4 {
5 	int nextmode = -1, nextmuts = -1, gamemode = -1, mutators = -1, maptime = 0, minremain = 0,
6 		lastcamera = 0, lasttvcam = 0, lasttvchg = 0, lastzoom = 0, lastmousetype = 0, liquidchan = -1, fogdist = 0;
7 	bool intermission = false, prevzoom = false, zooming = false;
8 	float swayfade = 0, swayspeed = 0, swaydist = 0;
9 	vec swaydir(0, 0, 0), swaypush(0, 0, 0);
10     string clientmap = "";
11 
12 	gameent *player1 = new gameent();
13 	vector<gameent *> players;
14 	dynent fpsmodel;
15 
16 	ICOMMANDG(resetvars, "", (), return); // server side
17 
18 	VARW(numplayers, 0, 0, MAXCLIENTS);
19 	SVARW(obitwater, "");
20 	SVARW(obitdeath, "");
21 	SVARW(mapmusic, "");
22 
23 	VARP(mouseinvert, 0, 0, 1);
24 	VARP(mouseabsolute, 0, 0, 1);
25 	VARP(mousetype, 0, 0, 2);
26 	VARP(mousedeadzone, 0, 10, 100);
27 	VARP(mousepanspeed, 1, 30, INT_MAX-1);
28 
29 	VARP(thirdperson, 0, 0, 1);
30 	VARP(dynlightents, 0, 2, 2);
31 	FVARP(playerblend, 0, 1, 1);
32 
33 	VARP(thirdpersonmodel, 0, 1, 1);
34 	VARP(thirdpersonfov, 90, 120, 150);
35 	FVARP(thirdpersonblend, 0, 0.5f, 1);
36 	FVARP(thirdpersondist, -100, 1.f, 100);
37 
38 	VARP(firstpersonmodel, 0, 1, 1);
39 	VARP(firstpersonfov, 90, 100, 150);
40 	VARP(firstpersonsway, 0, 1, 1);
41 	FVARP(firstpersonswaystep, 1, 18.0f, 100);
42 	FVARP(firstpersonswayside, 0, 0.04f, 1);
43 	FVARP(firstpersonswayup, 0, 0.05f, 1);
44 	FVARP(firstpersonblend, 0, 1, 1);
45 	FVARP(firstpersondist, -10000, -0.25f, 10000);
46 	FVARP(firstpersonshift, -10000, 0.3f, 10000);
47 	FVARP(firstpersonadjust, -10000, -0.07f, 10000);
48 
49 	VARP(editfov, 1, 120, 179);
50 	VARP(specfov, 1, 120, 179);
51 	VARP(specmode, 0, 1, 1); // 0 = float, 1 = tv
52 	VARP(spectvtime, 1000, 10000, INT_MAX-1);
53 	FVARP(spectvspeed, 0, 1, 1000);
54 	FVARP(spectvpitch, 0, 1, 1000);
55 	VARP(waitmode, 0, 1, 1); // 0 = float, 1 = tv
56 	VARP(waittvtime, 1000, 5000, INT_MAX-1);
57 	FVARP(waittvspeed, 0, 1, 1000);
58 	FVARP(waittvpitch, 0, 1, 1000);
59 	VARP(deathcamstyle, 0, 1, 2); // 0 = no follow, 1 = follow attacker, 2 = follow self
60 	FVARP(deathcamspeed, 0, 2.f, 1000);
61 
62 	FVARP(sensitivity, 1e-3f, 10.0f, 1000);
63 	FVARP(yawsensitivity, 1e-3f, 10.0f, 1000);
64 	FVARP(pitchsensitivity, 1e-3f, 7.5f, 1000);
65 	FVARP(mousesensitivity, 1e-3f, 1.0f, 1000);
66 	FVARP(zoomsensitivity, 0, 0.75f, 1);
67 
68 	VARP(zoommousetype, 0, 0, 2);
69 	VARP(zoommousedeadzone, 0, 25, 100);
70 	VARP(zoommousepanspeed, 1, 10, INT_MAX-1);
71 	VARP(zoomfov, 1, 10, 150);
72 	VARP(zoomtime, 1, 100, 10000);
73 
74 	VARFP(zoomlevel, 1, 4, 10, checkzoom());
75 	VARP(zoomlevels, 1, 5, 10);
76 	VARP(zoomdefault, 0, 0, 10); // 0 = last used, else defines default level
77 	VARP(zoomscroll, 0, 0, 1); // 0 = stop at min/max, 1 = go to opposite end
78 
79 	VARP(shownamesabovehead, 0, 2, 2);
80 	VARP(showstatusabovehead, 0, 2, 2);
81 	VARP(showteamabovehead, 0, 1, 3);
82 	VARP(showdamageabovehead, 0, 0, 3);
83 	FVARP(aboveheadblend, 0.f, 0.75f, 1.f);
84 	FVAR(aboveheadsmooth, 0, 0.5f, 1);
85 	VAR(aboveheadsmoothmillis, 1, 200, 10000);
86 	VARP(aboveheadfade, 500, 5000, INT_MAX-1);
87 
88 	VARP(showobituaries, 0, 4, 5); // 0 = off, 1 = only me, 2 = 1 + announcements, 3 = 2 + but dying bots, 4 = 3 + but bot vs bot, 5 = all
89 	VARP(showobitdists, 0, 0, 1);
90 	VARP(showplayerinfo, 0, 2, 2); // 0 = none, 1 = CON_MESG, 2 = CON_EVENT
91 	VARP(playdamagetones, 0, 1, 3);
92 
93 	VARP(quakefade, 0, 100, INT_MAX-1);
94     VARP(ragdolls, 0, 1, 1);
95 	FVARP(bloodscale, 0, 1, 1000);
96 	VARP(bloodfade, 1, 10000, INT_MAX-1);
97 	FVARP(gibscale, 0, 1, 1000);
98 	VARP(gibfade, 1, 10000, INT_MAX-1);
99 	VARP(fireburnfade, 0, 75, INT_MAX-1);
100 	FVARP(impulsescale, 0, 1, 1000);
101 	VARP(impulsefade, 0, 200, INT_MAX-1);
102 
103 	ICOMMAND(gamemode, "", (), intret(gamemode));
104 	ICOMMAND(mutators, "", (), intret(mutators));
105 	ICOMMAND(getintermission, "", (), intret(intermission ? 1 : 0));
106 
start()107 	void start() { }
108 
gametitle()109 	const char *gametitle() { return connected() ? server::gamename(gamemode, mutators) : "ready"; }
gametext()110 	const char *gametext() { return connected() ? mapname : "not connected"; }
111 
thirdpersonview(bool viewonly)112 	bool thirdpersonview(bool viewonly)
113 	{
114         if(!viewonly && (player1->state == CS_DEAD || player1->state == CS_WAITING)) return true;
115 		if(!thirdperson) return false;
116 		if(player1->state == CS_EDITING) return false;
117 		if(player1->state == CS_SPECTATOR) return false;
118 		if(inzoom()) return false;
119 		return true;
120 	}
121 	ICOMMAND(isthirdperson, "i", (int *viewonly), intret(thirdpersonview(*viewonly ? true : false) ? 1 : 0));
122 
mousestyle()123 	int mousestyle()
124 	{
125 		if(inzoom()) return zoommousetype;
126 		return mousetype;
127 	}
128 
deadzone()129 	int deadzone()
130 	{
131 		if(inzoom()) return zoommousedeadzone;
132 		return mousedeadzone;
133 	}
134 
panspeed()135 	int panspeed()
136 	{
137 		if(inzoom()) return zoommousepanspeed;
138 		return mousepanspeed;
139 	}
140 
fov()141 	int fov()
142 	{
143 		if(player1->state == CS_EDITING) return editfov;
144 		if(player1->state == CS_SPECTATOR) return specfov;
145 		if(thirdpersonview(true)) return thirdpersonfov;
146 		return firstpersonfov;
147 	}
148 
checkzoom()149 	void checkzoom()
150 	{
151 		if(zoomdefault > zoomlevels) zoomdefault = zoomlevels;
152 		if(zoomlevel < 0) zoomlevel = zoomdefault ? zoomdefault : zoomlevels;
153 		if(zoomlevel > zoomlevels) zoomlevel = zoomlevels;
154 	}
155 
setzoomlevel(int level)156 	void setzoomlevel(int level)
157 	{
158 		checkzoom();
159 		zoomlevel += level;
160 		if(zoomlevel > zoomlevels) zoomlevel = zoomscroll ? 1 : zoomlevels;
161 		else if(zoomlevel < 1) zoomlevel = zoomscroll ? zoomlevels : 1;
162 	}
163 	ICOMMAND(setzoom, "i", (int *level), setzoomlevel(*level));
164 
zoomset(bool on,int millis)165 	void zoomset(bool on, int millis)
166 	{
167 		if(on != zooming)
168 		{
169 			resetcursor();
170 			lastzoom = millis-max(zoomtime-(millis-lastzoom), 0);
171 			prevzoom = zooming;
172 			if(zoomdefault && on) zoomlevel = zoomdefault;
173 		}
174 		checkzoom();
175 		zooming = on;
176 	}
177 
zoomallow()178 	bool zoomallow()
179 	{
180 		if(allowmove(player1) && weaptype[player1->weapselect].zooms) return true;
181 		zoomset(false, 0);
182 		return false;
183 	}
184 
inzoom()185 	bool inzoom()
186 	{
187 		if(zoomallow() && lastzoom && (zooming || lastmillis-lastzoom <= zoomtime))
188 			return true;
189 		return false;
190 	}
191 	ICOMMAND(iszooming, "", (), intret(inzoom() ? 1 : 0));
192 
inzoomswitch()193 	bool inzoomswitch()
194 	{
195 		if(zoomallow() && lastzoom && ((zooming && lastmillis-lastzoom >= zoomtime/2) || (!zooming && lastmillis-lastzoom <= zoomtime/2)))
196 			return true;
197 		return false;
198 	}
199 
resetsway()200 	void resetsway()
201 	{
202 		swaydir = swaypush = vec(0, 0, 0);
203 		swayfade = swayspeed = swaydist = 0;
204 	}
205 
addsway(gameent * d)206 	void addsway(gameent *d)
207 	{
208 		if(firstpersonsway)
209 		{
210 			float maxspeed = physics::movevelocity(d);
211 			if(d->physstate >= PHYS_SLOPE)
212 			{
213 				swayspeed = min(sqrtf(d->vel.x*d->vel.x + d->vel.y*d->vel.y), maxspeed);
214 				swaydist += swayspeed*curtime/1000.0f;
215 				swaydist = fmod(swaydist, 2*firstpersonswaystep);
216 				swayfade = 1;
217 			}
218 			else if(swayfade > 0)
219 			{
220 				swaydist += swayspeed*swayfade*curtime/1000.0f;
221 				swaydist = fmod(swaydist, 2*firstpersonswaystep);
222 				swayfade -= 0.5f*(curtime*maxspeed)/(firstpersonswaystep*1000.0f);
223 			}
224 
225 			float k = pow(0.7f, curtime/25.0f);
226 			swaydir.mul(k);
227 			vec vel = vec(d->vel).add(d->falling).mul(FWV(impulsestyle) && d->action[AC_IMPULSE] && (d->move || d->strafe) ? 5 : 1);
228 			float speedscale = max(vel.magnitude(), maxspeed);
229 			if(speedscale > 0) swaydir.add(vec(vel).mul((1-k)/(15*speedscale)));
230 			swaypush.mul(pow(0.5f, curtime/25.0f));
231 		}
232 		else resetsway();
233 	}
234 
announce(int idx,int targ,gameent * d,const char * msg,...)235 	void announce(int idx, int targ, gameent *d, const char *msg, ...)
236 	{
237 		if(targ >= 0 && msg && *msg)
238 		{
239 			defvformatstring(text, msg, msg);
240 			conoutft(targ, "%s", text);
241 		}
242 		if(idx >= 0)
243 		{
244 			if(d && issound(d->aschan)) removesound(d->aschan);
245 			physent *t = !d || d == player1 ? camera1 : d;
246 			playsound(idx, t->o, t, t == camera1 ? SND_FORCED : 0, 255, getworldsize()/2, 0, d ? &d->aschan : NULL);
247 		}
248 	}
249 	ICOMMAND(announce, "iis", (int *idx, int *targ, char *s), announce(*idx, *targ, NULL, "\fw%s", s));
250 
tvmode()251 	bool tvmode()
252 	{
253 		if(!m_edit(gamemode)) switch(player1->state)
254 		{
255 			case CS_SPECTATOR: if(specmode) return true; break;
256 			case CS_WAITING: if(waitmode && (!player1->lastdeath || lastmillis-player1->lastdeath >= 500)) return true; break;
257 			default: break;
258 		}
259 		return false;
260 	}
261 
262 	ICOMMAND(specmodeswitch, "", (), specmode = specmode ? 0 : 1);
263 	ICOMMAND(waitmodeswitch, "", (), waitmode = waitmode ? 0 : 1);
264 
allowmove(physent * d)265     bool allowmove(physent *d)
266     {
267 		if(d == player1)
268 		{
269 			if(UI::hascursor(true)) return false;
270 			if(tvmode()) return false;
271 		}
272         if(d->type == ENT_PLAYER || d->type == ENT_AI)
273         {
274 			if(d->state == CS_DEAD || d->state == CS_WAITING || d->state == CS_SPECTATOR || intermission)
275 				return false;
276         }
277         return true;
278     }
279 
choosearenaweap(gameent * d,const char * s)280 	void choosearenaweap(gameent *d, const char *s)
281 	{
282 		if(m_arena(gamemode, mutators))
283 		{
284 			int weap = -1;
285 			if(*s >= '0' && *s <= '9') weap = atoi(s);
286 			else
287 			{
288 				loopi(WEAP_SUPER) if(!strcasecmp(weaptype[i].name, s))
289 				{
290 					weap = i;
291 					break;
292 				}
293 			}
294 			if(weap < WEAP_OFFSET || weap >= WEAP_SUPER || weap == WEAP_GRENADE) weap = WEAP_MELEE;
295 			client::addmsg(SV_ARENAWEAP, "ri2", d->clientnum, weap);
296 			conoutft(CON_SELF, "\fwyou will spawn with: %s%s", weaptype[weap].text, (weap >= WEAP_OFFSET ? weaptype[weap].name : "random weapons"));
297 		}
298 		else conoutft(CON_MESG, "\foweapon selection is only available in arena");
299 	}
300 	ICOMMAND(arenaweap, "s", (char *s), choosearenaweap(player1, s));
301 
respawn(gameent * d)302 	void respawn(gameent *d)
303 	{
304 		if(d->state == CS_DEAD && d->respawned < 0 && (!d->lastdeath || lastmillis-d->lastdeath >= 500))
305 		{
306 			client::addmsg(SV_TRYSPAWN, "ri", d->clientnum);
307 			d->respawned = lastmillis;
308 		}
309 	}
310 
deadscale(gameent * d,float amt=1,bool timechk=false)311 	float deadscale(gameent *d, float amt = 1, bool timechk = false)
312 	{
313 		float total = amt;
314 		if(d->state == CS_DEAD || d->state == CS_WAITING)
315 		{
316 			int len = m_delay(gamemode, mutators);
317 			if(!len && d->aitype >= AI_START) len = ai::aideadfade;
318 			if(len && (!timechk || len > 1000))
319 			{
320 				int interval = min(len/3, 1000), over = max(len-interval, 500), millis = lastmillis-d->lastdeath;
321 				if(millis <= len) { if(millis >= over) total *= 1.f-(float(millis-over)/float(interval)); }
322 				else total = 0;
323 			}
324 		}
325 		return total;
326 	}
327 
transscale(gameent * d,bool third=true)328 	float transscale(gameent *d, bool third = true)
329 	{
330 		float total = d == player1 ? (third ? thirdpersonblend : firstpersonblend) : playerblend;
331 		if(d->state == CS_ALIVE)
332 		{
333 			int prot = m_protect(gamemode, mutators), millis = d->protect(lastmillis, prot); // protect returns time left
334 			if(millis > 0) total *= 1.f-(float(millis)/float(prot));
335 			if(d == player1 && inzoom())
336 			{
337 				int frame = lastmillis-lastzoom;
338 				float pc = frame <= zoomtime ? float(frame)/float(zoomtime) : 1.f;
339 				total *= zooming ? 1.f-pc : pc;
340 			}
341 		}
342 		else total = deadscale(d, total);
343 		return total;
344 	}
345 
adddynlights()346 	void adddynlights()
347 	{
348 		if(dynlightents)
349 		{
350 			projs::adddynlights();
351 			entities::adddynlights();
352 			if(dynlightents > 1)
353 			{
354 				if(m_ctf(gamemode)) ctf::adddynlights();
355 				if(m_stf(gamemode)) stf::adddynlights();
356 			}
357 			if(fireburntime)
358 			{
359 				gameent *d = NULL;
360 				loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d->lastfire && lastmillis-d->lastfire <= fireburntime)
361 				{
362 					float pc = float((lastmillis-d->lastfire)%fireburndelay)/float(fireburndelay/2); pc = deadscale(d, pc > 1.f ? 2.f-pc : pc);
363 					adddynlight(d->headpos(-d->height*0.5f), d->height*(1.f+(pc*0.5f)+(rnd(50)/100.f)), vec(1.1f*max(pc,0.5f), 0.5f*max(pc,0.2f), 0.125f*pc));
364 				}
365 			}
366 		}
367 	}
368 
spawneffect(int type,const vec & o,int colour,int radius,float vel,int fade,float size,float blend)369 	void spawneffect(int type, const vec &o, int colour, int radius, float vel, int fade, float size, float blend)
370 	{
371 		regularshape(type, radius, colour, 21, 25, fade, o, size, blend, -vel, 0, vel*2);
372 		adddynlight(vec(o).add(vec(0, 0, radius)), radius*2, vec(colour>>16, (colour>>8)&0xFF, colour&0xFF).mul(2.f/0xFF).mul(blend), fade, fade/3);
373 	}
374 
impulseeffect(gameent * d,bool effect)375 	void impulseeffect(gameent *d, bool effect)
376 	{
377 		if(effect || (FWV(impulsestyle) && d->state == CS_ALIVE && (d->turnside || (d->action[AC_IMPULSE] && (!d->ai || d->move || d->strafe)))))
378 		{
379 			int num = int((effect ? 25 : 5)*impulsescale), len = effect ? impulsefade : impulsefade/5;
380 			if(num > 0 && len > 0)
381 			{
382 				if(d->type == ENT_PLAYER || d->type == ENT_AI)
383 				{
384 					regularshape(PART_FIREBALL, int(d->radius), firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->lfoot, 1, 0.5f, -15, 0, 5);
385 					regularshape(PART_FIREBALL, int(d->radius), firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->rfoot, 1, 0.5f, -15, 0, 5);
386 				}
387 				else regularshape(PART_FIREBALL, int(d->radius)*2, firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->feetpos(), 1, 0.5f, -15, 0, 5);
388 			}
389 		}
390 	}
391 
fireeffect(gameent * d)392 	void fireeffect(gameent *d)
393 	{
394 		if(fireburntime && d->lastfire && (d != player1 || thirdpersonview()) && lastmillis-d->lastfire <= fireburntime)
395 		{
396 			float pc = lastmillis-d->lastfire >= fireburntime-500 ? 1.f-((lastmillis-d->lastfire-(fireburntime-500))/500.f) : 1.f;
397 			regular_part_create(PART_FIREBALL_SOFT, max(int(fireburnfade*pc),1), d->headpos(-d->height*0.35f), firecols[rnd(FIRECOLOURS)], d->height*deadscale(d, 0.65f), 0.75f, -15, 0);
398 		}
399 	}
400 
pointatplayer()401 	gameent *pointatplayer()
402 	{
403 		loopv(players)
404 		{
405 			gameent *o = players[i];
406 			if(!o) continue;
407 			vec pos = player1->headpos();
408             float dist;
409 			if(intersect(o, pos, worldpos, dist)) return o;
410 		}
411 		return NULL;
412 	}
413 
setmode(int nmode,int nmuts)414 	void setmode(int nmode, int nmuts)
415 	{
416 		nextmode = nmode; nextmuts = nmuts;
417 		server::modecheck(&nextmode, &nextmuts);
418 	}
419 	ICOMMAND(mode, "ii", (int *val, int *mut), setmode(*val, *mut));
420 
checkcamera()421 	void checkcamera()
422 	{
423 		camera1 = &camera;
424 		if(camera1->type != ENT_CAMERA)
425 		{
426 			camera1->reset();
427 			camera1->type = ENT_CAMERA;
428             camera1->collidetype = COLLIDE_AABB;
429 			camera1->state = CS_ALIVE;
430 			camera1->height = camera1->zradius = camera1->radius = camera1->xradius = camera1->yradius = 2;
431 		}
432 		if((player1->state != CS_WAITING && player1->state != CS_SPECTATOR) || tvmode())
433 		{
434 			camera1->vel = vec(0, 0, 0);
435 			camera1->move = camera1->strafe = 0;
436 		}
437 	}
438 
resetcamera()439 	void resetcamera()
440 	{
441 		lastcamera = 0;
442 		zoomset(false, 0);
443 		resetcursor();
444 		checkcamera();
445 		camera1->o = player1->o;
446 		camera1->yaw = player1->yaw;
447 		camera1->pitch = player1->pitch;
448 		camera1->roll = player1->calcroll(false);
449 		camera1->resetinterp();
450 		player1->resetinterp();
451 	}
452 
resetworld()453 	void resetworld()
454 	{
455 		hud::sb.showscores(false);
456 		cleargui();
457 	}
458 
resetstate()459 	void resetstate()
460 	{
461 		resetworld();
462 		resetcamera();
463 	}
464 
heightoffset(gameent * d,bool local)465 	void heightoffset(gameent *d, bool local)
466 	{
467 		d->o.z -= d->height;
468 		if(d->state == CS_ALIVE)
469 		{
470 			if(physics::iscrouching(d))
471 			{
472 				bool crouching = d->action[AC_CROUCH];
473 				float crouchoff = 1.f-CROUCHHEIGHT;
474 				if(!crouching)
475 				{
476 					float z = d->o.z, zoff = d->zradius*crouchoff, zrad = d->zradius-zoff, frac = zoff/10.f;
477 					d->o.z += zrad;
478 					loopi(10)
479 					{
480 						d->o.z += frac;
481 						if(!collide(d, vec(0, 0, 1), 0.f, false))
482 						{
483 							crouching = true;
484 							break;
485 						}
486 					}
487 					if(crouching)
488 					{
489 						if(d->actiontime[AC_CROUCH] >= 0) d->actiontime[AC_CROUCH] = max(PHYSMILLIS-(lastmillis-d->actiontime[AC_CROUCH]), 0)-lastmillis;
490 					}
491 					else if(d->actiontime[AC_CROUCH] < 0)
492 						d->actiontime[AC_CROUCH] = lastmillis-max(PHYSMILLIS-(lastmillis+d->actiontime[AC_CROUCH]), 0);
493 					d->o.z = z;
494 				}
495 				if(d->type == ENT_PLAYER || d->type == ENT_AI)
496 				{
497 					int crouchtime = abs(d->actiontime[AC_CROUCH]);
498 					float amt = lastmillis-crouchtime <= PHYSMILLIS ? clamp(float(lastmillis-crouchtime)/PHYSMILLIS, 0.f, 1.f) : 1.f;
499 					if(!crouching) amt = 1.f-amt;
500 					crouchoff *= amt;
501 				}
502 				d->height = d->zradius-(d->zradius*crouchoff);
503 			}
504 			else d->height = d->zradius;
505 		}
506 		else d->height = d->zradius;
507 		d->o.z += d->height;
508 	}
509 
checkoften(gameent * d,bool local)510 	void checkoften(gameent *d, bool local)
511 	{
512 		d->checktags();
513 		adjustscaled(int, d->quake, quakefade);
514 		if(d->aitype <= AI_BOT) heightoffset(d, local);
515 		loopi(WEAP_MAX) if(d->weapstate[i] != WEAP_S_IDLE)
516 		{
517 			if(d->state != CS_ALIVE || (d->weapstate[i] != WEAP_S_POWER && lastmillis-d->weaplast[i] >= d->weapwait[i]))
518 				d->setweapstate(i, WEAP_S_IDLE, 0, lastmillis);
519 		}
520 		if(d->respawned > 0 && lastmillis-d->respawned >= PHYSMILLIS*4) d->respawned = -1;
521 		if(d->suicided > 0 && lastmillis-d->suicided >= PHYSMILLIS*4) d->suicided = -1;
522 		if(d->lastfire > 0)
523 		{
524 			if(lastmillis-d->lastfire >= fireburntime-500)
525 			{
526 				if(lastmillis-d->lastfire >= fireburntime)
527 				{
528 					if(issound(d->fschan)) removesound(d->fschan);
529 					d->fschan = -1; d->lastfire = 0;
530 				}
531 				else if(issound(d->fschan)) sounds[d->fschan].vol = int((d != player1 ? 128 : 224)*(1.f-(lastmillis-d->lastfire-(fireburntime-500))/500.f));
532 			}
533 		}
534 		else if(issound(d->fschan))
535 		{
536 			removesound(d->fschan);
537 			d->fschan = -1;
538 		}
539 	}
540 
541 
otherplayers()542 	void otherplayers()
543 	{
544 		loopv(players) if(players[i])
545 		{
546             gameent *d = players[i];
547             const int lagtime = lastmillis-d->lastupdate;
548             if(d->ai || !lagtime || intermission) continue;
549             //else if(lagtime > 1000) continue;
550 			physics::smoothplayer(d, 1, false);
551 		}
552 	}
553 
quake(const vec & o,int damage,int radius)554 	void quake(const vec &o, int damage, int radius)
555 	{
556 		gameent *d;
557         loopi(numdynents()) if((d = (gameent *)iterdynents(i)))
558 			d->quake = clamp(d->quake+max(int(damage*(1.f-d->o.dist(o)/EXPLOSIONSCALE/radius)*(m_insta(gamemode, mutators) ? 0.25f : 1.f)), 1), 0, 1000);
559 	}
560 
fireburn(gameent * d,int weap,int flags)561 	bool fireburn(gameent *d, int weap, int flags)
562 	{
563 		if(fireburntime && (doesburn(weap, flags) || flags&HIT_MELT || (weap == -1 && flags&HIT_BURN)))
564 		{
565 			if(!issound(d->fschan)) playsound(S_BURNFIRE, d->o, d, SND_LOOP, d != player1 ? 128 : 224, -1, -1, &d->fschan);
566 			if(flags&HIT_FULL) d->lastfire = lastmillis;
567 			else return true;
568 		}
569 		return false;
570 	}
571 
572     struct damagetone
573     {
574         enum { BURN = 1<<0 };
575 
576         gameent *actor;
577         int damage, flags;
578 
damagetonegame::damagetone579         damagetone() {}
damagetonegame::damagetone580         damagetone(gameent *actor, int damage, int flags) : actor(actor), damage(damage), flags(flags) {}
581 
mergegame::damagetone582         bool merge(const damagetone &m)
583         {
584             if(actor != m.actor || flags != m.flags) return false;
585             damage += m.damage;
586             return true;
587         }
588 
playgame::damagetone589         void play()
590         {
591             int snd = 0;
592             if(flags & BURN) snd = 8;
593             else if(damage >= 200) snd = 7;
594             else if(damage >= 150) snd = 6;
595             else if(damage >= 100) snd = 5;
596             else if(damage >= 75) snd = 4;
597             else if(damage >= 50) snd = 3;
598             else if(damage >= 25) snd = 2;
599             else if(damage >= 10) snd = 1;
600             playsound(S_DAMAGE1+snd, actor->o, actor, SND_FORCED, -1, -1, -1);
601         }
602     };
603     vector<damagetone> damagetones;
604 
removedamagetones(gameent * actor)605     void removedamagetones(gameent *actor)
606     {
607         loopvrev(damagetones) if(damagetones[i].actor == actor) damagetones.removeunordered(i);
608     }
609 
mergedamagetone(gameent * actor,int damage,int flags)610     void mergedamagetone(gameent *actor, int damage, int flags)
611     {
612         damagetone dt(actor, damage, flags);
613         loopv(damagetones) if(damagetones[i].merge(dt)) return;
614         damagetones.add(dt);
615     }
616 
flushdamagetones()617     void flushdamagetones()
618     {
619         loopv(damagetones) damagetones[i].play();
620         damagetones.setsizenodelete(0);
621     }
622 
623 	static int alarmchan = -1;
hiteffect(int weap,int flags,int damage,gameent * d,gameent * actor,vec & dir,bool local)624 	void hiteffect(int weap, int flags, int damage, gameent *d, gameent *actor, vec &dir, bool local)
625 	{
626 		bool burning = fireburn(d, weap, flags);
627 		if(!local || burning)
628 		{
629 			if(hithurts(flags))
630 			{
631 				if(d == player1) hud::damage(damage, actor->o, actor, weap);
632 				if(d->type == ENT_PLAYER || d->type == ENT_AI)
633 				{
634 					vec p = d->headpos();
635 					p.z += 0.6f*(d->height + d->aboveeye) - d->height;
636 					if(!isaitype(d->aitype) || aistyle[d->aitype].maxspeed)
637 					{
638 						if(!kidmode && bloodscale > 0)
639 							part_splash(PART_BLOOD, int(clamp(damage/2, 2, 10)*bloodscale), bloodfade, p, 0x88FFFF, 1.5f, 1, 100, DECAL_BLOOD, int(d->radius*4));
640 						else part_splash(PART_HINT, int(clamp(damage/2, 2, 10)), bloodfade, p, 0xFFFF88, 1.5f, 1, 50, DECAL_STAIN, int(d->radius*4));
641 					}
642 					if(showdamageabovehead > (d != player1 ? 0 : 1))
643 					{
644 						string ds;
645 						if(showdamageabovehead > 2) formatstring(ds)("<sub>-%d (%d%%)", damage, flags&HIT_HEAD ? 100 : (flags&HIT_TORSO ? 50 : 25));
646 						else formatstring(ds)("<sub>-%d", damage);
647 						part_textcopy(d->abovehead(), ds, PART_TEXT, aboveheadfade, 0x888888, 3, 1, -10, 0, d);
648 					}
649 					if(d->aitype < AI_START && !issound(d->vschan)) playsound(S_PAIN1+rnd(5), d->o, d, 0, -1, -1, -1, &d->vschan);
650 					if(!burning) d->quake = clamp(d->quake+max(damage/2, 1), 0, 1000);
651 					d->lastpain = lastmillis;
652 				}
653 				if(d != actor)
654 				{
655 					bool sameteam = m_team(gamemode, mutators) && d->team == actor->team;
656 					if(sameteam) { if(actor == player1 && !burning && !issound(alarmchan)) playsound(S_ALARM, actor->o, actor, 0, -1, -1, -1, &alarmchan); }
657 					else if(playdamagetones >= (actor == player1 ? 1 : (d == player1 ? 2 : 3))) mergedamagetone(actor, damage, burning ? damagetone::BURN : 0);
658 					if(!burning && !sameteam) actor->lasthit = lastmillis;
659 				}
660 			}
661 			if(isweap(weap) && !burning && (d == player1 || (d->ai && aistyle[d->aitype].maxspeed)))
662 			{
663 				float force = (float(damage)/float(weaptype[weap].damage[flags&HIT_ALT ? 1 : 0]))*(100.f/d->weight)*weaptype[weap].hitpush[flags&HIT_ALT ? 1 : 0];
664 				if(flags&HIT_WAVE || !hithurts(flags)) force *= wavepushscale;
665 				else if(d->health <= 0) force *= deadpushscale;
666 				else force *= hitpushscale;
667 				vec push = dir; push.z += 0.125f; push.mul(force);
668 				d->vel.add(push);
669 				if(flags&HIT_WAVE || flags&HIT_EXPLODE || weap == WEAP_MELEE) d->lastpush = lastmillis;
670 			}
671 		}
672 	}
673 
damaged(int weap,int flags,int damage,int health,gameent * d,gameent * actor,int millis,vec & dir)674 	void damaged(int weap, int flags, int damage, int health, gameent *d, gameent *actor, int millis, vec &dir)
675 	{
676 		if(d->state != CS_ALIVE || intermission) return;
677 		if(hithurts(flags))
678 		{
679 			d->dodamage(health);
680 			if(actor->type == ENT_PLAYER || actor->type == ENT_AI) actor->totaldamage += damage;
681 			ai::damaged(d, actor);
682 		}
683 		hiteffect(weap, flags, damage, d, actor, dir, actor == player1 || actor->ai);
684 	}
685 
killed(int weap,int flags,int damage,gameent * d,gameent * actor,int style)686 	void killed(int weap, int flags, int damage, gameent *d, gameent *actor, int style)
687 	{
688 		if(d->type != ENT_PLAYER && d->type != ENT_AI) return;
689 		bool burning = fireburn(d, weap, flags);
690         d->lastregen = 0;
691         d->lastpain = lastmillis;
692 		d->state = CS_DEAD;
693 		d->deaths++;
694 		int anc = -1, dth = d->aitype >= AI_START || style&FRAG_OBLITERATE ? S_SPLOSH : S_DIE1+rnd(2);
695 		if(d == player1) anc = !m_duke(gamemode, mutators) && !m_trial(gamemode) ? S_V_FRAGGED : -1;
696 		else d->resetinterp();
697 		formatstring(d->obit)("%s ", colorname(d));
698 		if(d != actor && actor->lastattacker == d->clientnum) actor->lastattacker = -1;
699 		d->lastattacker = actor->clientnum;
700 		if(d == actor)
701         {
702         	if(d->aitype == AI_TURRET) concatstring(d->obit, "was destroyed");
703         	else if(flags&HIT_DEATH) concatstring(d->obit, *obitdeath ? obitdeath : "died");
704         	else if(flags&HIT_WATER) concatstring(d->obit, *obitwater ? obitwater : "died");
705         	else if(flags&HIT_MELT) concatstring(d->obit, "melted into a ball of fire");
706 			else if(flags&HIT_SPAWN) concatstring(d->obit, "tried to spawn inside solid matter");
707 			else if(flags&HIT_LOST) concatstring(d->obit, "got very, very lost");
708         	else if(flags && isweap(weap) && !burning)
709         	{
710 				static const char *suicidenames[WEAP_MAX] = {
711 					"punched themself",
712 					"ate a bullet",
713 					"discovered buckshot bounces",
714 					"got caught in their own crossfire",
715 					"spontaneously combusted",
716 					"tried to make out with plasma",
717 					"got a good shock",
718 					"kicked it, kamikaze style",
719 					"pulled off an insta-stunt",
720 					"was gibbed"
721 				};
722         		concatstring(d->obit, suicidenames[weap]);
723         	}
724         	else if(flags&HIT_BURN || burning) concatstring(d->obit, "burned up");
725         	else if(style&FRAG_OBLITERATE) concatstring(d->obit, "was obliterated");
726         	else concatstring(d->obit, "suicided");
727         }
728 		else
729 		{
730 			concatstring(d->obit, "was ");
731 			if(d->aitype == AI_TURRET) concatstring(d->obit, "destroyed by");
732 			else
733 			{
734 				static const char *obitnames[4][WEAP_MAX] = {
735 					{
736 						"smacked down by",
737 						"pierced by",
738 						"sprayed with buckshot by",
739 						"riddled with holes by",
740 						"char-grilled by",
741 						"plasmified by",
742 						"laser shocked by",
743 						"blown to pieces by",
744 						"lasered by",
745 						"gibbed"
746 					},
747 					{
748 						"smacked down by",
749 						"pierced by",
750 						"filled with lead by",
751 						"spliced apart by",
752 						"fireballed by",
753 						"shown the light by",
754 						"was given laser burn by",
755 						"blown to pieces by",
756 						"was given laser burn by",
757 						"gibbed"
758 					},
759 					{
760 						"smacked down by",
761 						"capped by",
762 						"scrambled by",
763 						"air conditioned courtesy of",
764 						"char-grilled by",
765 						"plasmafied by",
766 						"expertly sniped by",
767 						"blown to pieces by",
768 						"expertly sniped by",
769 						"gibbed"
770 					},
771 					{
772 						"knocked into next week by",
773 						"skewered by",
774 						"turned into little chunks by",
775 						"swiss-cheesed by",
776 						"barbequed by chef",
777 						"reduced to ooze by",
778 						"given laser shock treatment by",
779 						"obliterated by",
780 						"lasered in half by",
781 						"gibbed"
782 					}
783 				};
784 
785 				int o = style&FRAG_OBLITERATE ? 3 : (style&FRAG_HEADSHOT ? 2 : (flags&HIT_ALT ? 1 : 0));
786 				concatstring(d->obit, burning ? "set ablaze by" : (isweap(weap) ? obitnames[o][weap] : "killed by"));
787 			}
788 			bool override = false;
789 			vec az = actor->abovehead(), dz = d->abovehead();
790 			if(!m_fight(gamemode) || actor->aitype >= AI_START)
791 			{
792 				concatstring(d->obit, " ");
793 				concatstring(d->obit, colorname(actor));
794 			}
795 			else if(m_team(gamemode, mutators) && d->team == actor->team)
796 			{
797 				concatstring(d->obit, " \fs\fzawteam-mate\fS ");
798 				concatstring(d->obit, colorname(actor));
799 				if(actor == player1) { anc = S_ALARM; override = true; }
800 			}
801 			else
802 			{
803 				if(style&FRAG_REVENGE)
804 				{
805 					concatstring(d->obit, " \fs\fzoyvengeful\fS");
806 					part_text(az, "<super>\fzoyAVENGED", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
807 					part_text(dz, "<super>\fzoyREVENGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4;
808 					if(actor == player1) d->dominated = false;
809 					else if(d == player1) actor->dominating = false;
810 					anc = S_V_REVENGE; override = true;
811 				}
812 				else if(style&FRAG_DOMINATE)
813 				{
814 					concatstring(d->obit, " \fs\fzoydominating\fS");
815 					part_text(az, "<super>\fzoyDOMINATING", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
816 					part_text(dz, "<super>\fzoyDOMINATED", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4;
817 					if(actor == player1) d->dominating = true;
818 					else if(d == player1) actor->dominated = true;
819 					anc = S_V_DOMINATE; override = true;
820 				}
821 				concatstring(d->obit, " ");
822 				concatstring(d->obit, colorname(actor));
823 
824 				if(style&FRAG_MKILL1)
825 				{
826 					concatstring(d->obit, " \fs\fzRedouble-killing\fS");
827 					part_text(az, "<super>\fzvrDOUBLE-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
828 					if(actor == player1) { part_text(dz, "<super>\fzvrDOUBLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; }
829 					if(!override) anc = S_V_MKILL1;
830 				}
831 				else if(style&FRAG_MKILL2)
832 				{
833 					concatstring(d->obit, " \fs\fzRetriple-killing\fS");
834 					part_text(az, "<super>\fzvrTRIPLE-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
835 					if(actor == player1) { part_text(dz, "<super>\fzvrTRIPLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; }
836 					if(!override) anc = S_V_MKILL1;
837 				}
838 				else if(style&FRAG_MKILL3)
839 				{
840 					concatstring(d->obit, " \fs\fzRemulti-killing\fS");
841 					part_text(az, "<super>\fzvrMULTI-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
842 					if(actor == player1) { part_text(dz, "<super>\fzvrMULTI", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; }
843 					if(!override) anc = S_V_MKILL1;
844 				}
845 			}
846 
847 			if(style&FRAG_HEADSHOT)
848 			{
849 				part_text(az, "<super>\fzcwHEADSHOT", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
850 				if(!override) anc = S_V_HEADSHOT;
851 			}
852 
853 			if(style&FRAG_SPREE1)
854 			{
855 				concatstring(d->obit, " in total \fs\fzcgcarnage\fS");
856 				part_text(az, "<super>\fzcgCARNAGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
857 				if(!override) anc = S_V_SPREE1;
858 				override = true;
859 			}
860 			else if(style&FRAG_SPREE2)
861 			{
862 				concatstring(d->obit, " on a \fs\fzcgslaughter\fS");
863 				part_text(az, "<super>\fzcgSLAUGHTER", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
864 				if(!override) anc = S_V_SPREE2;
865 				override = true;
866 			}
867 			else if(style&FRAG_SPREE3)
868 			{
869 				concatstring(d->obit, " on a \fs\fzcgmassacre\fS");
870 				part_text(az, "<super>\fzcgMASSACRE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
871 				if(!override) anc = S_V_SPREE3;
872 				override = true;
873 			}
874 			else if(style&FRAG_SPREE4)
875 			{
876 				concatstring(d->obit, " in a \fs\fzcgbloodbath\fS");
877 				part_text(az, "<super>\fzcgBLOODBATH", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
878 				if(!override) anc = S_V_SPREE4;
879 				override = true;
880 			}
881 			else if(style&FRAG_SPREE5)
882 			{
883 				concatstring(d->obit," on a \fs\fzcgrampage\fS");
884 				part_text(az, "<super>\fzcgRAMPAGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
885 				if(!override) anc = S_V_SPREE5;
886 				override = true;
887 			}
888 			else if(style&FRAG_SPREE6)
889 			{
890 				concatstring(d->obit, " who seems \fs\fzcgunstoppable\fS");
891 				part_text(az, "<super>\fzcgUNSTOPPABLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4;
892 				if(!override) anc = S_V_SPREE6;
893 				override = true;
894 			}
895 		}
896 		if(d != actor)
897 		{
898 			if(actor->state == CS_ALIVE) copystring(actor->obit, d->obit);
899 			actor->lastkill = lastmillis;
900 		}
901 		if(dth >= 0)
902 		{
903 			if(issound(d->vschan)) removesound(d->vschan);
904 			playsound(dth, d->o, d, 0, -1, -1, -1, &d->vschan);
905 		}
906 		if(showobituaries && (d->aitype < AI_START || actor->aitype < (d->aitype >= AI_START ? AI_BOT : AI_START)))
907 		{
908 			bool isme = (d == player1 || actor == player1), show = false;
909 			if(((!m_fight(gamemode) && !isme) || actor->aitype >= AI_START) && anc >= 0) anc = -1;
910 			if(flags&HIT_LOST) show = true;
911 			else switch(showobituaries)
912 			{
913 				case 1: if(isme || m_duke(gamemode, mutators)) show = true; break;
914 				case 2: if(isme || anc >= 0 || m_duke(gamemode, mutators)) show = true; break;
915 				case 3: if(isme || d->aitype < 0 || anc >= 0 || m_duke(gamemode, mutators)) show = true; break;
916 				case 4: if(isme || d->aitype < 0 || actor->aitype < 0 || anc >= 0 || m_duke(gamemode, mutators)) show = true; break;
917 				case 5: default: show = true; break;
918 			}
919 			int target = show ? (isme ? CON_SELF : CON_INFO) : -1;
920 			if(showobitdists) announce(anc, target, d, "\fs\fw%s\fS (@\fs\fc%.2f\fSm)", d->obit, actor->o.dist(d->o)/8.f);
921 			else announce(anc, target, d, "\fw%s", d->obit);
922 		}
923 		if(gibscale > 0)
924 		{
925 			vec pos = vec(d->o).sub(vec(0, 0, d->height*0.5f));
926 			int gibs = clamp(max(damage,5)/5, 1, 10), amt = int((rnd(gibs)+gibs+1)*gibscale);
927 			loopi(amt)
928 				projs::create(pos, vec(pos).add(d->vel), true, d, !isaitype(d->aitype) || aistyle[d->aitype].maxspeed ? PRJ_GIBS : PRJ_DEBRIS, (gibfade ? rnd(gibfade)+(gibfade/10) : 1000), 0, rnd(500)+1, 50);
929 		}
930 		if(m_team(gamemode, mutators) && d->team == actor->team && d != actor && actor == player1)
931 		{
932 			hud::teamkills.add(lastmillis);
933 			if(hud::numteamkills() >= hud::teamkillnum) hud::lastteam = lastmillis;
934 		}
935 		ai::killed(d, actor);
936 	}
937 
timeupdate(int timeremain)938 	void timeupdate(int timeremain)
939 	{
940 		minremain = timeremain;
941 		if(!timeremain && !intermission)
942 		{
943 			player1->stopmoving(true);
944 			hud::sb.showscores(true, true);
945 			intermission = true;
946 			smartmusic(true, false);
947 		}
948 	}
949 
newclient(int cn)950 	gameent *newclient(int cn)
951 	{
952 		if(cn < 0 || cn >= MAXPLAYERS)
953 		{
954 			defformatstring(cnmsg)("clientnum [%d]", cn);
955 			neterr(cnmsg);
956 			return NULL;
957 		}
958 
959 		if(cn == player1->clientnum) return player1;
960 
961 		while(cn >= players.length()) players.add(NULL);
962 
963 		if(!players[cn])
964 		{
965 			gameent *d = new gameent();
966 			d->clientnum = cn;
967 			players[cn] = d;
968 		}
969 
970 		return players[cn];
971 	}
972 
getclient(int cn)973 	gameent *getclient(int cn)
974 	{
975 		if(cn == player1->clientnum) return player1;
976 		if(players.inrange(cn)) return players[cn];
977 		return NULL;
978 	}
979 
clientdisconnected(int cn)980 	void clientdisconnected(int cn)
981 	{
982 		if(!players.inrange(cn)) return;
983 		gameent *d = players[cn];
984 		if(!d) return;
985 		if(d->name[0] && showplayerinfo && (d->aitype < 0 || ai::showaiinfo))
986 			conoutft(showplayerinfo > 1 ? int(CON_EVENT) : int(CON_MESG), "\fo%s left the game", colorname(d));
987 		loopv(client::mapvotes) if(client::mapvotes[i].player == d) { client::mapvotes.remove(i); break; }
988 		projs::remove(d);
989         removedamagetones(d);
990         if(m_ctf(gamemode)) ctf::removeplayer(d);
991         if(m_stf(gamemode)) stf::removeplayer(d);
992 		DELETEP(players[cn]);
993 		players[cn] = NULL;
994 		cleardynentcache();
995 	}
996 
preload()997     void preload()
998     {
999 		maskpackagedirs(~PACKAGEDIR_OCTA);
1000     	int n = m_fight(gamemode) && m_team(gamemode, mutators) ? numteams(gamemode, mutators)+1 : 1;
1001     	loopi(n)
1002     	{
1003 			loadmodel(teamtype[i].tpmdl, -1, true);
1004 			loadmodel(teamtype[i].fpmdl, -1, true);
1005     	}
1006     	weapons::preload();
1007 		projs::preload();
1008         entities::preload();
1009 		if(m_edit(gamemode) || m_stf(gamemode)) stf::preload();
1010         if(m_edit(gamemode) || m_ctf(gamemode)) ctf::preload();
1011 		maskpackagedirs(~0);
1012     }
1013 
resetmap(bool empty)1014 	void resetmap(bool empty) // called just before a map load
1015 	{
1016 		if(!empty) smartmusic(true, false);
1017 	}
1018 
startmap(const char * name,const char * reqname,bool empty)1019 	void startmap(const char *name, const char *reqname, bool empty)	// called just after a map load
1020 	{
1021 		intermission = false;
1022 		maptime = 0;
1023 		projs::reset();
1024 		resetworld();
1025 		if(*name)
1026 		{
1027 			conoutft(CON_MESG, "\fs\fw%s by %s [\fa%s\fS]", *maptitle ? maptitle : "Untitled", *mapauthor ? mapauthor : "Unknown", server::gamename(gamemode, mutators));
1028 			preload();
1029 		}
1030 		// reset perma-state
1031 		gameent *d;
1032 		loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && (d->type == ENT_PLAYER || d->type == ENT_AI))
1033 			d->mapchange(lastmillis, m_health(gamemode, mutators));
1034 		entities::spawnplayer(player1, -1, false); // prevent the player from being in the middle of nowhere
1035 		resetcamera();
1036 		if(!empty) client::sendinfo = client::sendcrc = true;
1037 		fogdist = max(getvar("fog")-16, 64);
1038         copystring(clientmap, reqname ? reqname : (name ? name : ""));
1039 		resetsway();
1040 	}
1041 
intersectclosest(vec & from,vec & to,gameent * at)1042 	gameent *intersectclosest(vec &from, vec &to, gameent *at)
1043 	{
1044 		gameent *best = NULL, *o;
1045 		float bestdist = 1e16f;
1046 		loopi(numdynents()) if((o = (gameent *)iterdynents(i)))
1047 		{
1048             if(!o || o==at || o->state!=CS_ALIVE || !physics::issolid(o)) continue;
1049             float dist;
1050 			if(intersect(o, from, to, dist) && dist < bestdist)
1051 			{
1052 				best = o;
1053 				bestdist = dist;
1054 			}
1055 		}
1056 		return best;
1057 	}
1058 
numdynents()1059 	int numdynents() { return 1+players.length(); }
iterdynents(int i)1060 	dynent *iterdynents(int i)
1061 	{
1062 		if(!i) return player1;
1063 		i--;
1064 		if(i<players.length()) return players[i];
1065 		i -= players.length();
1066 		return NULL;
1067 	}
1068 
duplicatename(gameent * d,char * name=NULL)1069 	bool duplicatename(gameent *d, char *name = NULL)
1070 	{
1071 		if(!name) name = d->name;
1072 		if(d!=player1 && !strcmp(name, player1->name)) return true;
1073 		loopv(players) if(players[i] && d!=players[i] && !strcmp(name, players[i]->name)) return true;
1074 		return false;
1075 	}
1076 
colorname(gameent * d,char * name,const char * prefix,bool team,bool dupname)1077 	char *colorname(gameent *d, char *name, const char *prefix, bool team, bool dupname)
1078 	{
1079 		if(!name) name = d->name;
1080 		static string cname;
1081 		formatstring(cname)("%s\fs%s%s", *prefix ? prefix : "", teamtype[d->team].chat, name);
1082 		if(!name[0] || d->aitype >= 0 || (dupname && duplicatename(d, name)))
1083 		{
1084 			defformatstring(s)(" [\fs\fc%s%d\fS]", d->aitype >= 0 ? "\fe" : "", d->clientnum);
1085 			concatstring(cname, s);
1086 		}
1087 		concatstring(cname, "\fS");
1088 		return cname;
1089 	}
1090 
suicide(gameent * d,int flags)1091 	void suicide(gameent *d, int flags)
1092 	{
1093 		if((d == player1 || d->ai) && d->state == CS_ALIVE && d->suicided < 0)
1094 		{
1095 			fireburn(d, -1, flags);
1096 			client::addmsg(SV_SUICIDE, "ri2", d->clientnum, flags);
1097 			d->suicided = lastmillis;
1098 		}
1099 	}
1100 	ICOMMAND(kill, "",  (), { suicide(player1, 0); });
1101 
lighteffects(dynent * e,vec & color,vec & dir)1102 	void lighteffects(dynent *e, vec &color, vec &dir) { }
1103 
particletrack(particle * p,uint type,int & ts,bool lastpass)1104     void particletrack(particle *p, uint type, int &ts,  bool lastpass)
1105     {
1106         if(!p || !p->owner || (p->owner->type != ENT_PLAYER && p->owner->type != ENT_AI)) return;
1107 		gameent *d = (gameent *)p->owner;
1108         switch(type&0xFF)
1109         {
1110         	case PT_TEXT: case PT_ICON:
1111         	{
1112         		vec q = p->owner->abovehead(); q.z = p->o.z;
1113 				float k = pow(aboveheadsmooth, float(curtime)/float(aboveheadsmoothmillis));
1114 				p->o.mul(k).add(q.mul(1-k));
1115         		break;
1116         	}
1117         	case PT_TAPE: case PT_LIGHTNING:
1118         	{
1119         		float dist = p->o.dist(p->d);
1120 				p->d = p->o = d->muzzlepos(d->weapselect);
1121         		vec dir; vecfromyawpitch(d->yaw, d->pitch, 1, 0, dir);
1122         		p->d.add(dir.mul(dist));
1123 				break;
1124         	}
1125         	case PT_PART: case PT_FIREBALL: case PT_FLARE:
1126         	{
1127 				p->o = d->muzzlepos(d->weapselect);
1128 				break;
1129         	}
1130         	default: break;
1131         }
1132     }
1133 
dynlighttrack(physent * owner,vec & o)1134     void dynlighttrack(physent *owner, vec &o) { }
1135 
newmap(int size)1136 	void newmap(int size) { client::addmsg(SV_NEWMAP, "ri", size); }
1137 
loadworld(stream * f,int maptype)1138 	void loadworld(stream *f, int maptype) { }
saveworld(stream * f)1139 	void saveworld(stream *f) { }
1140 
fixfullrange(float & yaw,float & pitch,float & roll,bool full)1141 	void fixfullrange(float &yaw, float &pitch, float &roll, bool full)
1142 	{
1143 		if(full)
1144 		{
1145 			while(pitch < -180.0f) pitch += 360.0f;
1146 			while(pitch >= 180.0f) pitch -= 360.0f;
1147 			while(roll < -180.0f) roll += 360.0f;
1148 			while(roll >= 180.0f) roll -= 360.0f;
1149 		}
1150 		else
1151 		{
1152 			if(pitch > 89.9f) pitch = 89.9f;
1153 			if(pitch < -89.9f) pitch = -89.9f;
1154 			if(roll > 89.9f) roll = 89.9f;
1155 			if(roll < -89.9f) roll = -89.9f;
1156 		}
1157 		while(yaw < 0.0f) yaw += 360.0f;
1158 		while(yaw >= 360.0f) yaw -= 360.0f;
1159 	}
1160 
fixrange(float & yaw,float & pitch)1161 	void fixrange(float &yaw, float &pitch)
1162 	{
1163 		float r = 0.f;
1164 		fixfullrange(yaw, pitch, r, false);
1165 	}
1166 
fixview(int w,int h)1167 	void fixview(int w, int h)
1168 	{
1169 		if(inzoom())
1170 		{
1171 			int frame = lastmillis-lastzoom, f = zoomfov, t = zoomtime;
1172 			checkzoom();
1173 			if(zoomlevels > 1 && zoomlevel < zoomlevels) f = fov()-(((fov()-zoomfov)/zoomlevels)*zoomlevel);
1174 			float diff = float(fov()-f), amt = frame < t ? clamp(float(frame)/float(t), 0.f, 1.f) : 1.f;
1175 			if(!zooming) amt = 1.f-amt;
1176 			curfov = fov()-(amt*diff);
1177 		}
1178 		else curfov = float(fov());
1179 	}
1180 
mousemove(int dx,int dy,int x,int y,int w,int h)1181 	bool mousemove(int dx, int dy, int x, int y, int w, int h)
1182 	{
1183 		bool hascursor = UI::hascursor(true);
1184 		#define mousesens(a,b,c) ((float(a)/float(b))*c)
1185 		if(hascursor || (mousestyle() >= 1 && player1->state != CS_WAITING && player1->state != CS_SPECTATOR))
1186 		{
1187 			if(mouseabsolute) // absolute positions, unaccelerated
1188 			{
1189 				cursorx = clamp(float(x)/float(w), 0.f, 1.f);
1190 				cursory = clamp(float(y)/float(h), 0.f, 1.f);
1191 				return false;
1192 			}
1193 			else
1194 			{
1195 				cursorx = clamp(cursorx+mousesens(dx, w, mousesensitivity), 0.f, 1.f);
1196 				cursory = clamp(cursory+mousesens(dy, h, mousesensitivity*(!hascursor && mouseinvert ? -1.f : 1.f)), 0.f, 1.f);
1197 				return true;
1198 			}
1199 		}
1200 		else if(!tvmode())
1201 		{
1202 			physent *target = player1->state == CS_WAITING || player1->state == CS_SPECTATOR ? camera1 : (allowmove(player1) ? player1 : NULL);
1203 			if(target)
1204 			{
1205 				float scale = (inzoom() && zoomsensitivity > 0 && zoomsensitivity < 1 ? 1.f-(zoomlevel/float(zoomlevels+1)*zoomsensitivity) : 1.f)*sensitivity;
1206 				target->yaw += mousesens(dx, w, yawsensitivity*scale);
1207 				target->pitch -= mousesens(dy, h, pitchsensitivity*scale*(!hascursor && mouseinvert ? -1.f : 1.f));
1208 				fixfullrange(target->yaw, target->pitch, target->roll, false);
1209 			}
1210 			return true;
1211 		}
1212 		return false;
1213 	}
1214 
project(int w,int h)1215 	void project(int w, int h)
1216 	{
1217 		int style = UI::hascursor() ? -1 : mousestyle();
1218 		if(style != lastmousetype)
1219 		{
1220 			resetcursor();
1221 			lastmousetype = style;
1222 		}
1223 		if(style >= 0) vecfromcursor(cursorx, cursory, 1.f, cursordir);
1224 	}
1225 
1226 	struct camstate
1227 	{
1228 		int ent, idx;
1229 		vec pos, dir;
1230 		vector<int> cansee;
1231 		float mindist, maxdist, score;
1232 		bool alter;
1233 
camstategame::camstate1234 		camstate() : idx(-1), mindist(32.f), maxdist(512.f), alter(false) { reset(); }
~camstategame::camstate1235 		~camstate() {}
1236 
resetgame::camstate1237 		void reset()
1238 		{
1239 			cansee.setsize(0);
1240 			dir = vec(0, 0, 0);
1241 			score = 0.f;
1242 			alter = false;
1243 		}
1244 
camsortgame::camstate1245 		static int camsort(const camstate *a, const camstate *b)
1246 		{
1247 			int asee = a->cansee.length(), bsee = b->cansee.length();
1248 			if(a->alter && asee) asee = 1;
1249 			if(b->alter && bsee) bsee = 1;
1250 			if(asee > bsee) return -1;
1251 			if(asee < bsee) return 1;
1252 			if(a->score < b->score) return -1;
1253 			if(a->score > b->score) return 1;
1254 			return 0;
1255 		}
1256 	};
1257 	vector<camstate> cameras;
1258 
getyawpitch(const vec & from,const vec & pos,float & yaw,float & pitch)1259 	void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch)
1260 	{
1261 		float dist = from.dist(pos);
1262 		yaw = -(float)atan2(pos.x-from.x, pos.y-from.y)/PI*180+180;
1263 		pitch = asin((pos.z-from.z)/dist)/RAD;
1264 	}
1265 
scaleyawpitch(float & yaw,float & pitch,float targyaw,float targpitch,float frame,float scale)1266 	void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float frame, float scale)
1267 	{
1268 		if(yaw < targyaw-180.0f) yaw += 360.0f;
1269 		if(yaw > targyaw+180.0f) yaw -= 360.0f;
1270 		float offyaw = fabs(targyaw-yaw)*frame, offpitch = fabs(targpitch-pitch)*frame*scale;
1271 		if(targyaw > yaw)
1272 		{
1273 			yaw += offyaw;
1274 			if(targyaw < yaw) yaw = targyaw;
1275 		}
1276 		else if(targyaw < yaw)
1277 		{
1278 			yaw -= offyaw;
1279 			if(targyaw > yaw) yaw = targyaw;
1280 		}
1281 		if(targpitch > pitch)
1282 		{
1283 			pitch += offpitch;
1284 			if(targpitch < pitch) pitch = targpitch;
1285 		}
1286 		else if(targpitch < pitch)
1287 		{
1288 			pitch -= offpitch;
1289 			if(targpitch > pitch) pitch = targpitch;
1290 		}
1291 		fixrange(yaw, pitch);
1292 	}
1293 
cameraplayer()1294 	void cameraplayer()
1295 	{
1296 		if(player1->state != CS_WAITING && player1->state != CS_SPECTATOR && player1->state != CS_DEAD && !tvmode())
1297 		{
1298 			player1->aimyaw = camera1->yaw;
1299 			player1->aimpitch = camera1->pitch;
1300 			fixrange(player1->aimyaw, player1->aimpitch);
1301 			if(lastcamera && mousestyle() >= 1 && !UI::hascursor())
1302 			{
1303 				physent *d = mousestyle() != 2 ? player1 : camera1;
1304 				float amt = clamp(float(lastmillis-lastcamera)/100.f, 0.f, 1.f)*panspeed();
1305 				float zone = float(deadzone())/200.f, cx = cursorx-0.5f, cy = 0.5f-cursory;
1306 				if(cx > zone || cx < -zone) d->yaw += ((cx > zone ? cx-zone : cx+zone)/(1.f-zone))*amt;
1307 				if(cy > zone || cy < -zone) d->pitch += ((cy > zone ? cy-zone : cy+zone)/(1.f-zone))*amt;
1308 				fixfullrange(d->yaw, d->pitch, d->roll, false);
1309 			}
1310 		}
1311 	}
1312 
cameratv()1313 	void cameratv()
1314 	{
1315 		bool isspec = player1->state == CS_SPECTATOR;
1316 		if(cameras.empty()) loopk(2)
1317 		{
1318 			physent d = *player1;
1319 			d.radius = d.height = 4.f;
1320 			d.state = CS_ALIVE;
1321 			loopv(entities::ents) if(entities::ents[i]->type == CAMERA || (k && !enttype[entities::ents[i]->type].noisy))
1322 			{
1323 				gameentity &e = *(gameentity *)entities::ents[i];
1324 				vec pos(e.o);
1325 				if(e.type == MAPMODEL)
1326 				{
1327 					mapmodelinfo &mmi = getmminfo(e.attrs[0]);
1328 					vec center, radius;
1329 					mmi.m->collisionbox(0, center, radius);
1330 					if(!mmi.m->ellipsecollide) rotatebb(center, radius, int(e.attrs[1]));
1331 					pos.z += ((center.z-radius.z)+radius.z*2*mmi.m->height)*3.f;
1332 				}
1333 				else if(enttype[e.type].radius) pos.z += enttype[e.type].radius;
1334 				d.o = pos;
1335 				if(physics::entinmap(&d, false))
1336 				{
1337 					camstate &c = cameras.add();
1338 					c.pos = pos;
1339 					c.ent = i;
1340 					if(!k)
1341 					{
1342 						c.idx = e.attrs[0];
1343 						if(e.attrs[1]) c.mindist = e.attrs[1];
1344 						if(e.attrs[2]) c.maxdist = e.attrs[2];
1345 					}
1346 				}
1347 			}
1348 			lasttvcam = lasttvchg = 0;
1349 			if(!cameras.empty()) break;
1350 		}
1351 		#define unsettvmode(q) \
1352 		{ \
1353 			if(q) \
1354 			{ \
1355 				camera1->o.x = camera1->o.y = camera1->o.z = getworldsize(); \
1356 				camera1->o.x *= 0.5f; camera1->o.y *= 0.5f; \
1357 			} \
1358 			camera1->resetinterp(); \
1359 			setvar(isspec ? "specmode" : "waitmode", 0, true); \
1360 			return; \
1361 		}
1362 
1363 		if(!cameras.empty())
1364 		{
1365 			camstate *cam = &cameras[0];
1366 			int entidx = cam->ent, len = (isspec ? spectvtime : waittvtime);
1367 			bool alter = cam->alter, renew = !lasttvcam || lastmillis-lasttvcam >= len,
1368 				override = renew || !lasttvcam || lastmillis-lasttvcam >= max(len/10, 1500);
1369 			#define addcamentity(q,p) \
1370 			{ \
1371 				vec trg, pos = p; \
1372 				float dist = c.pos.dist(pos); \
1373 				if(dist >= c.mindist && dist <= min(c.maxdist, float(fogdist)) && raycubelos(c.pos, pos, trg)) \
1374 				{ \
1375 					c.cansee.add(q); \
1376 					avg.add(pos); \
1377 				} \
1378 			}
1379 			#define updatecamorient \
1380 			{ \
1381 				if((k || j) && c.cansee.length()) \
1382 				{ \
1383 					vec dir = vec(avg).div(c.cansee.length()).sub(c.pos).normalize(); \
1384 					vectoyawpitch(dir, yaw, pitch); \
1385 				} \
1386 			}
1387 			#define dircamentity(q,p) \
1388 			{ \
1389 				vec trg, pos = p; \
1390 				if(getsight(c.pos, yaw, pitch, pos, trg, min(c.maxdist, float(fogdist)), curfov, fovy)) \
1391 				{ \
1392 					c.dir.add(pos); \
1393 					c.score += c.alter ? rnd(32) : c.pos.dist(pos); \
1394 				} \
1395 				else \
1396 				{ \
1397 					avg.sub(pos); \
1398 					c.cansee.remove(q); \
1399 					updatecamorient; \
1400 				} \
1401 			}
1402 			loopk(2)
1403 			{
1404 				int found = 0;
1405 				loopvj(cameras)
1406 				{
1407 					camstate &c = cameras[j];
1408 					vec avg(0, 0, 0);
1409 					c.reset();
1410 					switch(k)
1411 					{
1412 						case 0: default:
1413 						{
1414 							gameent *d;
1415 							loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d->aitype < AI_START && (d->state == CS_ALIVE || d->state == CS_DEAD || d->state == CS_WAITING))
1416 								addcamentity(i, d->feetpos());
1417 							break;
1418 						}
1419 						case 1:
1420 						{
1421 							c.alter = true;
1422 							loopv(entities::ents) if(entities::ents[i]->type == WEAPON || entities::ents[i]->type == FLAG)
1423 								addcamentity(i, entities::ents[i]->o);
1424 							break;
1425 						}
1426 					}
1427 					float yaw = camera1->yaw, pitch = camera1->pitch;
1428 					updatecamorient;
1429 					switch(k)
1430 					{
1431 						case 0: default:
1432 						{
1433 							gameent *d;
1434 							loopvrev(c.cansee) if((d = (gameent *)iterdynents(c.cansee[i])))
1435 								dircamentity(i, d->feetpos());
1436 							break;
1437 						}
1438 						case 1:
1439 						{
1440 							loopvrev(c.cansee) if(entities::ents.inrange(c.cansee[i]))
1441 								dircamentity(i, entities::ents[c.cansee[i]]->o);
1442 							break;
1443 						}
1444 					}
1445 					if(!c.cansee.empty())
1446 					{
1447 						float amt = float(c.cansee.length());
1448 						c.dir.div(amt);
1449 						c.score /= amt;
1450 						found++;
1451 					}
1452 					else
1453 					{
1454 						c.score = 0;
1455 						if(override && !k && !j && !alter) renew = true; // quick scotty, get a new cam
1456 					}
1457 					if(!renew || !override) break;
1458 				}
1459 				if(override && !found && (k || !alter))
1460 				{
1461 					if(!k) renew = true;
1462 					else unsettvmode(lasttvcam ? false : true);
1463 				}
1464 				else break;
1465 			}
1466 			if(renew)
1467 			{
1468 				cameras.sort(camstate::camsort);
1469 				cam = &cameras[0];
1470 				lasttvcam = lastmillis;
1471 				if(!lasttvchg || cam->ent != entidx) lasttvchg = lastmillis;
1472 			}
1473 			else if(alter && !cam->cansee.length()) cam->alter = true;
1474 			camera1->o = cam->pos;
1475 			if(cam->ent != entidx || !cam->alter)
1476 			{
1477 				vec dir = vec(cam->dir).sub(camera1->o).normalize();
1478 				vectoyawpitch(dir, camera1->aimyaw, camera1->aimpitch);
1479 			}
1480 			if(cam->ent != entidx || cam->alter) { camera1->yaw = camera1->aimyaw; camera1->pitch = camera1->aimpitch; }
1481 			else
1482 			{
1483 				float speed = isspec ? spectvspeed : waittvspeed, scale = isspec ? spectvpitch : waittvpitch;
1484 				if(speed > 0) scaleyawpitch(camera1->yaw, camera1->pitch, camera1->aimyaw, camera1->aimpitch, (float(curtime)/1000.f)*speed, scale);
1485 			}
1486 			camera1->resetinterp();
1487 		}
1488 		else unsettvmode(true);
1489 	}
1490 
updateworld()1491 	void updateworld()		// main game update loop
1492 	{
1493 		if(connected())
1494 		{
1495 			if(!maptime) { maptime = -1; return; } // skip the first loop
1496 			else if(maptime < 0)
1497 			{
1498 				maptime = lastmillis;
1499 				//if(m_lobby(gamemode)) smartmusic(true, false);
1500 				//else
1501 				if(*mapmusic && (!music || !Mix_PlayingMusic() || strcmp(mapmusic, musicfile))) playmusic(mapmusic, "");
1502 				else musicdone(false);
1503 				RUNWORLD("on_start");
1504 				return;
1505 			}
1506 		}
1507         if(!curtime) { gets2c(); if(player1->clientnum >= 0) client::c2sinfo(); return; }
1508 
1509        	if(!*player1->name && !menuactive()) showgui("name");
1510         if(connected())
1511         {
1512         	player1->conopen = commandmillis > 0 || UI::hascursor(true);
1513             // do shooting/projectile update here before network update for greater accuracy with what the player sees
1514 			if(allowmove(player1)) cameraplayer();
1515 			else player1->stopmoving(player1->state != CS_WAITING && player1->state != CS_SPECTATOR);
1516 
1517             gameent *d = NULL;
1518             loopi(numdynents()) if((d = (gameent *)iterdynents(i)) != NULL && (d->type == ENT_PLAYER || d->type == ENT_AI))
1519             {
1520 				checkoften(d, d == player1 || d->ai);
1521 				if(d == player1)
1522 				{
1523 					int state = d->weapstate[d->weapselect];
1524 					if(weaptype[d->weapselect].zooms)
1525 					{
1526 						if(state == WEAP_S_SHOOT || (state == WEAP_S_RELOAD && lastmillis-d->weaplast[d->weapselect] >= max(d->weapwait[d->weapselect]-zoomtime, 1)))
1527 							state = WEAP_S_IDLE;
1528 					}
1529 					if(zooming && (!weaptype[d->weapselect].zooms || state != WEAP_S_IDLE)) zoomset(false, lastmillis);
1530 					else if(weaptype[d->weapselect].zooms && state == WEAP_S_IDLE && zooming != d->action[AC_ALTERNATE])
1531 						zoomset(d->action[AC_ALTERNATE], lastmillis);
1532 				}
1533             }
1534 
1535             physics::update();
1536             projs::update();
1537 			ai::update();
1538             if(!intermission)
1539             {
1540 				entities::update();
1541 				if(player1->state == CS_ALIVE) weapons::shoot(player1, worldpos);
1542             }
1543             otherplayers();
1544         }
1545         else if(!menuactive()) showgui("main");
1546 
1547 		gets2c();
1548 		adjustscaled(int, hud::damageresidue, hud::damageresiduefade);
1549 		if(connected())
1550 		{
1551             flushdamagetones();
1552 			if(player1->state == CS_DEAD || player1->state == CS_WAITING)
1553 			{
1554 				if(player1->ragdoll) moveragdoll(player1, true);
1555 				else if(lastmillis-player1->lastpain <= 2000)
1556 					physics::move(player1, 10, false);
1557 			}
1558 			else
1559             {
1560                 if(player1->ragdoll) cleanragdoll(player1);
1561 				if(player1->state == CS_EDITING) physics::move(player1, 10, true);
1562 				else if(!intermission && player1->state == CS_ALIVE)
1563 				{
1564 					physics::move(player1, 10, true);
1565 					addsway(player1);
1566 					entities::checkitems(player1);
1567 					weapons::reload(player1);
1568 				}
1569             }
1570 			checkcamera();
1571 			if(player1->state == CS_DEAD)
1572 			{
1573 				gameent *a = deathcamstyle ? (deathcamstyle == 2 ? player1 : getclient(player1->lastattacker)) : NULL;
1574 				if(a)
1575 				{
1576 					vec dir = vec(a->headpos(-a->height*0.5f)).sub(camera1->o).normalize();
1577 					float yaw = camera1->yaw, pitch = camera1->pitch;
1578 					vectoyawpitch(dir, yaw, pitch);
1579 					if(deathcamspeed > 0) scaleyawpitch(camera1->yaw, camera1->pitch, yaw, pitch, (float(curtime)/1000.f)*deathcamspeed, 4.f);
1580 					camera1->aimyaw = camera1->yaw;
1581 					camera1->aimpitch = camera1->pitch;
1582 				}
1583 			}
1584 			else if(tvmode()) cameratv();
1585 			else if(player1->state == CS_WAITING || player1->state == CS_SPECTATOR)
1586 			{
1587 				camera1->move = player1->move;
1588 				camera1->strafe = player1->strafe;
1589 				physics::move(camera1, 10, true);
1590 			}
1591 			if(player1->state == CS_SPECTATOR)
1592 			{
1593 				player1->aimyaw = player1->yaw = camera1->yaw;
1594 				player1->aimpitch = player1->pitch = camera1->pitch;
1595 				player1->o = camera1->o;
1596 				player1->resetinterp();
1597 			}
1598             if(hud::sb.canshowscores()) hud::sb.showscores(true);
1599 		}
1600 
1601 		if(player1->clientnum >= 0) client::c2sinfo();
1602 	}
1603 
recomputecamera(int w,int h)1604 	void recomputecamera(int w, int h)
1605 	{
1606 		fixview(w, h);
1607 		checkcamera();
1608 		if(client::ready())
1609 		{
1610 			if(!lastcamera)
1611 			{
1612 				resetcursor();
1613 				cameras.setsize(0);
1614 				if(mousestyle() == 2 && player1->state != CS_WAITING && player1->state != CS_SPECTATOR)
1615 				{
1616 					camera1->yaw = player1->aimyaw = player1->yaw;
1617 					camera1->pitch = player1->aimpitch = player1->pitch;
1618 				}
1619 			}
1620 
1621 			if(player1->state == CS_DEAD || player1->state == CS_WAITING || player1->state == CS_SPECTATOR)
1622 			{
1623 				camera1->aimyaw = camera1->yaw;
1624 				camera1->aimpitch = camera1->pitch;
1625 			}
1626 			else
1627 			{
1628 				camera1->o = player1->headpos();
1629 				if(mousestyle() <= 1)
1630 					findorientation(camera1->o, player1->yaw, player1->pitch, worldpos);
1631 
1632 				camera1->aimyaw = mousestyle() <= 1 ? player1->yaw : player1->aimyaw;
1633 				camera1->aimpitch = mousestyle() <= 1 ? player1->pitch : player1->aimpitch;
1634 				if(thirdpersonview(true) && thirdpersondist)
1635 				{
1636 					vec dir;
1637 					vecfromyawpitch(camera1->aimyaw, camera1->aimpitch, thirdpersondist > 0 ? -1 : 1, 0, dir);
1638 					physics::movecamera(camera1, dir, fabs(thirdpersondist), 1.0f);
1639 				}
1640                 camera1->resetinterp();
1641 
1642 				switch(mousestyle())
1643 				{
1644 					case 0:
1645 					case 1:
1646 					{
1647 						camera1->yaw = player1->yaw;
1648 						camera1->pitch = player1->pitch;
1649 						if(mousestyle())
1650 						{
1651 							camera1->aimyaw = camera1->yaw;
1652 							camera1->aimpitch = camera1->pitch;
1653 						}
1654 						break;
1655 					}
1656 					case 2:
1657 					{
1658 						float yaw, pitch;
1659 						vectoyawpitch(cursordir, yaw, pitch);
1660 						fixrange(yaw, pitch);
1661 						findorientation(camera1->o, yaw, pitch, worldpos);
1662 						if(allowmove(player1))
1663 						{
1664 							player1->yaw = yaw;
1665 							player1->pitch = pitch;
1666 						}
1667 						break;
1668 					}
1669 				}
1670 				fixfullrange(camera1->yaw, camera1->pitch, camera1->roll, false);
1671 				fixrange(camera1->aimyaw, camera1->aimpitch);
1672 			}
1673 			camera1->roll = player1->calcroll(physics::iscrouching(player1));
1674 			vecfromyawpitch(camera1->yaw, camera1->pitch, 1, 0, camdir);
1675 			vecfromyawpitch(camera1->yaw, 0, 0, -1, camright);
1676 			vecfromyawpitch(camera1->yaw, camera1->pitch+90, 1, 0, camup);
1677 
1678 			camera1->inmaterial = lookupmaterial(camera1->o);
1679 			camera1->inliquid = isliquid(camera1->inmaterial&MATF_VOLUME);
1680 
1681 			switch(camera1->inmaterial)
1682 			{
1683 				case MAT_WATER:
1684 				{
1685 					if(!issound(liquidchan))
1686 						playsound(S_UNDERWATER, camera1->o, camera1, SND_LOOP|SND_NOATTEN|SND_NODELAY|SND_NOCULL, -1, -1, -1, &liquidchan);
1687 					break;
1688 				}
1689 				default:
1690 				{
1691 					if(issound(liquidchan)) removesound(liquidchan);
1692 					liquidchan = -1;
1693 					break;
1694 				}
1695 			}
1696 
1697 			lastcamera = lastmillis;
1698 		}
1699 	}
1700 
1701 	VAR(animoverride, -1, 0, ANIM_MAX-1);
1702 	VAR(testanims, 0, 0, 1);
1703 
numanims()1704 	int numanims() { return ANIM_MAX; }
1705 
findanims(const char * pattern,vector<int> & anims)1706 	void findanims(const char *pattern, vector<int> &anims)
1707 	{
1708 		loopi(sizeof(animnames)/sizeof(animnames[0]))
1709 			if(*animnames[i] && matchanim(animnames[i], pattern))
1710 				anims.add(i);
1711 	}
1712 
renderclient(gameent * d,bool third,float trans,float size,int team,modelattach * attachments,bool secondary,int animflags,int animdelay,int lastaction,bool early)1713 	void renderclient(gameent *d, bool third, float trans, float size, int team, modelattach *attachments, bool secondary, int animflags, int animdelay, int lastaction, bool early)
1714 	{
1715 		const char *mdl = "";
1716 		if(d->aitype <= AI_BOT)
1717 		{
1718 			if(third) mdl = teamtype[team].tpmdl;
1719 			else mdl = teamtype[team].fpmdl;
1720 		}
1721 		else if(d->aitype < AI_MAX) mdl = aistyle[d->aitype].mdl;
1722 		else return;
1723 
1724 		float yaw = d->yaw, pitch = d->pitch, roll = d->calcroll(physics::iscrouching(d));
1725 		vec o = vec(third ? d->feetpos() : d->headpos());
1726 		if(!third)
1727 		{
1728 			vec dir;
1729 			if(firstpersonsway)
1730 			{
1731 				vecfromyawpitch(d->yaw, 0, 0, 1, dir);
1732 				float steps = swaydist/firstpersonswaystep*M_PI;
1733 				dir.mul(firstpersonswayside*cosf(steps));
1734 				dir.z = firstpersonswayup*(fabs(sinf(steps)) - 1);
1735 				o.add(dir).add(swaydir).add(swaypush);
1736 			}
1737 			if(firstpersondist != 0.f)
1738 			{
1739 				vecfromyawpitch(yaw, pitch, 1, 0, dir);
1740 				dir.mul(player1->radius*firstpersondist);
1741 				o.add(dir);
1742 			}
1743 			if(firstpersonshift != 0.f)
1744 			{
1745 				vecfromyawpitch(yaw, pitch, 0, -1, dir);
1746 				dir.mul(player1->radius*firstpersonshift);
1747 				o.add(dir);
1748 			}
1749 			if(firstpersonadjust != 0.f)
1750 			{
1751 				vecfromyawpitch(yaw, pitch+90.f, 1, 0, dir);
1752 				dir.mul(player1->height*firstpersonadjust);
1753 				o.add(dir);
1754 			}
1755 		}
1756 
1757 		int anim = animflags, basetime = lastaction, basetime2 = 0;
1758 		if(animoverride)
1759 		{
1760 			anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP;
1761 			basetime = 0;
1762 		}
1763 		else
1764 		{
1765 			if(secondary && (d->aitype <= AI_BOT || aistyle[d->aitype].maxspeed))
1766 			{
1767 				if(physics::liquidcheck(d) && d->physstate <= PHYS_FALL)
1768 					anim |= (((allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? int(ANIM_SWIM) : int(ANIM_SINK))|ANIM_LOOP)<<ANIM_SECONDARY;
1769 				else if(d->physstate == PHYS_FALL && !d->onladder && FWV(impulsestyle) && d->impulse[IM_TYPE] != IM_T_NONE && lastmillis-d->impulse[IM_TIME] <= 1000) { anim |= ANIM_IMPULSE_DASH<<ANIM_SECONDARY; basetime2 = d->impulse[IM_TIME]; }
1770 				else if(d->physstate == PHYS_FALL && !d->onladder && d->actiontime[AC_JUMP] && lastmillis-d->actiontime[AC_JUMP] <= 1000) { anim |= ANIM_JUMP<<ANIM_SECONDARY; basetime2 = d->actiontime[AC_JUMP]; }
1771 				else if(d->physstate == PHYS_FALL && !d->onladder && d->timeinair >= 1000) anim |= (ANIM_JUMP|ANIM_END)<<ANIM_SECONDARY;
1772 				else if(FWV(impulsestyle) && d->action[AC_IMPULSE] && (d->move || d->strafe))
1773 				{
1774 					if(d->move>0)		anim |= (ANIM_IMPULSE_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1775 					else if(d->strafe)	anim |= ((d->strafe>0 ? ANIM_IMPULSE_LEFT : ANIM_IMPULSE_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY;
1776 					else if(d->move<0)	anim |= (ANIM_IMPULSE_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1777 				}
1778 				else if(d->action[AC_CROUCH] || d->actiontime[AC_CROUCH]<0)
1779 				{
1780 					if(d->move>0)		anim |= (ANIM_CRAWL_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1781 					else if(d->strafe)	anim |= ((d->strafe>0 ? ANIM_CRAWL_LEFT : ANIM_CRAWL_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY;
1782 					else if(d->move<0)	anim |= (ANIM_CRAWL_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1783 					else				anim |= (ANIM_CROUCH|ANIM_LOOP)<<ANIM_SECONDARY;
1784 				}
1785 				else if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1786 				else if(d->strafe) anim |= ((d->strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY;
1787 				else if(d->move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY;
1788 			}
1789 
1790 			if((anim>>ANIM_SECONDARY)&ANIM_INDEX) switch(anim&ANIM_INDEX)
1791 			{
1792 				case ANIM_IDLE: case ANIM_MELEE: case ANIM_PISTOL: case ANIM_SHOTGUN: case ANIM_SMG:
1793 				case ANIM_GRENADE: case ANIM_FLAMER: case ANIM_PLASMA: case ANIM_RIFLE:
1794 				{
1795                     anim = (anim>>ANIM_SECONDARY) | ((anim&((1<<ANIM_SECONDARY)-1))<<ANIM_SECONDARY);
1796                     swap(basetime, basetime2);
1797 					break;
1798 				}
1799 				default: break;
1800 			}
1801 		}
1802 
1803         if(third && testanims && d == player1) yaw = 0; else yaw += 90;
1804         if(anim == ANIM_DYING) pitch *= max(1.f-(lastmillis-basetime)/500.f, 0.f);
1805 
1806         if(d->ragdoll && (!ragdolls || anim!=ANIM_DYING)) cleanragdoll(d);
1807 
1808 		if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY;
1809 
1810 		int flags = MDL_LIGHT;
1811 #if 0 // breaks linkpos
1812 		if(d != player1 && !(anim&ANIM_RAGDOLL)) flags |= MDL_CULL_VFC|MDL_CULL_OCCLUDED|MDL_CULL_QUERY;
1813 #endif
1814         if(d->type == ENT_PLAYER)
1815         {
1816             if(!early && third) flags |= MDL_FULLBRIGHT;
1817         }
1818 		else flags |= MDL_CULL_DIST;
1819         if(early) flags |= MDL_NORENDER;
1820 		else if(third && (anim&ANIM_INDEX)!=ANIM_DEAD) flags |= MDL_DYNSHADOW;
1821 		dynent *e = third ? (dynent *)d : (dynent *)&fpsmodel;
1822 		rendermodel(NULL, mdl, anim, o, yaw, pitch, roll, flags, e, attachments, basetime, basetime2, trans, size);
1823 	}
1824 
renderplayer(gameent * d,bool third,float trans,float size,bool early=false)1825 	void renderplayer(gameent *d, bool third, float trans, float size, bool early = false)
1826 	{
1827 		if(d->state == CS_SPECTATOR) return;
1828 		if(trans <= 0.f || (d == player1 && (third ? thirdpersonmodel : firstpersonmodel) < 1))
1829 		{
1830 			if(d->state == CS_ALIVE && rendernormally && (early || d != player1))
1831 				trans = 1e-16f; // we need tag_muzzle/tag_waist
1832 			else return; // screw it, don't render them
1833 		}
1834 
1835         modelattach a[8];
1836 		int ai = 0, team = m_fight(gamemode) && m_team(gamemode, mutators) ? d->team : TEAM_NEUTRAL,
1837 			weap = d->weapselect, lastaction = 0, animflags = ANIM_IDLE|ANIM_LOOP, animdelay = 0;
1838 		bool secondary = false, showweap = d->aitype <= AI_BOT ? isweap(weap) : aistyle[d->aitype].useweap;
1839 
1840 		if(d->state == CS_DEAD || d->state == CS_WAITING)
1841 		{
1842 			showweap = false;
1843 			animflags = ANIM_DYING;
1844 			lastaction = d->lastpain;
1845             if(ragdolls)
1846             {
1847                 if(!validragdoll(d, lastaction)) animflags |= ANIM_RAGDOLL;
1848             }
1849             else
1850             {
1851 			    int t = lastmillis-lastaction;
1852 			    if(t < 0) return;
1853 			    if(t > 1000) animflags = ANIM_DEAD|ANIM_LOOP;
1854             }
1855         }
1856 		else if(d->state == CS_EDITING)
1857 		{
1858 			animflags = ANIM_EDIT|ANIM_LOOP;
1859 			showweap = false;
1860 		}
1861 #if 0
1862 		else if(intermission)
1863 		{
1864 			lastaction = lastmillis;
1865 			animflags = ANIM_LOSE|ANIM_LOOP;
1866 			animdelay = 1000;
1867 			if(m_fight(gamemode) && m_team(gamemode, mutators))
1868 			{
1869 				loopv(bestteams) if(bestteams[i] == d->team)
1870 				{
1871 					animflags = ANIM_WIN|ANIM_LOOP;
1872 					break;
1873 				}
1874 			}
1875 			else if(bestplayers.find(d) >= 0) animflags = ANIM_WIN|ANIM_LOOP;
1876 		}
1877 #endif
1878 		else if(third && lastmillis-d->lastpain <= 300)
1879 		{
1880 			secondary = third;
1881 			lastaction = d->lastpain;
1882 			animflags = ANIM_PAIN;
1883 			animdelay = 300;
1884 		}
1885 		else
1886 		{
1887 			secondary = third;
1888 			if(showweap)
1889 			{
1890 				lastaction = d->weaplast[weap];
1891 				animdelay = d->weapwait[weap];
1892 				switch(d->weapstate[weap])
1893 				{
1894 					case WEAP_S_SWITCH:
1895 					case WEAP_S_PICKUP:
1896 					{
1897 						if(lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3)
1898 						{
1899 							if(!d->hasweap(d->lastweap, m_weapon(gamemode, mutators))) showweap = false;
1900 							else weap = d->lastweap;
1901 						}
1902 						else if(!d->hasweap(weap, m_weapon(gamemode, mutators))) showweap = false;
1903 						animflags = ANIM_SWITCH+(d->weapstate[weap]-WEAP_S_SWITCH);
1904 						break;
1905 					}
1906 					case WEAP_S_POWER:
1907 					{
1908 						if(weaptype[weap].power) animflags = weaptype[weap].anim+d->weapstate[weap];
1909 						else animflags = weaptype[weap].anim|ANIM_LOOP;
1910 						break;
1911 					}
1912 					case WEAP_S_SHOOT:
1913 					{
1914 						if(!d->hasweap(weap, m_weapon(gamemode, mutators)) || (!weaptype[weap].reloads && lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3))
1915 							showweap = false;
1916 						animflags = weaptype[weap].anim+d->weapstate[weap];
1917 						break;
1918 					}
1919 					case WEAP_S_RELOAD:
1920 					{
1921 						if(weap != WEAP_MELEE)
1922 						{
1923 							if(!d->hasweap(weap, m_weapon(gamemode, mutators)) || (!weaptype[weap].reloads && lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3))
1924 								showweap = false;
1925 							animflags = weaptype[weap].anim+d->weapstate[weap];
1926 							break;
1927 						}
1928 					}
1929 					case WEAP_S_IDLE: case WEAP_S_WAIT: default:
1930 					{
1931 						if(!d->hasweap(weap, m_weapon(gamemode, mutators))) showweap = false;
1932 						animflags = weaptype[weap].anim|ANIM_LOOP;
1933 						break;
1934 					}
1935 				}
1936 			}
1937 		}
1938 
1939 		if(third && d->type == ENT_PLAYER && !shadowmapping && !envmapping && trans > 1e-16f && d->o.squaredist(camera1->o) <= maxparticledistance*maxparticledistance)
1940 		{
1941 			vec pos = d->abovehead(2);
1942 			float blend = aboveheadblend*trans;
1943 			if(shownamesabovehead > (d != player1 ? 0 : 1))
1944 			{
1945 				const char *name = colorname(d, NULL, d->aitype < 0 ? "<super>" : "<default>");
1946 				if(name && *name)
1947 				{
1948                     part_textcopy(pos, name, PART_TEXT, 1, 0xFFFFFF, 2, blend);
1949 					pos.z += 2;
1950 				}
1951 			}
1952 			if(showstatusabovehead > (d != player1 ? 0 : 1))
1953 			{
1954 				Texture *t = NULL;
1955 				if(d->state == CS_DEAD || d->state == CS_WAITING) t = textureload(hud::deadtex, 3);
1956 				else if(d->state == CS_ALIVE)
1957 				{
1958 					if(d->conopen) t = textureload(hud::conopentex, 3);
1959 					else if(m_team(gamemode, mutators) && showteamabovehead > (d != player1 ? (d->team != player1->team ? 1 : 0) : 2))
1960 						t = textureload(hud::teamtex(d->team), 3);
1961 					else if(d->dominating) t = textureload(hud::dominatingtex, 3);
1962 					else if(d->dominated) t = textureload(hud::dominatedtex, 3);
1963 				}
1964 				if(t)
1965 				{
1966 					part_icon(pos, t, 2, blend);
1967 					pos.z += 2;
1968 				}
1969 			}
1970 		}
1971 		bool hasweapon = showweap && *weaptype[weap].vwep;
1972 		if(hasweapon) a[ai++] = modelattach("tag_weapon", weaptype[weap].vwep, ANIM_VWEP|ANIM_LOOP, 0); // we could probably animate this too now..
1973         if(rendernormally && (early || d != player1))
1974         {
1975 			const char *muzzle = hasweapon ? "tag_muzzle" : "tag_weapon";
1976 			//if(d->aitype == AI_TURRET && (d->ammo[d->weapselect]+(d->weapstate[d->weapselect] == WEAP_S_SHOOT ? 1 : 0))%2) muzzle = "tag_muzzle2";
1977 			a[ai++] = modelattach(muzzle, &d->muzzle);
1978         	if(third && (d->type == ENT_PLAYER || (d->type == ENT_AI && (!isaitype(d->aitype) || aistyle[d->aitype].maxspeed))))
1979         	{
1980         		a[ai++] = modelattach("tag_head", &d->head);
1981         		a[ai++] = modelattach("tag_torso", &d->torso);
1982         		a[ai++] = modelattach("tag_waist", &d->waist);
1983         		a[ai++] = modelattach("tag_lfoot", &d->lfoot);
1984         		a[ai++] = modelattach("tag_rfoot", &d->rfoot);
1985         	}
1986         }
1987         renderclient(d, third, trans, size, team, a[0].tag ? a : NULL, secondary, animflags, animdelay, lastaction, early);
1988 	}
1989 
rendercheck(gameent * d)1990 	void rendercheck(gameent *d)
1991 	{
1992 		d->checktags();
1993 		impulseeffect(d, false);
1994 		fireeffect(d);
1995 	}
1996 
render()1997 	void render()
1998 	{
1999 		startmodelbatches();
2000 		gameent *d;
2001         loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) renderplayer(d, true, transscale(d, true), deadscale(d, 1, true));
2002 		entities::render();
2003 		projs::render();
2004 		if(m_stf(gamemode)) stf::render();
2005         if(m_ctf(gamemode)) ctf::render();
2006         ai::render();
2007         if(rendernormally) loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) d->cleartags();
2008 		endmodelbatches();
2009         if(rendernormally) loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) rendercheck(d);
2010 	}
2011 
renderavatar(bool early)2012     void renderavatar(bool early)
2013     {
2014     	if(rendernormally && early) player1->cleartags();
2015         if((thirdpersonview() || !rendernormally))
2016 			renderplayer(player1, true, transscale(player1, thirdpersonview(true)), deadscale(player1, 1, true), early);
2017         else if(!thirdpersonview() && player1->state == CS_ALIVE)
2018             renderplayer(player1, false, transscale(player1, false), deadscale(player1, 1, true), early);
2019 		if(rendernormally && early) rendercheck(player1);
2020     }
2021 
clientoption(char * arg)2022 	bool clientoption(char *arg) { return false; }
2023 }
2024 #undef GAMEWORLD
2025