1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_user.c 1513 2020-04-18 10:49:18Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: p_user.c,v $
21 // Revision 1.17  2004/07/27 08:19:37  exl
22 // New fmod, fs functions, bugfix or 2, patrol nodes
23 //
24 // Revision 1.16  2003/07/14 12:37:54  darkwolf95
25 // Fixed bug where frags don't display for Player 2 on death while in splitscreen.
26 //
27 // Revision 1.15  2001/05/27 13:42:48  bpereira
28 //
29 // Revision 1.14  2001/04/04 20:24:21  judgecutor
30 // Added support for the 3D Sound
31 //
32 // Revision 1.13  2001/03/03 06:17:33  bpereira
33 // Revision 1.12  2001/01/27 11:02:36  bpereira
34 //
35 // Revision 1.11  2001/01/25 22:15:44  bpereira
36 // added heretic support
37 //
38 // Revision 1.10  2000/11/04 16:23:43  bpereira
39 //
40 // Revision 1.9  2000/11/02 17:50:09  stroggonmeth
41 // Big 3Dfloors & FraggleScript commit!!
42 //
43 // Revision 1.8  2000/10/21 08:43:31  bpereira
44 // Revision 1.7  2000/08/31 14:30:56  bpereira
45 // Revision 1.6  2000/08/03 17:57:42  bpereira
46 // Revision 1.5  2000/04/23 16:19:52  bpereira
47 // Revision 1.4  2000/04/16 18:38:07  bpereira
48 // Revision 1.3  2000/03/29 19:39:48  bpereira
49 // Revision 1.2  2000/02/27 00:42:10  hurdler
50 // Revision 1.1.1.1  2000/02/22 20:32:32  hurdler
51 // Initial import into CVS (v1.29 pr3)
52 //
53 //
54 // DESCRIPTION:
55 //      Player related stuff.
56 //      Bobbing POV/weapon, movement.
57 //      Pending weapon.
58 //
59 //-----------------------------------------------------------------------------
60 
61 #include "doomincl.h"
62 #include "d_event.h"
63 #include "g_game.h"
64 #include "p_local.h"
65 #include "r_main.h"
66 #include "r_things.h"
67   // skins
68 #include "s_sound.h"
69 #include "p_setup.h"
70 #include "p_inter.h"
71 #include "m_random.h"
72 
73 #include "hardware/hw3sound.h"
74 
75 
76 // Index of the special effects (INVUL inverse) map.
77 #define INVERSECOLORMAP         32
78 
79 
80 //
81 // Movement.
82 //
83 
84 // 16 pixels of bob
85 #define MAXBOB  0x100000
86 
87 //added:22-02-98: initial momz when player jumps (moves up)
88 fixed_t jumpgravity = (6*FRACUNIT/NEWTICRATERATIO); // variable by fragglescript
89 
90 boolean  onground;
91 int	 extramovefactor = 0;
92 
93 
94 //
95 // P_Thrust
96 // Moves the given origin along a given angle.
97 //
P_Thrust(player_t * player,angle_t angle,fixed_t move)98 void P_Thrust(player_t *player, angle_t angle, fixed_t move)
99 {
100     mobj_t * pmo = player->mo;
101 #if 0
102     // friction and movefactor are now sector attributes
103     if(pmo->subsector->sector->special == 15  // heretic ice
104        && !(player->powers[pw_flight] && (pmo->z > pmo->floorz)))
105     {
106         move>>=2;  // Friction_Low
107     }
108 #endif
109     pmo->momx += FixedMul(move, cosine_ANG(angle));
110     pmo->momy += FixedMul(move, sine_ANG(angle));
111 }
112 
113 
114 // P_Thrust_Bob
115 // [WDJ] Thrust and independent bob.
116 // Contribute to bob effort, independent of thrust momentum.
117 // Do not bob when riding along on conveyors, or sliding, even though moving.
118 // Bob is adapted for effort in mud and ice by the caller.
119 
P_Thrust_Bob(player_t * player,angle_t angle,fixed_t moveth,fixed_t movebob)120 static void P_Thrust_Bob( player_t * player, angle_t angle, fixed_t moveth, fixed_t movebob )
121 {
122     int angf = ANGLE_TO_FINE( angle );
123     // thrust
124     player->mo->momx += FixedMul(moveth, finecosine[angf]);
125     player->mo->momy += FixedMul(moveth, finesine[angf]);
126     // bob
127     player->bob_momx += FixedMul(movebob, finecosine[angf]);
128     player->bob_momy += FixedMul(movebob, finesine[angf]);
129 }
130 
131 
132 
133 #ifdef CLIENTPREDICTION2
134 //
135 // P_ThrustSpirit
136 // Moves the given origin along a given angle.
137 //
P_ThrustSpirit(player_t * player,angle_t angle,fixed_t move)138 void P_ThrustSpirit(player_t *player, angle_t angle, fixed_t move)
139 {
140 #if 0
141     // friction and movefactor are now sector attributes
142     if(player->spirit->subsector->sector->special == 15
143     && !(player->powers[pw_flight] && !(player->spirit->z <= player->spirit->floorz))) // Friction_Low
144     {
145         move>>=2;  // Friction_Low
146     }
147 #endif
148     player->spirit->momx += FixedMul(move, cosine_ANG(angle));
149     player->spirit->momy += FixedMul(move, sine_ANG(angle));
150 }
151 #endif
152 
153 
154 static fixed_t  prev_viewheight = -1;  // detect changes in cv_viewheight
155 
156 //
157 // P_CalcHeight
158 // Calculate the walking / running height adjustment
159 //
160 // Called from P_PlayerThink after P_PlayerMove
161 // Called from P_MoveSpirit from Local_Maketic
162 // Called from P_DeathThink from P_PlayerThink, without P_PlayerMove
P_CalcHeight(player_t * player)163 void P_CalcHeight (player_t* player)
164 {
165     int         angle;
166     fixed_t     bob;
167     fixed_t     calc_viewheight, on_floor_viewheight;
168     mobj_t      * pmo = player->mo;
169     mobj_t      * smo = pmo;  // spirit (same as pmo unless using CLIENTPREDICTION2)
170 
171     // Regular movement bobbing
172     // (needs to be calculated for gun swing even if not on ground)
173     // OPTIMIZE: tablify angle
174     // Note: a LUT allows for effects like a ramp with low health.
175 
176 #ifdef CLIENTPREDICTION2
177     if( player->spirit )
178         smo = player->spirit;
179 #endif
180 
181     player->bob = ((FixedMul (player->bob_momx,player->bob_momx)
182                    +FixedMul (player->bob_momy,player->bob_momy))*NEWTICRATERATIO)>>2;
183 
184     // [WDJ] Boom 2.02, when on ice, limited bob to MAXBOB>>2.
185     // Moving onto ice would cause sudden bob position change.
186     // Went obsolete with MBF player bob.
187 
188     if (player->bob>MAXBOB)
189         player->bob = MAXBOB;
190 
191     // from heretic
192     if( pmo->flags2&MF2_FLY && !onground )
193         player->bob = FRACUNIT/2;
194 
195     if (player->cheats & CF_NOMOMENTUM)  // as in heretic because of fly bob
196     {
197         //added:15-02-98: it seems to be useless code!
198         //player->viewz = pmo->z + (((unsigned int)cv_viewheight.EV)<<FRACBITS);
199 
200         //if (player->viewz > pmo->ceilingz-4*FRACUNIT)
201         //    player->viewz = pmo->ceilingz-4*FRACUNIT;
202         player->viewz = smo->z + player->viewheight;
203         return;
204     }
205 
206     if( EV_legacy )
207 #ifdef CLIENTPREDICTION2
208       angle = (FINEANGLES/20*localgametic/NEWTICRATERATIO)&FINEMASK;
209 #else
210       angle = (FINEANGLES/20*leveltime/NEWTICRATERATIO)&FINEMASK;
211 #endif
212     else
213       angle = (FINEANGLES/20*leveltime)&FINEMASK;
214     bob = FixedMul ( player->bob/2, finesine[angle]);
215 
216     // move viewheight
217     calc_viewheight = ((unsigned int)cv_viewheight.EV) << FRACBITS; // default eye view height
218 
219     // The original was designed for a constant viewheight.
220     // Some users want to vary it during play using fragglescript.
221     if (player->playerstate == PST_LIVE)
222     {
223         on_floor_viewheight = calc_viewheight;
224 
225         if (calc_viewheight != prev_viewheight)
226         {
227             // cv_viewheight has changed
228             if ( prev_viewheight < 0 )
229             {
230                 player->viewheight = calc_viewheight;  // init quickly
231             }
232             else
233             {
234                 // provide a gradual viewheight change
235                 fixed_t dv = (calc_viewheight - prev_viewheight) >> 3;
236                 // when dv == 0, let through unaltered viewheight,
237                 // otherwise altered viewheight keeps retriggering this code
238                 if (dv)
239                    calc_viewheight = prev_viewheight + dv;  // slow rise and fall
240                 on_floor_viewheight = prev_viewheight;  // lessen falling effect
241                 player->deltaviewheight = (calc_viewheight - player->viewheight) >> 3;
242             }
243             prev_viewheight = calc_viewheight;
244         }
245 
246         player->viewheight += player->deltaviewheight;
247 
248         if (player->viewheight > on_floor_viewheight)
249         {
250             // player feet not on floor, so fall to viewheight
251             player->viewheight = calc_viewheight;
252             player->deltaviewheight = 0;
253         }
254 
255         if (player->viewheight < calc_viewheight/2)
256         {
257             // rise from floor
258             player->viewheight = calc_viewheight/2;
259             if (player->deltaviewheight <= 0)
260                 player->deltaviewheight = 1;
261         }
262 
263         // changers of viewheight will have also set deltaviewheight
264         if (player->deltaviewheight)
265         {
266             player->deltaviewheight += FRACUNIT/4;
267             if (!player->deltaviewheight)
268                 player->deltaviewheight = 1;
269         }
270     }
271 
272     if(player->chickenTics)  // heretic chicken morph
273         player->viewz = smo->z + player->viewheight-(20*FRACUNIT);
274     else
275         player->viewz = smo->z + player->viewheight + bob;
276 
277     if(pmo->flags2&MF2_FEETARECLIPPED
278         && player->playerstate != PST_DEAD
279         && pmo->z <= pmo->floorz)
280     {
281         player->viewz -= FOOTCLIPSIZE;
282     }
283 
284     if (player->viewz > smo->ceilingz-4*FRACUNIT)
285         player->viewz = smo->ceilingz-4*FRACUNIT;
286     if (player->viewz < smo->floorz+4*FRACUNIT)
287         player->viewz = smo->floorz+4*FRACUNIT;
288 }
289 
290 
291 
292 static byte  EN_move_doom = 0;
293 static byte  EN_cmd_abs_angle = 1;  // legacy absolute angle commands
294 
295 // local version control
DemoAdapt_p_user(void)296 void DemoAdapt_p_user( void )
297 {
298     // Move for Doom, Boom, MBF demo
299     EN_move_doom =
300        EN_doom_etc
301        && (EV_legacy < 128);  // Legacy demo, orig Doom, Boom, MBF
302 
303     // abs angle in legacy demos only
304     EN_cmd_abs_angle = (EV_legacy >= 125);
305 }
306 
307 
308 //
309 // P_MovePlayer
310 //
311 // Called from P_PlayerThink
P_MovePlayer(player_t * player)312 void P_MovePlayer (player_t* player)
313 {
314     mobj_t *   pmo = player->mo;
315     ticcmd_t*  cmd = &player->cmd;
316     int  movefactor = ORIG_FRICTION_FACTOR; // default
317     int  bobfactor = ORIG_FRICTION_FACTOR;
318 
319     if(EN_cmd_abs_angle)
320         pmo->angle = (cmd->angleturn<<16);
321     else
322         pmo->angle += (cmd->angleturn<<16);
323 
324     stat_tic_moved++;
325 #ifdef TICCMD_148
326     if( (cmd->ticflags & TC_received) == 0)
327         stat_tic_miss++;
328 #else
329     if( (cmd->angleturn & TICCMD_RECEIVED) == 0)
330         stat_tic_miss++;
331 #endif
332 
333     // Do not let the player control movement
334     //  if not onground.
335     onground = (pmo->z <= pmo->floorz)
336                || (pmo->flags & MF_BOUNCES) // MBF
337                || (pmo->flags2&(MF2_ONMOBJ|MF2_FLY))  // heretic
338                || (player->cheats & CF_FLYAROUND);   // cheat
339 
340     if(EN_variable_friction && onground)
341     {
342         movefactor = P_GetMoveFactor(pmo); // gets got_movefactor, got_friction
343 //        CONS_Printf("friction: %X, movefactor: %i\n", got_friction, movefactor);
344 
345         // [WDJ] bobfactor from killough, via prboom, adapted to legacy.
346         // killough 11/98:
347         // On sludge, make bobbing depend on efficiency.
348         // On ice, make it depend on effort.
349 
350         // [WDJ] Test the friction and movefactor from GetMovefactor.
351         bobfactor = (got_friction < ORIG_FRICTION) ?
352              got_movefactor  // mud
353            : ORIG_FRICTION_FACTOR;  // ice  (killough)
354 //           : (ORIG_FRICTION_FACTOR + got_movefactor)/2;  // ice [WDJ]
355 
356     }
357 
358     if (!onground)
359         bobfactor >>= 2;  // air and underwater
360     else if (pmo->eflags & MF_UNDERWATER)
361         bobfactor >>= 1;
362 
363     if( EN_move_doom )
364     {
365         // Doom, Boom, MBF movement
366         boolean  jumpover = player->cheats & CF_JUMPOVER;  // legacy cheat
367         if (cmd->forwardmove && (onground || jumpover))
368         {
369             // dirty hack to let the player avatar walk over a small wall
370             // while in the air
371             if (jumpover)
372             {
373                 if(pmo->momz > 0)
374                     P_Thrust (player, pmo->angle, 5*movefactor);
375             }
376             else
377             {
378                 P_Thrust_Bob( player, pmo->angle, cmd->forwardmove*movefactor, cmd->forwardmove*bobfactor );
379             }
380         }
381 
382         if (cmd->sidemove && onground)
383         {
384             P_Thrust_Bob( player, pmo->angle-ANG90, cmd->sidemove*movefactor, cmd->sidemove*bobfactor );
385         }
386 
387         player->aiming = (signed char)cmd->aiming;
388     }
389     else
390     {
391         // most current
392         fixed_t   movepushforward=0, movepushside=0;
393         player->aiming = cmd->aiming<<16;
394         if( player->chickenTics )
395         {
396             // [WDJ] Moved to after other movefactor, so it can have some effect.
397             // movefactor = 2500;  // heretic chicken
398             // Modify movefactor to chicken size, (chicken on ice)
399             movefactor = movefactor * 625 / 512;
400               // * 2500 / 2048
401         }
402 
403         if (cmd->forwardmove)
404         {
405             movepushforward = cmd->forwardmove * (movefactor + extramovefactor);
406 
407             if (pmo->eflags & MF_UNDERWATER)
408             {
409                 // half forward speed when waist under water
410                 // a little better grip if feets touch the ground
411                 if (!onground)
412                     movepushforward >>= 1;
413                 else
414                     movepushforward = movepushforward *3/4;
415             }
416             else
417             {
418                 // allow very small movement while in air for gameplay
419                 if (!onground)
420                     movepushforward >>= 3;
421             }
422 
423             P_Thrust_Bob( player, pmo->angle, movepushforward, cmd->forwardmove*bobfactor);
424         }
425 
426         if (cmd->sidemove)
427         {
428             movepushside = cmd->sidemove * (movefactor + extramovefactor);
429             if (pmo->eflags & MF_UNDERWATER)
430             {
431                 if (!onground)
432                     movepushside >>= 1;
433                 else
434                     movepushside = movepushside *3/4;
435             }
436             else
437             {
438                 if (!onground)
439                     movepushside >>= 3;
440             }
441 
442             P_Thrust_Bob( player, pmo->angle-ANG90, movepushside, cmd->sidemove*bobfactor);
443         }
444 
445         // mouselook swim when waist underwater
446         pmo->eflags &= ~MF_SWIMMING;
447         if (pmo->eflags & MF_UNDERWATER)
448         {
449             fixed_t a;
450             // swim up/down full move when forward full speed
451             a = FixedMul( movepushforward*50, sine_ANG(player->aiming) >>5 );
452 
453             if ( a != 0 ) {
454                 pmo->eflags |= MF_SWIMMING;
455                 pmo->momz += a;
456             }
457         }
458     }
459 
460     //added:22-02-98: jumping
461     if (cmd->buttons & BT_JUMP)
462     {
463         if( pmo->flags2&MF2_FLY )
464             player->flyheight = 10;
465         else
466         if(pmo->eflags & MF_UNDERWATER)
467             //TODO: goub gloub when push up in water
468             pmo->momz = jumpgravity/2;
469         else
470         // can't jump while in air, can't jump while jumping
471         if( onground && !(player->jumpdown & 1))
472         {
473             pmo->momz = jumpgravity;
474             if( !(player->cheats & CF_FLYAROUND) )
475             {
476                 S_StartScreamSound (pmo, sfx_jump);
477                 // keep jumping ok if FLY mode.
478                 player->jumpdown |= 1;
479             }
480         }
481     }
482     else
483         player->jumpdown &= ~1;
484 
485 
486     if (cmd->forwardmove || cmd->sidemove)
487     {
488         if( player->chickenTics )
489         {
490             if( pmo->state == &states[S_CHICPLAY])
491                 P_SetMobjState(pmo, S_CHICPLAY_RUN1);
492         }
493         else
494             if(pmo->state == &states[S_PLAY])
495                 P_SetMobjState(pmo, S_PLAY_RUN1);
496     }
497 #ifdef TICCMD_148
498     if( EN_heretic && (cmd->ticflags & TC_flydown) )
499 #else
500     if( EN_heretic && (cmd->angleturn & BT_FLYDOWN) )
501 #endif
502     {
503         player->flyheight = -10;
504     }
505 /* HERETODO
506     fly = cmd->lookfly>>4;
507     if(fly > 7)
508         fly -= 16;
509     if(fly && player->powers[pw_flight])
510     {
511         if(fly != TOCENTER)
512         {
513             player->flyheight = fly*2;
514             if(!(pmo->flags2&MF2_FLY))
515             {
516                 pmo->flags2 |= MF2_FLY;
517                 pmo->flags |= MF_NOGRAVITY;
518             }
519         }
520         else
521         {
522             pmo->flags2 &= ~MF2_FLY;
523             pmo->flags &= ~MF_NOGRAVITY;
524         }
525     }
526     else if(fly > 0)
527     {
528         P_PlayerUseArtifact(player, arti_fly);
529     }*/
530     if(pmo->flags2&MF2_FLY)
531     {
532         pmo->momz = player->flyheight*FRACUNIT;
533         if(player->flyheight)
534             player->flyheight /= 2;
535     }
536 }
537 
538 
539 
540 //
541 // P_DeathThink
542 // Fall on your face when dying.
543 // Decrease POV height to floor height.
544 //
545 #define ANG5    (ANG90/18)
546 
P_DeathThink(player_t * player)547 void P_DeathThink (player_t* player)
548 {
549     mobj_t * pmo = player->mo;
550     angle_t  angle;
551 
552     P_MovePsprites (player);
553 
554     // fall to the ground
555     if (player->viewheight > 6*FRACUNIT)
556         player->viewheight -= FRACUNIT;
557 
558     if (player->viewheight < 6*FRACUNIT)
559         player->viewheight = 6*FRACUNIT;
560 
561     player->deltaviewheight = 0;
562     onground = pmo->z <= pmo->floorz;
563 
564     P_CalcHeight (player);
565 
566     mobj_t *attacker = player->attacker;
567 
568     // watch my killer (if there is one)
569     if (attacker && attacker != pmo)
570     {
571         angle = R_PointToAngle2 (pmo->x,
572                                  pmo->y,
573                                  player->attacker->x,
574                                  player->attacker->y);
575 
576         angle_t delta = angle - pmo->angle;
577 
578         if (delta < ANG5 || delta > (unsigned)-ANG5)
579         {
580             // Looking at killer,
581             //  so fade damage flash down.
582             pmo->angle = angle;
583 
584             if (player->damagecount)
585                 player->damagecount--;
586         }
587         else if (delta < ANG180)
588             pmo->angle += ANG5;
589         else
590             pmo->angle -= ANG5;
591 
592         //added:22-02-98:
593         // change aiming to look up or down at the attacker (DOESNT WORK)
594         // FIXME : the aiming returned seems to be too up or down... later
595         /*
596         fixed_t dist = P_AproxDistance(attacker->x - pmo->x, attacker->y - pmo->y);
597         fixed_t dz = attacker->z +(attacker->height>>1) -pmo->z;
598         angle_t pitch = 0;
599         if (dist)
600           pitch = ArcTan(FixedDiv(dz, dist));
601         */
602         int32_t pitch = (attacker->z - pmo->z)>>17;
603         player->aiming = G_ClipAimingPitch(pitch);
604 
605     }
606     else if (player->damagecount)
607         player->damagecount--;
608 
609     if (player->cmd.buttons & BT_USE)
610     {
611         player->playerstate = PST_REBORN;
612         pmo->special2 = 666;
613     }
614 }
615 
616 //----------------------------------------------------------------------------
617 //
618 // PROC P_ChickenPlayerThink
619 //
620 //----------------------------------------------------------------------------
621 
P_ChickenPlayerThink(player_t * player)622 void P_ChickenPlayerThink(player_t *player)
623 {
624     mobj_t * pmo = player->mo;
625 
626     if(player->health > 0)
627     { // Handle beak movement
628         P_UpdateBeak(player, &player->psprites[ps_weapon]);
629     }
630     if(player->chickenTics&15)
631     {
632         return;
633     }
634     // Heretic uses of P_Random
635     if(!(pmo->momx+pmo->momy) && PP_Random(ph_chickenthink) < 160)
636     { // Twitch view angle
637         pmo->angle += PP_SignedRandom(ph_chickenthink)<<19;
638     }
639     if((pmo->z <= pmo->floorz) && (PP_Random(ph_chickenthink) < 32))
640     { // Jump and noise
641         pmo->momz += FRACUNIT;
642         P_SetMobjState(pmo, S_CHICPLAY_PAIN);
643         return;
644     }
645     if(PP_Random(ph_chickenthink) < 48)
646     { // Just noise
647         S_StartScreamSound(pmo, sfx_chicact);
648     }
649 }
650 
651 //----------------------------------------------------------------------------
652 //
653 // FUNC P_UndoPlayerChicken
654 //
655 //----------------------------------------------------------------------------
656 
657 // [WDJ] Fixed to keep the same player mobj.
658 // Used to change the player mobj, and hide the prev as a corpse above
659 // the ceiling using S_FREETARGMOBJ.
P_UndoPlayerChicken(player_t * player)660 boolean P_UndoPlayerChicken(player_t *player)
661 {
662     mobj_t *fog;
663     mobj_t * pmo = player->mo;
664     weapontype_t weapon;
665     int oldflags2;
666 
667     weapon = pmo->special1;  // saved by player morph
668     oldflags2 = pmo->flags2;
669     // Morph back into player
670     if( ! P_MorphMobj(pmo, MT_PLAYER, MM_testsize,
671 #ifdef PLAYER_CHICKEN_KEEPS_SHADOW
672                       MF_SHADOW
673 #else
674                       0
675 #endif
676                       ) )
677     { // Didn't fit
678         player->chickenTics = 2*35;  // retry later
679         return false;
680     }
681     if(oldflags2&MF2_FLY)  // preserve fly flags
682     {
683         pmo->flags2 |= MF2_FLY;
684         pmo->flags |= MF_NOGRAVITY;
685     }
686     // Restore player skin and skincolor.
687     pmo->skin = skins[player->skin];  // restore player skin
688     pmo->tflags |= (player->skincolor) << MFT_TRANSSHIFT;
689     pmo->reactiontime = 18;
690     player->chickenTics = 0;
691 #ifndef PLAYER_CHICKEN_KEEPS_SHADOW
692     // Not in vanilla Heretic, but implied by clearing MF_SHADOW.
693     // Legacy2 clears it.
694     player->powers[pw_invisibility] = 0;
695 #endif
696     player->powers[pw_weaponlevel2] = 0;
697     player->weaponinfo = wpnlev1info;
698     player->health = pmo->health = MAXHEALTH;
699 
700     // This telefog is placed differently than others.
701     int angf = ANGLE_TO_FINE( pmo->angle );
702     fog = P_SpawnMobj(pmo->x+20*finecosine[angf], pmo->y+20*finesine[angf],
703                       pmo->z+TELEFOGHEIGHT, MT_TFOG);
704     S_StartObjSound(fog, sfx_telept);
705     P_PostChickenWeapon(player, weapon);
706     return true;
707 }
708 
709 //
710 // P_MoveCamera : make sure the camera is not outside the world
711 //                and looks at the player avatar
712 //
713 
714 // [WDJ] There is only one camera, so when there are two players, it can
715 // only be used as chase camera for one, and that is player1
716 // It now records who it is chasing.
717 camera_t camera;
718 
719 //#define VIEWCAM_DIST    (128<<FRACBITS)
720 //#define VIEWCAM_HEIGHT  (20<<FRACBITS)
721 
722 consvar_t cv_cam_dist   = {"cam_dist"  ,"128"  ,CV_FLOAT,NULL};
723 consvar_t cv_cam_height = {"cam_height", "20"   ,CV_FLOAT,NULL};
724 consvar_t cv_cam_speed  = {"cam_speed" ,  "0.25",CV_FLOAT,NULL};
725 
P_ResetCamera(player_t * player)726 void P_ResetCamera (player_t *player)
727 {
728     fixed_t x,y,z;
729 
730     camera.chase = player;
731     x = player->mo->x;
732     y = player->mo->y;
733     z = player->mo->z + (((unsigned int)cv_viewheight.EV)<<FRACBITS);
734 
735     // hey we should make sure that the sounds are heard from the camera
736     // instead of the marine's head : TO DO
737 
738     // set bits for the camera object
739     if (!camera.mo)
740         camera.mo = P_SpawnMobj (x,y,z, MT_CHASECAM);
741     else
742     {
743         camera.mo->x = x;
744         camera.mo->y = y;
745         camera.mo->z = z;
746     }
747 
748     camera.mo->angle = player->mo->angle;
749     camera.aiming = 0;
750 }
751 
752 // Unused
753 #if 0
754 fixed_t cameraz;
755 
756 boolean PTR_FindCameraPoint (intercept_t* in)
757 {
758 //  Disabled all except return false
759 #if 0
760     int         side;
761     fixed_t             slope;
762     fixed_t             dist;
763     line_t*             li;
764 
765     li = in->d.line;
766 
767     if ( !(li->flags & ML_TWOSIDED) )
768         return false;
769 
770     // crosses a two sided line
771     //added:16-02-98: Fab comments : sets opentop, openbottom, openrange
772     //                lowfloor is the height of the lowest floor
773     //                         (be it front or back)
774     P_LineOpening (li);
775 
776     dist = FixedMul (attackrange, in->frac);
777 
778     if (li->frontsector->floorheight != li->backsector->floorheight)
779     {
780         //added:18-02-98: comments :
781         // find the slope aiming on the border between the two floors
782         slope = FixedDiv (openbottom - cameraz , dist);
783         if (slope > aimslope)
784             return false;
785     }
786 
787     if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
788     {
789         slope = FixedDiv (opentop - shootz , dist);
790         if (slope < aimslope)
791             goto hitline;
792     }
793 
794     return true;
795 
796     // hit line
797   hitline:
798 #endif
799     // stop the search
800     return false;
801 }
802 #endif
803 
804 
805 
P_MoveChaseCamera(player_t * player)806 void P_MoveChaseCamera (player_t *player)
807 {
808     angle_t       angle;
809     int           angf;
810     fixed_t       x, y, z, viewpointx, viewpointy;
811     fixed_t       dist;
812     float         f1, f2;
813     subsector_t*  newsubsec;
814     mobj_t*       pmo = player->mo;
815 
816     if (!camera.mo)
817         P_ResetCamera (player);
818 
819     angf = ANGLE_TO_FINE( pmo->angle );
820 
821     // sets ideal cam pos
822     dist  = cv_cam_dist.value;
823     x = pmo->x - FixedMul( finecosine[angf], dist);
824     y = pmo->y - FixedMul( finesine[angf], dist);
825     z = pmo->z + (((unsigned int)cv_viewheight.EV)<<FRACBITS) + cv_cam_height.value;
826 
827 /*    P_PathTraverse ( pmo->x, pmo->y, x, y, PT_ADDLINES, PTR_UseTraverse );*/
828 
829     // move camera down to move under lower ceilings
830     newsubsec = R_IsPointInSubsector ((pmo->x + camera.mo->x)>>1,(pmo->y + camera.mo->y)>>1);
831     if (!newsubsec)
832     {
833         // use player sector
834         if (pmo->subsector->sector->ceilingheight - camera.mo->height < z)
835             z = pmo->subsector->sector->ceilingheight - camera.mo->height-11*FRACUNIT; // don't be blocked by a opened door
836     }
837     else
838     // camera fit ?
839     if (newsubsec->sector->ceilingheight - camera.mo->height < z)
840     {
841         // no fit
842         z = newsubsec->sector->ceilingheight - camera.mo->height - 11*FRACUNIT;
843     }
844 
845     // does the camera fit in its own sector
846     newsubsec = R_PointInSubsector (camera.mo->x, camera.mo->y);
847     if (newsubsec->sector->ceilingheight - camera.mo->height < z)
848         z = newsubsec->sector->ceilingheight - camera.mo->height - 11*FRACUNIT;
849 
850 
851     // point viewed by the camera
852     // this point is just 64 unit forward the player
853     dist = 64 << FRACBITS;
854     viewpointx = pmo->x + FixedMul( finecosine[angf], dist);  // player angle
855     viewpointy = pmo->y + FixedMul( finesine[angf], dist);
856 
857     camera.mo->angle = R_PointToAngle2(camera.mo->x, camera.mo->y,
858                                        viewpointx, viewpointy);
859 
860     // follow the player
861     camera.mo->momx = FixedMul(x - camera.mo->x, cv_cam_speed.value);
862     camera.mo->momy = FixedMul(y - camera.mo->y, cv_cam_speed.value);
863     camera.mo->momz = FixedMul(z - camera.mo->z, cv_cam_speed.value);
864 
865     // compute aiming to look toward the viewed point
866     f1=FIXED_TO_FLOAT(viewpointx - camera.mo->x);
867     f2=FIXED_TO_FLOAT(viewpointy - camera.mo->y);
868     dist=sqrt(f1*f1+f2*f2)*FRACUNIT;
869     angle=R_PointToAngle2(0, camera.mo->z, dist,
870                          pmo->z + (pmo->height>>1) + sine_ANG(player->aiming)*64);
871 
872     angle = G_ClipAimingPitch(angle);
873     dist = camera.aiming - angle;
874     camera.aiming -= (dist>>3);
875 }
876 
877 
878 byte weapontobutton[NUMWEAPONS]=
879   {wp_fist    <<BT_WEAPONSHIFT,
880    wp_pistol  <<BT_WEAPONSHIFT,
881    wp_shotgun <<BT_WEAPONSHIFT,
882    wp_chaingun<<BT_WEAPONSHIFT,
883    wp_missile <<BT_WEAPONSHIFT,
884    wp_plasma  <<BT_WEAPONSHIFT,
885    wp_bfg     <<BT_WEAPONSHIFT,
886   (wp_fist    <<BT_WEAPONSHIFT) | BT_EXTRAWEAPON,// wp_chainsaw
887   (wp_shotgun <<BT_WEAPONSHIFT) | BT_EXTRAWEAPON};//wp_supershotgun
888 
889 #ifdef CLIENTPREDICTION2
890 
CL_ResetSpiritPosition(mobj_t * mobj)891 void CL_ResetSpiritPosition(mobj_t *mobj)
892 {
893     P_UnsetThingPosition(mobj->player->spirit);
894     mobj->player->spirit->x=mobj->x;
895     mobj->player->spirit->y=mobj->y;
896     mobj->player->spirit->z=mobj->z;
897     mobj->player->spirit->momx=0;
898     mobj->player->spirit->momy=0;
899     mobj->player->spirit->momz=0;
900     mobj->player->spirit->angle=mobj->angle;
901     P_SetThingPosition(mobj->player->spirit);
902 }
903 
P_ProcessCmdSpirit(player_t * player,ticcmd_t * cmd)904 void P_ProcessCmdSpirit (player_t* player,ticcmd_t *cmd)
905 {
906     fixed_t   movepushforward=0, movepushside=0;
907 #ifdef PARANOIA
908     if(!player)
909         I_Error("P_MoveSpirit : player null");
910     if(!player->spirit)
911         I_Error("P_MoveSpirit : player->spirit null");
912     if(!cmd)
913         I_Error("P_MoveSpirit : cmd null");
914 #endif
915 
916     // don't move if dead
917     if( player->playerstate != PST_LIVE )
918     {
919 #ifdef TICCMD_148
920         cmd->ticflags &= ~TC_XY;  // turn off clientprediction
921 #else
922         cmd->angleturn &= ~TICCMD_XY;
923 #endif
924         return;
925     }
926     onground = (player->spirit->z <= player->spirit->floorz) ||
927                (player->cheats & CF_FLYAROUND);
928 
929     if (player->spirit->reactiontime)
930     {
931         player->spirit->reactiontime--;
932         return;
933     }
934 
935     player->spirit->angle = cmd->angleturn<<16;
936 #ifdef TICCMD_148
937     cmd->ticflags |= TC_XY;
938 #else
939     cmd->angleturn |= TICCMD_XY;
940 #endif
941 /*
942     // now weapon is allways send change is detected at receiver side
943     if(cmd->buttons & BT_CHANGE)
944     {
945         player->spirit->movedir = cmd->buttons & (BT_WEAPONMASK | BT_EXTRAWEAPON);
946         cmd->buttons &=~BT_CHANGE;
947     }
948     else
949     {
950         if( player->pendingweapon!=wp_nochange )
951             player->spirit->movedir=weapontobutton[player->pendingweapon];
952         cmd->buttons&=~(BT_WEAPONMASK | BT_EXTRAWEAPON);
953         cmd->buttons|=player->spirit->movedir;
954     }
955 */
956     if (cmd->forwardmove)
957     {
958         movepushforward = cmd->forwardmove * movefactor;
959 
960         if (player->spirit->eflags & MF_UNDERWATER)
961         {
962             // half forward speed when waist under water
963             // a little better grip if feets touch the ground
964             if (!onground)
965                 movepushforward >>= 1;
966             else
967                 movepushforward = movepushforward *3/4;
968         }
969         else
970         {
971             // allow very small movement while in air for gameplay
972             if (!onground)
973                 movepushforward >>= 3;
974         }
975 
976         P_ThrustSpirit (player->spirit, player->spirit->angle, movepushforward);
977     }
978 
979     if (cmd->sidemove)
980     {
981         movepushside = cmd->sidemove * movefactor;
982         if (player->spirit->eflags & MF_UNDERWATER)
983         {
984             if (!onground)
985                 movepushside >>= 1;
986             else
987                 movepushside = movepushside *3/4;
988         }
989         else
990             if (!onground)
991                 movepushside >>= 3;
992 
993         P_ThrustSpirit (player->spirit, player->spirit->angle-ANG90, movepushside);
994     }
995 
996     // mouselook swim when waist underwater
997     player->spirit->eflags &= ~MF_SWIMMING;
998     if (player->spirit->eflags & MF_UNDERWATER)
999     {
1000         fixed_t a;
1001         // swim up/down full move when forward full speed
1002         a = FixedMul( movepushforward*50, finesine[ (cmd->aiming>>(ANGLETOFINESHIFT-16)) ] >>5 );
1003 
1004         if ( a != 0 ) {
1005             player->spirit->eflags |= MF_SWIMMING;
1006             player->spirit->momz += a;
1007         }
1008     }
1009 
1010     //added:22-02-98: jumping
1011     if (cmd->buttons & BT_JUMP)
1012     {
1013         // can't jump while in air, can't jump while jumping
1014         if (!(player->jumpdown & 2) &&
1015              (onground || (player->spirit->eflags & MF_UNDERWATER)) )
1016         {
1017             if (onground)
1018                 player->spirit->momz = jumpgravity;
1019             else //water content
1020                 player->spirit->momz = jumpgravity/2;
1021 
1022             //TODO: goub gloub when push up in water
1023 
1024             if ( !(player->cheats & CF_FLYAROUND) && onground && !(player->spirit->eflags & MF_UNDERWATER))
1025             {
1026                 S_StartScreamSound(player->spirit, sfx_jump);
1027 
1028                 // keep jumping ok if FLY mode.
1029                 player->jumpdown |= 2;
1030             }
1031         }
1032     }
1033     else
1034         player->jumpdown &= ~2;
1035 
1036 }
1037 
P_MoveSpirit(player_t * p,ticcmd_t * cmd,int realtics)1038 void P_MoveSpirit (player_t* p,ticcmd_t *cmd, int realtics)
1039 {
1040     if( gamestate != GS_LEVEL )
1041         return;
1042 
1043     if(p->spirit)
1044     {
1045         int    i;
1046 
1047         p->spirit->flags|=MF_SOLID;
1048         for(i=0;i<realtics;i++)
1049         {
1050             P_ProcessCmdSpirit(p,cmd);
1051             P_MobjThinker(p->spirit);
1052         }
1053         p->spirit->flags&=~MF_SOLID;
1054         P_CalcHeight (p);                 // z-bobing of player
1055         A_TicWeapon(p, &p->psprites[0]);  // bobing of weapon
1056         cmd->x=p->spirit->x;
1057         cmd->y=p->spirit->y;
1058         spirit_update=true;
1059     }
1060     else
1061     if(p->mo)
1062     {
1063         cmd->x=p->mo->x;
1064         cmd->y=p->mo->y;
1065     }
1066 }
1067 
1068 #endif
1069 
1070 
1071 //
1072 // P_PlayerThink
1073 //
1074 
1075 boolean playerdeadview; //Fab:25-04-98:show dm rankings while in death view
1076 
1077 // Fixed to not change the player mobj.
P_PlayerThink(player_t * player)1078 void P_PlayerThink (player_t* player)
1079 {
1080     ticcmd_t*           cmd;
1081     weapontype_t        newweapon;
1082     mobj_t *  pmo = player->mo;
1083 
1084 #ifdef PARANOIA
1085     if(!pmo) I_Error("p_playerthink : players[%d].mo == NULL",player-players);
1086 #endif
1087 
1088     // fixme: do this in the cheat code
1089     if (player->cheats & CF_NOCLIP)
1090         pmo->flags |= MF_NOCLIP;
1091     else
1092         pmo->flags &= ~MF_NOCLIP;
1093 
1094     // chain saw run forward
1095     cmd = &player->cmd;
1096     if (pmo->flags & MF_JUSTATTACKED)
1097     {
1098         // added : now angle turn is a absolute value not relative
1099         if( ! EN_cmd_abs_angle )
1100             cmd->angleturn = 0;
1101         cmd->forwardmove = 0xc800/512;
1102         cmd->sidemove = 0;
1103 #ifdef TICCMD_148
1104         cmd->ticflags = 0;  // stun
1105 #endif
1106         pmo->flags &= ~MF_JUSTATTACKED;
1107     }
1108 
1109     if (player->playerstate == PST_REBORN)
1110     {
1111 #ifdef PARANOIA
1112         I_SoftError("player %d is in PST_REBORN\n");
1113 #endif
1114         // it is not "normal" but far to be critical
1115         return;
1116     }
1117 
1118     if (player->playerstate == PST_DEAD)
1119     {
1120         //Fab:25-04-98: show the dm rankings while dead, only in deathmatch
1121         //DarkWolf95:July 03, 2003:fixed bug where rankings only show on player1's death
1122         if (player== displayplayer_ptr
1123             || player== displayplayer2_ptr ) // NULL when unused
1124             playerdeadview = true;
1125 
1126         P_DeathThink (player);
1127 
1128         //added:26-02-98:camera may still move when guy is dead
1129         if (camera.chase == player)
1130             P_MoveChaseCamera ( player );
1131         return;
1132     }
1133     else
1134     {
1135         if ( player== displayplayer_ptr )
1136             playerdeadview = false;
1137     }
1138     if( player->chickenTics )
1139         P_ChickenPlayerThink(player);
1140 
1141     // check water content, set stuff in mobj
1142     P_MobjCheckWater (pmo);
1143 
1144     // Move around.
1145     // Reactiontime is used to prevent movement
1146     //  for a bit after a teleport.
1147     if (pmo->reactiontime)
1148         pmo->reactiontime--;
1149     else
1150         P_MovePlayer (player);
1151 
1152     //added:26-02-98: calculate the camera movement
1153     //added:22-02-98: bob view only if looking by the marine's eyes
1154     if (camera.chase == player)
1155         P_MoveChaseCamera ( player );  // camera view adjust
1156     else
1157 #ifdef CLIENTPREDICTION2
1158         ;
1159 #else
1160         P_CalcHeight (player);  // viewheight adjust, bob view
1161 #endif
1162 
1163 
1164     // check special sectors : damage & secrets
1165     P_PlayerInSpecialSector (player);
1166 
1167     //
1168     // TODO water splashes
1169     //
1170 #if 0
1171     if( (EV_legacy >= 125) && player->specialsector == )
1172     {
1173         if ((pmo->momx >  (2*FRACUNIT) ||
1174              pmo->momx < (-2*FRACUNIT) ||
1175              pmo->momy >  (2*FRACUNIT) ||
1176              pmo->momy < (-2*FRACUNIT) ||
1177              pmo->momz >  (2*FRACUNIT)) &&  // jump out of water
1178              !(gametic % (32 * NEWTICRATERATIO)) )
1179         {
1180             //
1181             // make sure we disturb the surface of water (we touch it)
1182             //
1183             int waterz = pmo->subsector->sector->floorheight + (FRACUNIT/4);
1184 
1185             // half in the water
1186             if(pmo->eflags & MF_TOUCHWATER)
1187             {
1188                 if (pmo->z <= pmo->floorz) // onground
1189                 {
1190                     fixed_t whater_height = waterz - pmo->subsector->sector->floorheight;
1191 
1192                     if( whater_height < (pmo->height>>2))
1193                         S_StartObjSound(pmo, sfx_splash);
1194                     else
1195                         S_StartObjSound(pmo, sfx_floush);
1196                 }
1197                 else
1198                     S_StartObjSound(pmo, sfx_floush);
1199             }
1200         }
1201     }
1202 #endif
1203 
1204     // Check for weapon change.
1205 //#ifndef CLIENTPREDICTION2
1206     if (cmd->buttons & BT_CHANGE)
1207 //#endif
1208     {
1209 
1210         // The actual changing of the weapon is done
1211         //  when the weapon psprite can do it
1212         //  (read: not in the middle of an attack).
1213         newweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT;
1214         if(demoversion<128)
1215         {
1216             // Old Doom, !EN_boom
1217             if (newweapon == wp_fist
1218                 && player->weaponowned[wp_chainsaw]
1219                 && !(player->readyweapon == wp_chainsaw
1220                      && player->powers[pw_strength]))
1221             {
1222                 newweapon = wp_chainsaw;
1223             }
1224 
1225             if ( (gamemode == doom2_commercial)
1226                 && newweapon == wp_shotgun
1227                 && player->weaponowned[wp_supershotgun]
1228                 && player->readyweapon != wp_supershotgun)
1229             {
1230                 newweapon = wp_supershotgun;
1231             }
1232         }
1233         else
1234         {
1235             if(cmd->buttons&BT_EXTRAWEAPON)
1236                switch(newweapon) {
1237                   case wp_shotgun :
1238                        if( (gamemode == doom2_commercial)
1239                             && player->weaponowned[wp_supershotgun])
1240                            newweapon = wp_supershotgun;
1241                        break;
1242                   case wp_fist :
1243                        if( player->weaponowned[wp_chainsaw])
1244                            newweapon = wp_chainsaw;
1245                        break;
1246                   default:
1247                        break;
1248                }
1249         }
1250 
1251         if (player->weaponowned[newweapon]
1252             && newweapon != player->readyweapon)
1253         {
1254             // Do not go to plasma or BFG in shareware,
1255             //  even if cheated.
1256             if ((newweapon != wp_plasma
1257                  && newweapon != wp_bfg)
1258                 || (gamemode != doom_shareware) )
1259             {
1260                 player->pendingweapon = newweapon;
1261             }
1262         }
1263     }
1264 
1265     // check for use
1266     if (cmd->buttons & BT_USE)
1267     {
1268         if (!player->usedown)
1269         {
1270             P_UseLines (player);
1271             player->usedown = true;
1272         }
1273     }
1274     else
1275         player->usedown = false;
1276     // Chicken counter
1277     if(player->chickenTics)
1278     {
1279         // Chicken attack counter
1280         if(player->chickenPeck)
1281             player->chickenPeck -= 3;
1282         // Attempt to undo the chicken
1283         if(!--player->chickenTics)
1284         {
1285             // Fixed to not change the player mobj.
1286             P_UndoPlayerChicken(player);
1287         }
1288     }
1289 
1290     // cycle psprites
1291     P_MovePsprites (player);
1292     // Counters, time dependend power ups.
1293 
1294     // Strength counts up to diminish fade.
1295     if (player->powers[pw_strength])
1296         player->powers[pw_strength]++;
1297 
1298     if (player->powers[pw_invulnerability])
1299         player->powers[pw_invulnerability]--;
1300 
1301     // the MF_SHADOW activates the tr_transhi translucency while it is set
1302     // (it doesnt use a preset value through FF_TRANSMASK)
1303     if (player->powers[pw_invisibility])
1304         if (! --player->powers[pw_invisibility] )
1305             pmo->flags &= ~MF_SHADOW;
1306 
1307     if (player->powers[pw_infrared])
1308         player->powers[pw_infrared]--;
1309 
1310     if (player->powers[pw_ironfeet])
1311         player->powers[pw_ironfeet]--;
1312 
1313     if (player->powers[pw_flight])
1314     {
1315         if(!--player->powers[pw_flight])
1316         {
1317 /* HERETODO
1318             if(pmo->z != pmo->floorz)
1319                 player->centering = true;
1320 */
1321             // timed out heretic fly power
1322             pmo->flags2 &= ~MF2_FLY;
1323             pmo->flags &= ~MF_NOGRAVITY;
1324            // BorderTopRefresh = true; //make sure the sprite's cleared out
1325         }
1326     }
1327     if(player->powers[pw_weaponlevel2])
1328     {
1329         if( --player->powers[pw_weaponlevel2] == 0 )
1330         {
1331             player->weaponinfo = wpnlev1info;
1332             // end of weaponlevel2 power
1333             if((player->readyweapon == wp_phoenixrod)
1334                 && (player->psprites[ps_weapon].state
1335                 != &states[S_PHOENIXREADY])
1336                 && (player->psprites[ps_weapon].state
1337                 != &states[S_PHOENIXUP]))
1338             {
1339                 P_SetPsprite(player, ps_weapon, S_PHOENIXREADY);
1340                 player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2;
1341                 player->refire = 0;
1342             }
1343             else if((player->readyweapon == wp_gauntlets)
1344                 || (player->readyweapon == wp_staff))
1345             {
1346                 player->pendingweapon = player->readyweapon;
1347             }
1348             //BorderTopRefresh = true;
1349         }
1350     }
1351 
1352     if (player->damagecount)
1353         player->damagecount--;
1354 
1355     if (player->bonuscount)
1356         player->bonuscount--;
1357 
1358     if (player->key_pickup)
1359         player->key_pickup--;
1360     if (player->health_pickup)
1361         player->health_pickup--;
1362     if (player->armor_pickup)
1363         player->armor_pickup--;
1364     if (player->ammo_pickup)
1365         player->ammo_pickup--;
1366     if (player->weapon_pickup)
1367         player->weapon_pickup--;
1368 
1369     // Handling colormaps.
1370     if (player->powers[pw_invulnerability])
1371     {
1372         if (player->powers[pw_invulnerability] > BLINKTHRESHOLD
1373             || (player->powers[pw_invulnerability]&8) )
1374             player->fixedcolormap = INVERSECOLORMAP;
1375         else
1376             player->fixedcolormap = 0;
1377     }
1378     else if (player->powers[pw_infrared])
1379     {
1380         if (player->powers[pw_infrared] > BLINKTHRESHOLD
1381             || (player->powers[pw_infrared]&8) )
1382         {
1383             // almost full bright
1384             player->fixedcolormap = 1;
1385         }
1386         else
1387             player->fixedcolormap = 0;
1388     }
1389     else
1390         player->fixedcolormap = 0;
1391 }
1392 
1393 //----------------------------------------------------------------------------
1394 //
1395 // PROC P_PlayerNextArtifact
1396 //
1397 //----------------------------------------------------------------------------
1398 
P_PlayerNextArtifact(player_t * player)1399 void P_PlayerNextArtifact(player_t *player)
1400 {
1401     player->inv_ptr--;
1402     if(player->inv_ptr < 6)
1403     {
1404         player->st_curpos--;
1405         if(player->st_curpos < 0)
1406             player->st_curpos = 0;
1407     }
1408     if(player->inv_ptr < 0)
1409     {
1410         player->inv_ptr = player->inventorySlotNum-1;
1411         if(player->inv_ptr < 6)
1412             player->st_curpos = player->inv_ptr;
1413         else
1414             player->st_curpos = 6;
1415     }
1416 }
1417 
1418 //----------------------------------------------------------------------------
1419 //
1420 // PROC P_PlayerRemoveArtifact
1421 //
1422 //----------------------------------------------------------------------------
1423 
P_PlayerRemoveArtifact(player_t * player,int slot)1424 static void P_PlayerRemoveArtifact(player_t *player, int slot)
1425 {
1426     int i;
1427 
1428     if(!(--player->inventory[slot].count))
1429     { // Used last of a type - compact the artifact list
1430         player->inventory[slot].type = arti_none;
1431         for(i = slot+1; i < player->inventorySlotNum; i++)
1432             player->inventory[i-1] = player->inventory[i];
1433         player->inventorySlotNum--;
1434 
1435         // Set position markers and get next readyArtifact
1436         player->inv_ptr--;
1437         if(player->inv_ptr < 6)
1438         {
1439             player->st_curpos--;
1440             if( player->st_curpos < 0 )
1441                 player->st_curpos = 0;
1442         }
1443         if( player->inv_ptr >= player->inventorySlotNum)
1444             player->inv_ptr = player->inventorySlotNum-1;
1445         if( player->inv_ptr < 0)
1446             player->inv_ptr = 0;
1447     }
1448 }
1449 
1450 //----------------------------------------------------------------------------
1451 //
1452 // PROC P_PlayerUseArtifact
1453 //
1454 //----------------------------------------------------------------------------
1455 
P_PlayerUseArtifact(player_t * player,artitype_t arti)1456 void P_PlayerUseArtifact(player_t *player, artitype_t arti)
1457 {
1458     int i;
1459 
1460     for(i = 0; i < player->inventorySlotNum; i++)
1461     {
1462         if(player->inventory[i].type == arti)
1463         { // Found match - try to use
1464             if(P_UseArtifact(player, arti))
1465             { // Artifact was used - remove it from inventory
1466                 P_PlayerRemoveArtifact(player, i);
1467                 if(player == consoleplayer_ptr
1468                    || player == displayplayer2_ptr ) // NULL when unused
1469                 {
1470                     S_StartSound(sfx_artiuse);
1471                     H_ArtifactFlash = 4;
1472                 }
1473             }
1474             else
1475             { // Unable to use artifact, advance pointer
1476                 P_PlayerNextArtifact(player);
1477             }
1478             break;
1479         }
1480     }
1481 }
1482 
1483 //----------------------------------------------------------------------------
1484 //
1485 // PROC P_ArtiTele
1486 //
1487 //----------------------------------------------------------------------------
1488 
P_ArtiTele(player_t * player)1489 void P_ArtiTele(player_t *player)
1490 {
1491     int i;
1492     fixed_t destX;
1493     fixed_t destY;
1494     angle_t destAngle;
1495     mapthing_t * mtp;
1496 
1497     if( deathmatch )
1498     {
1499         // Heretic use of P_Random
1500         i = PP_Random(ph_telearti) % numdmstarts;
1501         mtp = deathmatchstarts[i];
1502     }
1503     else
1504     {
1505         mtp = playerstarts[0];
1506     }
1507 
1508     destX = mtp->x<<FRACBITS;
1509     destY = mtp->y<<FRACBITS;
1510     destAngle = wad_to_angle(mtp->angle);
1511     P_Teleport(player->mo, destX, destY, destAngle);
1512     S_StartSound(sfx_wpnup); // Full volume laugh
1513 }
1514 
1515 
1516 //----------------------------------------------------------------------------
1517 //
1518 // FUNC P_UseArtifact
1519 //
1520 // Returns true if artifact was used.
1521 //
1522 //----------------------------------------------------------------------------
1523 
P_UseArtifact(player_t * player,artitype_t arti)1524 boolean P_UseArtifact(player_t *player, artitype_t arti)
1525 {
1526     mobj_t *mo;
1527     mobj_t * pmo = player->mo;
1528     angle_t angle;
1529 
1530     switch(arti)
1531     {
1532     case arti_invulnerability:
1533         if(!P_GivePower(player, pw_invulnerability))
1534             goto ret_fail;
1535         break;
1536     case arti_invisibility:
1537         if(!P_GivePower(player, pw_invisibility))
1538             goto ret_fail;
1539         break;
1540     case arti_health:
1541         if(!P_GiveHealth(player, 25))
1542             goto ret_fail;
1543         break;
1544     case arti_superhealth:
1545         if(!P_GiveHealth(player, 100))
1546             goto ret_fail;
1547         break;
1548     case arti_tomeofpower:
1549         if(player->chickenTics)
1550         { // Attempt to undo chicken
1551             // Fixed to not change the player mobj.
1552             if(P_UndoPlayerChicken(player) == false)
1553             { // Failed
1554                 P_DamageMobj(pmo, NULL, NULL, 10000);
1555             }
1556             else
1557             { // Succeeded
1558                 player->chickenTics = 0;
1559 #ifdef XPEREMNTAL_HW3S
1560                 S_StartScreamSound(pmo, sfx_wpnup);
1561 #else
1562                 S_StartObjSound(pmo, sfx_wpnup);
1563 #endif
1564             }
1565         }
1566         else
1567         {
1568             if(!P_GivePower(player, pw_weaponlevel2))
1569                 goto ret_fail;
1570             if(player->readyweapon == wp_staff)
1571             {
1572                 P_SetPsprite(player, ps_weapon, S_STAFFREADY2_1);
1573             }
1574             else if(player->readyweapon == wp_gauntlets)
1575             {
1576                 P_SetPsprite(player, ps_weapon, S_GAUNTLETREADY2_1);
1577             }
1578         }
1579         break;
1580     case arti_torch:
1581         if(!P_GivePower(player, pw_infrared))
1582             goto ret_fail;
1583         break;
1584     case arti_firebomb:
1585         angle = pmo->angle>>ANGLETOFINESHIFT;
1586         mo = P_SpawnMobj(pmo->x+24*finecosine[angle],
1587             pmo->y+24*finesine[angle], pmo->z - 15*FRACUNIT*
1588             ((pmo->flags2&MF2_FEETARECLIPPED) != 0), MT_FIREBOMB);
1589         mo->target = pmo;
1590         break;
1591     case arti_egg:
1592         P_SpawnPlayerMissile(pmo, MT_EGGFX);
1593         P_SPMAngle(pmo, MT_EGGFX, pmo->angle-(ANG45/6));
1594         P_SPMAngle(pmo, MT_EGGFX, pmo->angle+(ANG45/6));
1595         P_SPMAngle(pmo, MT_EGGFX, pmo->angle-(ANG45/3));
1596         P_SPMAngle(pmo, MT_EGGFX, pmo->angle+(ANG45/3));
1597         break;
1598     case arti_fly:
1599         if(!P_GivePower(player, pw_flight))
1600             goto ret_fail;
1601         break;
1602     case arti_teleport:
1603         P_ArtiTele(player);
1604         break;
1605     default:
1606         goto ret_fail;
1607     }
1608     return(true);
1609 
1610 ret_fail:
1611     return(false);
1612 }
1613 
1614 
P_SetPlayer_color(player_t * player,byte color)1615 void P_SetPlayer_color( player_t * player, byte color )
1616 {
1617     color = color % NUMSKINCOLORS;
1618     player->skincolor = color;
1619 
1620     // a copy of color, in proper position for use with color tables
1621     mobj_t *  pmo = player->mo;
1622     if( pmo )
1623         pmo->tflags = (pmo->tflags & ~MFT_TRANSLATION6) | (color << MFT_TRANSSHIFT);
1624 }
1625