1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  p_user.c
12 /// \brief New stuff?
13 ///        Player related stuff.
14 ///        Bobbing POV/weapon, movement.
15 ///        Pending weapon.
16 
17 #include "doomdef.h"
18 #include "i_system.h"
19 #include "d_event.h"
20 #include "d_net.h"
21 #include "g_game.h"
22 #include "p_local.h"
23 #include "r_main.h"
24 #include "s_sound.h"
25 #include "r_skins.h"
26 #include "d_think.h"
27 #include "r_sky.h"
28 #include "p_setup.h"
29 #include "m_random.h"
30 #include "m_misc.h"
31 #include "i_video.h"
32 #include "p_slopes.h"
33 #include "p_spec.h"
34 #include "r_splats.h"
35 #include "z_zone.h"
36 #include "w_wad.h"
37 #include "hu_stuff.h"
38 // We need to affect the NiGHTS hud
39 #include "st_stuff.h"
40 #include "lua_script.h"
41 #include "lua_hook.h"
42 #include "b_bot.h"
43 // Objectplace
44 #include "m_cheat.h"
45 // Thok camera snap (ctrl-f "chalupa")
46 #include "g_input.h"
47 
48 #ifdef HW3SOUND
49 #include "hardware/hw3sound.h"
50 #endif
51 
52 #ifdef HWRENDER
53 #include "hardware/hw_light.h"
54 #include "hardware/hw_main.h"
55 #endif
56 
57 #if 0
58 static void P_NukeAllPlayers(player_t *player);
59 #endif
60 
61 //
62 // Jingle stuff.
63 //
64 
65 jingle_t jingleinfo[NUMJINGLES] = {
66 	// {musname, looping, reset, nest}
67 	{""        , false}, // JT_NONE
68 	{""        , false}, // JT_OTHER
69 	{""        , false}, // JT_MASTER
70 	{"_1up"    , false},
71 	{"_shoes"  ,  true},
72 	{"_inv"    , false},
73 	{"_minv"   , false},
74 	{"_drown"  , false},
75 	{"_super"  ,  true},
76 	{"_gover"  , false},
77 	{"_ntime"  , false},  // JT_NIGHTSTIMEOUT
78 	{"_drown"  , false}   // JT_SSTIMEOUT
79 	// {"_clear"  , false},
80 	// {"_inter"  ,  true},
81 	// {"_conti"  ,  true}
82 };
83 
84 //
85 // Movement.
86 //
87 
88 // 16 pixels of bob
89 #define MAXBOB (0x10 << FRACBITS)
90 
91 static boolean onground;
92 
93 //
94 // P_Thrust
95 // Moves the given origin along a given angle.
96 //
P_Thrust(mobj_t * mo,angle_t angle,fixed_t move)97 void P_Thrust(mobj_t *mo, angle_t angle, fixed_t move)
98 {
99 	angle >>= ANGLETOFINESHIFT;
100 
101 	mo->momx += FixedMul(move, FINECOSINE(angle));
102 
103 	if (!(twodlevel || (mo->flags2 & MF2_TWOD)))
104 		mo->momy += FixedMul(move, FINESINE(angle));
105 }
106 
107 #if 0
108 static inline void P_ThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move)
109 {
110 	angle >>= ANGLETOFINESHIFT;
111 
112 	mo->momx += FixedMul(move, FINECOSINE(angle));
113 	mo->momy += FixedMul(move, FINESINE(angle));
114 }
115 
116 static inline void P_VectorInstaThrust(fixed_t xa, fixed_t xb, fixed_t xc, fixed_t ya, fixed_t yb, fixed_t yc,
117 	fixed_t za, fixed_t zb, fixed_t zc, fixed_t momentum, mobj_t *mo)
118 {
119 	fixed_t a1, b1, c1, a2, b2, c2, i, j, k;
120 
121 	a1 = xb - xa;
122 	b1 = yb - ya;
123 	c1 = zb - za;
124 	a2 = xb - xc;
125 	b2 = yb - yc;
126 	c2 = zb - zc;
127 /*
128 	// Convert to unit vectors...
129 	a1 = FixedDiv(a1,FixedSqrt(FixedMul(a1,a1) + FixedMul(b1,b1) + FixedMul(c1,c1)));
130 	b1 = FixedDiv(b1,FixedSqrt(FixedMul(a1,a1) + FixedMul(b1,b1) + FixedMul(c1,c1)));
131 	c1 = FixedDiv(c1,FixedSqrt(FixedMul(c1,c1) + FixedMul(c1,c1) + FixedMul(c1,c1)));
132 
133 	a2 = FixedDiv(a2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
134 	b2 = FixedDiv(b2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
135 	c2 = FixedDiv(c2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
136 */
137 	// Calculate the momx, momy, and momz
138 	i = FixedMul(momentum, FixedMul(b1, c2) - FixedMul(c1, b2));
139 	j = FixedMul(momentum, FixedMul(c1, a2) - FixedMul(a1, c2));
140 	k = FixedMul(momentum, FixedMul(a1, b2) - FixedMul(a1, c2));
141 
142 	mo->momx = i;
143 	mo->momy = j;
144 	mo->momz = k;
145 }
146 #endif
147 
148 //
149 // P_InstaThrust
150 // Moves the given origin along a given angle instantly.
151 //
152 // FIXTHIS: belongs in another file, not here
153 //
P_InstaThrust(mobj_t * mo,angle_t angle,fixed_t move)154 void P_InstaThrust(mobj_t *mo, angle_t angle, fixed_t move)
155 {
156 	angle >>= ANGLETOFINESHIFT;
157 
158 	mo->momx = FixedMul(move, FINECOSINE(angle));
159 
160 	if (!(twodlevel || (mo->flags2 & MF2_TWOD)))
161 		mo->momy = FixedMul(move,FINESINE(angle));
162 }
163 
P_InstaThrustEvenIn2D(mobj_t * mo,angle_t angle,fixed_t move)164 void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move)
165 {
166 	angle >>= ANGLETOFINESHIFT;
167 
168 	mo->momx = FixedMul(move, FINECOSINE(angle));
169 	mo->momy = FixedMul(move, FINESINE(angle));
170 }
171 
172 // Returns a location (hard to explain - go see how it is used)
P_ReturnThrustX(mobj_t * mo,angle_t angle,fixed_t move)173 fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move)
174 {
175 	(void)mo;
176 	angle >>= ANGLETOFINESHIFT;
177 	return FixedMul(move, FINECOSINE(angle));
178 }
P_ReturnThrustY(mobj_t * mo,angle_t angle,fixed_t move)179 fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move)
180 {
181 	(void)mo;
182 	angle >>= ANGLETOFINESHIFT;
183 	return FixedMul(move, FINESINE(angle));
184 }
185 
186 //
187 // P_AutoPause
188 // Returns true when gameplay should be halted even if the game isn't necessarily paused.
189 //
P_AutoPause(void)190 boolean P_AutoPause(void)
191 {
192 	// Don't pause even on menu-up or focus-lost in netgames or record attack
193 	if (netgame || modeattacking || gamestate == GS_TITLESCREEN || (marathonmode && gamestate == GS_INTERMISSION))
194 		return false;
195 
196 	return (menuactive || ( window_notinfocus && cv_pauseifunfocused.value ));
197 }
198 
199 //
200 // P_CalcHeight
201 // Calculate the walking / running height adjustment
202 //
P_CalcHeight(player_t * player)203 void P_CalcHeight(player_t *player)
204 {
205 	INT32 angle;
206 	fixed_t bob;
207 	fixed_t pviewheight;
208 	mobj_t *mo = player->mo;
209 
210 	// Regular movement bobbing.
211 	// Should not be calculated when not on ground (FIXTHIS?)
212 	// OPTIMIZE: tablify angle
213 	// Note: a LUT allows for effects
214 	//  like a ramp with low health.
215 
216 	player->bob = FixedMul(cv_movebob.value,
217 		(FixedMul(player->rmomx,player->rmomx)
218 		+ FixedMul(player->rmomy,player->rmomy))>>2);
219 
220 	if (player->bob > FixedMul(cv_movebob.value, FixedMul(MAXBOB, mo->scale)))
221 		player->bob = FixedMul(cv_movebob.value, FixedMul(MAXBOB, mo->scale));
222 
223 	if (!P_IsObjectOnGround(mo))
224 	{
225 		if (mo->eflags & MFE_VERTICALFLIP)
226 		{
227 			player->viewz = mo->z + mo->height - player->viewheight;
228 			if (player->viewz < mo->floorz + FixedMul(FRACUNIT, mo->scale))
229 				player->viewz = mo->floorz + FixedMul(FRACUNIT, mo->scale);
230 		}
231 		else
232 		{
233 			player->viewz = mo->z + player->viewheight;
234 			if (player->viewz > mo->ceilingz - FixedMul(FRACUNIT, mo->scale))
235 				player->viewz = mo->ceilingz - FixedMul(FRACUNIT, mo->scale);
236 		}
237 		return;
238 	}
239 
240 	angle = (FINEANGLES/20*localgametic)&FINEMASK;
241 	bob = FixedMul(player->bob/2, FINESINE(angle));
242 
243 	// move viewheight
244 	pviewheight = FixedMul(41*player->height/48, mo->scale); // default eye view height
245 
246 	if (player->playerstate == PST_LIVE)
247 	{
248 		player->viewheight += player->deltaviewheight;
249 
250 		if (player->viewheight > pviewheight)
251 		{
252 			player->viewheight = pviewheight;
253 			player->deltaviewheight = 0;
254 		}
255 
256 		if (player->viewheight < pviewheight/2)
257 		{
258 			player->viewheight = pviewheight/2;
259 			if (player->deltaviewheight <= 0)
260 				player->deltaviewheight = 1;
261 		}
262 
263 		if (player->deltaviewheight)
264 		{
265 			player->deltaviewheight += FixedMul(FRACUNIT/4, mo->scale);
266 			if (!player->deltaviewheight)
267 				player->deltaviewheight = 1;
268 		}
269 	}
270 
271 	if (player->mo->eflags & MFE_VERTICALFLIP)
272 		player->viewz = mo->z + mo->height - player->viewheight - bob;
273 	else
274 		player->viewz = mo->z + player->viewheight + bob;
275 
276 	if (player->viewz > mo->ceilingz-FixedMul(4*FRACUNIT, mo->scale))
277 		player->viewz = mo->ceilingz-FixedMul(4*FRACUNIT, mo->scale);
278 	if (player->viewz < mo->floorz+FixedMul(4*FRACUNIT, mo->scale))
279 		player->viewz = mo->floorz+FixedMul(4*FRACUNIT, mo->scale);
280 }
281 
282 /** Decides if a player is moving.
283   * \param pnum The player number to test.
284   * \return True if the player is considered to be moving.
285   * \author Graue <graue@oceanbase.org>
286   */
P_PlayerMoving(INT32 pnum)287 boolean P_PlayerMoving(INT32 pnum)
288 {
289 	player_t *p = &players[pnum];
290 
291 	if (!Playing())
292 		return false;
293 
294 	if (p->jointime < 5*TICRATE || p->playerstate == PST_DEAD || p->playerstate == PST_REBORN || p->spectator)
295 		return false;
296 
297 	return gamestate == GS_LEVEL && p->mo && p->mo->health > 0
298 		&& (abs(p->rmomx) >= FixedMul(FRACUNIT/2, p->mo->scale)
299 			|| abs(p->rmomy) >= FixedMul(FRACUNIT/2, p->mo->scale)
300 			|| abs(p->mo->momz) >= FixedMul(FRACUNIT/2, p->mo->scale)
301 			|| p->climbing || p->powers[pw_tailsfly]
302 			|| (p->pflags & PF_JUMPED) || (p->pflags & PF_SPINNING));
303 }
304 
305 // P_GetNextEmerald
306 //
307 // Gets the number (0 based) of the next emerald to obtain
308 //
P_GetNextEmerald(void)309 UINT8 P_GetNextEmerald(void)
310 {
311 	if (gamemap >= sstage_start && gamemap <= sstage_end)
312 		return (UINT8)(gamemap - sstage_start);
313 	if (gamemap >= smpstage_start || gamemap <= smpstage_end)
314 		return (UINT8)(gamemap - smpstage_start);
315 	return 0;
316 }
317 
318 //
319 // P_GiveEmerald
320 //
321 // Award an emerald upon completion
322 // of a special stage.
323 //
P_GiveEmerald(boolean spawnObj)324 void P_GiveEmerald(boolean spawnObj)
325 {
326 	UINT8 em = P_GetNextEmerald();
327 
328 	S_StartSound(NULL, sfx_cgot); // Got the emerald!
329 	emeralds |= (1 << em);
330 	stagefailed = false;
331 
332 	if (spawnObj)
333 	{
334 		// The Chaos Emerald begins to orbit us!
335 		// Only visibly give it to ONE person!
336 		UINT8 i, pnum = ((playeringame[consoleplayer]) && (!players[consoleplayer].spectator) && (players[consoleplayer].mo)) ? consoleplayer : 255;
337 		for (i = 0; i < MAXPLAYERS; i++)
338 		{
339 			mobj_t *emmo;
340 			if (!playeringame[i])
341 				continue;
342 			if (players[i].spectator)
343 				continue;
344 			if (!players[i].mo)
345 				continue;
346 
347 			emmo = P_SpawnMobjFromMobj(players[i].mo, 0, 0, players[i].mo->height, MT_GOTEMERALD);
348 			if (!emmo)
349 				continue;
350 			P_SetTarget(&emmo->target, players[i].mo);
351 			P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
352 
353 			// Make sure we're not being carried before our tracer is changed
354 			if (players[i].powers[pw_carry] != CR_NIGHTSMODE)
355 				players[i].powers[pw_carry] = CR_NONE;
356 
357 			P_SetTarget(&players[i].mo->tracer, emmo);
358 
359 			if (pnum == 255)
360 			{
361 				pnum = i;
362 				continue;
363 			}
364 
365 			if (i == pnum)
366 				continue;
367 
368 			emmo->flags2 |= MF2_DONTDRAW;
369 		}
370 	}
371 }
372 
373 //
374 // P_GiveFinishFlags
375 //
376 // Give the player visual indicators
377 // that they've finished the map.
378 //
P_GiveFinishFlags(player_t * player)379 void P_GiveFinishFlags(player_t *player)
380 {
381 	angle_t angle = FixedAngle(player->mo->angle << FRACBITS);
382 	UINT8 i;
383 
384 	if (!player->mo)
385 		return;
386 
387 	if (!(netgame||multiplayer))
388 		return;
389 
390 	for (i = 0; i < 3; i++)
391 	{
392 		angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK;
393 		fixed_t xoffs = FINECOSINE(fa);
394 		fixed_t yoffs = FINESINE(fa);
395 		mobj_t* flag = P_SpawnMobjFromMobj(player->mo, xoffs, yoffs, 0, MT_FINISHFLAG);
396 		flag->angle = angle;
397 		angle += FixedAngle(120*FRACUNIT);
398 
399 		P_SetTarget(&flag->target, player->mo);
400 	}
401 }
402 
403 #if 0
404 //
405 // P_ResetScore
406 //
407 // This is called when your chain is reset.
408 void P_ResetScore(player_t *player)
409 {
410 	// Formally a host for Chaos mode behavior
411 
412 	player->scoreadd = 0;
413 }
414 #endif
415 
416 //
417 // P_FindLowestMare
418 //
419 // Returns the lowest open mare available
420 //
P_FindLowestMare(void)421 UINT8 P_FindLowestMare(void)
422 {
423 	thinker_t *th;
424 	mobj_t *mo2;
425 	UINT8 mare = UINT8_MAX;
426 
427 	if (gametyperules & GTR_RACE)
428 		return 0;
429 
430 	// scan the thinkers
431 	// to find the egg capsule with the lowest mare
432 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
433 	{
434 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
435 			continue;
436 
437 		mo2 = (mobj_t *)th;
438 
439 		if (mo2->type != MT_EGGCAPSULE)
440 			continue;
441 		if (mo2->health <= 0)
442 			continue;
443 
444 		{
445 			const UINT8 threshold = (UINT8)mo2->threshold;
446 			if (mare == 255)
447 				mare = threshold;
448 			else if (threshold < mare)
449 				mare = threshold;
450 		}
451 	}
452 
453 	CONS_Debug(DBG_NIGHTS, "Lowest mare found: %d\n", mare);
454 
455 	return mare;
456 }
457 
458 //
459 // P_TransferToNextMare
460 //
461 // Transfers the player to the next Mare.
462 // (Finds the lowest mare # for capsules that have not been destroyed).
463 // Returns true if successful, false if there is no other mare.
464 //
P_TransferToNextMare(player_t * player)465 boolean P_TransferToNextMare(player_t *player)
466 {
467 	thinker_t *th;
468 	mobj_t *mo2;
469 	mobj_t *closestaxis = NULL;
470 	INT32 lowestaxisnum = -1;
471 	UINT8 mare = P_FindLowestMare();
472 	fixed_t dist1, dist2 = 0;
473 
474 	if (mare == 255)
475 		return false;
476 
477 	CONS_Debug(DBG_NIGHTS, "Mare is %d\n", mare);
478 
479 	player->mare = mare;
480 	player->marelap = 0;
481 	player->marebonuslap = 0;
482 
483 	// scan the thinkers
484 	// to find the closest axis point
485 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
486 	{
487 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
488 			continue;
489 
490 		mo2 = (mobj_t *)th;
491 
492 		if (mo2->type != MT_AXIS)
493 			continue;
494 
495 		if (mo2->threshold != mare)
496 			continue;
497 
498 		if (closestaxis == NULL)
499 		{
500 			closestaxis = mo2;
501 			lowestaxisnum = mo2->health;
502 			dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
503 		}
504 		else if (mo2->health < lowestaxisnum)
505 		{
506 			dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
507 
508 			if (dist1 < dist2)
509 			{
510 				closestaxis = mo2;
511 				lowestaxisnum = mo2->health;
512 				dist2 = dist1;
513 			}
514 		}
515 	}
516 
517 	if (closestaxis == NULL)
518 		return false;
519 
520 	P_SetTarget(&player->mo->target, closestaxis);
521 	return true;
522 }
523 
524 //
525 // P_FindAxis
526 //
527 // Given a mare and axis number, returns
528 // the mobj for that axis point.
P_FindAxis(INT32 mare,INT32 axisnum)529 static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
530 {
531 	thinker_t *th;
532 	mobj_t *mo2;
533 
534 	// scan the thinkers
535 	// to find the closest axis point
536 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
537 	{
538 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
539 			continue;
540 
541 		mo2 = (mobj_t *)th;
542 
543 		// Axis things are only at beginning of list.
544 		if (!(mo2->flags2 & MF2_AXIS))
545 			return NULL;
546 
547 		if (mo2->type != MT_AXIS)
548 			continue;
549 		if (mo2->health != axisnum)
550 			continue;
551 		if (mo2->threshold != mare)
552 			continue;
553 		return mo2;
554 	}
555 
556 	return NULL;
557 }
558 
559 //
560 // P_FindAxisTransfer
561 //
562 // Given a mare and axis number, returns
563 // the mobj for that axis transfer point.
P_FindAxisTransfer(INT32 mare,INT32 axisnum,mobjtype_t type)564 static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
565 {
566 	thinker_t *th;
567 	mobj_t *mo2;
568 
569 	// scan the thinkers
570 	// to find the closest axis point
571 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
572 	{
573 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
574 			continue;
575 
576 		mo2 = (mobj_t *)th;
577 
578 		// Axis things are only at beginning of list.
579 		if (!(mo2->flags2 & MF2_AXIS))
580 			return NULL;
581 
582 		if (mo2->type != type)
583 			continue;
584 		if (mo2->health != axisnum)
585 			continue;
586 		if (mo2->threshold != mare)
587 			continue;
588 		return mo2;
589 	}
590 
591 	return NULL;
592 }
593 
594 //
595 // P_TransferToAxis
596 //
597 // Finds the CLOSEST axis with the number specified.
P_TransferToAxis(player_t * player,INT32 axisnum)598 void P_TransferToAxis(player_t *player, INT32 axisnum)
599 {
600 	thinker_t *th;
601 	mobj_t *mo2;
602 	mobj_t *closestaxis;
603 	INT32 mare = player->mare;
604 	fixed_t dist1, dist2 = 0;
605 
606 	CONS_Debug(DBG_NIGHTS, "Transferring to axis %d\nLeveltime: %u...\n", axisnum, leveltime);
607 
608 	closestaxis = NULL;
609 
610 	// scan the thinkers
611 	// to find the closest axis point
612 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
613 	{
614 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
615 			continue;
616 
617 		mo2 = (mobj_t *)th;
618 
619 		if (mo2->type != MT_AXIS)
620 			continue;
621 		if (mo2->health != axisnum)
622 			continue;
623 		if (mo2->threshold != mare)
624 			continue;
625 
626 		if (closestaxis == NULL)
627 		{
628 			closestaxis = mo2;
629 			dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
630 		}
631 		else
632 		{
633 			dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
634 
635 			if (dist1 < dist2)
636 			{
637 				closestaxis = mo2;
638 				dist2 = dist1;
639 			}
640 		}
641 	}
642 
643 	if (!closestaxis)
644 	{
645 		CONS_Debug(DBG_NIGHTS, "ERROR: Specified axis point to transfer to not found!\n%d\n", axisnum);
646 	}
647 	else
648 	{
649 		CONS_Debug(DBG_NIGHTS, "Transferred to axis %d, mare %d\n", closestaxis->health, closestaxis->threshold);
650 	}
651 
652 	P_SetTarget(&player->mo->target, closestaxis);
653 }
654 
655 //
656 // P_DeNightserizePlayer
657 //
658 // Whoops! Ran out of NiGHTS time!
659 //
P_DeNightserizePlayer(player_t * player)660 static void P_DeNightserizePlayer(player_t *player)
661 {
662 	thinker_t *th;
663 	mobj_t *mo2;
664 
665 	player->powers[pw_carry] = CR_NIGHTSFALL;
666 
667 	player->powers[pw_underwater] = 0;
668 	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
669 	player->secondjump = 0;
670 	player->homing = 0;
671 	player->climbing = 0;
672 	player->mo->fuse = 0;
673 	player->speed = 0;
674 	player->marelap = 0;
675 	player->marebonuslap = 0;
676 	player->flyangle = 0;
677 	player->anotherflyangle = 0;
678 	player->mo->rollangle = 0;
679 
680 	P_SetTarget(&player->mo->target, NULL);
681 	P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
682 
683 	player->mo->flags &= ~MF_NOGRAVITY;
684 
685 	player->mo->skin = &skins[player->skin];
686 	player->followitem = skins[player->skin].followitem;
687 	player->mo->color = player->skincolor;
688 	G_GhostAddColor(GHC_RETURNSKIN);
689 
690 	// Restore aiming angle
691 	if (player == &players[consoleplayer])
692 		localaiming = 0;
693 	else if (player == &players[secondarydisplayplayer])
694 		localaiming2 = 0;
695 
696 	P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
697 
698 	// If in a special stage, add some preliminary exit time.
699 	if (G_IsSpecialStage(gamemap))
700 	{
701 		INT32 i;
702 		for (i = 0; i < MAXPLAYERS; i++)
703 			if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE)
704 				players[i].nightstime = 1; // force everyone else to fall too.
705 		player->exiting = 3*TICRATE;
706 
707 		// If you screwed up, kiss your score and ring bonus goodbye.
708 		// But only do this in special stage (and instakill!) In regular stages, wait til we hit the ground.
709 		player->marescore = player->spheres =\
710 		 player->rings = 0;
711 	}
712 
713 	// Check to see if the player should be killed.
714 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
715 	{
716 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
717 			continue;
718 
719 		mo2 = (mobj_t *)th;
720 		if (!(mo2->type == MT_NIGHTSDRONE))
721 			continue;
722 
723 		if (mo2->flags2 & MF2_AMBUSH)
724 		{
725 			player->marescore = player->spheres =\
726 			 player->rings = 0;
727 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
728 
729 			// Reset music to beginning if MIXNIGHTSCOUNTDOWN
730 			if ((mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
731 #ifdef _WIN32
732 				&& S_MusicType() != MU_MID
733 #endif
734 			)
735 				S_SetMusicPosition(0);
736 		}
737 
738 		break;
739 	}
740 
741 	if (player->mo->scale != player->oldscale)
742 		player->mo->destscale = player->oldscale;
743 	player->oldscale = 0;
744 
745 	// Restore from drowning music
746 	if ((mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
747 #ifdef _WIN32
748 		&& S_MusicType() != MU_MID
749 #endif
750 	)
751 	{
752 		S_StopSoundByNum(sfx_timeup);
753 		S_StopFadingMusic();
754 		S_SetInternalMusicVolume(100);
755 
756 		// Reset the music if you did not destroy all the capsules, because you failed.
757 		// Why make the all-capsules exception: because it's your reward for nearly finishing the level!
758 		// (unless the player auto-loses upon denightserizing; for death case, see above.)
759 		if (P_FindLowestMare() != UINT8_MAX || G_IsSpecialStage(gamemap))
760 			S_SetMusicPosition(0);
761 	}
762 	else
763 	{
764 		music_stack_fadein = 0; // HACK: Change fade-in for restore music
765 		P_RestoreMusic(player);
766 	}
767 
768 	P_RunDeNightserizeExecutors(player->mo);
769 }
770 
771 //
772 // P_NightserizePlayer
773 //
774 // NiGHTS Time!
P_NightserizePlayer(player_t * player,INT32 nighttime)775 void P_NightserizePlayer(player_t *player, INT32 nighttime)
776 {
777 	UINT8 oldmare, oldmarelap, oldmarebonuslap;
778 
779 	// Bots can't be NiGHTSerized, silly!1 :P
780 	if (player->bot)
781 		return;
782 
783 	if (player->powers[pw_carry] != CR_NIGHTSMODE)
784 	{
785 		player->mo->height = P_GetPlayerHeight(player); // Just to make sure jumping into the drone doesn't result in a squashed hitbox.
786 		player->oldscale = player->mo->scale;
787 
788 		if (skins[player->skin].sprites[SPR2_NFLY].numframes == 0) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin.
789 		{
790 			player->mo->skin = &skins[DEFAULTNIGHTSSKIN];
791 			if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
792 				player->mo->color = skins[DEFAULTNIGHTSSKIN].prefcolor;
793 			player->followitem = skins[DEFAULTNIGHTSSKIN].followitem;
794 			G_GhostAddColor(GHC_NIGHTSSKIN);
795 		}
796 	}
797 
798 	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
799 	player->homing = 0;
800 	player->mo->fuse = 0;
801 	player->speed = 0;
802 	player->climbing = 0;
803 	player->secondjump = 0;
804 	player->flyangle = 0;
805 	player->anotherflyangle = 0;
806 	player->mo->rollangle = 0;
807 
808 	player->powers[pw_shield] = SH_NONE;
809 	player->powers[pw_super] = 0;
810 
811 	player->mo->flags |= MF_NOGRAVITY;
812 
813 	player->nightstime = player->startedtime = player->lapstartedtime = nighttime*TICRATE;
814 	player->bonustime = false;
815 
816 	// Restore from drowning music
817 	if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
818 	{
819 		S_StopSoundByNum(sfx_timeup);
820 		S_StopFadingMusic();
821 		S_SetInternalMusicVolume(100);
822 	}
823 	else
824 	{
825 		music_stack_fadein = 0; // HACK: Change fade-in for restore music
826 		P_RestoreMusic(player);
827 	}
828 
829 	if (gametyperules & GTR_RACE)
830 	{
831 		if (player->drillmeter < 48*20)
832 			player->drillmeter = 48*20;
833 	}
834 	else
835 	{
836 		if (player->drillmeter < 40*20)
837 			player->drillmeter = 40*20;
838 	}
839 
840 	oldmare = player->mare;
841 	oldmarelap = player->marelap;
842 	oldmarebonuslap = player->marebonuslap;
843 
844 	if (!P_TransferToNextMare(player))
845 	{
846 		INT32 i;
847 		INT32 total_spheres = 0;
848 		INT32 total_rings = 0;
849 
850 		P_SetTarget(&player->mo->target, NULL);
851 
852 		if (G_IsSpecialStage(gamemap))
853 		{
854 			for (i = 0; i < MAXPLAYERS; i++)
855 				if (playeringame[i]/* && players[i].powers[pw_carry] == CR_NIGHTSMODE*/)
856 				{
857 					total_spheres += players[i].spheres;
858 					total_rings += players[i].rings;
859 				}
860 		}
861 
862 		for (i = 0; i < MAXPLAYERS; i++)
863 		{
864 			if (!playeringame[i] || !players[i].mo || players[i].spectator)
865 				continue;
866 
867 			players[i].texttimer = (3 * TICRATE) - 10;
868 			players[i].textvar = 4; // Score and grades
869 			players[i].lastmare = players[i].mare;
870 			players[i].lastmarelap = players[i].marelap;
871 			players[i].lastmarebonuslap = players[i].marebonuslap;
872 			if (G_IsSpecialStage(gamemap))
873 			{
874 				players[i].finishedspheres = (INT16)total_spheres;
875 				players[i].finishedrings = (INT16)total_rings;
876 				P_AddPlayerScore(player, total_spheres * 50);
877 			}
878 			else
879 			{
880 				players[i].finishedspheres = (INT16)(players[i].spheres);
881 				players[i].finishedrings = (INT16)(players[i].rings);
882 				P_AddPlayerScore(&players[i], (players[i].spheres) * 50);
883 			}
884 
885 			// Add score to leaderboards now
886 			if (!(netgame||multiplayer) && P_IsLocalPlayer(&players[i]))
887 				G_AddTempNightsRecords(players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
888 
889 			// transfer scores anyway
890 			players[i].totalmarescore += players[i].marescore;
891 			players[i].lastmarescore = players[i].marescore;
892 			players[i].marescore = 0;
893 
894 			players[i].spheres = players[i].rings = 0;
895 			P_DoPlayerExit(&players[i]);
896 		}
897 	}
898 	else if (oldmare != player->mare)
899 	{
900 		/// \todo Handle multi-mare special stages.
901 		// Spheres bonus
902 		P_AddPlayerScore(player, (player->spheres) * 50);
903 
904 		player->lastmare = oldmare;
905 		player->lastmarelap = oldmarelap;
906 		player->lastmarebonuslap = oldmarebonuslap;
907 		player->texttimer = 4*TICRATE;
908 		player->textvar = 4; // Score and grades
909 		player->finishedspheres = (INT16)(player->spheres);
910 		player->finishedrings = (INT16)(player->rings);
911 
912 		// Add score to temp leaderboards
913 		if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
914 			G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
915 
916 		// Starting a new mare, transfer scores
917 		player->totalmarescore += player->marescore;
918 		player->lastmarescore = player->marescore;
919 		player->marescore = 0;
920 		player->marebegunat = leveltime;
921 		player->lapbegunat = leveltime;
922 
923 		player->spheres = player->rings = 0;
924 	}
925 	else
926 	{
927 		player->textvar = 5; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit
928 		player->texttimer = 40;
929 
930 		// Don't show before title card
931 		// Not consistency safe, but this only affects drawing
932 		if (timeinmap + 40 < (110 - 70))
933 			player->texttimer = (UINT8)((110 - 70) - timeinmap);
934 	}
935 
936 	if (player->drone && player->drone->scale != player->mo->scale)
937 		player->mo->destscale = player->drone->scale;
938 
939 	// force NiGHTS to face forward or backward
940 	if (player->mo->target)
941 	{
942 		player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
943 		player->drawangle = player->angle_pos
944 			+ ((player->mo->target->flags2 & MF2_AMBUSH) ? // if axis is invert, take the opposite right angle
945 				-ANGLE_90 : ANGLE_90); // flyangle is always 0 here, below is kept for posterity
946 				/*(player->flyangle > 90 && player->flyangle < 270 ? ANGLE_90 : -ANGLE_90)
947 				: (player->flyangle > 90 && player->flyangle < 270 ? -ANGLE_90 : ANGLE_90));*/
948 	}
949 
950 	// Do this before setting CR_NIGHTSMODE so we can tell if player was non-NiGHTS
951 	P_RunNightserizeExecutors(player->mo);
952 
953 	player->powers[pw_carry] = CR_NIGHTSMODE;
954 	P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_TRANS1);
955 }
956 
P_GetJumpFlags(player_t * player)957 pflags_t P_GetJumpFlags(player_t *player)
958 {
959 	if (player->charflags & SF_NOJUMPDAMAGE)
960 		return (PF_JUMPED|PF_NOJUMPDAMAGE);
961 	return PF_JUMPED;
962 }
963 
964 //
965 // P_PlayerInPain
966 //
967 // Is player in pain??
968 // Checks for painstate and pw_flashing, if both found return true
969 //
P_PlayerInPain(player_t * player)970 boolean P_PlayerInPain(player_t *player)
971 {
972 	// no silly, sliding isn't pain
973 	if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate] && player->powers[pw_flashing])
974 		return true;
975 
976 	if (player->mo->state == &states[S_PLAY_STUN])
977 		return true;
978 
979 	return false;
980 }
981 
982 //
983 // P_DoPlayerPain
984 //
985 // Player was hit,
986 // put them in pain.
987 //
P_DoPlayerPain(player_t * player,mobj_t * source,mobj_t * inflictor)988 void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
989 {
990 	if (player->powers[pw_carry] == CR_ROPEHANG)
991 		P_SetTarget(&player->mo->tracer, NULL);
992 
993 	{
994 		angle_t ang;
995 		fixed_t fallbackspeed;
996 
997 		P_ResetPlayer(player);
998 		P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
999 
1000 		if (player->mo->eflags & MFE_VERTICALFLIP)
1001 			player->mo->z--;
1002 		else
1003 			player->mo->z++;
1004 
1005 		if (player->mo->eflags & MFE_UNDERWATER)
1006 			P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
1007 		else
1008 			P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
1009 
1010 		if (inflictor)
1011 		{
1012 			if (inflictor->type == MT_WALLSPIKE)
1013 				ang = inflictor->angle;
1014 			else
1015 				ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
1016 
1017 			// explosion and rail rings send you farther back, making it more difficult
1018 			// to recover
1019 			if ((inflictor->flags2 & MF2_SCATTER) && source)
1020 			{
1021 				fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
1022 
1023 				dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
1024 
1025 				if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
1026 					dist = FixedMul(4*FRACUNIT, inflictor->scale);
1027 
1028 				fallbackspeed = dist;
1029 			}
1030 			else if (inflictor->flags2 & MF2_EXPLOSION)
1031 			{
1032 				if (inflictor->flags2 & MF2_RAILRING)
1033 					fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
1034 				else
1035 					fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
1036 			}
1037 			else if (inflictor->flags2 & MF2_RAILRING)
1038 				fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
1039 			else
1040 				fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
1041 		}
1042 		else
1043 		{
1044 			ang = ((player->mo->momx || player->mo->momy) ? R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0) : player->drawangle);
1045 			fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
1046 		}
1047 
1048 		player->drawangle = ang + ANGLE_180;
1049 		P_InstaThrust(player->mo, ang, fallbackspeed);
1050 	}
1051 
1052 	// Point penalty for hitting a hazard during tag.
1053 	// Discourages players from intentionally hurting themselves to avoid being tagged.
1054 	if (((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG)
1055 	&& (!(player->pflags & PF_GAMETYPEOVER) && !(player->pflags & PF_TAGIT)))
1056 	{
1057 		if (player->score >= 50)
1058 			player->score -= 50;
1059 		else
1060 			player->score = 0;
1061 	}
1062 
1063 	player->powers[pw_flashing] = flashingtics;
1064 
1065 	if (player->timeshit != UINT8_MAX)
1066 		++player->timeshit;
1067 }
1068 
1069 //
1070 // P_ResetPlayer
1071 //
1072 // Useful when you want to kill everything the player is doing.
P_ResetPlayer(player_t * player)1073 void P_ResetPlayer(player_t *player)
1074 {
1075 	player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY|PF_BOUNCING);
1076 
1077 	if (player->powers[pw_carry] == CR_ROLLOUT)
1078 	{
1079 		if (player->mo->tracer && !P_MobjWasRemoved(player->mo->tracer))
1080 		{
1081 			player->mo->tracer->flags |= MF_PUSHABLE;
1082 			P_SetTarget(&player->mo->tracer->tracer, NULL);
1083 		}
1084 		P_SetTarget(&player->mo->tracer, NULL);
1085 		player->powers[pw_carry] = CR_NONE;
1086 	}
1087 
1088 	if (!(player->powers[pw_carry] == CR_NIGHTSMODE || player->powers[pw_carry] == CR_NIGHTSFALL || player->powers[pw_carry] == CR_BRAKGOOP || player->powers[pw_carry] == CR_MINECART))
1089 		player->powers[pw_carry] = CR_NONE;
1090 
1091 	player->secondjump = 0;
1092 	player->glidetime = 0;
1093 	player->homing = 0;
1094 	player->climbing = 0;
1095 	player->powers[pw_tailsfly] = 0;
1096 	player->onconveyor = 0;
1097 	player->skidtime = 0;
1098 	if (player-players == consoleplayer && botingame)
1099 		CV_SetValue(&cv_analog[1], true);
1100 }
1101 
1102 // P_PlayerCanDamage
1103 //
1104 // Can player do damage?
1105 //
P_PlayerCanDamage(player_t * player,mobj_t * thing)1106 boolean P_PlayerCanDamage(player_t *player, mobj_t *thing)
1107 {
1108 	fixed_t bottomheight, topheight;
1109 
1110 	if (!player->mo || player->spectator || !thing || P_MobjWasRemoved(thing))
1111 		return false;
1112 
1113 	{
1114 		UINT8 shouldCollide = LUAh_PlayerCanDamage(player, thing);
1115 		if (P_MobjWasRemoved(thing))
1116 			return false; // removed???
1117 		if (shouldCollide == 1)
1118 			return true; // force yes
1119 		else if (shouldCollide == 2)
1120 			return false; // force no
1121 	}
1122 
1123 	// Invinc/super. Not for Monitors.
1124 	if (!(thing->flags & MF_MONITOR) && (player->powers[pw_invulnerability] || player->powers[pw_super]))
1125 		return true;
1126 
1127 	// NiGHTS drill. Wasn't originally for monitors, but that's more an oversight being corrected than anything else.
1128 	if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
1129 		return true;
1130 
1131 	// Jumping.
1132 	if ((player->pflags & PF_JUMPED)
1133 	&& (!(player->pflags & PF_NOJUMPDAMAGE)
1134 		|| (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
1135 		return true;
1136 
1137 	// Spinning.
1138 	if (player->pflags & PF_SPINNING)
1139 		return true;
1140 
1141 	if (player->dashmode >= DASHMODE_THRESHOLD && (player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE))
1142 		return true;
1143 
1144 	// From the front.
1145 	if (((player->pflags & PF_GLIDING) || (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
1146 	&& (player->drawangle - R_PointToAngle2(player->mo->x - player->mo->momx, player->mo->y - player->mo->momy, thing->x, thing->y) +  + ANGLE_90) < ANGLE_180)
1147 		return true;
1148 
1149 	// From the top/bottom.
1150 	bottomheight = player->mo->z;
1151 	topheight = player->mo->z + player->mo->height;
1152 
1153 	if (player->mo->eflags & MFE_VERTICALFLIP)
1154 	{
1155 		fixed_t swap = bottomheight;
1156 		bottomheight = topheight;
1157 		topheight = swap;
1158 	}
1159 
1160 	if (P_MobjFlip(player->mo)*(bottomheight - (thing->z + thing->height/2)) > 0)
1161 	{
1162 		if ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) < 0))
1163 			return true;
1164 	}
1165 	else if (P_MobjFlip(player->mo)*(topheight - (thing->z + thing->height/2)) < 0)
1166 	{
1167 		if (player->charability == CA_FLY && player->panim == PA_ABILITY && !(player->mo->eflags & MFE_UNDERWATER) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0))
1168 			return true;
1169 	}
1170 
1171 	// Shield stomp.
1172 	if (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY))
1173 		return true;
1174 
1175 	return false;
1176 }
1177 
1178 
1179 
1180 //
1181 // P_GivePlayerRings
1182 //
1183 // Gives rings to the player, and does any special things required.
1184 // Call this function when you want to increment the player's health.
1185 //
1186 
P_GivePlayerRings(player_t * player,INT32 num_rings)1187 void P_GivePlayerRings(player_t *player, INT32 num_rings)
1188 {
1189 	if (!player)
1190 		return;
1191 
1192 	if (player->bot)
1193 		player = &players[consoleplayer];
1194 
1195 	if (!player->mo)
1196 		return;
1197 
1198 	player->rings += num_rings;
1199 
1200 	player->totalring += num_rings;
1201 
1202 	// Can only get up to 9999 rings, sorry!
1203 	if (player->rings > 9999)
1204 		player->rings = 9999;
1205 	else if (player->rings < 0)
1206 		player->rings = 0;
1207 
1208 	// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
1209 	if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives() && player->lives != INFLIVES)
1210 	{
1211 		INT32 gainlives = 0;
1212 
1213 		while (player->xtralife < maxXtraLife && player->rings >= 100 * (player->xtralife+1))
1214 		{
1215 			++gainlives;
1216 			++player->xtralife;
1217 		}
1218 
1219 		if (gainlives)
1220 		{
1221 			player->lives += gainlives;
1222 			if (player->lives > 99)
1223 				player->lives = 99;
1224 			else if (player->lives < 1)
1225 				player->lives = 1;
1226 
1227 			P_PlayLivesJingle(player);
1228 		}
1229 	}
1230 }
1231 
P_GivePlayerSpheres(player_t * player,INT32 num_spheres)1232 void P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
1233 {
1234 	if (!player)
1235 		return;
1236 
1237 	if (player->bot)
1238 		player = &players[consoleplayer];
1239 
1240 	if (!player->mo)
1241 		return;
1242 
1243 	player->spheres += num_spheres;
1244 
1245 	// Can only get up to 9999 spheres, sorry!
1246 	if (player->spheres > 9999)
1247 		player->spheres = 9999;
1248 	else if (player->spheres < 0)
1249 		player->spheres = 0;
1250 }
1251 
1252 //
1253 // P_GivePlayerLives
1254 //
1255 // Gives the player an extra life.
1256 // Call this function when you want to add lives to the player.
1257 //
P_GivePlayerLives(player_t * player,INT32 numlives)1258 void P_GivePlayerLives(player_t *player, INT32 numlives)
1259 {
1260 	UINT8 prevlives = player->lives;
1261 	if (!player)
1262 		return;
1263 
1264 	if (player->bot)
1265 		player = &players[consoleplayer];
1266 
1267 	if (gamestate == GS_LEVEL)
1268 	{
1269 		if (player->lives == INFLIVES || !(gametyperules & GTR_LIVES))
1270 		{
1271 			P_GivePlayerRings(player, 100*numlives);
1272 			return;
1273 		}
1274 
1275 		if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0)
1276 		{
1277 			P_GivePlayerRings(player, 100*numlives);
1278 			if (player->lives - prevlives >= numlives)
1279 				goto docooprespawn;
1280 
1281 			numlives = (numlives + prevlives - player->lives);
1282 		}
1283 	}
1284 	else if (player->lives == INFLIVES)
1285 		return;
1286 
1287 	player->lives += numlives;
1288 
1289 	if (player->lives > 99)
1290 		player->lives = 99;
1291 	else if (player->lives < 1)
1292 		player->lives = 1;
1293 
1294 docooprespawn:
1295 	if (cv_coopstarposts.value)
1296 		return;
1297 	if (prevlives > 0)
1298 		return;
1299 	if (!player->spectator)
1300 		return;
1301 	P_SpectatorJoinGame(player);
1302 }
1303 
P_GiveCoopLives(player_t * player,INT32 numlives,boolean sound)1304 void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound)
1305 {
1306 	if (!((netgame || multiplayer) && G_GametypeUsesCoopLives()))
1307 	{
1308 		P_GivePlayerLives(player, numlives);
1309 		if (sound)
1310 			P_PlayLivesJingle(player);
1311 	}
1312 	else
1313 	{
1314 		INT32 i;
1315 		for (i = 0; i < MAXPLAYERS; i++)
1316 		{
1317 			if (!playeringame[i])
1318 				continue;
1319 
1320 			P_GivePlayerLives(&players[i], numlives);
1321 			if (sound)
1322 				P_PlayLivesJingle(&players[i]);
1323 		}
1324 	}
1325 }
1326 
1327 //
1328 // P_DoSuperTransformation
1329 //
1330 // Transform into Super Sonic!
P_DoSuperTransformation(player_t * player,boolean giverings)1331 void P_DoSuperTransformation(player_t *player, boolean giverings)
1332 {
1333 	player->powers[pw_super] = 1;
1334 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player))
1335 		P_PlayJingle(player, JT_SUPER);
1336 
1337 	S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
1338 
1339 	player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0;
1340 
1341 	// Transformation animation
1342 	P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1);
1343 
1344 	if (giverings && player->rings < 50)
1345 		player->rings = 50;
1346 
1347 	// Just in case.
1348 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
1349 	{
1350 		player->powers[pw_extralife] = 0;
1351 		player->powers[pw_invulnerability] = 0;
1352 		player->powers[pw_sneakers] = 0;
1353 	}
1354 
1355 	if (!G_CoopGametype())
1356 	{
1357 		HU_SetCEchoFlags(0);
1358 		HU_SetCEchoDuration(5);
1359 		HU_DoCEcho(va("%s\\is now super.\\\\\\\\", player_names[player-players]));
1360 	}
1361 
1362 	P_PlayerFlagBurst(player, false);
1363 }
1364 
1365 // Adds to the player's score
P_AddPlayerScore(player_t * player,UINT32 amount)1366 void P_AddPlayerScore(player_t *player, UINT32 amount)
1367 {
1368 	UINT32 oldscore;
1369 
1370 	if (player->bot)
1371 		player = &players[consoleplayer];
1372 
1373 	// NiGHTS does it different!
1374 	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->typeoflevel & TOL_NIGHTS)
1375 	{
1376 		if ((netgame || multiplayer) && G_IsSpecialStage(gamemap))
1377 		{ // Pseudo-shared score for multiplayer special stages.
1378 			INT32 i;
1379 			for (i = 0; i < MAXPLAYERS; i++)
1380 				if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE)
1381 				{
1382 					oldscore = players[i].marescore;
1383 
1384 					// Don't go above MAXSCORE.
1385 					if (players[i].marescore + amount < MAXSCORE)
1386 						players[i].marescore += amount;
1387 					else
1388 						players[i].marescore = MAXSCORE;
1389 
1390 					// Continues are worthless in netgames.
1391 					// If that stops being the case uncomment this.
1392 /*					if (!ultimatemode && continuesInSession && players[i].marescore > 50000
1393 					&& oldscore < 50000)
1394 					{
1395 						players[i].continues += 1;
1396 						players[i].gotcontinue = true;
1397 						if (P_IsLocalPlayer(player))
1398 							S_StartSound(NULL, sfx_s3kac);
1399 					} */
1400 				}
1401 		}
1402 		else
1403 		{
1404 			oldscore = player->marescore;
1405 
1406 			// Don't go above MAXSCORE.
1407 			if (player->marescore + amount < MAXSCORE)
1408 				player->marescore += amount;
1409 			else
1410 				player->marescore = MAXSCORE;
1411 
1412 			if (!ultimatemode && continuesInSession && G_IsSpecialStage(gamemap)
1413 			&& player->marescore >= 50000 && oldscore < 50000)
1414 			{
1415 				player->continues += 1;
1416 				player->gotcontinue = true;
1417 				if (P_IsLocalPlayer(player))
1418 					S_StartSound(NULL, sfx_s3kac);
1419 			}
1420 		}
1421 
1422 		if (G_CoopGametype())
1423 			return;
1424 	}
1425 
1426 	oldscore = player->score;
1427 
1428 	// Don't go above MAXSCORE.
1429 	player->score += amount;
1430 	if (player->score > MAXSCORE)
1431 		player->score = MAXSCORE;
1432 
1433 	// check for extra lives every 50000 pts
1434 	if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
1435 	{
1436 		P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000));
1437 		P_PlayLivesJingle(player);
1438 	}
1439 
1440 	// In team match, all awarded points are incremented to the team's running score.
1441 	if ((gametyperules & (GTR_TEAMS|GTR_TEAMFLAGS)) == GTR_TEAMS)
1442 	{
1443 		if (player->ctfteam == 1)
1444 			redscore += amount;
1445 		else if (player->ctfteam == 2)
1446 			bluescore += amount;
1447 	}
1448 }
1449 
1450 // Steals from every enemy's score.
P_StealPlayerScore(player_t * player,UINT32 amount)1451 void P_StealPlayerScore(player_t *player, UINT32 amount)
1452 {
1453 	boolean teams = G_GametypeHasTeams();
1454 	UINT32 stolen = 0;
1455 	int i;
1456 	for (i = 0; i < MAXPLAYERS; i++)
1457 	{
1458 		if (&players[i] == player
1459 		|| (teams && players[i].ctfteam == player->ctfteam))
1460 			continue;
1461 		if (players[i].score >= amount)
1462 		{
1463 			stolen += amount;
1464 			players[i].score -= amount;
1465 		}
1466 		else
1467 		{
1468 			stolen += players[i].score;
1469 			players[i].score = 0;
1470 		}
1471 	}
1472 	if (stolen > 0)
1473 	{
1474 		// In team match, all stolen points are removed from the enemy team's running score.
1475 		if ((gametyperules & (GTR_TEAMS|GTR_TEAMFLAGS)) == GTR_TEAMS)
1476 		{
1477 			if (player->ctfteam == 1)
1478 				bluescore -= amount;
1479 			else if (player->ctfteam == 2)
1480 				redscore -= amount;
1481 		}
1482 		P_AddPlayerScore(player, stolen);
1483 	}
1484 }
1485 
1486 //
1487 // P_PlayLivesJingle
1488 //
P_PlayLivesJingle(player_t * player)1489 void P_PlayLivesJingle(player_t *player)
1490 {
1491 	if (player && !P_IsLocalPlayer(player))
1492 		return;
1493 
1494 	if (mariomode)
1495 		S_StartSound(NULL, sfx_marioa);
1496 	else if (use1upSound || cv_1upsound.value)
1497 		S_StartSound(NULL, sfx_oneup);
1498 	else
1499 	{
1500 		P_PlayJingle(player, JT_1UP);
1501 		if (player)
1502 			player->powers[pw_extralife] = extralifetics + 1;
1503 		strlcpy(S_sfx[sfx_None].caption, "One-up", 7);
1504 		S_StartCaption(sfx_None, -1, extralifetics+1);
1505 	}
1506 }
1507 
P_PlayJingle(player_t * player,jingletype_t jingletype)1508 void P_PlayJingle(player_t *player, jingletype_t jingletype)
1509 {
1510 	const char *musname = jingleinfo[jingletype].musname;
1511 	UINT16 musflags = 0;
1512 	boolean looping = jingleinfo[jingletype].looping;
1513 
1514 	char newmusic[7];
1515 	strncpy(newmusic, musname, 7);
1516 #ifdef HAVE_LUA_MUSICPLUS
1517  	if(LUAh_MusicJingle(jingletype, newmusic, &musflags, &looping))
1518  		return;
1519 #endif
1520 	newmusic[6] = 0;
1521 
1522 	P_PlayJingleMusic(player, newmusic, musflags, looping, jingletype);
1523 }
1524 
1525 //
1526 // P_PlayJingleMusic
1527 //
P_PlayJingleMusic(player_t * player,const char * musname,UINT16 musflags,boolean looping,UINT16 status)1528 void P_PlayJingleMusic(player_t *player, const char *musname, UINT16 musflags, boolean looping, UINT16 status)
1529 {
1530 	// If gamestate != GS_LEVEL, always play the jingle (1-up intermission)
1531 	if (gamestate == GS_LEVEL && player && !P_IsLocalPlayer(player))
1532 		return;
1533 
1534 	S_RetainMusic(musname, musflags, looping, 0, status);
1535 	S_StopMusic();
1536 	S_ChangeMusicInternal(musname, looping);
1537 }
1538 
P_EvaluateMusicStatus(UINT16 status,const char * musname)1539 boolean P_EvaluateMusicStatus(UINT16 status, const char *musname)
1540 {
1541 	// \todo lua hook
1542 	int i;
1543 	boolean result = false;
1544 
1545 	for (i = 0; i < MAXPLAYERS; i++)
1546 	{
1547 		if (!P_IsLocalPlayer(&players[i]))
1548 			continue;
1549 
1550 		switch(status)
1551 		{
1552 			case JT_1UP: // Extra life
1553 				result = (players[i].powers[pw_extralife] > 1);
1554 				break;
1555 
1556 			case JT_SHOES:  // Speed shoes
1557 				if (players[i].powers[pw_sneakers] > 1 && !players[i].powers[pw_super])
1558 				{
1559 					//strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
1560 					//S_StartCaption(sfx_None, -1, players[i].powers[pw_sneakers]);
1561 					result = true;
1562 				}
1563 				else
1564 					result = false;
1565 				break;
1566 
1567 			case JT_INV: // Invincibility
1568 			case JT_MINV: // Mario Invincibility
1569 				if (players[i].powers[pw_invulnerability] > 1)
1570 				{
1571 					//strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
1572 					//S_StartCaption(sfx_None, -1, players[i].powers[pw_invulnerability]);
1573 					result = true;
1574 				}
1575 				else
1576 					result = false;
1577 				break;
1578 
1579 			case JT_DROWN:  // Drowning
1580 				result = (players[i].powers[pw_underwater] && players[i].powers[pw_underwater] <= 11*TICRATE + 1);
1581 				break;
1582 
1583 			case JT_SUPER:  // Super Sonic
1584 				result = (players[i].powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC));
1585 				break;
1586 
1587 			case JT_GOVER: // Game Over
1588 				result = (players[i].lives <= 0);
1589 				break;
1590 
1591 			case JT_NIGHTSTIMEOUT: // NiGHTS Time Out (10 seconds)
1592 			case JT_SSTIMEOUT:
1593 				result = (players[i].nightstime && players[i].nightstime <= 10*TICRATE);
1594 				break;
1595 
1596 			case JT_OTHER:  // Other state
1597 				result = LUAh_ShouldJingleContinue(&players[i], musname);
1598 				break;
1599 
1600 			case JT_NONE:   // Null state
1601 			case JT_MASTER: // Main level music
1602 			default:
1603 				result = true;
1604 		}
1605 
1606 		if (result)
1607 			break;
1608  	}
1609 
1610 	return result;
1611  }
1612 
1613 //
1614 // P_RestoreMusic
1615 //
1616 // Restores music after some special music change
1617 //
P_RestoreMusic(player_t * player)1618 void P_RestoreMusic(player_t *player)
1619 {
1620 	if (!P_IsLocalPlayer(player)) // Only applies to a local player
1621 		return;
1622 
1623 	S_SpeedMusic(1.0f);
1624 
1625 	// Jingles have a priority in this order, so follow it
1626 	// and as a default case, go down the music stack.
1627 
1628 	// Extra life
1629 	if (player->powers[pw_extralife] > 1)
1630 		return;
1631 
1632 	// Super
1633 	else if (player->powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC)
1634 		&& !S_RecallMusic(JT_SUPER, false))
1635 		P_PlayJingle(player, JT_SUPER);
1636 
1637 	// Invulnerability
1638 	else if (player->powers[pw_invulnerability] > 1 && !player->powers[pw_super])
1639 	{
1640 		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
1641 		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
1642 		if (!S_RecallMusic(JT_INV, false) && !S_RecallMusic(JT_MINV, false))
1643 			P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV);
1644 	}
1645 
1646 	// Shoes
1647 	else if (player->powers[pw_sneakers] > 1 && !player->powers[pw_super])
1648 	{
1649 		strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
1650 		S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
1651 		if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)
1652 		{
1653 			S_SpeedMusic(1.4f);
1654 			if (!S_RecallMusic(JT_MASTER, true))
1655 				S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
1656 		}
1657 		else if (!S_RecallMusic(JT_SHOES, false))
1658 			P_PlayJingle(player, JT_SHOES);
1659 	}
1660 
1661 	// Default
1662 	else if (!S_RecallMusic(JT_NONE, false)) // go down the stack
1663 	{
1664 		CONS_Debug(DBG_BASIC, "Cannot find any music in resume stack!\n");
1665 	 	S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
1666  	}
1667 }
1668 
1669 //
1670 // P_IsObjectInGoop
1671 //
1672 // Returns true if the object is inside goop water.
1673 // (Spectators and objects otherwise without gravity cannot have goop gravity!)
1674 //
P_IsObjectInGoop(mobj_t * mo)1675 boolean P_IsObjectInGoop(mobj_t *mo)
1676 {
1677 	if (mo->player && mo->player->spectator)
1678 		return false;
1679 
1680 	if (mo->flags & MF_NOGRAVITY)
1681 		return false;
1682 
1683 	return ((mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) == (MFE_UNDERWATER|MFE_GOOWATER));
1684 }
1685 
1686 //
1687 // P_IsObjectOnGround
1688 //
1689 // Returns true if the player is
1690 // on the ground. Takes reverse
1691 // gravity and FOFs into account.
1692 //
P_IsObjectOnGround(mobj_t * mo)1693 boolean P_IsObjectOnGround(mobj_t *mo)
1694 {
1695 	if (P_IsObjectInGoop(mo) && !(mo->player && mo->player->pflags & PF_BOUNCING))
1696 	{
1697 /*
1698 		// It's a crazy hack that checking if you're on the ground
1699 		// would actually CHANGE your position and momentum,
1700 		if (mo->z < mo->floorz)
1701 		{
1702 			mo->z = mo->floorz;
1703 			mo->momz = 0;
1704 		}
1705 		else if (mo->z + mo->height > mo->ceilingz)
1706 		{
1707 			mo->z = mo->ceilingz - mo->height;
1708 			mo->momz = 0;
1709 		}
1710 */
1711 		// but I don't want you to ever 'stand' while submerged in goo.
1712 		// You're in constant vertical momentum, even if you get stuck on something.
1713 		// No exceptions.
1714 		return false;
1715 	}
1716 
1717 	if (mo->eflags & MFE_VERTICALFLIP)
1718 	{
1719 		if (mo->z+mo->height >= mo->ceilingz)
1720 			return true;
1721 	}
1722 	else
1723 	{
1724 		if (mo->z <= mo->floorz)
1725 			return true;
1726 	}
1727 
1728 	return false;
1729 }
1730 
1731 //
1732 // P_IsObjectOnGroundIn
1733 //
1734 // Returns true if the player is
1735 // on the ground in a specific sector. Takes reverse
1736 // gravity and FOFs into account.
1737 //
P_IsObjectOnGroundIn(mobj_t * mo,sector_t * sec)1738 boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec)
1739 {
1740 	ffloor_t *rover;
1741 
1742 	// Is the object in reverse gravity?
1743 	if (mo->eflags & MFE_VERTICALFLIP)
1744 	{
1745 		// Detect if the player is on the ceiling.
1746 		if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec))
1747 			return true;
1748 		// Otherwise, detect if the player is on the bottom of a FOF.
1749 		else
1750 		{
1751 			for (rover = sec->ffloors; rover; rover = rover->next)
1752 			{
1753 				// If the FOF doesn't exist, continue.
1754 				if (!(rover->flags & FF_EXISTS))
1755 					continue;
1756 
1757 				// If the FOF is configured to let the object through, continue.
1758 				if (!((rover->flags & FF_BLOCKPLAYER && mo->player)
1759 					|| (rover->flags & FF_BLOCKOTHERS && !mo->player)))
1760 					continue;
1761 
1762 				// If the the platform is intangible from below, continue.
1763 				if (rover->flags & FF_PLATFORM)
1764 					continue;
1765 
1766 				// If the FOF is a water block, continue. (Unnecessary check?)
1767 				if (rover->flags & FF_SWIMMABLE)
1768 					continue;
1769 
1770 				// Actually check if the player is on the suitable FOF.
1771 				if (mo->z+mo->height == P_GetSpecialBottomZ(mo, sectors + rover->secnum, sec))
1772 					return true;
1773 			}
1774 		}
1775 	}
1776 	// Nope!
1777 	else
1778 	{
1779 		// Detect if the player is on the floor.
1780 		if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec))
1781 			return true;
1782 		// Otherwise, detect if the player is on the top of a FOF.
1783 		else
1784 		{
1785 			for (rover = sec->ffloors; rover; rover = rover->next)
1786 			{
1787 				// If the FOF doesn't exist, continue.
1788 				if (!(rover->flags & FF_EXISTS))
1789 					continue;
1790 
1791 				// If the FOF is configured to let the object through, continue.
1792 				if (!((rover->flags & FF_BLOCKPLAYER && mo->player)
1793 					|| (rover->flags & FF_BLOCKOTHERS && !mo->player)))
1794 					continue;
1795 
1796 				// If the the platform is intangible from above, continue.
1797 				if (rover->flags & FF_REVERSEPLATFORM)
1798 					continue;
1799 
1800 				// If the FOF is a water block, continue. (Unnecessary check?)
1801 				if (rover->flags & FF_SWIMMABLE)
1802 					continue;
1803 
1804 				// Actually check if the player is on the suitable FOF.
1805 				if (mo->z == P_GetSpecialTopZ(mo, sectors + rover->secnum, sec))
1806 					return true;
1807 			}
1808 		}
1809 	}
1810 
1811 	return false;
1812 }
1813 
1814 //
1815 // P_SetObjectMomZ
1816 //
1817 // Sets the player momz appropriately.
1818 // Takes reverse gravity into account.
1819 //
P_SetObjectMomZ(mobj_t * mo,fixed_t value,boolean relative)1820 void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative)
1821 {
1822 	if (mo->eflags & MFE_VERTICALFLIP)
1823 		value = -value;
1824 
1825 	if (mo->scale != FRACUNIT)
1826 		value = FixedMul(value, mo->scale);
1827 
1828 	if (relative)
1829 		mo->momz += value;
1830 	else
1831 		mo->momz = value;
1832 }
1833 
1834 //
1835 // P_IsLocalPlayer
1836 //
1837 // Returns true if player is
1838 // on the local machine.
1839 //
P_IsLocalPlayer(player_t * player)1840 boolean P_IsLocalPlayer(player_t *player)
1841 {
1842 	return ((splitscreen && player == &players[secondarydisplayplayer]) || player == &players[consoleplayer]);
1843 }
1844 
1845 //
1846 // P_SpawnShieldOrb
1847 //
1848 // Spawns the shield orb on the player
1849 // depending on which shield they are
1850 // supposed to have.
1851 //
P_SpawnShieldOrb(player_t * player)1852 void P_SpawnShieldOrb(player_t *player)
1853 {
1854 	mobjtype_t orbtype;
1855 	thinker_t *th;
1856 	mobj_t *shieldobj, *ov;
1857 
1858 #ifdef PARANOIA
1859 	if (!player->mo)
1860 		I_Error("P_SpawnShieldOrb: player->mo is NULL!\n");
1861 #endif
1862 
1863 	if (LUAh_ShieldSpawn(player))
1864 		return;
1865 
1866 	if (player->powers[pw_shield] & SH_FORCE)
1867 		orbtype = MT_FORCE_ORB;
1868 	else switch (player->powers[pw_shield] & SH_NOSTACK)
1869 	{
1870 	case SH_WHIRLWIND:
1871 		orbtype = MT_WHIRLWIND_ORB;
1872 		break;
1873 	case SH_ATTRACT:
1874 		orbtype = MT_ATTRACT_ORB;
1875 		break;
1876 	case SH_ELEMENTAL:
1877 		orbtype = MT_ELEMENTAL_ORB;
1878 		break;
1879 	case SH_ARMAGEDDON:
1880 		orbtype = MT_ARMAGEDDON_ORB;
1881 		break;
1882 	case SH_PITY:
1883 	case SH_PINK: // PITY IN PINK
1884 		orbtype = MT_PITY_ORB;
1885 		break;
1886 	case SH_FLAMEAURA:
1887 		orbtype = MT_FLAMEAURA_ORB;
1888 		break;
1889 	case SH_BUBBLEWRAP:
1890 		orbtype = MT_BUBBLEWRAP_ORB;
1891 		break;
1892 	case SH_THUNDERCOIN:
1893 		orbtype = MT_THUNDERCOIN_ORB;
1894 		break;
1895 	default:
1896 		return;
1897 	}
1898 
1899 	// blaze through the thinkers to see if an orb already exists!
1900 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
1901 	{
1902 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
1903 			continue;
1904 
1905 		shieldobj = (mobj_t *)th;
1906 
1907 		if (shieldobj->type == orbtype && shieldobj->target == player->mo)
1908 			P_RemoveMobj(shieldobj); //kill the old one(s)
1909 	}
1910 
1911 	shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
1912 	shieldobj->flags2 |= MF2_SHIELD;
1913 	P_SetTarget(&shieldobj->target, player->mo);
1914 	if ((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK)
1915 	{
1916 		shieldobj->color = SKINCOLOR_PINK;
1917 		shieldobj->colorized = true;
1918 	}
1919 	else
1920 		shieldobj->color = (UINT16)shieldobj->info->painchance;
1921 	shieldobj->threshold = (player->powers[pw_shield] & SH_FORCE) ? SH_FORCE : (player->powers[pw_shield] & SH_NOSTACK);
1922 
1923 	if (shieldobj->info->seestate)
1924 	{
1925 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
1926 		P_SetTarget(&ov->target, shieldobj);
1927 		P_SetMobjState(ov, shieldobj->info->seestate);
1928 		P_SetTarget(&shieldobj->tracer, ov);
1929 	}
1930 	if (shieldobj->info->meleestate)
1931 	{
1932 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
1933 		P_SetTarget(&ov->target, shieldobj);
1934 		P_SetMobjState(ov, shieldobj->info->meleestate);
1935 	}
1936 	if (shieldobj->info->missilestate)
1937 	{
1938 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
1939 		P_SetTarget(&ov->target, shieldobj);
1940 		P_SetMobjState(ov, shieldobj->info->missilestate);
1941 	}
1942 	if (player->powers[pw_shield] & SH_FORCE)
1943 	{
1944 		//Copy and pasted from P_ShieldLook in p_mobj.c
1945 		shieldobj->movecount = (player->powers[pw_shield] & SH_FORCEHP);
1946 		if (shieldobj->movecount < 1)
1947 		{
1948 			if (shieldobj->info->painstate)
1949 				P_SetMobjState(shieldobj,shieldobj->info->painstate);
1950 			else
1951 				shieldobj->flags2 |= MF2_SHADOW;
1952 		}
1953 	}
1954 }
1955 
1956 //
1957 // P_SwitchShield
1958 //
1959 // Handles the possibility of switching between
1960 // the non-stack layer of shields thoroughly,
1961 // then adds the desired one.
1962 //
P_SwitchShield(player_t * player,UINT16 shieldtype)1963 void P_SwitchShield(player_t *player, UINT16 shieldtype)
1964 {
1965 	boolean donthavealready;
1966 
1967 	// If you already have a bomb shield, use it!
1968 	if ((shieldtype == SH_ARMAGEDDON) && (player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
1969 		P_BlackOw(player);
1970 
1971 	donthavealready = (shieldtype & SH_FORCE)
1972 		? (!(player->powers[pw_shield] & SH_FORCE) || (player->powers[pw_shield] & SH_FORCEHP) < (shieldtype & ~SH_FORCE))
1973 		: ((player->powers[pw_shield] & SH_NOSTACK) != shieldtype);
1974 
1975 	if (donthavealready)
1976 	{
1977 		boolean stopshieldability = (shieldtype & SH_FORCE)
1978 		? (!(player->powers[pw_shield] & SH_FORCE))
1979 		: true;
1980 
1981 		// Just in case.
1982 		if (stopshieldability && player->pflags & PF_SHIELDABILITY)
1983 		{
1984 			player->pflags &= ~(PF_SPINNING|PF_SHIELDABILITY); // They'll still have PF_THOKKED...
1985 			player->homing = 0;
1986 		}
1987 
1988 		player->powers[pw_shield] = shieldtype|(player->powers[pw_shield] & SH_STACK);
1989 		P_SpawnShieldOrb(player);
1990 
1991 		if (shieldtype & SH_PROTECTWATER)
1992 		{
1993 			if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
1994 			{
1995 				player->powers[pw_underwater] = 0;
1996 				P_RestoreMusic(player);
1997 			}
1998 			else
1999 				player->powers[pw_underwater] = 0;
2000 
2001 			if (player->powers[pw_spacetime] > 1)
2002 			{
2003 				player->powers[pw_spacetime] = 0;
2004 				P_RestoreMusic(player);
2005 			}
2006 		}
2007 	}
2008 }
2009 
2010 //
2011 // P_SpawnGhostMobj
2012 //
2013 // Spawns a ghost object on the player
2014 //
P_SpawnGhostMobj(mobj_t * mobj)2015 mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
2016 {
2017 	mobj_t *ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST);
2018 
2019 	P_SetScale(ghost, mobj->scale);
2020 	ghost->destscale = mobj->scale;
2021 
2022 	if (mobj->eflags & MFE_VERTICALFLIP)
2023 	{
2024 		ghost->eflags |= MFE_VERTICALFLIP;
2025 		ghost->z += mobj->height - ghost->height;
2026 	}
2027 
2028 	ghost->color = mobj->color;
2029 	ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
2030 
2031 	ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
2032 	ghost->rollangle = mobj->rollangle;
2033 	ghost->sprite = mobj->sprite;
2034 	ghost->sprite2 = mobj->sprite2;
2035 	ghost->frame = mobj->frame;
2036 	ghost->tics = -1;
2037 	ghost->frame &= ~FF_TRANSMASK;
2038 	ghost->frame |= tr_trans50<<FF_TRANSSHIFT;
2039 	ghost->fuse = ghost->info->damage;
2040 	ghost->skin = mobj->skin;
2041 
2042 	if (mobj->flags2 & MF2_OBJECTFLIP)
2043 		ghost->flags |= MF2_OBJECTFLIP;
2044 
2045 	if (mobj->player && mobj->player->followmobj)
2046 	{
2047 		mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
2048 		P_SetTarget(&ghost2->tracer, ghost);
2049 		P_SetTarget(&ghost->tracer, ghost2);
2050 		ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
2051 	}
2052 
2053 	return ghost;
2054 }
2055 
2056 //
2057 // P_SpawnThokMobj
2058 //
2059 // Spawns the appropriate thok object on the player
2060 //
P_SpawnThokMobj(player_t * player)2061 void P_SpawnThokMobj(player_t *player)
2062 {
2063 	mobj_t *mobj;
2064 	mobjtype_t type = player->thokitem;
2065 	fixed_t zheight;
2066 
2067 	if (player->skincolor == 0)
2068 		return;
2069 
2070 	if (player->spectator)
2071 		return;
2072 
2073 	if (!type)
2074 		return;
2075 
2076 	if (type == MT_GHOST)
2077 		mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us
2078 	else
2079 	{
2080 		if (player->mo->eflags & MFE_VERTICALFLIP)
2081 			zheight = player->mo->z + player->mo->height + FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT) - FixedMul(mobjinfo[type].height, player->mo->scale);
2082 		else
2083 			zheight = player->mo->z - FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT);
2084 
2085 		if (!(player->mo->eflags & MFE_VERTICALFLIP) && zheight < player->mo->floorz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT))
2086 			zheight = player->mo->floorz;
2087 		else if (player->mo->eflags & MFE_VERTICALFLIP && zheight + FixedMul(mobjinfo[type].height, player->mo->scale) > player->mo->ceilingz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT))
2088 			zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale);
2089 
2090 		mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type);
2091 
2092 		// set to player's angle, just in case
2093 		mobj->angle = player->drawangle;
2094 
2095 		// color and skin
2096 		mobj->color = player->mo->color;
2097 		mobj->skin = player->mo->skin;
2098 
2099 		// vertical flip
2100 		if (player->mo->eflags & MFE_VERTICALFLIP)
2101 			mobj->flags2 |= MF2_OBJECTFLIP;
2102 		mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP);
2103 
2104 		// scale
2105 		P_SetScale(mobj, (mobj->destscale = player->mo->scale));
2106 
2107 		if (type == MT_THOK) // spintrail-specific modification for MT_THOK
2108 		{
2109 			mobj->frame = FF_TRANS70;
2110 			mobj->fuse = mobj->tics;
2111 		}
2112 	}
2113 
2114 	P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do
2115 	G_GhostAddThok();
2116 }
2117 
2118 //
2119 // P_SpawnSpinMobj
2120 //
2121 // Spawns the appropriate spin object on the player
2122 //
P_SpawnSpinMobj(player_t * player,mobjtype_t type)2123 void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
2124 {
2125 	mobj_t *mobj;
2126 	fixed_t zheight;
2127 
2128 	if (player->skincolor == 0)
2129 		return;
2130 
2131 	if (player->spectator)
2132 		return;
2133 
2134 	if (!type)
2135 		return;
2136 
2137 	if (type == MT_GHOST)
2138 		mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us
2139 	else
2140 	{
2141 		if (player->mo->eflags & MFE_VERTICALFLIP)
2142 			zheight = player->mo->z + player->mo->height + FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT) - FixedMul(mobjinfo[type].height, player->mo->scale);
2143 		else
2144 			zheight = player->mo->z - FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT);
2145 
2146 		if (!(player->mo->eflags & MFE_VERTICALFLIP) && zheight < player->mo->floorz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT))
2147 			zheight = player->mo->floorz;
2148 		else if (player->mo->eflags & MFE_VERTICALFLIP && zheight + FixedMul(mobjinfo[type].height, player->mo->scale) > player->mo->ceilingz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT))
2149 			zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale);
2150 
2151 		mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type);
2152 
2153 		// set to player's angle, just in case
2154 		mobj->angle = player->drawangle;
2155 
2156 		// color and skin
2157 		mobj->color = player->mo->color;
2158 		mobj->skin = player->mo->skin;
2159 
2160 		// vertical flip
2161 		if (player->mo->eflags & MFE_VERTICALFLIP)
2162 			mobj->flags2 |= MF2_OBJECTFLIP;
2163 		mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP);
2164 
2165 		// scale
2166 		P_SetScale(mobj, player->mo->scale);
2167 		mobj->destscale = player->mo->scale;
2168 
2169 		if (type == MT_THOK) // spintrail-specific modification for MT_THOK
2170 		{
2171 			mobj->frame = FF_TRANS70;
2172 			mobj->fuse = mobj->tics;
2173 		}
2174 	}
2175 
2176 	P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do
2177 }
2178 
2179 /** Called when \p player finishes the level.
2180   *
2181   * Only use for cases where the player should be able to move
2182   * while waiting for others to finish. Otherwise, use P_DoPlayerExit().
2183   *
2184   * In single player or if ::cv_exitmove is disabled, this will also cause
2185   * P_PlayerThink() to call P_DoPlayerExit(), so you do not need to
2186   * make a special cases for those.
2187   *
2188   * \param player The player who finished the level.
2189   * \sa P_DoPlayerExit
2190   *
2191   */
P_DoPlayerFinish(player_t * player)2192 void P_DoPlayerFinish(player_t *player)
2193 {
2194 	if (player->pflags & PF_FINISHED)
2195 		return;
2196 
2197 	player->pflags |= PF_FINISHED;
2198 	P_GiveFinishFlags(player);
2199 
2200 	if (netgame)
2201 		CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);
2202 
2203 	player->powers[pw_underwater] = 0;
2204 	player->powers[pw_spacetime] = 0;
2205 	P_RestoreMusic(player);
2206 }
2207 
2208 //
2209 // P_DoPlayerExit
2210 //
2211 // Player exits the map via sector trigger
P_DoPlayerExit(player_t * player)2212 void P_DoPlayerExit(player_t *player)
2213 {
2214 	if (player->exiting)
2215 		return;
2216 
2217 	if (cv_allowexitlevel.value == 0 && !G_PlatformGametype())
2218 		return;
2219 	else if (gametyperules & GTR_RACE) // If in Race Mode, allow
2220 	{
2221 		if (!countdown) // a 60-second wait ala Sonic 2.
2222 			countdown = (cv_countdowntime.value - 1)*TICRATE + 1; // Use cv_countdowntime
2223 
2224 		player->exiting = 3*TICRATE;
2225 
2226 		if (!countdown2)
2227 			countdown2 = (8 + cv_countdowntime.value)*TICRATE + 1; // 8 sec more than countdowntime -- 11 is too much
2228 
2229 		if (P_CheckRacers())
2230 			player->exiting = (14*TICRATE)/5 + 1;
2231 	}
2232 	else
2233 		player->exiting = (14*TICRATE)/5 + 2; // Accidental death safeguard???
2234 
2235 	//player->pflags &= ~PF_GLIDING;
2236 	if (player->climbing)
2237 	{
2238 		player->climbing = 0;
2239 		player->pflags |= P_GetJumpFlags(player);
2240 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
2241 	}
2242 	else if (player->pflags & PF_STARTDASH)
2243 	{
2244 		player->pflags &= ~PF_STARTDASH;
2245 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
2246 	}
2247 	player->powers[pw_underwater] = 0;
2248 	player->powers[pw_spacetime] = 0;
2249 	P_RestoreMusic(player);
2250 }
2251 
2252 #define SPACESPECIAL 12
P_InSpaceSector(mobj_t * mo)2253 boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
2254 {
2255 	sector_t *sector = mo->subsector->sector;
2256 	fixed_t topheight, bottomheight;
2257 
2258 	if (GETSECSPECIAL(sector->special, 1) == SPACESPECIAL)
2259 		return true;
2260 
2261 	if (sector->ffloors)
2262 	{
2263 		ffloor_t *rover;
2264 
2265 		for (rover = sector->ffloors; rover; rover = rover->next)
2266 		{
2267 			if (!(rover->flags & FF_EXISTS))
2268 				continue;
2269 
2270 			if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL)
2271 				continue;
2272 			topheight    = P_GetFFloorTopZAt   (rover, mo->x, mo->y);
2273 			bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
2274 
2275 			if (mo->z + (mo->height/2) > topheight)
2276 				continue;
2277 
2278 			if (mo->z + (mo->height/2) < bottomheight)
2279 				continue;
2280 
2281 			return true;
2282 		}
2283 	}
2284 
2285 	return false; // No vacuum here, Captain!
2286 }
2287 
2288 //
2289 // P_PlayerHitFloor
2290 //
2291 // Handles player hitting floor surface.
2292 // Returns whether to clip momz.
P_PlayerHitFloor(player_t * player,boolean dorollstuff)2293 boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
2294 {
2295 	boolean clipmomz;
2296 
2297 	I_Assert(player->mo != NULL);
2298 
2299 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
2300 		return true;
2301 
2302 	if ((clipmomz = !(P_CheckDeathPitCollide(player->mo))) && player->mo->health && !player->spectator)
2303 	{
2304 		if (dorollstuff)
2305 		{
2306 			if ((player->charability2 == CA2_SPINDASH) && !((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_THOKKED) && !(player->charability == CA_THOK && player->secondjump)
2307 			&& (player->cmd.buttons & BT_SPIN) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale)))
2308 				player->pflags = (player->pflags|PF_SPINNING) & ~PF_THOKKED;
2309 			else if (!(player->pflags & PF_STARTDASH))
2310 				player->pflags &= ~PF_SPINNING;
2311 		}
2312 
2313 		if (player->pflags & PF_BOUNCING)
2314 		{
2315 			if (dorollstuff && player->mo->state-states != S_PLAY_BOUNCE_LANDING)
2316 			{
2317 				P_MobjCheckWater(player->mo);
2318 				player->mo->momz *= -1;
2319 				P_DoAbilityBounce(player, true);
2320 				if (player->scoreadd)
2321 					player->scoreadd--;
2322 			}
2323 			else
2324 				player->mo->z += P_MobjFlip(player->mo);
2325 			clipmomz = false;
2326 		}
2327 		else
2328 		{
2329 			P_MobjCheckWater(player->mo);
2330 			if (player->pflags & PF_SPINNING)
2331 			{
2332 				if (!(player->pflags & PF_STARTDASH) && player->panim != PA_ROLL && player->panim != PA_ETC
2333 				&& player->panim != PA_ABILITY && player->panim != PA_ABILITY2)
2334 				{
2335 					P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
2336 					S_StartSound(player->mo, sfx_spin);
2337 				}
2338 			}
2339 			else if (player->pflags & PF_GLIDING) // ground gliding
2340 			{
2341 				if (dorollstuff)
2342 				{
2343 					player->skidtime = TICRATE;
2344 					P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
2345 					P_SpawnSkidDust(player, player->mo->radius, true); // make sure the player knows they landed
2346 					player->mo->tics = -1;
2347 				}
2348 				else if (!player->skidtime)
2349 					player->pflags &= ~PF_GLIDING;
2350 			}
2351 			else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY))
2352 			&& (player->mo->floorz != player->mo->watertop) && player->mo->state-states == S_PLAY_FALL)
2353 			{
2354 				if (player->mo->state-states != S_PLAY_GLIDE_LANDING)
2355 				{
2356 					P_ResetPlayer(player);
2357 					P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE_LANDING);
2358 					player->pflags |= PF_STASIS;
2359 					if (player->speed > FixedMul(player->runspeed, player->mo->scale))
2360 						player->skidtime += player->mo->tics;
2361 					player->mo->momx = ((player->mo->momx - player->cmomx)/2) + player->cmomx;
2362 					player->mo->momy = ((player->mo->momy - player->cmomy)/2) + player->cmomy;
2363 					if (player->powers[pw_super])
2364 					{
2365 						P_Earthquake(player->mo, player->mo, 256*FRACUNIT);
2366 						S_StartSound(player->mo, sfx_s3k49);
2367 					}
2368 					else
2369 						S_StartSound(player->mo, sfx_s3k4c);
2370 				}
2371 			}
2372 			else if (player->charability2 == CA2_MELEE
2373 				&& ((player->panim == PA_ABILITY2) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY && player->cmd.buttons & (BT_JUMP|BT_SPIN))))
2374 			{
2375 				if (player->mo->state-states != S_PLAY_MELEE_LANDING)
2376 				{
2377 					mobjtype_t type = player->revitem;
2378 					P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING);
2379 					player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
2380 					S_StartSound(player->mo, sfx_s3k8b);
2381 					player->pflags |= PF_FULLSTASIS;
2382 
2383 					// hearticles
2384 					if (type)
2385 					{
2386 						UINT8 i = 0;
2387 						angle_t throwang = -(2*ANG30);
2388 						fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale);
2389 						fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale);
2390 						fixed_t zo = 6*player->mo->scale;
2391 						fixed_t mu = FixedMul(player->maxdash, player->mo->scale);
2392 						fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy);
2393 						fixed_t ev;
2394 						mobj_t *missile = NULL;
2395 						if (mu2 < mu)
2396 							mu2 = mu;
2397 						ev = (50*FRACUNIT - (mu/25))/50;
2398 						while (i < 5)
2399 						{
2400 							missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type);
2401 							P_SetTarget(&missile->target, player->mo);
2402 							missile->angle = throwang + player->drawangle;
2403 							P_Thrust(missile, player->drawangle + ANGLE_90,
2404 								P_ReturnThrustY(missile, throwang, mu)); // side to side component
2405 							P_Thrust(missile, player->drawangle, mu2); // forward component
2406 							P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true);
2407 							missile->momz += player->mo->pmomz;
2408 							missile->fuse = TICRATE/2;
2409 							missile->extravalue2 = ev;
2410 
2411 							i++;
2412 							throwang += ANG30;
2413 						}
2414 						if (mobjinfo[type].seesound && missile)
2415 							S_StartSound(missile, missile->info->seesound);
2416 					}
2417 				}
2418 			}
2419 			else if (player->charability == CA_GLIDEANDCLIMB && (player->mo->state-states == S_PLAY_GLIDE_LANDING))
2420 				;
2421 			else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
2422 				;
2423 			else if (dorollstuff && player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH)
2424 			{
2425 				fixed_t runspd = FixedMul(player->runspeed, player->mo->scale);
2426 
2427 				// See comments in P_MovePlayer for explanation of changes.
2428 
2429 				if (player->powers[pw_super])
2430 					runspd = FixedMul(runspd, 5*FRACUNIT/3);
2431 
2432 				runspd = FixedMul(runspd, player->mo->movefactor);
2433 
2434 				if (maptol & TOL_2D)
2435 					runspd = FixedMul(runspd, 2*FRACUNIT/3);
2436 
2437 				if (player->cmomx || player->cmomy)
2438 				{
2439 					if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
2440 						P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
2441 					else if (player->speed >= runspd
2442 					&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
2443 						P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
2444 					else if ((player->rmomx || player->rmomy)
2445 					&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
2446 						P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
2447 					else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE)
2448 						P_SetPlayerMobjState(player->mo, S_PLAY_STND);
2449 				}
2450 				else
2451 				{
2452 					if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
2453 						P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
2454 					else if (player->speed >= runspd
2455 					&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
2456 						P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
2457 					else if ((player->mo->momx || player->mo->momy)
2458 					&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
2459 						P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
2460 					else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE)
2461 						P_SetPlayerMobjState(player->mo, S_PLAY_STND);
2462 				}
2463 			}
2464 
2465 			if (!(player->pflags & PF_GLIDING))
2466 				player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
2467 			player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
2468 			player->secondjump = 0;
2469 			player->glidetime = 0;
2470 			player->climbing = 0;
2471 			player->powers[pw_tailsfly] = 0;
2472 
2473 			if (player->pflags & PF_SHIELDABILITY)
2474 			{
2475 				player->pflags &= ~PF_SHIELDABILITY;
2476 
2477 				if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
2478 				{
2479 					if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
2480 						S_StartSound(player->mo, sfx_s3k4c);
2481 					else // create a fire pattern on the ground
2482 					{
2483 						S_StartSound(player->mo, sfx_s3k47);
2484 						P_ElementalFire(player, true);
2485 					}
2486 					P_SetObjectMomZ(player->mo,
2487 					(player->mo->eflags & MFE_UNDERWATER)
2488 					? 6*FRACUNIT/5
2489 					: 5*FRACUNIT/2,
2490 					false);
2491 					P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
2492 					player->mo->momx = player->mo->momy = 0;
2493 					clipmomz = false;
2494 				}
2495 				else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
2496 				{
2497 					P_DoBubbleBounce(player);
2498 					clipmomz = false;
2499 				}
2500 			}
2501 		}
2502 	}
2503 
2504 	return clipmomz;
2505 }
2506 
P_InQuicksand(mobj_t * mo)2507 boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
2508 {
2509 	sector_t *sector = mo->subsector->sector;
2510 	fixed_t topheight, bottomheight;
2511 
2512 	fixed_t flipoffset = ((mo->eflags & MFE_VERTICALFLIP) ? (mo->height/2) : 0);
2513 
2514 	if (sector->ffloors)
2515 	{
2516 		ffloor_t *rover;
2517 
2518 		for (rover = sector->ffloors; rover; rover = rover->next)
2519 		{
2520 			if (!(rover->flags & FF_EXISTS))
2521 				continue;
2522 
2523 			if (!(rover->flags & FF_QUICKSAND))
2524 				continue;
2525 
2526 			topheight    = P_GetFFloorTopZAt   (rover, mo->x, mo->y);
2527 			bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
2528 
2529 			if (mo->z + flipoffset > topheight)
2530 				continue;
2531 
2532 			if (mo->z + (mo->height/2) + flipoffset < bottomheight)
2533 				continue;
2534 
2535 			return true;
2536 		}
2537 	}
2538 
2539 	return false; // No sand here, Captain!
2540 }
2541 
P_PlayerCanBust(player_t * player,ffloor_t * rover)2542 static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover)
2543 {
2544 	if (!(rover->flags & FF_EXISTS))
2545 		return false;
2546 
2547 	if (!(rover->flags & FF_BUSTUP))
2548 		return false;
2549 
2550 	/*if (rover->master->frontsector->crumblestate != CRUMBLE_NONE)
2551 		return false;*/
2552 
2553 	// If it's an FF_SHATTER, you can break it just by touching it.
2554 	if (rover->flags & FF_SHATTER)
2555 		return true;
2556 
2557 	// If it's an FF_SPINBUST, you can break it if you are in your spinning frames
2558 	// (either from jumping or spindashing).
2559 	if (rover->flags & FF_SPINBUST)
2560 	{
2561 		if ((player->pflags & PF_SPINNING) && !(player->pflags & PF_STARTDASH))
2562 			return true;
2563 
2564 		if ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE))
2565 			return true;
2566 	}
2567 
2568 	// Strong abilities can break even FF_STRONGBUST.
2569 	if (player->charflags & SF_CANBUSTWALLS)
2570 		return true;
2571 
2572 	if (player->pflags & PF_BOUNCING)
2573 		return true;
2574 
2575 	if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
2576 		return true;
2577 
2578 	if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
2579 		return true;
2580 
2581 	// Everyone else is out of luck.
2582 	if (rover->flags & FF_STRONGBUST)
2583 		return false;
2584 
2585 	// Spinning (and not jumping)
2586 	if ((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
2587 		return true;
2588 
2589 	// Super
2590 	if (player->powers[pw_super])
2591 		return true;
2592 
2593 	// Dashmode
2594 	if ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE) && player->dashmode >= DASHMODE_THRESHOLD)
2595 		return true;
2596 
2597 	// NiGHTS drill
2598 	if (player->pflags & PF_DRILLING)
2599 		return true;
2600 
2601 	// Recording for Metal Sonic
2602 	if (metalrecording)
2603 		return true;
2604 
2605 	return false;
2606 }
2607 
P_CheckBustableBlocks(player_t * player)2608 static void P_CheckBustableBlocks(player_t *player)
2609 {
2610 	msecnode_t *node;
2611 	fixed_t oldx;
2612 	fixed_t oldy;
2613 
2614 	if ((netgame || multiplayer) && player->spectator)
2615 		return;
2616 
2617 	oldx = player->mo->x;
2618 	oldy = player->mo->y;
2619 
2620 	if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways
2621 	{
2622 		P_UnsetThingPosition(player->mo);
2623 		player->mo->x += player->mo->momx;
2624 		player->mo->y += player->mo->momy;
2625 		P_SetThingPosition(player->mo);
2626 	}
2627 
2628 	for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
2629 	{
2630 		ffloor_t *rover;
2631 		fixed_t topheight, bottomheight;
2632 
2633 		if (!node->m_sector)
2634 			break;
2635 
2636 		if (!node->m_sector->ffloors)
2637 			continue;
2638 
2639 		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
2640 		{
2641 			if (!P_PlayerCanBust(player, rover))
2642 				continue;
2643 
2644 			topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
2645 			bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
2646 
2647 			if (((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
2648 			|| ((P_MobjFlip(player->mo)*player->mo->momz < 0) && (player->pflags & PF_BOUNCING || ((player->charability2 == CA2_MELEE) && (player->panim == PA_ABILITY2)))))
2649 			{
2650 				topheight -= player->mo->momz;
2651 				bottomheight -= player->mo->momz;
2652 			}
2653 
2654 			// Height checks
2655 			if (rover->flags & FF_SHATTERBOTTOM)
2656 			{
2657 				if (player->mo->z + player->mo->momz + player->mo->height < bottomheight)
2658 					continue;
2659 
2660 				if (player->mo->z + player->mo->height > bottomheight)
2661 					continue;
2662 			}
2663 			else if (rover->flags & FF_SPINBUST)
2664 			{
2665 				if (player->mo->z + player->mo->momz > topheight)
2666 					continue;
2667 
2668 				if (player->mo->z + player->mo->height < bottomheight)
2669 					continue;
2670 			}
2671 			else if (rover->flags & FF_SHATTER)
2672 			{
2673 				if (player->mo->z + player->mo->momz > topheight)
2674 					continue;
2675 
2676 				if (player->mo->z + player->mo->momz + player->mo->height < bottomheight)
2677 					continue;
2678 			}
2679 			else
2680 			{
2681 				if (player->mo->z >= topheight)
2682 					continue;
2683 
2684 				if (player->mo->z + player->mo->height < bottomheight)
2685 					continue;
2686 			}
2687 
2688 			// Impede the player's fall a bit
2689 			if (((rover->flags & FF_SPINBUST) || (rover->flags & FF_SHATTER)) && player->mo->z >= topheight)
2690 				player->mo->momz >>= 1;
2691 			else if (rover->flags & FF_SHATTER)
2692 			{
2693 				player->mo->momx >>= 1;
2694 				player->mo->momy >>= 1;
2695 			}
2696 
2697 			//if (metalrecording)
2698 			//	G_RecordBustup(rover);
2699 
2700 			EV_CrumbleChain(NULL, rover); // node->m_sector
2701 
2702 			// Run a linedef executor??
2703 			if (rover->master->flags & ML_EFFECT5)
2704 				P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), player->mo, node->m_sector);
2705 
2706 			goto bustupdone;
2707 		}
2708 	}
2709 bustupdone:
2710 	if (!(player->pflags & PF_BOUNCING))
2711 	{
2712 		P_UnsetThingPosition(player->mo);
2713 		player->mo->x = oldx;
2714 		player->mo->y = oldy;
2715 		P_SetThingPosition(player->mo);
2716 	}
2717 }
2718 
P_CheckBouncySectors(player_t * player)2719 static void P_CheckBouncySectors(player_t *player)
2720 {
2721 	msecnode_t *node;
2722 	fixed_t oldx;
2723 	fixed_t oldy;
2724 	fixed_t oldz;
2725 	vector3_t momentum;
2726 
2727 	oldx = player->mo->x;
2728 	oldy = player->mo->y;
2729 	oldz = player->mo->z;
2730 
2731 	P_UnsetThingPosition(player->mo);
2732 	player->mo->x += player->mo->momx;
2733 	player->mo->y += player->mo->momy;
2734 	player->mo->z += player->mo->momz;
2735 	P_SetThingPosition(player->mo);
2736 
2737 	for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
2738 	{
2739 		ffloor_t *rover;
2740 
2741 		if (!node->m_sector)
2742 			break;
2743 
2744 		if (!node->m_sector->ffloors)
2745 			continue;
2746 
2747 		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
2748 		{
2749 			fixed_t bouncestrength;
2750 			fixed_t topheight, bottomheight;
2751 
2752 			if (!(rover->flags & FF_EXISTS))
2753 				continue; // FOFs should not be bouncy if they don't even "exist"
2754 
2755 			if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15)
2756 				continue; // this sector type is required for FOFs to be bouncy
2757 
2758 			topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
2759 			bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
2760 
2761 			if (player->mo->z > topheight)
2762 				continue;
2763 
2764 			if (player->mo->z + player->mo->height < bottomheight)
2765 				continue;
2766 
2767 			bouncestrength = P_AproxDistance(rover->master->dx, rover->master->dy)/100;
2768 
2769 			if (oldz < P_GetFOFTopZ(player->mo, node->m_sector, rover, oldx, oldy, NULL)
2770 					&& oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL))
2771 			{
2772 				player->mo->momx = -FixedMul(player->mo->momx,bouncestrength);
2773 				player->mo->momy = -FixedMul(player->mo->momy,bouncestrength);
2774 
2775 				if (player->pflags & PF_SPINNING)
2776 				{
2777 					player->pflags &= ~PF_SPINNING;
2778 					player->pflags |= P_GetJumpFlags(player);
2779 					player->pflags |= PF_THOKKED;
2780 				}
2781 			}
2782 			else
2783 			{
2784 				fixed_t newmom;
2785 				pslope_t *slope = (abs(oldz - topheight) < abs(oldz + player->mo->height - bottomheight)) ? *rover->t_slope : *rover->b_slope;
2786 
2787 				momentum.x = player->mo->momx;
2788 				momentum.y = player->mo->momy;
2789 				momentum.z = player->mo->momz*2;
2790 
2791 				if (slope)
2792 					P_ReverseQuantizeMomentumToSlope(&momentum, slope);
2793 
2794 				newmom = momentum.z = -FixedMul(momentum.z,bouncestrength)/2;
2795 
2796 				if (abs(newmom) < (bouncestrength*2))
2797 					goto bouncydone;
2798 
2799 				if (!(rover->master->flags & ML_BOUNCY))
2800 				{
2801 					if (newmom > 0)
2802 					{
2803 						if (newmom < 8*FRACUNIT)
2804 							newmom = 8*FRACUNIT;
2805 					}
2806 					else if (newmom < 0)
2807 					{
2808 						if (newmom > -8*FRACUNIT)
2809 							newmom = -8*FRACUNIT;
2810 					}
2811 				}
2812 
2813 				if (newmom > P_GetPlayerHeight(player)/2)
2814 					newmom = P_GetPlayerHeight(player)/2;
2815 				else if (newmom < -P_GetPlayerHeight(player)/2)
2816 					newmom = -P_GetPlayerHeight(player)/2;
2817 
2818 				momentum.z = newmom*2;
2819 
2820 				if (slope)
2821 					P_QuantizeMomentumToSlope(&momentum, slope);
2822 
2823 				player->mo->momx = momentum.x;
2824 				player->mo->momy = momentum.y;
2825 				player->mo->momz = momentum.z/2;
2826 
2827 				if (player->pflags & PF_SPINNING)
2828 				{
2829 					player->pflags &= ~PF_SPINNING;
2830 					player->pflags |= P_GetJumpFlags(player);
2831 					player->pflags |= PF_THOKKED;
2832 				}
2833 			}
2834 
2835 			if ((player->pflags & PF_SPINNING) && player->speed < FixedMul(1<<FRACBITS, player->mo->scale) && player->mo->momz)
2836 			{
2837 				player->pflags &= ~PF_SPINNING;
2838 				player->pflags |= P_GetJumpFlags(player);
2839 			}
2840 
2841 			goto bouncydone;
2842 		}
2843 	}
2844 bouncydone:
2845 	P_UnsetThingPosition(player->mo);
2846 	player->mo->x = oldx;
2847 	player->mo->y = oldy;
2848 	player->mo->z = oldz;
2849 	P_SetThingPosition(player->mo);
2850 }
2851 
P_CheckQuicksand(player_t * player)2852 static void P_CheckQuicksand(player_t *player)
2853 {
2854 	ffloor_t *rover;
2855 	fixed_t sinkspeed, friction;
2856 	fixed_t topheight, bottomheight;
2857 
2858 	if (!(player->mo->subsector->sector->ffloors && player->mo->momz <= 0))
2859 		return;
2860 
2861 	for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next)
2862 	{
2863 		if (!(rover->flags & FF_EXISTS)) continue;
2864 
2865 		if (!(rover->flags & FF_QUICKSAND))
2866 			continue;
2867 
2868 		topheight    = P_GetFFloorTopZAt   (rover, player->mo->x, player->mo->y);
2869 		bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
2870 
2871 		if (topheight >= player->mo->z && bottomheight < player->mo->z + player->mo->height)
2872 		{
2873 			sinkspeed = abs(rover->master->v1->x - rover->master->v2->x)>>1;
2874 
2875 			sinkspeed = FixedDiv(sinkspeed,TICRATE*FRACUNIT);
2876 
2877 			if (player->mo->eflags & MFE_VERTICALFLIP)
2878 			{
2879 				fixed_t ceilingheight = P_GetCeilingZ(player->mo, player->mo->subsector->sector, player->mo->x, player->mo->y, NULL);
2880 
2881 				player->mo->z += sinkspeed;
2882 
2883 				if (player->mo->z + player->mo->height >= ceilingheight)
2884 					player->mo->z = ceilingheight - player->mo->height;
2885 
2886 				if (player->mo->momz <= 0)
2887 					P_PlayerHitFloor(player, false);
2888 			}
2889 			else
2890 			{
2891 				fixed_t floorheight = P_GetFloorZ(player->mo, player->mo->subsector->sector, player->mo->x, player->mo->y, NULL);
2892 
2893 				player->mo->z -= sinkspeed;
2894 
2895 				if (player->mo->z <= floorheight)
2896 					player->mo->z = floorheight;
2897 
2898 				if (player->mo->momz >= 0)
2899 					P_PlayerHitFloor(player, false);
2900 			}
2901 
2902 			friction = abs(rover->master->v1->y - rover->master->v2->y)>>6;
2903 
2904 			player->mo->momx = FixedMul(player->mo->momx, friction);
2905 			player->mo->momy = FixedMul(player->mo->momy, friction);
2906 		}
2907 	}
2908 }
2909 
2910 //
2911 // P_CheckSneakerAndLivesTimer
2912 //
2913 // Restores music from sneaker and life fanfares
2914 //
P_CheckSneakerAndLivesTimer(player_t * player)2915 static void P_CheckSneakerAndLivesTimer(player_t *player)
2916 {
2917 	if (player->powers[pw_extralife] == 1) // Extra Life!
2918 		P_RestoreMusic(player);
2919 
2920 	if (player->powers[pw_sneakers] == 1)
2921 		P_RestoreMusic(player);
2922 }
2923 
2924 //
2925 // P_CheckUnderwaterAndSpaceTimer
2926 //
2927 // Restores music from underwater and space warnings, and handles number generation
2928 //
P_CheckUnderwaterAndSpaceTimer(player_t * player)2929 static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
2930 {
2931 	tic_t timeleft = (player->powers[pw_spacetime]) ? player->powers[pw_spacetime] : player->powers[pw_underwater];
2932 
2933 	if (player->exiting || (player->pflags & PF_FINISHED))
2934 		player->powers[pw_underwater] = player->powers[pw_spacetime] = 0;
2935 
2936 	timeleft--; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity
2937 
2938 	if ((timeleft == 11*TICRATE) // 5
2939 	 || (timeleft ==  9*TICRATE) // 4
2940 	 || (timeleft ==  7*TICRATE) // 3
2941 	 || (timeleft ==  5*TICRATE) // 2
2942 	 || (timeleft ==  3*TICRATE) // 1
2943 	 || (timeleft ==  1*TICRATE) // 0
2944 	) {
2945 		fixed_t height = (player->mo->eflags & MFE_VERTICALFLIP)
2946 		? player->mo->z - FixedMul(8*FRACUNIT + mobjinfo[MT_DROWNNUMBERS].height, FixedMul(player->mo->scale, player->shieldscale))
2947 		: player->mo->z + player->mo->height + FixedMul(8*FRACUNIT, FixedMul(player->mo->scale, player->shieldscale));
2948 
2949 		mobj_t *numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
2950 
2951 		timeleft /= (2*TICRATE); // To be strictly accurate it'd need to be ((timeleft/TICRATE) - 1)/2, but integer division rounds down for us
2952 
2953 		if (player->charflags & SF_MACHINE)
2954 		{
2955 			S_StartSound(player->mo, sfx_buzz1);
2956 			timeleft += 6;
2957 		}
2958 		else
2959 			S_StartSound(player->mo, sfx_dwnind);
2960 
2961 		if (timeleft) // Don't waste time setting the state if the time is 0.
2962 			P_SetMobjState(numbermobj, numbermobj->info->spawnstate+timeleft);
2963 
2964 		P_SetTarget(&numbermobj->target, player->mo);
2965 		numbermobj->threshold = 40;
2966 		numbermobj->destscale = player->mo->scale;
2967 		P_SetScale(numbermobj, player->mo->scale);
2968 	}
2969 	// Underwater timer runs out
2970 	else if (timeleft == 1)
2971 	{
2972 		if ((netgame || multiplayer) && P_IsLocalPlayer(player))
2973 			S_ChangeMusic(mapmusname, mapmusflags, true);
2974 
2975 		if (player->powers[pw_spacetime] == 1)
2976 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPACEDROWN);
2977 		else
2978 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DROWNED);
2979 	}
2980 
2981 	if (!(player->mo->eflags & MFE_UNDERWATER) && player->powers[pw_underwater])
2982 	{
2983 		if (player->powers[pw_underwater] <= 12*TICRATE + 1)
2984 		{
2985 			player->powers[pw_underwater] = 0;
2986 			P_RestoreMusic(player);
2987 		}
2988 		else
2989 			player->powers[pw_underwater] = 0;
2990 	}
2991 
2992 	if (player->powers[pw_spacetime] > 1 && !P_InSpaceSector(player->mo))
2993 		player->powers[pw_spacetime] = 0;
2994 
2995 	// Underwater audio cues
2996 	if (P_IsLocalPlayer(player) && !player->bot)
2997 	{
2998 		if ((player->powers[pw_underwater] == 25*TICRATE + 1)
2999 		|| (player->powers[pw_underwater] == 20*TICRATE + 1)
3000 		|| (player->powers[pw_underwater] == 15*TICRATE + 1))
3001 			S_StartSound(NULL, sfx_wtrdng);
3002 
3003 		if (player->powers[pw_underwater] == 11*TICRATE + 1
3004 		&& player == &players[consoleplayer])
3005 		{
3006 			P_PlayJingle(player, JT_DROWN);
3007 		}
3008 	}
3009 }
3010 
3011 //
3012 // P_CheckInvincibilityTimer
3013 //
3014 // Restores music from invincibility, and handles invincibility sparkles
3015 //
P_CheckInvincibilityTimer(player_t * player)3016 static void P_CheckInvincibilityTimer(player_t *player)
3017 {
3018 	if (!player->powers[pw_invulnerability])
3019 		return;
3020 
3021 	if (mariomode && !player->powers[pw_super])
3022 		player->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (numskincolors - SKINCOLOR_RUBY))); // Passes through all saturated colours
3023 	else if (leveltime % (TICRATE/7) == 0)
3024 	{
3025 		mobj_t *sparkle = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_IVSP);
3026 		sparkle->destscale = player->mo->scale;
3027 		P_SetScale(sparkle, player->mo->scale);
3028 	}
3029 
3030 	// Resume normal music stuff.
3031 	if (player->powers[pw_invulnerability] == 1)
3032 	{
3033 		if (!player->powers[pw_super])
3034 		{
3035 			if (mariomode)
3036 			{
3037 				if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
3038 				{
3039 					player->mo->color = SKINCOLOR_WHITE;
3040 					G_GhostAddColor(GHC_FIREFLOWER);
3041 				}
3042 				else
3043 				{
3044 					player->mo->color = player->skincolor;
3045 					G_GhostAddColor(GHC_NORMAL);
3046 				}
3047 			}
3048 
3049 			// If you had a shield, restore its visual significance
3050 			P_SpawnShieldOrb(player);
3051 		}
3052 
3053 		if (!player->powers[pw_super] || (mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
3054 			P_RestoreMusic(player);
3055 	}
3056 }
3057 
3058 //
3059 // P_DoBubbleBreath
3060 //
3061 // Handles bubbles spawned by the player
3062 //
P_DoBubbleBreath(player_t * player)3063 static void P_DoBubbleBreath(player_t *player)
3064 {
3065 	fixed_t x = player->mo->x;
3066 	fixed_t y = player->mo->y;
3067 	fixed_t z = player->mo->z;
3068 	mobj_t *bubble = NULL;
3069 
3070 	if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_PROTECTWATER) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) || player->spectator)
3071 		return;
3072 
3073 	if (player->charflags & SF_MACHINE)
3074 	{
3075 		if (player->powers[pw_underwater] && P_RandomChance((128-(player->powers[pw_underwater]/4))*FRACUNIT/256))
3076 		{
3077 			fixed_t r = player->mo->radius>>FRACBITS;
3078 			x += (P_RandomRange(r, -r)<<FRACBITS);
3079 			y += (P_RandomRange(r, -r)<<FRACBITS);
3080 			z += (P_RandomKey(player->mo->height>>FRACBITS)<<FRACBITS);
3081 			bubble = P_SpawnMobj(x, y, z, MT_WATERZAP);
3082 			S_StartSound(bubble, sfx_beelec);
3083 		}
3084 	}
3085 	else
3086 	{
3087 		if (player->mo->eflags & MFE_VERTICALFLIP)
3088 			z += player->mo->height - FixedDiv(player->mo->height,5*(FRACUNIT/4));
3089 		else
3090 			z += FixedDiv(player->mo->height,5*(FRACUNIT/4));
3091 
3092 		if (P_RandomChance(FRACUNIT/16))
3093 			bubble = P_SpawnMobj(x, y, z, MT_SMALLBUBBLE);
3094 		else if (P_RandomChance(3*FRACUNIT/256))
3095 			bubble = P_SpawnMobj(x, y, z, MT_MEDIUMBUBBLE);
3096 	}
3097 
3098 	if (bubble)
3099 	{
3100 		bubble->threshold = 42;
3101 		bubble->destscale = player->mo->scale;
3102 		P_SetScale(bubble, bubble->destscale);
3103 	}
3104 
3105 	// Tails stirs up the water while flying in it
3106 	if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM)
3107 	{
3108 		fixed_t radius = player->mo->radius;
3109 		angle_t fa = ((leveltime%45)*FINEANGLES/8) & FINEMASK;
3110 		fixed_t stirwaterx = FixedMul(FINECOSINE(fa),radius);
3111 		fixed_t stirwatery = FixedMul(FINESINE(fa),radius);
3112 		fixed_t stirwaterz;
3113 
3114 		if (player->mo->eflags & MFE_VERTICALFLIP)
3115 			stirwaterz = player->mo->z + player->mo->height - (4<<FRACBITS);
3116 		else
3117 			stirwaterz = player->mo->z + (4<<FRACBITS);
3118 
3119 		bubble = P_SpawnMobj(
3120 			player->mo->x + stirwaterx,
3121 			player->mo->y + stirwatery,
3122 			stirwaterz, MT_SMALLBUBBLE);
3123 		bubble->destscale = player->mo->scale;
3124 		P_SetScale(bubble,bubble->destscale);
3125 
3126 		bubble = P_SpawnMobj(
3127 			player->mo->x - stirwaterx,
3128 			player->mo->y - stirwatery,
3129 			stirwaterz, MT_SMALLBUBBLE);
3130 		bubble->destscale = player->mo->scale;
3131 		P_SetScale(bubble,bubble->destscale);
3132 	}
3133 }
3134 
3135 //
3136 // P_DoPlayerHeadSigns
3137 //
3138 // Spawns "IT" and "GOT FLAG" signs for Tag and CTF respectively
3139 //
P_DoPlayerHeadSigns(player_t * player)3140 static void P_DoPlayerHeadSigns(player_t *player)
3141 {
3142 	if (G_TagGametype())
3143 	{
3144 		// If you're "IT", show a big "IT" over your head for others to see.
3145 		if (player->pflags & PF_TAGIT)
3146 		{
3147 			if (!P_IsLocalPlayer(player)) // Don't display it on your own view.
3148 			{
3149 				if (!(player->mo->eflags & MFE_VERTICALFLIP))
3150 					P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height, MT_TAG);
3151 				else
3152 					P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z - mobjinfo[MT_TAG].height, MT_TAG)->eflags |= MFE_VERTICALFLIP;
3153 			}
3154 		}
3155 	}
3156 	else if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) // If you have the flag (duh).
3157 	{
3158 		// Spawn a got-flag message over the head of the player that
3159 		// has it (but not on your own screen if you have the flag).
3160 		if (splitscreen || player != &players[consoleplayer])
3161 		{
3162 			mobj_t *sign = P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
3163 					player->mo->z+player->mo->momz, MT_GOTFLAG);
3164 			if (player->mo->eflags & MFE_VERTICALFLIP)
3165 			{
3166 				sign->z += player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTFLAG].height-FixedMul(16*FRACUNIT, player->mo->scale);
3167 				sign->eflags |= MFE_VERTICALFLIP;
3168 			}
3169 			else
3170 				sign->z += P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale);
3171 
3172 			if (player->gotflag & GF_REDFLAG)
3173 				sign->frame = 1|FF_FULLBRIGHT;
3174 			else //if (player->gotflag & GF_BLUEFLAG)
3175 				sign->frame = 2|FF_FULLBRIGHT;
3176 		}
3177 	}
3178 }
3179 
3180 //
3181 // P_DoClimbing
3182 //
3183 // Handles player climbing
3184 //
P_DoClimbing(player_t * player)3185 static void P_DoClimbing(player_t *player)
3186 {
3187 	ticcmd_t *cmd = &player->cmd;
3188 	fixed_t platx;
3189 	fixed_t platy;
3190 	subsector_t *glidesector;
3191 	boolean climb = true;
3192 
3193 	platx = P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
3194 	platy = P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
3195 
3196 	glidesector = R_PointInSubsectorOrNull(player->mo->x + platx, player->mo->y + platy);
3197 
3198 	{
3199 		boolean floorclimb = false;
3200 		boolean thrust = false;
3201 		boolean boostup = false;
3202 		boolean skyclimber = false;
3203 		fixed_t floorheight, ceilingheight;
3204 
3205 		if (!glidesector)
3206 			floorclimb = true;
3207 		else
3208 		{
3209 			floorheight   = P_GetSectorFloorZAt  (glidesector->sector, player->mo->x, player->mo->y);
3210 			ceilingheight = P_GetSectorCeilingZAt(glidesector->sector, player->mo->x, player->mo->y);
3211 
3212 			if (glidesector->sector->ffloors)
3213 			{
3214 				ffloor_t *rover;
3215 				fixed_t topheight, bottomheight;
3216 
3217 				for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
3218 				{
3219 					if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS)))
3220 						continue;
3221 
3222 					floorclimb = true;
3223 
3224 					topheight    = P_GetFFloorTopZAt   (rover, player->mo->x, player->mo->y);
3225 					bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
3226 
3227 					// Only supports rovers that are moving like an 'elevator', not just the top or bottom.
3228 					if (rover->master->frontsector->floorspeed && rover->master->frontsector->ceilspeed == 42)
3229 					{
3230 						if ((!(player->mo->eflags & MFE_VERTICALFLIP) && (bottomheight < player->mo->z+player->mo->height)
3231 							&& (topheight >= player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale)))
3232 						|| ((player->mo->eflags & MFE_VERTICALFLIP) && (topheight > player->mo->z)
3233 							&& (bottomheight <= player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale))))
3234 						{
3235 							if (cmd->forwardmove != 0)
3236 								player->mo->momz += rover->master->frontsector->floorspeed;
3237 							else
3238 							{
3239 								player->mo->momz = rover->master->frontsector->floorspeed;
3240 								climb = false;
3241 							}
3242 						}
3243 					}
3244 
3245 					// Gravity is flipped, so the comments are, too.
3246 					if (player->mo->eflags & MFE_VERTICALFLIP)
3247 					{
3248 						// Trying to climb down past the bottom of the FOF
3249 						if ((topheight >= player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) >= topheight))
3250 						{
3251 							fixed_t bottomheight2;
3252 							ffloor_t *roverbelow;
3253 							boolean foundfof = false;
3254 							floorclimb = true;
3255 							boostup = false;
3256 
3257 							// Is there a FOF directly below this one that we can move onto?
3258 							for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next)
3259 							{
3260 								if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS)))
3261 									continue;
3262 
3263 								if (roverbelow == rover)
3264 									continue;
3265 
3266 								bottomheight2 = P_GetFFloorBottomZAt(roverbelow, player->mo->x, player->mo->y);
3267 								if (bottomheight2 < topheight + FixedMul(16*FRACUNIT, player->mo->scale))
3268 									foundfof = true;
3269 							}
3270 
3271 							if (!foundfof)
3272 								player->mo->momz = 0;
3273 						}
3274 
3275 						// Below the FOF
3276 						if (topheight <= player->mo->z)
3277 						{
3278 							floorclimb = false;
3279 							boostup = false;
3280 							thrust = false;
3281 						}
3282 
3283 						// Above the FOF
3284 						if (bottomheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale))
3285 						{
3286 							floorclimb = false;
3287 							thrust = true;
3288 							boostup = true;
3289 						}
3290 					}
3291 					else
3292 					{
3293 						// Trying to climb down past the bottom of a FOF
3294 						if ((bottomheight <= player->mo->z) && ((player->mo->z + player->mo->momz) <= bottomheight))
3295 						{
3296 							fixed_t topheight2;
3297 							ffloor_t *roverbelow;
3298 							boolean foundfof = false;
3299 							floorclimb = true;
3300 							boostup = false;
3301 
3302 							// Is there a FOF directly below this one that we can move onto?
3303 							for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next)
3304 							{
3305 								if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS)))
3306 									continue;
3307 
3308 								if (roverbelow == rover)
3309 									continue;
3310 
3311 								topheight2 = P_GetFFloorTopZAt(roverbelow, player->mo->x, player->mo->y);
3312 								if (topheight2 > bottomheight - FixedMul(16*FRACUNIT, player->mo->scale))
3313 									foundfof = true;
3314 							}
3315 
3316 							if (!foundfof)
3317 								player->mo->momz = 0;
3318 						}
3319 
3320 						// Below the FOF
3321 						if (bottomheight >= player->mo->z + player->mo->height)
3322 						{
3323 							floorclimb = false;
3324 							boostup = false;
3325 							thrust = false;
3326 						}
3327 
3328 						// Above the FOF
3329 						if (topheight < player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale))
3330 						{
3331 							floorclimb = false;
3332 							thrust = true;
3333 							boostup = true;
3334 						}
3335 					}
3336 
3337 					if (floorclimb)
3338 					{
3339 						if (rover->flags & FF_CRUMBLE && !(netgame && player->spectator))
3340 							EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, !(rover->flags & FF_NORETURN));
3341 						break;
3342 					}
3343 				}
3344 			}
3345 
3346 			// Gravity is flipped, so are comments.
3347 			if (player->mo->eflags & MFE_VERTICALFLIP)
3348 			{
3349 				// Trying to climb down past the upper texture area
3350 				if ((floorheight >= player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) >= floorheight))
3351 				{
3352 					boolean foundfof = false;
3353 					floorclimb = true;
3354 
3355 					// Is there a FOF directly below that we can move onto?
3356 					if (glidesector->sector->ffloors)
3357 					{
3358 						fixed_t bottomheight;
3359 						ffloor_t *rover;
3360 						for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
3361 						{
3362 							if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS)))
3363 								continue;
3364 
3365 							bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
3366 							if (bottomheight < floorheight + FixedMul(16*FRACUNIT, player->mo->scale))
3367 							{
3368 								foundfof = true;
3369 								break;
3370 							}
3371 						}
3372 					}
3373 
3374 					if (!foundfof)
3375 						player->mo->momz = 0;
3376 				}
3377 
3378 				// Reached the top of the lower texture area
3379 				if (!floorclimb && ceilingheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale)
3380 					&& (glidesector->sector->ceilingpic == skyflatnum || floorheight < (player->mo->z - FixedMul(8*FRACUNIT, player->mo->scale))))
3381 				{
3382 					thrust = true;
3383 					boostup = true;
3384 					// Play climb-up animation here
3385 				}
3386 			}
3387 			else
3388 			{
3389 				// Trying to climb down past the upper texture area
3390 				if ((ceilingheight <= player->mo->z) && ((player->mo->z + player->mo->momz) <= ceilingheight))
3391 				{
3392 					boolean foundfof = false;
3393 					floorclimb = true;
3394 
3395 					// Is there a FOF directly below that we can move onto?
3396 					if (glidesector->sector->ffloors)
3397 					{
3398 						fixed_t topheight;
3399 						ffloor_t *rover;
3400 						for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
3401 						{
3402 							if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS)))
3403 								continue;
3404 
3405 							topheight = P_GetFFloorTopZAt(rover, player->mo->x, player->mo->y);
3406 							if (topheight > ceilingheight - FixedMul(16*FRACUNIT, player->mo->scale))
3407 							{
3408 								foundfof = true;
3409 								break;
3410 							}
3411 						}
3412 					}
3413 
3414 					if (!foundfof)
3415 						player->mo->momz = 0;
3416 				}
3417 
3418 				// Allow climbing from a FOF or lower texture onto the upper texture and vice versa.
3419 				if (player->mo->z > ceilingheight - FixedMul(16*FRACUNIT, player->mo->scale))
3420 				{
3421 					floorclimb = true;
3422 					thrust = false;
3423 					boostup = false;
3424 				}
3425 
3426 				// Reached the top of the lower texture area
3427 				if (!floorclimb && floorheight < player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale)
3428 					&& (glidesector->sector->ceilingpic == skyflatnum || ceilingheight > (player->mo->z + player->mo->height + FixedMul(8*FRACUNIT, player->mo->scale))))
3429 				{
3430 					thrust = true;
3431 					boostup = true;
3432 					// Play climb-up animation here
3433 				}
3434 			}
3435 
3436 			// Trying to climb on the sky
3437 			if ((ceilingheight < player->mo->z) && glidesector->sector->ceilingpic == skyflatnum)
3438 			{
3439 				skyclimber = true;
3440 			}
3441 
3442 			// Climbing on the lower texture area?
3443 			if ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale) < floorheight)
3444 				|| ((player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height <= floorheight))
3445 			{
3446 				floorclimb = true;
3447 
3448 				if (glidesector->sector->floorspeed)
3449 				{
3450 					if (cmd->forwardmove != 0)
3451 						player->mo->momz += glidesector->sector->floorspeed;
3452 					else
3453 					{
3454 						player->mo->momz = glidesector->sector->floorspeed;
3455 						climb = false;
3456 					}
3457 				}
3458 			}
3459 			// Climbing on the upper texture area?
3460 			else if ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z >= ceilingheight)
3461 				|| ((player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale) > ceilingheight))
3462 			{
3463 				floorclimb = true;
3464 
3465 				if (glidesector->sector->ceilspeed)
3466 				{
3467 					if (cmd->forwardmove != 0)
3468 						player->mo->momz += glidesector->sector->ceilspeed;
3469 					else
3470 					{
3471 						player->mo->momz = glidesector->sector->ceilspeed;
3472 						climb = false;
3473 					}
3474 				}
3475 			}
3476 		}
3477 
3478 		if (player->lastsidehit != -1 && player->lastlinehit != -1)
3479 		{
3480 			thinker_t *think;
3481 			scroll_t *scroller;
3482 			angle_t sideangle;
3483 			fixed_t dx, dy;
3484 
3485 			for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
3486 			{
3487 				if (think->function.acp1 != (actionf_p1)T_Scroll)
3488 					continue;
3489 
3490 				scroller = (scroll_t *)think;
3491 
3492 				if (scroller->type != sc_side)
3493 					continue;
3494 
3495 				if (scroller->affectee != player->lastsidehit)
3496 					continue;
3497 
3498 				if (scroller->accel)
3499 				{
3500 					dx = scroller->vdx;
3501 					dy = scroller->vdy;
3502 				}
3503 				else
3504 				{
3505 					dx = scroller->dx;
3506 					dy = scroller->dy;
3507 				}
3508 
3509 				if (cmd->forwardmove != 0)
3510 				{
3511 					player->mo->momz += dy;
3512 					climb = true;
3513 				}
3514 				else
3515 				{
3516 					player->mo->momz = dy;
3517 					climb = false;
3518 				}
3519 
3520 				sideangle = R_PointToAngle2(lines[player->lastlinehit].v2->x,lines[player->lastlinehit].v2->y,lines[player->lastlinehit].v1->x,lines[player->lastlinehit].v1->y);
3521 
3522 				if (cmd->sidemove != 0)
3523 				{
3524 					P_Thrust(player->mo, sideangle, dx);
3525 					climb = true;
3526 				}
3527 				else
3528 				{
3529 					P_InstaThrust(player->mo, sideangle, dx);
3530 					climb = false;
3531 				}
3532 			}
3533 		}
3534 
3535 		if (cmd->sidemove != 0 || cmd->forwardmove != 0)
3536 			climb = true;
3537 		else
3538 			climb = false;
3539 
3540 		if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
3541 			&& player->mo->state-states != S_PLAY_CLIMB)
3542 			P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB);
3543 		else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
3544 			P_SetPlayerMobjState(player->mo, S_PLAY_CLING);
3545 
3546 		if (!floorclimb)
3547 		{
3548 			if (boostup)
3549 			{
3550 				P_SetObjectMomZ(player->mo, 2*FRACUNIT, true);
3551 				if (cmd->forwardmove)
3552 					player->mo->momz = 2*player->mo->momz/3;
3553 			}
3554 			if (thrust)
3555 				P_Thrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Lil' boost up.
3556 
3557 			player->climbing = 0;
3558 			player->pflags |= P_GetJumpFlags(player);
3559 			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
3560 		}
3561 
3562 		if (skyclimber)
3563 		{
3564 			player->climbing = 0;
3565 			player->pflags |= P_GetJumpFlags(player);
3566 			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
3567 		}
3568 	}
3569 
3570 	if (cmd->sidemove != 0 || cmd->forwardmove != 0)
3571 		climb = true;
3572 	else
3573 		climb = false;
3574 
3575 	if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
3576 		&& player->mo->state-states != S_PLAY_CLIMB)
3577 		P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB);
3578 	else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
3579 		P_SetPlayerMobjState(player->mo, S_PLAY_CLING);
3580 
3581 	if (cmd->buttons & BT_SPIN && !(player->pflags & PF_JUMPSTASIS))
3582 	{
3583 		player->climbing = 0;
3584 		player->pflags |= P_GetJumpFlags(player);
3585 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
3586 		P_SetObjectMomZ(player->mo, 4*FRACUNIT, false);
3587 		P_Thrust(player->mo, player->mo->angle, FixedMul(-4*FRACUNIT, player->mo->scale));
3588 	}
3589 
3590 #define CLIMBCONEMAX FixedAngle(90*FRACUNIT)
3591 	if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
3592 	{
3593 		angle_t angdiff = P_GetLocalAngle(player) - player->mo->angle;
3594 		if (angdiff < ANGLE_180 && angdiff > CLIMBCONEMAX)
3595 			P_SetLocalAngle(player, player->mo->angle + CLIMBCONEMAX);
3596 		else if (angdiff > ANGLE_180 && angdiff < InvAngle(CLIMBCONEMAX))
3597 			P_SetLocalAngle(player, player->mo->angle - CLIMBCONEMAX);
3598 	}
3599 
3600 	if (player->climbing == 0)
3601 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
3602 
3603 	if (player->climbing && P_IsObjectOnGround(player->mo))
3604 	{
3605 		P_ResetPlayer(player);
3606 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
3607 	}
3608 }
3609 
3610 //
3611 // PIT_CheckSolidsTeeter
3612 // AKA: PIT_HacksToStopPlayersTeeteringOnGargoyles
3613 //
3614 
3615 static mobj_t *teeterer; // the player checking for teetering
3616 static boolean solidteeter; // saves whether the player can teeter on this or not
3617 static fixed_t highesttop; // highest floor found so far
3618 // reserved for standing on multiple objects
3619 static boolean couldteeter;
3620 static fixed_t teeterxl, teeterxh;
3621 static fixed_t teeteryl, teeteryh;
3622 
PIT_CheckSolidsTeeter(mobj_t * thing)3623 static boolean PIT_CheckSolidsTeeter(mobj_t *thing)
3624 {
3625 	fixed_t blockdist;
3626 	fixed_t tiptop = FixedMul(MAXSTEPMOVE, teeterer->scale);
3627 	fixed_t thingtop = thing->z + thing->height;
3628 	fixed_t teeterertop = teeterer->z + teeterer->height;
3629 
3630 	if (!teeterer || !thing)
3631 		return true;
3632 
3633 	if (!(thing->flags & MF_SOLID))
3634 		return true;
3635 
3636 	if (thing->flags & MF_NOCLIP)
3637 		return true;
3638 
3639 	if (thing == teeterer)
3640 		return true;
3641 
3642 	if (thing->player && cv_tailspickup.value && !(gametyperules & GTR_HIDEFROZEN))
3643 		return true;
3644 
3645 	blockdist = teeterer->radius + thing->radius;
3646 
3647 	if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist)
3648 		return true; // didn't hit it
3649 
3650 	if (teeterer->eflags & MFE_VERTICALFLIP)
3651 	{
3652 		if (thingtop < teeterer->z)
3653 			return true;
3654 		if (thing->z > highesttop)
3655 			return true;
3656 		highesttop = thing->z;
3657 		if (thing->z > teeterertop + tiptop)
3658 		{
3659 			solidteeter = true;
3660 			return true;
3661 		}
3662 	}
3663 	else
3664 	{
3665 		if (thing->z > teeterertop)
3666 			return true;
3667 		if (thingtop < highesttop)
3668 			return true;
3669 		highesttop = thingtop;
3670 		if (thingtop < teeterer->z - tiptop)
3671 		{
3672 			solidteeter = true;
3673 			return true;
3674 		}
3675 	}
3676 
3677 	// are you standing on this?
3678 	if ((teeterer->eflags & MFE_VERTICALFLIP && thing->z - FixedMul(FRACUNIT, teeterer->scale) == teeterertop)
3679 	|| (!(teeterer->eflags & MFE_VERTICALFLIP) && thingtop + FixedMul(FRACUNIT, teeterer->scale) == teeterer->z))
3680 	{
3681 		fixed_t teeterdist = thing->radius - FixedMul(5*FRACUNIT, teeterer->scale);
3682 		// how far are you from the edge?
3683 		if (abs(teeterer->x - thing->x) > teeterdist || abs(teeterer->y - thing->y) > teeterdist)
3684 		{
3685 			if (couldteeter) // player is standing on another object already, see if we can stand on both and not teeter!
3686 			{
3687 				if (thing->x - teeterdist < teeterxl)
3688 					teeterxl = thing->x - teeterdist;
3689 				if (thing->x + teeterdist > teeterxh)
3690 					teeterxh = thing->x + teeterdist;
3691 				if (thing->y - teeterdist < teeteryl)
3692 					teeteryl = thing->y - teeterdist;
3693 				if (thing->y + teeterdist > teeteryh)
3694 					teeteryh = thing->y + teeterdist;
3695 
3696 				if (teeterer->x < teeterxl)
3697 					return true;
3698 				if (teeterer->x > teeterxh)
3699 					return true;
3700 				if (teeterer->y < teeteryl)
3701 					return true;
3702 				if (teeterer->y > teeteryh)
3703 					return true;
3704 
3705 				solidteeter = false; // you can stop teetering now!
3706 				couldteeter = false; // just in case...
3707 				return false;
3708 			}
3709 			else
3710 			{
3711 				// too far! don't change teeter status though
3712 				// save teeter x/y limits to bring up later
3713 				teeterxl = thing->x - teeterdist;
3714 				teeterxh = thing->x + teeterdist;
3715 				teeteryl = thing->y - teeterdist;
3716 				teeteryh = thing->y + teeterdist;
3717 			}
3718 			couldteeter = true;
3719 			return true;
3720 		}
3721 		solidteeter = false;
3722 		couldteeter = false;
3723 		return false; // you're definitely not teetering, that's the end of the matter
3724 	}
3725 	solidteeter = false;
3726 	return true; // you're not teetering but it's not neccessarily over, YET
3727 }
3728 
3729 //
3730 // P_DoTeeter
3731 //
3732 // Handles player teetering
3733 //
P_DoTeeter(player_t * player)3734 static void P_DoTeeter(player_t *player)
3735 {
3736 	boolean teeter = false;
3737 	boolean roverfloor; // solid 3d floors?
3738 	fixed_t floorheight, ceilingheight;
3739 	fixed_t topheight, bottomheight; // for 3d floor usage
3740 	const fixed_t tiptop = FixedMul(MAXSTEPMOVE, player->mo->scale); // Distance you have to be above the ground in order to teeter.
3741 
3742 	if (player->mo->standingslope && player->mo->standingslope->zdelta >= (FRACUNIT/2)) // Always teeter if the slope is too steep.
3743 		teeter = true;
3744 	else // Let's do some checks...
3745 	{
3746 		UINT8 i;
3747 		sector_t *sec;
3748 		fixed_t highestceilingheight = INT32_MIN;
3749 		fixed_t lowestfloorheight = INT32_MAX;
3750 
3751 		teeter = false;
3752 		roverfloor = false;
3753 		for (i = 0; i < 4; i++)
3754 		{
3755 			ffloor_t *rover;
3756 
3757 #define xsign ((i & 1) ? -1 : 1) // 0 -> 1 | 1 -> -1 | 2 -> 1 | 3 -> -1
3758 #define ysign ((i & 2) ? 1 : -1) // 0 -> 1 | 1 -> 1 | 2 -> -1 | 3 -> -1
3759 			fixed_t checkx = player->mo->x + (xsign*FixedMul(5*FRACUNIT, player->mo->scale));
3760 			fixed_t checky = player->mo->y + (ysign*FixedMul(5*FRACUNIT, player->mo->scale));
3761 #undef xsign
3762 #undef ysign
3763 
3764 			sec = R_PointInSubsector(checkx, checky)->sector;
3765 
3766 			ceilingheight = P_GetSectorCeilingZAt(sec, checkx, checky);
3767 			floorheight   = P_GetSectorFloorZAt  (sec, checkx, checky);
3768 			highestceilingheight = (ceilingheight > highestceilingheight) ? ceilingheight : highestceilingheight;
3769 			lowestfloorheight = (floorheight < lowestfloorheight) ? floorheight : lowestfloorheight;
3770 
3771 			if (!(sec->ffloors))
3772 				continue; // move on to the next subsector
3773 
3774 			for (rover = sec->ffloors; rover; rover = rover->next)
3775 			{
3776 				if (!(rover->flags & FF_EXISTS)) continue;
3777 
3778 				topheight    = P_GetFFloorTopZAt   (rover, player->mo->x, player->mo->y);
3779 				bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
3780 
3781 				if (P_CheckSolidLava(rover))
3782 					;
3783 				else if (!(rover->flags & FF_BLOCKPLAYER || rover->flags & FF_QUICKSAND))
3784 					continue; // intangible 3d floor
3785 
3786 				if (player->mo->eflags & MFE_VERTICALFLIP)
3787 				{
3788 					if (bottomheight > ceilingheight) // Above the ceiling
3789 						continue;
3790 
3791 					if (bottomheight > player->mo->z + player->mo->height + tiptop
3792 						|| (topheight < player->mo->z
3793 						&& player->mo->z + player->mo->height < ceilingheight - tiptop))
3794 					{
3795 						teeter = true;
3796 						roverfloor = true;
3797 					}
3798 					else
3799 					{
3800 						teeter = false;
3801 						roverfloor = true;
3802 						break;
3803 					}
3804 				}
3805 				else
3806 				{
3807 					if (topheight < floorheight) // Below the floor
3808 						continue;
3809 
3810 					if (topheight < player->mo->z - tiptop
3811 						|| (bottomheight > player->mo->z + player->mo->height
3812 						&& player->mo->z > floorheight + tiptop))
3813 					{
3814 						teeter = true;
3815 						roverfloor = true;
3816 					}
3817 					else
3818 					{
3819 						teeter = false;
3820 						roverfloor = true;
3821 						break;
3822 					}
3823 				}
3824 			}
3825 			break; // break out of loop now, we're done
3826 		}
3827 
3828 		if (player->mo->eflags & MFE_VERTICALFLIP)
3829 		{
3830 			if (!teeter && !roverfloor && (highestceilingheight > player->mo->ceilingz + tiptop))
3831 					teeter = true;
3832 		}
3833 		else
3834 		{
3835 			if (!teeter && !roverfloor && (lowestfloorheight < player->mo->floorz - tiptop))
3836 					teeter = true;
3837 		}
3838 	}
3839 
3840 	{
3841 		INT32 bx, by, xl, xh, yl, yh;
3842 
3843 		yh = (unsigned)(player->mo->y + player->mo->radius - bmaporgy)>>MAPBLOCKSHIFT;
3844 		yl = (unsigned)(player->mo->y - player->mo->radius - bmaporgy)>>MAPBLOCKSHIFT;
3845 		xh = (unsigned)(player->mo->x + player->mo->radius - bmaporgx)>>MAPBLOCKSHIFT;
3846 		xl = (unsigned)(player->mo->x - player->mo->radius - bmaporgx)>>MAPBLOCKSHIFT;
3847 
3848 		BMBOUNDFIX(xl, xh, yl, yh);
3849 
3850 	// Polyobjects
3851 		validcount++;
3852 
3853 		for (by = yl; by <= yh; by++)
3854 			for (bx = xl; bx <= xh; bx++)
3855 			{
3856 				INT32 offset;
3857 				polymaplink_t *plink; // haleyjd 02/22/06
3858 
3859 				if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
3860 					continue;
3861 
3862 				offset = by*bmapwidth + bx;
3863 
3864 				// haleyjd 02/22/06: consider polyobject lines
3865 				plink = polyblocklinks[offset];
3866 
3867 				while (plink)
3868 				{
3869 					polyobj_t *po = plink->po;
3870 
3871 					if (po->validcount != validcount) // if polyobj hasn't been checked
3872 					{
3873 						sector_t *polysec;
3874 						fixed_t polytop, polybottom;
3875 
3876 						po->validcount = validcount;
3877 
3878 						if (!(po->flags & POF_SOLID))
3879 						{
3880 							plink = (polymaplink_t *)(plink->link.next);
3881 							continue;
3882 						}
3883 
3884 						if (!P_MobjInsidePolyobj(po, player->mo))
3885 						{
3886 							plink = (polymaplink_t *)(plink->link.next);
3887 							continue;
3888 						}
3889 
3890 						// We're inside it! Yess...
3891 						polysec = po->lines[0]->backsector;
3892 
3893 						if (po->flags & POF_CLIPPLANES)
3894 						{
3895 							polytop = polysec->ceilingheight;
3896 							polybottom = polysec->floorheight;
3897 						}
3898 						else
3899 						{
3900 							polytop = INT32_MAX;
3901 							polybottom = INT32_MIN;
3902 						}
3903 
3904 						if (player->mo->eflags & MFE_VERTICALFLIP)
3905 						{
3906 							if (polybottom > player->mo->ceilingz) // Above the ceiling
3907 							{
3908 								plink = (polymaplink_t *)(plink->link.next);
3909 								continue;
3910 							}
3911 
3912 							if (polybottom > player->mo->z + player->mo->height + tiptop
3913 							|| (polytop < player->mo->z
3914 							&& player->mo->z + player->mo->height < player->mo->ceilingz - tiptop))
3915 								teeter = true;
3916 							else
3917 							{
3918 								teeter = false;
3919 								break;
3920 							}
3921 						}
3922 						else
3923 						{
3924 							if (polytop < player->mo->floorz) // Below the floor
3925 							{
3926 								plink = (polymaplink_t *)(plink->link.next);
3927 								continue;
3928 							}
3929 
3930 							if (polytop < player->mo->z - tiptop
3931 							|| (polybottom > player->mo->z + player->mo->height
3932 							&& player->mo->z > player->mo->floorz + tiptop))
3933 								teeter = true;
3934 							else
3935 							{
3936 								teeter = false;
3937 								break;
3938 							}
3939 						}
3940 					}
3941 					plink = (polymaplink_t *)(plink->link.next);
3942 				}
3943 			}
3944 		if (teeter) // only bother with objects as a last resort if you were already teetering
3945 		{
3946 			mobj_t *oldtmthing = tmthing;
3947 			teeterer = player->mo;
3948 			P_SetTarget(&tmthing, teeterer);
3949 			teeterxl = teeterxh = player->mo->x;
3950 			teeteryl = teeteryh = player->mo->y;
3951 			couldteeter = false;
3952 			solidteeter = teeter;
3953 			for (by = yl; by <= yh; by++)
3954 				for (bx = xl; bx <= xh; bx++)
3955 				{
3956 					highesttop = INT32_MIN;
3957 					if (!P_BlockThingsIterator(bx, by, PIT_CheckSolidsTeeter))
3958 						goto teeterdone; // we've found something that stops us teetering at all, how about we stop already
3959 				}
3960 teeterdone:
3961 			teeter = solidteeter;
3962 			P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers
3963 		}
3964 	}
3965 	if (teeter)
3966 	{
3967 		if (player->panim == PA_IDLE)
3968 			P_SetPlayerMobjState(player->mo, S_PLAY_EDGE);
3969 	}
3970 	else if (player->panim == PA_EDGE)
3971 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
3972 }
3973 
3974 //
3975 // P_SetWeaponDelay
3976 //
3977 // Sets weapon delay.  Properly accounts for Knux's firing rate bonus.
3978 //
P_SetWeaponDelay(player_t * player,INT32 delay)3979 static void P_SetWeaponDelay(player_t *player, INT32 delay)
3980 {
3981 	player->weapondelay = delay;
3982 
3983 	if (player->skin == 2) // Knuckles
3984 	{
3985 		// Multiply before dividing.
3986 		// Loss of precision can make a surprisingly large difference.
3987 		player->weapondelay *= 2;
3988 		player->weapondelay /= 3;
3989 	}
3990 }
3991 
3992 //
3993 // P_DrainWeaponAmmo
3994 //
3995 // Reduces rings and weapon ammo. Also penalizes the player
3996 // for using weapon rings with no normal rings! >:V
3997 //
P_DrainWeaponAmmo(player_t * player,INT32 power)3998 static void P_DrainWeaponAmmo (player_t *player, INT32 power)
3999 {
4000 	player->powers[power]--;
4001 
4002 	if (player->rings < 1)
4003 	{
4004 		player->ammoremovalweapon = player->currentweapon;
4005 		player->ammoremovaltimer  = ammoremovaltics;
4006 
4007 		if (player->powers[power] > 0) // can't take a ring that doesn't exist
4008 		{
4009 			player->powers[power]--;
4010 			player->ammoremoval = 2;
4011 		}
4012 		else
4013 			player->ammoremoval = 1;
4014 	}
4015 	else
4016 		player->rings--;
4017 }
4018 
4019 //
4020 // P_DoFiring()
4021 //
4022 // Handles firing ring weapons and Mario fireballs.
4023 //
P_DoFiring(player_t * player,ticcmd_t * cmd)4024 static void P_DoFiring(player_t *player, ticcmd_t *cmd)
4025 {
4026 	INT32 i;
4027 	mobj_t *mo = NULL;
4028 
4029 	I_Assert(player != NULL);
4030 	I_Assert(!P_MobjWasRemoved(player->mo));
4031 
4032 	if (!(cmd->buttons & (BT_ATTACK|BT_FIRENORMAL)))
4033 	{
4034 		// Not holding any firing buttons anymore.
4035 		// Release the grenade / whatever.
4036 		player->pflags &= ~PF_ATTACKDOWN;
4037 		return;
4038 	}
4039 
4040 	if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
4041 		return;
4042 
4043 	if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->weapondelay))
4044 	{
4045 		player->pflags |= PF_ATTACKDOWN;
4046 		mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
4047 		if (mo)
4048 			P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed);
4049 		S_StartSound(player->mo, sfx_mario7);
4050 		P_SetWeaponDelay(player, TICRATE); // Short delay between fireballs so you can't spam them everywhere
4051 		return;
4052 	}
4053 
4054 	if (!G_RingSlingerGametype() || player->weapondelay)
4055 		return;
4056 
4057 	player->pflags |= PF_ATTACKDOWN;
4058 
4059 	if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
4060 		goto firenormal; //code repetition sucks.
4061 	// Bounce ring
4062 	else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
4063 	{
4064 		P_DrainWeaponAmmo(player, pw_bouncering);
4065 		P_SetWeaponDelay(player, TICRATE/4);
4066 
4067 		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
4068 
4069 		if (mo)
4070 			mo->fuse = 3*TICRATE; // Bounce Ring time
4071 	}
4072 	// Rail ring
4073 	else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
4074 	{
4075 		P_DrainWeaponAmmo(player, pw_railring);
4076 		P_SetWeaponDelay(player, (3*TICRATE)/2);
4077 
4078 		mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
4079 
4080 		// Rail has no unique thrown object, therefore its sound plays here.
4081 		S_StartSound(player->mo, sfx_rail1);
4082 	}
4083 	// Automatic
4084 	else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
4085 	{
4086 		P_DrainWeaponAmmo(player, pw_automaticring);
4087 		player->pflags &= ~PF_ATTACKDOWN;
4088 		P_SetWeaponDelay(player, 2);
4089 
4090 		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
4091 	}
4092 	// Explosion
4093 	else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
4094 	{
4095 		P_DrainWeaponAmmo(player, pw_explosionring);
4096 		P_SetWeaponDelay(player, (3*TICRATE)/2);
4097 
4098 		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
4099 	}
4100 	// Grenade
4101 	else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
4102 	{
4103 		P_DrainWeaponAmmo(player, pw_grenadering);
4104 		P_SetWeaponDelay(player, TICRATE/3);
4105 
4106 		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
4107 
4108 		if (mo)
4109 		{
4110 			//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
4111 			mo->fuse = mo->info->reactiontime;
4112 		}
4113 	}
4114 	// Scatter
4115 	// Note: Ignores MF2_RAILRING
4116 	else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring])
4117 	{
4118 		fixed_t oldz = player->mo->z;
4119 		angle_t shotangle = player->mo->angle;
4120 		angle_t oldaiming = player->aiming;
4121 
4122 		P_DrainWeaponAmmo(player, pw_scatterring);
4123 		P_SetWeaponDelay(player, (2*TICRATE)/3);
4124 
4125 		// Center
4126 		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER);
4127 		if (mo)
4128 			shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y);
4129 
4130 		// Left
4131 		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER);
4132 
4133 		// Right
4134 		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER);
4135 
4136 		// Down
4137 		player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale);
4138 		player->aiming += ANG1;
4139 		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
4140 
4141 		// Up
4142 		player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale);
4143 		player->aiming -= ANG2;
4144 		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
4145 
4146 		player->mo->z = oldz;
4147 		player->aiming = oldaiming;
4148 		return;
4149 	}
4150 	// No powers, just a regular ring.
4151 	else
4152 	{
4153 firenormal:
4154 		// Infinity ring was selected.
4155 		// Mystic wants this ONLY to happen specifically if it's selected,
4156 		// and to not be able to get around it EITHER WAY with firenormal.
4157 
4158 		// Infinity Ring
4159 		if (player->currentweapon == 0
4160 		&& player->powers[pw_infinityring])
4161 		{
4162 			P_SetWeaponDelay(player, TICRATE/4);
4163 
4164 			mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0);
4165 
4166 			player->powers[pw_infinityring]--;
4167 		}
4168 		// Red Ring
4169 		else
4170 		{
4171 			if (player->rings <= 0)
4172 				return;
4173 			P_SetWeaponDelay(player, TICRATE/4);
4174 
4175 			mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0);
4176 
4177 			if (mo)
4178 				P_ColorTeamMissile(mo, player);
4179 
4180 			player->rings--;
4181 		}
4182 	}
4183 
4184 	if (mo)
4185 	{
4186 		if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
4187 		{
4188 			const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP);
4189 			for (i = 0; i < 256; i++)
4190 			{
4191 				if (nblockmap)
4192 				{
4193 					P_UnsetThingPosition(mo);
4194 					mo->flags |= MF_NOBLOCKMAP;
4195 					P_SetThingPosition(mo);
4196 				}
4197 
4198 				if (i&1)
4199 					P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
4200 
4201 				if (P_RailThinker(mo))
4202 					break; // mobj was removed (missile hit a wall) or couldn't move
4203 			}
4204 
4205 			// Other rail sound plays at contact point.
4206 			S_StartSound(mo, sfx_rail2);
4207 		}
4208 	}
4209 }
4210 
4211 //
4212 // P_DoSuperStuff()
4213 //
4214 // Handle related superform functionality.
4215 //
P_DoSuperStuff(player_t * player)4216 static void P_DoSuperStuff(player_t *player)
4217 {
4218 	mobj_t *spark;
4219 	ticcmd_t *cmd = &player->cmd;
4220 	if (player->mo->state >= &states[S_PLAY_SUPER_TRANS1]
4221 	&& player->mo->state < &states[S_PLAY_SUPER_TRANS6])
4222 		return; // don't do anything right now, we're in the middle of transforming!
4223 
4224 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
4225 		return; // NiGHTS Super doesn't mix with normal super
4226 
4227 	if (player->powers[pw_super])
4228 	{
4229 		// If you're super and not Sonic, de-superize!
4230 		if (!(ALL7EMERALDS(emeralds) && player->charflags & SF_SUPER))
4231 		{
4232 			player->powers[pw_super] = 0;
4233 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
4234 			if (P_IsLocalPlayer(player))
4235 			{
4236 				music_stack_noposition = true; // HACK: Do not reposition next music
4237 				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
4238 			}
4239 			P_RestoreMusic(player);
4240 			P_SpawnShieldOrb(player);
4241 
4242 			// Restore color
4243 			if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
4244 			{
4245 				player->mo->color = SKINCOLOR_WHITE;
4246 				G_GhostAddColor(GHC_FIREFLOWER);
4247 			}
4248 			else
4249 			{
4250 				player->mo->color = player->skincolor;
4251 				G_GhostAddColor(GHC_NORMAL);
4252 			}
4253 
4254 			if (!G_CoopGametype())
4255 			{
4256 				HU_SetCEchoFlags(0);
4257 				HU_SetCEchoDuration(5);
4258 				HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
4259 			}
4260 			return;
4261 		}
4262 
4263 		player->mo->color = (player->pflags & PF_GODMODE && cv_debug == 0)
4264 		? (SKINCOLOR_SUPERSILVER1 + 5*(((signed)leveltime >> 1) % 7)) // A wholesome easter egg.
4265 		: skins[player->skin].supercolor + abs( ( (player->powers[pw_super] >> 1) % 9) - 4); // This is where super flashing is handled.
4266 
4267 		G_GhostAddColor(GHC_SUPER);
4268 
4269 		if (player->mo->state == &states[S_PLAY_SUPER_TRANS6]) // stop here for now
4270 			return;
4271 
4272 		// Deplete one ring every second while super
4273 		if ((leveltime % TICRATE == 0) && !(player->exiting))
4274 			player->rings--;
4275 
4276 		if ((cmd->forwardmove != 0 || cmd->sidemove != 0 || player->powers[pw_carry])
4277 		&& !(leveltime % TICRATE) && (player->mo->momx || player->mo->momy))
4278 		{
4279 			spark = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SUPERSPARK);
4280 			spark->destscale = player->mo->scale;
4281 			P_SetScale(spark, player->mo->scale);
4282 		}
4283 
4284 		// Ran out of rings while super!
4285 		if (player->rings <= 0 || player->exiting)
4286 		{
4287 			player->powers[pw_emeralds] = 0; // lost the power stones
4288 			P_SpawnGhostMobj(player->mo);
4289 
4290 			player->powers[pw_super] = 0;
4291 
4292 			// Restore color
4293 			if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
4294 			{
4295 				player->mo->color = SKINCOLOR_WHITE;
4296 				G_GhostAddColor(GHC_FIREFLOWER);
4297 			}
4298 			else
4299 			{
4300 				player->mo->color = player->skincolor;
4301 				G_GhostAddColor(GHC_NORMAL);
4302 			}
4303 
4304 			if (!G_CoopGametype())
4305 				player->powers[pw_flashing] = flashingtics-1;
4306 
4307 			if (player->mo->sprite2 & FF_SPR2SUPER)
4308 				P_SetPlayerMobjState(player->mo, player->mo->state-states);
4309 
4310 			// Inform the netgame that the champion has fallen in the heat of battle.
4311 			if (!G_CoopGametype())
4312 			{
4313 				S_StartSound(NULL, sfx_s3k66); //let all players hear it.
4314 				HU_SetCEchoFlags(0);
4315 				HU_SetCEchoDuration(5);
4316 				HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
4317 			}
4318 
4319 			// Resume normal music if you're the console player
4320 			if (P_IsLocalPlayer(player))
4321 			{
4322 				music_stack_noposition = true; // HACK: Do not reposition next music
4323 				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
4324 			}
4325 			P_RestoreMusic(player);
4326 
4327 			// If you had a shield, restore its visual significance.
4328 			P_SpawnShieldOrb(player);
4329 		}
4330 	}
4331 }
4332 
4333 //
4334 // P_SuperReady
4335 //
4336 // Returns true if player is ready to turn super, duh
4337 //
P_SuperReady(player_t * player)4338 boolean P_SuperReady(player_t *player)
4339 {
4340 	if (!player->powers[pw_super]
4341 	&& !player->powers[pw_invulnerability]
4342 	&& !player->powers[pw_tailsfly]
4343 	&& (player->charflags & SF_SUPER)
4344 	&& (player->pflags & PF_JUMPED)
4345 	&& !(player->powers[pw_shield] & SH_NOSTACK)
4346 	&& !(maptol & TOL_NIGHTS)
4347 	&& ALL7EMERALDS(emeralds)
4348 	&& (player->rings >= 50))
4349 		return true;
4350 
4351 	return false;
4352 }
4353 
4354 //
4355 // P_DoJump
4356 //
4357 // Jump routine for the player
4358 //
P_DoJump(player_t * player,boolean soundandstate)4359 void P_DoJump(player_t *player, boolean soundandstate)
4360 {
4361 	fixed_t factor;
4362 	const fixed_t dist6 = FixedMul(FixedDiv(player->speed, player->mo->scale), player->actionspd)/20;
4363 
4364 	if (player->pflags & PF_JUMPSTASIS)
4365 		return;
4366 
4367 	if (!player->jumpfactor)
4368 		return;
4369 
4370 	if (player->climbing)
4371 	{
4372 		// Jump this high.
4373 		if (player->powers[pw_super])
4374 			player->mo->momz = 5*FRACUNIT;
4375 		else if (player->mo->eflags & MFE_UNDERWATER)
4376 			player->mo->momz = 2*FRACUNIT;
4377 		else
4378 			player->mo->momz = 15*(FRACUNIT/4);
4379 
4380 		player->drawangle = player->mo->angle = player->mo->angle - ANGLE_180; // Turn around from the wall you were climbing.
4381 
4382 		if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
4383 			P_SetPlayerAngle(player, player->mo->angle);
4384 
4385 		player->climbing = 0; // Stop climbing, duh!
4386 		P_InstaThrust(player->mo, player->mo->angle, FixedMul(6*FRACUNIT, player->mo->scale)); // Jump off the wall.
4387 	}
4388 	// Quicksand jumping.
4389 	else if (P_InQuicksand(player->mo))
4390 	{
4391 		if (player->mo->ceilingz-player->mo->floorz <= player->mo->height-1)
4392 			return;
4393 		player->mo->momz += (39*(FRACUNIT/4))>>1;
4394 		if (player->mo->momz >= 6*FRACUNIT)
4395 			player->mo->momz = 6*FRACUNIT; //max momz in quicksand
4396 		else if (player->mo->momz < 0) // still descending?
4397 			player->mo->momz = (39*(FRACUNIT/4))>>1; // just default to the jump height.
4398 	}
4399 	else if (!(player->pflags & PF_JUMPED)) // Jump
4400 	{
4401 		if (player->mo->ceilingz-player->mo->floorz <= player->mo->height-1)
4402 			return;
4403 
4404 		if (player->powers[pw_carry] == CR_PTERABYTE)
4405 		{
4406 			S_StartSound(player->mo, sfx_s3kd7s);
4407 			player->mo->tracer->cusval += 10;
4408 			player->mo->tracer->watertop = P_RandomRange(-player->mo->tracer->cusval, player->mo->tracer->cusval) << (FRACBITS - 1);
4409 			player->mo->tracer->waterbottom = P_RandomRange(-player->mo->tracer->cusval, player->mo->tracer->cusval) << (FRACBITS - 1);
4410 			player->mo->tracer->cvmem = P_RandomRange(-player->mo->tracer->cusval, player->mo->tracer->cusval) << (FRACBITS - 1);
4411 			return;
4412 		}
4413 
4414 		// Jump this high.
4415 		if (player->powers[pw_carry] == CR_PLAYER)
4416 		{
4417 			player->mo->momz = 9*FRACUNIT;
4418 			player->powers[pw_carry] = CR_NONE;
4419 			P_SetTarget(&player->mo->tracer, NULL);
4420 			if (player-players == consoleplayer && botingame)
4421 				CV_SetValue(&cv_analog[1], true);
4422 		}
4423 		else if (player->powers[pw_carry] == CR_GENERIC)
4424 		{
4425 			player->mo->momz = 9*FRACUNIT;
4426 			player->powers[pw_carry] = CR_NONE;
4427 			if (!(player->mo->tracer->flags & MF_MISSILE)) // Missiles remember their owner!
4428 				P_SetTarget(&player->mo->tracer->target, NULL);
4429 			P_SetTarget(&player->mo->tracer, NULL);
4430 		}
4431 		else if (player->powers[pw_carry] == CR_ROPEHANG)
4432 		{
4433 			player->mo->momz = 12*FRACUNIT;
4434 			player->powers[pw_carry] = CR_NONE;
4435 			P_SetTarget(&player->mo->tracer, NULL);
4436 		}
4437 		else if (player->powers[pw_carry] == CR_ROLLOUT)
4438 		{
4439 			player->mo->momz = 9*FRACUNIT;
4440 			if (player->mo->tracer)
4441 			{
4442 				if (P_MobjFlip(player->mo->tracer)*player->mo->tracer->momz > 0)
4443 					player->mo->momz += player->mo->tracer->momz;
4444 				if (!P_IsObjectOnGround(player->mo->tracer))
4445 					P_SetObjectMomZ(player->mo->tracer, -9*FRACUNIT, true);
4446 				player->mo->tracer->flags |= MF_PUSHABLE;
4447 				P_SetTarget(&player->mo->tracer->tracer, NULL);
4448 			}
4449 			player->powers[pw_carry] = CR_NONE;
4450 			P_SetTarget(&player->mo->tracer, NULL);
4451 		}
4452 		else if (player->mo->eflags & MFE_GOOWATER)
4453 		{
4454 			player->mo->momz = 7*FRACUNIT;
4455 			if (player->charability == CA_JUMPBOOST && onground)
4456 			{
4457 				if (player->charflags & SF_MULTIABILITY)
4458 					player->mo->momz += FixedMul(FRACUNIT/4, dist6);
4459 				else
4460 					player->mo->momz += FixedMul(FRACUNIT/8, dist6);
4461 			}
4462 		}
4463 		else if (maptol & TOL_NIGHTS)
4464 			player->mo->momz = 18*FRACUNIT;
4465 		else if (player->powers[pw_super] && !(player->charflags & SF_NOSUPERJUMPBOOST))
4466 		{
4467 			player->mo->momz = 13*FRACUNIT;
4468 
4469 			// Add a boost for super characters with float/slowfall and multiability.
4470 			if (player->charability == CA_JUMPBOOST)
4471 			{
4472 				if (player->charflags & SF_MULTIABILITY)
4473 					player->mo->momz += FixedMul(FRACUNIT/4, dist6);
4474 				else
4475 					player->mo->momz += FixedMul(FRACUNIT/8, dist6);
4476 			}
4477 		}
4478 		else
4479 		{
4480 			player->mo->momz = 39*(FRACUNIT/4); // Default jump momentum.
4481 			if (player->charability == CA_JUMPBOOST && onground)
4482 			{
4483 				if (player->charflags & SF_MULTIABILITY)
4484 					player->mo->momz += FixedMul(FRACUNIT/4, dist6);
4485 				else
4486 					player->mo->momz += FixedMul(FRACUNIT/8, dist6);
4487 			}
4488 		}
4489 
4490 		// Reduce player momz by 58.5% when underwater.
4491 		if (player->mo->eflags & MFE_UNDERWATER)
4492 			player->mo->momz = FixedMul(player->mo->momz, FixedDiv(117*FRACUNIT, 200*FRACUNIT));
4493 
4494 		player->pflags |= PF_STARTJUMP;
4495 	}
4496 
4497 	factor = player->jumpfactor;
4498 
4499 	if (twodlevel || (player->mo->flags2 & MF2_TWOD))
4500 		factor += player->jumpfactor / 10;
4501 
4502 	if (player->charflags & SF_MULTIABILITY && player->charability == CA_DOUBLEJUMP && (player->actionspd >> FRACBITS) != -1)
4503 		factor -= max(0, player->secondjump * player->jumpfactor / ((player->actionspd >> FRACBITS) + 1)); // Reduce the jump height each time
4504 
4505 	//if (maptol & TOL_NIGHTS)
4506 	//	factor = player->jumpfactor; // all skins jump the same. if you nerf jumping abilities, you may want this.
4507 
4508 	P_SetObjectMomZ(player->mo, FixedMul(factor, player->mo->momz), false); // Custom height
4509 
4510 	// set just an eensy above the ground
4511 	if (player->mo->eflags & MFE_VERTICALFLIP)
4512 	{
4513 		player->mo->z--;
4514 		if (player->mo->pmomz < 0)
4515 			player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump.
4516 		player->mo->pmomz = 0;
4517 	}
4518 	else
4519 	{
4520 		player->mo->z++;
4521 		if (player->mo->pmomz > 0)
4522 			player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump.
4523 		player->mo->pmomz = 0;
4524 	}
4525 	player->mo->eflags &= ~MFE_APPLYPMOMZ;
4526 
4527 	player->pflags |= P_GetJumpFlags(player);;
4528 
4529 	if (player->charflags & SF_NOJUMPDAMAGE)
4530 		player->pflags &= ~PF_SPINNING;
4531 
4532 	if (soundandstate)
4533 	{
4534 		if (!player->spectator)
4535 			S_StartSound(player->mo, sfx_jump); // Play jump sound!
4536 
4537 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
4538 	}
4539 }
4540 
P_DoSpinDashDust(player_t * player)4541 static void P_DoSpinDashDust(player_t *player)
4542 {
4543 	UINT32 i;
4544 	mobj_t *particle;
4545 	INT32 prandom[3];
4546 	for (i = 0; i <= (leveltime%7)/2; i++) { // 1, 2, 3 or 4 particles
4547 		particle = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SPINDUST);
4548 
4549 		if (player->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version
4550 			P_SetMobjState(particle, S_SPINDUST_BUBBLE1);
4551 		else if (player->powers[pw_shield] == SH_ELEMENTAL)
4552 			P_SetMobjState(particle, S_SPINDUST_FIRE1);
4553 
4554 		P_SetTarget(&particle->target, player->mo);
4555 		particle->destscale = (2*player->mo->scale)/3;
4556 		P_SetScale(particle, particle->destscale);
4557 		if (player->mo->eflags & MFE_VERTICALFLIP) // readjust z position if needed
4558 			particle->z = player->mo->z + player->mo->height - particle->height;
4559 		prandom[0] = P_RandomFixed()<<2; // P_RandomByte()<<10
4560 		prandom[1] = P_RandomRange(-30, 30); // P_RandomRange(-ANG30/FRACUNIT, ANG30/FRACUNIT)*FRACUNIT
4561 		prandom[2] = P_RandomFixed()<<3; // P_RandomByte()<<11
4562 		P_SetObjectMomZ(particle, player->dashspeed/50 + prandom[0], false);
4563 		P_InstaThrust(particle,
4564 				player->drawangle + (prandom[1]*ANG1),
4565 				-FixedMul(player->dashspeed/12 + FRACUNIT + prandom[2], player->mo->scale));
4566 		P_TryMove(particle, particle->x+particle->momx, particle->y+particle->momy, true);
4567 	}
4568 }
4569 
4570 //
4571 // P_DoSpinAbility
4572 //
4573 // Player spindash handling
4574 //
P_DoSpinAbility(player_t * player,ticcmd_t * cmd)4575 static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
4576 {
4577 	boolean canstand = true; // can we stand on the ground? (mostly relevant for slopes)
4578 	if (player->pflags & PF_STASIS
4579 		&& (player->pflags & PF_JUMPSTASIS || player->mo->state-states != S_PLAY_GLIDE_LANDING))
4580 		return;
4581 
4582 	if (cmd->buttons & BT_SPIN)
4583 	{
4584 		if (LUAh_SpinSpecial(player))
4585 			return;
4586 	}
4587 
4588 	canstand = (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2);
4589 
4590 	///////////////////////////////
4591 	// ability-specific behavior //
4592 	///////////////////////////////
4593 	if (!(player->pflags & PF_SLIDING) && !player->exiting && !P_PlayerInPain(player))
4594 	{
4595 		switch (player->charability2)
4596 		{
4597 			case CA2_SPINDASH: // Spinning and Spindashing
4598 				 // Start revving
4599 				if ((cmd->buttons & BT_SPIN) && (player->speed < FixedMul(5<<FRACBITS, player->mo->scale) || player->mo->state - states == S_PLAY_GLIDE_LANDING)
4600 					&& !player->mo->momz && onground && !(player->pflags & (PF_SPINDOWN|PF_SPINNING))
4601 						&& canstand)
4602 				{
4603 					player->mo->momx = player->cmomx;
4604 					player->mo->momy = player->cmomy;
4605 					player->pflags |= (PF_SPINDOWN|PF_STARTDASH|PF_SPINNING);
4606 					player->dashspeed = player->mindash;
4607 					P_SetPlayerMobjState(player->mo, S_PLAY_SPINDASH);
4608 					if (!player->spectator)
4609 						S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
4610 				}
4611 				 // Revving
4612 				else if ((cmd->buttons & BT_SPIN) && (player->pflags & PF_STARTDASH))
4613 				{
4614 					if (player->speed > 5*player->mo->scale)
4615 					{
4616 						player->pflags &= ~PF_STARTDASH;
4617 						P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
4618 						S_StartSound(player->mo, sfx_spin);
4619 						break;
4620 					}
4621 					if (player->dashspeed < player->mindash)
4622 						player->dashspeed = player->mindash;
4623 
4624 					if (player->dashspeed > player->maxdash)
4625 						player->dashspeed = player->maxdash;
4626 
4627 					if (player->dashspeed < player->maxdash && player->mindash != player->maxdash)
4628 					{
4629 #define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
4630 						fixed_t soundcalculation = chargecalculation;
4631 						player->dashspeed += FRACUNIT;
4632 						if (!player->spectator && soundcalculation != chargecalculation)
4633 							S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
4634 #undef chargecalculation
4635 					}
4636 					if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
4637 					{
4638 						P_SpawnSpinMobj(player, player->revitem);
4639 						G_GhostAddRev();
4640 					}
4641 				}
4642 				// If not moving up or down, and travelling faster than a speed of five while not holding
4643 				// down the spin button and not spinning.
4644 				// AKA Just go into a spin on the ground, you idiot. ;)
4645 				else if ((cmd->buttons & BT_SPIN || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
4646 					&& !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
4647 						|| !canstand) && !(player->pflags & (PF_SPINDOWN|PF_SPINNING)))
4648 				{
4649 					player->pflags |= (PF_SPINDOWN|PF_SPINNING);
4650 					P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
4651 					if (!player->spectator)
4652 						S_StartSound(player->mo, sfx_spin);
4653 				}
4654 				else
4655 				// Catapult the player from a spindash rev!
4656 				if (onground && !(player->pflags & PF_SPINDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING))
4657 				{
4658 					player->pflags &= ~PF_STARTDASH;
4659 					if (player->powers[pw_carry] == CR_BRAKGOOP)
4660 						player->dashspeed = 0;
4661 
4662 					if (!((gametyperules & GTR_RACE) && leveltime < 4*TICRATE))
4663 					{
4664 						if (player->dashspeed)
4665 						{
4666 							P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
4667 							P_InstaThrust(player->mo, player->mo->angle, (player->speed = FixedMul(player->dashspeed, player->mo->scale))); // catapult forward ho!!
4668 						}
4669 						else
4670 						{
4671 							P_SetPlayerMobjState(player->mo, S_PLAY_STND);
4672 							player->pflags &= ~PF_SPINNING;
4673 						}
4674 
4675 						if (!player->spectator)
4676 							S_StartSound(player->mo, sfx_zoom);
4677 					}
4678 
4679 					player->dashspeed = 0;
4680 				}
4681 				break;
4682 			case CA2_GUNSLINGER:
4683 				if (!player->mo->momz && onground && !player->weapondelay && canstand)
4684 				{
4685 					if (player->speed > FixedMul(10<<FRACBITS, player->mo->scale))
4686 					{}
4687 					else
4688 					{
4689 						mobj_t *lockon = P_LookForEnemies(player, false, true);
4690 						if (lockon)
4691 						{
4692 							if (P_IsLocalPlayer(player)) // Only display it on your own view.
4693 							{
4694 								mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
4695 								P_SetTarget(&visual->target, lockon);
4696 							}
4697 						}
4698 						if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_SPINDOWN))
4699 						{
4700 							mobj_t *bullet;
4701 
4702 							P_SetPlayerMobjState(player->mo, S_PLAY_FIRE);
4703 
4704 #define zpos(posmo) (posmo->z + (posmo->height - mobjinfo[player->revitem].height)/2)
4705 							if (lockon)
4706 							{
4707 								player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
4708 								bullet = P_SpawnPointMissile(player->mo, lockon->x, lockon->y, zpos(lockon), player->revitem, player->mo->x, player->mo->y, zpos(player->mo));
4709 								if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
4710 									P_SetPlayerAngle(player, player->mo->angle);
4711 							}
4712 							else
4713 							{
4714 								bullet = P_SpawnPointMissile(player->mo, player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, FRACUNIT), player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, FRACUNIT), zpos(player->mo), player->revitem, player->mo->x, player->mo->y, zpos(player->mo));
4715 								if (bullet)
4716 								{
4717 									bullet->flags &= ~MF_NOGRAVITY;
4718 									bullet->momx >>= 1;
4719 									bullet->momy >>= 1;
4720 								}
4721 							}
4722 							player->drawangle = player->mo->angle;
4723 #undef zpos
4724 
4725 							player->mo->momx >>= 1;
4726 							player->mo->momy >>= 1;
4727 							player->pflags |= PF_SPINDOWN;
4728 							P_SetWeaponDelay(player, TICRATE/2);
4729 						}
4730 					}
4731 				}
4732 				break;
4733 			case CA2_MELEE: // Melee attack
4734 				if (player->panim != PA_ABILITY2 && (cmd->buttons & BT_SPIN)
4735 				&& !player->mo->momz && onground && !(player->pflags & PF_SPINDOWN)
4736 				&& canstand)
4737 				{
4738 					P_ResetPlayer(player);
4739 					player->pflags |= PF_THOKKED;
4740 #if 0
4741 					if ((player->charability == CA_TWINSPIN) && (player->speed > FixedMul(player->runspeed, player->mo->scale)))
4742 					{
4743 						P_DoJump(player, false);
4744 						player->pflags &= ~PF_STARTJUMP;
4745 						player->mo->momz = FixedMul(player->mo->momz, 3*FRACUNIT/2); // NOT 1.5 times the jump height, but 2.25 times.
4746 						P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN);
4747 						S_StartSound(player->mo, sfx_s3k8b);
4748 					}
4749 					else
4750 #endif
4751 					{
4752 						player->mo->z += P_MobjFlip(player->mo);
4753 						P_SetObjectMomZ(player->mo, player->mindash, false);
4754 						if (P_MobjFlip(player->mo)*player->mo->pmomz > 0)
4755 							player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump.
4756 						else
4757 							player->mo->pmomz = 0;
4758 						if (player->mo->eflags & MFE_UNDERWATER)
4759 							player->mo->momz >>= 1;
4760 #if 0
4761 						if (FixedMul(player->speed, FINECOSINE(((player->mo->angle - R_PointToAngle2(0, 0, player->rmomx, player->rmomy)) >> ANGLETOFINESHIFT) & FINEMASK)) < FixedMul(player->maxdash, player->mo->scale))
4762 #else
4763 						if (player->speed < FixedMul(player->maxdash, player->mo->scale))
4764 #endif
4765 						{
4766 							if (player->panim == PA_IDLE)
4767 								player->drawangle = player->mo->angle;
4768 							P_InstaThrust(player->mo, player->drawangle, FixedMul(player->maxdash, player->mo->scale));
4769 						}
4770 						player->mo->momx += player->cmomx;
4771 						player->mo->momy += player->cmomy;
4772 						P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
4773 						S_StartSound(player->mo, sfx_s3k42);
4774 					}
4775 					player->pflags |= PF_SPINDOWN;
4776 				}
4777 				break;
4778 		}
4779 	}
4780 
4781 	///////////////////////////////
4782 	// general spinning behavior //
4783 	///////////////////////////////
4784 
4785 	// Rolling normally
4786 	if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
4787 		&& player->speed < 5*player->mo->scale && canstand)
4788 	{
4789 		if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
4790 			P_InstaThrust(player->mo, player->mo->angle, 10*player->mo->scale);
4791 		else
4792 		{
4793 			player->skidtime = 0;
4794 			player->pflags &= ~PF_SPINNING;
4795 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
4796 			player->mo->momx = player->cmomx;
4797 			player->mo->momy = player->cmomy;
4798 		}
4799 	}
4800 
4801 	if (onground && player->pflags & PF_STARTDASH)
4802 	{
4803 		// Spawn spin dash dust
4804 		if (!(player->charflags & SF_NOSPINDASHDUST) && !(player->mo->eflags & MFE_GOOWATER))
4805 			P_DoSpinDashDust(player);
4806 	}
4807 }
4808 
4809 //
4810 // P_DoJumpShield
4811 //
4812 // Jump Shield Activation
4813 //
P_DoJumpShield(player_t * player)4814 void P_DoJumpShield(player_t *player)
4815 {
4816 	boolean electric = ((player->powers[pw_shield] & SH_PROTECTELECTRIC) == SH_PROTECTELECTRIC);
4817 
4818 	if (player->pflags & PF_THOKKED)
4819 		return;
4820 
4821 	player->pflags &= ~PF_JUMPED;
4822 	P_DoJump(player, false);
4823 	player->secondjump = 0;
4824 	player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
4825 	player->pflags &= ~(PF_STARTJUMP|PF_SPINNING|PF_BOUNCING);
4826 	if (electric)
4827 	{
4828 		mobj_t *spark;
4829 		INT32 i;
4830 #define numangles 6
4831 #define limitangle (360/numangles)
4832 		const angle_t travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
4833 		for (i = 0; i < numangles; i++)
4834 		{
4835 			spark = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_THUNDERCOIN_SPARK);
4836 			P_InstaThrust(spark, travelangle + i*(ANGLE_MAX/numangles), FixedMul(4*FRACUNIT, spark->scale));
4837 			if (i % 2)
4838 				P_SetObjectMomZ(spark, -4*FRACUNIT, false);
4839 			spark->fuse = 18;
4840 		}
4841 #undef limitangle
4842 #undef numangles
4843 		player->pflags &= ~PF_NOJUMPDAMAGE;
4844 		P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
4845 		S_StartSound(player->mo, sfx_s3k45);
4846 	}
4847 	else
4848 	{
4849 		player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
4850 		P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
4851 		S_StartSound(player->mo, sfx_wdjump);
4852 	}
4853 }
4854 
4855 //
4856 // P_DoBubbleBounce
4857 //
4858 // Bubblewrap shield landing handling
4859 //
P_DoBubbleBounce(player_t * player)4860 void P_DoBubbleBounce(player_t *player)
4861 {
4862 	player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SHIELDABILITY);
4863 	S_StartSound(player->mo, sfx_s3k44);
4864 	P_MobjCheckWater(player->mo);
4865 	P_DoJump(player, false);
4866 	if (player->charflags & SF_NOJUMPSPIN)
4867 		P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
4868 	else
4869 		P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
4870 	player->pflags |= PF_THOKKED;
4871 	player->pflags &= ~PF_STARTJUMP;
4872 	player->secondjump = UINT8_MAX;
4873 	player->mo->momz = FixedMul(player->mo->momz, 11*FRACUNIT/8);
4874 }
4875 
4876 //
4877 // P_DoAbilityBounce
4878 //
4879 // CA_BOUNCE landing handling
4880 //
P_DoAbilityBounce(player_t * player,boolean changemomz)4881 void P_DoAbilityBounce(player_t *player, boolean changemomz)
4882 {
4883 	if (player->mo->state-states == S_PLAY_BOUNCE_LANDING)
4884 		return;
4885 
4886 	if (changemomz)
4887 	{
4888 		fixed_t prevmomz = player->mo->momz, minmomz;
4889 
4890 		if (P_MobjFlip(player->mo)*prevmomz < 0)
4891 			prevmomz = 0;
4892 		else if (player->mo->eflags & MFE_UNDERWATER)
4893 			prevmomz /= 2;
4894 
4895 		P_DoJump(player, false);
4896 		player->pflags &= ~(PF_STARTJUMP|PF_JUMPED);
4897 		minmomz = FixedMul(player->mo->momz, 3*FRACUNIT/2);
4898 
4899 		if (player->mo->eflags & MFE_VERTICALFLIP) // Use "min" or "max" depending on if the player is flipped
4900 			player->mo->momz = min(minmomz, (minmomz + prevmomz)/2);
4901 		else
4902 			player->mo->momz = max(minmomz, (minmomz + prevmomz)/2);
4903 	}
4904 
4905 	S_StartSound(player->mo, sfx_boingf);
4906 	P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE_LANDING);
4907 	player->pflags |= PF_BOUNCING|PF_THOKKED;
4908 }
4909 
4910 //
4911 // P_TwinSpinRejuvenate
4912 //
4913 // CA_TWINSPIN landing handling
4914 //
P_TwinSpinRejuvenate(player_t * player,mobjtype_t type)4915 void P_TwinSpinRejuvenate(player_t *player, mobjtype_t type)
4916 {
4917 	fixed_t actionspd;
4918 	angle_t movang, ang, fa;
4919 	fixed_t v, h;
4920 	UINT8 i;
4921 
4922 	if (!player->mo || !type)
4923 		return;
4924 
4925 	actionspd = FixedMul(player->actionspd, player->mo->scale);
4926 
4927 	fa = (R_PointToAngle2(0, 0, player->mo->momz, FixedHypot(player->mo->momx, player->mo->momy))>>ANGLETOFINESHIFT) & FINEMASK;
4928 	movang = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
4929 	ang = 0;
4930 
4931 	v = FixedMul(actionspd, FINESINE(fa));
4932 	h = actionspd - FixedMul(actionspd, FINECOSINE(fa));
4933 
4934 	// hearticles
4935 	for (i = 0; i <= 7; i++)
4936 	{
4937 		fixed_t side = actionspd - FixedMul(h, abs(FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK)));
4938 		fixed_t xo = P_ReturnThrustX(NULL, ang + movang, side);
4939 		fixed_t yo = P_ReturnThrustY(NULL, ang + movang, side);
4940 		fixed_t zo = -FixedMul(FINECOSINE(((ang>>ANGLETOFINESHIFT) & FINEMASK)), v);
4941 		mobj_t *missile = P_SpawnMobjFromMobj(player->mo,
4942 			xo,
4943 			yo,
4944 			player->mo->height/2 + zo,
4945 			type);
4946 		P_SetTarget(&missile->target, player->mo);
4947 		P_SetScale(missile, (missile->destscale >>= 1));
4948 		missile->angle = ang + movang;
4949 		missile->fuse = TICRATE/2;
4950 		missile->extravalue2 = (99*FRACUNIT)/100;
4951 		missile->momx = xo;
4952 		missile->momy = yo;
4953 		missile->momz = zo;
4954 
4955 		ang += ANGLE_45;
4956 	}
4957 
4958 	player->pflags &= ~PF_THOKKED;
4959 }
4960 
4961 //
4962 // P_Telekinesis
4963 //
4964 // Morph's fancy stuff-moving character ability
4965 // +ve thrust pushes away, -ve thrust pulls in
4966 //
P_Telekinesis(player_t * player,fixed_t thrust,fixed_t range)4967 void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
4968 {
4969 	thinker_t *th;
4970 	mobj_t *mo2;
4971 	fixed_t dist = 0;
4972 	angle_t an;
4973 
4974 	if (player->powers[pw_super]) // increase range when super
4975 		range *= 2;
4976 
4977 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
4978 	{
4979 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
4980 			continue;
4981 
4982 		mo2 = (mobj_t *)th;
4983 
4984 		if (mo2 == player->mo)
4985 			continue;
4986 
4987 		if (!((mo2->flags & MF_SHOOTABLE && mo2->flags & MF_ENEMY) || mo2->type == MT_EGGGUARD || mo2->player))
4988 			continue;
4989 
4990 		dist = P_AproxDistance(P_AproxDistance(player->mo->x-mo2->x, player->mo->y-mo2->y), player->mo->z-mo2->z);
4991 
4992 		if (range < dist)
4993 			continue;
4994 
4995 		if (!P_CheckSight(player->mo, mo2))
4996 			continue; // if your psychic powers can't "see" it don't bother
4997 
4998 		an = R_PointToAngle2(player->mo->x, player->mo->y, mo2->x, mo2->y);
4999 
5000 		if (mo2->health > 0)
5001 		{
5002 			P_Thrust(mo2, an, thrust);
5003 
5004 			if (mo2->type == MT_GOLDBUZZ || mo2->type == MT_REDBUZZ)
5005 				mo2->tics += 8;
5006 		}
5007 	}
5008 
5009 	P_SpawnThokMobj(player);
5010 	player->pflags |= PF_THOKKED;
5011 }
5012 
P_DoTwinSpin(player_t * player)5013 static void P_DoTwinSpin(player_t *player)
5014 {
5015 	player->pflags &= ~PF_NOJUMPDAMAGE;
5016 	player->pflags |= P_GetJumpFlags(player) | PF_THOKKED;
5017 	S_StartSound(player->mo, sfx_s3k42);
5018 	player->mo->frame = 0;
5019 	P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN);
5020 }
5021 
5022 //
5023 // returns true if the player used a shield ability, false otherwise
5024 // passing in the mobjs from P_DoJumpStuff is a bit hackily specific, but I don't care enough to make a more elaborate solution (I think that is more appropriately approached with a more general MT_LOCKON spawning system)
5025 //
P_PlayerShieldThink(player_t * player,ticcmd_t * cmd,mobj_t * lockonthok,mobj_t * visual)5026 static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lockonthok, mobj_t *visual)
5027 {
5028 	mobj_t *lockonshield = NULL;
5029 
5030 	if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN)
5031 		&& ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted
5032 	{
5033 		if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY))
5034 		{
5035 			if ((lockonshield = P_LookForEnemies(player, false, false)))
5036 			{
5037 				if (P_IsLocalPlayer(player)) // Only display it on your own view.
5038 				{
5039 					boolean dovis = true;
5040 					if (lockonshield == lockonthok)
5041 					{
5042 						if (leveltime & 2)
5043 							dovis = false;
5044 						else if (visual)
5045 							P_RemoveMobj(visual);
5046 					}
5047 					if (dovis)
5048 					{
5049 						visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
5050 						P_SetTarget(&visual->target, lockonshield);
5051 						P_SetMobjStateNF(visual, visual->info->spawnstate+1);
5052 					}
5053 				}
5054 			}
5055 		}
5056 		if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player))) // Spin button effects
5057 		{
5058 			// Force stop
5059 			if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
5060 			{
5061 				player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
5062 				player->mo->momx = player->mo->momy = player->mo->momz = 0;
5063 				S_StartSound(player->mo, sfx_ngskid);
5064 			}
5065 			else
5066 			{
5067 				switch (player->powers[pw_shield] & SH_NOSTACK)
5068 				{
5069 					// Whirlwind jump/Thunder jump
5070 					case SH_WHIRLWIND:
5071 					case SH_THUNDERCOIN:
5072 						P_DoJumpShield(player);
5073 						break;
5074 					// Armageddon pow
5075 					case SH_ARMAGEDDON:
5076 						player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
5077 						P_BlackOw(player);
5078 						break;
5079 					// Attraction blast
5080 					case SH_ATTRACT:
5081 						player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
5082 						player->homing = 2;
5083 						P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonshield));
5084 						if (lockonshield)
5085 							{
5086 								player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonshield->x, lockonshield->y);
5087 									player->pflags &= ~PF_NOJUMPDAMAGE;
5088 									P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
5089 									S_StartSound(player->mo, sfx_s3k40);
5090 									player->homing = 3*TICRATE;
5091 							}
5092 							else
5093 								S_StartSound(player->mo, sfx_s3ka6);
5094 							break;
5095 						// Elemental stomp/Bubble bounce
5096 						case SH_ELEMENTAL:
5097 						case SH_BUBBLEWRAP:
5098 							{
5099 								boolean elem = ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL);
5100 								player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
5101 								if (elem)
5102 								{
5103 									player->mo->momx = player->mo->momy = 0;
5104 									S_StartSound(player->mo, sfx_s3k43);
5105 								}
5106 								else
5107 								{
5108 									player->mo->momx -= (player->mo->momx/3);
5109 									player->mo->momy -= (player->mo->momy/3);
5110 									player->pflags &= ~PF_NOJUMPDAMAGE;
5111 									P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
5112 									S_StartSound(player->mo, sfx_s3k44);
5113 								}
5114 								player->secondjump = 0;
5115 								P_SetObjectMomZ(player->mo, -24*FRACUNIT, false);
5116 								break;
5117 							}
5118 						// Flame burst
5119 						case SH_FLAMEAURA:
5120 							player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
5121 							P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale));
5122 							player->drawangle = player->mo->angle;
5123 							player->pflags &= ~PF_NOJUMPDAMAGE;
5124 							P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
5125 							S_StartSound(player->mo, sfx_s3k43);
5126 						default:
5127 							break;
5128 				}
5129 			}
5130 		}
5131 		return player->pflags & PF_SHIELDABILITY;
5132 	}
5133 	return false;
5134 }
5135 
5136 //
5137 // P_DoJumpStuff
5138 //
5139 // Handles player jumping
5140 //
P_DoJumpStuff(player_t * player,ticcmd_t * cmd)5141 static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
5142 {
5143 	mobj_t *lockonthok = NULL, *visual = NULL;
5144 
5145 	if (player->pflags & PF_JUMPSTASIS)
5146 		return;
5147 
5148 	if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) && (lockonthok = P_LookForEnemies(player, true, false)))
5149 	{
5150 		if (P_IsLocalPlayer(player)) // Only display it on your own view.
5151 		{
5152 			visual = P_SpawnMobj(lockonthok->x, lockonthok->y, lockonthok->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
5153 			P_SetTarget(&visual->target, lockonthok);
5154 		}
5155 	}
5156 
5157 	//////////////////
5158 	//SHIELD ACTIVES//
5159 	//& SUPER FLOAT!//
5160 	//////////////////
5161 
5162 	if ((player->pflags & PF_JUMPED) && !player->exiting && !P_PlayerInPain(player))
5163 	{
5164 		if (onground || player->climbing || player->powers[pw_carry])
5165 			;
5166 		else if ((gametyperules & GTR_TEAMFLAGS) && player->gotflag)
5167 			;
5168 		else if (player->pflags & (PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player has used an ability previously
5169 			;
5170 		else if (P_PlayerShieldThink(player, cmd, lockonthok, visual))
5171 			;
5172 		else if ((cmd->buttons & BT_SPIN))
5173 		{
5174 			if (!(player->pflags & PF_SPINDOWN) && P_SuperReady(player))
5175 			{
5176 				// If you can turn super and aren't already,
5177 				// and you don't have a shield, do it!
5178 				P_DoSuperTransformation(player, false);
5179 			}
5180 			else if (!LUAh_JumpSpinSpecial(player))
5181 				switch (player->charability)
5182 				{
5183 					case CA_THOK:
5184 						if (player->powers[pw_super]) // Super Sonic float
5185 						{
5186 							if ((player->speed > 5*player->mo->scale) // FixedMul(5<<FRACBITS, player->mo->scale), but scale is FRACUNIT-based
5187 							&& (P_MobjFlip(player->mo)*player->mo->momz <= 0))
5188 							{
5189 								if (player->panim != PA_RUN && player->panim != PA_WALK)
5190 								{
5191 									if (player->speed >= FixedMul(player->runspeed, player->mo->scale))
5192 										P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN);
5193 									else
5194 										P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT);
5195 								}
5196 
5197 								player->mo->momz = 0;
5198 								player->pflags &= ~(PF_STARTJUMP|PF_SPINNING);
5199 								player->secondjump = 1;
5200 							}
5201 						}
5202 						break;
5203 					case CA_TELEKINESIS:
5204 						if (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || (player->charflags & SF_MULTIABILITY))
5205 						{
5206 							P_Telekinesis(player,
5207 								-FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player)
5208 								FixedMul(384*FRACUNIT, player->mo->scale));
5209 						}
5210 						break;
5211 					case CA_TWINSPIN:
5212 						if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || player->charflags & SF_MULTIABILITY))
5213 							P_DoTwinSpin(player);
5214 						break;
5215 					default:
5216 						break;
5217 				}
5218 		}
5219 	}
5220 
5221 	if (player->charability == CA_AIRDRILL)
5222 	{
5223 		if (player->pflags & PF_JUMPED)
5224 		{
5225 			if (cmd->buttons & BT_SPIN && player->secondjump < 42) // speed up falling down
5226 				player->secondjump++;
5227 
5228 			if (player->flyangle > 0 && player->pflags & PF_THOKKED)
5229 			{
5230 				player->flyangle--;
5231 
5232 				P_SetObjectMomZ(player->mo, ((player->flyangle-24 - player->secondjump*3)*((player->actionspd>>FRACBITS)/12 + 1)<<FRACBITS)/7, false);
5233 
5234 				P_SpawnThokMobj(player);
5235 
5236 				if ((player->mo->eflags & MFE_UNDERWATER))
5237 					P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->normalspeed, player->mo->scale)*(80-player->flyangle - (player->actionspd>>FRACBITS)/2)/80);
5238 				else
5239 					P_InstaThrust(player->mo, player->mo->angle, ((FixedMul(player->normalspeed - player->actionspd/4, player->mo->scale))*2)/3);
5240 
5241 				player->drawangle = player->mo->angle;
5242 			}
5243 		}
5244 	}
5245 
5246 	///////////////
5247 	// CHARACTER //
5248 	// ABILITIES!//
5249 	///////////////
5250 
5251 	if (cmd->buttons & BT_JUMP && !player->exiting && !P_PlayerInPain(player))
5252 	{
5253 		if (LUAh_JumpSpecial(player))
5254 			;
5255 		// all situations below this require jump button not to be pressed already
5256 		else if (player->pflags & PF_JUMPDOWN)
5257 			;
5258 		// Jump S3&K style while in quicksand.
5259 		else if (P_InQuicksand(player->mo))
5260 		{
5261 			P_DoJump(player, true);
5262 			player->secondjump = 0;
5263 			player->pflags &= ~PF_THOKKED;
5264 		}
5265 		else if (player->powers[pw_carry] == CR_MACESPIN && player->mo->tracer)
5266 		{
5267 			player->powers[pw_carry] = CR_NONE;
5268 			P_SetTarget(&player->mo->tracer, NULL);
5269 			player->powers[pw_flashing] = TICRATE/4;
5270 		}
5271 		// can't jump while in air, can't jump while jumping
5272 		else if (onground || player->climbing || player->powers[pw_carry])
5273 		{
5274 			P_DoJump(player, true);
5275 			player->secondjump = 0;
5276 			player->pflags &= ~PF_THOKKED;
5277 		}
5278 		else if (player->pflags & PF_SLIDING || ((gametyperules & GTR_TEAMFLAGS) && player->gotflag) || player->pflags & PF_SHIELDABILITY)
5279 			;
5280 		/*else if (P_SuperReady(player))
5281 		{
5282 			// If you can turn super and aren't already,
5283 			// and you don't have a shield, do it!
5284 			P_DoSuperTransformation(player, false);
5285 		}*/
5286 		else if (player->pflags & PF_JUMPED)
5287 		{
5288 			if (!LUAh_AbilitySpecial(player))
5289 			switch (player->charability)
5290 			{
5291 				case CA_THOK:
5292 				case CA_HOMINGTHOK:
5293 				case CA_JUMPTHOK: // Credit goes to CZ64 and Sryder13 for the original
5294 					// Now it's Sonic's abilities turn!
5295 					// THOK!
5296 					if (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY))
5297 					{
5298 						// Catapult the player
5299 						fixed_t actionspd = player->actionspd;
5300 
5301 						if (player->charflags & SF_DASHMODE)
5302 							actionspd = max(player->normalspeed, FixedDiv(player->speed, player->mo->scale));
5303 
5304 						if (player->mo->eflags & MFE_UNDERWATER)
5305 							actionspd >>= 1;
5306 
5307 						if ((player->charability == CA_JUMPTHOK) && !(player->pflags & PF_THOKKED))
5308 						{
5309 							player->pflags &= ~PF_JUMPED;
5310 							P_DoJump(player, false);
5311 						}
5312 
5313 						P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale));
5314 
5315 						if (maptol & TOL_2D)
5316 						{
5317 							player->mo->momx /= 2;
5318 							player->mo->momy /= 2;
5319 						}
5320 						if (player->charability == CA_HOMINGTHOK)
5321 						{
5322 							player->mo->momx /= 2;
5323 							player->mo->momy /= 2;
5324 						}
5325 
5326 						if (player->charability == CA_HOMINGTHOK)
5327 						{
5328 							P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonthok));
5329 							if (lockonthok)
5330 							{
5331 								P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
5332 								player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonthok->x, lockonthok->y);
5333 								player->homing = 3*TICRATE;
5334 							}
5335 							else
5336 							{
5337 								P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
5338 								player->pflags &= ~PF_JUMPED;
5339 								player->mo->height = P_GetPlayerHeight(player);
5340 							}
5341 							player->pflags &= ~PF_NOJUMPDAMAGE;
5342 						}
5343 
5344 						player->drawangle = player->mo->angle;
5345 
5346 						if (player->mo->info->attacksound && !player->spectator)
5347 							S_StartSound(player->mo, player->mo->info->attacksound); // Play the THOK sound
5348 
5349 						P_SpawnThokMobj(player);
5350 
5351 						player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
5352 						player->pflags |= PF_THOKKED;
5353 
5354 						// Change localangle to match for simple controls? (P.S. chalupa)
5355 						// disabled because it seemed to disorient people and Z-targeting exists now
5356 						/*if (!demoplayback)
5357 						{
5358 							if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(gc_turnleft) || PLAYER1INPUTDOWN(gc_turnright)))
5359 								P_SetPlayerAngle(player, player->mo->angle);;
5360 							else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(gc_turnleft) || PLAYER2INPUTDOWN(gc_turnright)))
5361 								P_SetPlayerAngle(player, player->mo->angle);
5362 						}*/
5363 					}
5364 					break;
5365 
5366 				case CA_FLY:
5367 				case CA_SWIM:
5368 					// If currently in the air from a jump, and you pressed the
5369 					// button again and have the ability to fly, do so!
5370 					if (player->charability == CA_SWIM && !(player->mo->eflags & MFE_UNDERWATER))
5371 						; // Can't do anything if you're a fish out of water!
5372 					else if (!(player->pflags & PF_THOKKED) && !(player->powers[pw_tailsfly]))
5373 					{
5374 						P_SetPlayerMobjState(player->mo, S_PLAY_FLY); // Change to the flying animation
5375 
5376 						player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
5377 
5378 						player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH);
5379 						if (player->bot == 1)
5380 							player->pflags |= PF_THOKKED;
5381 						else
5382 							player->pflags |= (PF_THOKKED|PF_CANCARRY);
5383 					}
5384 					break;
5385 				case CA_GLIDEANDCLIMB:
5386 					// Now Knuckles-type abilities are checked.
5387 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5388 					{
5389 						fixed_t glidespeed = FixedMul(player->actionspd, player->mo->scale);
5390 						fixed_t playerspeed = player->speed;
5391 
5392 						if (player->mo->eflags & MFE_UNDERWATER)
5393 						{
5394 							glidespeed >>= 1;
5395 							playerspeed = 2*playerspeed/3;
5396 							if (!(player->powers[pw_super] || player->powers[pw_sneakers]))
5397 							{
5398 								player->mo->momx = (2*(player->mo->momx - player->cmomx)/3) + player->cmomx;
5399 								player->mo->momy = (2*(player->mo->momy - player->cmomy)/3) + player->cmomy;
5400 							}
5401 						}
5402 
5403 						player->pflags |= PF_GLIDING|PF_THOKKED;
5404 						player->glidetime = 0;
5405 
5406 						P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
5407 						if (playerspeed < glidespeed)
5408 							P_Thrust(player->mo, player->mo->angle, glidespeed - playerspeed);
5409 						player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
5410 					}
5411 					break;
5412 				case CA_DOUBLEJUMP: // Double-Jump
5413 					if (!(player->pflags & PF_THOKKED) || ((player->charflags & SF_MULTIABILITY) && (player->secondjump < (player->actionspd >> FRACBITS))))
5414 					{
5415 						player->pflags |= PF_THOKKED;
5416 						player->pflags &= ~PF_JUMPED;
5417 						P_DoJump(player, true);
5418 						player->secondjump++;
5419 					}
5420 					break;
5421 				case CA_FLOAT: // Float
5422 				case CA_SLOWFALL: // Slow descent hover
5423 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5424 					{
5425 						if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD)
5426 							P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
5427 						else if (player->speed >= FixedMul(player->runspeed, player->mo->scale))
5428 							P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN);
5429 						else
5430 							P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT);
5431 						player->pflags |= PF_THOKKED;
5432 						player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING);
5433 						player->secondjump = 1;
5434 					}
5435 					break;
5436 				case CA_TELEKINESIS:
5437 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5438 					{
5439 						P_Telekinesis(player,
5440 							FixedMul(player->actionspd, player->mo->scale), // +ve thrust (pushing away from player)
5441 							FixedMul(384*FRACUNIT, player->mo->scale));
5442 					}
5443 					break;
5444 				case CA_FALLSWITCH:
5445 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5446 					{
5447 						player->mo->momz = -player->mo->momz;
5448 						P_SpawnThokMobj(player);
5449 						player->pflags |= PF_THOKKED;
5450 					}
5451 					break;
5452 				case CA_AIRDRILL:
5453 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5454 					{
5455 						player->flyangle = 56 + (60-(player->actionspd>>FRACBITS))/3;
5456 						player->pflags |= PF_THOKKED;
5457 						S_StartSound(player->mo, sfx_spndsh);
5458 					}
5459 					break;
5460 				case CA_BOUNCE:
5461 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5462 					{
5463 						P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE);
5464 						player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
5465 						player->pflags |= PF_THOKKED|PF_BOUNCING;
5466 						player->mo->momx >>= 1;
5467 						player->mo->momy >>= 1;
5468 						player->mo->momz >>= 1;
5469 					}
5470 					break;
5471 				case CA_TWINSPIN:
5472 					if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY)
5473 						P_DoTwinSpin(player);
5474 					break;
5475 				default:
5476 					break;
5477 			}
5478 		}
5479 		else if (player->pflags & PF_THOKKED)
5480 		{
5481 			if (!LUAh_AbilitySpecial(player))
5482 				switch (player->charability)
5483 				{
5484 					case CA_FLY:
5485 					case CA_SWIM: // Swim
5486 						if (player->charability == CA_SWIM && !(player->mo->eflags & MFE_UNDERWATER))
5487 							; // Can't do anything if you're a fish out of water!
5488 						else if (player->powers[pw_tailsfly]) // If currently flying, give an ascend boost.
5489 						{
5490 							player->fly1 = 20;
5491 
5492 							if (player->charability == CA_SWIM)
5493 								player->fly1 /= 2;
5494 
5495 							// Slow down!
5496 							if (player->speed > FixedMul(8*FRACUNIT, player->mo->scale) && player->speed > FixedMul(player->normalspeed>>1, player->mo->scale))
5497 								P_Thrust(player->mo, R_PointToAngle2(0,0,player->mo->momx,player->mo->momy), FixedMul(-4*FRACUNIT, player->mo->scale));
5498 						}
5499 						break;
5500 					default:
5501 						break;
5502 				}
5503 		}
5504 		else if ((!(player->charflags & SF_NOSHIELDABILITY)) && ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super] && !LUAh_ShieldSpecial(player)))
5505 			P_DoJumpShield(player);
5506 	}
5507 
5508 	// HOMING option.
5509 	if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT // Sonic 3D Blast.
5510 	&& player->pflags & PF_SHIELDABILITY)
5511 	{
5512 		if (player->homing && player->mo->tracer)
5513 		{
5514 			if (!P_HomingAttack(player->mo, player->mo->tracer))
5515 			{
5516 				player->pflags &= ~PF_SHIELDABILITY;
5517 				player->secondjump = UINT8_MAX;
5518 				P_SetObjectMomZ(player->mo, 6*FRACUNIT, false);
5519 				if (player->mo->eflags & MFE_UNDERWATER)
5520 					player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3);
5521 				player->homing = 0;
5522 			}
5523 		}
5524 
5525 		// If you're not jumping, then you obviously wouldn't be homing.
5526 		if (!(player->pflags & PF_JUMPED))
5527 			player->homing = 0;
5528 	}
5529 	else if (player->charability == CA_HOMINGTHOK) // Sonic Adventure.
5530 	{
5531 		// If you've got a target, chase after it!
5532 		if (player->homing && player->mo->tracer)
5533 		{
5534 			P_SpawnThokMobj(player);
5535 
5536 			// But if you don't, then stop homing.
5537 			if (!P_HomingAttack(player->mo, player->mo->tracer))
5538 			{
5539 				if (player->mo->eflags & MFE_UNDERWATER)
5540 					P_SetObjectMomZ(player->mo, FixedDiv(457*FRACUNIT,72*FRACUNIT), false);
5541 				else
5542 					P_SetObjectMomZ(player->mo, 10*FRACUNIT, false);
5543 
5544 				player->mo->momx = player->mo->momy = player->homing = 0;
5545 
5546 				if (player->mo->tracer->flags2 & MF2_FRET)
5547 					P_InstaThrust(player->mo, player->mo->angle, -(player->speed>>3));
5548 
5549 				if (!(player->mo->tracer->flags & MF_BOSS))
5550 					player->pflags &= ~PF_THOKKED;
5551 
5552 				P_SetPlayerMobjState(player->mo, S_PLAY_SPRING);
5553 				player->pflags |= PF_NOJUMPDAMAGE;
5554 			}
5555 		}
5556 
5557 		// If you're not jumping, then you obviously wouldn't be homing.
5558 		if (!(player->pflags & PF_JUMPED))
5559 			player->homing = 0;
5560 	}
5561 	else
5562 		player->homing = 0;
5563 
5564 	if (cmd->buttons & BT_JUMP)
5565 	{
5566 		player->pflags |= PF_JUMPDOWN;
5567 
5568 		if ((!(gametyperules & GTR_TEAMFLAGS) || !player->gotflag) && !player->exiting)
5569 		{
5570 			if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP && player->charability != CA_THOK)
5571 			{
5572 				fixed_t potentialmomz;
5573 				if (player->charability == CA_SLOWFALL)
5574 					potentialmomz = -gravity*4;
5575 				else
5576 					potentialmomz = ((player->speed < 10*player->mo->scale)
5577 					? (player->speed - 10*player->mo->scale)/5
5578 					: 0);
5579 				if (P_MobjFlip(player->mo)*player->mo->momz < potentialmomz)
5580 					player->mo->momz = P_MobjFlip(player->mo)*potentialmomz;
5581 				player->pflags &= ~PF_SPINNING;
5582 			}
5583 		}
5584 	}
5585 	else // If not pressing the jump button
5586 	{
5587 		player->pflags &= ~PF_JUMPDOWN;
5588 
5589 		// Repeat abilities, but not double jump!
5590 		if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP && player->charability != CA_AIRDRILL && player->charability != CA_THOK)
5591 		{
5592 			if (player->charflags & SF_MULTIABILITY)
5593 			{
5594 				player->pflags |= (PF_JUMPED|PF_NOJUMPDAMAGE);
5595 				player->secondjump = 0;
5596 			}
5597 			else
5598 				player->secondjump = 2;
5599 
5600 			P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
5601 		}
5602 
5603 		// If letting go of the jump button while still on ascent, cut the jump height.
5604 		if (((player->pflags & (PF_JUMPED|PF_STARTJUMP)) == (PF_JUMPED|PF_STARTJUMP)) && (P_MobjFlip(player->mo)*player->mo->momz > 0))
5605 		{
5606 			player->mo->momz >>= 1;
5607 			player->pflags &= ~PF_STARTJUMP;
5608 		}
5609 	}
5610 }
5611 
5612 //
5613 // P_GetPlayerControlDirection
5614 //
5615 // Determines if the player is pressing in the direction they are moving
5616 //
5617 // 0 = no controls pressed/no movement
5618 // 1 = pressing in the direction of movement
5619 // 2 = pressing in the opposite direction of movement
5620 //
P_GetPlayerControlDirection(player_t * player)5621 INT32 P_GetPlayerControlDirection(player_t *player)
5622 {
5623 	ticcmd_t *cmd = &player->cmd;
5624 	angle_t controllerdirection, controlplayerdirection;
5625 	camera_t *thiscam;
5626 	angle_t dangle;
5627 	fixed_t tempx = 0, tempy = 0;
5628 	angle_t tempangle, origtempangle;
5629 
5630 	if (splitscreen && player == &players[secondarydisplayplayer])
5631 		thiscam = &camera2;
5632 	else
5633 		thiscam = &camera;
5634 
5635 	if (!cmd->forwardmove && !cmd->sidemove)
5636 		return 0;
5637 
5638 	if (!player->mo->momx && !player->mo->momy)
5639 		return 0;
5640 
5641 	if (twodlevel || player->mo->flags2 & MF2_TWOD)
5642 	{
5643 		if (!cmd->sidemove)
5644 			return 0;
5645 		if (!player->mo->momx)
5646 			return 0;
5647 		origtempangle = tempangle = 0; // relative to the axis rather than the player!
5648 		controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
5649 	}
5650 	else if ((P_ControlStyle(player) & CS_LMAOGALOG) && thiscam->chase)
5651 	{
5652 		if (player->awayviewtics)
5653 			origtempangle = tempangle = player->awayviewmobj->angle;
5654 		else
5655 			origtempangle = tempangle = thiscam->angle;
5656 		controlplayerdirection = player->mo->angle;
5657 	}
5658 	else
5659 	{
5660 		origtempangle = tempangle = player->mo->angle;
5661 		controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
5662 	}
5663 
5664 	// Calculate the angle at which the controls are pointing
5665 	// to figure out the proper mforward and mbackward.
5666 	tempangle >>= ANGLETOFINESHIFT;
5667 	if (!(twodlevel || player->mo->flags2 & MF2_TWOD)) // in 2d mode, sidemove is treated as the forwards/backwards direction
5668 	{
5669 		tempx += FixedMul(cmd->forwardmove*FRACUNIT,FINECOSINE(tempangle));
5670 		tempy += FixedMul(cmd->forwardmove*FRACUNIT,FINESINE(tempangle));
5671 
5672 		tempangle = origtempangle-ANGLE_90;
5673 		tempangle >>= ANGLETOFINESHIFT;
5674 	}
5675 	tempx += FixedMul(cmd->sidemove*FRACUNIT,FINECOSINE(tempangle));
5676 	tempy += FixedMul(cmd->sidemove*FRACUNIT,FINESINE(tempangle));
5677 
5678 	controllerdirection = R_PointToAngle2(0, 0, tempx, tempy);
5679 
5680 	dangle = controllerdirection - controlplayerdirection;
5681 
5682 	if (dangle > ANGLE_180) //flip to keep to one side
5683 		dangle = InvAngle(dangle);
5684 
5685 	if (dangle > ANGLE_90)
5686 		return 2; // Controls pointing backwards from player
5687 	else
5688 		return 1; // Controls pointing in player's general direction
5689 }
5690 
5691 // Control scheme for 2d levels.
P_2dMovement(player_t * player)5692 static void P_2dMovement(player_t *player)
5693 {
5694 	ticcmd_t *cmd;
5695 	INT32 topspeed, acceleration, thrustfactor;
5696 	fixed_t movepushforward = 0;
5697 	angle_t movepushangle = 0;
5698 	fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale);
5699 
5700 	cmd = &player->cmd;
5701 
5702 	if (player->exiting || player->pflags & PF_STASIS)
5703 	{
5704 		cmd->forwardmove = cmd->sidemove = 0;
5705 		if (player->pflags & PF_GLIDING)
5706 		{
5707 			if (!player->skidtime)
5708 				player->pflags &= ~PF_GLIDING;
5709 			else if (player->exiting)
5710 			{
5711 				player->pflags &= ~PF_GLIDING;
5712 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
5713 				player->skidtime = 0;
5714 			}
5715 		}
5716 		if (player->pflags & PF_BOUNCING)
5717 			player->pflags &= ~PF_BOUNCING;
5718 		if (player->pflags & PF_SPINNING && !player->exiting)
5719 		{
5720 			player->pflags &= ~PF_SPINNING;
5721 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
5722 		}
5723 	}
5724 
5725 	// cmomx/cmomy stands for the conveyor belt speed.
5726 	if (player->onconveyor == 2) // Wind/Current
5727 	{
5728 		//if (player->mo->z > player->mo->watertop || player->mo->z + player->mo->height < player->mo->waterbottom)
5729 		if (!(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
5730 			player->cmomx = player->cmomy = 0;
5731 	}
5732 	else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt
5733 		player->cmomx = player->cmomy = 0;
5734 	else if (player->onconveyor != 2 && player->onconveyor != 4 && player->onconveyor != 1)
5735 		player->cmomx = player->cmomy = 0;
5736 
5737 	player->rmomx = player->mo->momx - player->cmomx;
5738 	player->rmomy = player->mo->momy - player->cmomy;
5739 
5740 	// Calculates player's speed based on absolute-value-of-a-number formula
5741 	player->speed = abs(player->rmomx);
5742 
5743 	if (player->pflags & PF_GLIDING)
5744 	{
5745 		// Angle fix.
5746 		if (player->mo->angle < ANGLE_180 && player->mo->angle > ANGLE_90)
5747 			player->mo->angle = ANGLE_180;
5748 		else if (player->mo->angle < ANGLE_90 && player->mo->angle > 0)
5749 			player->mo->angle = 0;
5750 
5751 		if (cmd->sidemove > 0 && player->mo->angle != 0 && player->mo->angle >= ANGLE_180)
5752 			player->mo->angle += 1280<<FRACBITS;
5753 		else if (cmd->sidemove < 0 && player->mo->angle != ANGLE_180 && (player->mo->angle > ANGLE_180 || player->mo->angle == 0))
5754 			player->mo->angle -= 1280<<FRACBITS;
5755 		else if (cmd->sidemove == 0)
5756 		{
5757 			if (player->mo->angle >= ANGLE_270)
5758 				player->mo->angle += 1280<<FRACBITS;
5759 			else if (player->mo->angle < ANGLE_270 && player->mo->angle > ANGLE_180)
5760 				player->mo->angle -= 1280<<FRACBITS;
5761 		}
5762 	}
5763 	else if (cmd->sidemove && !(player->climbing) && !P_PlayerInPain(player))
5764 	{
5765 		if (cmd->sidemove > 0)
5766 			player->mo->angle = 0;
5767 		else if (cmd->sidemove < 0)
5768 			player->mo->angle = ANGLE_180;
5769 	}
5770 
5771 	P_SetPlayerAngle(player, player->mo->angle);
5772 
5773 	if (player->pflags & PF_GLIDING)
5774 		movepushangle = player->mo->angle;
5775 	else
5776 	{
5777 		if (cmd->sidemove > 0)
5778 			movepushangle = 0;
5779 		else if (cmd->sidemove < 0)
5780 			movepushangle = ANGLE_180;
5781 		else
5782 			movepushangle = player->mo->angle;
5783 	}
5784 
5785 	// Do not let the player control movement if not onground.
5786 	onground = P_IsObjectOnGround(player->mo);
5787 
5788 	player->aiming = cmd->aiming<<FRACBITS;
5789 
5790 	// Set the player speeds.
5791 	if (maptol & TOL_2D)
5792 		normalspd = FixedMul(normalspd, 2*FRACUNIT/3);
5793 
5794 	if (player->powers[pw_super] || player->powers[pw_sneakers])
5795 	{
5796 		thrustfactor = player->thrustfactor*2;
5797 		acceleration = player->accelstart/2 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration/2;
5798 
5799 		if (player->powers[pw_tailsfly])
5800 			topspeed = normalspd;
5801 		else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER) && !(player->pflags & PF_SLIDING))
5802 		{
5803 			topspeed = normalspd;
5804 			acceleration = 2*acceleration/3;
5805 		}
5806 		else
5807 			topspeed = normalspd * 2;
5808 	}
5809 	else
5810 	{
5811 		thrustfactor = player->thrustfactor;
5812 		acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
5813 
5814 		if (player->powers[pw_tailsfly])
5815 			topspeed = normalspd/2;
5816 		else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER) && !(player->pflags & PF_SLIDING))
5817 		{
5818 			topspeed = normalspd/2;
5819 			acceleration = 2*acceleration/3;
5820 		}
5821 		else
5822 			topspeed = normalspd;
5823 	}
5824 
5825 //////////////////////////////////////
5826 	if (player->climbing)
5827 	{
5828 		if (cmd->forwardmove != 0)
5829 			P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1), false); // 2/3 while super
5830 
5831 		player->mo->momx = 0;
5832 	}
5833 	else if (cmd->sidemove != 0 && !(player->pflags & PF_GLIDING || player->exiting
5834 		|| (P_PlayerInPain(player) && !onground)))
5835 	{
5836 		movepushforward = abs(cmd->sidemove) * (thrustfactor * acceleration);
5837 
5838 		// allow very small movement while in air for gameplay
5839 		if (!onground)
5840 			movepushforward >>= 1; // Proper air movement
5841 
5842 		// Allow a bit of movement while spinning
5843 		if ((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING)
5844 		{
5845 			if (!(player->pflags & PF_STARTDASH))
5846 				movepushforward = movepushforward/48;
5847 			else
5848 				movepushforward = 0;
5849 		}
5850 
5851 		movepushforward = FixedMul(movepushforward, player->mo->scale);
5852 		if (player->rmomx < topspeed && cmd->sidemove > 0) // Sonic's Speed
5853 			P_Thrust(player->mo, movepushangle, movepushforward);
5854 		else if (player->rmomx > -topspeed && cmd->sidemove < 0)
5855 			P_Thrust(player->mo, movepushangle, movepushforward);
5856 	}
5857 }
5858 
5859 //#define OLD_MOVEMENT_CODE 1
P_3dMovement(player_t * player)5860 static void P_3dMovement(player_t *player)
5861 {
5862 	ticcmd_t *cmd;
5863 	angle_t movepushangle, movepushsideangle; // Analog
5864 	INT32 topspeed, acceleration, thrustfactor;
5865 	fixed_t movepushforward = 0, movepushside = 0;
5866 	INT32 mforward = 0, mbackward = 0;
5867 	angle_t dangle; // replaces old quadrants bits
5868 	fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale);
5869 	controlstyle_e controlstyle;
5870 	boolean spin = ((onground = P_IsObjectOnGround(player->mo)) && (player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING && (player->rmomx || player->rmomy) && !(player->pflags & PF_STARTDASH));
5871 	fixed_t oldMagnitude, newMagnitude;
5872 	vector3_t totalthrust;
5873 
5874 	totalthrust.x = totalthrust.y = 0; // I forget if this is needed
5875 	totalthrust.z = FRACUNIT*P_MobjFlip(player->mo)/3; // A bit of extra push-back on slopes
5876 
5877 	// Get the old momentum; this will be needed at the end of the function! -SH
5878 	oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
5879 
5880 	controlstyle = P_ControlStyle(player);
5881 
5882 	cmd = &player->cmd;
5883 
5884 	if (player->exiting || player->pflags & PF_STASIS)
5885 	{
5886 		cmd->forwardmove = cmd->sidemove = 0;
5887 		if (player->pflags & PF_GLIDING)
5888 		{
5889 			if (!player->skidtime)
5890 				player->pflags &= ~PF_GLIDING;
5891 			else if (player->exiting)
5892 			{
5893 				player->pflags &= ~PF_GLIDING;
5894 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
5895 				player->skidtime = 0;
5896 			}
5897 		}
5898 		if (player->pflags & PF_BOUNCING)
5899 			player->pflags &= ~PF_BOUNCING;
5900 		if (player->pflags & PF_SPINNING && !player->exiting)
5901 		{
5902 			player->pflags &= ~PF_SPINNING;
5903 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
5904 		}
5905 	}
5906 
5907 	if (controlstyle & CS_LMAOGALOG)
5908 	{
5909 		movepushangle = (cmd->angleturn<<16 /* not FRACBITS */);
5910 	}
5911 	else
5912 	{
5913 		movepushangle = player->mo->angle;
5914 	}
5915 	movepushsideangle = movepushangle-ANGLE_90;
5916 
5917 	// cmomx/cmomy stands for the conveyor belt speed.
5918 	if (player->onconveyor == 2) // Wind/Current
5919 	{
5920 		//if (player->mo->z > player->mo->watertop || player->mo->z + player->mo->height < player->mo->waterbottom)
5921 		if (!(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
5922 			player->cmomx = player->cmomy = 0;
5923 	}
5924 	else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt
5925 		player->cmomx = player->cmomy = 0;
5926 	else if (player->onconveyor != 2 && player->onconveyor != 4 && player->onconveyor != 1)
5927 		player->cmomx = player->cmomy = 0;
5928 
5929 	player->rmomx = player->mo->momx - player->cmomx;
5930 	player->rmomy = player->mo->momy - player->cmomy;
5931 
5932 	// Calculates player's speed based on distance-of-a-line formula
5933 	player->speed = P_AproxDistance(player->rmomx, player->rmomy);
5934 
5935 	// Monster Iestyn - 04-11-13
5936 	// Quadrants are stupid, excessive and broken, let's do this a much simpler way!
5937 	// Get delta angle from rmom angle and player angle first
5938 	dangle = R_PointToAngle2(0,0, player->rmomx, player->rmomy) - player->mo->angle;
5939 	if (dangle > ANGLE_180) //flip to keep to one side
5940 		dangle = InvAngle(dangle);
5941 
5942 	// now use it to determine direction!
5943 	if (dangle <= ANGLE_45) // angles 0-45 or 315-360
5944 		mforward = 1; // going forwards
5945 	else if (dangle >= ANGLE_135) // angles 135-225
5946 		mbackward = 1; // going backwards
5947 
5948 	// anything else will leave both at 0, so no need to do anything else
5949 
5950 	// When sliding, don't allow forward/back
5951 	if (player->pflags & PF_SLIDING)
5952 		cmd->forwardmove = 0;
5953 	else if (onground && player->mo->state == states+S_PLAY_PAIN)
5954 		P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
5955 
5956 	player->aiming = cmd->aiming<<FRACBITS;
5957 
5958 	// Set the player speeds.
5959 	if (player->pflags & PF_SLIDING)
5960 	{
5961 		normalspd = FixedMul(36<<FRACBITS, player->mo->scale);
5962 		thrustfactor = 5;
5963 		acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40;
5964 		topspeed = normalspd;
5965 	}
5966 	else if (player->bot)
5967 	{ // Bot steals player 1's stats
5968 		normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale);
5969 		thrustfactor = players[consoleplayer].thrustfactor;
5970 		acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration;
5971 
5972 		if (player->powers[pw_tailsfly])
5973 			topspeed = normalspd/2;
5974 		else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER))
5975 		{
5976 			topspeed = normalspd/2;
5977 			acceleration = 2*acceleration/3;
5978 		}
5979 		else
5980 			topspeed = normalspd;
5981 	}
5982 	else
5983 	{
5984 		if (player->powers[pw_super] || player->powers[pw_sneakers])
5985 		{
5986 			topspeed = 5 * normalspd / 3; // 1.67x
5987 			thrustfactor = player->thrustfactor*2;
5988 			acceleration = player->accelstart/2 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration/2;
5989 		}
5990 		else
5991 		{
5992 			topspeed = normalspd;
5993 			thrustfactor = player->thrustfactor;
5994 			acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
5995 		}
5996 
5997 		if (player->powers[pw_tailsfly])
5998 			topspeed >>= 1;
5999 		else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER))
6000 		{
6001 			topspeed >>= 1;
6002 			acceleration = 2*acceleration/3;
6003 		}
6004 	}
6005 
6006 	if (spin) // Prevent gaining speed whilst rolling!
6007 	{
6008 		const fixed_t ns = FixedDiv(549*ORIG_FRICTION,500*FRACUNIT); // P_XYFriction
6009 		topspeed = FixedMul(oldMagnitude, ns);
6010 	}
6011 
6012 	// Better maneuverability while flying
6013 	if (player->powers[pw_tailsfly])
6014 	{
6015 		thrustfactor = player->thrustfactor*2;
6016 		acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
6017 	}
6018 	else
6019 	{
6020 		if (player->pflags & PF_BOUNCING)
6021 		{
6022 			if (player->mo->state-states == S_PLAY_BOUNCE_LANDING)
6023 			{
6024 				thrustfactor = player->thrustfactor*8;
6025 				acceleration = player->accelstart/8 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration/8;
6026 			}
6027 			else
6028 			{
6029 				thrustfactor = (3*player->thrustfactor)/4;
6030 				acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
6031 			}
6032 		}
6033 
6034 		if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
6035 			acceleration = FixedMul(acceleration<<FRACBITS, player->mo->movefactor)>>FRACBITS;
6036 	}
6037 
6038 	// Forward movement
6039 	if (player->climbing)
6040 	{
6041 		if (cmd->forwardmove)
6042 		{
6043 			if (player->mo->eflags & MFE_UNDERWATER)
6044 				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 20*FRACUNIT/3 : 10*FRACUNIT), false); // 2/3 while super
6045 			else
6046 				P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1), false); // 2/3 while super
6047 		}
6048 	}
6049 	else if (!(controlstyle == CS_LMAOGALOG)
6050 		&& cmd->forwardmove != 0 && !(player->pflags & PF_GLIDING || player->exiting
6051 		|| (P_PlayerInPain(player) && !onground)))
6052 	{
6053 		movepushforward = cmd->forwardmove * (thrustfactor * acceleration);
6054 
6055 		// Allow a bit of movement while spinning
6056 		if ((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING)
6057 		{
6058 			if ((mforward && cmd->forwardmove > 0) || (mbackward && cmd->forwardmove < 0)
6059 			|| (player->pflags & PF_STARTDASH))
6060 				movepushforward = 0;
6061 			else if (onground)
6062 				movepushforward >>= 4;
6063 			else
6064 				movepushforward >>= 3;
6065 		}
6066 		// allow very small movement while in air for gameplay
6067 		else if (!onground)
6068 			movepushforward >>= 2; // proper air movement
6069 
6070 		movepushforward = FixedMul(movepushforward, player->mo->scale);
6071 
6072 		totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward);
6073 		totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward);
6074 	}
6075 	// Sideways movement
6076 	if (player->climbing)
6077 	{
6078 		if (player->mo->eflags & MFE_UNDERWATER)
6079 			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, player->powers[pw_super] ? 20*FRACUNIT/3 : 10*FRACUNIT)); // 2/3 while super
6080 		else
6081 			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedDiv(cmd->sidemove*player->mo->scale, player->powers[pw_super] ? 5*FRACUNIT : 15*FRACUNIT>>1)); // 2/3 while super
6082 	}
6083 	// Analog movement control
6084 	else if (controlstyle == CS_LMAOGALOG)
6085 	{
6086 		if (!(player->pflags & PF_GLIDING || player->exiting || P_PlayerInPain(player)))
6087 		{
6088 			angle_t controldirection;
6089 
6090 			// Calculate the angle at which the controls are pointing
6091 			// to figure out the proper mforward and mbackward.
6092 			// (Why was it so complicated before? ~Red)
6093 			controldirection = R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT)+movepushangle;
6094 
6095 			movepushforward = FixedHypot(cmd->sidemove, cmd->forwardmove) * (thrustfactor * acceleration);
6096 
6097 			// Allow a bit of movement while spinning
6098 			if ((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING)
6099 			{
6100 				if ((mforward && cmd->forwardmove > 0) || (mbackward && cmd->forwardmove < 0)
6101 				|| (player->pflags & PF_STARTDASH))
6102 					movepushforward = 0;
6103 				else if (onground)
6104 					movepushforward >>= 4;
6105 				else
6106 					movepushforward >>= 3;
6107 			}
6108 			// allow very small movement while in air for gameplay
6109 			else if (!onground)
6110 				movepushforward >>= 2; // proper air movement
6111 
6112 			movepushsideangle = controldirection;
6113 
6114 			movepushforward = FixedMul(movepushforward, player->mo->scale);
6115 
6116 			totalthrust.x += P_ReturnThrustX(player->mo, controldirection, movepushforward);
6117 			totalthrust.y += P_ReturnThrustY(player->mo, controldirection, movepushforward);
6118 		}
6119 	}
6120 	else if (cmd->sidemove && !(player->pflags & PF_GLIDING) && !player->exiting && !P_PlayerInPain(player))
6121 	{
6122 		movepushside = cmd->sidemove * (thrustfactor * acceleration);
6123 
6124 		// allow very small movement while in air for gameplay
6125 		if (!onground)
6126 		{
6127 			movepushside >>= 2; // proper air movement
6128 			// Reduce movepushslide even more if over "max" flight speed
6129 			if (((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING) || (player->powers[pw_tailsfly] && player->speed > topspeed))
6130 				movepushside >>= 2;
6131 		}
6132 		// Allow a bit of movement while spinning
6133 		else if ((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING)
6134 		{
6135 			if (player->pflags & PF_STARTDASH)
6136 				movepushside = 0;
6137 			else if (onground)
6138 				movepushside >>= 4;
6139 			else
6140 				movepushside >>= 3;
6141 		}
6142 
6143 		// Finally move the player now that their speed/direction has been decided.
6144 		movepushside = FixedMul(movepushside, player->mo->scale);
6145 
6146 		totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside);
6147 		totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside);
6148 	}
6149 
6150 	if ((totalthrust.x || totalthrust.y)
6151 		&& player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) {
6152 		// Factor thrust to slope, but only for the part pushing up it!
6153 		// The rest is unaffected.
6154 		angle_t thrustangle = R_PointToAngle2(0, 0, totalthrust.x, totalthrust.y)-player->mo->standingslope->xydirection;
6155 
6156 		if (player->mo->standingslope->zdelta < 0) { // Direction goes down, so thrustangle needs to face toward
6157 			if (thrustangle < ANGLE_90 || thrustangle > ANGLE_270) {
6158 				P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope);
6159 			}
6160 		} else { // Direction goes up, so thrustangle needs to face away
6161 			if (thrustangle > ANGLE_90 && thrustangle < ANGLE_270) {
6162 				P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope);
6163 			}
6164 		}
6165 	}
6166 
6167 	player->mo->momx += totalthrust.x;
6168 	player->mo->momy += totalthrust.y;
6169 
6170 	// Time to ask three questions:
6171 	// 1) Are we over topspeed?
6172 	// 2) If "yes" to 1, were we moving over topspeed to begin with?
6173 	// 3) If "yes" to 2, are we now going faster?
6174 
6175 	// If "yes" to 3, normalize to our initial momentum; this will allow thoks to stay as fast as they normally are.
6176 	// If "no" to 3, ignore it; the player might be going too fast, but they're slowing down, so let them.
6177 	// If "no" to 2, normalize to topspeed, so we can't suddenly run faster than it of our own accord.
6178 	// If "no" to 1, we're not reaching any limits yet, so ignore this entirely!
6179 	// -Shadow Hog
6180 	newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
6181 	if (newMagnitude > topspeed)
6182 	{
6183 		fixed_t tempmomx, tempmomy;
6184 		if (oldMagnitude > topspeed && !spin)
6185 		{
6186 			if (newMagnitude > oldMagnitude)
6187 			{
6188 				tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude);
6189 				tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude);
6190 				player->mo->momx = tempmomx + player->cmomx;
6191 				player->mo->momy = tempmomy + player->cmomy;
6192 			}
6193 			// else do nothing
6194 		}
6195 		else
6196 		{
6197 			tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), topspeed);
6198 			tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), topspeed);
6199 			player->mo->momx = tempmomx + player->cmomx;
6200 			player->mo->momy = tempmomy + player->cmomy;
6201 		}
6202 	}
6203 }
6204 
6205 //
6206 // P_SpectatorMovement
6207 //
6208 // Control for spectators in multiplayer
6209 //
P_SpectatorMovement(player_t * player)6210 static void P_SpectatorMovement(player_t *player)
6211 {
6212 	ticcmd_t *cmd = &player->cmd;
6213 
6214 	player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
6215 
6216 	ticruned++;
6217 	if (!(cmd->angleturn & TICCMD_RECEIVED))
6218 		ticmiss++;
6219 
6220 	if (cmd->buttons & BT_JUMP)
6221 		player->mo->z += FRACUNIT*16;
6222 	else if (cmd->buttons & BT_SPIN)
6223 		player->mo->z -= FRACUNIT*16;
6224 
6225 	if (player->mo->z > player->mo->ceilingz - player->mo->height)
6226 		player->mo->z = player->mo->ceilingz - player->mo->height;
6227 	if (player->mo->z < player->mo->floorz)
6228 		player->mo->z = player->mo->floorz;
6229 
6230 	// Aiming needed for SEENAMES, etc.
6231 	// We may not need to fire as a spectator, but this is still handy!
6232 	player->aiming = cmd->aiming<<FRACBITS;
6233 
6234 	player->mo->momx = player->mo->momy = player->mo->momz = 0;
6235 	if (cmd->forwardmove != 0)
6236 	{
6237 		P_Thrust(player->mo, player->mo->angle, cmd->forwardmove*(FRACUNIT/2));
6238 
6239 		// Quake-style flying spectators :D
6240 		player->mo->momz += FixedMul(cmd->forwardmove*(FRACUNIT/2), AIMINGTOSLOPE(player->aiming));
6241 	}
6242 	if (cmd->sidemove != 0)
6243 	{
6244 		P_Thrust(player->mo, player->mo->angle-ANGLE_90, cmd->sidemove*(FRACUNIT/2));
6245 	}
6246 }
6247 
6248 //
6249 // P_ShootLine
6250 //
6251 // Fun and fancy
6252 // graphical indicator
6253 // for building/debugging
6254 // NiGHTS levels!
P_ShootLine(mobj_t * source,mobj_t * dest,fixed_t height)6255 static void P_ShootLine(mobj_t *source, mobj_t *dest, fixed_t height)
6256 {
6257 	mobj_t *mo;
6258 	INT32 i;
6259 	fixed_t temp;
6260 	INT32 speed, seesound;
6261 
6262 	temp = dest->z;
6263 	dest->z = height;
6264 
6265 	seesound = mobjinfo[MT_REDRING].seesound;
6266 	speed = mobjinfo[MT_REDRING].speed;
6267 	mobjinfo[MT_REDRING].seesound = sfx_None;
6268 	mobjinfo[MT_REDRING].speed = 20*FRACUNIT;
6269 
6270 	mo = P_SpawnXYZMissile(source, dest, MT_REDRING, source->x, source->y, height);
6271 
6272 	dest->z = temp;
6273 	if (mo)
6274 	{
6275 		mo->flags2 |= MF2_RAILRING;
6276 		mo->flags2 |= MF2_DONTDRAW;
6277 		mo->flags |= MF_NOCLIPHEIGHT;
6278 		mo->flags |= MF_NOCLIP;
6279 		mo->flags &= ~MF_MISSILE;
6280 		mo->fuse = 3;
6281 	}
6282 
6283 	for (i = 0; i < 32; i++)
6284 	{
6285 		if (mo)
6286 		{
6287 			if (!(mo->flags & MF_NOBLOCKMAP))
6288 			{
6289 				P_UnsetThingPosition(mo);
6290 				mo->flags |= MF_NOBLOCKMAP;
6291 				P_SetThingPosition(mo);
6292 			}
6293 			if (i&1)
6294 				P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
6295 
6296 			P_UnsetThingPosition(mo);
6297 			mo->x += mo->momx;
6298 			mo->y += mo->momy;
6299 			mo->z += mo->momz;
6300 			P_SetThingPosition(mo);
6301 		}
6302 		else
6303 		{
6304 			mobjinfo[MT_REDRING].seesound = seesound;
6305 			mobjinfo[MT_REDRING].speed = speed;
6306 			return;
6307 		}
6308 	}
6309 	mobjinfo[MT_REDRING].seesound = seesound;
6310 	mobjinfo[MT_REDRING].speed = speed;
6311 }
6312 
6313 #define MAXDRILLSPEED 14000
6314 #define MAXNORMALSPEED 6250 //6000
6315 
P_NightsTransferPoints(player_t * player,fixed_t xspeed,fixed_t radius)6316 static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t radius)
6317 {
6318 	if (player->pflags & PF_TRANSFERTOCLOSEST)
6319 	{
6320 		const angle_t fa = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y);
6321 		P_InstaThrust(player->mo, fa, xspeed/10);
6322 	}
6323 	else
6324 	{
6325 		const angle_t fa = player->angle_pos>>ANGLETOFINESHIFT;
6326 		const angle_t faold = player->old_angle_pos>>ANGLETOFINESHIFT;
6327 		player->mo->momx = FixedMul(FINECOSINE(fa),radius) - FixedMul(FINECOSINE(faold),radius);
6328 		player->mo->momy = FixedMul(FINESINE(fa),radius) - FixedMul(FINESINE(faold),radius);
6329 	}
6330 
6331 	if (player->exiting)
6332 		return;
6333 
6334 	{
6335 		boolean notallowed;
6336 		mobj_t *hack = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_NULL);
6337 		hack->flags = MF_NOGRAVITY;
6338 		hack->radius = player->mo->radius;
6339 		hack->height = player->mo->height;
6340 		hack->z = player->mo->z;
6341 		P_SetThingPosition(hack);
6342 		notallowed = (!(P_TryMove(hack, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, true)));
6343 		P_RemoveMobj(hack);
6344 		if (notallowed)
6345 			return;
6346 	}
6347 
6348 	{
6349 		const INT32 sequence = player->mo->target->threshold;
6350 		mobj_t *transfer1 = NULL;
6351 		mobj_t *transfer2 = NULL;
6352 		mobj_t *axis;
6353 		mobj_t *mo2;
6354 		thinker_t *th;
6355 		line_t transfer1line;
6356 		line_t transfer2line;
6357 		boolean transfer1last = false;
6358 		boolean transfer2last = false;
6359 		vertex_t vertices[4];
6360 		fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
6361 
6362 		// Find next waypoint
6363 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
6364 		{
6365 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
6366 				continue;
6367 
6368 			mo2 = (mobj_t *)th;
6369 
6370 			// Axis things are only at beginning of list.
6371 			if (!(mo2->flags2 & MF2_AXIS))
6372 				break;
6373 			if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
6374 				continue;
6375 			if (mo2->threshold != sequence)
6376 				continue;
6377 
6378 			if (player->pflags & PF_TRANSFERTOCLOSEST)
6379 			{
6380 				if (mo2->health == player->axis1->health)
6381 					transfer1 = mo2;
6382 				else if (mo2->health == player->axis2->health)
6383 					transfer2 = mo2;
6384 			}
6385 			else
6386 			{
6387 				if (mo2->health == player->mo->target->health)
6388 					transfer1 = mo2;
6389 				else if (mo2->health == player->mo->target->health + 1)
6390 					transfer2 = mo2;
6391 			}
6392 		}
6393 
6394 		// It might be possible that one wasn't found.
6395 		// Is it because we're at the end of the track?
6396 		// Look for a wrapper point.
6397 		if (!transfer1)
6398 		{
6399 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
6400 			{
6401 				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
6402 					continue;
6403 
6404 				mo2 = (mobj_t *)th;
6405 
6406 				// Axis things are only at beginning of list.
6407 				if (!(mo2->flags2 & MF2_AXIS))
6408 					break;
6409 				if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
6410 					continue;
6411 				if (mo2->threshold != sequence)
6412 					continue;
6413 
6414 				if (!transfer1)
6415 				{
6416 					transfer1 = mo2;
6417 					transfer1last = true;
6418 				}
6419 				else if (mo2->health > transfer1->health)
6420 				{
6421 					transfer1 = mo2;
6422 					transfer1last = true;
6423 				}
6424 			}
6425 		}
6426 		if (!transfer2)
6427 		{
6428 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
6429 			{
6430 				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
6431 					continue;
6432 
6433 				mo2 = (mobj_t *)th;
6434 
6435 				// Axis things are only at beginning of list.
6436 				if (!(mo2->flags2 & MF2_AXIS))
6437 					break;
6438 				if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
6439 					continue;
6440 				if (mo2->threshold != sequence)
6441 					continue;
6442 
6443 				if (!transfer2)
6444 				{
6445 					transfer2 = mo2;
6446 					transfer2last = true;
6447 				}
6448 				else if (mo2->health > transfer2->health)
6449 				{
6450 					transfer2 = mo2;
6451 					transfer2last = true;
6452 				}
6453 			}
6454 		}
6455 
6456 		if (!(transfer1 && transfer2)) // We can't continue...
6457 			I_Error("Mare does not form a complete circuit!\n");
6458 
6459 		transfer1line.v1 = &vertices[0];
6460 		transfer1line.v2 = &vertices[1];
6461 		transfer2line.v1 = &vertices[2];
6462 		transfer2line.v2 = &vertices[3];
6463 
6464 		if (cv_debug && (leveltime % TICRATE == 0))
6465 		{
6466 			CONS_Debug(DBG_NIGHTS, "Transfer1 : %d\n", transfer1->health);
6467 			CONS_Debug(DBG_NIGHTS, "Transfer2 : %d\n", transfer2->health);
6468 		}
6469 		//CONS_Debug(DBG_NIGHTS, "Xspeed : %d", truexspeed);
6470 
6471 		//CONS_Debug(DBG_NIGHTS, "T1 is at %d, %d\n", transfer1->x>>FRACBITS, transfer1->y>>FRACBITS);
6472 		//CONS_Debug(DBG_NIGHTS, "T2 is at %d, %d\n", transfer2->x>>FRACBITS, transfer2->y>>FRACBITS);
6473 		//CONS_Debug(DBG_NIGHTS, "Distance from T1: %d\n", P_AproxDistance(transfer1->x - player->mo->x, transfer1->y - player->mo->y)>>FRACBITS);
6474 		//CONS_Debug(DBG_NIGHTS, "Distance from T2: %d\n", P_AproxDistance(transfer2->x - player->mo->x, transfer2->y - player->mo->y)>>FRACBITS);
6475 
6476 		// Transfer1 is closer to the player than transfer2
6477 		if (P_AproxDistance(transfer1->x - player->mo->x, transfer1->y - player->mo->y)>>FRACBITS
6478 			< P_AproxDistance(transfer2->x - player->mo->x, transfer2->y - player->mo->y)>>FRACBITS)
6479 		{
6480 			//CONS_Debug(DBG_NIGHTS, " must be < 0 to transfer\n");
6481 
6482 			if (transfer1->type == MT_AXISTRANSFERLINE)
6483 			{
6484 				if (transfer1last)
6485 					axis = P_FindAxis(transfer1->threshold, transfer1->health-2);
6486 				else if (player->pflags & PF_TRANSFERTOCLOSEST)
6487 					axis = P_FindAxis(transfer1->threshold, transfer1->health-1);
6488 				else
6489 					axis = P_FindAxis(transfer1->threshold, transfer1->health);
6490 
6491 				if (!axis)
6492 				{
6493 					CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #1\n");
6494 					return;
6495 				}
6496 
6497 				//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
6498 
6499 				transfer1line.v1->x = axis->x;
6500 				transfer1line.v1->y = axis->y;
6501 
6502 				transfer1line.v2->x = transfer1->x;
6503 				transfer1line.v2->y = transfer1->y;
6504 
6505 				if (cv_debug & DBG_NIGHTS)
6506 					P_ShootLine(axis, transfer1, player->mo->z);
6507 
6508 				//CONS_Debug(DBG_NIGHTS, "closest %d\n", transfer1->health);
6509 
6510 				transfer1line.dx = transfer1line.v2->x - transfer1line.v1->x;
6511 				transfer1line.dy = transfer1line.v2->y - transfer1line.v1->y;
6512 
6513 				if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer1line)
6514 						!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer1line)
6515 						&& truexspeed < 0)
6516 				{
6517 					if (cv_debug & DBG_NIGHTS)
6518 					{
6519 						HU_SetCEchoDuration(1);
6520 						HU_DoCEcho("transfer!");
6521 						HU_SetCEchoDuration(5);
6522 						S_StartSound(NULL, sfx_strpst);
6523 					}
6524 					if (player->pflags & PF_TRANSFERTOCLOSEST)
6525 					{
6526 						player->pflags &= ~PF_TRANSFERTOCLOSEST;
6527 						P_TransferToAxis(player, transfer1->health - 1);
6528 					}
6529 					else
6530 					{
6531 						player->pflags |= PF_TRANSFERTOCLOSEST;
6532 						P_SetTarget(&player->axis2, transfer1);
6533 						P_SetTarget(&player->axis1, P_FindAxisTransfer(transfer1->threshold, transfer1->health-1, MT_AXISTRANSFERLINE));//P_FindAxis(transfer1->threshold, axis->health-2);
6534 					}
6535 				}
6536 			}
6537 			else
6538 			{
6539 				// Transfer1
6540 				if (transfer1last)
6541 					axis = P_FindAxis(transfer1->threshold, 1);
6542 				else
6543 					axis = P_FindAxis(transfer1->threshold, transfer1->health);
6544 
6545 				if (!axis)
6546 				{
6547 					CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #2\n");
6548 					return;
6549 				}
6550 
6551 				//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
6552 
6553 				transfer1line.v1->x = axis->x;
6554 				transfer1line.v1->y = axis->y;
6555 
6556 				if (cv_debug & DBG_NIGHTS)
6557 					P_ShootLine(transfer1, P_FindAxis(transfer1->threshold, transfer1->health-1), player->mo->z);
6558 
6559 				//axis = P_FindAxis(transfer1->threshold, transfer1->health-1);
6560 
6561 				//CONS_Debug(DBG_NIGHTS, "%d\n", axis->health);
6562 
6563 				transfer1line.v2->x = transfer1->x;
6564 				transfer1line.v2->y = transfer1->y;
6565 
6566 				transfer1line.dx = transfer1line.v2->x - transfer1line.v1->x;
6567 				transfer1line.dy = transfer1line.v2->y - transfer1line.v1->y;
6568 
6569 				if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer1line)
6570 					!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer1line)
6571 						&& truexspeed < 0)
6572 				{
6573 					if (cv_debug & DBG_NIGHTS)
6574 					{
6575 						HU_SetCEchoDuration(1);
6576 						HU_DoCEcho("transfer!");
6577 						HU_SetCEchoDuration(5);
6578 						S_StartSound(NULL, sfx_strpst);
6579 					}
6580 					if (player->mo->target->health < transfer1->health)
6581 					{
6582 						// Find the next axis with a ->health
6583 						// +1 from the current axis.
6584 						if (transfer1last)
6585 							P_TransferToAxis(player, transfer1->health - 1);
6586 						else
6587 							P_TransferToAxis(player, transfer1->health);
6588 					}
6589 					else if (player->mo->target->health >= transfer1->health)
6590 					{
6591 						// Find the next axis with a ->health
6592 						// -1 from the current axis.
6593 						P_TransferToAxis(player, transfer1->health - 1);
6594 					}
6595 				}
6596 			}
6597 		}
6598 		else
6599 		{
6600 			//CONS_Debug(DBG_NIGHTS, " must be > 0 to transfer\n");
6601 			if (transfer2->type == MT_AXISTRANSFERLINE)
6602 			{
6603 				if (transfer2last)
6604 					axis = P_FindAxis(transfer2->threshold, 1);
6605 				else if (player->pflags & PF_TRANSFERTOCLOSEST)
6606 					axis = P_FindAxis(transfer2->threshold, transfer2->health);
6607 				else
6608 					axis = P_FindAxis(transfer2->threshold, transfer2->health - 1);
6609 
6610 				if (!axis)
6611 					axis = P_FindAxis(transfer2->threshold, 1);
6612 
6613 				if (!axis)
6614 				{
6615 					CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #3\n");
6616 					return;
6617 				}
6618 
6619 				//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
6620 
6621 				transfer2line.v1->x = axis->x;
6622 				transfer2line.v1->y = axis->y;
6623 
6624 				transfer2line.v2->x = transfer2->x;
6625 				transfer2line.v2->y = transfer2->y;
6626 
6627 				//CONS_Debug(DBG_NIGHTS, "closest %d\n", transfer2->health);
6628 
6629 				if (cv_debug & DBG_NIGHTS)
6630 					P_ShootLine(axis, transfer2, player->mo->z);
6631 
6632 				transfer2line.dx = transfer2line.v2->x - transfer2line.v1->x;
6633 				transfer2line.dy = transfer2line.v2->y - transfer2line.v1->y;
6634 
6635 				if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer2line)
6636 						!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer2line)
6637 						&& truexspeed > 0)
6638 				{
6639 					if (cv_debug & DBG_NIGHTS)
6640 					{
6641 						HU_SetCEchoDuration(1);
6642 						HU_DoCEcho("transfer!");
6643 						HU_SetCEchoDuration(5);
6644 						S_StartSound(NULL, sfx_strpst);
6645 					}
6646 					if (player->pflags & PF_TRANSFERTOCLOSEST)
6647 					{
6648 						player->pflags &= ~PF_TRANSFERTOCLOSEST;
6649 
6650 						if (!P_FindAxis(transfer2->threshold, transfer2->health))
6651 							transfer2last = true;
6652 
6653 						if (transfer2last)
6654 							P_TransferToAxis(player, 1);
6655 						else
6656 							P_TransferToAxis(player, transfer2->health);
6657 					}
6658 					else
6659 					{
6660 						player->pflags |= PF_TRANSFERTOCLOSEST;
6661 						P_SetTarget(&player->axis1, transfer2);
6662 						P_SetTarget(&player->axis2, P_FindAxisTransfer(transfer2->threshold, transfer2->health+1, MT_AXISTRANSFERLINE));//P_FindAxis(transfer2->threshold, axis->health + 2);
6663 					}
6664 				}
6665 			}
6666 			else
6667 			{
6668 				// Transfer2
6669 				if (transfer2last)
6670 					axis = P_FindAxis(transfer2->threshold, 1);
6671 				else
6672 					axis = P_FindAxis(transfer2->threshold, transfer2->health);
6673 
6674 				if (!axis)
6675 					axis = P_FindAxis(transfer2->threshold, 1);
6676 
6677 				if (!axis)
6678 				{
6679 					CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #4\n");
6680 					return;
6681 				}
6682 
6683 				//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
6684 
6685 				transfer2line.v1->x = axis->x;
6686 				transfer2line.v1->y = axis->y;
6687 
6688 				if (cv_debug & DBG_NIGHTS)
6689 					P_ShootLine(transfer2, P_FindAxis(transfer2->threshold, transfer2->health-1), player->mo->z);
6690 
6691 				//axis = P_FindAxis(transfer2->threshold, transfer2->health-1);
6692 
6693 				//CONS_Debug(DBG_NIGHTS, "%d\n", axis->health);
6694 
6695 				transfer2line.v2->x = transfer2->x;
6696 				transfer2line.v2->y = transfer2->y;
6697 
6698 				transfer2line.dx = transfer2line.v2->x - transfer2line.v1->x;
6699 				transfer2line.dy = transfer2line.v2->y - transfer2line.v1->y;
6700 
6701 				if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer2line)
6702 					!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer2line)
6703 						&& truexspeed > 0)
6704 				{
6705 					if (cv_debug & DBG_NIGHTS)
6706 					{
6707 						HU_SetCEchoDuration(1);
6708 						HU_DoCEcho("transfer!");
6709 						HU_SetCEchoDuration(5);
6710 						S_StartSound(NULL, sfx_strpst);
6711 					}
6712 					if (player->mo->target->health < transfer2->health)
6713 					{
6714 						if (!P_FindAxis(transfer2->threshold, transfer2->health))
6715 							transfer2last = true;
6716 
6717 						if (transfer2last)
6718 							P_TransferToAxis(player, 1);
6719 						else
6720 							P_TransferToAxis(player, transfer2->health);
6721 					}
6722 					else if (player->mo->target->health >= transfer2->health)
6723 						P_TransferToAxis(player, transfer2->health - 1);
6724 				}
6725 			}
6726 		}
6727 	}
6728 }
6729 
6730 //
6731 // P_DoNiGHTSCapsule
6732 //
6733 // Handles blowing up the Ideya (emerald) capsule for NiGHTS mode
6734 //
P_DoNiGHTSCapsule(player_t * player)6735 static void P_DoNiGHTSCapsule(player_t *player)
6736 {
6737 	INT32 i, spherecount, totalduration, popduration, deductinterval, deductquantity, sphereresult, firstpoptic, startingspheres;
6738 	INT32 tictimer = ++player->capsule->extravalue2;
6739 
6740 	if (abs(player->mo->x-player->capsule->x) <= 3*FRACUNIT)
6741 	{
6742 		P_UnsetThingPosition(player->mo);
6743 		player->mo->x = player->capsule->x;
6744 		P_SetThingPosition(player->mo);
6745 		player->mo->momx = 0;
6746 	}
6747 
6748 	if (abs(player->mo->y-player->capsule->y) <= 3*FRACUNIT)
6749 	{
6750 		P_UnsetThingPosition(player->mo);
6751 		player->mo->y = player->capsule->y;
6752 		P_SetThingPosition(player->mo);
6753 		player->mo->momy = 0;
6754 	}
6755 
6756 	if (abs(player->mo->z - (player->capsule->z+(player->capsule->height/3))) <= 3*FRACUNIT)
6757 	{
6758 		player->mo->z = player->capsule->z+(player->capsule->height/3);
6759 		player->mo->momz = 0;
6760 	}
6761 
6762 	if (player->mo->x > player->capsule->x)
6763 		player->mo->momx = -3*FRACUNIT;
6764 	else if (player->mo->x < player->capsule->x)
6765 		player->mo->momx = 3*FRACUNIT;
6766 
6767 	if (player->mo->y > player->capsule->y)
6768 		player->mo->momy = -3*FRACUNIT;
6769 	else if (player->mo->y < player->capsule->y)
6770 		player->mo->momy = 3*FRACUNIT;
6771 
6772 	if (player->mo->z > player->capsule->z+(player->capsule->height/3))
6773 		player->mo->momz = -3*FRACUNIT;
6774 	else if (player->mo->z < player->capsule->z+(player->capsule->height/3))
6775 		player->mo->momz = 3*FRACUNIT;
6776 
6777 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
6778 	{
6779 		if (player->mo->momx || player->mo->momy || player->mo->momz)
6780 		{
6781 			if (player->mo->state != &states[S_PLAY_NIGHTS_PULL])
6782 				P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_PULL);
6783 		}
6784 		else if (player->mo->state != &states[S_PLAY_NIGHTS_ATTACK])
6785 		{
6786 			S_StartSound(player->mo, sfx_spin);
6787 			P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_ATTACK);
6788 		}
6789 	}
6790 	else
6791 	{
6792 		if (!(player->pflags & PF_JUMPED) && !(player->pflags & PF_SPINNING))
6793 			player->pflags |= PF_JUMPED;
6794 		if (player->panim != PA_ROLL)
6795 			P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
6796 	}
6797 
6798 	if (!(player->charflags & SF_NONIGHTSROTATION))
6799 	{
6800 		if ((player->mo->state == &states[S_PLAY_NIGHTS_PULL])
6801 		&& (player->mo->sprite2 == SPR2_NPUL))
6802 			player->mo->rollangle -= ANG30;
6803 		else
6804 			player->mo->rollangle = 0;
6805 	}
6806 
6807 	if (G_IsSpecialStage(gamemap))
6808 	{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
6809 		for (i = 0; i < MAXPLAYERS; i++)
6810 			if (playeringame[i] && (&players[i] != player) && players[i].spheres > 0)
6811 			{
6812 				player->spheres += players[i].spheres;
6813 				players[i].spheres = 0;
6814 			}
6815 	}
6816 
6817 	if (player->capsule->extravalue2 <= 0 && player->capsule->health > 0)
6818 		P_RunNightsCapsuleTouchExecutors(player->mo, true, player->spheres >= player->capsule->health); // run capsule entrance executors
6819 
6820 	// Time to blow it up!
6821 	if (player->mo->x == player->capsule->x
6822 		&& player->mo->y == player->capsule->y
6823 		&& player->mo->z == player->capsule->z+(player->capsule->height/3))
6824 	{
6825 		if (player->capsule->lastlook < 0)
6826 		{
6827 			// Stretch the sphere deduction across the capsule time!
6828 			// 1. Force the remaining capsule time to `popduration`
6829 			// 2. Given `popduration` and `spherecount`, at what tic interval do we deduct spheres? `deductinterval`
6830 			// 3. And on each deduction, how many spheres do we deduct? `deductquantity`
6831 			// 4. Store the expected capsule health upon completion: `sphereresult`
6832 			spherecount = min(player->spheres, player->capsule->health);
6833 			totalduration = min(40 + spherecount, 60);
6834 
6835 			popduration = player->capsule->extravalue1 = max(totalduration - tictimer, 1);
6836 			deductinterval = player->capsule->cvmem = max(FixedFloor(FixedDiv(popduration, spherecount))/FRACUNIT, 1);
6837 			deductquantity = player->capsule->cusval = max(FixedRound(FixedDiv(spherecount, popduration))/FRACUNIT, 1);
6838 			sphereresult = player->capsule->movecount = player->capsule->health - spherecount;
6839 			firstpoptic = player->capsule->lastlook = tictimer;
6840 		}
6841 		else
6842 		{
6843 			popduration = player->capsule->extravalue1;
6844 			deductinterval = player->capsule->cvmem;
6845 			deductquantity = player->capsule->cusval;
6846 			sphereresult = player->capsule->movecount;
6847 			firstpoptic = player->capsule->lastlook;
6848 		}
6849 
6850 		if (tictimer - firstpoptic < popduration)
6851 		{
6852 			if (!((tictimer - firstpoptic) % deductinterval))
6853 			{
6854 				// Did you somehow get more spheres during destruct?
6855 				if (player->capsule->health <= sphereresult && player->spheres > 0 && player->capsule->health > 0)
6856 					sphereresult = max(sphereresult - player->spheres, 0);
6857 
6858 				if (player->capsule->health > sphereresult && player->spheres > 0)
6859 				{
6860 					player->spheres -= deductquantity;
6861 					player->capsule->health -= deductquantity;
6862 				}
6863 
6864 				if (player->spheres < 0)
6865 					player->spheres = 0;
6866 
6867 				if (player->capsule->health < sphereresult)
6868 					player->capsule->health = sphereresult;
6869 			}
6870 
6871 			// Spawn a 'pop' for every 2 tics
6872 			if (!((tictimer - firstpoptic) % 2))
6873 				S_StartSound(P_SpawnMobj(player->capsule->x + ((P_SignedRandom()/2)<<FRACBITS),
6874 				player->capsule->y + ((P_SignedRandom()/2)<<FRACBITS),
6875 				player->capsule->z + (player->capsule->height/2) + ((P_SignedRandom()/2)<<FRACBITS),
6876 				MT_SONIC3KBOSSEXPLODE),sfx_s3kb4);
6877 		}
6878 		else
6879 		{
6880 			if (player->spheres != 0 && player->capsule->health > 0)
6881 			{
6882 				if (player->spheres < player->capsule->health)
6883 				{
6884 					player->capsule->health -= player->spheres;
6885 					player->spheres = 0;
6886 				}
6887 				else
6888 				{
6889 					startingspheres = player->spheres - player->capsule->health;
6890 					player->capsule->health = 0;
6891 					player->spheres = startingspheres;
6892 				}
6893 			}
6894 
6895 			if (player->capsule->health <= 0)
6896 			{
6897 				player->capsule->flags &= ~MF_NOGRAVITY;
6898 				player->capsule->momz = 5*FRACUNIT;
6899 				player->capsule->reactiontime = 0;
6900 				tictimer = -1;
6901 
6902 				for (i = 0; i < MAXPLAYERS; i++)
6903 					if (playeringame[i] && !player->exiting && players[i].mare == player->mare)
6904 					{
6905 						players[i].bonustime = true;
6906 						players[i].texttimer = 4*TICRATE;
6907 						players[i].textvar = 1; // Time Bonus
6908 						players[i].finishedtime = players[i].nightstime;
6909 						if (!G_IsSpecialStage(gamemap))
6910 							P_AddPlayerScore(&players[i], (players[i].finishedtime/TICRATE) * 100);
6911 						P_FlashPal(&players[i], PAL_WHITE, 8);
6912 					}
6913 
6914 				if (G_IsSpecialStage(gamemap))
6915 				{
6916 					tic_t lowest_time;
6917 
6918 					/*for (i = 0; i < MAXPLAYERS; i++)
6919 					{
6920 						if (!playeringame[i] || players[i].spectator || !players[i].mo || !players[i].mo->tracer)
6921 							continue;
6922 
6923 						emmo = P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z + players[i].mo->info->height, MT_GOTEMERALD);
6924 						P_SetTarget(&emmo->target, players[i].mo);
6925 						P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
6926 					}*/
6927 
6928 					if (player->powers[pw_carry] == CR_NIGHTSMODE)
6929 					{
6930 						// The Chaos Emerald begins to orbit us!
6931 						UINT8 em = P_GetNextEmerald();
6932 						// Only give it to ONE person, and THAT player has to get to the goal!
6933 						mobj_t *emmo = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD);
6934 						emmo->health = em; // for identification
6935 						P_SetTarget(&emmo->target, player->mo);
6936 						P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
6937 						P_SetTarget(&player->mo->tracer, emmo);
6938 					}
6939 
6940 					// Okay, we're doing this down here because we're handling time weirdly for co-op special stages
6941 					// and because P_AddPlayerScore gives score to everyone in co-op special stages.
6942 					// Find the player with the lowest time remaining and award points based on that time instead.
6943 					lowest_time = player->finishedtime;
6944 					for (i = 0; i < MAXPLAYERS; i++)
6945 						if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE)
6946 							if (players[i].finishedtime < lowest_time)
6947 								lowest_time = players[i].finishedtime;
6948 					P_AddPlayerScore(player, (lowest_time/TICRATE) * 100);
6949 				}
6950 				else
6951 				{
6952 					/*for (i = 0; i < 16; i++)
6953 					{
6954 						mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true, 0);
6955 						flicky->z += player->capsule->height/2;
6956 						flicky->angle = (i*(ANGLE_MAX/16));
6957 						P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT);
6958 					}*/
6959 					mobj_t *idya = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD);
6960 					idya->extravalue2 = player->mare/5;
6961 					idya->health = player->mare + 1; // for identification
6962 					P_SetTarget(&idya->target, player->mo);
6963 					P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5));
6964 
6965 					if (player->mo->tracer)
6966 					{
6967 						P_SetTarget(&idya->hnext, player->mo->tracer);
6968 						idya->extravalue1 = (angle_t)(player->mo->tracer->extravalue1 - 72*ANG1);
6969 						if (idya->extravalue1 > player->mo->tracer->extravalue1)
6970 							idya->extravalue1 -= (72*ANG1)/idya->extravalue1;
6971 					}
6972 					P_SetTarget(&player->mo->tracer, idya);
6973 				}
6974 				for (i = 0; i < MAXPLAYERS; i++)
6975 					if (playeringame[i] && players[i].mare == player->mare)
6976 						P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead!
6977 				S_StartScreamSound(player->mo, sfx_ngdone);
6978 				P_SwitchSpheresBonusMode(true);
6979 				P_RunNightsCapsuleTouchExecutors(player->mo, false, true); // run capsule exit executors, and we destroyed it
6980 			}
6981 			else
6982 			{
6983 				S_StartScreamSound(player->mo, sfx_lose);
6984 				player->texttimer = 4*TICRATE;
6985 				player->textvar = 3; // Get more rings!
6986 				player->capsule->reactiontime = 0;
6987 				player->capsule->extravalue1 = player->capsule->cvmem =\
6988 				 player->capsule->cusval = player->capsule->movecount =\
6989 				 player->capsule->lastlook = player->capsule->extravalue2 = -1;
6990 				P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked rings
6991 			}
6992 		}
6993 	}
6994 	else if (player->capsule->lastlook > -1)
6995 		// We somehow moved out of the capsule (OBJECTPLACE?)
6996 		// So recalculate all the timings
6997 		player->capsule->lastlook = player->capsule->extravalue2 = -1;
6998 }
6999 
7000 //
7001 // P_MoveNiGHTSToDrone
7002 //
7003 // Pull NiGHTS to the drone during Nightserizing
7004 //
P_MoveNiGHTSToDrone(player_t * player)7005 static void P_MoveNiGHTSToDrone(player_t *player)
7006 {
7007 	boolean flip, topaligned, middlealigned, bottomoffsetted;
7008 	fixed_t droneboxmandiff, zofs;
7009 
7010 	if (!player->drone)
7011 		return;
7012 
7013 	flip = player->drone->flags2 & MF2_OBJECTFLIP;
7014 	topaligned = (player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
7015 	middlealigned = (player->drone->flags & MF_GRENADEBOUNCE) && !(player->drone->flags & MF_SLIDEME);
7016 	bottomoffsetted = !(player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
7017 	droneboxmandiff = max(player->drone->height - player->mo->height, 0);
7018 
7019 	if (!flip)
7020 	{
7021 		if (topaligned)
7022 			zofs = droneboxmandiff;
7023 		else if (middlealigned)
7024 			zofs = droneboxmandiff / 2;
7025 		else if (bottomoffsetted)
7026 			zofs = FixedMul(24*FRACUNIT, player->drone->scale);
7027 		else
7028 			zofs = 0;
7029 	}
7030 	else
7031 	{
7032 		if (topaligned)
7033 			zofs = 0;
7034 		else if (middlealigned)
7035 			zofs = droneboxmandiff / 2;
7036 		else if (bottomoffsetted)
7037 			zofs = droneboxmandiff - FixedMul(24*FRACUNIT, player->drone->scale);
7038 		else
7039 			zofs = droneboxmandiff;
7040 	}
7041 
7042 	player->mo->momx = player->mo->momy = player->mo->momz = 0;
7043 	P_TeleportMove(player->mo, player->drone->x, player->drone->y, player->drone->z + zofs);
7044 	P_SetTarget(&player->drone, NULL);
7045 }
7046 
7047 //
7048 // P_NiGHTSMovement
7049 //
7050 // Movement code for NiGHTS!
7051 //
P_NiGHTSMovement(player_t * player)7052 static void P_NiGHTSMovement(player_t *player)
7053 {
7054 	fixed_t drillamt = 0;
7055 	boolean still = false, moved = false, backwardaxis = false, firstdrill;
7056 	INT16 newangle = 0;
7057 	fixed_t xspeed, yspeed;
7058 	thinker_t *th;
7059 	mobj_t *mo2;
7060 	mobj_t *closestaxis = NULL;
7061 	fixed_t newx, newy, radius;
7062 	angle_t movingangle;
7063 	ticcmd_t *cmd = &player->cmd;
7064 	INT32 thrustfactor;
7065 	INT32 i;
7066 	statenum_t flystate;
7067 	UINT16 visangle;
7068 	angle_t rollangle = 0;
7069 
7070 	player->pflags &= ~PF_DRILLING;
7071 
7072 	firstdrill = false;
7073 
7074 	if (player->drillmeter > 96*20)
7075 		player->drillmeter = 96*20;
7076 
7077 	if (player->drilldelay)
7078 		player->drilldelay--;
7079 
7080 	if (!(cmd->buttons & BT_JUMP))
7081 	{
7082 		// Always have just a TINY bit of drill power.
7083 		if (player->drillmeter <= 0)
7084 			player->drillmeter = TICRATE/10;
7085 	}
7086 
7087 	if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
7088 	{
7089 		P_DeNightserizePlayer(player);
7090 		return;
7091 	}
7092 
7093 	if (G_IsSpecialStage(gamemap))
7094 	{
7095 		boolean capsule = false;
7096 		// NiGHTS special stages have a pseudo-shared timer, so check if ANYONE is feeding the capsule.
7097 		for (i = 0; i < MAXPLAYERS; i++)
7098 			if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/
7099 			&& (players[i].capsule && players[i].capsule->reactiontime))
7100 				capsule = true;
7101 		if (!capsule
7102 		&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
7103 			&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
7104 		&& !player->exiting)
7105 			player->nightstime--;
7106 	}
7107 	else if (!(gametyperules & GTR_RACE)
7108 	&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
7109 			&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
7110 	&& !(player->capsule && player->capsule->reactiontime)
7111 	&& !player->exiting)
7112 		player->nightstime--;
7113 
7114 	if (!player->nightstime)
7115 	{
7116 		P_DeNightserizePlayer(player);
7117 		S_StartScreamSound(player->mo, sfx_s3k66);
7118 		return;
7119 	}
7120 	else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE)
7121 	{
7122 		if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
7123 		{
7124 			S_FadeMusic(0, 10*MUSICRATE);
7125 			S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS.
7126 		}
7127 		else
7128 			P_PlayJingle(player, ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? JT_NIGHTSTIMEOUT : JT_SSTIMEOUT);
7129 	}
7130 
7131 	if (player->mo->z < player->mo->floorz)
7132 		player->mo->z = player->mo->floorz;
7133 
7134 	if (player->mo->z+player->mo->height > player->mo->ceilingz)
7135 		player->mo->z = player->mo->ceilingz - player->mo->height;
7136 
7137 	newx = P_ReturnThrustX(player->mo, player->mo->angle, 3*FRACUNIT)+player->mo->x;
7138 	newy = P_ReturnThrustY(player->mo, player->mo->angle, 3*FRACUNIT)+player->mo->y;
7139 
7140 	if (!player->mo->target)
7141 	{
7142 		fixed_t dist1, dist2 = 0;
7143 
7144 		// scan the thinkers
7145 		// to find the closest axis point
7146 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
7147 		{
7148 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
7149 				continue;
7150 
7151 			mo2 = (mobj_t *)th;
7152 
7153 			if (mo2->type != MT_AXIS)
7154 				continue;
7155 			if (mo2->threshold != player->mare)
7156 				continue;
7157 
7158 			if (closestaxis == NULL)
7159 			{
7160 				closestaxis = mo2;
7161 				dist2 = R_PointToDist2(newx, newy, mo2->x, mo2->y) - mo2->radius;
7162 			}
7163 			else
7164 			{
7165 				dist1 = R_PointToDist2(newx, newy, mo2->x, mo2->y) - mo2->radius;
7166 
7167 				if (dist1 < dist2)
7168 				{
7169 					closestaxis = mo2;
7170 					dist2 = dist1;
7171 				}
7172 			}
7173 		}
7174 		P_SetTarget(&player->mo->target, closestaxis);
7175 	}
7176 
7177 	if (!player->mo->target) // Uh-oh!
7178 	{
7179 		CONS_Debug(DBG_NIGHTS, "No axis points found!\n");
7180 		return;
7181 	}
7182 
7183 	// The 'ambush' flag says you should rotate
7184 	// the other way around the axis.
7185 	if (player->mo->target->flags2 & MF2_AMBUSH)
7186 		backwardaxis = true;
7187 
7188 	player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
7189 
7190 	player->old_angle_pos = player->angle_pos;
7191 
7192 	radius = player->mo->target->radius;
7193 
7194 	player->mo->flags |= MF_NOGRAVITY;
7195 
7196 	if (player->mo->eflags & MFE_VERTICALFLIP)
7197 		cmd->forwardmove = (SINT8)(-cmd->forwardmove);
7198 
7199 	if (!(player->pflags & PF_TRANSFERTOCLOSEST))
7200 	{
7201 		fixed_t realdist = R_PointToDist2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
7202 		// teleport player to correct radius if neccessary
7203 		if (realdist>>FRACBITS != radius>>FRACBITS)
7204 		{
7205 			CONS_Debug(DBG_NIGHTS, "Aligning player with axis\n");
7206 			P_UnsetThingPosition(player->mo);
7207 			if (realdist == 0) // other method won't work if we're exactly on the target lol
7208 			{
7209 				const angle_t fa = player->old_angle_pos>>ANGLETOFINESHIFT;
7210 				player->mo->x = player->mo->target->x + FixedMul(FINECOSINE(fa), radius);
7211 				player->mo->y = player->mo->target->y + FixedMul(FINESINE(fa), radius);
7212 			}
7213 			else
7214 			{
7215 				player->mo->x = player->mo->target->x + FixedMul(FixedDiv(player->mo->x - player->mo->target->x, realdist), radius);
7216 				player->mo->y = player->mo->target->y + FixedMul(FixedDiv(player->mo->y - player->mo->target->y, realdist), radius);
7217 			}
7218 			P_SetThingPosition(player->mo);
7219 		}
7220 	}
7221 
7222 	// Currently reeling from being hit.
7223 	if (player->powers[pw_flashing] > (2*flashingtics)/3)
7224 	{
7225 		{
7226 			const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
7227 			const fixed_t speed = FixedDiv(player->speed*FRACUNIT,50*FRACUNIT);
7228 
7229 			xspeed = FixedMul(FINECOSINE(fa),speed);
7230 			yspeed = FixedMul(FINESINE(fa),speed);
7231 		}
7232 
7233 		if (!(player->pflags & PF_TRANSFERTOCLOSEST))
7234 		{
7235 			xspeed = FixedMul(xspeed, FixedDiv(1024*FRACUNIT, player->mo->target->radius));
7236 
7237 			if (backwardaxis)
7238 				xspeed *= -1;
7239 
7240 			player->angle_pos += FixedAngleC(FixedDiv(xspeed,5*FRACUNIT),40*FRACUNIT);
7241 		}
7242 
7243 		player->mo->momz = 0;
7244 
7245 		P_NightsTransferPoints(player, xspeed, radius);
7246 		return;
7247 	}
7248 
7249 	if (player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
7250 		&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
7251 	{
7252 		player->mo->momx = player->mo->momy = player->mo->momz = 0;
7253 		player->mo->rollangle = 0;
7254 		return;
7255 	}
7256 
7257 	if (player->exiting > 0) // && player->exiting < 2*TICRATE)
7258 	{
7259 		player->mo->momx = player->mo->momy = 0;
7260 
7261 		if (!(gametyperules & GTR_RACE))
7262 			P_SetObjectMomZ(player->mo, FRACUNIT/2, (P_MobjFlip(player->mo)*player->mo->momz >= 0));
7263 		else
7264 			player->mo->momz = 0;
7265 
7266 #if 0//def ROTSPRITE
7267 		if (!(player->charflags & SF_NONIGHTSROTATION) && player->mo->momz)
7268 		{
7269 			if (player->mo->state != &states[S_PLAY_NIGHTS_DRILL])
7270 				P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_DRILL);
7271 			player->mo->rollangle = ANGLE_90;
7272 		}
7273 		else
7274 #endif
7275 		{
7276 			if (player->mo->state != &states[S_PLAY_NIGHTS_FLOAT])
7277 				P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_FLOAT);
7278 			player->drawangle += ANGLE_22h;
7279 		}
7280 
7281 		player->mo->flags |= MF_NOCLIPHEIGHT;
7282 		return;
7283 	}
7284 
7285 	// Spawn the little sparkles on each side of the player.
7286 	if (leveltime & 1)
7287 	{
7288 		mobj_t *firstmobj;
7289 		mobj_t *secondmobj;
7290 		fixed_t spawndist = FixedMul(16*FRACUNIT, player->mo->scale);
7291 		fixed_t z = player->mo->z + player->mo->height/2;
7292 
7293 		if (player->mo->eflags & MFE_VERTICALFLIP)
7294 			z -= FixedMul(mobjinfo[MT_NIGHTSPARKLE].height, player->mo->scale);
7295 
7296 		firstmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle+ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle+ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
7297 		secondmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle-ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle-ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
7298 
7299 		firstmobj->destscale = secondmobj->destscale = player->mo->scale;
7300 		P_SetTarget(&firstmobj->target, player->mo);
7301 		P_SetScale(firstmobj, player->mo->scale);
7302 		P_SetTarget(&secondmobj->target, player->mo);
7303 		P_SetScale(secondmobj, player->mo->scale);
7304 
7305 		// Superloop turns sparkles red
7306 		if (player->powers[pw_nights_superloop])
7307 		{
7308 			P_SetMobjState(firstmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
7309 			P_SetMobjState(secondmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
7310 		}
7311 	}
7312 
7313 	// Paraloop helper is now separate from sparkles
7314 	// It also spawns every tic to avoid failed paraloops
7315 	{
7316 		mobj_t *helpermobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_NIGHTSLOOPHELPER);
7317 		helpermobj->fuse = player->mo->fuse = leveltime;
7318 		P_SetTarget(&helpermobj->target, player->mo);
7319 		P_SetScale(helpermobj, player->mo->scale);
7320 	}
7321 
7322 	if (player->bumpertime)
7323 	{
7324 		player->pflags |= (PF_STARTJUMP|PF_DRILLING);
7325 		newangle = (INT16)player->flyangle;
7326 	}
7327 	else if (cmd->buttons & BT_JUMP && player->drillmeter && player->drilldelay == 0)
7328 	{
7329 		if (!(player->pflags & PF_STARTJUMP))
7330 			firstdrill = true;
7331 
7332 		player->pflags |= (PF_STARTJUMP|PF_DRILLING);
7333 	}
7334 	else
7335 	{
7336 		player->pflags &= ~PF_STARTJUMP;
7337 
7338 		if (cmd->sidemove != 0)
7339 			moved = true;
7340 
7341 		if (player->drillmeter & 1)
7342 			player->drillmeter++; // I'll be nice and give them one.
7343 	}
7344 
7345 	if (cmd->forwardmove != 0)
7346 		moved = true;
7347 
7348 	if (!player->bumpertime)
7349 	{
7350 		if (moved)
7351 		{
7352 			if (player->pflags & PF_DRILLING)
7353 				drillamt += 100*FRACUNIT;
7354 			else
7355 			{
7356 				const fixed_t fforward = abs(cmd->forwardmove)*FRACUNIT;
7357 				const fixed_t fside = abs(cmd->sidemove)*FRACUNIT;
7358 				const fixed_t dist = FixedHypot(fforward, fside);
7359 
7360 				drillamt += dist > 50*FRACUNIT ? 50*FRACUNIT : dist;
7361 
7362 				// Accel up from 50 * 2.5
7363 				drillamt = FixedMul(drillamt, 5*FRACUNIT/2);
7364 			}
7365 			if ((player->pflags & PF_DRILLING && player->speed < MAXDRILLSPEED) || player->speed < MAXNORMALSPEED)
7366 				player->speed += FixedInt(drillamt);
7367 		}
7368 
7369 		if (player->pflags & PF_DRILLING)
7370 		{
7371 			if (player->speed < MAXDRILLSPEED)
7372 				player->speed += 150;
7373 
7374 			if (--player->drillmeter == 0)
7375 				player->drilldelay = TICRATE*2;
7376 		}
7377 
7378 		if (player->speed < 0)
7379 			player->speed = 0;
7380 
7381 		if (!cmd->forwardmove)
7382 		{
7383 			if (cmd->sidemove > 0)
7384 				newangle = 0;
7385 			else if (cmd->sidemove < 0)
7386 				newangle = 180;
7387 		}
7388 		else if (!cmd->sidemove)
7389 		{
7390 			if (cmd->forwardmove > 0)
7391 				newangle = 90;
7392 			else if (cmd->forwardmove < 0)
7393 				newangle = 270;
7394 		}
7395 		else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axises.
7396 			newangle = (INT16)FixedInt(AngleFixed(R_PointToAngle2(0,0, cmd->sidemove*FRACUNIT, cmd->forwardmove*FRACUNIT)));
7397 
7398 		newangle -= player->viewrollangle / ANG1;
7399 
7400 		if (newangle < 0 && moved)
7401 			newangle = (INT16)(360+newangle);
7402 	}
7403 
7404 	if (player->pflags & PF_DRILLING)
7405 		thrustfactor = 2;
7406 	else
7407 	{
7408 		thrustfactor = 8;
7409 
7410 		// Decelerate while turning normally.
7411 		if (moved && player->flyangle != newangle && player->speed > 12000)
7412 			player->speed -= 60;
7413 	}
7414 
7415 	for (i = 0; i < thrustfactor; i++)
7416 	{
7417 		if (moved && player->flyangle != newangle)
7418 		{
7419 			INT32 anglediff = (((newangle-player->flyangle)+360)%360);
7420 			INT32 angledif2 = (((player->flyangle-newangle)+360)%360);
7421 
7422 			// player->flyangle is the one to move
7423 			// newangle is the "move to"
7424 			if (anglediff == 0 && angledif2 == 0)
7425 				break;
7426 
7427 			if (anglediff>angledif2)
7428 				player->flyangle--;
7429 			else // if (anglediff<angledif2)
7430 				player->flyangle++;
7431 		}
7432 
7433 		// Buff out negatives, >360 angles...
7434 		player->flyangle = ((player->flyangle + 360) % 360);
7435 	}
7436 
7437 	if (!(player->speed)
7438 		&& cmd->forwardmove == 0)
7439 		still = true;
7440 
7441 	// No more bumper braking
7442 	if (!player->bumpertime
7443 	 && ((cmd->buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)
7444 	  || (cmd->buttons & BT_SPIN)))
7445 	{
7446 		if (!(player->pflags & PF_STARTDASH))
7447 			S_StartSound(player->mo, sfx_ngskid);
7448 
7449 		// You can tap the button to only slow down a bit,
7450 		// or hold it to slow to a crawl as before, your choice.
7451 		if (player->speed > 8000)
7452 			player->speed -= 2000;
7453 		else if (player->speed > 1000)
7454 			player->speed -= (player->speed/4);
7455 		else {
7456 			player->speed -= 60;
7457 			if (player->speed < 0)
7458 			{
7459 				player->speed = 0;
7460 				still = true;
7461 			}
7462 		}
7463 
7464 		player->pflags |= PF_STARTDASH;
7465 	}
7466 	else
7467 		player->pflags &= ~PF_STARTDASH;
7468 
7469 	{
7470 		const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
7471 		const fixed_t speed = FixedDiv(player->speed*FRACUNIT,50*FRACUNIT);
7472 		xspeed = FixedMul(FINECOSINE(fa),speed);
7473 		yspeed = FixedMul(FINESINE(fa),speed);
7474 	}
7475 
7476 	if (!(player->pflags & PF_TRANSFERTOCLOSEST))
7477 	{
7478 		xspeed = FixedMul(xspeed, FixedDiv(1024*FRACUNIT, player->mo->target->radius));
7479 
7480 		if (backwardaxis)
7481 			xspeed *= -1;
7482 
7483 		player->angle_pos += FixedAngleC(FixedDiv(xspeed,5*FRACUNIT),40*FRACUNIT);
7484 	}
7485 
7486 	P_NightsTransferPoints(player, xspeed, radius);
7487 
7488 	if (still)
7489 		player->mo->momz = -FRACUNIT;
7490 	else
7491 		player->mo->momz = yspeed/11;
7492 
7493 	if (player->mo->momz > 20*FRACUNIT)
7494 		player->mo->momz = 20*FRACUNIT;
7495 	else if (player->mo->momz < -20*FRACUNIT)
7496 		player->mo->momz = -20*FRACUNIT;
7497 
7498 	// You can create splashes as you fly across water.
7499 	if (((!(player->mo->eflags & MFE_VERTICALFLIP)
7500 		&& player->mo->z + P_GetPlayerHeight(player) >= player->mo->watertop && player->mo->z <= player->mo->watertop)
7501 	|| (player->mo->eflags & MFE_VERTICALFLIP
7502 		&& player->mo->z + player->mo->height - P_GetPlayerHeight(player) <= player->mo->waterbottom && player->mo->z + player->mo->height >= player->mo->waterbottom))
7503 	&& player->speed > 9000 && leveltime % (TICRATE/7) == 0 && !player->spectator)
7504 	{
7505 		mobjtype_t splishtype = (player->mo->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH;
7506 		mobj_t *water = P_SpawnMobj(player->mo->x, player->mo->y,
7507 			((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[splishtype].height, player->mo->scale) : player->mo->watertop), splishtype);
7508 		if (player->mo->eflags & MFE_GOOWATER)
7509 			S_StartSound(water, sfx_ghit);
7510 		else if (player->mo->eflags & MFE_TOUCHLAVA)
7511 			S_StartSound(water, sfx_splash);
7512 		else
7513 			S_StartSound(water, sfx_wslap);
7514 		if (player->mo->eflags & MFE_VERTICALFLIP)
7515 		{
7516 			water->flags2 |= MF2_OBJECTFLIP;
7517 			water->eflags |= MFE_VERTICALFLIP;
7518 		}
7519 		water->destscale = player->mo->scale;
7520 		P_SetScale(water, player->mo->scale);
7521 	}
7522 
7523 	if (player->mo->momx || player->mo->momy)
7524 	{
7525 		player->mo->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
7526 		player->drawangle = player->mo->angle;
7527 	}
7528 
7529 	if (still)
7530 	{
7531 		player->anotherflyangle = 0;
7532 		movingangle = 0;
7533 	}
7534 	else
7535 	{
7536 		INT32 neg = 1;
7537 
7538 		// Special cases to prevent the angle from being
7539 		// calculated incorrectly when wrapped.
7540 		if (backwardaxis && (player->old_angle_pos > ANG350 && player->angle_pos < ANG10))
7541 			neg = -1;
7542 		else if (backwardaxis ^ (player->old_angle_pos < ANG10 && player->angle_pos > ANG350))
7543 			neg = -1;
7544 		else if (player->angle_pos > player->old_angle_pos)
7545 			neg = -1;
7546 
7547 		movingangle = R_PointToAngle2(0, 0, neg*R_PointToDist2(player->mo->momx, player->mo->momy, 0, 0), player->mo->momz);
7548 		player->anotherflyangle = (movingangle >> ANGLETOFINESHIFT) * 360/FINEANGLES;
7549 	}
7550 
7551 	// NiGHTS flying state
7552 	// Yep, I just ripped out almost 1000 lines of code.
7553 	// (and then toast revamped the entire thing again to be better, but not by much)
7554 	if (still)
7555 		flystate = (P_IsObjectOnGround(player->mo)) ? S_PLAY_NIGHTS_STAND : S_PLAY_NIGHTS_FLOAT;
7556 	else
7557 	{
7558 		flystate = (player->pflags & PF_DRILLING) ? S_PLAY_NIGHTS_DRILL : S_PLAY_NIGHTS_FLY;
7559 		if (player->charflags & SF_NONIGHTSROTATION)
7560 		{
7561 #if 0
7562 			visangle = ((player->anotherflyangle + 7) % 360)/15;
7563 			if (visangle > 18) // Over 270 degrees.
7564 				visangle = 30 - visangle;
7565 			else if (visangle > 12) // Over 180 degrees.
7566 				visangle -= 6;
7567 			else if (visangle > 6) // Over 90 degrees.
7568 				visangle = 12 - visangle;
7569 
7570 			if (player->mo->eflags & MFE_VERTICALFLIP && visangle) // S_PLAY_NIGHTS_FLY0 stays the same, even in reverse gravity
7571 			{
7572 				if (visangle > 6)
7573 					visangle -= 6; // shift to S_PLAY_NIGHTS_FLY1-6
7574 				else
7575 					visangle += 6; // shift to S_PLAY_NIGHTS_FLY7-C
7576 			}
7577 
7578 			flystate += (visangle*2); // S_PLAY_NIGHTS_FLY0-C - the *2 is to skip over drill states
7579 #endif
7580 		}
7581 		else
7582 		{
7583 			angle_t a = R_PointToAngle(player->mo->x, player->mo->y) - player->mo->angle;
7584 			visangle = (player->flyangle % 360);
7585 
7586 			if (player->flyangle >= 90 && player->flyangle <= 270)
7587 			{
7588 				if (player->flyangle == 270 && (a < ANGLE_180))
7589 					;
7590 				else if (player->flyangle == 90 && (a < ANGLE_180))
7591 					;
7592 				else
7593 					visangle += 180;
7594 			}
7595 
7596 			rollangle = FixedAngle(visangle<<FRACBITS);
7597 		}
7598 	}
7599 
7600 	if (player->mo->state != &states[flystate])
7601 		P_SetPlayerMobjState(player->mo, flystate);
7602 
7603 	if (player->charflags & SF_NONIGHTSROTATION)
7604 		player->mo->rollangle = 0;
7605 	else
7606 		player->mo->rollangle = rollangle;
7607 
7608 	P_SetPlayerAngle(player, player->mo->angle);
7609 
7610 	// Check for crushing in our new location
7611 	if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
7612 		&& !(player->mo->flags & MF_NOCLIP))
7613 	{
7614 		// Flashing won't run out until you STOP being crushed.
7615 		if (player->powers[pw_flashing] == 1)
7616 			player->powers[pw_flashing] = 3;
7617 		else
7618 			P_DamageMobj(player->mo, NULL, NULL, 1, 0);
7619 	}
7620 
7621 	if (movingangle >= ANGLE_90 && movingangle <= ANGLE_180)
7622 		movingangle = movingangle - ANGLE_180;
7623 	else if (movingangle >= ANGLE_180 && movingangle <= ANGLE_270)
7624 		movingangle = movingangle - ANGLE_180;
7625 	else if (movingangle >= ANGLE_270)
7626 		movingangle = InvAngle(movingangle);
7627 
7628 	if (player == &players[consoleplayer])
7629 		localaiming = movingangle;
7630 	else if (player == &players[secondarydisplayplayer])
7631 		localaiming2 = movingangle;
7632 
7633 	if ((player->pflags & PF_DRILLING) && !player->bumpertime)
7634 	{
7635 		if (firstdrill)
7636 		{
7637 			S_StartSound(player->mo, sfx_drill1);
7638 			player->drilltimer = 32;
7639 		}
7640 		else if (player->drilltimer == 32)
7641 		{
7642 			// drill mash penalty
7643 			player->drilltimer = 31;
7644 			player->drillmeter -= TICRATE/2;
7645 			if (player->drillmeter <= 0)
7646 				player->drillmeter = TICRATE/10;
7647 		}
7648 		else if (--player->drilltimer == 11)
7649 			// give that drill mash penalty back (after 0.6 seconds)
7650 			player->drillmeter += TICRATE/2;
7651 		else if (player->drilltimer <= 0)
7652 		{
7653 			player->drilltimer = 10;
7654 			S_StartSound(player->mo, sfx_drill2);
7655 		}
7656 	}
7657 
7658 	if (objectplacing)
7659 		OP_NightsObjectplace(player);
7660 }
7661 
7662 // May be used in future for CTF
7663 #if 0
7664 static void P_PlayerDropWeapon(player_t *player)
7665 {
7666 	mobj_t *mo = NULL;
7667 
7668 	if (player->powers[pw_homingring])
7669 	{
7670 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_HOMINGRING);
7671 		player->powers[pw_homingring] = 0;
7672 	}
7673 	else if (player->powers[pw_railring])
7674 	{
7675 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_RAILRING);
7676 		player->powers[pw_railring] = 0;
7677 	}
7678 	else if (player->powers[pw_automaticring])
7679 	{
7680 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_AUTOMATICRING);
7681 		player->powers[pw_automaticring] = 0;
7682 	}
7683 	else if (player->powers[pw_explosionring])
7684 	{
7685 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_EXPLOSIONRING);
7686 		player->powers[pw_explosionring] = 0;
7687 	}
7688 	else if (player->powers[pw_scatterring])
7689 	{
7690 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_SCATTERRING);
7691 		player->powers[pw_scatterring] = 0;
7692 	}
7693 	else if (player->powers[pw_grenadering])
7694 	{
7695 		mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_GRENADERING);
7696 		player->powers[pw_grenadering] = 0;
7697 	}
7698 
7699 	if (mo)
7700 	{
7701 		player->rings--;
7702 		P_InstaThrust(mo, player->mo->angle-ANGLE_180, 8*FRACUNIT);
7703 		P_SetObjectMomZ(mo, 4*FRACUNIT, false);
7704 		mo->flags2 |= MF2_DONTRESPAWN;
7705 		mo->flags &= ~MF_NOGRAVITY;
7706 		mo->flags &= ~MF_NOCLIPHEIGHT;
7707 		mo->fuse = 12*TICRATE;
7708 	}
7709 }
7710 #endif
7711 
P_BlackOw(player_t * player)7712 void P_BlackOw(player_t *player)
7713 {
7714 	INT32 i;
7715 	S_StartSound (player->mo, sfx_bkpoof); // Sound the BANG!
7716 
7717 	for (i = 0; i < MAXPLAYERS; i++)
7718 		if (playeringame[i] && P_AproxDistance(player->mo->x - players[i].mo->x,
7719 			player->mo->y - players[i].mo->y) < 1536*FRACUNIT)
7720 			P_FlashPal(&players[i], PAL_NUKE, 10);
7721 
7722 	P_NukeEnemies(player->mo, player->mo, 1536*FRACUNIT); // Search for all nearby enemies and nuke their pants off!
7723 	player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
7724 }
7725 
P_ElementalFire(player_t * player,boolean cropcircle)7726 void P_ElementalFire(player_t *player, boolean cropcircle)
7727 {
7728 	fixed_t newx;
7729 	fixed_t newy;
7730 	fixed_t ground;
7731 	mobj_t *flame;
7732 	angle_t travelangle;
7733 	INT32 i;
7734 
7735 	I_Assert(player != NULL);
7736 	I_Assert(player->mo != NULL);
7737 	I_Assert(!P_MobjWasRemoved(player->mo));
7738 
7739 	if (player->mo->eflags & MFE_VERTICALFLIP)
7740 		ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
7741 	else
7742 		ground = player->mo->floorz;
7743 
7744 	if (cropcircle)
7745 		ground += P_MobjFlip(player->mo);
7746 
7747 	if (cropcircle)
7748 	{
7749 #define numangles 8
7750 #define limitangle (180/numangles)
7751 		travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
7752 		for (i = 0; i < numangles; i++)
7753 		{
7754 			flame = P_SpawnMobj(player->mo->x, player->mo->y, ground, MT_SPINFIRE);
7755 			flame->flags &= ~MF_NOGRAVITY;
7756 			P_SetTarget(&flame->target, player->mo);
7757 			flame->angle = travelangle + i*(ANGLE_MAX/numangles);
7758 			flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
7759 			flame->destscale = player->mo->scale;
7760 			P_SetScale(flame, player->mo->scale);
7761 			flame->flags2 = (flame->flags2 & ~MF2_OBJECTFLIP)|(player->mo->flags2 & MF2_OBJECTFLIP);
7762 			flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
7763 			P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
7764 			P_SetObjectMomZ(flame, 3*FRACUNIT, false);
7765 			if (!(gametyperules & GTR_FRIENDLY))
7766 			{
7767 				P_SetMobjState(flame, S_TEAM_SPINFIRE1);
7768 				flame->color = player->mo->color;
7769 			}
7770 		}
7771 #undef limitangle
7772 #undef numangles
7773 	}
7774 	else
7775 	{
7776 		travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
7777 		for (i = 0; i < 2; i++)
7778 		{
7779 
7780 			newx = player->mo->x + P_ReturnThrustX(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
7781 			newy = player->mo->y + P_ReturnThrustY(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
7782 
7783 			if (player->mo->standingslope)
7784 			{
7785 				ground = P_GetSlopeZAt(player->mo->standingslope, newx, newy);
7786 				if (player->mo->eflags & MFE_VERTICALFLIP)
7787 					ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
7788 			}
7789 
7790 			flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
7791 			P_SetTarget(&flame->target, player->mo);
7792 			flame->angle = travelangle;
7793 			flame->fuse = TICRATE*6;
7794 			flame->destscale = player->mo->scale;
7795 			P_SetScale(flame, player->mo->scale);
7796 			flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
7797 			if (!(gametyperules & GTR_FRIENDLY))
7798 			{
7799 				P_SetMobjState(flame, S_TEAM_SPINFIRE1);
7800 				flame->color = player->mo->color;
7801 			}
7802 
7803 			flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
7804 			P_XYMovement(flame);
7805 			if (P_MobjWasRemoved(flame))
7806 				continue;
7807 
7808 			if (player->mo->eflags & MFE_VERTICALFLIP)
7809 			{
7810 				if (flame->z + flame->height < flame->ceilingz)
7811 					P_RemoveMobj(flame);
7812 			}
7813 			else if (flame->z > flame->floorz)
7814 				P_RemoveMobj(flame);
7815 		}
7816 	}
7817 }
7818 
7819 //
7820 // P_SpawnSkidDust
7821 //
7822 // Spawns spindash dust randomly around the player within a certain radius.
7823 //
P_SpawnSkidDust(player_t * player,fixed_t radius,boolean sound)7824 void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound)
7825 {
7826 	mobj_t *mo = player->mo;
7827 	mobj_t *particle;
7828 
7829 	particle = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_SPINDUST);
7830 	if (radius >>= FRACBITS)
7831 	{
7832 		P_UnsetThingPosition(particle);
7833 		particle->x += P_RandomRange(-radius, radius) << FRACBITS;
7834 		particle->y += P_RandomRange(-radius, radius) << FRACBITS;
7835 		P_SetThingPosition(particle);
7836 	}
7837 	particle->tics = 10;
7838 
7839 	particle->destscale = (2*mo->scale)/3;
7840 	P_SetScale(particle, particle->destscale);
7841 	P_SetObjectMomZ(particle, FRACUNIT, false);
7842 
7843 	if (mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version
7844 		P_SetMobjState(particle, S_SPINDUST_BUBBLE1);
7845 	else if (player->powers[pw_shield] == SH_ELEMENTAL)
7846 		P_SetMobjState(particle, S_SPINDUST_FIRE1);
7847 
7848 	if (sound)
7849 		S_StartSound(mo, sfx_s3k7e); // the proper "Knuckles eats dirt" sfx.
7850 }
7851 
P_SkidStuff(player_t * player)7852 static void P_SkidStuff(player_t *player)
7853 {
7854 	fixed_t pmx = player->rmomx + player->cmomx;
7855 	fixed_t pmy = player->rmomy + player->cmomy;
7856 
7857 	// Knuckles glides into the dirt.
7858 	if (player->pflags & PF_GLIDING && player->skidtime)
7859 	{
7860 		// Fell off a ledge...
7861 		if (!onground)
7862 		{
7863 			player->skidtime = 0;
7864 			player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE);
7865 			player->pflags |= PF_THOKKED;
7866 			P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
7867 		}
7868 		// Get up and brush yourself off, idiot.
7869 		else if (player->glidetime > 15 || !(player->cmd.buttons & BT_JUMP))
7870 		{
7871 			P_ResetPlayer(player);
7872 			P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE_LANDING);
7873 			player->pflags |= PF_STASIS;
7874 			if (player->speed > FixedMul(player->runspeed, player->mo->scale))
7875 				player->skidtime += player->mo->tics;
7876 			player->mo->momx = ((player->mo->momx - player->cmomx)/2) + player->cmomx;
7877 			player->mo->momy = ((player->mo->momy - player->cmomy)/2) + player->cmomy;
7878 		}
7879 		// Didn't stop yet? Skid FOREVER!
7880 		else if (player->skidtime == 1)
7881 			player->skidtime = 3*TICRATE+1;
7882 		// Spawn a particle every 3 tics.
7883 		else if (!(player->skidtime % 3) && !(player->charflags & SF_NOSKID))
7884 		{
7885 			P_SpawnSkidDust(player, player->mo->radius, true);
7886 		}
7887 	}
7888 	// Skidding!
7889 	else if (onground && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID))
7890 	{
7891 		if (player->skidtime)
7892 		{
7893 			// Spawn a particle every 3 tics.
7894 			if (!(player->skidtime % 3))
7895 			{
7896 				if (player->mo->state-states == S_PLAY_GLIDE_LANDING)
7897 					P_SpawnSkidDust(player, player->mo->radius, true);
7898 				else
7899 					P_SpawnSkidDust(player, 0, false);
7900 			}
7901 		}
7902 		else if (P_AproxDistance(pmx, pmy) >= FixedMul(player->runspeed/2, player->mo->scale) // if you were moving faster than half your run speed last frame
7903 		&& (player->mo->momx != pmx || player->mo->momy != pmy) // and you are moving differently this frame
7904 		&& P_GetPlayerControlDirection(player) == 2) // and your controls are pointing in the opposite direction to your movement
7905 		{ // check for skidding
7906 			angle_t mang = R_PointToAngle2(0,0,pmx,pmy); // movement angle
7907 			angle_t pang = R_PointToAngle2(pmx,pmy,player->mo->momx,player->mo->momy); // push angle
7908 			angle_t dang = mang - pang; // delta angle
7909 
7910 			if (dang > ANGLE_180) // Make delta angle always positive, invert it if it's negative.
7911 				dang = InvAngle(dang);
7912 
7913 			// If your push angle is more than this close to a full 180 degrees, trigger a skid.
7914 			if (dang > ANGLE_157h)
7915 			{
7916 				if (player->mo->state-states != S_PLAY_SKID)
7917 					P_SetPlayerMobjState(player->mo, S_PLAY_SKID);
7918 				player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
7919 				S_StartSound(player->mo, sfx_skid);
7920 			}
7921 		}
7922 	}
7923 	else {
7924 		if (player->skidtime) {
7925 			player->skidtime = 0;
7926 			S_StopSound(player->mo);
7927 		}
7928 	}
7929 }
7930 
7931 //
7932 // P_MovePlayer
P_MovePlayer(player_t * player)7933 void P_MovePlayer(player_t *player)
7934 {
7935 	ticcmd_t *cmd;
7936 	INT32 i;
7937 
7938 	fixed_t runspd;
7939 
7940 	if (player->mo->state >= &states[S_PLAY_SUPER_TRANS1] && player->mo->state <= &states[S_PLAY_SUPER_TRANS6])
7941 	{
7942 		player->mo->momx = player->mo->momy = player->mo->momz = 0;
7943 		return;
7944 	}
7945 
7946 	cmd = &player->cmd;
7947 	runspd = FixedMul(player->runspeed, player->mo->scale);
7948 
7949 	// This was done in Sonic 3 & Knuckles, but has been missed in Sonic Mania and the Taxman/Stealth mobile remakes. Thanks to NeoHazard for his 2017 blogpost on the matter, because this oversight otherwise almost made it all the way to 2.2's release.
7950 	//https://s3unlocked.blogspot.com/2017/12/over-threshold.html
7951 	if (player->powers[pw_super])
7952 		runspd = FixedMul(runspd, 5*FRACUNIT/3);
7953 
7954 	// Let's have some movement speed fun on low-friction surfaces, JUST for players... (high friction surfaces shouldn't have any adjustment, since the acceleration in this game is super high and that ends up cheesing high-friction surfaces.)
7955 	runspd = FixedMul(runspd, player->mo->movefactor);
7956 
7957 	// Control relinquishing stuff!
7958 	if ((player->powers[pw_carry] == CR_BRAKGOOP)
7959 	|| (player->pflags & PF_GLIDING && player->skidtime)
7960 	|| (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
7961 	|| (player->charability2 == CA2_MELEE && player->mo->state-states == S_PLAY_MELEE_LANDING))
7962 		player->pflags |= PF_FULLSTASIS;
7963 	else if (player->powers[pw_nocontrol])
7964 	{
7965 		player->pflags |= PF_STASIS;
7966 		if (!(player->powers[pw_nocontrol] & (1<<15)))
7967 			player->pflags |= PF_JUMPSTASIS;
7968 	}
7969 
7970 	if (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
7971 	{
7972 		player->pflags |= PF_STASIS;
7973 	}
7974 
7975 	// note: don't unset stasis here
7976 
7977 	if (!player->spectator && G_TagGametype())
7978 	{
7979 		// If we have stasis already here, it's because it's forced on us
7980 		// by a linedef executor or what have you
7981 		boolean forcestasis = false;
7982 
7983 		//During hide time, taggers cannot move.
7984 		if (leveltime < hidetime * TICRATE)
7985 		{
7986 			if (player->pflags & PF_TAGIT)
7987 				forcestasis = true;
7988 		}
7989 		else if (gametyperules & GTR_HIDEFROZEN)
7990 		{
7991 			if (!(player->pflags & PF_TAGIT))
7992 			{
7993 				forcestasis = true;
7994 				if (player->pflags & PF_GAMETYPEOVER) // Already hit.
7995 					player->powers[pw_flashing] = 5;
7996 			}
7997 		}
7998 
7999 		if (forcestasis)
8000 		{
8001 			player->pflags |= PF_FULLSTASIS;
8002 			// If you're in stasis in tag, you don't drown.
8003 			if (player->powers[pw_underwater] <= 12*TICRATE + 1)
8004 				P_RestoreMusic(player);
8005 			player->powers[pw_underwater] = player->powers[pw_spacetime] = 0;
8006 		}
8007 	}
8008 
8009 	if (player->spectator)
8010 	{
8011 		player->mo->eflags &= ~MFE_VERTICALFLIP; // deflip...
8012 		P_SpectatorMovement(player);
8013 		return;
8014 	}
8015 
8016 	// Locate the capsule for this mare.
8017 	else if (maptol & TOL_NIGHTS)
8018 	{
8019 		if (P_PlayerFullbright(player))
8020 		{
8021 			player->mo->color = ((skin_t *)player->mo->skin)->supercolor
8022 				+ ((player->nightstime == player->startedtime)
8023 					? 4
8024 					: abs((((signed)leveltime >> 1) % 9) - 4)); // This is where super flashing is handled.
8025 			G_GhostAddColor(GHC_SUPER);
8026 		}
8027 
8028 		if (!player->capsule && !player->bonustime)
8029 		{
8030 			thinker_t *th;
8031 			mobj_t *mo2;
8032 
8033 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
8034 			{
8035 				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
8036 					continue;
8037 
8038 				mo2 = (mobj_t *)th;
8039 
8040 				if (mo2->type == MT_EGGCAPSULE
8041 					&& mo2->threshold == player->mare)
8042 					P_SetTarget(&player->capsule, mo2);
8043 			}
8044 		}
8045 		else if (player->capsule && player->capsule->reactiontime > 0 && player == &players[player->capsule->reactiontime-1])
8046 		{
8047 			P_DoNiGHTSCapsule(player);
8048 			return;
8049 		}
8050 
8051 		// Suck player into their drone
8052 		if (player->drone)
8053 		{
8054 			P_MoveNiGHTSToDrone(player);
8055 			return;
8056 		}
8057 
8058 		// Test revamped NiGHTS movement.
8059 		if (player->powers[pw_carry] == CR_NIGHTSMODE)
8060 		{
8061 			P_NiGHTSMovement(player);
8062 			// No more goto blockchecking, let's just do all that here =D
8063 			if (CheckForBustableBlocks)
8064 				P_CheckBustableBlocks(player);
8065 			if (CheckForBouncySector)
8066 				P_CheckBouncySectors(player);
8067 			if (CheckForQuicksand)
8068 				P_CheckQuicksand(player);
8069 			return;
8070 		}
8071 		else if (player->powers[pw_carry] == CR_NIGHTSFALL && P_IsObjectOnGround(player->mo))
8072 		{
8073 			if (G_IsSpecialStage(gamemap))
8074 			{
8075 				if (player == &players[displayplayer]) // only play the sound for yourself landing
8076 					S_StartSound(NULL, sfx_s3k6a);
8077 				for (i = 0; i < MAXPLAYERS; i++)
8078 					if (playeringame[i])
8079 						players[i].exiting = (14*TICRATE)/5 + 1;
8080 			}
8081 			else {
8082 				// Damage whether or not we have spheres, as player should recoil upon losing points
8083 				P_DamageMobj(player->mo, NULL, NULL, 1, 0);
8084 
8085 				// Now deduct our mare score!
8086 				player->marescore = player->spheres =\
8087 				 player->rings = 0;
8088 			}
8089 			player->powers[pw_carry] = CR_NONE;
8090 		}
8091 	}
8092 
8093 	//////////////////////
8094 	// MOVEMENT CODE	//
8095 	//////////////////////
8096 
8097 	if (twodlevel || player->mo->flags2 & MF2_TWOD) // 2d-level, so special control applies.
8098 		P_2dMovement(player);
8099 	else
8100 	{
8101 		if (!player->climbing)
8102 		{
8103 			switch (P_ControlStyle(player))
8104 			{
8105 			case CS_LEGACY:
8106 			case CS_STANDARD:
8107 				player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
8108 				break;
8109 
8110 			case CS_LMAOGALOG:
8111 				break; // handled elsewhere
8112 
8113 			case CS_SIMPLE:
8114 				if (cmd->forwardmove || cmd->sidemove)
8115 				{
8116 					angle_t controlangle = R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
8117 					player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */) + controlangle;
8118 				}
8119 				else
8120 				{
8121 					angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0;
8122 					player->mo->angle = player->drawangle + drawangleoffset;
8123 				}
8124 
8125 				break;
8126 			}
8127 		}
8128 
8129 		ticruned++;
8130 		if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
8131 			ticmiss++;
8132 
8133 		P_3dMovement(player);
8134 	}
8135 
8136 	if (maptol & TOL_2D)
8137 		runspd = FixedMul(runspd, 2*FRACUNIT/3);
8138 
8139 	P_SkidStuff(player);
8140 
8141 	/////////////////////////
8142 	// MOVEMENT ANIMATIONS //
8143 	/////////////////////////
8144 
8145 	if ((cmd->forwardmove != 0 || cmd->sidemove != 0) || (player->powers[pw_super] && !onground))
8146 	{
8147 		// If the player is in dashmode, here's their peelout.
8148 		if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim == PA_RUN && !player->skidtime && (onground || ((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super]))
8149 			P_SetPlayerMobjState (player->mo, S_PLAY_DASH);
8150 		// If the player is moving fast enough,
8151 		// break into a run!
8152 		else if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime
8153 		&& (onground || ((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super]))
8154 		{
8155 			if (!onground)
8156 				P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN);
8157 			else
8158 				P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
8159 		}
8160 
8161 		// Floating at slow speeds has its own special animation.
8162 		else if ((((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super]) && player->panim == PA_IDLE && !onground)
8163 			P_SetPlayerMobjState (player->mo, S_PLAY_FLOAT);
8164 
8165 		// Otherwise, just walk.
8166 		else if ((player->rmomx || player->rmomy) && player->panim == PA_IDLE)
8167 			P_SetPlayerMobjState (player->mo, S_PLAY_WALK);
8168 	}
8169 
8170 	// If your peelout animation is playing, and you're
8171 	// going too slow, switch back to the run.
8172 	if (player->charflags & SF_DASHMODE && player->panim == PA_DASH && player->dashmode < DASHMODE_THRESHOLD)
8173 		P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
8174 
8175 	// If your running animation is playing, and you're
8176 	// going too slow, switch back to the walking frames.
8177 	if (player->panim == PA_RUN && player->speed < runspd)
8178 	{
8179 		if (!onground && (((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super]))
8180 			P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT);
8181 		else
8182 			P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8183 	}
8184 
8185 	// Correct floating when ending up on the ground.
8186 	if (onground)
8187 	{
8188 		if (player->mo->state-states == S_PLAY_FLOAT)
8189 			P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8190 		else if (player->mo->state-states == S_PLAY_FLOAT_RUN)
8191 			P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
8192 	}
8193 
8194 	// If Springing (or nojumpspinning), but travelling DOWNWARD, change back!
8195 	if ((player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0)
8196 		|| ((((player->charflags & SF_NOJUMPSPIN) && (player->pflags & PF_JUMPED) && player->panim == PA_JUMP))
8197 			&& (P_MobjFlip(player->mo)*player->mo->momz < 0)))
8198 		P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
8199 	// If doing an air animation but on the ground, change back!
8200 	else if (onground && (player->panim == PA_SPRING || player->panim == PA_FALL || player->panim == PA_RIDE || player->panim == PA_JUMP) && !player->mo->momz)
8201 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
8202 
8203 	// If you are stopped and are still walking, stand still!
8204 	if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK)
8205 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
8206 
8207 //////////////////
8208 //GAMEPLAY STUFF//
8209 //////////////////
8210 
8211 	// Make sure you're not "jumping" on the ground
8212 	if (onground && player->pflags & PF_JUMPED && !(player->pflags & PF_GLIDING)
8213 	&& P_MobjFlip(player->mo)*player->mo->momz < 0)
8214 	{
8215 		player->pflags &= ~(PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY);
8216 		player->secondjump = 0;
8217 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
8218 	}
8219 
8220 	if ((!(player->charability == CA_GLIDEANDCLIMB) || player->gotflag) // If you can't glide, then why the heck would you be gliding?
8221 		&& (player->pflags & PF_GLIDING || player->climbing))
8222 	{
8223 		if (onground)
8224 			P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8225 		else
8226 		{
8227 			player->pflags |= P_GetJumpFlags(player);
8228 			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8229 		}
8230 		player->pflags &= ~PF_GLIDING;
8231 		player->glidetime = 0;
8232 		player->climbing = 0;
8233 	}
8234 
8235 	if ((!(player->charability == CA_BOUNCE) || player->gotflag) // If you can't bounce, then why the heck would you be bouncing?
8236 		&& (player->pflags & PF_BOUNCING))
8237 	{
8238 		if (onground)
8239 			P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8240 		else
8241 		{
8242 			player->pflags |= P_GetJumpFlags(player);
8243 			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8244 		}
8245 		player->pflags &= ~PF_BOUNCING;
8246 	}
8247 
8248 	// Glide MOMZ
8249 	// AKA my own gravity. =)
8250 	if (player->pflags & PF_GLIDING)
8251 	{
8252 		mobj_t *mo = player->mo; // seriously why isn't this at the top of the function hngngngng
8253 		fixed_t glidespeed = player->actionspd;
8254 		fixed_t momx = mo->momx - player->cmomx, momy = mo->momy - player->cmomy;
8255 		angle_t angle, moveangle = R_PointToAngle2(0, 0, momx, momy);
8256 		boolean swimming = mo->state - states == S_PLAY_SWIM;
8257 		boolean in2d = mo->flags2 & MF2_TWOD || twodlevel;
8258 
8259 		if (player->powers[pw_super] || player->powers[pw_sneakers])
8260 			glidespeed *= 2;
8261 
8262 		if (player->mo->eflags & MFE_VERTICALFLIP)
8263 		{
8264 			if (player->mo->momz > FixedMul(2*FRACUNIT, player->mo->scale))
8265 				player->mo->momz -= FixedMul(3*(FRACUNIT/4), player->mo->scale);
8266 		}
8267 		else
8268 		{
8269 			if (player->mo->momz < FixedMul(-2*FRACUNIT, player->mo->scale))
8270 				player->mo->momz += FixedMul(3*(FRACUNIT/4), player->mo->scale);
8271 		}
8272 
8273 		// Strafing while gliding.
8274 		if ((P_ControlStyle(player) & CS_LMAOGALOG) || in2d)
8275 			angle = mo->angle;
8276 		else if (swimming)
8277 			angle = mo->angle + R_PointToAngle2(0, 0, cmd->forwardmove<<FRACBITS, -cmd->sidemove<<FRACBITS);
8278 		else
8279 			angle = mo->angle - FixedAngle(cmd->sidemove * FRACUNIT);
8280 
8281 		if (!player->skidtime) // TODO: make sure this works in 2D!
8282 		{
8283 			angle_t anglediff = angle - moveangle;
8284 			fixed_t accelfactor = 4*FRACUNIT - 3*FINECOSINE((anglediff >> ANGLETOFINESHIFT) & FINEMASK);
8285 			fixed_t speed, scale = mo->scale;
8286 
8287 			if (in2d)
8288 			{
8289 				if (mo->eflags & MFE_UNDERWATER)
8290 					speed = FixedMul((glidespeed>>1) + player->glidetime*750, scale);
8291 				else
8292 					speed = FixedMul(glidespeed + player->glidetime*1500, scale);
8293 				P_InstaThrust(mo, angle, speed);
8294 			}
8295 			else if (swimming)
8296 			{
8297 				fixed_t minspeed;
8298 
8299 				if (anglediff > ANGLE_180)
8300 					anglediff = InvAngle(InvAngle(anglediff) >> 3);
8301 				else
8302 					anglediff = anglediff >> 3;
8303 
8304 				minspeed = FixedMul((glidespeed>>1) + player->glidetime*750, scale); // underwater-specific
8305 				speed = FixedHypot(momx, momy) - abs(P_ReturnThrustY(mo, anglediff, mo->scale));
8306 
8307 				if (speed < minspeed)
8308 				{
8309 					momx += P_ReturnThrustX(mo, angle, FixedMul(accelfactor, scale));
8310 					momy += P_ReturnThrustY(mo, angle, FixedMul(accelfactor, scale));
8311 					speed = FixedHypot(momx, momy); // recalculate speed
8312 				}
8313 
8314 				mo->momx = P_ReturnThrustX(mo, moveangle + anglediff, speed) + player->cmomx;
8315 				mo->momy = P_ReturnThrustY(mo, moveangle + anglediff, speed) + player->cmomy;
8316 			}
8317 			else
8318 			{
8319 				fixed_t newMagnitude, oldMagnitude = R_PointToDist2(momx, momy, 0, 0);
8320 
8321 				if (mo->eflags & MFE_UNDERWATER)
8322 					speed = FixedMul((glidespeed>>1) + player->glidetime*750, scale);
8323 				else
8324 					speed = FixedMul(glidespeed + player->glidetime*1500, scale);
8325 
8326 				P_Thrust(mo, angle, FixedMul(accelfactor, scale));
8327 
8328 				newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
8329 				if (newMagnitude > speed)
8330 				{
8331 					fixed_t tempmomx, tempmomy;
8332 					if (oldMagnitude > speed)
8333 					{
8334 						if (newMagnitude > oldMagnitude)
8335 						{
8336 							tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude);
8337 							tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude);
8338 							player->mo->momx = tempmomx + player->cmomx;
8339 							player->mo->momy = tempmomy + player->cmomy;
8340 						}
8341 						// else do nothing
8342 					}
8343 					else
8344 					{
8345 						tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), speed);
8346 						tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), speed);
8347 						player->mo->momx = tempmomx + player->cmomx;
8348 						player->mo->momy = tempmomy + player->cmomy;
8349 					}
8350 				}
8351 			}
8352 		}
8353 
8354 		player->glidetime++;
8355 
8356 		if (!(player->pflags & PF_JUMPDOWN)) // If not holding the jump button
8357 		{
8358 			P_ResetPlayer(player); // down, stop gliding.
8359 			if (onground)
8360 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8361 			else if (player->charflags & SF_MULTIABILITY)
8362 			{
8363 				player->pflags |= P_GetJumpFlags(player);
8364 				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8365 			}
8366 			else
8367 			{
8368 				player->pflags |= PF_THOKKED;
8369 				player->mo->momx >>= 1;
8370 				player->mo->momy >>= 1;
8371 				P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
8372 			}
8373 		}
8374 	}
8375 	else if (player->climbing) // 'Deceleration' for climbing on walls.
8376 	{
8377 
8378 		if (!player->cmd.forwardmove)
8379 			player->mo->momz = 0;
8380 	}
8381 	else if (player->pflags & PF_BOUNCING)
8382 	{
8383 		if (!(player->pflags & PF_JUMPDOWN)) // If not holding the jump button
8384 		{
8385 			P_ResetPlayer(player); // down, stop bouncing.
8386 			player->pflags |= PF_THOKKED;
8387 			if (onground)
8388 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8389 			else if (player->charflags & SF_MULTIABILITY)
8390 			{
8391 				player->pflags |= P_GetJumpFlags(player);
8392 				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8393 			}
8394 			else
8395 			{
8396 				player->mo->momx >>= 1;
8397 				player->mo->momy >>= 1;
8398 				player->mo->momz >>= 1;
8399 				P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
8400 			}
8401 		}
8402 	}
8403 	else if (player->mo->state-states == S_PLAY_BOUNCE)
8404 		P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
8405 
8406 	// If you're running fast enough, you can create splashes as you run in shallow water.
8407 	if (!player->climbing
8408 	&& ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height >= player->mo->watertop && player->mo->z <= player->mo->watertop)
8409 		|| (player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height >= player->mo->waterbottom && player->mo->z <= player->mo->waterbottom))
8410 	&& (player->speed > runspd || (player->pflags & PF_STARTDASH))
8411 	&& leveltime % (TICRATE/7) == 0 && player->mo->momz == 0 && !(player->pflags & PF_SLIDING) && !player->spectator)
8412 	{
8413 		mobjtype_t splishtype = (player->mo->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH;
8414 		mobj_t *water = P_SpawnMobj(player->mo->x - P_ReturnThrustX(NULL, player->mo->angle, player->mo->radius), player->mo->y - P_ReturnThrustY(NULL, player->mo->angle, player->mo->radius),
8415 			((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[splishtype].height, player->mo->scale) : player->mo->watertop), splishtype);
8416 		if (player->mo->eflags & MFE_GOOWATER)
8417 			S_StartSound(water, sfx_ghit);
8418 		else if (player->mo->eflags & MFE_TOUCHLAVA)
8419 			S_StartSound(water, sfx_splash);
8420 		else
8421 			S_StartSound(water, sfx_wslap);
8422 		if (player->mo->eflags & MFE_VERTICALFLIP)
8423 		{
8424 			water->flags2 |= MF2_OBJECTFLIP;
8425 			water->eflags |= MFE_VERTICALFLIP;
8426 		}
8427 		water->destscale = player->mo->scale;
8428 		P_SetScale(water, player->mo->scale);
8429 	}
8430 
8431 	// Little water sound while touching water - just a nicety.
8432 	if ((player->mo->eflags & MFE_TOUCHWATER) && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator)
8433 	{
8434 		if (P_RandomChance(FRACUNIT/2) && leveltime % TICRATE == 0)
8435 			S_StartSound(player->mo, sfx_floush);
8436 	}
8437 
8438 	////////////////
8439 	//TAILS FLYING//
8440 	////////////////
8441 
8442 	if (!(player->charability == CA_FLY || player->charability == CA_SWIM)) // why are you flying when you cannot fly?!
8443 	{
8444 		if (player->powers[pw_tailsfly]
8445 		|| player->mo->state-states == S_PLAY_FLY_TIRED)
8446 		{
8447 			if (onground)
8448 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
8449 			else
8450 			{
8451 				player->pflags |= P_GetJumpFlags(player);
8452 				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8453 			}
8454 		}
8455 		player->powers[pw_tailsfly] = 0;
8456 	}
8457 
8458 	if (player->gotflag && player->powers[pw_tailsfly])
8459 		player->powers[pw_tailsfly] = 1;
8460 
8461 	// If not in a fly position, don't think you're flying!
8462 	if (player->panim != PA_ABILITY)
8463 		player->powers[pw_tailsfly] = 0;
8464 
8465 	if (player->charability == CA_FLY || (player->charability == CA_SWIM && player->mo->eflags & MFE_UNDERWATER))
8466 	{
8467 		// Fly counter for Tails.
8468 		if (player->powers[pw_tailsfly])
8469 		{
8470 			const fixed_t actionspd = player->actionspd/100;
8471 
8472 			if (player->charflags & SF_MULTIABILITY)
8473 			{
8474 				// Adventure-style flying by just holding the button down
8475 				if (cmd->buttons & BT_JUMP && !(player->pflags & PF_STASIS) && !player->exiting)
8476 					P_SetObjectMomZ(player->mo, actionspd/4, true);
8477 			}
8478 			else
8479 			{
8480 				// Classic flying
8481 				if (player->fly1)
8482 				{
8483 					if (P_MobjFlip(player->mo)*player->mo->momz < FixedMul(5*actionspd, player->mo->scale))
8484 						P_SetObjectMomZ(player->mo, actionspd/2, true);
8485 
8486 					P_SetPlayerMobjState(player->mo, player->mo->state->nextstate);
8487 
8488 					player->fly1--;
8489 				}
8490 			}
8491 
8492 			// Tails Put-Put noise
8493 			if (player->charability == CA_FLY
8494 				&& (player->pflags & PF_CANCARRY)
8495 				&& !(player->mo->eflags & MFE_UNDERWATER)
8496 				&& leveltime % 10 == 0
8497 				&& !player->spectator)
8498 				S_StartSound(player->mo, sfx_putput);
8499 
8500 			// Descend
8501 			if (cmd->buttons & BT_SPIN && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER))
8502 				if (P_MobjFlip(player->mo)*player->mo->momz > -FixedMul(5*actionspd, player->mo->scale))
8503 				{
8504 					if (player->fly1 > 2)
8505 						player->fly1 = 2;
8506 					P_SetObjectMomZ(player->mo, -actionspd/2, true);
8507 				}
8508 
8509 		}
8510 		else
8511 		{
8512 			// Tails-gets-tired Stuff
8513 			if (player->panim == PA_ABILITY && player->mo->state-states != S_PLAY_FLY_TIRED)
8514 				P_SetPlayerMobjState(player->mo, S_PLAY_FLY_TIRED);
8515 
8516 			if (player->charability == CA_FLY && (leveltime % 10 == 0)
8517 				&& player->mo->state-states == S_PLAY_FLY_TIRED
8518 				&& !(player->mo->eflags & MFE_UNDERWATER)
8519 				&& !player->spectator)
8520 				S_StartSound(player->mo, sfx_pudpud);
8521 		}
8522 	}
8523 
8524 	// End your chain if you're on the ground or climbing a wall.
8525 	// But not if invincible! Allow for some crazy long chains with it.
8526 	// Also keep in mind the PF_JUMPED check.
8527 	// If we lacked this, stepping up while jumping up would reset score.
8528 	// (for instance, when climbing up off a wall.)
8529 	if ((onground || player->climbing) && !(player->pflags & PF_JUMPED) && player->powers[pw_invulnerability] <= 1)
8530 		P_ResetScore(player);
8531 
8532 	// Show the "THOK!" graphic when spinning quickly across the ground. (even applies to non-spinners, in the case of zoom tubes)
8533 	if (player->pflags & PF_SPINNING && P_AproxDistance(player->speed, player->mo->momz) > FixedMul(15<<FRACBITS, player->mo->scale) && !(player->pflags & PF_JUMPED))
8534 	{
8535 		P_SpawnSpinMobj(player, player->spinitem);
8536 		G_GhostAddSpin();
8537 	}
8538 
8539 
8540 	////////////////////////////
8541 	//SPINNING AND SPINDASHING//
8542 	////////////////////////////
8543 
8544 	// If the player isn't on the ground, make sure they aren't in a "starting dash" position.
8545 	if (!onground && player->powers[pw_carry] != CR_NIGHTSMODE)
8546 	{
8547 		player->pflags &= ~PF_STARTDASH;
8548 		player->dashspeed = 0;
8549 	}
8550 
8551 	if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
8552 	&& (player->pflags & PF_SPINNING) && player->speed > FixedMul(4<<FRACBITS, player->mo->scale) && onground && (leveltime & 1)
8553 	&& !(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
8554 		P_ElementalFire(player, false);
8555 
8556 	P_DoSpinAbility(player, cmd);
8557 
8558 	// jumping
8559 	P_DoJumpStuff(player, cmd);
8560 
8561 	// If you're not spinning, you'd better not be spindashing!
8562 	if (!(player->pflags & PF_SPINNING) && player->powers[pw_carry] != CR_NIGHTSMODE)
8563 		player->pflags &= ~PF_STARTDASH;
8564 
8565 	//////////////////
8566 	//ANALOG CONTROL//
8567 	//////////////////
8568 
8569 	// This really looks like it should be moved to P_3dMovement. -Red
8570 	if (P_ControlStyle(player) == CS_LMAOGALOG
8571 		&& (cmd->forwardmove != 0 || cmd->sidemove != 0) && !player->climbing && !twodlevel && !(player->mo->flags2 & MF2_TWOD))
8572 	{
8573 		// If travelling slow enough, face the way the controls
8574 		// point and not your direction of movement.
8575 		if (player->speed < FixedMul(5*FRACUNIT, player->mo->scale) || player->pflags & PF_GLIDING || !onground)
8576 		{
8577 			angle_t tempangle;
8578 
8579 			tempangle = (cmd->angleturn << 16);
8580 
8581 #ifdef REDSANALOG // Ease to it. Chillax. ~Red
8582 			tempangle += R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT);
8583 			{
8584 				fixed_t tweenvalue = max(abs(cmd->forwardmove), abs(cmd->sidemove));
8585 
8586 				if (tweenvalue < 10 && (cmd->buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) {
8587 					tempangle = (cmd->angleturn << 16);
8588 					tweenvalue = 16;
8589 				}
8590 
8591 				tweenvalue *= tweenvalue*tweenvalue*1536;
8592 
8593 				//if (player->pflags & PF_GLIDING)
8594 					//tweenvalue >>= 1;
8595 
8596 				tempangle -= player->mo->angle;
8597 
8598 				if (tempangle < ANGLE_180 && tempangle > tweenvalue)
8599 					player->mo->angle += tweenvalue;
8600 				else if (tempangle >= ANGLE_180 && InvAngle(tempangle) > tweenvalue)
8601 					player->mo->angle -= tweenvalue;
8602 				else
8603 					player->mo->angle += tempangle;
8604 			}
8605 #else
8606 			// Less math this way ~Red
8607 			player->mo->angle = R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT)+tempangle;
8608 #endif
8609 		}
8610 		// Otherwise, face the direction you're travelling.
8611 		else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_DASH || player->panim == PA_ROLL || player->panim == PA_JUMP
8612 		|| (player->panim == PA_ABILITY && player->mo->state-states == S_PLAY_GLIDE))
8613 			player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
8614 
8615 		// Update the local angle control.
8616 		P_SetPlayerAngle(player, player->mo->angle);
8617 	}
8618 
8619 	if (player->climbing == 1)
8620 		P_DoClimbing(player);
8621 
8622 	if (player->climbing > 1)
8623 	{
8624 		P_InstaThrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Shove up against the wall
8625 		player->climbing--;
8626 	}
8627 
8628 	// Make sure you're not teetering when you shouldn't be.
8629 	if (player->panim == PA_EDGE
8630 	&& (player->mo->momx || player->mo->momy || player->mo->momz))
8631 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
8632 
8633 	// Check for teeter!
8634 	if (!(player->mo->momz || player->mo->momx || player->mo->momy) && !(player->mo->eflags & MFE_GOOWATER)
8635 	&& player->panim == PA_IDLE && !(player->powers[pw_carry]))
8636 		P_DoTeeter(player);
8637 
8638 	// Toss a flag
8639 	if (G_GametypeHasTeams() && (cmd->buttons & BT_TOSSFLAG) && !(player->powers[pw_super]) && !(player->tossdelay))
8640 	{
8641 		if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
8642 			P_PlayerEmeraldBurst(player, true); // Toss emeralds
8643 		else
8644 			P_PlayerFlagBurst(player, true);
8645 	}
8646 
8647 	// check for fire
8648 	if (!player->exiting)
8649 		P_DoFiring(player, cmd);
8650 
8651 	{
8652 		boolean atspinheight = false;
8653 		fixed_t oldheight = player->mo->height;
8654 		fixed_t luaheight = LUAh_PlayerHeight(player);
8655 
8656 		if (luaheight != -1)
8657 		{
8658 			player->mo->height = luaheight;
8659 			if (luaheight <= P_GetPlayerSpinHeight(player))
8660 				atspinheight = true; // spinning will not save you from being crushed
8661 		}
8662 		// Less height while spinning. Good for spinning under things...?
8663 		else if (P_PlayerShouldUseSpinHeight(player))
8664 		{
8665 			player->mo->height = P_GetPlayerSpinHeight(player);
8666 			atspinheight = true;
8667 		}
8668 		else
8669 			player->mo->height = P_GetPlayerHeight(player);
8670 
8671 		if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling
8672 			player->mo->z -= player->mo->height - oldheight;
8673 
8674 		// Crush test...
8675 		if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
8676 			&& !(player->mo->flags & MF_NOCLIP))
8677 		{
8678 			if (!atspinheight)
8679 			{
8680 				player->pflags |= PF_SPINNING;
8681 				P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
8682 			}
8683 			else if (player->mo->ceilingz - player->mo->floorz < player->mo->height)
8684 			{
8685 				if ((netgame || multiplayer) && player->spectator)
8686 					P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators
8687 				else
8688 					P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED);
8689 
8690 				if (player->playerstate == PST_DEAD)
8691 					return;
8692 			}
8693 		}
8694 	}
8695 
8696 #ifdef HWRENDER
8697 	if (rendermode == render_opengl && cv_fovchange.value)
8698 	{
8699 		fixed_t speed;
8700 		const fixed_t runnyspeed = 20*FRACUNIT;
8701 
8702 		speed = R_PointToDist2(player->rmomx, player->rmomy, 0, 0);
8703 
8704 		if (speed > player->normalspeed-5*FRACUNIT)
8705 			speed = player->normalspeed-5*FRACUNIT;
8706 
8707 		if (speed >= runnyspeed)
8708 			player->fovadd = speed-runnyspeed;
8709 		else
8710 			player->fovadd = 0;
8711 
8712 		if (player->fovadd < 0)
8713 			player->fovadd = 0;
8714 	}
8715 	else
8716 		player->fovadd = 0;
8717 #endif
8718 
8719 	// Look for blocks to bust up
8720 	// Because of FF_SHATTER, we should look for blocks constantly,
8721 	// not just when spinning or playing as Knuckles
8722 	if (CheckForBustableBlocks)
8723 		P_CheckBustableBlocks(player);
8724 
8725 	// Check for a BOUNCY sector!
8726 	if (CheckForBouncySector)
8727 		P_CheckBouncySectors(player);
8728 
8729 	// Look for Quicksand!
8730 	if (CheckForQuicksand)
8731 		P_CheckQuicksand(player);
8732 
8733 	if (P_IsObjectOnGround(player->mo))
8734 		player->mo->pmomz = 0;
8735 }
8736 
P_DoZoomTube(player_t * player)8737 static void P_DoZoomTube(player_t *player)
8738 {
8739 	fixed_t speed;
8740 	mobj_t *waypoint = NULL;
8741 	fixed_t dist;
8742 	boolean reverse;
8743 
8744 	player->mo->height = P_GetPlayerSpinHeight(player);
8745 
8746 	if (player->speed > 0)
8747 		reverse = false;
8748 	else
8749 		reverse = true;
8750 
8751 	player->powers[pw_flashing] = 1;
8752 
8753 	speed = abs(player->speed);
8754 
8755 	// change slope
8756 	dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
8757 
8758 	if (dist < 1)
8759 		dist = 1;
8760 
8761 	player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
8762 	player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
8763 	player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
8764 
8765 	// Calculate the distance between the player and the waypoint
8766 	// 'dist' already equals this.
8767 
8768 	// Will the player go past the waypoint?
8769 	if (speed > dist)
8770 	{
8771 		speed -= dist;
8772 		// If further away, set XYZ of player to waypoint location
8773 		P_UnsetThingPosition(player->mo);
8774 		player->mo->x = player->mo->tracer->x;
8775 		player->mo->y = player->mo->tracer->y;
8776 		player->mo->z = player->mo->tracer->z;
8777 		P_SetThingPosition(player->mo);
8778 
8779 		// ugh, duh!!
8780 		player->mo->floorz = player->mo->subsector->sector->floorheight;
8781 		player->mo->ceilingz = player->mo->subsector->sector->ceilingheight;
8782 
8783 		CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
8784 
8785 		// Find next waypoint
8786 		waypoint = reverse ? P_GetPreviousWaypoint(player->mo->tracer, false) : P_GetNextWaypoint(player->mo->tracer, false);
8787 
8788 		if (waypoint)
8789 		{
8790 			CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
8791 
8792 			P_SetTarget(&player->mo->tracer, waypoint);
8793 
8794 			// calculate MOMX/MOMY/MOMZ for next waypoint
8795 
8796 			// change slope
8797 			dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
8798 
8799 			if (dist < 1)
8800 				dist = 1;
8801 
8802 			player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
8803 			player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
8804 			player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
8805 		}
8806 		else
8807 		{
8808 			P_SetTarget(&player->mo->tracer, NULL); // Else, we just let them fly.
8809 			player->powers[pw_carry] = CR_NONE;
8810 
8811 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
8812 		}
8813 	}
8814 
8815 	// change angle
8816 	if (player->mo->tracer)
8817 	{
8818 		player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
8819 		P_SetPlayerAngle(player, player->mo->angle);
8820 	}
8821 }
8822 
8823 //
8824 // P_DoRopeHang
8825 //
8826 // Kinda like P_DoZoomTube
8827 // but a little different.
8828 //
P_DoRopeHang(player_t * player)8829 static void P_DoRopeHang(player_t *player)
8830 {
8831 	INT32 sequence;
8832 	fixed_t speed;
8833 	mobj_t *waypoint = NULL;
8834 	fixed_t dist;
8835 	fixed_t playerz;
8836 
8837 	player->mo->height = P_GetPlayerHeight(player);
8838 
8839 	// Play the 'clink' sound only if the player is moving.
8840 	if (!(leveltime & 7) && player->speed)
8841 		S_StartSound(player->mo, sfx_s3k55);
8842 
8843 	playerz = player->mo->z + player->mo->height;
8844 
8845 	speed = abs(player->speed);
8846 
8847 	sequence = player->mo->tracer->threshold;
8848 
8849 	// change slope
8850 	dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
8851 
8852 	if (dist < 1)
8853 		dist = 1;
8854 
8855 	player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
8856 	player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
8857 	player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
8858 
8859 	if (player->cmd.buttons & BT_SPIN && !(player->pflags & PF_STASIS)) // Drop off of the rope
8860 	{
8861 		player->pflags |= (P_GetJumpFlags(player)|PF_SPINDOWN);
8862 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8863 
8864 		P_SetTarget(&player->mo->tracer, NULL);
8865 		player->powers[pw_carry] = CR_NONE;
8866 
8867 		return;
8868 	}
8869 
8870 	if (player->mo->state-states != S_PLAY_RIDE)
8871 		P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
8872 
8873 	// If not allowed to move, we're done here.
8874 	if (!speed)
8875 		return;
8876 
8877 	// Calculate the distance between the player and the waypoint
8878 	// 'dist' already equals this.
8879 
8880 	// Will the player go past the waypoint?
8881 	if (speed > dist)
8882 	{
8883 		speed -= dist;
8884 		// If further away, set XYZ of player to waypoint location
8885 		P_UnsetThingPosition(player->mo);
8886 		player->mo->x = player->mo->tracer->x;
8887 		player->mo->y = player->mo->tracer->y;
8888 		player->mo->z = player->mo->tracer->z - player->mo->height;
8889 		playerz = player->mo->tracer->z;
8890 		P_SetThingPosition(player->mo);
8891 
8892 		CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
8893 
8894 		// Find next waypoint
8895 		waypoint = P_GetNextWaypoint(player->mo->tracer, false);
8896 
8897 		if (!(player->mo->tracer->flags & MF_SLIDEME) && !waypoint)
8898 		{
8899 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, wrapping to start...\n");
8900 
8901 			// Wrap around back to first waypoint
8902 			waypoint = P_GetFirstWaypoint(sequence);
8903 		}
8904 
8905 		if (waypoint)
8906 		{
8907 			CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
8908 
8909 			P_SetTarget(&player->mo->tracer, waypoint);
8910 
8911 			// calculate MOMX/MOMY/MOMZ for next waypoint
8912 			// change slope
8913 			dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
8914 
8915 			if (dist < 1)
8916 				dist = 1;
8917 
8918 			player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
8919 			player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
8920 			player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
8921 		}
8922 		else
8923 		{
8924 			if (player->mo->tracer->flags & MF_SLIDEME)
8925 			{
8926 				player->pflags |= P_GetJumpFlags(player);
8927 				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
8928 			}
8929 
8930 			P_SetTarget(&player->mo->tracer, NULL);
8931 			player->powers[pw_carry] = CR_NONE;
8932 
8933 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found!\n");
8934 		}
8935 	}
8936 }
8937 
8938 #if 0
8939 //
8940 // P_NukeAllPlayers
8941 //
8942 // Hurts all players
8943 // source = guy who gets the credit
8944 //
8945 static void P_NukeAllPlayers(player_t *player)
8946 {
8947 	mobj_t *mo;
8948 	UINT8 i;
8949 
8950 	for (i = 0; i < MAXPLAYERS; i++)
8951 	{
8952 		if (!playeringame[i])
8953 			continue;
8954 		if (players[i].spectator)
8955 			continue;
8956 		if (!players[i].mo)
8957 			continue;
8958 		if (players[i].mo == player->mo)
8959 			continue;
8960 		if (players[i].mo->health <= 0)
8961 			continue;
8962 
8963 		P_DamageMobj(players[i].mo, player->mo, player->mo, 1, 0);
8964 	}
8965 
8966 	CONS_Printf(M_GetText("%s caused a world of pain.\n"), player_names[player-players]);
8967 
8968 	return;
8969 }
8970 #endif
8971 
8972 //
8973 // P_NukeEnemies
8974 // Looks for something you can hit - Used for bomb shield
8975 //
P_NukeEnemies(mobj_t * inflictor,mobj_t * source,fixed_t radius)8976 void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
8977 {
8978 	const fixed_t ns = 60 << FRACBITS;
8979 	mobj_t *mo;
8980 	angle_t fa;
8981 	thinker_t *think;
8982 	INT32 i;
8983 
8984 	for (i = 0; i < 16; i++)
8985 	{
8986 		fa = (i*(FINEANGLES/16));
8987 		mo = P_SpawnMobj(inflictor->x, inflictor->y, inflictor->z, MT_SUPERSPARK);
8988 		if (!P_MobjWasRemoved(mo))
8989 		{
8990 			mo->momx = FixedMul(FINESINE(fa),ns);
8991 			mo->momy = FixedMul(FINECOSINE(fa),ns);
8992 		}
8993 	}
8994 
8995 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
8996 	{
8997 		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
8998 			continue;
8999 
9000 		mo = (mobj_t *)think;
9001 
9002 		if (!(mo->flags & MF_SHOOTABLE) && !(mo->type == MT_EGGGUARD || mo->type == MT_MINUS))
9003 			continue;
9004 
9005 		if (mo->flags & MF_MONITOR)
9006 			continue; // Monitors cannot be 'nuked'.
9007 
9008 		if (!G_RingSlingerGametype() && mo->type == MT_PLAYER)
9009 			continue; // Don't hurt players in Co-Op!
9010 
9011 		if (abs(inflictor->x - mo->x) > radius || abs(inflictor->y - mo->y) > radius || abs(inflictor->z - mo->z) > radius)
9012 			continue; // Workaround for possible integer overflow in the below -Red
9013 
9014 		if (P_AproxDistance(P_AproxDistance(inflictor->x - mo->x, inflictor->y - mo->y), inflictor->z - mo->z) > radius)
9015 			continue;
9016 
9017 		if (mo->type == MT_MINUS && !(mo->flags & (MF_SPECIAL|MF_SHOOTABLE)))
9018 			mo->flags = (mo->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE;
9019 
9020 		if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
9021 			P_KillMobj(mo->tracer, inflictor, source, DMG_NUKE);
9022 
9023 		if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players!
9024 			P_DamageMobj(mo, inflictor, source, 1, DMG_NUKE);
9025 		else
9026 			P_DamageMobj(mo, inflictor, source, 1000, DMG_NUKE);
9027 	}
9028 }
9029 
9030 //
9031 // P_Earthquake
9032 // Used for Super Knuckles' landing - damages enemies within the given radius
9033 //
P_Earthquake(mobj_t * inflictor,mobj_t * source,fixed_t radius)9034 void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius)
9035 {
9036 	const fixed_t scaledradius = FixedMul(radius, inflictor->scale);
9037 	const fixed_t ns = scaledradius/12;
9038 	mobj_t *mo;
9039 	angle_t fa;
9040 	INT32 i;
9041 	boolean grounded = P_IsObjectOnGround(inflictor);
9042 
9043 	for (i = 0; i < 16; i++)
9044 	{
9045 		fa = (i*(FINEANGLES/16));
9046 		mo = P_SpawnMobjFromMobj(inflictor, 0, 0, 0, MT_SUPERSPARK);
9047 		if (!P_MobjWasRemoved(mo))
9048 		{
9049 			if (grounded)
9050 			{
9051 				mo->momx = FixedMul(FINESINE(fa),ns);
9052 				mo->momy = FixedMul(FINECOSINE(fa),ns);
9053 			}
9054 			else
9055 			{
9056 				P_InstaThrust(mo, inflictor->angle + ANGLE_90, FixedMul(FINECOSINE(fa),ns));
9057 				mo->momz = FixedMul(FINESINE(fa),ns);
9058 			}
9059 		}
9060 	}
9061 
9062 	if (inflictor->player && P_IsLocalPlayer(inflictor->player))
9063 	{
9064 		quake.epicenter = NULL;
9065 		quake.intensity = 8*inflictor->scale;
9066 		quake.time = 8;
9067 		quake.radius = scaledradius;
9068 	}
9069 
9070 	P_RadiusAttack(inflictor, source, radius, 0, false);
9071 }
9072 
9073 //
9074 // P_LookForFocusTarget
9075 // Looks for a target for a player to focus on, for Z-targeting etc.
9076 // exclude would be the current focus target, to ignore.
9077 // direction, if set, requires the target to be to the left (1) or right (-1) of the angle
9078 // mobjflags can be used to limit the flags of objects that can be focused
9079 //
P_LookForFocusTarget(player_t * player,mobj_t * exclude,SINT8 direction,UINT8 lockonflags)9080 mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction, UINT8 lockonflags)
9081 {
9082 	mobj_t *mo;
9083 	thinker_t *think;
9084 	mobj_t *closestmo = NULL;
9085 	const fixed_t maxdist = 2560*player->mo->scale;
9086 	const angle_t span = ANGLE_45;
9087 	fixed_t dist, closestdist = 0;
9088 	angle_t dangle, closestdangle = 0;
9089 
9090 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
9091 	{
9092 		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
9093 			continue;
9094 
9095 		mo = (mobj_t *)think;
9096 
9097 		if (mo->flags & MF_NOCLIPTHING)
9098 			continue;
9099 
9100 		if (mo == player->mo || mo == exclude)
9101 			continue;
9102 
9103 		if (mo->health <= 0) // dead
9104 			continue;
9105 
9106 		switch (mo->type)
9107 		{
9108 		case MT_TNTBARREL:
9109 			if (lockonflags & LOCK_INTERESTS)
9110 				break;
9111 			/*FALLTHRU*/
9112 		case MT_PLAYER: // Don't chase other players!
9113 		case MT_DETON:
9114 			continue; // Don't be STUPID, Sonic!
9115 
9116 		case MT_FAKEMOBILE:
9117 			if (!(lockonflags & LOCK_BOSS))
9118 				continue;
9119 			break;
9120 
9121 		case MT_EGGSHIELD:
9122 			if (!(lockonflags & LOCK_ENEMY))
9123 				continue;
9124 			break;
9125 
9126 		case MT_EGGSTATUE:
9127 			if (tutorialmode)
9128 				break; // Always focus egg statue in the tutorial
9129 			/*FALLTHRU*/
9130 		default:
9131 
9132 			if ((lockonflags & LOCK_BOSS) && ((mo->flags & (MF_BOSS|MF_SHOOTABLE)) == (MF_BOSS|MF_SHOOTABLE))) // allows if it has the flags desired XOR it has the invert aimable flag
9133 			{
9134 				if (mo->flags2 & MF2_FRET)
9135 					continue;
9136 				break;
9137 			}
9138 
9139 			if ((lockonflags & LOCK_ENEMY) && (!((mo->flags & (MF_ENEMY|MF_SHOOTABLE)) == (MF_ENEMY|MF_SHOOTABLE)) != !(mo->flags2 & MF2_INVERTAIMABLE))) // allows if it has the flags desired XOR it has the invert aimable flag
9140 				break;
9141 
9142 			if ((lockonflags & LOCK_INTERESTS) && (mo->flags & (MF_PUSHABLE|MF_MONITOR))) // allows if it has the flags desired XOR it has the invert aimable flag
9143 				break;
9144 
9145 			continue; // not a valid object
9146 		}
9147 
9148 		{
9149 			fixed_t zdist = (player->mo->z + player->mo->height/2) - (mo->z + mo->height/2);
9150 			dist = P_AproxDistance(player->mo->x-mo->x, player->mo->y-mo->y);
9151 
9152 			if (abs(zdist) > dist)
9153 				continue; // Don't home outside of desired angle!
9154 
9155 			dist = P_AproxDistance(dist, zdist);
9156 			if (dist > maxdist)
9157 				continue; // out of range
9158 		}
9159 
9160 		if ((twodlevel || player->mo->flags2 & MF2_TWOD)
9161 		&& abs(player->mo->y-mo->y) > player->mo->radius)
9162 			continue; // not in your 2d plane
9163 
9164 		dangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y) - (
9165 			!exclude ? player->mo->angle : R_PointToAngle2(player->mo->x, player->mo->y, exclude->x, exclude->y)
9166 		);
9167 
9168 		if (direction)
9169 		{
9170 			if (direction == 1 && dangle > ANGLE_180)
9171 				continue; // To the right of the player
9172 			if (direction == -1 && dangle < ANGLE_180)
9173 				continue; // To the left of the player
9174 		}
9175 
9176 		if (dangle > ANGLE_180)
9177 			dangle = InvAngle(dangle);
9178 
9179 		if (dangle > span)
9180 			continue; // behind back
9181 
9182 		// Inflate dist by angle difference to bias toward objects at a closer angle
9183 		dist = FixedDiv(dist, FINECOSINE(dangle>>ANGLETOFINESHIFT)*3);
9184 
9185 		if (closestmo && (exclude ? (dangle > closestdangle) : (dist > closestdist)))
9186 			continue;
9187 
9188 		if (!P_CheckSight(player->mo, mo))
9189 			continue; // out of sight
9190 
9191 		closestmo = mo;
9192 		closestdist = dist;
9193 		closestdangle = dangle;
9194 	}
9195 
9196 	return closestmo;
9197 }
9198 
9199 //
9200 // P_LookForEnemies
9201 // Looks for something you can hit - Used for homing attack
9202 // If nonenemies is true, includes monitors and springs!
9203 // If bullet is true, you can look up and the distance is further,
9204 // but your total angle span you can look is limited to compensate. (Also, allows monitors.)
9205 // If you modify this, please also modify P_HomingAttack.
9206 //
P_LookForEnemies(player_t * player,boolean nonenemies,boolean bullet)9207 mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
9208 {
9209 	mobj_t *mo;
9210 	thinker_t *think;
9211 	mobj_t *closestmo = NULL;
9212 	const fixed_t maxdist = FixedMul((bullet ? RING_DIST*2 : RING_DIST), player->mo->scale);
9213 	const angle_t span = (bullet ? ANG30 : ANGLE_90);
9214 	fixed_t dist, closestdist = 0;
9215 	const mobjflag_t nonenemiesdisregard = (bullet ? 0 : MF_MONITOR)|MF_SPRING;
9216 
9217 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
9218 	{
9219 		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
9220 			continue;
9221 
9222 		mo = (mobj_t *)think;
9223 
9224 		if (mo->flags & MF_NOCLIPTHING)
9225 			continue;
9226 
9227 		if (mo->health <= 0) // dead
9228 			continue;
9229 
9230 		if (!((mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (mo->flags & MF_SHOOTABLE)) || (mo->flags & MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag
9231 			continue; // not a valid target
9232 
9233 		if (mo == player->mo)
9234 			continue;
9235 
9236 		if (mo->flags2 & MF2_FRET)
9237 			continue;
9238 
9239 		if (!nonenemies && mo->flags & nonenemiesdisregard)
9240 			continue;
9241 
9242 		if (!bullet && mo->type == MT_DETON) // Don't be STUPID, Sonic!
9243 			continue;
9244 
9245 		{
9246 			fixed_t zdist = (player->mo->z + player->mo->height/2) - (mo->z + mo->height/2);
9247 			dist = P_AproxDistance(player->mo->x-mo->x, player->mo->y-mo->y);
9248 			if (bullet)
9249 			{
9250 				if ((R_PointToAngle2(0, 0, dist, zdist) + span) > span*2)
9251 					continue; // Don't home outside of desired angle!
9252 			}
9253 			else // Don't home upwards!
9254 			{
9255 				if (player->mo->eflags & MFE_VERTICALFLIP)
9256 				{
9257 					if (mo->z+mo->height < player->mo->z+player->mo->height-FixedMul(MAXSTEPMOVE, player->mo->scale))
9258 							continue;
9259 				}
9260 				else if (mo->z > player->mo->z+FixedMul(MAXSTEPMOVE, player->mo->scale))
9261 					continue;
9262 			}
9263 
9264 			dist = P_AproxDistance(dist, zdist);
9265 			if (dist > maxdist)
9266 				continue; // out of range
9267 		}
9268 
9269 		if ((twodlevel || player->mo->flags2 & MF2_TWOD)
9270 		&& abs(player->mo->y-mo->y) > player->mo->radius)
9271 			continue; // not in your 2d plane
9272 
9273 		if (mo->type == MT_PLAYER) // Don't chase after other players!
9274 			continue;
9275 
9276 		if (closestmo && dist > closestdist)
9277 			continue;
9278 
9279 		if ((R_PointToAngle2(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius), mo->x, mo->y) - player->mo->angle + span) > span*2)
9280 			continue; // behind back
9281 
9282 		if (!P_CheckSight(player->mo, mo))
9283 			continue; // out of sight
9284 
9285 		closestmo = mo;
9286 		closestdist = dist;
9287 	}
9288 
9289 	return closestmo;
9290 }
9291 
P_HomingAttack(mobj_t * source,mobj_t * enemy)9292 boolean P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
9293 {
9294 	fixed_t zdist;
9295 	fixed_t dist;
9296 	fixed_t ns = 0;
9297 
9298 	if (!enemy)
9299 		return false;
9300 
9301 	if (enemy->flags & MF_NOCLIPTHING)
9302 		return false;
9303 
9304 	if (enemy->health <= 0) // dead
9305 		return false;
9306 
9307 	if (source->player && (!((enemy->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (enemy->flags & MF_SHOOTABLE)) || (enemy->flags & MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE))) // allows if it has the flags desired XOR it has the invert aimable flag
9308 		return false;
9309 
9310 	if (enemy->flags2 & MF2_FRET)
9311 		return false;
9312 
9313 	// change angle
9314 	source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y);
9315 	if (source->player)
9316 	{
9317 		source->player->drawangle = source->angle;
9318 		if (!demoplayback || P_ControlStyle(source->player) == CS_LMAOGALOG)
9319 			P_SetPlayerAngle(source->player, source->angle);
9320 	}
9321 
9322 	// change slope
9323 	zdist = ((P_MobjFlip(source) == -1) ? (enemy->z + enemy->height) - (source->z + source->height) : (enemy->z - source->z));
9324 	dist = P_AproxDistance(P_AproxDistance(enemy->x - source->x, enemy->y - source->y), zdist);
9325 
9326 	if (dist < 1)
9327 		dist = 1;
9328 
9329 	if (source->type == MT_DETON && enemy->player) // For Deton Chase (Unused)
9330 		ns = FixedDiv(FixedMul(enemy->player->normalspeed, enemy->scale), FixedDiv(20*FRACUNIT,17*FRACUNIT));
9331 	else if (source->type != MT_PLAYER)
9332 	{
9333 		if (source->threshold == 32000)
9334 			ns = FixedMul(source->info->speed/2, source->scale);
9335 		else
9336 			ns = FixedMul(source->info->speed, source->scale);
9337 	}
9338 	else if (source->player)
9339 	{
9340 		if (source->player->charability == CA_HOMINGTHOK && !(source->player->pflags & PF_SHIELDABILITY))
9341 			ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2);
9342 		else
9343 			ns = FixedMul(45*FRACUNIT, source->scale);
9344 	}
9345 
9346 	source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns);
9347 	source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns);
9348 	source->momz = FixedMul(FixedDiv(zdist, dist), ns);
9349 
9350 	return true;
9351 }
9352 
9353 // Search for emeralds
P_FindEmerald(void)9354 void P_FindEmerald(void)
9355 {
9356 	thinker_t *th;
9357 	mobj_t *mo2;
9358 
9359 	hunt1 = hunt2 = hunt3 = NULL;
9360 
9361 	// scan the remaining thinkers
9362 	// to find all emeralds
9363 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
9364 	{
9365 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
9366 			continue;
9367 
9368 		mo2 = (mobj_t *)th;
9369 		if (mo2->type == MT_EMERHUNT)
9370 		{
9371 			if (!hunt1)
9372 				hunt1 = mo2;
9373 			else if (!hunt2)
9374 				hunt2 = mo2;
9375 			else if (!hunt3)
9376 				hunt3 = mo2;
9377 		}
9378 	}
9379 	return;
9380 }
9381 
9382 //
9383 // P_GetLives
9384 // Get extra lives in new co-op if you're allowed to.
9385 //
9386 
P_GetLives(player_t * player)9387 boolean P_GetLives(player_t *player)
9388 {
9389 	INT32 i, maxlivesplayer = -1, livescheck = 1;
9390 	if (!(netgame || multiplayer)
9391 	|| !G_GametypeUsesCoopLives()
9392 	|| (player->lives == INFLIVES))
9393 		return true;
9394 
9395 	if (cv_cooplives.value == 0) // infinite lives
9396 	{
9397 		if (player->lives < 1)
9398 			player->lives = 1;
9399 		return true;
9400 	}
9401 
9402 	if ((cv_cooplives.value == 2 || cv_cooplives.value == 1) && player->lives > 0)
9403 		return true;
9404 
9405 	if (cv_cooplives.value == 1)
9406 		return false;
9407 
9408 	for (i = 0; i < MAXPLAYERS; i++)
9409 	{
9410 		if (!playeringame[i])
9411 			continue;
9412 
9413 		if (players[i].lives > livescheck)
9414 		{
9415 			maxlivesplayer = i;
9416 			livescheck = players[i].lives;
9417 		}
9418 	}
9419 	if (maxlivesplayer != -1 && &players[maxlivesplayer] != player)
9420 	{
9421 		if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer])))
9422 			S_StartSound(NULL, sfx_jshard); // placeholder
9423 		if (players[maxlivesplayer].lives != INFLIVES)
9424 			players[maxlivesplayer].lives--;
9425 		player->lives++;
9426 		if (player->lives < 1)
9427 			player->lives = 1;
9428 		return true;
9429 	}
9430 	return (player->lives > 0);
9431 }
9432 
9433 //
9434 // P_ConsiderAllGone
9435 // Shamelessly lifted from TD. Thanks, Sryder!
9436 //
9437 
P_ConsiderAllGone(void)9438 static void P_ConsiderAllGone(void)
9439 {
9440 	INT32 i, lastdeadplayer = -1, deadtimercheck = INT32_MAX;
9441 
9442 	if (countdown2)
9443 		return;
9444 
9445 	for (i = 0; i < MAXPLAYERS; i++)
9446 	{
9447 		if (!playeringame[i])
9448 			continue;
9449 
9450 		if (players[i].playerstate != PST_DEAD && !players[i].spectator && players[i].mo && players[i].mo->health)
9451 			break;
9452 
9453 		if (players[i].spectator)
9454 		{
9455 			if (lastdeadplayer == -1)
9456 				lastdeadplayer = i;
9457 		}
9458 		else if (players[i].lives > 0)
9459 		{
9460 			lastdeadplayer = i;
9461 			if (players[i].deadtimer < deadtimercheck)
9462 				deadtimercheck = players[i].deadtimer;
9463 		}
9464 	}
9465 
9466 	if (i == MAXPLAYERS && lastdeadplayer != -1 && deadtimercheck > 2*TICRATE) // the last killed player will reset the level in G_DoReborn
9467 	{
9468 		//players[lastdeadplayer].spectator = true;
9469 		players[lastdeadplayer].outofcoop = true;
9470 		players[lastdeadplayer].playerstate = PST_REBORN;
9471 	}
9472 }
9473 
P_RestoreMultiMusic(player_t * player)9474 void P_RestoreMultiMusic(player_t *player)
9475 {
9476 	if (netgame)
9477 	{
9478 		if (P_IsLocalPlayer(player))
9479 			S_ChangeMusic(mapmusname, mapmusflags, true);
9480 	}
9481 	else if (multiplayer) // local multiplayer only
9482 	{
9483 		// Restore the other player's music once we're dead for long enough
9484 		// -- that is, as long as they aren't dead too
9485 		if (player == &players[displayplayer] && players[secondarydisplayplayer].lives > 0)
9486 			P_RestoreMusic(&players[secondarydisplayplayer]);
9487 		else if (player == &players[secondarydisplayplayer] && players[displayplayer].lives > 0)
9488 			P_RestoreMusic(&players[displayplayer]);
9489 	}
9490 }
9491 
9492 //
9493 // P_DeathThink
9494 // Fall on your face when dying.
9495 // Decrease POV height to floor height.
9496 //
9497 
P_DeathThink(player_t * player)9498 static void P_DeathThink(player_t *player)
9499 {
9500 	INT32 j = MAXPLAYERS;
9501 
9502 	ticcmd_t *cmd = &player->cmd;
9503 	player->deltaviewheight = 0;
9504 
9505 	if (player->deadtimer < INT32_MAX)
9506 		player->deadtimer++;
9507 
9508 	if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already
9509 		goto notrealplayer;
9510 
9511 	// continue logic
9512 	if (!(netgame || multiplayer) && player->lives <= 0)
9513 	{
9514 		if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0))
9515 			G_UseContinue();
9516 		else if (player->deadtimer >= gameovertics)
9517 			G_UseContinue(); // Even if we don't have one this handles ending the game
9518 	}
9519 
9520 	if ((cv_cooplives.value != 1)
9521 	&& (G_GametypeUsesCoopLives())
9522 	&& (netgame || multiplayer)
9523 	&& (player->lives <= 0))
9524 	{
9525 		for (j = 0; j < MAXPLAYERS; j++)
9526 		{
9527 			if (!playeringame[j])
9528 				continue;
9529 
9530 			if (players[j].lives > 1)
9531 				break;
9532 		}
9533 	}
9534 
9535 	// Force respawn if idle for more than 30 seconds in shooter modes.
9536 	if (player->deadtimer > 30*TICRATE && !G_PlatformGametype())
9537 		player->playerstate = PST_REBORN;
9538 	else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages!
9539 	{
9540 		if (G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
9541 		{
9542 			P_ConsiderAllGone();
9543 			if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE)))
9544 			{
9545 				//player->spectator = true;
9546 				player->outofcoop = true;
9547 				player->playerstate = PST_REBORN;
9548 			}
9549 		}
9550 		else
9551 		{
9552 			// Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes.
9553 			if (cmd->buttons & BT_JUMP)
9554 			{
9555 				// You're a spectator, so respawn right away.
9556 				if ((gametyperules & GTR_SPECTATORS) && player->spectator)
9557 					player->playerstate = PST_REBORN;
9558 				else
9559 				{
9560 					// Give me one second.
9561 					INT32 respawndelay = TICRATE;
9562 
9563 					// Non-platform gametypes
9564 					if (gametyperules & GTR_RESPAWNDELAY)
9565 						respawndelay = (cv_respawntime.value*TICRATE);
9566 
9567 					// You've been dead for enough time.
9568 					// You may now respawn.
9569 					if (player->deadtimer > respawndelay)
9570 						player->playerstate = PST_REBORN;
9571 				}
9572 			}
9573 
9574 			// Single player auto respawn
9575 			if (!(netgame || multiplayer) && player->deadtimer > TICRATE<<1)
9576 				player->playerstate = PST_REBORN;
9577 		}
9578 	}
9579 	else if ((netgame || multiplayer) && player->deadtimer >= 8*TICRATE)
9580 	{
9581 		INT32 i, deadtimercheck = INT32_MAX;
9582 
9583 		// In a net/multiplayer game, and out of lives
9584 		if (G_CompetitionGametype())
9585 		{
9586 			for (i = 0; i < MAXPLAYERS; i++)
9587 			{
9588 				if (!playeringame[i])
9589 					continue;
9590 				if (!players[i].exiting && players[i].lives)
9591 					break;
9592 				if (players[i].deadtimer < deadtimercheck)
9593 					deadtimercheck = players[i].deadtimer;
9594 			}
9595 
9596 			if (i == MAXPLAYERS && deadtimercheck == 8*TICRATE)
9597 			{
9598 				// Everyone's either done with the race, or dead.
9599 				if (!countdown2 || countdown2 > 1*TICRATE)
9600 					countdown2 = 1*TICRATE;
9601 			}
9602 		}
9603 		//else if (G_CoopGametype()) -- moved to G_DoReborn
9604 	}
9605 
9606 	if (G_CoopGametype() && (multiplayer || netgame) && (player->lives <= 0) && (player->deadtimer >= 8*TICRATE || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))))
9607 	{
9608 		//player->spectator = true;
9609 		player->outofcoop = true;
9610 		player->playerstate = PST_REBORN;
9611 	}
9612 
9613 	if ((gametyperules & GTR_RACE) || (G_CoopGametype() && (multiplayer || netgame)))
9614 	{
9615 		// Keep time rolling in race mode
9616 		if (!(countdown2 && !countdown) && !player->exiting && !(player->pflags & PF_GAMETYPEOVER) && !stoppedclock)
9617 		{
9618 			if (gametyperules & GTR_RACE)
9619 			{
9620 				if (leveltime >= 4*TICRATE)
9621 					player->realtime = leveltime - 4*TICRATE;
9622 				else
9623 					player->realtime = 0;
9624 			}
9625 			else
9626 				player->realtime = leveltime;
9627 		}
9628 
9629 		// Return to level music
9630 		if (!G_CoopGametype() && player->lives <= 0 && player->deadtimer == gameovertics)
9631 			P_RestoreMultiMusic(player);
9632 	}
9633 
9634 notrealplayer:
9635 
9636 	if (!player->mo)
9637 		return;
9638 
9639 	P_CalcHeight(player);
9640 }
9641 
9642 //
9643 // P_MoveCamera: make sure the camera is not outside the world and looks at the player avatar
9644 //
9645 
9646 camera_t camera, camera2; // Two cameras.. one for split!
9647 
CV_CamRotate_OnChange(void)9648 static void CV_CamRotate_OnChange(void)
9649 {
9650 	if (cv_cam_rotate.value < 0)
9651 		CV_SetValue(&cv_cam_rotate, cv_cam_rotate.value + 360);
9652 	else if (cv_cam_rotate.value > 359)
9653 		CV_SetValue(&cv_cam_rotate, cv_cam_rotate.value % 360);
9654 }
9655 
CV_CamRotate2_OnChange(void)9656 static void CV_CamRotate2_OnChange(void)
9657 {
9658 	if (cv_cam2_rotate.value < 0)
9659 		CV_SetValue(&cv_cam2_rotate, cv_cam2_rotate.value + 360);
9660 	else if (cv_cam2_rotate.value > 359)
9661 		CV_SetValue(&cv_cam2_rotate, cv_cam2_rotate.value % 360);
9662 }
9663 
9664 static CV_PossibleValue_t CV_CamSpeed[] = {{0, "MIN"}, {1*FRACUNIT, "MAX"}, {0, NULL}};
9665 static CV_PossibleValue_t rotation_cons_t[] = {{1, "MIN"}, {25, "MAX"}, {0, NULL}};
9666 static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}};
9667 static CV_PossibleValue_t multiplier_cons_t[] = {{0, "MIN"}, {3*FRACUNIT, "MAX"}, {0, NULL}};
9668 
9669 consvar_t cv_cam_dist = CVAR_INIT ("cam_curdist", "160", CV_FLOAT, NULL, NULL);
9670 consvar_t cv_cam_height = CVAR_INIT ("cam_curheight", "25", CV_FLOAT, NULL, NULL);
9671 consvar_t cv_cam_still = CVAR_INIT ("cam_still", "Off", 0, CV_OnOff, NULL);
9672 consvar_t cv_cam_speed = CVAR_INIT ("cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL);
9673 consvar_t cv_cam_rotate = CVAR_INIT ("cam_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate_OnChange);
9674 consvar_t cv_cam_rotspeed = CVAR_INIT ("cam_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL);
9675 consvar_t cv_cam_turnmultiplier = CVAR_INIT ("cam_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL);
9676 consvar_t cv_cam_orbit = CVAR_INIT ("cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL);
9677 consvar_t cv_cam_adjust = CVAR_INIT ("cam_adjust", "On", CV_SAVE, CV_OnOff, NULL);
9678 consvar_t cv_cam2_dist = CVAR_INIT ("cam2_curdist", "160", CV_FLOAT, NULL, NULL);
9679 consvar_t cv_cam2_height = CVAR_INIT ("cam2_curheight", "25", CV_FLOAT, NULL, NULL);
9680 consvar_t cv_cam2_still = CVAR_INIT ("cam2_still", "Off", 0, CV_OnOff, NULL);
9681 consvar_t cv_cam2_speed = CVAR_INIT ("cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL);
9682 consvar_t cv_cam2_rotate = CVAR_INIT ("cam2_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate2_OnChange);
9683 consvar_t cv_cam2_rotspeed = CVAR_INIT ("cam2_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL);
9684 consvar_t cv_cam2_turnmultiplier = CVAR_INIT ("cam2_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL);
9685 consvar_t cv_cam2_orbit = CVAR_INIT ("cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL);
9686 consvar_t cv_cam2_adjust = CVAR_INIT ("cam2_adjust", "On", CV_SAVE, CV_OnOff, NULL);
9687 
9688 // [standard vs simple][p1 or p2]
9689 consvar_t cv_cam_savedist[2][2] = {
9690 	{ // standard
9691 		CVAR_INIT ("cam_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist),
9692 		CVAR_INIT ("cam2_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist),
9693 	},
9694 	{ // simple
9695 		CVAR_INIT ("cam_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist),
9696 		CVAR_INIT ("cam2_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist),
9697 
9698 	}
9699 };
9700 consvar_t cv_cam_saveheight[2][2] = {
9701 	{ // standard
9702 		CVAR_INIT ("cam_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist),
9703 		CVAR_INIT ("cam2_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist),
9704 	},
9705 	{ // simple
9706 		CVAR_INIT ("cam_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist),
9707 		CVAR_INIT ("cam2_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist),
9708 
9709 	}
9710 };
9711 
CV_UpdateCamDist(void)9712 void CV_UpdateCamDist(void)
9713 {
9714 	CV_Set(&cv_cam_dist, va("%f", FIXED_TO_FLOAT(cv_cam_savedist[cv_useranalog[0].value][0].value)));
9715 	CV_Set(&cv_cam_height, va("%f", FIXED_TO_FLOAT(cv_cam_saveheight[cv_useranalog[0].value][0].value)));
9716 }
9717 
CV_UpdateCam2Dist(void)9718 void CV_UpdateCam2Dist(void)
9719 {
9720 	CV_Set(&cv_cam2_dist, va("%f", FIXED_TO_FLOAT(cv_cam_savedist[cv_useranalog[1].value][1].value)));
9721 	CV_Set(&cv_cam2_height, va("%f", FIXED_TO_FLOAT(cv_cam_saveheight[cv_useranalog[1].value][1].value)));
9722 }
9723 
9724 fixed_t t_cam_dist = -42;
9725 fixed_t t_cam_height = -42;
9726 fixed_t t_cam_rotate = -42;
9727 fixed_t t_cam2_dist = -42;
9728 fixed_t t_cam2_height = -42;
9729 fixed_t t_cam2_rotate = -42;
9730 
9731 #define MAXCAMERADIST 140*FRACUNIT // Max distance the camera can be in front of the player (2D mode)
9732 
P_ResetCamera(player_t * player,camera_t * thiscam)9733 void P_ResetCamera(player_t *player, camera_t *thiscam)
9734 {
9735 	tic_t tries = 0;
9736 	fixed_t x, y, z;
9737 
9738 	if (!player->mo)
9739 		return;
9740 
9741 	if (thiscam->chase && player->mo->health <= 0)
9742 		return;
9743 
9744 	thiscam->chase = true;
9745 	x = player->mo->x - P_ReturnThrustX(player->mo, thiscam->angle, player->mo->radius);
9746 	y = player->mo->y - P_ReturnThrustY(player->mo, thiscam->angle, player->mo->radius);
9747 	if (player->mo->eflags & MFE_VERTICALFLIP)
9748 		z = player->mo->z + player->mo->height - (41*player->height/48) - 16*FRACUNIT;
9749 	else
9750 		z = player->mo->z + (41*player->height/48);
9751 
9752 	// set bits for the camera
9753 	thiscam->x = x;
9754 	thiscam->y = y;
9755 	thiscam->z = z;
9756 
9757 	if ((thiscam == &camera && G_ControlStyle(1) == CS_SIMPLE)
9758 	|| (thiscam == &camera2 && G_ControlStyle(2) == CS_SIMPLE))
9759 	{
9760 		thiscam->angle = P_GetLocalAngle(player);
9761 		thiscam->aiming = (thiscam == &camera) ? localaiming : localaiming2;
9762 	}
9763 	else if (!(thiscam == &camera && (cv_cam_still.value || cv_analog[0].value))
9764 	&& !(thiscam == &camera2 && (cv_cam2_still.value || cv_analog[1].value)))
9765 	{
9766 		thiscam->angle = player->mo->angle;
9767 		thiscam->aiming = 0;
9768 	}
9769 	thiscam->relativex = 0;
9770 
9771 	thiscam->subsector = R_PointInSubsector(thiscam->x,thiscam->y);
9772 
9773 	thiscam->radius = 20*FRACUNIT;
9774 	thiscam->height = 16*FRACUNIT;
9775 
9776 	while (!P_MoveChaseCamera(player,thiscam,true) && ++tries < 2*TICRATE);
9777 }
9778 
P_MoveChaseCamera(player_t * player,camera_t * thiscam,boolean resetcalled)9779 boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled)
9780 {
9781 	angle_t angle = 0, focusangle = 0, focusaiming = 0;
9782 	fixed_t x, y, z, dist, distxy, distz, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight, slopez = 0;
9783 	INT32 camrotate;
9784 	boolean camstill, cameranoclip, camorbit;
9785 	mobj_t *mo, *sign = NULL;
9786 	subsector_t *newsubsec;
9787 	fixed_t f1, f2;
9788 
9789 	static fixed_t camsideshift[2] = {0, 0};
9790 	fixed_t shiftx = 0, shifty = 0;
9791 
9792 	// We probably shouldn't move the camera if there is no player or player mobj somehow
9793 	if (!player || !player->mo)
9794 		return true;
9795 
9796 	mo = player->mo;
9797 
9798 	if (player->playerstate == PST_REBORN)
9799 	{
9800 		P_CalcChasePostImg(player, thiscam);
9801 		return true;
9802 	}
9803 
9804 	if (player->exiting)
9805 	{
9806 		if (mo->target && mo->target->type == MT_SIGN && mo->target->spawnpoint
9807 		&& !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value)
9808 		&& !(twodlevel || (mo->flags2 & MF2_TWOD)))
9809 			sign = mo->target;
9810 		else if ((player->powers[pw_carry] == CR_NIGHTSMODE)
9811 		&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
9812 		&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6]))
9813 		{
9814 			P_CalcChasePostImg(player, thiscam);
9815 			return true;
9816 		}
9817 	}
9818 
9819 	cameranoclip = (sign || player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
9820 
9821 	if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || tutorialmode))
9822 	{
9823 		if (player->spectator) // force cam off for spectators
9824 			return true;
9825 
9826 		if (!cv_chasecam.value && thiscam == &camera)
9827 			return true;
9828 
9829 		if (!cv_chasecam2.value && thiscam == &camera2)
9830 			return true;
9831 	}
9832 
9833 	if (!thiscam->chase && !resetcalled)
9834 	{
9835 		if (player == &players[consoleplayer])
9836 			focusangle = localangle;
9837 		else if (player == &players[secondarydisplayplayer])
9838 			focusangle = localangle2;
9839 		else
9840 			focusangle = mo->angle;
9841 		if (thiscam == &camera)
9842 			camrotate = cv_cam_rotate.value;
9843 		else if (thiscam == &camera2)
9844 			camrotate = cv_cam2_rotate.value;
9845 		else
9846 			camrotate = 0;
9847 		thiscam->angle = focusangle + FixedAngle(camrotate*FRACUNIT);
9848 		P_ResetCamera(player, thiscam);
9849 		return true;
9850 	}
9851 
9852 	thiscam->radius = FixedMul(20*FRACUNIT, mo->scale);
9853 	thiscam->height = FixedMul(16*FRACUNIT, mo->scale);
9854 
9855 	// Don't run while respawning from a starpost
9856 	// Inu 4/8/13 Why not?!
9857 //	if (leveltime > 0 && timeinmap <= 0)
9858 //		return true;
9859 
9860 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
9861 	{
9862 		focusangle = mo->angle;
9863 		focusaiming = 0;
9864 	}
9865 	else if (sign)
9866 	{
9867 		focusangle = FixedAngle(sign->spawnpoint->angle << FRACBITS) + ANGLE_180;
9868 		focusaiming = 0;
9869 	}
9870 	else if (player == &players[consoleplayer])
9871 	{
9872 		focusangle = localangle;
9873 		focusaiming = localaiming;
9874 	}
9875 	else if (player == &players[secondarydisplayplayer])
9876 	{
9877 		focusangle = localangle2;
9878 		focusaiming = localaiming2;
9879 	}
9880 	else
9881 	{
9882 		focusangle = player->cmd.angleturn << 16;
9883 		focusaiming = player->aiming;
9884 	}
9885 
9886 	if (P_CameraThinker(player, thiscam, resetcalled))
9887 		return true;
9888 
9889 	if (tutorialmode)
9890 	{
9891 		// force defaults because we have a camera look section
9892 		camspeed = (INT32)(atof(cv_cam_speed.defaultvalue) * FRACUNIT);
9893 		camstill = (!stricmp(cv_cam_still.defaultvalue, "off")) ? false : true;
9894 		camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true;
9895 		camrotate = atoi(cv_cam_rotate.defaultvalue);
9896 		camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale);
9897 		camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), mo->scale);
9898 	}
9899 	else if (thiscam == &camera)
9900 	{
9901 		camspeed = cv_cam_speed.value;
9902 		camstill = cv_cam_still.value;
9903 		camorbit = cv_cam_orbit.value;
9904 		camrotate = cv_cam_rotate.value;
9905 		camdist = FixedMul(cv_cam_dist.value, mo->scale);
9906 		camheight = FixedMul(cv_cam_height.value, mo->scale);
9907 	}
9908 	else // Camera 2
9909 	{
9910 		camspeed = cv_cam2_speed.value;
9911 		camstill = cv_cam2_still.value;
9912 		camorbit = cv_cam2_orbit.value;
9913 		camrotate = cv_cam2_rotate.value;
9914 		camdist = FixedMul(cv_cam2_dist.value, mo->scale);
9915 		camheight = FixedMul(cv_cam2_height.value, mo->scale);
9916 	}
9917 
9918 	if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
9919 		camheight = FixedMul(camheight, player->camerascale);
9920 
9921 #ifdef REDSANALOG
9922 	if (P_ControlStyle(player) == CS_LMAOGALOG && (player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) {
9923 		camstill = true;
9924 
9925 		if (camspeed < 4*FRACUNIT/5)
9926 			camspeed = 4*FRACUNIT/5;
9927 	}
9928 #endif // REDSANALOG
9929 
9930 	if (mo->eflags & MFE_VERTICALFLIP)
9931 		camheight += thiscam->height;
9932 
9933 	if (twodlevel || (mo->flags2 & MF2_TWOD))
9934 		angle = ANGLE_90;
9935 	else if (camstill || resetcalled || player->playerstate == PST_DEAD)
9936 		angle = thiscam->angle;
9937 	else if (player->powers[pw_carry] == CR_NIGHTSMODE) // NiGHTS Level
9938 	{
9939 		if ((player->pflags & PF_TRANSFERTOCLOSEST) && player->axis1 && player->axis2)
9940 		{
9941 			angle = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y);
9942 			angle += ANGLE_90;
9943 		}
9944 		else if (mo->target)
9945 		{
9946 			if (mo->target->flags2 & MF2_AMBUSH)
9947 				angle = R_PointToAngle2(mo->target->x, mo->target->y, mo->x, mo->y);
9948 			else
9949 				angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
9950 		}
9951 	}
9952 	else if (P_ControlStyle(player) == CS_LMAOGALOG && !sign) // Analog
9953 		angle = R_PointToAngle2(thiscam->x, thiscam->y, mo->x, mo->y);
9954 	else if (demoplayback)
9955 	{
9956 		angle = focusangle;
9957 		focusangle = R_PointToAngle2(thiscam->x, thiscam->y, mo->x, mo->y);
9958 		if (player == &players[consoleplayer])
9959 		{
9960 			if (focusangle >= localangle)
9961 				P_ForceLocalAngle(player, localangle + (abs((signed)(focusangle - localangle))>>5));
9962 			else
9963 				P_ForceLocalAngle(player, localangle - (abs((signed)(focusangle - localangle))>>5));
9964 		}
9965 	}
9966 	else
9967 		angle = focusangle + FixedAngle(camrotate*FRACUNIT);
9968 
9969 	if (!resetcalled && (cv_analog[0].value || demoplayback) && ((thiscam == &camera && t_cam_rotate != -42) || (thiscam == &camera2
9970 		&& t_cam2_rotate != -42)))
9971 	{
9972 		angle = FixedAngle(camrotate*FRACUNIT);
9973 		thiscam->angle = angle;
9974 	}
9975 
9976 	if ((((thiscam == &camera) && cv_analog[0].value) || ((thiscam != &camera) && cv_analog[1].value) || demoplayback) && !sign && !objectplacing && !(twodlevel || (mo->flags2 & MF2_TWOD)) && (player->powers[pw_carry] != CR_NIGHTSMODE) && displayplayer == consoleplayer)
9977 	{
9978 #ifdef REDSANALOG
9979 		if ((player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)); else
9980 #endif
9981 		if (player->cmd.buttons & BT_CAMRIGHT)
9982 		{
9983 			if (thiscam == &camera)
9984 				angle -= FixedAngle(cv_cam_rotspeed.value*FRACUNIT);
9985 			else
9986 				angle -= FixedAngle(cv_cam2_rotspeed.value*FRACUNIT);
9987 		}
9988 		else if (player->cmd.buttons & BT_CAMLEFT)
9989 		{
9990 			if (thiscam == &camera)
9991 				angle += FixedAngle(cv_cam_rotspeed.value*FRACUNIT);
9992 			else
9993 				angle += FixedAngle(cv_cam2_rotspeed.value*FRACUNIT);
9994 		}
9995 	}
9996 
9997 	if (G_ControlStyle((thiscam == &camera) ? 1 : 2) == CS_SIMPLE && !sign)
9998 	{
9999 		// Shift the camera slightly to the sides depending on the player facing direction
10000 		UINT8 forplayer = (thiscam == &camera) ? 0 : 1;
10001 		fixed_t shift = FixedMul(FINESINE((player->mo->angle - angle) >> ANGLETOFINESHIFT), cv_cam_shiftfacing[forplayer].value);
10002 
10003 		if (player->powers[pw_carry] == CR_NIGHTSMODE)
10004 		{
10005 			fixed_t cos = FINECOSINE((angle_t) (player->flyangle * ANG1)>>ANGLETOFINESHIFT);
10006 			shift = FixedMul(shift, min(FRACUNIT, player->speed*abs(cos)/6000));
10007 			shift += FixedMul(camsideshift[forplayer] - shift, FRACUNIT-(camspeed>>2));
10008 		}
10009 		else if (ticcmd_centerviewdown[(thiscam == &camera) ? 0 : 1])
10010 			shift = FixedMul(camsideshift[forplayer], FRACUNIT-camspeed);
10011 		else
10012 			shift += FixedMul(camsideshift[forplayer] - shift, FRACUNIT-(camspeed>>3));
10013 		camsideshift[forplayer] = shift;
10014 
10015 		shift = FixedMul(shift, camdist);
10016 		shiftx = -FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), shift);
10017 		shifty = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), shift);
10018 	}
10019 
10020 	// sets ideal cam pos
10021 	if (twodlevel || (mo->flags2 & MF2_TWOD))
10022 		dist = 480<<FRACBITS;
10023 	else if (player->powers[pw_carry] == CR_NIGHTSMODE
10024 		|| ((maptol & TOL_NIGHTS) && player->capsule && player->capsule->reactiontime > 0 && player == &players[player->capsule->reactiontime-1]))
10025 		dist = 320<<FRACBITS;
10026 	else
10027 	{
10028 		dist = camdist;
10029 
10030 		if (sign) // signpost camera has specific placement
10031 		{
10032 			camheight = mo->scale << 7;
10033 			camspeed = FRACUNIT/12;
10034 		}
10035 		else if (P_ControlStyle(player) == CS_LMAOGALOG) // x1.2 dist for analog
10036 		{
10037 			dist = FixedMul(dist, 6*FRACUNIT/5);
10038 			camheight = FixedMul(camheight, 6*FRACUNIT/5);
10039 		}
10040 
10041 		if (player->climbing || player->exiting || player->playerstate == PST_DEAD || (player->powers[pw_carry] == CR_ROPEHANG || player->powers[pw_carry] == CR_GENERIC || player->powers[pw_carry] == CR_MACESPIN))
10042 			dist <<= 1;
10043 	}
10044 
10045 	if (!sign && !(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
10046 		dist = FixedMul(dist, player->camerascale);
10047 
10048 	checkdist = dist;
10049 
10050 	if (checkdist < 128*FRACUNIT)
10051 		checkdist = 128*FRACUNIT;
10052 
10053 	if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) // This block here is like 90% Lach's work, thanks bud
10054 	{
10055 		if (!resetcalled && ((thiscam == &camera && cv_cam_adjust.value) || (thiscam == &camera2 && cv_cam2_adjust.value)))
10056 		{
10057 			if (!(mo->eflags & MFE_JUSTHITFLOOR) && (P_IsObjectOnGround(mo)) // Check that player is grounded
10058 			&& thiscam->ceilingz - thiscam->floorz >= P_GetPlayerHeight(player)) // Check that camera's sector is large enough for the player to fit into, at least
10059 			{
10060 				if (mo->eflags & MFE_VERTICALFLIP) // if player is upside-down
10061 				{
10062 					//z = min(z, thiscam->ceilingz); // solution 1: change new z coordinate to be at LEAST its ground height
10063 					slopez += min(thiscam->ceilingz - mo->z, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player
10064 				}
10065 				else // player is not upside-down
10066 				{
10067 					//z = max(z, thiscam->floorz); // solution 1: change new z coordinate to be at LEAST its ground height
10068 					slopez += max(thiscam->floorz - mo->z - mo->height, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player
10069 				}
10070 			}
10071 		}
10072 	}
10073 
10074 	if (camorbit) //Sev here, I'm guessing this is where orbital cam lives
10075 	{
10076 #ifdef HWRENDER
10077 		if (rendermode == render_opengl && !cv_glshearing.value)
10078 			distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK));
10079 		else
10080 #endif
10081 			distxy = dist;
10082 		distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)) + slopez;
10083 	}
10084 	else
10085 	{
10086 		distxy = dist;
10087 		distz = slopez;
10088 	}
10089 
10090 	if (sign)
10091 	{
10092 		x = sign->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy);
10093 		y = sign->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy);
10094 	}
10095 	else
10096 	{
10097 		x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy);
10098 		y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy);
10099 	}
10100 
10101 #if 0
10102 	if (twodlevel || (mo->flags2 & MF2_TWOD))
10103 	{
10104 		// Camera doesn't ALWAYS need to move, only when running...
10105 		if (abs(mo->momx) > 10)
10106 		{
10107 			// Move the camera all smooth-like, not jerk it around...
10108 			if (mo->momx > 0)
10109 			{
10110 				if (thiscam->relativex < MAXCAMERADIST)
10111 					thiscam->relativex += 4*FRACUNIT;
10112 			}
10113 			else if (mo->momx < 0)
10114 			{
10115 				if (thiscam->relativex > -MAXCAMERADIST)
10116 					thiscam->relativex -= 4*FRACUNIT;
10117 			}
10118 		}
10119 		else // If speed is less than required, start moving the camera back.
10120 		{
10121 			if (thiscam->relativex > 0)
10122 				thiscam->relativex -= 4*FRACUNIT;
10123 			else if (thiscam->relativex < 0)
10124 				thiscam->relativex += 4*FRACUNIT;
10125 		}
10126 
10127 		// Add the relative x to the global x
10128 		x += thiscam->relativex;
10129 		y += mo->momy << 1;
10130 	}
10131 #endif // bad 2D camera code
10132 
10133 	pviewheight = FixedMul(41*player->height/48, mo->scale);
10134 
10135 	if (sign)
10136 	{
10137 		if (sign->eflags & MFE_VERTICALFLIP)
10138 			z = sign->ceilingz - pviewheight - camheight;
10139 		else
10140 			z = sign->floorz + pviewheight + camheight;
10141 	}
10142 	else
10143 	{
10144 		if (mo->eflags & MFE_VERTICALFLIP)
10145 			z = mo->z + mo->height - pviewheight - camheight + distz;
10146 		else
10147 			z = mo->z + pviewheight + camheight + distz;
10148 	}
10149 
10150 	// move camera down to move under lower ceilings
10151 	newsubsec = R_PointInSubsectorOrNull(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
10152 
10153 	if (!newsubsec)
10154 		newsubsec = thiscam->subsector;
10155 
10156 	if (newsubsec)
10157 	{
10158 		fixed_t myfloorz, myceilingz;
10159 		fixed_t midz = thiscam->z + (thiscam->z - mo->z)/2;
10160 		fixed_t midx = ((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1);
10161 		fixed_t midy = ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1);
10162 
10163 		// Cameras use the heightsec's heights rather then the actual sector heights.
10164 		// If you can see through it, why not move the camera through it too?
10165 		if (newsubsec->sector->camsec >= 0)
10166 		{
10167 			myfloorz = sectors[newsubsec->sector->camsec].floorheight;
10168 			myceilingz = sectors[newsubsec->sector->camsec].ceilingheight;
10169 		}
10170 		else if (newsubsec->sector->heightsec >= 0)
10171 		{
10172 			myfloorz = sectors[newsubsec->sector->heightsec].floorheight;
10173 			myceilingz = sectors[newsubsec->sector->heightsec].ceilingheight;
10174 		}
10175 		else
10176 		{
10177 			myfloorz = P_CameraGetFloorZ(thiscam, newsubsec->sector, midx, midy, NULL);
10178 			myceilingz = P_CameraGetCeilingZ(thiscam, newsubsec->sector, midx, midy, NULL);
10179 		}
10180 
10181 		// Check list of fake floors and see if floorz/ceilingz need to be altered.
10182 		if (newsubsec->sector->ffloors)
10183 		{
10184 			ffloor_t *rover;
10185 			fixed_t delta1, delta2;
10186 			INT32 thingtop = midz + thiscam->height;
10187 
10188 			for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
10189 			{
10190 				fixed_t topheight, bottomheight;
10191 				if (!(rover->flags & FF_BLOCKOTHERS) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERALL) || GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
10192 					continue;
10193 
10194 				topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
10195 				bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
10196 
10197 				delta1 = midz - (bottomheight
10198 					+ ((topheight - bottomheight)/2));
10199 				delta2 = thingtop - (bottomheight
10200 					+ ((topheight - bottomheight)/2));
10201 				if (topheight > myfloorz && abs(delta1) < abs(delta2))
10202 					myfloorz = topheight;
10203 				if (bottomheight < myceilingz && abs(delta1) >= abs(delta2))
10204 					myceilingz = bottomheight;
10205 			}
10206 		}
10207 
10208 	// Check polyobjects and see if floorz/ceilingz need to be altered
10209 	{
10210 		INT32 xl, xh, yl, yh, bx, by;
10211 		validcount++;
10212 
10213 		xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
10214 		xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
10215 		yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
10216 		yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
10217 
10218 		BMBOUNDFIX(xl, xh, yl, yh);
10219 
10220 		for (by = yl; by <= yh; by++)
10221 			for (bx = xl; bx <= xh; bx++)
10222 			{
10223 				INT32 offset;
10224 				polymaplink_t *plink; // haleyjd 02/22/06
10225 
10226 				if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
10227 					continue;
10228 
10229 				offset = by*bmapwidth + bx;
10230 
10231 				// haleyjd 02/22/06: consider polyobject lines
10232 				plink = polyblocklinks[offset];
10233 
10234 				while (plink)
10235 				{
10236 					polyobj_t *po = plink->po;
10237 
10238 					if (po->validcount != validcount) // if polyobj hasn't been checked
10239 					{
10240 						sector_t *polysec;
10241 						fixed_t delta1, delta2, thingtop;
10242 						fixed_t polytop, polybottom;
10243 
10244 						po->validcount = validcount;
10245 
10246 						if (!P_PointInsidePolyobj(po, x, y) || !(po->flags & POF_SOLID))
10247 						{
10248 							plink = (polymaplink_t *)(plink->link.next);
10249 							continue;
10250 						}
10251 
10252 						// We're inside it! Yess...
10253 						polysec = po->lines[0]->backsector;
10254 
10255 						if (GETSECSPECIAL(polysec->special, 4) == 12)
10256 						{ // Camera noclip polyobj.
10257 							plink = (polymaplink_t *)(plink->link.next);
10258 							continue;
10259 						}
10260 
10261 						if (po->flags & POF_CLIPPLANES)
10262 						{
10263 							polytop = polysec->ceilingheight;
10264 							polybottom = polysec->floorheight;
10265 						}
10266 						else
10267 						{
10268 							polytop = INT32_MAX;
10269 							polybottom = INT32_MIN;
10270 						}
10271 
10272 						thingtop = midz + thiscam->height;
10273 						delta1 = midz - (polybottom + ((polytop - polybottom)/2));
10274 						delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
10275 
10276 						if (polytop > myfloorz && abs(delta1) < abs(delta2))
10277 							myfloorz = polytop;
10278 
10279 						if (polybottom < myceilingz && abs(delta1) >= abs(delta2))
10280 							myceilingz = polybottom;
10281 					}
10282 					plink = (polymaplink_t *)(plink->link.next);
10283 				}
10284 			}
10285 	}
10286 
10287 		// crushed camera
10288 		if (myceilingz <= myfloorz + thiscam->height && !resetcalled && !cameranoclip)
10289 		{
10290 			P_ResetCamera(player, thiscam);
10291 			return true;
10292 		}
10293 
10294 		// camera fit?
10295 		if (myceilingz != myfloorz
10296 			&& myceilingz - thiscam->height < z)
10297 		{
10298 /*			// no fit
10299 			if (!resetcalled && !cameranoclip)
10300 			{
10301 				P_ResetCamera(player, thiscam);
10302 				return true;
10303 			}
10304 */
10305 			z = myceilingz - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
10306 			// is the camera fit is there own sector
10307 		}
10308 
10309 		// Make the camera a tad smarter with 3d floors
10310 		if (newsubsec->sector->ffloors && !cameranoclip)
10311 		{
10312 			ffloor_t *rover;
10313 
10314 			for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
10315 			{
10316 				fixed_t topheight, bottomheight;
10317 				if ((rover->flags & FF_BLOCKOTHERS) && (rover->flags & FF_RENDERALL) && (rover->flags & FF_EXISTS) && GETSECSPECIAL(rover->master->frontsector->special, 4) != 12)
10318 				{
10319 					topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
10320 					bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
10321 
10322 					if (bottomheight - thiscam->height < z
10323 						&& midz < bottomheight)
10324 						z = bottomheight - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
10325 
10326 					else if (topheight + thiscam->height > z
10327 						&& midz > topheight)
10328 						z = topheight;
10329 
10330 					if ((mo->z >= topheight && midz < bottomheight)
10331 						|| ((mo->z < bottomheight && mo->z+mo->height < topheight) && midz >= topheight))
10332 					{
10333 						// Can't see
10334 						if (!resetcalled)
10335 							P_ResetCamera(player, thiscam);
10336 						return true;
10337 					}
10338 				}
10339 			}
10340 		}
10341 	}
10342 
10343 	if (mo->type == MT_EGGTRAP)
10344 		z = mo->z + 128*FRACUNIT + pviewheight + camheight;
10345 
10346 	if (thiscam->z < thiscam->floorz && !cameranoclip)
10347 		thiscam->z = thiscam->floorz;
10348 
10349 	// point viewed by the camera
10350 	// this point is just 64 unit forward the player
10351 	dist = FixedMul(64 << FRACBITS, mo->scale);
10352 	if (sign)
10353 	{
10354 		viewpointx = sign->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
10355 		viewpointy = sign->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
10356 	}
10357 	else
10358 	{
10359 		viewpointx = mo->x + shiftx + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
10360 		viewpointy = mo->y + shifty + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
10361 	}
10362 
10363 	if (!camstill && !resetcalled && !paused)
10364 		thiscam->angle = R_PointToAngle2(thiscam->x, thiscam->y, viewpointx, viewpointy);
10365 
10366 /*
10367 	if (twodlevel || (mo->flags2 & MF2_TWOD))
10368 		thiscam->angle = angle;
10369 */
10370 	// follow the player
10371 	/*if (player->playerstate != PST_DEAD && (camspeed) != 0)
10372 	{
10373 		if (P_AproxDistance(mo->x - thiscam->x, mo->y - thiscam->y) > (checkdist + P_AproxDistance(mo->momx, mo->momy)) * 4
10374 			|| abs(mo->z - thiscam->z) > checkdist * 3)
10375 		{
10376 			if (!resetcalled)
10377 				P_ResetCamera(player, thiscam);
10378 			return true;
10379 		}
10380 	}*/
10381 
10382 	if (twodlevel || (mo->flags2 & MF2_TWOD))
10383 	{
10384 		thiscam->momx = x-thiscam->x;
10385 		thiscam->momy = y-thiscam->y;
10386 		thiscam->momz = z-thiscam->z;
10387 	}
10388 	else
10389 	{
10390 		thiscam->momx = FixedMul(x - thiscam->x, camspeed);
10391 		thiscam->momy = FixedMul(y - thiscam->y, camspeed);
10392 
10393 		if (GETSECSPECIAL(thiscam->subsector->sector->special, 1) == 6
10394 			&& thiscam->z < thiscam->subsector->sector->floorheight + 256*FRACUNIT
10395 			&& FixedMul(z - thiscam->z, camspeed) < 0)
10396 		{
10397 			thiscam->momz = 0; // Don't go down a death pit
10398 		}
10399 		else
10400 			thiscam->momz = FixedMul(z - thiscam->z, camspeed);
10401 
10402 		thiscam->momx += FixedMul(shiftx, camspeed);
10403 		thiscam->momy += FixedMul(shifty, camspeed);
10404 	}
10405 
10406 	// compute aming to look the viewed point
10407 	f1 = viewpointx-thiscam->x;
10408 	f2 = viewpointy-thiscam->y;
10409 	dist = FixedHypot(f1, f2);
10410 
10411 	if (mo->eflags & MFE_VERTICALFLIP)
10412 		angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, (sign ? sign->ceilingz : mo->z + mo->height) - P_GetPlayerHeight(player));
10413 	else
10414 		angle = R_PointToAngle2(0, thiscam->z, dist, (sign ? sign->floorz : mo->z) + P_GetPlayerHeight(player));
10415 	if (player->playerstate != PST_DEAD)
10416 		angle += (focusaiming < ANGLE_180 ? focusaiming/2 : InvAngle(InvAngle(focusaiming)/2)); // overcomplicated version of '((signed)focusaiming)/2;'
10417 
10418 	if (twodlevel || (mo->flags2 & MF2_TWOD) || !camstill) // Keep the view still...
10419 	{
10420 		G_ClipAimingPitch((INT32 *)&angle);
10421 		dist = thiscam->aiming - angle;
10422 		thiscam->aiming -= (dist>>3);
10423 	}
10424 
10425 	// Make player translucent if camera is too close (only in single player).
10426 	if (!(multiplayer || netgame) && !splitscreen)
10427 	{
10428 		fixed_t vx = thiscam->x, vy = thiscam->y;
10429 		fixed_t vz = thiscam->z + thiscam->height / 2;
10430 		if (player->awayviewtics && player->awayviewmobj != NULL && !P_MobjWasRemoved(player->awayviewmobj))		// Camera must obviously exist
10431 		{
10432 			vx = player->awayviewmobj->x;
10433 			vy = player->awayviewmobj->y;
10434 			vz = player->awayviewmobj->z + player->awayviewmobj->height / 2;
10435 		}
10436 
10437 		/* check z distance too for orbital camera */
10438 		if (P_AproxDistance(P_AproxDistance(vx - mo->x, vy - mo->y),
10439 					vz - ( mo->z + mo->height / 2 )) < FixedMul(48*FRACUNIT, mo->scale))
10440 			mo->flags2 |= MF2_SHADOW;
10441 		else
10442 			mo->flags2 &= ~MF2_SHADOW;
10443 	}
10444 	else
10445 		mo->flags2 &= ~MF2_SHADOW;
10446 
10447 /*	if (!resetcalled && (player->powers[pw_carry] == CR_NIGHTSMODE && player->exiting))
10448 	{
10449 		// Don't let the camera match your movement.
10450 		thiscam->momz = 0;
10451 
10452 		// Only let the camera go a little bit upwards.
10453 		if (mo->eflags & MFE_VERTICALFLIP && thiscam->aiming < ANGLE_315 && thiscam->aiming > ANGLE_180)
10454 			thiscam->aiming = ANGLE_315;
10455 		else if (!(mo->eflags & MFE_VERTICALFLIP) && thiscam->aiming > ANGLE_45 && thiscam->aiming < ANGLE_180)
10456 			thiscam->aiming = ANGLE_45;
10457 	}
10458 	else */if (!resetcalled && (player->playerstate == PST_DEAD || player->playerstate == PST_REBORN))
10459 	{
10460 		// Don't let the camera match your movement.
10461 		thiscam->momz = 0;
10462 
10463 		// Only let the camera go a little bit downwards.
10464 		if (!(mo->eflags & MFE_VERTICALFLIP) && thiscam->aiming < ANGLE_337h && thiscam->aiming > ANGLE_180)
10465 			thiscam->aiming = ANGLE_337h;
10466 		else if (mo->eflags & MFE_VERTICALFLIP && thiscam->aiming > ANGLE_22h && thiscam->aiming < ANGLE_180)
10467 			thiscam->aiming = ANGLE_22h;
10468 	}
10469 
10470 	return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming);
10471 
10472 }
10473 
P_SpectatorJoinGame(player_t * player)10474 boolean P_SpectatorJoinGame(player_t *player)
10475 {
10476 	if (!G_CoopGametype() && !cv_allowteamchange.value)
10477 	{
10478 		if (P_IsLocalPlayer(player))
10479 			CONS_Printf(M_GetText("Server does not allow team change.\n"));
10480 		player->powers[pw_flashing] += 2*TICRATE; //to prevent message spam.
10481 	}
10482 
10483 	// Team changing in Team Match and CTF
10484 	// Pressing fire assigns you to a team that needs players if allowed.
10485 	// Partial code reproduction from p_tick.c autobalance code.
10486 	else if (G_GametypeHasTeams())
10487 	{
10488 		INT32 changeto = 0;
10489 		INT32 z, numplayersred = 0, numplayersblue = 0;
10490 
10491 		//find a team by num players, score, or random if all else fails.
10492 		for (z = 0; z < MAXPLAYERS; ++z)
10493 			if (playeringame[z])
10494 			{
10495 				if (players[z].ctfteam == 1)
10496 					++numplayersred;
10497 				else if (players[z].ctfteam == 2)
10498 					++numplayersblue;
10499 			}
10500 		// for z
10501 
10502 		if (numplayersblue > numplayersred)
10503 			changeto = 1;
10504 		else if (numplayersred > numplayersblue)
10505 			changeto = 2;
10506 		else if (bluescore > redscore)
10507 			changeto = 1;
10508 		else if (redscore > bluescore)
10509 			changeto = 2;
10510 		else
10511 			changeto = (P_RandomFixed() & 1) + 1;
10512 
10513 		if (!LUAh_TeamSwitch(player, changeto, true, false, false))
10514 			return false;
10515 
10516 		if (player->mo)
10517 		{
10518 			P_RemoveMobj(player->mo);
10519 			player->mo = NULL;
10520 		}
10521 		player->spectator = false;
10522 		player->ctfteam = changeto;
10523 		player->playerstate = PST_REBORN;
10524 
10525 		//Reset away view
10526 		if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
10527 		{
10528 			// Call ViewpointSwitch hooks here.
10529 			// The viewpoint was forcibly changed.
10530 			LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
10531 			displayplayer = consoleplayer;
10532 		}
10533 
10534 		if (changeto == 1)
10535 			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80');
10536 		else if (changeto == 2)
10537 			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x84', M_GetText("Blue team"), '\x80');
10538 
10539 		return true; // no more player->mo, cannot continue.
10540 	}
10541 	// Joining in game from firing.
10542 	else
10543 	{
10544 		// Exception for hide and seek. Don't join a game when you simply
10545 		// respawn in place and sit there for the rest of the round.
10546 		if (!((gametyperules & GTR_HIDEFROZEN) && leveltime > (hidetime * TICRATE)))
10547 		{
10548 			if (!LUAh_TeamSwitch(player, 3, true, false, false))
10549 				return false;
10550 			if (player->mo)
10551 			{
10552 				P_RemoveMobj(player->mo);
10553 				player->mo = NULL;
10554 			}
10555 			player->spectator = player->outofcoop = false;
10556 			player->playerstate = PST_REBORN;
10557 
10558 			if ((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG)
10559 			{
10560 				//Make joining players "it" after hidetime.
10561 				if (leveltime > (hidetime * TICRATE))
10562 				{
10563 					CONS_Printf(M_GetText("%s is now IT!\n"), player_names[player-players]); // Tell everyone who is it!
10564 					player->pflags |= PF_TAGIT;
10565 				}
10566 
10567 				P_CheckSurvivors();
10568 			}
10569 
10570 			//Reset away view
10571 			if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
10572 			{
10573 				// Call ViewpointSwitch hooks here.
10574 				// The viewpoint was forcibly changed.
10575 				LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
10576 				displayplayer = consoleplayer;
10577 			}
10578 
10579 			if (!G_CoopGametype())
10580 				CONS_Printf(M_GetText("%s entered the game.\n"), player_names[player-players]);
10581 			return true; // no more player->mo, cannot continue.
10582 		}
10583 		else
10584 		{
10585 			if (P_IsLocalPlayer(player))
10586 				CONS_Printf(M_GetText("You must wait until next round to enter the game.\n"));
10587 			player->powers[pw_flashing] += 2*TICRATE; //to prevent message spam.
10588 		}
10589 	}
10590 	return false;
10591 }
10592 
10593 // the below is first person only, if you're curious. check out P_CalcChasePostImg in p_mobj.c for chasecam
P_CalcPostImg(player_t * player)10594 static void P_CalcPostImg(player_t *player)
10595 {
10596 	sector_t *sector = player->mo->subsector->sector;
10597 	postimg_t *type;
10598 	INT32 *param;
10599 	fixed_t pviewheight;
10600 	size_t i;
10601 
10602 	if (player->mo->eflags & MFE_VERTICALFLIP)
10603 		pviewheight = player->mo->z + player->mo->height - player->viewheight;
10604 	else
10605 		pviewheight = player->mo->z + player->viewheight;
10606 
10607 	if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj))
10608 	{
10609 		sector = player->awayviewmobj->subsector->sector;
10610 		pviewheight = player->awayviewmobj->z + 20*FRACUNIT;
10611 	}
10612 
10613 	if (splitscreen && player == &players[secondarydisplayplayer])
10614 	{
10615 		type = &postimgtype2;
10616 		param = &postimgparam2;
10617 	}
10618 	else
10619 	{
10620 		type = &postimgtype;
10621 		param = &postimgparam;
10622 	}
10623 
10624 	// see if we are in heat (no, not THAT kind of heat...)
10625 	for (i = 0; i < sector->tags.count; i++)
10626 	{
10627 		if (Tag_FindLineSpecial(13, sector->tags.tags[i]) != -1)
10628 		{
10629 			*type = postimg_heat;
10630 			break;
10631 		}
10632 		else if (sector->ffloors)
10633 		{
10634 			ffloor_t *rover;
10635 			fixed_t topheight;
10636 			fixed_t bottomheight;
10637 			boolean gotres = false;
10638 
10639 			for (rover = sector->ffloors; rover; rover = rover->next)
10640 			{
10641 				size_t j;
10642 
10643 				if (!(rover->flags & FF_EXISTS))
10644 					continue;
10645 
10646 				topheight    = P_GetFFloorTopZAt   (rover, player->mo->x, player->mo->y);
10647 				bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
10648 
10649 				if (pviewheight >= topheight || pviewheight <= bottomheight)
10650 					continue;
10651 
10652 				for (j = 0; j < rover->master->frontsector->tags.count; j++)
10653 				{
10654 					if (Tag_FindLineSpecial(13, rover->master->frontsector->tags.tags[j]) != -1)
10655 					{
10656 						*type = postimg_heat;
10657 						gotres = true;
10658 						break;
10659 					}
10660 				}
10661 			}
10662 			if (gotres)
10663 				break;
10664 		}
10665 	}
10666 
10667 	// see if we are in water (water trumps heat)
10668 	if (sector->ffloors)
10669 	{
10670 		ffloor_t *rover;
10671 		fixed_t topheight;
10672 		fixed_t bottomheight;
10673 
10674 		for (rover = sector->ffloors; rover; rover = rover->next)
10675 		{
10676 			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || rover->flags & FF_BLOCKPLAYER)
10677 				continue;
10678 
10679 			topheight    = P_GetFFloorTopZAt   (rover, player->mo->x, player->mo->y);
10680 			bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y);
10681 
10682 			if (pviewheight >= topheight || pviewheight <= bottomheight)
10683 				continue;
10684 
10685 			*type = postimg_water;
10686 		}
10687 	}
10688 
10689 	if (player->mo->eflags & MFE_VERTICALFLIP)
10690 		*type = postimg_flip;
10691 
10692 #if 1
10693 	(void)param;
10694 #else
10695 	// Motion blur
10696 	if (player->speed > (35<<FRACBITS))
10697 	{
10698 		*type = postimg_motion;
10699 		*param = (player->speed - 32)/4;
10700 
10701 		if (*param > 5)
10702 			*param = 5;
10703 	}
10704 #endif
10705 }
10706 
P_DoPityCheck(player_t * player)10707 void P_DoPityCheck(player_t *player)
10708 {
10709 	// No pity outside of match or CTF.
10710 	if (player->spectator
10711 		|| !(gametyperules & GTR_PITYSHIELD))
10712 		return;
10713 
10714 	// Apply pity shield if available.
10715 	if ((player->pity >= 3 || player->pity < 0) && player->powers[pw_shield] == SH_NONE)
10716 	{
10717 		P_SwitchShield(player, SH_PITY);
10718 
10719 		if (player->pity > 0)
10720 			S_StartSound(player->mo, mobjinfo[MT_PITY_ICON].seesound);
10721 
10722 		player->pity = 0;
10723 	}
10724 }
10725 
10726 
P_GetMinecartSector(fixed_t x,fixed_t y,fixed_t z,fixed_t * nz)10727 static sector_t *P_GetMinecartSector(fixed_t x, fixed_t y, fixed_t z, fixed_t *nz)
10728 {
10729 	sector_t *sec = R_PointInSubsector(x, y)->sector;
10730 
10731 	if ((sec->ceilingheight - sec->floorheight) < 64*FRACUNIT)
10732 		return NULL;
10733 
10734 	if (sec->ffloors)
10735 	{
10736 		ffloor_t *rover;
10737 		for (rover = sec->ffloors; rover; rover = rover->next)
10738 		{
10739 			if (!(rover->flags & (FF_EXISTS|FF_BLOCKOTHERS)))
10740 				continue;
10741 
10742 			*nz = P_GetFFloorTopZAt(rover, x, y);
10743 			if (abs(z - *nz) <= 56*FRACUNIT)
10744 			{
10745 				sec = &sectors[rover->secnum];
10746 				return sec;
10747 			}
10748 		}
10749 
10750 	}
10751 
10752 	*nz = P_GetSectorFloorZAt(sec, x, y);
10753 	if (abs(z - *nz) > 56*FRACUNIT)
10754 		return NULL;
10755 
10756 	return sec;
10757 }
10758 
P_GetMinecartSpecialLine(sector_t * sec)10759 static INT32 P_GetMinecartSpecialLine(sector_t *sec)
10760 {
10761 	INT32 line = -1;
10762 	size_t i;
10763 
10764 	if (!sec)
10765 		return line;
10766 
10767 	for (i = 0; i < sec->tags.count; i++)
10768 		if (sec->tags.tags[i] != 0)
10769 			line = Tag_FindLineSpecial(16, sec->tags.tags[i]);
10770 
10771 	// Also try for lines facing the sector itself, with tag 0.
10772 	for (i = 0; i < sec->linecount; i++)
10773 	{
10774 		line_t *li = sec->lines[i];
10775 		if (Tag_Find(&li->tags, 0) && li->special == 16 && li->frontsector == sec)
10776 			line = li - lines;
10777 	}
10778 
10779 	return line;
10780 }
10781 
10782 // Get an axis of a certain ID number
P_GetAxis(INT32 num)10783 static mobj_t *P_GetAxis(INT32 num)
10784 {
10785 	thinker_t *th;
10786 	mobj_t *mobj;
10787 
10788 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
10789 	{
10790 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
10791 			continue;
10792 
10793 		mobj = (mobj_t *)th;
10794 
10795 		// NiGHTS axes spawn before anything else. If this mobj doesn't have MF2_AXIS, it means we reached the axes' end.
10796 		if (!(mobj->flags2 & MF2_AXIS))
10797 			break;
10798 
10799 		// Skip if this axis isn't the one we want.
10800 		if (mobj->health != num)
10801 			continue;
10802 
10803 		return mobj;
10804 	}
10805 
10806 	CONS_Alert(CONS_WARNING, "P_GetAxis: Track segment %d is missing!\n", num);
10807 	return NULL;
10808 }
10809 
10810 // Auxiliary function. For a given position and axis, it calculates the nearest "valid" snap-on position.
P_GetAxisPosition(fixed_t x,fixed_t y,mobj_t * amo,fixed_t * newx,fixed_t * newy,angle_t * targetangle,angle_t * grind)10811 static void P_GetAxisPosition(fixed_t x, fixed_t y, mobj_t *amo, fixed_t *newx, fixed_t *newy, angle_t *targetangle, angle_t *grind)
10812 {
10813 	fixed_t ax = amo->x;
10814 	fixed_t ay = amo->y;
10815 	angle_t ang;
10816 	angle_t gr = 0;
10817 
10818 	if (amo->type == MT_AXISTRANSFERLINE)
10819 	{
10820 		ang = amo->angle;
10821 		// Extra security for cardinal directions.
10822 		if (ang == ANGLE_90 || ang == ANGLE_270) // Vertical lines
10823 			x = ax;
10824 		else if (ang == 0 || ang == ANGLE_180) // Horizontal lines
10825 			y = ay;
10826 		else // Diagonal lines
10827 		{
10828 			fixed_t distance = R_PointToDist2(ax, ay, x, y);
10829 			angle_t fad = ((R_PointToAngle2(ax, ay, x, y) - ang) >> ANGLETOFINESHIFT) & FINEMASK;
10830 			fixed_t cosine = FINECOSINE(fad);
10831 			angle_t fa = (ang >> ANGLETOFINESHIFT) & FINEMASK;
10832 			distance = FixedMul(distance, cosine);
10833 			x = ax + FixedMul(distance, FINECOSINE(fa));
10834 			y = ay + FixedMul(distance, FINESINE(fa));
10835 		}
10836 	}
10837 	else // Keep minecart to circle
10838 	{
10839 		fixed_t rad = amo->radius;
10840 		fixed_t distfactor = FixedDiv(rad, R_PointToDist2(ax, ay, x, y));
10841 
10842 		gr = R_PointToAngle2(ax, ay, x, y);
10843 		ang = gr + ANGLE_90;
10844 		x = ax + FixedMul(x - ax, distfactor);
10845 		y = ay + FixedMul(y - ay, distfactor);
10846 	}
10847 
10848 	*newx = x;
10849 	*newy = y;
10850 	*targetangle = ang;
10851 	*grind = gr;
10852 }
10853 
P_SpawnSparks(mobj_t * mo,angle_t maindir)10854 static void P_SpawnSparks(mobj_t *mo, angle_t maindir)
10855 {
10856 	angle_t fa = (mo->angle >> ANGLETOFINESHIFT) & FINEMASK;
10857 	fixed_t c = FixedMul(FINECOSINE(fa), mo->radius);
10858 	fixed_t s = FixedMul(FINESINE(fa), mo->radius);
10859 	mobj_t *spark;
10860 	SINT8 b1 = (leveltime & 1) ? 1 : -1;
10861 	SINT8 b2 = (leveltime & 2) ? 1 : -1;
10862 	fixed_t r1 = FRACUNIT*P_RandomRange(-1, 1);
10863 	fixed_t r2 = FRACUNIT*P_RandomRange(-1, 1);
10864 	fixed_t r3 = FRACUNIT*P_RandomRange(-1, 1);
10865 	fixed_t fm = (maindir >> ANGLETOFINESHIFT) & FINEMASK;
10866 
10867 	spark = P_SpawnMobj(mo->x - b2*s + b1*c, mo->y + b2*c + b1*s, mo->z, MT_MINECARTSPARK);
10868 	spark->momx = mo->momx + r1 + 8*FINECOSINE(fm);
10869 	spark->momy = mo->momy + r2 + 8*FINESINE(fm);
10870 	spark->momz = mo->momz + r3;
10871 
10872 	P_Thrust(spark, R_PointToAngle2(mo->x, mo->y, spark->x, spark->y), 8*FRACUNIT);
10873 	P_SetScale(spark, FRACUNIT/4);
10874 	spark->destscale = spark->scale;
10875 	spark->fuse = TICRATE/3;
10876 }
10877 
10878 // Performs a proximity check on a given direction looking for rails.
P_LookForRails(mobj_t * mobj,fixed_t c,fixed_t s,angle_t targetangle,fixed_t xcom,fixed_t ycom)10879 static mobj_t *P_LookForRails(mobj_t* mobj, fixed_t c, fixed_t s, angle_t targetangle, fixed_t xcom, fixed_t ycom)
10880 {
10881 	INT16 interval = 16;
10882 	INT16 fwooffset = FixedHypot(mobj->momx, mobj->momy) >> FRACBITS;
10883 	fixed_t x = mobj->x;
10884 	fixed_t y = mobj->y;
10885 	fixed_t z = mobj->z;
10886 	UINT8 i;
10887 
10888 	for (i = 4; i <= 10; i++)
10889 	{
10890 		fixed_t nz;
10891 		INT32 lline;
10892 
10893 		x += interval*xcom*i + fwooffset*c*i;
10894 		y += interval*ycom*i + fwooffset*s*i;
10895 
10896 		lline = P_GetMinecartSpecialLine(P_GetMinecartSector(x, y, z, &nz));
10897 		if (lline != -1)
10898 		{
10899 			fixed_t nx, ny;
10900 			angle_t nang, dummy, angdiff;
10901 			mobj_t *mark;
10902 			mobj_t *snax = P_GetAxis(sides[lines[lline].sidenum[0]].textureoffset >> FRACBITS);
10903 			if (!snax)
10904 				return NULL;
10905 			P_GetAxisPosition(x, y, snax, &nx, &ny, &nang, &dummy);
10906 			angdiff = ((nang - targetangle) + ANG10/2) & ~ANGLE_180;
10907 			//Axes must be directly parallel or antiparallel, give or take 5 degrees.
10908 			if (angdiff < ANG10)
10909 			{
10910 				mark = P_SpawnMobj(nx, ny, nz, (mobjtype_t)mobj->info->raisestate);
10911 				return mark;
10912 			}
10913 		}
10914 	}
10915 	return NULL;
10916 }
10917 
P_ParabolicMove(mobj_t * mo,fixed_t x,fixed_t y,fixed_t z,fixed_t g,fixed_t speed)10918 static void P_ParabolicMove(mobj_t *mo, fixed_t x, fixed_t y, fixed_t z, fixed_t g, fixed_t speed)
10919 {
10920 	fixed_t dx = x - mo->x;
10921 	fixed_t dy = y - mo->y;
10922 	fixed_t dz = z - mo->z;
10923 	fixed_t dh = P_AproxDistance(dx, dy);
10924 	fixed_t c = FixedDiv(dx, dh);
10925 	fixed_t s = FixedDiv(dy, dh);
10926 	fixed_t fixConst = FixedDiv(speed, g);
10927 
10928 	mo->momx = FixedMul(c, speed);
10929 	mo->momy = FixedMul(s, speed);
10930 	mo->momz = FixedDiv(dh, 2*fixConst) + FixedDiv(dz, FixedDiv(dh, fixConst/2));
10931 }
10932 
P_MinecartThink(player_t * player)10933 static void P_MinecartThink(player_t *player)
10934 {
10935 	mobj_t *minecart = player->mo->tracer;
10936 	angle_t fa;
10937 
10938 	if (!minecart || P_MobjWasRemoved(minecart) || !minecart->health)
10939 	{
10940 		// Minecart died on you, so kill yourself.
10941 		P_KillMobj(player->mo, NULL, NULL, 0);
10942 		return;
10943 	}
10944 
10945 	//Limit player's angle to a cone.
10946 #define MINECARTCONEMAX FixedAngle(20*FRACUNIT)
10947 	{
10948 		angle_t angdiff = player->mo->angle - minecart->angle;
10949 		if (angdiff < ANGLE_180 && angdiff > MINECARTCONEMAX)
10950 			player->mo->angle = minecart->angle + MINECARTCONEMAX;
10951 		else if (angdiff > ANGLE_180 && angdiff < InvAngle(MINECARTCONEMAX))
10952 			player->mo->angle = minecart->angle - MINECARTCONEMAX;
10953 
10954 		if (angdiff + minecart->angle != player->mo->angle && (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG))
10955 		{
10956 			angdiff = P_GetLocalAngle(player) - minecart->angle;
10957 			if (angdiff < ANGLE_180 && angdiff > MINECARTCONEMAX)
10958 				P_SetLocalAngle(player, minecart->angle + MINECARTCONEMAX);
10959 			else if (angdiff > ANGLE_180 && angdiff < InvAngle(MINECARTCONEMAX))
10960 				P_SetLocalAngle(player, minecart->angle - MINECARTCONEMAX);
10961 
10962 
10963 		}
10964 	}
10965 
10966 	// Player holding jump?
10967 	if (!(player->cmd.buttons & BT_JUMP))
10968 		player->pflags &= ~PF_JUMPDOWN;
10969 
10970 	// Handle segments.
10971 	P_HandleMinecartSegments(minecart);
10972 
10973 	// Force 0 friction.
10974 	minecart->friction = FRACUNIT;
10975 
10976 	fa = (minecart->angle >> ANGLETOFINESHIFT) & FINEMASK;
10977 	if (!P_TryMove(minecart, minecart->x + FINECOSINE(fa), minecart->y + FINESINE(fa), true))
10978 	{
10979 		P_KillMobj(minecart, NULL, NULL, 0);
10980 		return;
10981 	}
10982 
10983 	if (P_IsObjectOnGround(minecart))
10984 	{
10985 		sector_t *sec;
10986 		INT32 lnum = -1;
10987 		fixed_t dummy;
10988 
10989 		// Just hit floor.
10990 		if (minecart->eflags & MFE_JUSTHITFLOOR)
10991 		{
10992 			S_StopSound(minecart);
10993 			S_StartSound(minecart, sfx_s3k96);
10994 		}
10995 
10996 		sec = P_GetMinecartSector(minecart->x, minecart->y, minecart->z, &dummy);
10997 
10998 		if (sec)
10999 			lnum = P_GetMinecartSpecialLine(sec);
11000 
11001 		// Update axis if the cart is standing on a rail.
11002 		if (sec && lnum != -1)
11003 		{
11004 			mobj_t *axis = P_GetAxis(sides[lines[lnum].sidenum[0]].textureoffset >> FRACBITS);
11005 			fixed_t newx, newy;
11006 			angle_t targetangle, grind;
11007 			angle_t prevangle, angdiff;
11008 			mobj_t *detleft = NULL;
11009 			mobj_t *detright = NULL;
11010 			mobj_t *sidelock = NULL;
11011 			boolean jumped = false;
11012 			fixed_t currentSpeed;
11013 
11014 			if (!axis)
11015 			{
11016 				P_KillMobj(minecart, NULL, NULL, 0);
11017 				return;
11018 			}
11019 
11020 			minecart->movefactor = 0;
11021 			P_ResetScore(player);
11022 			// Handle angle and position
11023 			P_GetAxisPosition(minecart->x, minecart->y, axis, &newx, &newy, &targetangle, &grind);
11024 			if (axis->type != MT_AXISTRANSFERLINE)
11025 				P_SpawnSparks(minecart, grind);
11026 			P_TryMove(minecart, newx, newy, true);
11027 
11028 			// Set angle based on target
11029 			prevangle = minecart->angle;
11030 			angdiff = targetangle - minecart->angle;
11031 			if (angdiff < ANGLE_90 + ANG2 || angdiff > ANGLE_270 - ANG2)
11032 				minecart->angle = targetangle;
11033 			else
11034 				minecart->angle = targetangle + ANGLE_180;
11035 			angdiff = (minecart->angle - prevangle);
11036 			if (angdiff && (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG))  // maintain relative angle on turns
11037 			{
11038 				player->mo->angle += angdiff;
11039 				P_SetPlayerAngle(player, (angle_t)(player->angleturn << 16) + angdiff);
11040 			}
11041 
11042 			// Sideways detection
11043 			if (minecart->flags2 & MF2_AMBUSH)
11044 			{
11045 				angle_t fa2 = (minecart->angle >> ANGLETOFINESHIFT) & FINEMASK;
11046 				fixed_t c = FINECOSINE(fa2);
11047 				fixed_t s = FINESINE(fa2);
11048 
11049 				detleft = P_LookForRails(minecart, c, s, targetangle, -s, c);
11050 				detright = P_LookForRails(minecart, c, s, targetangle, s, -c);
11051 			}
11052 
11053 			// How fast are we going?
11054 			currentSpeed = FixedHypot(minecart->momx, minecart->momy);
11055 			angdiff = R_PointToAngle2(0, 0, minecart->momx, minecart->momy) - minecart->angle;
11056 			if (angdiff > ANGLE_90 && angdiff < ANGLE_270)
11057 				currentSpeed *= -1;
11058 
11059 			// Player-specific behavior.
11060 			if (detleft && player->cmd.sidemove < 0)
11061 				sidelock = detleft;
11062 			else if (detright && player->cmd.sidemove > 0)
11063 				sidelock = detright;
11064 
11065 			//if (player->cmd.buttons & BT_SPIN && currentSpeed > 4*FRACUNIT)
11066 			//	currentSpeed -= FRACUNIT/8;
11067 
11068 			// Jumping
11069 			if (sidelock || ((player->cmd.buttons & BT_JUMP) && !(player->pflags & PF_JUMPDOWN)))
11070 			{
11071 				player->pflags |= PF_JUMPDOWN;
11072 
11073 				if (minecart->eflags & MFE_ONGROUND)
11074 					minecart->eflags &= ~MFE_ONGROUND;
11075 				minecart->z += P_MobjFlip(minecart);
11076 				if (sidelock)
11077 					P_ParabolicMove(minecart, sidelock->x, sidelock->y, sidelock->z, gravity, max(currentSpeed, 10 * FRACUNIT));
11078 				else
11079 					minecart->momz = 10 * FRACUNIT;
11080 
11081 				S_StartSound(minecart, sfx_s3k51);
11082 				jumped = true;
11083 			}
11084 
11085 			if (!jumped)
11086 			{
11087 				// Natural acceleration and boosters
11088 				if (currentSpeed < minecart->info->speed)
11089 					currentSpeed += FRACUNIT/4;
11090 
11091 				if (minecart->standingslope)
11092 				{
11093 					fixed_t fa2 = (minecart->angle >> ANGLETOFINESHIFT) & FINEMASK;
11094 					fixed_t front = P_GetSlopeZAt(minecart->standingslope, minecart->x, minecart->y);
11095 					fixed_t back = P_GetSlopeZAt(minecart->standingslope, minecart->x - FINECOSINE(fa2), minecart->y - FINESINE(fa2));
11096 
11097 					if (abs(front - back) < 3*FRACUNIT)
11098 						currentSpeed += (back - front)/3;
11099 				}
11100 
11101 				// Go forward at our current speed
11102 				P_InstaThrust(minecart, minecart->angle, currentSpeed);
11103 
11104 				// On-track ka-klong sound FX.
11105 				minecart->movecount += abs(currentSpeed);
11106 				if (minecart->movecount > 128*FRACUNIT)
11107 				{
11108 					minecart->movecount %= 128*FRACUNIT;
11109 					S_StartSound(minecart, minecart->info->activesound);
11110 				}
11111 			}
11112 		}
11113 		else
11114 		{
11115 			minecart->movefactor++;
11116 			if ((P_IsObjectOnGround(minecart) && minecart->movefactor >= 5) // off rail
11117 			|| (abs(minecart->momx) < minecart->scale/2 && abs(minecart->momy) < minecart->scale/2)) // hit a wall
11118 			{
11119 				P_KillMobj(minecart, NULL, NULL, 0);
11120 				return;
11121 			}
11122 		}
11123 	}
11124 
11125 	if (player->mo->state-states != S_PLAY_STND)
11126 	{
11127 		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
11128 		player->mo->tics = -1;
11129 	}
11130 
11131 	// Move player to minecart.
11132 	P_TeleportMove(player->mo, minecart->x - minecart->momx, minecart->y - minecart->momy, minecart->z + max(minecart->momz, 0) + 8*FRACUNIT);
11133 	if (player->powers[pw_carry] != CR_MINECART)
11134 		return;
11135 	player->mo->momx = player->mo->momy = player->mo->momz = 0;
11136 	P_TryMove(player->mo, player->mo->x + minecart->momx, player->mo->y + minecart->momy, true);
11137 
11138 	if (player->powers[pw_flashing] == flashingtics)
11139 		player->powers[pw_flashing]--;
11140 }
11141 
11142 // Handle Tails' fluff
P_DoTailsOverlay(player_t * player,mobj_t * tails)11143 static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
11144 {
11145 	// init...
11146 	boolean smilesonground = P_IsObjectOnGround(player->mo);
11147 	angle_t horizangle = player->drawangle;
11148 	fixed_t zoffs = 0;
11149 	fixed_t backwards = -1*FRACUNIT;
11150 	boolean doswim = (player->panim == PA_ABILITY && (player->mo->eflags & MFE_UNDERWATER));
11151 	boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || doswim);
11152 	angle_t rollangle;
11153 	boolean panimchange;
11154 	INT32 ticnum = 0;
11155 	statenum_t chosenstate;
11156 
11157 	if (!tails->skin)
11158 	{
11159 		tails->skin = player->mo->skin;
11160 		P_SetMobjState(tails, S_TAILSOVERLAY_STAND);
11161 		tails->movecount = -1;
11162 	}
11163 
11164 	panimchange = (tails->movecount != (INT32)player->panim);
11165 
11166 	// initial position...
11167 	if (doroll)
11168 	{
11169 		fixed_t testval, zdist;
11170 		if (player->speed < FRACUNIT)
11171 			testval = FRACUNIT;
11172 		else
11173 		{
11174 			testval = (FixedMul(player->speed, FINECOSINE((horizangle - R_PointToAngle2(0, 0, player->rmomx, player->rmomy)) >> ANGLETOFINESHIFT)));
11175 			if (testval < FRACUNIT)
11176 				testval = FRACUNIT;
11177 		}
11178 
11179 		if (doswim)
11180 			zdist = player->mo->momz<<1;
11181 		else if (smilesonground && !player->mo->reactiontime)
11182 			zdist = (player->mo->z - tails->threshold);
11183 		else
11184 			zdist = player->mo->momz;
11185 
11186 		rollangle = R_PointToAngle2(0, 0, testval, -P_MobjFlip(player->mo)*zdist);
11187 
11188 		if (!doswim)
11189 		{
11190 			zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT);
11191 			backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT);
11192 		}
11193 	}
11194 	else if (player->panim == PA_RUN)
11195 		backwards = -5*FRACUNIT;
11196 	else if (player->panim == PA_SPRING || player->panim == PA_JUMP)
11197 	{
11198 		zoffs += 4*FRACUNIT;
11199 		backwards /= 2;
11200 	}
11201 	else if (player->panim == PA_PAIN)
11202 		backwards /= 16;
11203 	else if (player->mo->state-states == S_PLAY_GASP)
11204 	{
11205 		backwards /= 16;
11206 		zoffs += 12*FRACUNIT;
11207 	}
11208 	else if (player->mo->state-states == S_PLAY_EDGE)
11209 	{
11210 		backwards /= 16;
11211 		zoffs = 3*FRACUNIT;
11212 	}
11213 	else if (player->panim == PA_ABILITY2)
11214 	{
11215 		zoffs = -7*FRACUNIT;
11216 		backwards = -9*FRACUNIT;
11217 	}
11218 	else if (player->panim == PA_ABILITY)
11219 		backwards = -5*FRACUNIT;
11220 
11221 	// sprite...
11222 	if (doroll)
11223 	{
11224 		statenum_t add = ((rollangle > ANGLE_180) ? 2 : 0);
11225 		if (add)
11226 			rollangle = InvAngle(rollangle);
11227 		rollangle += ANG15; // modify the thresholds to be nice clean numbers
11228 		if (rollangle > ANG60)
11229 			chosenstate = S_TAILSOVERLAY_PLUS60DEGREES + add;
11230 		else if (rollangle > ANG30)
11231 			chosenstate = S_TAILSOVERLAY_PLUS30DEGREES + add;
11232 		else
11233 			chosenstate = S_TAILSOVERLAY_0DEGREES;
11234 	}
11235 	else if (player->panim == PA_SPRING || player->panim == PA_JUMP)
11236 		chosenstate = S_TAILSOVERLAY_MINUS60DEGREES;
11237 	else if (player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
11238 		chosenstate = S_TAILSOVERLAY_PLUS60DEGREES;
11239 	else if (player->panim == PA_PAIN)
11240 		chosenstate = S_TAILSOVERLAY_PAIN;
11241 	else if (player->mo->state-states == S_PLAY_GASP)
11242 		chosenstate = S_TAILSOVERLAY_GASP;
11243 	else if (player->mo->state-states == S_PLAY_EDGE)
11244 		chosenstate = S_TAILSOVERLAY_EDGE;
11245 	else if (player->panim == PA_DASH)
11246 		chosenstate = S_TAILSOVERLAY_DASH;
11247 	else if (player->panim == PA_RUN)
11248 		chosenstate = S_TAILSOVERLAY_RUN;
11249 	else if (player->panim == PA_WALK)
11250 	{
11251 		if (!smilesonground || player->mo->state-states == S_PLAY_SKID)
11252 			chosenstate = S_TAILSOVERLAY_PLUS30DEGREES;
11253 		else if (player->speed >= FixedMul(player->runspeed/2, player->mo->scale))
11254 			chosenstate = S_TAILSOVERLAY_0DEGREES;
11255 		else
11256 			chosenstate = S_TAILSOVERLAY_MINUS30DEGREES;
11257 	}
11258 	else if (player->mo->sprite2 == SPR2_FLY)
11259 		chosenstate = S_TAILSOVERLAY_FLY;
11260 	else if (player->mo->sprite2 == SPR2_SWIM)
11261 		chosenstate = S_TAILSOVERLAY_FLY;
11262 	else if (player->mo->sprite2 == SPR2_TIRE)
11263 		chosenstate = S_TAILSOVERLAY_TIRE;
11264 	else if (player->panim == PA_ABILITY2)
11265 		chosenstate = S_TAILSOVERLAY_PLUS30DEGREES;
11266 	else if (player->panim == PA_IDLE)
11267 		chosenstate = S_TAILSOVERLAY_STAND;
11268 	else
11269 		chosenstate = S_INVISIBLE;
11270 
11271 	// state...
11272 	if (panimchange)
11273 	{
11274 		tails->sprite2 = -1;
11275 		P_SetMobjState(tails, chosenstate);
11276 	}
11277 	else
11278 	{
11279 		if (tails->state != states+chosenstate)
11280 		{
11281 			if (states[chosenstate].sprite == SPR_PLAY)
11282 				tails->sprite2 = P_GetSkinSprite2(((skin_t *)tails->skin), (states[chosenstate].frame & FF_FRAMEMASK), player);
11283 			P_SetMobjState(tails, chosenstate);
11284 		}
11285 	}
11286 
11287 #if 0
11288 	if (player->fly1 != 0 && player->powers[pw_tailsfly] != 0 && !smilesonground)
11289 		P_SetMobjState(tails, chosenstate);
11290 #endif
11291 
11292 	// animation...
11293 	if (player->panim == PA_SPRING || player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
11294 	{
11295 		if (FixedDiv(abs(player->mo->momz), player->mo->scale) < 20<<FRACBITS)
11296 			ticnum = 2;
11297 		else
11298 			ticnum = 1;
11299 	}
11300 	else if (player->panim == PA_PAIN)
11301 		ticnum = 2;
11302 	else if (player->mo->state-states == S_PLAY_GASP)
11303 		tails->tics = -1;
11304 	else if (player->mo->sprite2 == SPR2_TIRE)
11305 		ticnum = (doswim ? 2 : 4);
11306 	else if (player->panim != PA_IDLE)
11307 		ticnum = player->mo->tics;
11308 
11309 	if (ticnum && tails->tics > ticnum)
11310 		tails->tics = ticnum;
11311 
11312 	// final handling...
11313 	tails->color = player->mo->color;
11314 	tails->threshold = player->mo->z;
11315 	tails->movecount = player->panim;
11316 	tails->angle = horizangle;
11317 	P_SetScale(tails, player->mo->scale);
11318 	tails->destscale = player->mo->destscale;
11319 	tails->radius = player->mo->radius;
11320 	tails->height = player->mo->height;
11321 	zoffs = FixedMul(zoffs, tails->scale);
11322 
11323 	if (player->mo->eflags & MFE_VERTICALFLIP)
11324 	{
11325 		tails->eflags |= MFE_VERTICALFLIP;
11326 		tails->flags2 |= MF2_OBJECTFLIP;
11327 		zoffs = player->mo->height - tails->height - zoffs;
11328 	}
11329 	else
11330 	{
11331 		tails->eflags &= ~MFE_VERTICALFLIP;
11332 		tails->flags2 &= ~MF2_OBJECTFLIP;
11333 	}
11334 
11335 	P_UnsetThingPosition(tails);
11336 	tails->x = player->mo->x + P_ReturnThrustX(tails, tails->angle, FixedMul(backwards, tails->scale));
11337 	tails->y = player->mo->y + P_ReturnThrustY(tails, tails->angle, FixedMul(backwards, tails->scale));
11338 	tails->z = player->mo->z + zoffs;
11339 	P_SetThingPosition(tails);
11340 }
11341 
11342 // Metal Sonic's jet fume
P_DoMetalJetFume(player_t * player,mobj_t * fume)11343 static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
11344 {
11345 	static const UINT8 FUME_SKINCOLORS[] =
11346 	{
11347 		SKINCOLOR_ICY,
11348 		SKINCOLOR_SKY,
11349 		SKINCOLOR_CYAN,
11350 		SKINCOLOR_WAVE,
11351 		SKINCOLOR_TEAL,
11352 		SKINCOLOR_AQUA,
11353 		SKINCOLOR_SEAFOAM,
11354 		SKINCOLOR_MINT,
11355 		SKINCOLOR_PERIDOT,
11356 		SKINCOLOR_LIME,
11357 		SKINCOLOR_YELLOW,
11358 		SKINCOLOR_SANDY,
11359 		SKINCOLOR_GOLD,
11360 		SKINCOLOR_APRICOT,
11361 		SKINCOLOR_SUNSET
11362 	};
11363 	mobj_t *mo = player->mo;
11364 	angle_t angle = player->drawangle;
11365 	fixed_t dist;
11366 	panim_t panim = player->panim;
11367 	tic_t dashmode = min(player->dashmode, DASHMODE_MAX);
11368 	boolean underwater = mo->eflags & MFE_UNDERWATER;
11369 	statenum_t stat = fume->state-states;
11370 
11371 	if (panim != PA_WALK && panim != PA_RUN && panim != PA_DASH) // turn invisible when not in a coherent movement state
11372 	{
11373 		if (stat != fume->info->spawnstate)
11374 			P_SetMobjState(fume, fume->info->spawnstate);
11375 		return;
11376 	}
11377 
11378 	if (underwater) // No fume underwater; spawn bubbles instead!
11379 	{
11380 		fume->movedir	+= FixedAngle(FixedDiv(2 * player->speed, 3 * mo->scale));
11381 		fume->movefactor += player->speed;
11382 
11383 		if (fume->movefactor > FixedDiv(2 * player->normalspeed, 3 * mo->scale))
11384 		{
11385 			INT16 i;
11386 			fixed_t radiusV = 4*FRACUNIT;
11387 			fixed_t radiusX = P_ReturnThrustX(mo, angle, -mo->radius >> (panim == PA_WALK ? 1 : 0));
11388 			fixed_t radiusY = P_ReturnThrustY(mo, angle, -mo->radius >> (panim == PA_WALK ? 1 : 0));
11389 			fixed_t factorX = P_ReturnThrustX(mo, angle + ANGLE_90, mo->scale);
11390 			fixed_t factorY = P_ReturnThrustY(mo, angle + ANGLE_90, mo->scale);
11391 			fixed_t offsetH, offsetV, x, y, z;
11392 
11393 			for (i = -1; i < 2; i += 2)
11394 			{
11395 				offsetH = i*P_ReturnThrustX(fume, fume->movedir, radiusV);
11396 				offsetV = i*P_ReturnThrustY(fume, fume->movedir, radiusV);
11397 				x = mo->x + radiusX + FixedMul(offsetH, factorX);
11398 				y = mo->y + radiusY + FixedMul(offsetH, factorY);
11399 				z = mo->z + (mo->height >> 1) + offsetV;
11400 				P_SpawnMobj(x, y, z, MT_SMALLBUBBLE)->scale = mo->scale >> 1;
11401 			}
11402 
11403 			fume->movefactor = 0;
11404 		}
11405 
11406 		if (panim == PA_WALK)
11407 		{
11408 			if (stat != fume->info->spawnstate)
11409 			{
11410 				fume->threshold = 0;
11411 				P_SetMobjState(fume, fume->info->spawnstate);
11412 			}
11413 			return;
11414 		}
11415 	}
11416 
11417 	if (stat == fume->info->spawnstate) // If currently inivisble, activate!
11418 	{
11419 		P_SetMobjState(fume, (stat = fume->info->seestate));
11420 		P_SetScale(fume, mo->scale);
11421 	}
11422 
11423 	if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->seestate) // If in dashmode, grow really big and flash
11424 	{
11425 		fume->destscale = mo->scale;
11426 		fume->flags2 ^= MF2_DONTDRAW;
11427 		fume->flags2 |= mo->flags2 & MF2_DONTDRAW;
11428 	}
11429 	else // Otherwise, pick a size and color depending on speed and proximity to dashmode
11430 	{
11431 		if (dashmode == DASHMODE_THRESHOLD && dashmode > (tic_t)fume->movecount) // If just about to enter dashmode, play the startup animation again
11432 		{
11433 			P_SetMobjState(fume, (stat = fume->info->seestate));
11434 			P_SetScale(fume, mo->scale << 1);
11435 		}
11436 		fume->flags2 = (fume->flags2 & ~MF2_DONTDRAW) | (mo->flags2 & MF2_DONTDRAW);
11437 		fume->destscale = (mo->scale + FixedDiv(player->speed, player->normalspeed)) / (underwater ? 6 : 3);
11438 		fume->color = FUME_SKINCOLORS[(dashmode * sizeof(FUME_SKINCOLORS)) / (DASHMODE_MAX + 1)];
11439 
11440 		if (underwater)
11441 		{
11442 			fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10);
11443 			fume->threshold = 1;
11444 		}
11445 		else if (fume->threshold)
11446 		{
11447 			fume->frame = (fume->frame & FF_FRAMEMASK) | fume->state->frame;
11448 			fume->threshold = 0;
11449 		}
11450 	}
11451 
11452 	fume->movecount = dashmode; // keeps track of previous dashmode value so we know whether Metal is entering or leaving it
11453 	fume->eflags = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
11454 	fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); // Make sure to flip in reverse gravity!
11455 
11456 	// Finally, set its position
11457 	dist = -mo->radius - FixedMul(fume->info->radius, fume->destscale - mo->scale/3);
11458 
11459 	P_UnsetThingPosition(fume);
11460 	fume->x = mo->x + P_ReturnThrustX(fume, angle, dist);
11461 	fume->y = mo->y + P_ReturnThrustY(fume, angle, dist);
11462 	fume->z = mo->z + ((mo->height - fume->height) >> 1);
11463 	P_SetThingPosition(fume);
11464 
11465 	// If dashmode is high enough, spawn a trail
11466 	if (player->normalspeed >= skins[player->skin].normalspeed*2)
11467 		P_SpawnGhostMobj(fume);
11468 }
11469 
11470 //
11471 // P_PlayerThink
11472 //
11473 
P_PlayerThink(player_t * player)11474 void P_PlayerThink(player_t *player)
11475 {
11476 	ticcmd_t *cmd;
11477 	const size_t playeri = (size_t)(player - players);
11478 
11479 #ifdef PARANOIA
11480 	if (!player->mo)
11481 		I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
11482 #endif
11483 
11484 	// todo: Figure out what is actually causing these problems in the first place...
11485 	if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD!
11486 	{
11487 		CONS_Debug(DBG_GAMELOGIC, "P_PlayerThink: Player %s in PST_LIVE with 0 health. (\"Zombie bug\")\n", sizeu1(playeri));
11488 		player->playerstate = PST_DEAD;
11489 	}
11490 
11491 	if (player->bot)
11492 	{
11493 		if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD)
11494 		{
11495 			if (B_CheckRespawn(player))
11496 				player->playerstate = PST_REBORN;
11497 		}
11498 		if (player->playerstate == PST_REBORN)
11499 		{
11500 			LUAh_PlayerThink(player);
11501 			return;
11502 		}
11503 	}
11504 
11505 	if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5)))
11506 	{
11507 		seenplayer = NULL;
11508 
11509 		if (cv_seenames.value && cv_allowseenames.value &&
11510 			!(G_TagGametype() && (player->pflags & PF_TAGIT)))
11511 		{
11512 			mobj_t *mo = P_SpawnNameFinder(player->mo, MT_NAMECHECK);
11513 
11514 			if (mo)
11515 			{
11516 				short int i;
11517 				mo->flags |= MF_NOCLIPHEIGHT;
11518 				for (i = 0; i < 32; i++)
11519 				{
11520 					// Debug drawing
11521 //					if (i&1)
11522 //						P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
11523 					if (P_RailThinker(mo))
11524 						break; // mobj was removed (missile hit a wall) or couldn't move
11525 				}
11526 			}
11527 		}
11528 	}
11529 
11530 	if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
11531 	{
11532 		P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
11533 		player->awayviewtics = 0; // reset to zero
11534 	}
11535 
11536 	if (player->flashcount)
11537 		player->flashcount--;
11538 
11539 	if (player->awayviewtics && player->awayviewtics != -1)
11540 		player->awayviewtics--;
11541 
11542 	/// \note do this in the cheat code
11543 	if (player->pflags & PF_NOCLIP)
11544 		player->mo->flags |= MF_NOCLIP;
11545 	else
11546 		player->mo->flags &= ~MF_NOCLIP;
11547 
11548 	cmd = &player->cmd;
11549 
11550 	// Add some extra randomization.
11551 	if (cmd->forwardmove)
11552 		P_RandomFixed();
11553 
11554 #ifdef PARANOIA
11555 	if (player->playerstate == PST_REBORN)
11556 		I_Error("player %s is in PST_REBORN\n", sizeu1(playeri));
11557 #endif
11558 
11559 	if (gametyperules & GTR_RACE)
11560 	{
11561 		INT32 i;
11562 
11563 		// Check if all the players in the race have finished. If so, end the level.
11564 		for (i = 0; i < MAXPLAYERS; i++)
11565 		{
11566 			if (playeringame[i])
11567 			{
11568 				if (!players[i].exiting && players[i].lives > 0)
11569 					break;
11570 			}
11571 		}
11572 
11573 		if (i == MAXPLAYERS && player->exiting == 3*TICRATE) // finished
11574 			player->exiting = (14*TICRATE)/5 + 1;
11575 
11576 		// If 11 seconds are left on the timer,
11577 		// begin the drown music for countdown!
11578 		if (countdown == 11*TICRATE - 1 && P_IsLocalPlayer(player))
11579 			P_PlayJingle(player, JT_DROWN);
11580 
11581 		// If you've hit the countdown and you haven't made
11582 		//  it to the exit, you're a goner!
11583 		else if (countdown == 1 && !player->exiting && player->lives > 0)
11584 		{
11585 			if (netgame && player->mo->health > 0)
11586 				CONS_Printf(M_GetText("%s ran out of time.\n"), player_names[player-players]);
11587 
11588 			player->pflags |= PF_GAMETYPEOVER;
11589 
11590 			if (player->powers[pw_carry] == CR_NIGHTSMODE)
11591 			{
11592 				P_DeNightserizePlayer(player);
11593 				S_StartScreamSound(player->mo, sfx_s3k66);
11594 			}
11595 
11596 			player->lives = 2; // Don't start the game over music!
11597 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
11598 			player->lives = 0;
11599 
11600 			if (player->playerstate == PST_DEAD)
11601 			{
11602 				LUAh_PlayerThink(player);
11603 				return;
11604 			}
11605 		}
11606 	}
11607 
11608 	// If it is set, start subtracting
11609 	// Don't allow it to go back to 0
11610 	if (player->exiting > 1 && player->exiting < 3*TICRATE)
11611 		player->exiting--;
11612 
11613 	if (player->exiting && countdown2)
11614 		player->exiting = 5;
11615 
11616 	// The following code is disabled for now as this causes the game to freeze sometimes
11617 	// Monster Iestyn -- 16/08/19
11618 #if 0
11619 	// Same check as below, just at 1 second before
11620 	// so we can fade music
11621 	if (!exitfadestarted &&
11622 		player->exiting > 0 && player->exiting <= 1*TICRATE &&
11623 		(!multiplayer || G_CoopGametype() ? !mapheaderinfo[gamemap-1]->musinterfadeout : true) &&
11624 			// don't fade if we're fading during intermission. follows Y_StartIntermission intertype = int_coop
11625 		((gametyperules & GTR_RACE) ? countdown2 == 0 : true) && // don't fade on timeout
11626 		player->lives > 0 && // don't fade on game over (competition)
11627 		P_IsLocalPlayer(player))
11628 	{
11629 		if (cv_playersforexit.value)
11630 		{
11631 			INT32 i;
11632 
11633 			for (i = 0; i < MAXPLAYERS; i++)
11634 			{
11635 				if (!playeringame[i] || players[i].spectator || players[i].bot)
11636 					continue;
11637 				if (players[i].lives <= 0)
11638 					continue;
11639 
11640 				if (!players[i].exiting || players[i].exiting > 1*TICRATE)
11641 					break;
11642 			}
11643 
11644 			if (i == MAXPLAYERS)
11645 			{
11646 				exitfadestarted = true;
11647 				S_FadeOutStopMusic(1*MUSICRATE);
11648 			}
11649 		}
11650 		else
11651 		{
11652 			exitfadestarted = true;
11653 			S_FadeOutStopMusic(1*MUSICRATE);
11654 		}
11655 	}
11656 #endif
11657 
11658 	if (player->exiting == 2 || countdown2 == 2)
11659 	{
11660 		UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
11661 		if (numneeded) // Count to be sure everyone's exited
11662 		{
11663 			INT32 i, total = 0, exiting = 0;
11664 
11665 			for (i = 0; i < MAXPLAYERS; i++)
11666 			{
11667 				if (!playeringame[i] || players[i].spectator || players[i].bot)
11668 					continue;
11669 				if (players[i].quittime > 30 * TICRATE)
11670 					continue;
11671 				if (players[i].lives <= 0)
11672 					continue;
11673 
11674 				total++;
11675 				if (players[i].exiting && players[i].exiting < 4)
11676 					exiting++;
11677 			}
11678 
11679 			if (!total || ((4*exiting)/total) >= numneeded)
11680 			{
11681 				if (server)
11682 					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
11683 			}
11684 			else
11685 				player->exiting = 3;
11686 		}
11687 		else
11688 		{
11689 			if (server)
11690 				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
11691 		}
11692 	}
11693 
11694 	if (player->pflags & PF_FINISHED)
11695 	{
11696 		if (((gametyperules & GTR_FRIENDLY) && cv_exitmove.value) && !G_EnoughPlayersFinished())
11697 			player->exiting = 0;
11698 		else
11699 			P_DoPlayerExit(player);
11700 	}
11701 
11702 	// check water content, set stuff in mobj
11703 	P_MobjCheckWater(player->mo);
11704 
11705 #ifndef SECTORSPECIALSAFTERTHINK
11706 	if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
11707 		player->onconveyor = 0;
11708 	// check special sectors : damage & secrets
11709 
11710 	if (!player->spectator)
11711 		P_PlayerInSpecialSector(player);
11712 	else if (
11713 #else
11714 	if (player->spectator &&
11715 #endif
11716 	G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
11717 		P_ConsiderAllGone();
11718 
11719 	if (player->playerstate == PST_DEAD)
11720 	{
11721 		player->mo->flags2 &= ~MF2_SHADOW;
11722 		P_DeathThink(player);
11723 		LUAh_PlayerThink(player);
11724 		return;
11725 	}
11726 
11727 	// Make sure spectators always have a score and ring count of 0.
11728 	if (player->spectator)
11729 	{
11730 		if (!(gametyperules & GTR_CAMPAIGN))
11731 			player->score = 0;
11732 	}
11733 	else if ((netgame || multiplayer) && player->lives <= 0 && !G_CoopGametype())
11734 	{
11735 		// Outside of Co-Op, replenish a user's lives if they are depleted.
11736 		// of course, this is just a cheap hack, meh...
11737 		player->lives = cv_startinglives.value;
11738 	}
11739 
11740 	if ((gametyperules & GTR_RACE) && leveltime < 4*TICRATE)
11741 	{
11742 		cmd->buttons &= BT_SPIN; // Remove all buttons except BT_SPIN
11743 		cmd->forwardmove = 0;
11744 		cmd->sidemove = 0;
11745 	}
11746 
11747 	// Synchronizes the "real" amount of time spent in the level.
11748 	if (!player->exiting && !stoppedclock)
11749 	{
11750 		if (gametyperules & GTR_RACE)
11751 		{
11752 			if (leveltime >= 4*TICRATE)
11753 				player->realtime = leveltime - 4*TICRATE;
11754 			else
11755 				player->realtime = 0;
11756 		}
11757 		else
11758 			player->realtime = leveltime;
11759 	}
11760 
11761 	if (player->spectator && cmd->buttons & BT_ATTACK && !player->powers[pw_flashing] && G_GametypeHasSpectators())
11762 	{
11763 		if (P_SpectatorJoinGame(player))
11764 		{
11765 			LUAh_PlayerThink(player);
11766 			return; // player->mo was removed.
11767 		}
11768 	}
11769 
11770 	// Even if not NiGHTS, pull in nearby objects when walking around as John Q. Elliot.
11771 	if (!objectplacing && !((netgame || multiplayer) && player->spectator)
11772 	&& maptol & TOL_NIGHTS && (player->powers[pw_carry] != CR_NIGHTSMODE || player->powers[pw_nights_helper]))
11773 	{
11774 		thinker_t *th;
11775 		mobj_t *mo2;
11776 		fixed_t x = player->mo->x;
11777 		fixed_t y = player->mo->y;
11778 		fixed_t z = player->mo->z;
11779 
11780 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
11781 		{
11782 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
11783 				continue;
11784 
11785 			mo2 = (mobj_t *)th;
11786 
11787 			if (!(mo2->type == MT_RING || mo2->type == MT_COIN
11788 				|| mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE
11789 				|| mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR))
11790 				continue;
11791 
11792 			if (mo2->flags2 & MF2_NIGHTSPULL)
11793 				continue;
11794 
11795 			if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale))
11796 				continue;
11797 
11798 			// Yay! The thing's in reach! Pull it in!
11799 			mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
11800 			mo2->flags2 |= MF2_NIGHTSPULL;
11801 			// New NiGHTS attract speed dummied out because the older behavior
11802 			// is exploited as a mechanic. Uncomment to enable.
11803 			mo2->movefactor = 0; // 40*FRACUNIT; // initialize the NightsItemChase timer
11804 			P_SetTarget(&mo2->tracer, player->mo);
11805 		}
11806 	}
11807 
11808 	if (player->linktimer && !player->powers[pw_nights_linkfreeze])
11809 	{
11810 		if (--player->linktimer <= 0) // Link timer
11811 			player->linkcount = 0;
11812 	}
11813 
11814 	// Move around.
11815 	// Reactiontime is used to prevent movement
11816 	//  for a bit after a teleport.
11817 	if (player->mo->reactiontime)
11818 		player->mo->reactiontime--;
11819 	else if (player->powers[pw_carry] == CR_MINECART)
11820 	{
11821 		if (P_ControlStyle(player) != CS_LMAOGALOG)
11822 			player->mo->angle = (cmd->angleturn << 16 /* not FRACBITS */);
11823 
11824 		ticruned++;
11825 		if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
11826 			ticmiss++;
11827 
11828 		P_MinecartThink(player);
11829 	}
11830 	else if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && (player->powers[pw_carry] == CR_ROPEHANG || player->powers[pw_carry] == CR_ZOOMTUBE))
11831 	{
11832 		if (player->powers[pw_carry] == CR_ROPEHANG)
11833 		{
11834 			if (P_ControlStyle(player) != CS_LMAOGALOG)
11835 				player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
11836 
11837 			ticruned++;
11838 			if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
11839 				ticmiss++;
11840 
11841 			P_DoRopeHang(player);
11842 			P_DoJumpStuff(player, &player->cmd);
11843 		}
11844 		else //if (player->powers[pw_carry] == CR_ZOOMTUBE)
11845 		{
11846 			P_DoZoomTube(player);
11847 			if (!(player->panim == PA_ROLL))
11848 				P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
11849 		}
11850 		player->rmomx = player->rmomy = 0; // no actual momentum from your controls
11851 		P_ResetScore(player);
11852 	}
11853 	else
11854 	{
11855 		if (player->bumpertime == TICRATE/2 && player->mo->hnext)
11856 		{
11857 			// Center player to NiGHTS bumper here because if you try to set player's position in
11858 			// P_TouchSpecialThing case MT_NIGHTSBUMPER, that position is fudged in the time
11859 			// between that routine in the previous tic
11860 			// and reaching here in the current tic
11861 			P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y
11862 				, player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale));
11863 			P_SetTarget(&player->mo->hnext, NULL);
11864 		}
11865 		P_MovePlayer(player);
11866 	}
11867 
11868 	if (!player->mo)
11869 	{
11870 		LUAh_PlayerThink(player);
11871 		return; // P_MovePlayer removed player->mo.
11872 	}
11873 
11874 	// deez New User eXperiences.
11875 	{
11876 		angle_t oldang = player->drawangle, diff = 0;
11877 		UINT8 factor;
11878 		// Directionchar!
11879 		// Camera angle stuff.
11880 		if (player->exiting // no control, no modification
11881 		|| player->powers[pw_carry] == CR_NIGHTSMODE)
11882 			;
11883 		else if (!(player->pflags & PF_DIRECTIONCHAR)
11884 		|| (player->climbing) // stuff where the direction is forced at all times
11885 		|| (twodlevel || player->mo->flags2 & MF2_TWOD) // keep things synchronised up there, since the camera IS seperate from player motion when that happens
11886 		|| G_RingSlingerGametype()) // no firing rings in directions your player isn't aiming
11887 			player->drawangle = player->mo->angle;
11888 		else if (P_PlayerInPain(player))
11889 			;
11890 		else if (player->powers[pw_justsprung]) // restricted, potentially by lua
11891 		{
11892 #ifdef SPRINGSPIN
11893 			if (player->powers[pw_justsprung] & (1<<15))
11894 				player->drawangle += (player->powers[pw_justsprung] & ~(1<<15))*(ANG2+ANG1);
11895 #endif
11896 		}
11897 		else if (player->powers[pw_carry] && player->mo->tracer) // carry
11898 		{
11899 			switch (player->powers[pw_carry])
11900 			{
11901 				case CR_PLAYER:
11902 					if (player->mo->tracer->player)
11903 					{
11904 						player->drawangle = player->mo->tracer->player->drawangle;
11905 						break;
11906 					}
11907 					/* FALLTHRU */
11908 				case CR_MINECART:
11909 				case CR_GENERIC:
11910 				case CR_PTERABYTE:
11911 					player->drawangle = player->mo->tracer->angle;
11912 					break;
11913 				case CR_ROLLOUT:
11914 					if (cmd->forwardmove || cmd->sidemove) // only when you're pressing movement keys
11915 					{ // inverse direction!
11916 						diff = (((player->cmd.angleturn<<16) + R_PointToAngle2(0, 0, -cmd->forwardmove<<FRACBITS, cmd->sidemove<<FRACBITS)) - player->drawangle);
11917 						factor = 4;
11918 					}
11919 					break;
11920 				case CR_DUSTDEVIL:
11921 					player->drawangle += ANG20;
11922 					break;
11923 				/* -- in case we wanted to have the camera freely movable during zoom tubes
11924 				case CR_ZOOMTUBE:*/
11925 				case CR_ROPEHANG:
11926 					if (player->mo->momx || player->mo->momy)
11927 					{
11928 						player->drawangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
11929 						break;
11930 					}
11931 					/* FALLTHRU */
11932 				default:
11933 					player->drawangle = player->mo->angle;
11934 					break;
11935 			}
11936 		}
11937 		else if ((player->skidtime > (TICRATE/2 - 2) || ((player->pflags & (PF_SPINNING|PF_STARTDASH)) == PF_SPINNING)) && (abs(player->rmomx) > 5*player->mo->scale || abs(player->rmomy) > 5*player->mo->scale)) // spin/skid force
11938 			player->drawangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
11939 		else if (((player->charability2 == CA2_GUNSLINGER || player->charability2 == CA2_MELEE) && player->panim == PA_ABILITY2) || player->pflags & PF_STASIS || player->skidtime)
11940 			;
11941 		else
11942 		{
11943 			if (player->pflags & PF_GLIDING)
11944 			{
11945 				if (player->speed < player->mo->scale)
11946 					diff = player->mo->angle - player->drawangle;
11947 				else
11948 					diff = (R_PointToAngle2(0, 0, player->rmomx, player->rmomy) - player->drawangle);
11949 				factor = 4;
11950 			}
11951 			else if (player->pflags & PF_SLIDING)
11952 			{
11953 #if 0 // fun hydrocity style horizontal spin
11954 				if (player->mo->eflags & MFE_TOUCHWATER || player->powers[pw_flashing] > (flashingtics/4)*3)
11955 				{
11956 					diff = (player->mo->angle - player->drawangle);
11957 					factor = 16;
11958 				}
11959 				else
11960 				{
11961 					diff = factor = 0;
11962 					player->drawangle += ANGLE_22h;
11963 				}
11964 #else
11965 				diff = (player->mo->angle - player->drawangle);
11966 				factor = 16;
11967 #endif
11968 			}
11969 			else if (player->pflags & PF_STARTDASH)
11970 			{
11971 				diff = (player->mo->angle - player->drawangle);
11972 				factor = 4;
11973 			}
11974 			else if (cmd->forwardmove || cmd->sidemove) // only when you're pressing movement keys
11975 			{
11976 				diff = ((player->mo->angle + ((player->pflags & PF_ANALOGMODE) ? 0 : R_PointToAngle2(0, 0, cmd->forwardmove<<FRACBITS, -cmd->sidemove<<FRACBITS))) - player->drawangle);
11977 				factor = 4;
11978 			}
11979 			else if (player->rmomx || player->rmomy)
11980 				diff = factor = 0;
11981 			else
11982 			{
11983 				diff = (player->mo->angle - player->drawangle);
11984 				factor = 8;
11985 			}
11986 		}
11987 
11988 		if (diff)
11989 		{
11990 			if (diff > ANGLE_180)
11991 				diff = InvAngle(InvAngle(diff)/factor);
11992 			else
11993 				diff /= factor;
11994 			player->drawangle += diff;
11995 		}
11996 
11997 		// reset from waiting to standing when turning on the spot
11998 		if (player->panim == PA_IDLE)
11999 		{
12000 			diff = player->drawangle - oldang;
12001 			if (diff > ANGLE_180)
12002 				diff = InvAngle(diff);
12003 			if (diff > ANG10/2)
12004 			{
12005 				statenum_t stat = player->mo->state-states;
12006 				if (stat == S_PLAY_WAIT)
12007 					P_SetPlayerMobjState(player->mo, S_PLAY_STND);
12008 				else if (stat == S_PLAY_STND && player->mo->tics != -1)
12009 					player->mo->tics++;
12010 			}
12011 		}
12012 
12013 		// Autobrake! check ST_drawInput if you modify this
12014 		{
12015 			boolean currentlyonground = P_IsObjectOnGround(player->mo);
12016 
12017 			if (player->powers[pw_noautobrake])
12018 				;
12019 			else if (!player->powers[pw_carry] && !player->powers[pw_nocontrol]
12020 			&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE|PF_STASIS)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
12021 			&& !(cmd->forwardmove || cmd->sidemove)
12022 			&& (player->rmomx || player->rmomy)
12023 			&& (!player->capsule || (player->capsule->reactiontime != (player-players)+1)))
12024 			{
12025 				fixed_t acceleration = (player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration) * player->thrustfactor * 20;
12026 				angle_t moveAngle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
12027 
12028 				if (!currentlyonground)
12029 					acceleration /= 2;
12030 				// fake skidding! see P_SkidStuff for reference on conditionals
12031 				else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed, player->mo->scale)) // modified from player->runspeed/2 'cuz the skid was just TOO frequent ngl
12032 				{
12033 					if (player->mo->state-states != S_PLAY_SKID)
12034 						P_SetPlayerMobjState(player->mo, S_PLAY_SKID);
12035 					player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
12036 
12037 					if (P_IsLocalPlayer(player)) // the sound happens way more frequently now, so give co-op players' ears a brake...
12038 						S_StartSound(player->mo, sfx_skid);
12039 				}
12040 
12041 				if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
12042 					acceleration = FixedMul(acceleration<<FRACBITS, player->mo->movefactor)>>FRACBITS;
12043 
12044 				P_Thrust(player->mo, moveAngle, -acceleration);
12045 			}
12046 
12047 			if (!(player->pflags & PF_AUTOBRAKE)
12048 			|| player->powers[pw_carry]
12049 			|| player->panim == PA_SPRING
12050 			|| player->panim == PA_PAIN
12051 			|| !player->mo->health
12052 			|| player->climbing
12053 			|| player->pflags & (PF_SPINNING|PF_SLIDING)
12054 			|| player->bumpertime)
12055 				player->pflags &= ~PF_APPLYAUTOBRAKE;
12056 			else if (currentlyonground || player->powers[pw_tailsfly])
12057 				player->pflags |= PF_APPLYAUTOBRAKE;
12058 		}
12059 	}
12060 
12061 	player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
12062 
12063 	// Unset statis flags after moving.
12064 	// In other words, if you manually set stasis via code,
12065 	// it lasts for one tic.
12066 	player->pflags &= ~PF_FULLSTASIS;
12067 
12068 	if (player->onconveyor == 1)
12069 		player->onconveyor = 3;
12070 	else if (player->onconveyor == 3)
12071 		player->cmomy = player->cmomx = 0;
12072 
12073 	P_DoSuperStuff(player);
12074 	P_CheckSneakerAndLivesTimer(player);
12075 	P_DoBubbleBreath(player); // Spawn Sonic's bubbles
12076 	P_CheckUnderwaterAndSpaceTimer(player); // Display the countdown drown numbers!
12077 	P_CheckInvincibilityTimer(player); // Spawn Invincibility Sparkles
12078 	P_DoPlayerHeadSigns(player); // Spawn Tag/CTF signs over player's head
12079 
12080 #if 1
12081 	// "Blur" a bit when you have speed shoes and are going fast enough
12082 	if ((player->powers[pw_super] || player->powers[pw_sneakers]) && (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale))
12083 	{
12084 		mobj_t *gmobj = P_SpawnGhostMobj(player->mo);
12085 		gmobj->fuse = 2;
12086 		if (gmobj->tracer)
12087 			gmobj->tracer->fuse = 2;
12088 		if (leveltime & 1)
12089 		{
12090 			gmobj->frame &= ~FF_TRANSMASK;
12091 			gmobj->frame |= tr_trans70<<FF_TRANSSHIFT;
12092 			if (gmobj->tracer)
12093 			{
12094 				gmobj->tracer->frame &= ~FF_TRANSMASK;
12095 				gmobj->tracer->frame |= tr_trans70<<FF_TRANSSHIFT;
12096 			}
12097 		}
12098 
12099 		// Hide the mobj from our sights if we're the displayplayer and chasecam is off,
12100 		// or secondarydisplayplayer and chasecam2 is off.
12101 		// Why not just not spawn the mobj?  Well, I'd rather only flirt with
12102 		// consistency so much...
12103 		if ((player == &players[displayplayer] && !camera.chase)
12104 		|| (splitscreen && player == &players[secondarydisplayplayer] && !camera2.chase))
12105 			gmobj->flags2 |= MF2_DONTDRAW;
12106 	}
12107 #endif
12108 
12109 	// check for use
12110 	if (player->powers[pw_carry] != CR_NIGHTSMODE)
12111 	{
12112 		if (cmd->buttons & BT_SPIN)
12113 			player->pflags |= PF_SPINDOWN;
12114 		else
12115 			player->pflags &= ~PF_SPINDOWN;
12116 	}
12117 
12118 	// IF PLAYER NOT HERE THEN FLASH END IF
12119 	if (player->quittime && player->powers[pw_flashing] < flashingtics - 1
12120 	&& !(G_TagGametype() && !(player->pflags & PF_TAGIT)) && !player->gotflag)
12121 		player->powers[pw_flashing] = flashingtics - 1;
12122 
12123 	// Counters, time dependent power ups.
12124 	// Time Bonus & Ring Bonus count settings
12125 
12126 	if (player->ammoremovaltimer)
12127 	{
12128 		if (--player->ammoremovaltimer == 0)
12129 			player->ammoremoval = 0;
12130 	}
12131 
12132 	// Strength counts up to diminish fade.
12133 	if (player->powers[pw_sneakers] && player->powers[pw_sneakers] < UINT16_MAX)
12134 		player->powers[pw_sneakers]--;
12135 
12136 	if (player->powers[pw_invulnerability] && player->powers[pw_invulnerability] < UINT16_MAX)
12137 		player->powers[pw_invulnerability]--;
12138 
12139 	if (player->powers[pw_flashing] && player->powers[pw_flashing] < UINT16_MAX && ((player->powers[pw_carry] == CR_NIGHTSMODE) || player->powers[pw_flashing] < flashingtics))
12140 		player->powers[pw_flashing]--;
12141 
12142 	if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM) // tails fly counter
12143 		player->powers[pw_tailsfly]--;
12144 
12145 	if (player->powers[pw_pushing] && player->powers[pw_pushing] < UINT16_MAX)
12146 		player->powers[pw_pushing]--;
12147 
12148 	if (player->powers[pw_justsprung] & ((1<<15)-1) && player->powers[pw_justsprung] < UINT16_MAX)
12149 		player->powers[pw_justsprung]--;
12150 	else
12151 		player->powers[pw_justsprung] = 0;
12152 
12153 	if (player->powers[pw_noautobrake] && player->powers[pw_noautobrake] < UINT16_MAX)
12154 		player->powers[pw_noautobrake]--;
12155 
12156 	if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
12157 	{
12158 		if (player->powers[pw_underwater] <= 12*TICRATE+1)
12159 		{
12160 			player->powers[pw_underwater] = 0;
12161 			P_RestoreMusic(player); //incase they were about to drown
12162 		}
12163 		else
12164 			player->powers[pw_underwater] = 0;
12165 	}
12166 	else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer
12167 		player->powers[pw_underwater]--;
12168 
12169 	if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
12170 		player->powers[pw_spacetime] = 0;
12171 	else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer
12172 		player->powers[pw_spacetime]--;
12173 
12174 	if (player->powers[pw_gravityboots] && player->powers[pw_gravityboots] < UINT16_MAX)
12175 		player->powers[pw_gravityboots]--;
12176 
12177 	if (player->powers[pw_extralife] && player->powers[pw_extralife] < UINT16_MAX)
12178 		player->powers[pw_extralife]--;
12179 
12180 	if (player->powers[pw_nights_linkfreeze] && player->powers[pw_nights_linkfreeze] < UINT16_MAX)
12181 		player->powers[pw_nights_linkfreeze]--;
12182 
12183 	if (player->powers[pw_nights_superloop] && player->powers[pw_nights_superloop] < UINT16_MAX)
12184 		player->powers[pw_nights_superloop]--;
12185 
12186 	if (player->powers[pw_nights_helper] && player->powers[pw_nights_helper] < UINT16_MAX)
12187 		player->powers[pw_nights_helper]--;
12188 
12189 	if (player->powers[pw_nocontrol] & ((1<<15)-1) && player->powers[pw_nocontrol] < UINT16_MAX)
12190 		player->powers[pw_nocontrol]--;
12191 	else
12192 		player->powers[pw_nocontrol] = 0;
12193 
12194 	if (player->powers[pw_ignorelatch] & ((1<<15)-1) && player->powers[pw_ignorelatch] < UINT16_MAX)
12195 		player->powers[pw_ignorelatch]--;
12196 	else
12197 		player->powers[pw_ignorelatch] = 0;
12198 
12199 	//pw_super acts as a timer now
12200 	if (player->powers[pw_super]
12201 	&& (player->mo->state < &states[S_PLAY_SUPER_TRANS1]
12202 	|| player->mo->state > &states[S_PLAY_SUPER_TRANS6]))
12203 		player->powers[pw_super]++;
12204 
12205 	if (player->powers[pw_carry] == CR_BRAKGOOP)
12206 	{
12207 		if (!player->powers[pw_flashing])
12208 		{
12209 			if (player->mo->state != &states[S_PLAY_STND])
12210 				P_SetPlayerMobjState(player->mo, S_PLAY_STND);
12211 			else
12212 				player->mo->tics = 2;
12213 		}
12214 		else
12215 			P_SetTarget(&player->mo->tracer, NULL);
12216 
12217 		if (!player->mo->tracer)
12218 			player->powers[pw_carry] = CR_NONE;
12219 	}
12220 
12221 	if (player->bumpertime)
12222 		player->bumpertime--;
12223 
12224 	if (player->skidtime)
12225 		player->skidtime--;
12226 
12227 	if (player->weapondelay)
12228 		player->weapondelay--;
12229 
12230 	if (player->tossdelay)
12231 		player->tossdelay--;
12232 
12233 	if (player->homing)
12234 		player->homing--;
12235 
12236 	if (player->texttimer)
12237 	{
12238 		--player->texttimer;
12239 		if (!player->texttimer && !player->exiting && player->textvar >= 4)
12240 		{
12241 			player->texttimer = 4*TICRATE;
12242 			player->textvar = 2; // GET n RINGS!
12243 
12244 			if (player->capsule && player->capsule->health != player->capsule->spawnpoint->angle)
12245 				player->textvar++; // GET n MORE RINGS!
12246 		}
12247 	}
12248 
12249 	if (player->losstime && !player->powers[pw_flashing])
12250 		player->losstime--;
12251 
12252 	// Flash player after being hit.
12253 	if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < flashingtics && (leveltime & 1))
12254 		player->mo->flags2 |= MF2_DONTDRAW;
12255 	else
12256 		player->mo->flags2 &= ~MF2_DONTDRAW;
12257 
12258 	player->pflags &= ~PF_SLIDING;
12259 
12260 #define dashmode player->dashmode
12261 	// Dash mode - thanks be to VelocitOni
12262 	if ((player->charflags & SF_DASHMODE) && !player->gotflag && !player->powers[pw_carry] && !player->exiting && !(maptol & TOL_NIGHTS) && !metalrecording) // woo, dashmode! no nights tho.
12263 	{
12264 		tic_t prevdashmode = dashmode;
12265 		boolean totallyradical = player->speed >= FixedMul(player->runspeed, player->mo->scale);
12266 		boolean floating = (player->secondjump == 1);
12267 
12268 		if ((totallyradical && !floating) || (player->pflags & PF_STARTDASH))
12269 		{
12270 			if (dashmode < DASHMODE_MAX)
12271 				dashmode++; // Counter. Adds 1 to dash mode per tic in top speed.
12272 			if (dashmode == DASHMODE_THRESHOLD) // This isn't in the ">=" equation because it'd cause the sound to play infinitely.
12273 				S_StartSound(player->mo, (player->charflags & SF_MACHINE) ? sfx_kc4d : sfx_cdfm40); // If the player enters dashmode, play this sound on the the tic it starts.
12274 		}
12275 		else if ((!totallyradical || !floating) && !(player->pflags & PF_SPINNING))
12276 		{
12277 			if (dashmode > 3)
12278 			{
12279 				dashmode -= 3; // Rather than lose it all, it gently counts back down!
12280 				if ((dashmode+3) >= DASHMODE_THRESHOLD && dashmode < DASHMODE_THRESHOLD)
12281 					S_StartSound(player->mo, sfx_kc65);
12282 			}
12283 			else
12284 				dashmode = 0;
12285 		}
12286 
12287 		if (dashmode < DASHMODE_THRESHOLD) // Exits Dash Mode if you drop below speed/dash counter tics. Not in the above block so it doesn't keep disabling in midair.
12288 		{
12289 			if (prevdashmode >= DASHMODE_THRESHOLD)
12290 			{
12291 				player->normalspeed = skins[player->skin].normalspeed; // Reset to default if not capable of entering dash mode.
12292 				player->jumpfactor = skins[player->skin].jumpfactor;
12293 			}
12294 		}
12295 		else if (P_IsObjectOnGround(player->mo)) // Activate dash mode if we're on the ground.
12296 		{
12297 			if (player->normalspeed < skins[player->skin].normalspeed*2) // If the player normalspeed is not currently at normalspeed*2 in dash mode, add speed each tic
12298 				player->normalspeed += FRACUNIT/5; // Enter Dash Mode smoothly.
12299 
12300 			if (player->jumpfactor < FixedMul(skins[player->skin].jumpfactor, 5*FRACUNIT/4)) // Boost jump height.
12301 				player->jumpfactor += FRACUNIT/300;
12302 		}
12303 
12304 		if (player->normalspeed >= skins[player->skin].normalspeed*2)
12305 		{
12306 			mobj_t *ghost = P_SpawnGhostMobj(player->mo); // Spawns afterimages
12307 			ghost->fuse = 2; // Makes the images fade quickly
12308 			if (ghost->tracer && !P_MobjWasRemoved(ghost->tracer))
12309 				ghost->tracer->fuse = ghost->fuse;
12310 		}
12311 	}
12312 	else if (dashmode)
12313 	{
12314 		if (dashmode >= DASHMODE_THRESHOLD) // catch getting the flag!
12315 		{
12316 			player->normalspeed = skins[player->skin].normalspeed;
12317 			player->jumpfactor = skins[player->skin].jumpfactor;
12318 			S_StartSound(player->mo, sfx_kc65);
12319 		}
12320 		dashmode = 0;
12321 	}
12322 #undef dashmode
12323 
12324 	LUAh_PlayerThink(player);
12325 
12326 /*
12327 //	Colormap verification
12328 	{
12329 		INT32 i,j;
12330 		sector_t *controlsec;
12331 		for (j=0; j<numsectors; j++)
12332 		{
12333 			mtag_t sectag = Tag_FGet(&sectors[j].tags);
12334 			controlsec = NULL;
12335 			// Does this sector have a water linedef?
12336 			for (i=0; i<numlines;i++)
12337 			{
12338 				mtag_t linetag = Tag_FGet(&lines[i].tags);
12339 				if ((lines[i].special == 121 || lines[i].special == 123)
12340 				&& linetag == sectag)
12341 				{
12342 					controlsec = lines[i].frontsector;
12343 					break;
12344 				}
12345 			}
12346 
12347 			if (i < numlines && controlsec)
12348 			{
12349 				controlsectag = Tag_FGet(&controlsec->tags);
12350 				// Does this sector have a colormap?
12351 				for (i=0; i<numlines;i++)
12352 				{
12353 					if (lines[i].special == 606 && linetag == controlsectag)
12354 						break;
12355 				}
12356 
12357 				if (i == numlines)
12358 					CONS_Debug(DBG_GAMELOGIC, "%d, %d\n", j, sectag);
12359 			}
12360 		}
12361 
12362 		I_Error("I'm done!\n");
12363 	}*/
12364 }
12365 
12366 // Checks if the mobj is above lava. Used by Pterabyte.
P_MobjAboveLava(mobj_t * mobj)12367 static boolean P_MobjAboveLava(mobj_t *mobj)
12368 {
12369 	sector_t *sector = mobj->subsector->sector;
12370 
12371 	if (sector->ffloors)
12372 	{
12373 		ffloor_t *rover;
12374 
12375 		for (rover = sector->ffloors; rover; rover = rover->next)
12376 		{
12377 			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || GETSECSPECIAL(rover->master->frontsector->special, 1) != 3)
12378 				continue;
12379 
12380 			if (mobj->eflags & MFE_VERTICALFLIP)
12381 			{
12382 				if (*rover->bottomheight <= mobj->ceilingz && *rover->bottomheight >= mobj->z)
12383 					return true;
12384 			}
12385 			else
12386 			{
12387 				if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
12388 					return true;
12389 			}
12390 		}
12391 	}
12392 
12393 	return false;
12394 }
12395 
12396 //
12397 // P_PlayerAfterThink
12398 //
12399 // Thinker for player after all other thinkers have run
12400 //
P_PlayerAfterThink(player_t * player)12401 void P_PlayerAfterThink(player_t *player)
12402 {
12403 	ticcmd_t *cmd;
12404 	INT32 oldweapon = player->currentweapon;
12405 	camera_t *thiscam = NULL; // if not one of the displayed players, just don't bother
12406 
12407 #ifdef PARANOIA
12408 	if (!player->mo)
12409 	{
12410 		const size_t playeri = (size_t)(player - players);
12411 		I_Error("P_PlayerAfterThink: players[%s].mo == NULL", sizeu1(playeri));
12412 	}
12413 #endif
12414 
12415 	cmd = &player->cmd;
12416 
12417 #ifdef SECTORSPECIALSAFTERTHINK
12418 	if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
12419 		player->onconveyor = 0;
12420 	// check special sectors : damage & secrets
12421 
12422 	if (!player->spectator)
12423 		P_PlayerInSpecialSector(player);
12424 #endif
12425 
12426 	if (splitscreen && player == &players[secondarydisplayplayer])
12427 		thiscam = &camera2;
12428 	else if (player == &players[displayplayer])
12429 		thiscam = &camera;
12430 
12431 	if (player->playerstate == PST_DEAD)
12432 	{
12433 		// camera may still move when guy is dead
12434 		//if (!netgame)
12435 		{
12436 			if (thiscam && thiscam->chase)
12437 				P_MoveChaseCamera(player, thiscam, false);
12438 		}
12439 		if (player->followmobj)
12440 		{
12441 			P_RemoveMobj(player->followmobj);
12442 			P_SetTarget(&player->followmobj, NULL);
12443 		}
12444 		return;
12445 	}
12446 
12447 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
12448 	{
12449 		player->powers[pw_gravityboots] = 0;
12450 		//player->mo->eflags &= ~MFE_VERTICALFLIP;
12451 	}
12452 
12453 	if (!(player->pflags & PF_WPNDOWN))
12454 	{
12455 		if (cmd->buttons & BT_WEAPONNEXT)
12456 		{
12457 			player->currentweapon++;
12458 			player->currentweapon %= NUM_WEAPONS;
12459 			player->pflags |= PF_WPNDOWN;
12460 		}
12461 
12462 		if (cmd->buttons & BT_WEAPONPREV)
12463 		{
12464 			player->currentweapon--;
12465 			if (player->currentweapon < 0)
12466 				player->currentweapon = NUM_WEAPONS - 1;
12467 			player->pflags |= PF_WPNDOWN;
12468 
12469 			if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
12470 				player->currentweapon--;
12471 			if (player->currentweapon == WEP_EXPLODE && (!(player->ringweapons & RW_EXPLODE) || !player->powers[pw_explosionring]))
12472 				player->currentweapon--;
12473 			if (player->currentweapon == WEP_GRENADE && (!(player->ringweapons & RW_GRENADE) || !player->powers[pw_grenadering]))
12474 				player->currentweapon--;
12475 			if (player->currentweapon == WEP_SCATTER && (!(player->ringweapons & RW_SCATTER) || !player->powers[pw_scatterring]))
12476 				player->currentweapon--;
12477 			if (player->currentweapon == WEP_BOUNCE && (!(player->ringweapons & RW_BOUNCE) || !player->powers[pw_bouncering]))
12478 				player->currentweapon--;
12479 			if (player->currentweapon == WEP_AUTO && (!(player->ringweapons & RW_AUTO) || !player->powers[pw_automaticring]))
12480 				player->currentweapon = 0;
12481 		}
12482 
12483 		if (cmd->buttons & BT_WEAPONMASK)
12484 		{
12485 			//Read the bits to determine individual weapon ring selection.
12486 			INT32 weapon = (cmd->buttons & BT_WEAPONMASK);
12487 
12488 			switch (weapon)
12489 			{
12490 			case 1: //normal / infinity
12491 				player->currentweapon = 0;
12492 				player->pflags |= PF_WPNDOWN;
12493 				break;
12494 			case 2: //automatic
12495 				if ((player->ringweapons & RW_AUTO) && player->powers[pw_automaticring])
12496 				{
12497 					player->currentweapon = WEP_AUTO;
12498 					player->pflags |= PF_WPNDOWN;
12499 				}
12500 				break;
12501 			case 3: //bounce
12502 				if ((player->ringweapons & RW_BOUNCE) && player->powers[pw_bouncering])
12503 				{
12504 					player->currentweapon = WEP_BOUNCE;
12505 					player->pflags |= PF_WPNDOWN;
12506 				}
12507 				break;
12508 			case 4: //scatter
12509 				if ((player->ringweapons & RW_SCATTER) && player->powers[pw_scatterring])
12510 				{
12511 					player->currentweapon = WEP_SCATTER;
12512 					player->pflags |= PF_WPNDOWN;
12513 				}
12514 				break;
12515 			case 5: //grenade
12516 				if ((player->ringweapons & RW_GRENADE) && player->powers[pw_grenadering])
12517 				{
12518 					player->currentweapon = WEP_GRENADE;
12519 					player->pflags |= PF_WPNDOWN;
12520 				}
12521 				break;
12522 			case 6: //explosion
12523 				if ((player->ringweapons & RW_EXPLODE) && player->powers[pw_explosionring])
12524 				{
12525 					player->currentweapon = WEP_EXPLODE;
12526 					player->pflags |= PF_WPNDOWN;
12527 				}
12528 				break;
12529 			case 7: //rail
12530 				if ((player->ringweapons & RW_RAIL) && player->powers[pw_railring])
12531 				{
12532 					player->currentweapon = WEP_RAIL;
12533 					player->pflags |= PF_WPNDOWN;
12534 				}
12535 				break;
12536 			}
12537 		}
12538 	}
12539 
12540 	if (!(cmd->buttons & (BT_WEAPONNEXT|BT_WEAPONPREV|BT_WEAPONMASK)))
12541 		player->pflags &= ~PF_WPNDOWN;
12542 
12543 	// Weapon cycling if out of ammo for a certain weapon
12544 	if (player->currentweapon == WEP_AUTO && (!(player->ringweapons & RW_AUTO) || !player->powers[pw_automaticring]))
12545 		player->currentweapon++;
12546 	if (player->currentweapon == WEP_BOUNCE && (!(player->ringweapons & RW_BOUNCE) || !player->powers[pw_bouncering]))
12547 		player->currentweapon++;
12548 	if (player->currentweapon == WEP_SCATTER && (!(player->ringweapons & RW_SCATTER) || !player->powers[pw_scatterring]))
12549 		player->currentweapon++;
12550 	if (player->currentweapon == WEP_GRENADE && (!(player->ringweapons & RW_GRENADE) || !player->powers[pw_grenadering]))
12551 		player->currentweapon++;
12552 	if (player->currentweapon == WEP_EXPLODE && (!(player->ringweapons & RW_EXPLODE) || !player->powers[pw_explosionring]))
12553 		player->currentweapon++;
12554 	if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
12555 		player->currentweapon = 0;
12556 
12557 	if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
12558 		S_StartSound(NULL, sfx_wepchg);
12559 
12560 	if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED))
12561 		P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
12562 
12563 	/* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing)
12564 		P_SetTarget(&player->mo->tracer, NULL);
12565 	else */
12566 	if (player->mo->tracer)
12567 	{
12568 		switch (player->powers[pw_carry])
12569 		{
12570 			case CR_PLAYER: // being carried by a flying character (such as Tails)
12571 			{
12572 				mobj_t *tails = player->mo->tracer;
12573 				player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14*FRACUNIT,10*FRACUNIT));
12574 
12575 				if (tails->player && !(tails->player->pflags & PF_CANCARRY))
12576 					player->powers[pw_carry] = CR_NONE;
12577 
12578 				if (player->mo->eflags & MFE_VERTICALFLIP)
12579 				{
12580 					if ((tails->z + tails->height + player->mo->height + FixedMul(FRACUNIT, player->mo->scale)) <= tails->ceilingz
12581 						&& (tails->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame
12582 						player->mo->z = tails->z + tails->height + 12*player->mo->scale;
12583 					else
12584 						player->powers[pw_carry] = CR_NONE;
12585 				}
12586 				else
12587 				{
12588 					if ((tails->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale)) >= tails->floorz
12589 						&& !(tails->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame
12590 						player->mo->z = tails->z - player->mo->height - 12*player->mo->scale;
12591 					else
12592 						player->powers[pw_carry] = CR_NONE;
12593 				}
12594 
12595 				if (tails->health <= 0)
12596 					player->powers[pw_carry] = CR_NONE;
12597 				else
12598 				{
12599 					if (tails->player)
12600 						P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true);
12601 					else
12602 						P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->angle, 4*FRACUNIT), true);
12603 					player->mo->momx = tails->momx;
12604 					player->mo->momy = tails->momy;
12605 					player->mo->momz = tails->momz;
12606 				}
12607 
12608 				if (G_CoopGametype() && tails->player && tails->player->bot != 1)
12609 				{
12610 					player->mo->angle = tails->angle;
12611 
12612 					if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
12613 						P_SetPlayerAngle(player, player->mo->angle);
12614 				}
12615 
12616 				if (P_AproxDistance(player->mo->x - tails->x, player->mo->y - tails->y) > player->mo->radius)
12617 					player->powers[pw_carry] = CR_NONE;
12618 
12619 				if (player->powers[pw_carry] != CR_NONE)
12620 				{
12621 					if (player->mo->state-states != S_PLAY_RIDE)
12622 						P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
12623 					if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER))
12624 						tails->player->powers[pw_tailsfly] = 0;
12625 				}
12626 				else
12627 					P_SetTarget(&player->mo->tracer, NULL);
12628 
12629 				if (player-players == consoleplayer && botingame)
12630 					CV_SetValue(&cv_analog[1], (player->powers[pw_carry] != CR_PLAYER));
12631 				break;
12632 			}
12633 			case CR_GENERIC: // being carried by some generic item
12634 			{
12635 				mobj_t *item = player->mo->tracer;
12636 				// tracer is what you're hanging onto
12637 				P_UnsetThingPosition(player->mo);
12638 				player->mo->x = item->x;
12639 				player->mo->y = item->y;
12640 				if (player->mo->eflags & MFE_VERTICALFLIP)
12641 					player->mo->z = item->z + item->height - FixedDiv(player->mo->height, 3*FRACUNIT);
12642 				else
12643 					player->mo->z = item->z - FixedDiv(player->mo->height, 3*FRACUNIT/2);
12644 				player->mo->momx = player->mo->momy = player->mo->momz = 0;
12645 				P_SetThingPosition(player->mo);
12646 				if (player->mo->state-states != S_PLAY_RIDE)
12647 					P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
12648 
12649 				// Controllable missile
12650 				if (item->type == MT_BLACKEGGMAN_MISSILE)
12651 				{
12652 					if (cmd->forwardmove > 0)
12653 						item->momz += FixedMul(FRACUNIT/4, item->scale);
12654 					else if (cmd->forwardmove < 0)
12655 						item->momz -= FixedMul(FRACUNIT/4, item->scale);
12656 
12657 					item->angle = player->mo->angle;
12658 					P_InstaThrust(item, item->angle, FixedMul(item->info->speed, item->scale));
12659 
12660 					if (player->mo->z <= player->mo->floorz || item->health <= 0)
12661 					{
12662 						player->powers[pw_carry] = CR_NONE;
12663 						P_SetTarget(&player->mo->tracer, NULL);
12664 					}
12665 				}
12666 				break;
12667 			}
12668 			case CR_MACESPIN: // being carried by a spinning chain
12669 			{
12670 				mobj_t *chain;
12671 				mobj_t *macecenter;
12672 				if (!player->mo->tracer->tracer) // can't spin around a point if... there is no point in doing so
12673 					break;
12674 				chain = player->mo->tracer;
12675 				macecenter = player->mo->tracer->tracer;
12676 
12677 				player->mo->height = P_GetPlayerSpinHeight(player);
12678 				// tracer is what you're hanging onto....
12679 				player->mo->momx = (chain->x - player->mo->x)*2;
12680 				player->mo->momy = (chain->y - player->mo->y)*2;
12681 				player->mo->momz = (chain->z - (player->mo->height-chain->height/2) - player->mo->z)*2;
12682 				P_TeleportMove(player->mo, chain->x, chain->y, chain->z - (player->mo->height-chain->height/2));
12683 				if (!player->powers[pw_flashing]) // handle getting hurt
12684 				{
12685 					player->pflags |= PF_JUMPED;
12686 					player->pflags &= ~PF_NOJUMPDAMAGE;
12687 					player->secondjump = 0;
12688 					player->pflags &= ~PF_THOKKED;
12689 
12690 					if ((macecenter->flags & MF_SLIDEME) // Noclimb on chain parameters gives this
12691 					&& !(twodlevel || player->mo->flags2 & MF2_TWOD)) // why on earth would you want to turn them in 2D mode?
12692 					{
12693 						macecenter->angle += cmd->sidemove<<ANGLETOFINESHIFT;
12694 						player->mo->angle += cmd->sidemove<<ANGLETOFINESHIFT; // 2048 --> ANGLE_MAX
12695 
12696 						if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
12697 							P_SetPlayerAngle(player, player->mo->angle);
12698 					}
12699 				}
12700 				break;
12701 			}
12702 			case CR_DUSTDEVIL:
12703 			{
12704 				mobj_t *mo = player->mo, *dustdevil = player->mo->tracer;
12705 
12706 				if (abs(mo->x - dustdevil->x) > dustdevil->radius || abs(mo->y - dustdevil->y) > dustdevil->radius)
12707 				{
12708 					P_SetTarget(&player->mo->tracer, NULL);
12709 					player->powers[pw_carry] = CR_NONE;
12710 					break;
12711 				}
12712 
12713 				break;
12714 			}
12715 			case CR_ROLLOUT:
12716 			{
12717 				mobj_t *mo = player->mo, *rock = player->mo->tracer;
12718 				UINT8 walktics = mo->state->tics - P_GetPlayerControlDirection(player);
12719 
12720 				if (!rock || P_MobjWasRemoved(rock))
12721 				{
12722 					P_SetTarget(&player->mo->tracer, NULL);
12723 					player->powers[pw_carry] = CR_NONE;
12724 					break;
12725 				}
12726 
12727 				if (player->cmd.forwardmove || player->cmd.sidemove)
12728 				{
12729 					rock->movedir = (player->cmd.angleturn << FRACBITS) + R_PointToAngle2(0, 0, player->cmd.forwardmove << FRACBITS, -player->cmd.sidemove << FRACBITS);
12730 					P_Thrust(rock, rock->movedir, rock->scale >> 1);
12731 				}
12732 
12733 				mo->momx = rock->momx;
12734 				mo->momy = rock->momy;
12735 				mo->momz = 0;
12736 
12737 				if (player->panim == PA_IDLE && (mo->momx || mo->momy))
12738 				{
12739 					P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
12740 				}
12741 
12742 				if (player->panim == PA_WALK && mo->tics > walktics)
12743 				{
12744 					mo->tics = walktics;
12745 				}
12746 
12747 				P_TeleportMove(player->mo, rock->x, rock->y, rock->z + rock->height);
12748 				break;
12749 			}
12750 			case CR_PTERABYTE: // being carried by a Pterabyte
12751 			{
12752 				mobj_t *ptera = player->mo->tracer;
12753 				mobj_t *spawnpoint = ptera->tracer->tracer;
12754 				player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14 * FRACUNIT, 10 * FRACUNIT));
12755 
12756 				if (ptera->health <= 0)
12757 					goto dropoff;
12758 
12759 				if (P_MobjAboveLava(ptera) && ptera->movefactor <= 3*TICRATE - 10)
12760 					goto dropoff;
12761 
12762 				if (player->mo->eflags & MFE_VERTICALFLIP)
12763 				{
12764 					if ((ptera->z + ptera->height + player->mo->height + FixedMul(FRACUNIT, player->mo->scale)) <= ptera->ceilingz
12765 						&& (ptera->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame
12766 						player->mo->z = ptera->z + ptera->height + FixedMul(FRACUNIT, player->mo->scale);
12767 
12768 					if (ptera->ceilingz - ptera->z > spawnpoint->ceilingz - spawnpoint->z + 512*FRACUNIT && ptera->movefactor <= 3 * TICRATE - 10)
12769 						goto dropoff;
12770 				}
12771 				else
12772 				{
12773 					if ((ptera->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale)) >= ptera->floorz
12774 						&& !(ptera->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame
12775 						player->mo->z = ptera->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale);
12776 
12777 					if (ptera->z - ptera->floorz > spawnpoint->z - spawnpoint->floorz + 512 * FRACUNIT && ptera->movefactor <= 3 * TICRATE - 10)
12778 						goto dropoff;
12779 				}
12780 
12781 				ptera->movefactor--;
12782 				if (!ptera->movefactor)
12783 					goto dropoff;
12784 
12785 				if (ptera->cusval >= 50)
12786 				{
12787 					player->powers[pw_carry] = CR_NONE;
12788 					P_SetTarget(&player->mo->tracer, NULL);
12789 					P_KillMobj(ptera, player->mo, player->mo, 0);
12790 					player->mo->momz = 9*FRACUNIT;
12791 					player->pflags |= PF_APPLYAUTOBRAKE|PF_JUMPED|PF_THOKKED;
12792 					P_SetMobjState(player->mo, S_PLAY_ROLL);
12793 					break;
12794 				}
12795 
12796 				if (ptera->cusval)
12797 					ptera->cusval--;
12798 
12799 				P_TryMove(player->mo, ptera->x + ptera->watertop, ptera->y + ptera->waterbottom, true);
12800 				player->mo->z += ptera->cvmem;
12801 				player->mo->momx = ptera->momx;
12802 				player->mo->momy = ptera->momy;
12803 				player->mo->momz = ptera->momz;
12804 
12805 				if (P_AproxDistance(player->mo->x - ptera->x - ptera->watertop, player->mo->y - ptera->y - ptera->waterbottom) > player->mo->radius)
12806 					goto dropoff;
12807 
12808 				ptera->watertop >>= 1;
12809 				ptera->waterbottom >>= 1;
12810 				ptera->cvmem >>= 1;
12811 
12812 				if (player->mo->state-states != S_PLAY_FALL)
12813 					P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
12814 				break;
12815 
12816 			dropoff:
12817 				player->powers[pw_carry] = CR_NONE;
12818 				P_SetTarget(&player->mo->tracer, NULL);
12819 				ptera->movefactor = TICRATE;
12820 				ptera->extravalue1 |= 4;
12821 				break;
12822 			}
12823 			default:
12824 				break;
12825 		}
12826 	}
12827 
12828 	if (thiscam)
12829 	{
12830 		if (!thiscam->chase) // bob view only if looking through the player's eyes
12831 		{
12832 			P_CalcHeight(player);
12833 			P_CalcPostImg(player);
12834 		}
12835 		else
12836 		{
12837 			// defaults to make sure 1st person cam doesn't do anything weird on startup
12838 			player->deltaviewheight = 0;
12839 			player->viewheight = FixedMul(41*player->height/48, player->mo->scale);
12840 			if (player->mo->eflags & MFE_VERTICALFLIP)
12841 				player->viewz = player->mo->z + player->mo->height - player->viewheight;
12842 			else
12843 				player->viewz = player->mo->z + player->viewheight;
12844 			if (server || addedtogame)
12845 				P_MoveChaseCamera(player, thiscam, false); // calculate the camera movement
12846 		}
12847 	}
12848 
12849 	// spectator invisibility and nogravity.
12850 	if ((netgame || multiplayer) && player->spectator)
12851 	{
12852 		player->mo->flags2 |= MF2_DONTDRAW;
12853 		player->mo->flags |= MF_NOGRAVITY;
12854 	}
12855 
12856 	if (player->powers[pw_dye])
12857 	{
12858 		player->mo->colorized = true;
12859 		player->mo->color = player->powers[pw_dye];
12860 	}
12861 
12862 	if (player->followmobj && (player->spectator || player->mo->health <= 0 || player->followmobj->type != player->followitem))
12863 	{
12864 		P_RemoveMobj(player->followmobj);
12865 		P_SetTarget(&player->followmobj, NULL);
12866 	}
12867 
12868 	if (!player->spectator && player->mo->health && player->followitem)
12869 	{
12870 		if (!player->followmobj || P_MobjWasRemoved(player->followmobj))
12871 		{
12872 			P_SetTarget(&player->followmobj, P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem));
12873 			P_SetTarget(&player->followmobj->tracer, player->mo);
12874 			switch (player->followmobj->type)
12875 			{
12876 				case MT_METALJETFUME:
12877 					player->followmobj->colorized = true;
12878 					break;
12879 				default:
12880 					player->followmobj->flags2 |= MF2_LINKDRAW;
12881 					break;
12882 			}
12883 		}
12884 
12885 		if (player->followmobj)
12886 		{
12887 			if (LUAh_FollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj))
12888 				{;}
12889 			else
12890 			{
12891 				switch (player->followmobj->type)
12892 				{
12893 					case MT_TAILSOVERLAY: // c:
12894 						P_DoTailsOverlay(player, player->followmobj);
12895 						break;
12896 					case MT_METALJETFUME:
12897 						P_DoMetalJetFume(player, player->followmobj);
12898 						break;
12899 					default:
12900 						var1 = 1;
12901 						var2 = 0;
12902 						A_CapeChase(player->followmobj);
12903 						break;
12904 				}
12905 			}
12906 		}
12907 	}
12908 }
12909 
P_SetPlayerAngle(player_t * player,angle_t angle)12910 void P_SetPlayerAngle(player_t *player, angle_t angle)
12911 {
12912 	INT16 delta = (INT16)(angle >> 16) - player->angleturn;
12913 
12914 	P_ForceLocalAngle(player, P_GetLocalAngle(player) + (delta << 16));
12915 	player->angleturn += delta;
12916 }
12917 
P_SetLocalAngle(player_t * player,angle_t angle)12918 void P_SetLocalAngle(player_t *player, angle_t angle)
12919 {
12920 	INT16 delta = (INT16)((angle - P_GetLocalAngle(player)) >> 16);
12921 
12922 	P_ForceLocalAngle(player, P_GetLocalAngle(player) + (angle_t)(delta << 16));
12923 
12924 	if (player == &players[consoleplayer])
12925 		ticcmd_oldangleturn[0] += delta;
12926 	else if (player == &players[secondarydisplayplayer])
12927 		ticcmd_oldangleturn[1] += delta;
12928 }
12929 
P_GetLocalAngle(player_t * player)12930 angle_t P_GetLocalAngle(player_t *player)
12931 {
12932 	if (player == &players[consoleplayer])
12933 		return localangle;
12934 	else if (player == &players[secondarydisplayplayer])
12935 		return localangle2;
12936 	else
12937 		return 0;
12938 }
12939 
P_ForceLocalAngle(player_t * player,angle_t angle)12940 void P_ForceLocalAngle(player_t *player, angle_t angle)
12941 {
12942 	angle = angle & ~UINT16_MAX;
12943 
12944 	if (player == &players[consoleplayer])
12945 		localangle = angle;
12946 	else if (player == &players[secondarydisplayplayer])
12947 		localangle2 = angle;
12948 }
12949 
P_PlayerFullbright(player_t * player)12950 boolean P_PlayerFullbright(player_t *player)
12951 {
12952 	return (player->powers[pw_super]
12953 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE && (((skin_t *)player->mo->skin)->flags & (SF_SUPER|SF_NONIGHTSSUPER)) == SF_SUPER) // Super colours? Super bright!
12954 		&& (player->exiting
12955 			|| !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
12956 			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6])))); // Note the < instead of <=
12957 }
12958 
12959 #define JUMPCURLED(player) ((player->pflags & PF_JUMPED)\
12960 	&& (!(player->charflags & SF_NOJUMPSPIN))\
12961 	&& (player->panim == PA_JUMP || player->panim == PA_ROLL))\
12962 
12963 // returns true if the player can enter a sector that they could not if standing at their skin's full height
P_PlayerCanEnterSpinGaps(player_t * player)12964 boolean P_PlayerCanEnterSpinGaps(player_t *player)
12965 {
12966 	UINT8 canEnter = LUAh_PlayerCanEnterSpinGaps(player);
12967 	if (canEnter == 1)
12968 		return true;
12969 	else if (canEnter == 2)
12970 		return false;
12971 
12972 	return ((player->pflags & (PF_SPINNING|PF_GLIDING)) // players who are spinning or gliding
12973 		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) // players who are landing from a glide
12974 		|| JUMPCURLED(player)); // players who are jumpcurled, but only if they would normally jump that way
12975 }
12976 
12977 // returns true if the player should use their skin's spinheight instead of their skin's height
P_PlayerShouldUseSpinHeight(player_t * player)12978 boolean P_PlayerShouldUseSpinHeight(player_t *player)
12979 {
12980 	return ((player->pflags & (PF_SPINNING|PF_GLIDING))
12981 		|| (player->mo->state == &states[player->mo->info->painstate])
12982 		|| (player->panim == PA_ROLL)
12983 		|| ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
12984 			&& !(player->charflags & SF_NOJUMPSPIN))
12985 		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
12986 		|| JUMPCURLED(player));
12987 }
12988