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 = §ors[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(§ors[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