inkoalawetrust

PlayerPawn ZScript code (GZDoom)

Jul 13th, 2020
209
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 62.73 KB | None | 0 0
  1. class PlayerPawn : Actor
  2. {
  3.     const CROUCHSPEED = (1./12);
  4.     // [RH] # of ticks to complete a turn180
  5.     const TURN180_TICKS = ((TICRATE / 4) + 1);
  6.     // 16 pixels of bob
  7.     const MAXBOB = 16.;
  8.    
  9.     int         crouchsprite;
  10.     int         MaxHealth;
  11.     int         BonusHealth;
  12.     int         MugShotMaxHealth;
  13.     int         RunHealth;
  14.     private int PlayerFlags;
  15.     clearscope Inventory    InvFirst;       // first inventory item displayed on inventory bar
  16.     clearscope Inventory    InvSel;         // selected inventory item
  17.     Name        SoundClass;     // Sound class
  18.     Name        Portrait;
  19.     Name        Slot[10];
  20.     double      HexenArmor[5];
  21.  
  22.     // [GRB] Player class properties
  23.     double      JumpZ;
  24.     double      GruntSpeed;
  25.     double      FallingScreamMinSpeed, FallingScreamMaxSpeed;
  26.     double      ViewHeight;
  27.     double      ForwardMove1, ForwardMove2;
  28.     double      SideMove1, SideMove2;
  29.     TextureID   ScoreIcon;
  30.     int         SpawnMask;
  31.     Name            MorphWeapon;
  32.     double      AttackZOffset;          // attack height, relative to player center
  33.     double      UseRange;               // [NS] Distance at which player can +use
  34.     double      AirCapacity;            // Multiplier for air supply underwater.
  35.     Class<Inventory> FlechetteType;
  36.     color       DamageFade;             // [CW] Fades for when you are being damaged.
  37.     double      ViewBob;                // [SP] ViewBob Multiplier
  38.     double      FullHeight;
  39.     double      curBob;
  40.  
  41.     meta Name HealingRadiusType;
  42.     meta Name InvulMode;
  43.     meta Name Face;
  44.     meta int TeleportFreezeTime;
  45.     meta int ColorRangeStart;   // Skin color range
  46.     meta int ColorRangeEnd;
  47.    
  48.     property prefix: Player;
  49.     property HealRadiusType: HealingradiusType;
  50.     property InvulnerabilityMode: InvulMode;
  51.     property AttackZOffset: AttackZOffset;
  52.     property JumpZ: JumpZ;
  53.     property GruntSpeed: GruntSpeed;
  54.     property FallingScreamSpeed: FallingScreamMinSpeed, FallingScreamMaxSpeed;
  55.     property ViewHeight: ViewHeight;
  56.     property UseRange: UseRange;
  57.     property AirCapacity: AirCapacity;
  58.     property MaxHealth: MaxHealth;
  59.     property MugshotMaxHealth: MugshotMaxHealth;
  60.     property RunHealth: RunHealth;
  61.     property MorphWeapon: MorphWeapon;
  62.     property FlechetteType: FlechetteType;
  63.     property Portrait: Portrait;
  64.     property TeleportFreezeTime: TeleportFreezeTime;
  65.     property ViewBob: ViewBob;
  66.    
  67.     flagdef NoThrustWhenInvul: PlayerFlags, 0;
  68.     flagdef CanSuperMorph: PlayerFlags, 1;
  69.     flagdef CrouchableMorph: PlayerFlags, 2;
  70.    
  71.     Default
  72.     {
  73.         Health 100;
  74.         Radius 16;
  75.         Height 56;
  76.         Mass 100;
  77.         Painchance 255;
  78.         Speed 1;
  79.         +SOLID
  80.         +SHOOTABLE
  81.         +DROPOFF
  82.         +PICKUP
  83.         +NOTDMATCH
  84.         +FRIENDLY
  85.         +SLIDESONWALLS
  86.         +CANPASS
  87.         +CANPUSHWALLS
  88.         +FLOORCLIP
  89.         +WINDTHRUST
  90.         +TELESTOMP
  91.         +NOBLOCKMONST
  92.         Player.AttackZOffset 8;
  93.         Player.JumpZ 8;
  94.         Player.GruntSpeed 12;
  95.         Player.FallingScreamSpeed 35,40;
  96.         Player.ViewHeight 41;
  97.         Player.UseRange 64;
  98.         Player.ForwardMove 1,1;
  99.         Player.SideMove 1,1;
  100.         Player.ColorRange 0,0;
  101.         Player.SoundClass "player";
  102.         Player.DamageScreenColor "ff 00 00";
  103.         Player.MugShotMaxHealth 0;
  104.         Player.FlechetteType "ArtiPoisonBag3";
  105.         Player.AirCapacity 1;
  106.         Player.ViewBob 1;
  107.         Player.TeleportFreezeTime 18;
  108.         Obituary "$OB_MPDEFAULT";
  109.     }
  110.    
  111.    
  112.     //===========================================================================
  113.     //
  114.     // PlayerPawn :: Tick
  115.     //
  116.     //===========================================================================
  117.  
  118.     override void Tick()
  119.     {
  120.         if (player != NULL && player.mo == self && CanCrouch() && player.playerstate != PST_DEAD)
  121.         {
  122.             Height = FullHeight * player.crouchfactor;
  123.         }
  124.         else
  125.         {
  126.             if (health > 0) Height = FullHeight;
  127.         }
  128.         Super.Tick();
  129.     }
  130.  
  131.     //===========================================================================
  132.     //
  133.     //
  134.     //
  135.     //===========================================================================
  136.  
  137.     override void BeginPlay()
  138.     {
  139.         Super.BeginPlay ();
  140.         ChangeStatNum (STAT_PLAYER);
  141.         FullHeight = Height;
  142.         if (!SetupCrouchSprite(crouchsprite)) crouchsprite = 0;
  143.     }
  144.    
  145.     //===========================================================================
  146.     //
  147.     // PlayerPawn :: PostBeginPlay
  148.     //
  149.     //===========================================================================
  150.  
  151.     override void PostBeginPlay()
  152.     {
  153.         Super.PostBeginPlay();
  154.         WeaponSlots.SetupWeaponSlots(self);
  155.  
  156.         // Voodoo dolls: restore original floorz/ceilingz logic
  157.         if (player == NULL || player.mo != self)
  158.         {
  159.             FindFloorCeiling(FFCF_ONLYSPAWNPOS|FFCF_NOPORTALS);
  160.             SetZ(floorz);
  161.             FindFloorCeiling(FFCF_ONLYSPAWNPOS);
  162.         }
  163.         else
  164.         {
  165.             player.SendPitchLimits();
  166.         }
  167.     }
  168.  
  169.    
  170.     //===========================================================================
  171.     //
  172.     // PlayerPawn :: MarkPrecacheSounds
  173.     //
  174.     //===========================================================================
  175.  
  176.     override void MarkPrecacheSounds()
  177.     {
  178.         Super.MarkPrecacheSounds();
  179.         MarkPlayerSounds();
  180.     }
  181.    
  182.     //----------------------------------------------------------------------------
  183.     //
  184.     //
  185.     //
  186.     //----------------------------------------------------------------------------
  187.  
  188.     virtual void PlayIdle ()
  189.     {
  190.         if (InStateSequence(CurState, SeeState))
  191.             SetState (SpawnState);
  192.     }
  193.  
  194.     virtual void PlayRunning ()
  195.     {
  196.         if (InStateSequence(CurState, SpawnState) && SeeState != NULL)
  197.             SetState (SeeState);
  198.     }
  199.    
  200.     virtual void PlayAttacking ()
  201.     {
  202.         if (MissileState != null) SetState (MissileState);
  203.     }
  204.  
  205.     virtual void PlayAttacking2 ()
  206.     {
  207.         if (MeleeState != null) SetState (MeleeState);
  208.     }
  209.    
  210.     virtual void MorphPlayerThink()
  211.     {
  212.     }
  213.    
  214.     //----------------------------------------------------------------------------
  215.     //
  216.     //
  217.     //
  218.     //----------------------------------------------------------------------------
  219.  
  220.     virtual void OnRespawn()
  221.     {
  222.         if (sv_respawnprotect && (deathmatch || alwaysapplydmflags))
  223.         {
  224.             let invul = Powerup(Spawn("PowerInvulnerable"));
  225.             invul.EffectTics = 3 * TICRATE;
  226.             invul.BlendColor = 0;           // don't mess with the view
  227.             invul.bUndroppable = true;      // Don't drop self
  228.             bRespawnInvul = true;           // [RH] special effect
  229.         }
  230.     }
  231.    
  232.     //----------------------------------------------------------------------------
  233.     //
  234.     //
  235.     //
  236.     //----------------------------------------------------------------------------
  237.  
  238.     override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
  239.     {
  240.         if (victim.player != player && victim.IsTeammate(self))
  241.         {
  242.             victim = self;
  243.             return String.Format("$OB_FRIENDLY%d", random[Obituary](1, 4));
  244.         }
  245.         else
  246.         {
  247.             if (mod == 'Telefrag') return "$OB_MPTELEFRAG";
  248.  
  249.             String message;
  250.             if (inflictor != NULL && inflictor != self)
  251.             {
  252.                 message = inflictor.GetObituary(victim, inflictor, mod, playerattack);
  253.             }
  254.             if (message.Length() == 0 && playerattack && player.ReadyWeapon != NULL)
  255.             {
  256.                 message = player.ReadyWeapon.GetObituary(victim, inflictor, mod, playerattack);
  257.             }
  258.             if (message.Length() == 0)
  259.             {
  260.                 if (mod == 'BFGSplash') return "$OB_MPBFG_SPLASH";
  261.                 if (mod == 'Railgun') return "$OB_RAILGUN";
  262.                 message = Obituary;
  263.             }
  264.             return message;
  265.         }
  266.     }
  267.    
  268.     //----------------------------------------------------------------------------
  269.     //
  270.     // This is for SBARINFO.
  271.     //
  272.     //----------------------------------------------------------------------------
  273.  
  274.     clearscope int, int GetEffectTicsForItem(class<Inventory> item) const
  275.     {
  276.         let pg = (class<PowerupGiver>)(item);
  277.         if (pg != null)
  278.         {
  279.             let powerupType = (class<Powerup>)(GetDefaultByType(pg).PowerupType);
  280.             let powerup = Powerup(FindInventory(powerupType));
  281.             if(powerup != null)
  282.             {
  283.                 let maxtics = GetDefaultByType(pg).EffectTics;
  284.                 if (maxtics == 0) maxtics = powerup.default.EffectTics;
  285.                 return powerup.EffectTics, maxtics;
  286.             }
  287.         }
  288.         return -1, -1;
  289.     }
  290.    
  291.  
  292.     //===========================================================================
  293.     //
  294.     // PlayerPawn :: CheckWeaponSwitch
  295.     //
  296.     // Checks if weapons should be changed after picking up ammo
  297.     //
  298.     //===========================================================================
  299.  
  300.     void CheckWeaponSwitch(Class<Ammo> ammotype)
  301.     {
  302.         let player = self.player;
  303.         if (!player.GetNeverSwitch() && player.PendingWeapon == WP_NOCHANGE &&
  304.             (player.ReadyWeapon == NULL || player.ReadyWeapon.bWimpy_Weapon))
  305.         {
  306.             let best = BestWeapon (ammotype);
  307.             if (best != NULL && (player.ReadyWeapon == NULL ||
  308.                 best.SelectionOrder < player.ReadyWeapon.SelectionOrder))
  309.             {
  310.                 player.PendingWeapon = best;
  311.             }
  312.         }
  313.     }
  314.  
  315.     //---------------------------------------------------------------------------
  316.     //
  317.     // PROC P_FireWeapon
  318.     //
  319.     //---------------------------------------------------------------------------
  320.  
  321.     virtual void FireWeapon (State stat)
  322.     {
  323.         let player = self.player;
  324.        
  325.         // [SO] 9/2/02: People were able to do an awful lot of damage
  326.         // when they were observers...
  327.         if (player.Bot == null && bot_observer)
  328.         {
  329.             return;
  330.         }
  331.  
  332.         let weapn = player.ReadyWeapon;
  333.         if (weapn == null || !weapn.CheckAmmo (Weapon.PrimaryFire, true))
  334.         {
  335.             return;
  336.         }
  337.  
  338.         player.WeaponState &= ~WF_WEAPONBOBBING;
  339.         PlayAttacking ();
  340.         weapn.bAltFire = false;
  341.         if (stat == null)
  342.         {
  343.             stat = weapn.GetAtkState(!!player.refire);
  344.         }
  345.         player.SetPsprite(PSP_WEAPON, stat);
  346.         if (!weapn.bNoAlert)
  347.         {
  348.             SoundAlert (self, false);
  349.         }
  350.     }
  351.  
  352.     //---------------------------------------------------------------------------
  353.     //
  354.     // PROC P_FireWeaponAlt
  355.     //
  356.     //---------------------------------------------------------------------------
  357.  
  358.     virtual void FireWeaponAlt (State stat)
  359.     {
  360.         // [SO] 9/2/02: People were able to do an awful lot of damage
  361.         // when they were observers...
  362.         if (player.Bot == null && bot_observer)
  363.         {
  364.             return;
  365.         }
  366.  
  367.         let weapn = player.ReadyWeapon;
  368.         if (weapn == null || weapn.FindState('AltFire') == null || !weapn.CheckAmmo (Weapon.AltFire, true))
  369.         {
  370.             return;
  371.         }
  372.  
  373.         player.WeaponState &= ~WF_WEAPONBOBBING;
  374.         PlayAttacking ();
  375.         weapn.bAltFire = true;
  376.  
  377.         if (stat == null)
  378.         {
  379.             stat = weapn.GetAltAtkState(!!player.refire);
  380.         }
  381.  
  382.         player.SetPsprite(PSP_WEAPON, stat);
  383.         if (!weapn.bNoAlert)
  384.         {
  385.             SoundAlert (self, false);
  386.         }
  387.     }
  388.  
  389.     //---------------------------------------------------------------------------
  390.     //
  391.     // PROC P_CheckWeaponFire
  392.     //
  393.     // The player can fire the weapon.
  394.     // [RH] This was in A_WeaponReady before, but that only works well when the
  395.     // weapon's ready frames have a one tic delay.
  396.     //
  397.     //---------------------------------------------------------------------------
  398.  
  399.     void CheckWeaponFire ()
  400.     {
  401.         let player = self.player;
  402.         let weapon = player.ReadyWeapon;
  403.  
  404.         if (weapon == NULL)
  405.             return;
  406.  
  407.         // Check for fire. Some weapons do not auto fire.
  408.         if ((player.WeaponState & WF_WEAPONREADY) && (player.cmd.buttons & BT_ATTACK))
  409.         {
  410.             if (!player.attackdown || !weapon.bNoAutofire)
  411.             {
  412.                 player.attackdown = true;
  413.                 FireWeapon (NULL);
  414.                 return;
  415.             }
  416.         }
  417.         else if ((player.WeaponState & WF_WEAPONREADYALT) && (player.cmd.buttons & BT_ALTATTACK))
  418.         {
  419.             if (!player.attackdown || !weapon.bNoAutofire)
  420.             {
  421.                 player.attackdown = true;
  422.                 FireWeaponAlt (NULL);
  423.                 return;
  424.             }
  425.         }
  426.         else
  427.         {
  428.             player.attackdown = false;
  429.         }
  430.     }
  431.  
  432.     //---------------------------------------------------------------------------
  433.     //
  434.     // PROC P_CheckWeaponChange
  435.     //
  436.     // The player can change to another weapon at self time.
  437.     // [GZ] This was cut from P_CheckWeaponFire.
  438.     //
  439.     //---------------------------------------------------------------------------
  440.  
  441.     virtual void CheckWeaponChange ()
  442.     {
  443.         let player = self.player;
  444.         if ((player.WeaponState & WF_DISABLESWITCH) || // Weapon changing has been disabled.
  445.             player.morphTics != 0)                  // Morphed classes cannot change weapons.
  446.         { // ...so throw away any pending weapon requests.
  447.             player.PendingWeapon = WP_NOCHANGE;
  448.         }
  449.  
  450.         // Put the weapon away if the player has a pending weapon or has died, and
  451.         // we're at a place in the state sequence where dropping the weapon is okay.
  452.         if ((player.PendingWeapon != WP_NOCHANGE || player.health <= 0) &&
  453.             player.WeaponState & WF_WEAPONSWITCHOK)
  454.         {
  455.             DropWeapon();
  456.         }
  457.     }
  458.    
  459.     //------------------------------------------------------------------------
  460.     //
  461.     // PROC P_MovePsprites
  462.     //
  463.     // Called every tic by player thinking routine
  464.     //
  465.     //------------------------------------------------------------------------
  466.  
  467.     virtual void TickPSprites()
  468.     {
  469.         let player = self.player;
  470.         let pspr = player.psprites;
  471.         while (pspr)
  472.         {
  473.             // Destroy the psprite if it's from a weapon that isn't currently selected by the player
  474.             // or if it's from an inventory item that the player no longer owns.
  475.             if ((pspr.Caller == null ||
  476.                 (pspr.Caller is "Inventory" && Inventory(pspr.Caller).Owner != pspr.Owner.mo) ||
  477.                 (pspr.Caller is "Weapon" && pspr.Caller != pspr.Owner.ReadyWeapon)))
  478.             {
  479.                 pspr.Destroy();
  480.             }
  481.             else
  482.             {
  483.                 pspr.Tick();
  484.             }
  485.  
  486.             pspr = pspr.Next;
  487.         }
  488.  
  489.         if ((health > 0) || (player.ReadyWeapon != null && !player.ReadyWeapon.bNoDeathInput))
  490.         {
  491.             if (player.ReadyWeapon == null)
  492.             {
  493.                 if (player.PendingWeapon != WP_NOCHANGE)
  494.                     player.mo.BringUpWeapon();
  495.             }
  496.             else
  497.             {
  498.                 CheckWeaponChange();
  499.                 if (player.WeaponState & (WF_WEAPONREADY | WF_WEAPONREADYALT))
  500.                 {
  501.                     CheckWeaponFire();
  502.                 }
  503.                 // Check custom buttons
  504.                 CheckWeaponButtons();
  505.             }
  506.         }
  507.     }
  508.    
  509.     /*
  510.     ==================
  511.     =
  512.     = P_CalcHeight
  513.     =
  514.     =
  515.     Calculate the walking / running height adjustment
  516.     =
  517.     ==================
  518.     */
  519.  
  520.     virtual void CalcHeight()
  521.     {
  522.         let player = self.player;
  523.         double angle;
  524.         double bob;
  525.         bool still = false;
  526.  
  527.         // Regular movement bobbing
  528.         // (needs to be calculated for gun swing even if not on ground)
  529.  
  530.         // killough 10/98: Make bobbing depend only on player-applied motion.
  531.         //
  532.         // Note: don't reduce bobbing here if on ice: if you reduce bobbing here,
  533.         // it causes bobbing jerkiness when the player moves from ice to non-ice,
  534.         // and vice-versa.
  535.  
  536.         if (player.cheats & CF_NOCLIP2)
  537.         {
  538.             player.bob = 0;
  539.         }
  540.         else if (bNoGravity && !player.onground)
  541.         {
  542.             player.bob = 0.5;
  543.         }
  544.         else
  545.         {
  546.             player.bob = player.Vel dot player.Vel;
  547.             if (player.bob == 0)
  548.             {
  549.                 still = true;
  550.             }
  551.             else
  552.             {
  553.                 player.bob *= player.GetMoveBob();
  554.  
  555.                 if (player.bob > MAXBOB)
  556.                     player.bob = MAXBOB;
  557.             }
  558.         }
  559.  
  560.         double defaultviewheight = ViewHeight + player.crouchviewdelta;
  561.  
  562.         if (player.cheats & CF_NOVELOCITY)
  563.         {
  564.             player.viewz = pos.Z + defaultviewheight;
  565.  
  566.             if (player.viewz > ceilingz-4)
  567.                 player.viewz = ceilingz-4;
  568.  
  569.             return;
  570.         }
  571.  
  572.         if (still)
  573.         {
  574.             if (player.health > 0)
  575.             {
  576.                 angle = Level.maptime / (120 * TICRATE / 35.) * 360.;
  577.                 bob = player.GetStillBob() * sin(angle);
  578.             }
  579.             else
  580.             {
  581.                 bob = 0;
  582.             }
  583.         }
  584.         else
  585.         {
  586.             angle = Level.maptime / (20 * TICRATE / 35.) * 360.;
  587.             bob = player.bob * sin(angle) * (waterlevel > 1 ? 0.25f : 0.5f);
  588.         }
  589.  
  590.         // move viewheight
  591.         if (player.playerstate == PST_LIVE)
  592.         {
  593.             player.viewheight += player.deltaviewheight;
  594.  
  595.             if (player.viewheight > defaultviewheight)
  596.             {
  597.                 player.viewheight = defaultviewheight;
  598.                 player.deltaviewheight = 0;
  599.             }
  600.             else if (player.viewheight < (defaultviewheight/2))
  601.             {
  602.                 player.viewheight = defaultviewheight/2;
  603.                 if (player.deltaviewheight <= 0)
  604.                     player.deltaviewheight = 1 / 65536.;
  605.             }
  606.            
  607.             if (player.deltaviewheight)
  608.             {
  609.                 player.deltaviewheight += 0.25;
  610.                 if (!player.deltaviewheight)
  611.                     player.deltaviewheight = 1/65536.;
  612.             }
  613.         }
  614.  
  615.         if (player.morphTics)
  616.         {
  617.             bob = 0;
  618.         }
  619.         player.viewz = pos.Z + player.viewheight + (bob * clamp(ViewBob, 0. , 1.5)); // [SP] Allow DECORATE changes to view bobbing speed.
  620.         if (Floorclip && player.playerstate != PST_DEAD
  621.             && pos.Z <= floorz)
  622.         {
  623.             player.viewz -= Floorclip;
  624.         }
  625.         if (player.viewz > ceilingz - 4)
  626.         {
  627.             player.viewz = ceilingz - 4;
  628.         }
  629.         if (player.viewz < floorz + 4)
  630.         {
  631.             player.viewz = floorz + 4;
  632.         }
  633.     }
  634.  
  635.  
  636.     //==========================================================================
  637.     //
  638.     // P_DeathThink
  639.     //
  640.     //==========================================================================
  641.  
  642.     virtual void DeathThink ()
  643.     {
  644.         let player = self.player;
  645.         int dir;
  646.         double delta;
  647.  
  648.         player.Uncrouch();
  649.         TickPSprites();
  650.  
  651.         player.onground = (pos.Z <= floorz);
  652.         if (self is "PlayerChunk")
  653.         { // Flying bloody skull or flying ice chunk
  654.             player.viewheight = 6;
  655.             player.deltaviewheight = 0;
  656.             if (player.onground)
  657.             {
  658.                 if (Pitch > -19.)
  659.                 {
  660.                     double lookDelta = (-19. - Pitch) / 8;
  661.                     Pitch += lookDelta;
  662.                 }
  663.             }
  664.         }
  665.         else if (!bIceCorpse)
  666.         { // Fall to ground (if not frozen)
  667.             player.deltaviewheight = 0;
  668.             if (player.viewheight > 6)
  669.             {
  670.                 player.viewheight -= 1;
  671.             }
  672.             if (player.viewheight < 6)
  673.             {
  674.                 player.viewheight = 6;
  675.             }
  676.             if (Pitch < 0)
  677.             {
  678.                 Pitch += 3;
  679.             }
  680.             else if (Pitch > 0)
  681.             {
  682.                 Pitch -= 3;
  683.             }
  684.             if (abs(Pitch) < 3)
  685.             {
  686.                 Pitch = 0.;
  687.             }
  688.         }
  689.         player.mo.CalcHeight ();
  690.            
  691.         if (player.attacker && player.attacker != self)
  692.         { // Watch killer
  693.             double diff = deltaangle(angle, AngleTo(player.attacker));
  694.             double delta = abs(diff);
  695.    
  696.             if (delta < 10)
  697.             { // Looking at killer, so fade damage and poison counters
  698.                 if (player.damagecount)
  699.                 {
  700.                     player.damagecount--;
  701.                 }
  702.                 if (player.poisoncount)
  703.                 {
  704.                     player.poisoncount--;
  705.                 }
  706.             }
  707.             delta /= 8;
  708.             Angle += clamp(diff, -5., 5.);
  709.         }
  710.         else
  711.         {
  712.             if (player.damagecount)
  713.             {
  714.                 player.damagecount--;
  715.             }
  716.             if (player.poisoncount)
  717.             {
  718.                 player.poisoncount--;
  719.             }
  720.         }      
  721.  
  722.         if ((player.cmd.buttons & BT_USE ||
  723.             ((deathmatch || alwaysapplydmflags) && sv_forcerespawn)) && !sv_norespawn)
  724.         {
  725.             if (Level.maptime >= player.respawn_time || ((player.cmd.buttons & BT_USE) && player.Bot == NULL))
  726.             {
  727.                 player.cls = NULL;      // Force a new class if the player is using a random class
  728.                 player.playerstate = (multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn)) ? PST_REBORN : PST_ENTER;
  729.                 if (special1 > 2)
  730.                 {
  731.                     special1 = 0;
  732.                 }
  733.             }
  734.         }
  735.     }
  736.  
  737.     //===========================================================================
  738.     //
  739.     // PlayerPawn :: Die
  740.     //
  741.     //===========================================================================
  742.  
  743.     override void Die (Actor source, Actor inflictor, int dmgflags, Name MeansOfDeath)
  744.     {
  745.         Super.Die (source, inflictor, dmgflags, MeansOfDeath);
  746.  
  747.         if (player != NULL && player.mo == self) player.bonuscount = 0;
  748.  
  749.         if (player != NULL && player.mo != self)
  750.         { // Make the real player die, too
  751.             player.mo.Die (source, inflictor, dmgflags, MeansOfDeath);
  752.         }
  753.         else
  754.         {
  755.             if (player != NULL && sv_weapondrop)
  756.             { // Voodoo dolls don't drop weapons
  757.                 let weap = player.ReadyWeapon;
  758.                 if (weap != NULL)
  759.                 {
  760.                     // kgDROP - start - modified copy from a_action.cpp
  761.                     let di = weap.GetDropItems();
  762.  
  763.                     if (di != NULL)
  764.                     {
  765.                         while (di != NULL)
  766.                         {
  767.                             if (di.Name != 'None')
  768.                             {
  769.                                 class<Actor> ti = di.Name;
  770.                                 if (ti) A_DropItem (ti, di.Amount, di.Probability);
  771.                             }
  772.                             di = di.Next;
  773.                         }
  774.                     }
  775.                     else if (weap.SpawnState != NULL &&
  776.                         weap.SpawnState != GetDefaultByType('Actor').SpawnState)
  777.                     {
  778.                         let weapitem = Weapon(A_DropItem (weap.GetClass(), -1, 256));
  779.                         if (weapitem)
  780.                         {
  781.                             if (weap.AmmoGive1 && weap.Ammo1)
  782.                             {
  783.                                 weapitem.AmmoGive1 = weap.Ammo1.Amount;
  784.                             }
  785.                             if (weap.AmmoGive2 && weap.Ammo2)
  786.                             {
  787.                                 weapitem.AmmoGive2 = weap.Ammo2.Amount;
  788.                             }
  789.                             weapitem.bIgnoreSkill = true;
  790.                         }
  791.                     }
  792.                     else
  793.                     {
  794.                         let item = Inventory(A_DropItem (weap.AmmoType1, -1, 256));
  795.                         if (item != NULL)
  796.                         {
  797.                             item.Amount = weap.Ammo1.Amount;
  798.                             item.bIgnoreSkill = true;
  799.                         }
  800.                         item = Inventory(A_DropItem (weap.AmmoType2, -1, 256));
  801.                         if (item != NULL)
  802.                         {
  803.                             item.Amount = weap.Ammo2.Amount;
  804.                             item.bIgnoreSkill = true;
  805.                         }
  806.                     }
  807.                 }
  808.             }
  809.             if (!multiplayer && level.deathsequence != 'None')
  810.             {
  811.                 level.StartIntermission(level.deathsequence, FSTATE_EndingGame);
  812.             }
  813.         }
  814.     }
  815.    
  816.     //===========================================================================
  817.     //
  818.     // PlayerPawn :: FilterCoopRespawnInventory
  819.     //
  820.     // When respawning in coop, this function is called to walk through the dead
  821.     // player's inventory and modify it according to the current game flags so
  822.     // that it can be transferred to the new live player. This player currently
  823.     // has the default inventory, and the oldplayer has the inventory at the time
  824.     // of death.
  825.     //
  826.     //===========================================================================
  827.  
  828.     void FilterCoopRespawnInventory (PlayerPawn oldplayer)
  829.     {
  830.         // If we're losing everything, this is really simple.
  831.         if (sv_cooploseinventory)
  832.         {
  833.             oldplayer.DestroyAllInventory();
  834.             return;
  835.         }
  836.  
  837.         // Walk through the old player's inventory and destroy or modify
  838.         // according to dmflags.
  839.         Inventory next;
  840.         for (Inventory item = oldplayer.Inv; item != NULL; item = next)
  841.         {
  842.             next = item.Inv;
  843.  
  844.             // If this item is part of the default inventory, we never want
  845.             // to destroy it, although we might want to copy the default
  846.             // inventory amount.
  847.             let defitem = FindInventory (item.GetClass());
  848.  
  849.             if (sv_cooplosekeys && defitem == NULL && item is 'Key')
  850.             {
  851.                 item.Destroy();
  852.             }
  853.             else if (sv_cooploseweapons && defitem == NULL && item is 'Weapon')
  854.             {
  855.                 item.Destroy();
  856.             }
  857.             else if (sv_cooplosearmor && item is 'Armor')
  858.             {
  859.                 if (defitem == NULL)
  860.                 {
  861.                     item.Destroy();
  862.                 }
  863.                 else if (item is 'BasicArmor')
  864.                 {
  865.                     BasicArmor(item).SavePercent = BasicArmor(defitem).SavePercent;
  866.                     item.Amount = defitem.Amount;
  867.                 }
  868.                 else if (item is 'HexenArmor')
  869.                 {
  870.                     let to = HexenArmor(item);
  871.                     let from = HexenArmor(defitem);
  872.                     to.Slots[0] = from.Slots[0];
  873.                     to.Slots[1] = from.Slots[1];
  874.                     to.Slots[2] = from.Slots[2];
  875.                     to.Slots[3] = from.Slots[3];
  876.                 }
  877.             }
  878.             else if (sv_cooplosepowerups && defitem == NULL && item is  'Powerup')
  879.             {
  880.                 item.Destroy();
  881.             }
  882.             else if ((sv_cooploseammo || sv_coophalveammo) && item is 'Ammo')
  883.             {
  884.                 if (defitem == NULL)
  885.                 {
  886.                     if (sv_cooploseammo)
  887.                     {
  888.                         // Do NOT destroy the ammo, because a weapon might reference it.
  889.                         item.Amount = 0;
  890.                     }
  891.                     else if (item.Amount > 1)
  892.                     {
  893.                         item.Amount /= 2;
  894.                     }
  895.                 }
  896.                 else
  897.                 {
  898.                     // When set to lose ammo, you get to keep all your starting ammo.
  899.                     // When set to halve ammo, you won't be left with less than your starting amount.
  900.                     if (sv_cooploseammo)
  901.                     {
  902.                         item.Amount = defitem.Amount;
  903.                     }
  904.                     else if (item.Amount > 1)
  905.                     {
  906.                         item.Amount = MAX(item.Amount / 2, defitem.Amount);
  907.                     }
  908.                 }
  909.             }
  910.         }
  911.  
  912.         // Now destroy the default inventory this player is holding and move
  913.         // over the old player's remaining inventory.
  914.         DestroyAllInventory();
  915.         ObtainInventory (oldplayer);
  916.  
  917.         player.ReadyWeapon = NULL;
  918.         PickNewWeapon (NULL);
  919.     }
  920.  
  921.    
  922.     //----------------------------------------------------------------------------
  923.     //
  924.     // PROC P_CheckFOV
  925.     //
  926.     //----------------------------------------------------------------------------
  927.  
  928.     virtual void CheckFOV()
  929.     {
  930.         let player = self.player;
  931.  
  932.         // [RH] Zoom the player's FOV
  933.         float desired = player.DesiredFOV;
  934.         // Adjust FOV using on the currently held weapon.
  935.         if (player.playerstate != PST_DEAD &&       // No adjustment while dead.
  936.             player.ReadyWeapon != NULL &&           // No adjustment if no weapon.
  937.             player.ReadyWeapon.FOVScale != 0)       // No adjustment if the adjustment is zero.
  938.         {
  939.             // A negative scale is used to prevent G_AddViewAngle/G_AddViewPitch
  940.             // from scaling with the FOV scale.
  941.             desired *= abs(player.ReadyWeapon.FOVScale);
  942.         }
  943.         if (player.FOV != desired)
  944.         {
  945.             if (abs(player.FOV - desired) < 7.)
  946.             {
  947.                 player.FOV = desired;
  948.             }
  949.             else
  950.             {
  951.                 float zoom = MAX(7., abs(player.FOV - desired) * 0.025);
  952.                 if (player.FOV > desired)
  953.                 {
  954.                     player.FOV = player.FOV - zoom;
  955.                 }
  956.                 else
  957.                 {
  958.                     player.FOV = player.FOV + zoom;
  959.                 }
  960.             }
  961.         }
  962.     }
  963.  
  964.     //----------------------------------------------------------------------------
  965.     //
  966.     // PROC P_CheckCheats
  967.     //
  968.     //----------------------------------------------------------------------------
  969.  
  970.     virtual void CheckCheats()
  971.     {
  972.         let player = self.player;
  973.         // No-clip cheat
  974.         if ((player.cheats & (CF_NOCLIP | CF_NOCLIP2)) == CF_NOCLIP2)
  975.         { // No noclip2 without noclip
  976.             player.cheats &= ~CF_NOCLIP2;
  977.         }
  978.         bNoClip = (player.cheats & (CF_NOCLIP | CF_NOCLIP2) || Default.bNoClip);
  979.         if (player.cheats & CF_NOCLIP2)
  980.         {
  981.             bNoGravity = true;
  982.         }
  983.         else if (!bFly && !Default.bNoGravity)
  984.         {
  985.             bNoGravity = false;
  986.         }
  987.     }
  988.  
  989.     //----------------------------------------------------------------------------
  990.     //
  991.     // PROC P_CheckFrozen
  992.     //
  993.     //----------------------------------------------------------------------------
  994.  
  995.     virtual bool CheckFrozen()
  996.     {
  997.         let player = self.player;
  998.         UserCmd cmd = player.cmd;
  999.         bool totallyfrozen = player.IsTotallyFrozen();
  1000.  
  1001.         // [RH] Being totally frozen zeros out most input parameters.
  1002.         if (totallyfrozen)
  1003.         {
  1004.             if (gamestate == GS_TITLELEVEL)
  1005.             {
  1006.                 cmd.buttons = 0;
  1007.             }
  1008.             else
  1009.             {
  1010.                 cmd.buttons &= BT_USE;
  1011.             }
  1012.             cmd.pitch = 0;
  1013.             cmd.yaw = 0;
  1014.             cmd.roll = 0;
  1015.             cmd.forwardmove = 0;
  1016.             cmd.sidemove = 0;
  1017.             cmd.upmove = 0;
  1018.             player.turnticks = 0;
  1019.         }
  1020.         else if (player.cheats & CF_FROZEN)
  1021.         {
  1022.             cmd.forwardmove = 0;
  1023.             cmd.sidemove = 0;
  1024.             cmd.upmove = 0;
  1025.         }
  1026.         return totallyfrozen;
  1027.     }
  1028.  
  1029.     virtual bool CanCrouch() const
  1030.     {
  1031.         return player.morphTics == 0 || bCrouchableMorph;
  1032.     }
  1033.  
  1034.     //----------------------------------------------------------------------------
  1035.     //
  1036.     // PROC P_CrouchMove
  1037.     //
  1038.     //----------------------------------------------------------------------------
  1039.  
  1040.     virtual void CrouchMove(int direction)
  1041.     {
  1042.         let player = self.player;
  1043.        
  1044.         double defaultheight = FullHeight;
  1045.         double savedheight = Height;
  1046.         double crouchspeed = direction * CROUCHSPEED;
  1047.         double oldheight = player.viewheight;
  1048.  
  1049.         player.crouchdir = direction;
  1050.         player.crouchfactor += crouchspeed;
  1051.  
  1052.         // check whether the move is ok
  1053.         Height  = defaultheight * player.crouchfactor;
  1054.         if (!TryMove(Pos.XY, false, NULL))
  1055.         {
  1056.             Height = savedheight;
  1057.             if (direction > 0)
  1058.             {
  1059.                 // doesn't fit
  1060.                 player.crouchfactor -= crouchspeed;
  1061.                 return;
  1062.             }
  1063.         }
  1064.         Height = savedheight;
  1065.  
  1066.         player.crouchfactor = clamp(player.crouchfactor, 0.5, 1.);
  1067.         player.viewheight = ViewHeight * player.crouchfactor;
  1068.         player.crouchviewdelta = player.viewheight - ViewHeight;
  1069.  
  1070.         // Check for eyes going above/below fake floor due to crouching motion.
  1071.         CheckFakeFloorTriggers(pos.Z + oldheight, true);
  1072.     }
  1073.  
  1074.     //----------------------------------------------------------------------------
  1075.     //
  1076.     // PROC P_CheckCrouch
  1077.     //
  1078.     //----------------------------------------------------------------------------
  1079.  
  1080.     virtual void CheckCrouch(bool totallyfrozen)
  1081.     {
  1082.         let player = self.player;
  1083.         UserCmd cmd = player.cmd;
  1084.  
  1085.         if (cmd.buttons & BT_JUMP)
  1086.         {
  1087.             cmd.buttons &= ~BT_CROUCH;
  1088.         }
  1089.         if (CanCrouch() && player.health > 0 && level.IsCrouchingAllowed())
  1090.         {
  1091.             if (!totallyfrozen)
  1092.             {
  1093.                 int crouchdir = player.crouching;
  1094.  
  1095.                 if (crouchdir == 0)
  1096.                 {
  1097.                     crouchdir = (cmd.buttons & BT_CROUCH) ? -1 : 1;
  1098.                 }
  1099.                 else if (cmd.buttons & BT_CROUCH)
  1100.                 {
  1101.                     player.crouching = 0;
  1102.                 }
  1103.                 if (crouchdir == 1 && player.crouchfactor < 1 && pos.Z + height < ceilingz)
  1104.                 {
  1105.                     CrouchMove(1);
  1106.                 }
  1107.                 else if (crouchdir == -1 && player.crouchfactor > 0.5)
  1108.                 {
  1109.                     CrouchMove(-1);
  1110.                 }
  1111.             }
  1112.         }
  1113.         else
  1114.         {
  1115.             player.Uncrouch();
  1116.         }
  1117.  
  1118.         player.crouchoffset = -(ViewHeight) * (1 - player.crouchfactor);
  1119.     }
  1120.  
  1121.     //----------------------------------------------------------------------------
  1122.     //
  1123.     // P_Thrust
  1124.     //
  1125.     // moves the given origin along a given angle
  1126.     //
  1127.     //----------------------------------------------------------------------------
  1128.  
  1129.     void ForwardThrust (double move, double angle)
  1130.     {
  1131.         if ((waterlevel || bNoGravity) && Pitch != 0 && !player.GetClassicFlight())
  1132.         {
  1133.             double zpush = move * sin(Pitch);
  1134.             if (waterlevel && waterlevel < 2 && zpush < 0) zpush = 0;
  1135.             Vel.Z -= zpush;
  1136.             move *= cos(Pitch);
  1137.         }
  1138.         Thrust(move, angle);
  1139.     }
  1140.  
  1141.     //----------------------------------------------------------------------------
  1142.     //
  1143.     // P_Bob
  1144.     // Same as P_Thrust, but only affects bobbing.
  1145.     //
  1146.     // killough 10/98: We apply thrust separately between the real physical player
  1147.     // and the part which affects bobbing. This way, bobbing only comes from player
  1148.     // motion, nothing external, avoiding many problems, e.g. bobbing should not
  1149.     // occur on conveyors, unless the player walks on one, and bobbing should be
  1150.     // reduced at a regular rate, even on ice (where the player coasts).
  1151.     //
  1152.     //----------------------------------------------------------------------------
  1153.  
  1154.     void Bob (double angle, double move, bool forward)
  1155.     {
  1156.         if (forward && (waterlevel || bNoGravity) && Pitch != 0)
  1157.         {
  1158.             move *= cos(Pitch);
  1159.         }
  1160.         player.Vel += AngleToVector(angle, move);
  1161.     }
  1162.    
  1163.     //===========================================================================
  1164.     //
  1165.     // PlayerPawn :: TweakSpeeds
  1166.     //
  1167.     //===========================================================================
  1168.  
  1169.     virtual double, double TweakSpeeds (double forward, double side)
  1170.     {
  1171.         // Strife's player can't run when its health is below 10
  1172.         if (health <= RunHealth)
  1173.         {
  1174.             forward = clamp(forward, -gameinfo.normforwardmove[0]*256, gameinfo.normforwardmove[0]*256);
  1175.             side = clamp(side, -gameinfo.normsidemove[0]*256, gameinfo.normsidemove[0]*256);
  1176.         }
  1177.  
  1178.         // [GRB]
  1179.         if (abs(forward) < 0x3200)
  1180.         {
  1181.             forward *= ForwardMove1;
  1182.         }
  1183.         else
  1184.         {
  1185.             forward *= ForwardMove2;
  1186.         }
  1187.  
  1188.         if (abs(side) < 0x2800)
  1189.         {
  1190.             side *= SideMove1;
  1191.         }
  1192.         else
  1193.         {
  1194.             side *= SideMove2;
  1195.         }
  1196.  
  1197.         if (!player.morphTics)
  1198.         {
  1199.             double factor = 1.;
  1200.             for(let it = Inv; it != null; it = it.Inv)
  1201.             {
  1202.                 factor *= it.GetSpeedFactor ();
  1203.             }
  1204.             forward *= factor;
  1205.             side *= factor;
  1206.         }
  1207.         return forward, side;
  1208.     }
  1209.  
  1210.     //----------------------------------------------------------------------------
  1211.     //
  1212.     // PROC P_MovePlayer
  1213.     //
  1214.     //----------------------------------------------------------------------------
  1215.  
  1216.     virtual void MovePlayer ()
  1217.     {
  1218.         let player = self.player;
  1219.         UserCmd cmd = player.cmd;
  1220.  
  1221.         // [RH] 180-degree turn overrides all other yaws
  1222.         if (player.turnticks)
  1223.         {
  1224.             player.turnticks--;
  1225.             Angle += (180. / TURN180_TICKS);
  1226.         }
  1227.         else
  1228.         {
  1229.             Angle += cmd.yaw * (360./65536.);
  1230.         }
  1231.  
  1232.         player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
  1233.  
  1234.         // killough 10/98:
  1235.         //
  1236.         // We must apply thrust to the player and bobbing separately, to avoid
  1237.         // anomalies. The thrust applied to bobbing is always the same strength on
  1238.         // ice, because the player still "works just as hard" to move, while the
  1239.         // thrust applied to the movement varies with 'movefactor'.
  1240.  
  1241.         if (cmd.forwardmove | cmd.sidemove)
  1242.         {
  1243.             double forwardmove, sidemove;
  1244.             double bobfactor;
  1245.             double friction, movefactor;
  1246.             double fm, sm;
  1247.  
  1248.             [friction, movefactor] = GetFriction();
  1249.             bobfactor = friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR;
  1250.             if (!player.onground && !bNoGravity && !waterlevel)
  1251.             {
  1252.                 // [RH] allow very limited movement if not on ground.
  1253.                 movefactor *= level.aircontrol;
  1254.                 bobfactor*= level.aircontrol;
  1255.             }
  1256.  
  1257.             fm = cmd.forwardmove;
  1258.             sm = cmd.sidemove;
  1259.             [fm, sm] = TweakSpeeds (fm, sm);
  1260.             fm *= Speed / 256;
  1261.             sm *= Speed / 256;
  1262.  
  1263.             // When crouching, speed and bobbing have to be reduced
  1264.             if (CanCrouch() && player.crouchfactor != 1)
  1265.             {
  1266.                 fm *= player.crouchfactor;
  1267.                 sm *= player.crouchfactor;
  1268.                 bobfactor *= player.crouchfactor;
  1269.             }
  1270.  
  1271.             forwardmove = fm * movefactor * (35 / TICRATE);
  1272.             sidemove = sm * movefactor * (35 / TICRATE);
  1273.  
  1274.             if (forwardmove)
  1275.             {
  1276.                 Bob(Angle, cmd.forwardmove * bobfactor / 256., true);
  1277.                 ForwardThrust(forwardmove, Angle);
  1278.             }
  1279.             if (sidemove)
  1280.             {
  1281.                 let a = Angle - 90;
  1282.                 Bob(a, cmd.sidemove * bobfactor / 256., false);
  1283.                 Thrust(sidemove, a);
  1284.             }
  1285.  
  1286.             if (!(player.cheats & CF_PREDICTING) && (forwardmove != 0 || sidemove != 0))
  1287.             {
  1288.                 PlayRunning ();
  1289.             }
  1290.  
  1291.             if (player.cheats & CF_REVERTPLEASE)
  1292.             {
  1293.                 player.cheats &= ~CF_REVERTPLEASE;
  1294.                 player.camera = player.mo;
  1295.             }
  1296.         }
  1297.     }      
  1298.  
  1299.     //----------------------------------------------------------------------------
  1300.     //
  1301.     // PROC P_CheckPitch
  1302.     //
  1303.     //----------------------------------------------------------------------------
  1304.  
  1305.     virtual void CheckPitch()
  1306.     {
  1307.         let player = self.player;
  1308.         // [RH] Look up/down stuff
  1309.         if (!level.IsFreelookAllowed())
  1310.         {
  1311.             Pitch = 0.;
  1312.         }
  1313.         else
  1314.         {
  1315.             // The player's view pitch is clamped between -32 and +56 degrees,
  1316.             // which translates to about half a screen height up and (more than)
  1317.             // one full screen height down from straight ahead when view panning
  1318.             // is used.
  1319.             int clook = player.cmd.pitch;
  1320.             if (clook != 0)
  1321.             {
  1322.                 if (clook == -32768)
  1323.                 { // center view
  1324.                     player.centering = true;
  1325.                 }
  1326.                 else if (!player.centering)
  1327.                 {
  1328.                     // no more overflows with floating point. Yay! :)
  1329.                     Pitch = clamp(Pitch - clook * (360. / 65536.), player.MinPitch, player.MaxPitch);
  1330.                 }
  1331.             }
  1332.         }
  1333.         if (player.centering)
  1334.         {
  1335.             if (abs(Pitch) > 2.)
  1336.             {
  1337.                 Pitch *= (2. / 3.);
  1338.             }
  1339.             else
  1340.             {
  1341.                 Pitch = 0.;
  1342.                 player.centering = false;
  1343.                 if (PlayerNumber() == consoleplayer)
  1344.                 {
  1345.                     LocalViewPitch = 0;
  1346.                 }
  1347.             }
  1348.         }
  1349.     }
  1350.  
  1351.     //----------------------------------------------------------------------------
  1352.     //
  1353.     // PROC P_CheckJump
  1354.     //
  1355.     //----------------------------------------------------------------------------
  1356.  
  1357.     virtual void CheckJump()
  1358.     {
  1359.         let player = self.player;
  1360.         // [RH] check for jump
  1361.         if (player.cmd.buttons & BT_JUMP)
  1362.         {
  1363.             if (player.crouchoffset != 0)
  1364.             {
  1365.                 // Jumping while crouching will force an un-crouch but not jump
  1366.                 player.crouching = 1;
  1367.             }
  1368.             else if (waterlevel >= 2)
  1369.             {
  1370.                 Vel.Z = 4 * Speed;
  1371.             }
  1372.             else if (bNoGravity)
  1373.             {
  1374.                 Vel.Z = 3.;
  1375.             }
  1376.             else if (level.IsJumpingAllowed() && player.onground && player.jumpTics == 0)
  1377.             {
  1378.                 double jumpvelz = JumpZ * 35 / TICRATE;
  1379.                 double jumpfac = 0;
  1380.  
  1381.                 // [BC] If the player has the high jump power, double his jump velocity.
  1382.                 // (actually, pick the best factors from all active items.)
  1383.                 for (let p = Inv; p != null; p = p.Inv)
  1384.                 {
  1385.                     let pp = PowerHighJump(p);
  1386.                     if (pp)
  1387.                     {
  1388.                         double f = pp.Strength;
  1389.                         if (f > jumpfac) jumpfac = f;
  1390.                     }
  1391.                 }
  1392.                 if (jumpfac > 0) jumpvelz *= jumpfac;
  1393.  
  1394.                 Vel.Z += jumpvelz;
  1395.                 bOnMobj = false;
  1396.                 player.jumpTics = -1;
  1397.                 if (!(player.cheats & CF_PREDICTING)) A_StartSound("*jump", CHAN_BODY);
  1398.             }
  1399.         }
  1400.     }
  1401.  
  1402.     //----------------------------------------------------------------------------
  1403.     //
  1404.     // PROC P_CheckMoveUpDown
  1405.     //
  1406.     //----------------------------------------------------------------------------
  1407.  
  1408.     virtual void CheckMoveUpDown()
  1409.     {
  1410.         let player = self.player;
  1411.         UserCmd cmd = player.cmd;
  1412.  
  1413.         if (cmd.upmove == -32768)
  1414.         { // Only land if in the air
  1415.             if (bNoGravity && waterlevel < 2)
  1416.             {
  1417.                 bNoGravity = false;
  1418.             }
  1419.         }
  1420.         else if (cmd.upmove != 0)
  1421.         {
  1422.             // Clamp the speed to some reasonable maximum.
  1423.             cmd.upmove = clamp(cmd.upmove, -0x300, 0x300);
  1424.             if (waterlevel >= 2 ||  bFly || (player.cheats & CF_NOCLIP2))
  1425.             {
  1426.                 Vel.Z = Speed * cmd.upmove / 128.;
  1427.                 if (waterlevel < 2 && !bNoGravity)
  1428.                 {
  1429.                     bFly = true;
  1430.                     bNoGravity = true;
  1431.                     if ((Vel.Z <= -39) && !(player.cheats & CF_PREDICTING))
  1432.                     { // Stop falling scream
  1433.                         A_StopSound(CHAN_VOICE);
  1434.                     }
  1435.                 }
  1436.             }
  1437.             else if (cmd.upmove > 0 && !(player.cheats & CF_PREDICTING))
  1438.             {
  1439.                 let fly = FindInventory("ArtiFly");
  1440.                 if (fly != NULL)
  1441.                 {
  1442.                     UseInventory(fly);
  1443.                 }
  1444.             }
  1445.         }
  1446.     }
  1447.  
  1448.  
  1449.     //----------------------------------------------------------------------------
  1450.     //
  1451.     // PROC P_HandleMovement
  1452.     //
  1453.     //----------------------------------------------------------------------------
  1454.  
  1455.     virtual void HandleMovement()
  1456.     {
  1457.         let player = self.player;
  1458.         // [RH] Check for fast turn around
  1459.         if (player.cmd.buttons & BT_TURN180 && !(player.oldbuttons & BT_TURN180))
  1460.         {
  1461.             player.turnticks = TURN180_TICKS;
  1462.         }
  1463.  
  1464.         // Handle movement
  1465.         if (reactiontime)
  1466.         { // Player is frozen
  1467.             reactiontime--;
  1468.         }
  1469.         else
  1470.         {
  1471.             MovePlayer();
  1472.             CheckJump();
  1473.             CheckMoveUpDown();
  1474.         }
  1475.     }
  1476.  
  1477.     //----------------------------------------------------------------------------
  1478.     //
  1479.     // PROC P_CheckUndoMorph
  1480.     //
  1481.     //----------------------------------------------------------------------------
  1482.  
  1483.     virtual void CheckUndoMorph()
  1484.     {
  1485.         let player = self.player;
  1486.         // Morph counter
  1487.         if (player.morphTics)
  1488.         {
  1489.             if (player.chickenPeck)
  1490.             { // Chicken attack counter
  1491.                 player.chickenPeck -= 3;
  1492.             }
  1493.             if (!--player.morphTics)
  1494.             { // Attempt to undo the chicken/pig
  1495.                 player.mo.UndoPlayerMorph(player, MRF_UNDOBYTIMEOUT);
  1496.             }
  1497.         }
  1498.     }
  1499.  
  1500.     //----------------------------------------------------------------------------
  1501.     //
  1502.     // PROC P_CheckPoison
  1503.     //
  1504.     //----------------------------------------------------------------------------
  1505.  
  1506.     virtual void CheckPoison()
  1507.     {
  1508.         let player = self.player;
  1509.         if (player.poisoncount && !(Level.maptime & 15))
  1510.         {
  1511.             player.poisoncount -= 5;
  1512.             if (player.poisoncount < 0)
  1513.             {
  1514.                 player.poisoncount = 0;
  1515.             }
  1516.             player.PoisonDamage(player.poisoner, 1, true);
  1517.         }
  1518.     }
  1519.  
  1520.     //----------------------------------------------------------------------------
  1521.     //
  1522.     // PROC P_CheckDegeneration
  1523.     //
  1524.     //----------------------------------------------------------------------------
  1525.  
  1526.     virtual void CheckDegeneration()
  1527.     {
  1528.         // Apply degeneration.
  1529.         if (sv_degeneration)
  1530.         {
  1531.             let player = self.player;
  1532.             int maxhealth = GetMaxHealth(true);
  1533.             if ((Level.maptime % TICRATE) == 0 && player.health > maxhealth)
  1534.             {
  1535.                 if (player.health - 5 < maxhealth)
  1536.                     player.health = maxhealth;
  1537.                 else
  1538.                     player.health--;
  1539.  
  1540.                 health = player.health;
  1541.             }
  1542.         }
  1543.     }
  1544.  
  1545.     //----------------------------------------------------------------------------
  1546.     //
  1547.     // PROC P_CheckAirSupply
  1548.     //
  1549.     //----------------------------------------------------------------------------
  1550.  
  1551.     virtual void CheckAirSupply()
  1552.     {
  1553.         // Handle air supply
  1554.         //if (level.airsupply > 0)
  1555.         {
  1556.             let player = self.player;
  1557.             if (waterlevel < 3 || (bInvulnerable) || (player.cheats & (CF_GODMODE | CF_NOCLIP2)) || (player.cheats & CF_GODMODE2))
  1558.             {
  1559.                 ResetAirSupply();
  1560.             }
  1561.             else if (player.air_finished <= Level.maptime && !(Level.maptime & 31))
  1562.             {
  1563.                 DamageMobj(NULL, NULL, 2 + ((Level.maptime - player.air_finished) / TICRATE), 'Drowning');
  1564.             }
  1565.         }
  1566.     }
  1567.  
  1568.     //----------------------------------------------------------------------------
  1569.     //
  1570.     // PROC P_PlayerThink
  1571.     //
  1572.     //----------------------------------------------------------------------------
  1573.    
  1574.     virtual void PlayerThink()
  1575.     {
  1576.         let player = self.player;
  1577.         UserCmd cmd = player.cmd;
  1578.        
  1579.         CheckFOV();
  1580.  
  1581.         if (player.inventorytics)
  1582.         {
  1583.             player.inventorytics--;
  1584.         }
  1585.         CheckCheats();
  1586.  
  1587.         if (bJustAttacked)
  1588.         { // Chainsaw/Gauntlets attack auto forward motion
  1589.             cmd.yaw = 0;
  1590.             cmd.forwardmove = 0xc800/2;
  1591.             cmd.sidemove = 0;
  1592.             bJustAttacked = false;
  1593.         }
  1594.  
  1595.         bool totallyfrozen = CheckFrozen();
  1596.  
  1597.         // Handle crouching
  1598.         CheckCrouch(totallyfrozen);
  1599.         CheckMusicChange();
  1600.  
  1601.         if (player.playerstate == PST_DEAD)
  1602.         {
  1603.             DeathThink ();
  1604.             return;
  1605.         }
  1606.         if (player.jumpTics != 0)
  1607.         {
  1608.             player.jumpTics--;
  1609.             if (player.onground && player.jumpTics < -18)
  1610.             {
  1611.                 player.jumpTics = 0;
  1612.             }
  1613.         }
  1614.         if (player.morphTics && !(player.cheats & CF_PREDICTING))
  1615.         {
  1616.             MorphPlayerThink ();
  1617.         }
  1618.  
  1619.         CheckPitch();
  1620.         HandleMovement();
  1621.         CalcHeight ();
  1622.  
  1623.         if (!(player.cheats & CF_PREDICTING))
  1624.         {
  1625.             CheckEnvironment();
  1626.             // Note that after this point the PlayerPawn may have changed due to getting unmorphed or getting its skull popped so 'self' is no longer safe to use.
  1627.             // This also must not read mo into a local variable because several functions in this block can change the attached PlayerPawn.
  1628.             player.mo.CheckUse();
  1629.             player.mo.CheckUndoMorph();
  1630.             // Cycle psprites.
  1631.             player.mo.TickPSprites();
  1632.             // Other Counters
  1633.             if (player.damagecount) player.damagecount--;
  1634.             if (player.bonuscount) player.bonuscount--;
  1635.  
  1636.             if (player.hazardcount)
  1637.             {
  1638.                 player.hazardcount--;
  1639.                 if (!(Level.maptime % player.hazardinterval) && player.hazardcount > 16*TICRATE)
  1640.                     player.mo.DamageMobj (NULL, NULL, 5, player.hazardtype);
  1641.             }
  1642.             player.mo.CheckPoison();
  1643.             player.mo.CheckDegeneration();
  1644.             player.mo.CheckAirSupply();
  1645.         }
  1646.     }
  1647.  
  1648.     //---------------------------------------------------------------------------
  1649.     //
  1650.     // PROC P_BringUpWeapon
  1651.     //
  1652.     // Starts bringing the pending weapon up from the bottom of the screen.
  1653.     // This is only called to start the rising, not throughout it.
  1654.     //
  1655.     //---------------------------------------------------------------------------
  1656.  
  1657.     void BringUpWeapon ()
  1658.     {
  1659.         let player = self.player;
  1660.         if (player.PendingWeapon == WP_NOCHANGE)
  1661.         {
  1662.             if (player.ReadyWeapon != null)
  1663.             {
  1664.                 player.GetPSprite(PSP_WEAPON).y = WEAPONTOP;
  1665.                 player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState());
  1666.             }
  1667.             return;
  1668.         }
  1669.  
  1670.         let weapon = player.PendingWeapon;
  1671.  
  1672.         // If the player has a tome of power, use self weapon's powered up
  1673.         // version, if one is available.
  1674.         if (weapon != null &&
  1675.             weapon.SisterWeapon &&
  1676.             weapon.SisterWeapon.bPowered_Up &&
  1677.             player.mo.FindInventory ('PowerWeaponLevel2', true))
  1678.         {
  1679.             weapon = weapon.SisterWeapon;
  1680.         }
  1681.  
  1682.         player.PendingWeapon = WP_NOCHANGE;
  1683.         player.ReadyWeapon = weapon;
  1684.         player.mo.weaponspecial = 0;
  1685.  
  1686.         if (weapon != null)
  1687.         {
  1688.             weapon.PlayUpSound(self);
  1689.             player.refire = 0;
  1690.  
  1691.             player.GetPSprite(PSP_WEAPON).y = player.cheats & CF_INSTANTWEAPSWITCH? WEAPONTOP : WEAPONBOTTOM;
  1692.             // make sure that the previous weapon's flash state is terminated.
  1693.             // When coming here from a weapon drop it may still be active.
  1694.             player.SetPsprite(PSP_FLASH, null);
  1695.             player.SetPsprite(PSP_WEAPON, weapon.GetUpState());
  1696.         }
  1697.     }
  1698.    
  1699.     //===========================================================================
  1700.     //
  1701.     // PlayerPawn :: BestWeapon
  1702.     //
  1703.     // Returns the best weapon a player has, possibly restricted to a single
  1704.     // type of ammo.
  1705.     //
  1706.     //===========================================================================
  1707.  
  1708.     Weapon BestWeapon(Class<Ammo> ammotype)
  1709.     {
  1710.         Weapon bestMatch = NULL;
  1711.         int bestOrder = int.max;
  1712.         Inventory item;
  1713.         bool tomed = !!FindInventory ('PowerWeaponLevel2', true);
  1714.  
  1715.         // Find the best weapon the player has.
  1716.         for (item = Inv; item != NULL; item = item.Inv)
  1717.         {
  1718.             let weap = Weapon(item);
  1719.             if (weap == null)
  1720.                 continue;
  1721.  
  1722.             // Don't select it if it's worse than what was already found.
  1723.             if (weap.SelectionOrder > bestOrder)
  1724.                 continue;
  1725.  
  1726.             // Don't select it if its primary fire doesn't use the desired ammo.
  1727.             if (ammotype != NULL &&
  1728.                 (weap.Ammo1 == NULL ||
  1729.                  weap.Ammo1.GetClass() != ammotype))
  1730.                 continue;
  1731.  
  1732.             // Don't select it if the Tome is active and self isn't the powered-up version.
  1733.             if (tomed && weap.SisterWeapon != NULL && weap.SisterWeapon.bPowered_Up)
  1734.                 continue;
  1735.  
  1736.             // Don't select it if it's powered-up and the Tome is not active.
  1737.             if (!tomed && weap.bPowered_Up)
  1738.                 continue;
  1739.  
  1740.             // Don't select it if there isn't enough ammo to use its primary fire.
  1741.             if (!(weap.bAMMO_OPTIONAL) &&
  1742.                 !weap.CheckAmmo (Weapon.PrimaryFire, false))
  1743.                 continue;
  1744.  
  1745.             // Don't select if if there isn't enough ammo as determined by the weapon's author.
  1746.             if (weap.MinSelAmmo1 > 0 && (weap.Ammo1 == NULL || weap.Ammo1.Amount < weap.MinSelAmmo1))
  1747.                 continue;
  1748.             if (weap.MinSelAmmo2 > 0 && (weap.Ammo2 == NULL || weap.Ammo2.Amount < weap.MinSelAmmo2))
  1749.                 continue;
  1750.  
  1751.             // This weapon is usable!
  1752.             bestOrder = weap.SelectionOrder;
  1753.             bestMatch = weap;
  1754.         }
  1755.         return bestMatch;
  1756.     }
  1757.  
  1758.    
  1759.     //---------------------------------------------------------------------------
  1760.     //
  1761.     // PROC P_DropWeapon
  1762.     //
  1763.     // The player died, so put the weapon away.
  1764.     //
  1765.     //---------------------------------------------------------------------------
  1766.  
  1767.     void DropWeapon ()
  1768.     {
  1769.         let player = self.player;
  1770.         if (player == null)
  1771.         {
  1772.             return;
  1773.         }
  1774.         // Since the weapon is dropping, stop blocking switching.
  1775.         player.WeaponState &= ~WF_DISABLESWITCH;
  1776.         Weapon weap = player.ReadyWeapon;
  1777.         if ((weap != null) && (player.health > 0 || !weap.bNoDeathDeselect))
  1778.         {
  1779.             player.SetPsprite(PSP_WEAPON, weap.GetDownState());
  1780.         }
  1781.     }
  1782.    
  1783.     //===========================================================================
  1784.     //
  1785.     // PlayerPawn :: PickNewWeapon
  1786.     //
  1787.     // Picks a new weapon for this player. Used mostly for running out of ammo,
  1788.     // but it also works when an ACS script explicitly takes the ready weapon
  1789.     // away or the player picks up some ammo they had previously run out of.
  1790.     //
  1791.     //===========================================================================
  1792.  
  1793.     Weapon PickNewWeapon(Class<Ammo> ammotype)
  1794.     {
  1795.         Weapon best = BestWeapon (ammotype);
  1796.  
  1797.         if (best != NULL)
  1798.         {
  1799.             player.PendingWeapon = best;
  1800.             if (player.ReadyWeapon != NULL)
  1801.             {
  1802.                 DropWeapon();
  1803.             }
  1804.             else if (player.PendingWeapon != WP_NOCHANGE)
  1805.             {
  1806.                 BringUpWeapon ();
  1807.             }
  1808.         }
  1809.         return best;
  1810.     }
  1811.  
  1812.     //===========================================================================
  1813.     //
  1814.     // PlayerPawn :: GiveDefaultInventory
  1815.     //
  1816.     //===========================================================================
  1817.  
  1818.     virtual void GiveDefaultInventory ()
  1819.     {
  1820.         let player = self.player;
  1821.         if (player == NULL) return;
  1822.  
  1823.         // HexenArmor must always be the first item in the inventory because
  1824.         // it provides player class based protection that should not affect
  1825.         // any other protection item.
  1826.         let myclass = GetClass();
  1827.         GiveInventoryType('HexenArmor');
  1828.         let harmor = HexenArmor(FindInventory('HexenArmor'));
  1829.  
  1830.         harmor.Slots[4] = self.HexenArmor[0];
  1831.         for (int i = 0; i < 4; ++i)
  1832.         {
  1833.             harmor.SlotsIncrement[i] = self.HexenArmor[i + 1];
  1834.         }
  1835.  
  1836.         // BasicArmor must come right after that. It should not affect any
  1837.         // other protection item as well but needs to process the damage
  1838.         // before the HexenArmor does.
  1839.         GiveInventoryType('BasicArmor');
  1840.  
  1841.         // Now add the items from the DECORATE definition
  1842.         let di = GetDropItems();
  1843.  
  1844.         while (di)
  1845.         {
  1846.             Class<Actor> ti = di.Name;
  1847.             if (ti)
  1848.             {
  1849.                 let tinv = (class<Inventory>)(ti);
  1850.                 if (!tinv)
  1851.                 {
  1852.                     Console.Printf(TEXTCOLOR_ORANGE .. "%s is not an inventory item and cannot be given to a player as start item.\n", di.Name);
  1853.                 }
  1854.                 else
  1855.                 {
  1856.                     let item = FindInventory(tinv);
  1857.                     if (item != NULL)
  1858.                     {
  1859.                         item.Amount = clamp(
  1860.                             item.Amount + (di.Amount ? di.Amount : item.default.Amount), 0, item.MaxAmount);
  1861.                     }
  1862.                     else
  1863.                     {
  1864.                         item = Inventory(Spawn(ti));
  1865.                         item.bIgnoreSkill = true;   // no skill multipliers here
  1866.                         item.Amount = di.Amount;
  1867.                         let weap = Weapon(item);
  1868.                         if (weap)
  1869.                         {
  1870.                             // To allow better control any weapon is emptied of
  1871.                             // ammo before being given to the player.
  1872.                             weap.AmmoGive1 = weap.AmmoGive2 = 0;
  1873.                         }
  1874.                         bool res;
  1875.                         Actor check;
  1876.                         [res, check] = item.CallTryPickup(self);
  1877.                         if (!res)
  1878.                         {
  1879.                             item.Destroy();
  1880.                             item = NULL;
  1881.                         }
  1882.                         else if (check != self)
  1883.                         {
  1884.                             // Player was morphed. This is illegal at game start.
  1885.                             // This problem is only detectable when it's too late to do something about it...
  1886.                             ThrowAbortException("Cannot give morph item '%s' when starting a game!", di.Name);
  1887.                         }
  1888.                     }
  1889.                     let weap = Weapon(item);
  1890.                     if (weap != NULL && weap.CheckAmmo(Weapon.EitherFire, false))
  1891.                     {
  1892.                         player.ReadyWeapon = player.PendingWeapon = weap;
  1893.                     }
  1894.                 }
  1895.             }
  1896.             di = di.Next;
  1897.         }
  1898.     }
  1899.  
  1900.     //===========================================================================
  1901.     //
  1902.     // PlayerPawn :: GiveDeathmatchInventory
  1903.     //
  1904.     // Gives players items they should have in addition to their default
  1905.     // inventory when playing deathmatch. (i.e. all keys)
  1906.     //
  1907.     //===========================================================================
  1908.  
  1909.     virtual void GiveDeathmatchInventory()
  1910.     {
  1911.         for ( int i = 0; i < AllActorClasses.Size(); ++i)
  1912.         {
  1913.             let cls = (class<Key>)(AllActorClasses[i]);
  1914.             if (cls)
  1915.             {
  1916.                 let keyobj = GetDefaultByType(cls);
  1917.                 if (keyobj.special1 != 0)
  1918.                 {
  1919.                     GiveInventoryType(cls);
  1920.                 }
  1921.             }
  1922.         }
  1923.     }
  1924.  
  1925.     //===========================================================================
  1926.     //
  1927.     //
  1928.     //
  1929.     //===========================================================================
  1930.  
  1931.     override int GetMaxHealth(bool withupgrades) const
  1932.     {
  1933.         int ret = MaxHealth > 0? MaxHealth : ((Level.compatflags & COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
  1934.         if (withupgrades) ret += stamina + BonusHealth;
  1935.         return ret;
  1936.     }
  1937.    
  1938.     //===========================================================================
  1939.     //
  1940.     //
  1941.     //
  1942.     //===========================================================================
  1943.  
  1944.     virtual int GetTeleportFreezeTime()
  1945.     {
  1946.         if (TeleportFreezeTime <= 0) return 0;
  1947.         let item = inv;
  1948.         while (item != null)
  1949.         {
  1950.             if (item.GetNoTeleportFreeze()) return 0;
  1951.             item = item.inv;
  1952.         }
  1953.         return TeleportFreezeTime;
  1954.     }
  1955.    
  1956.    
  1957.     //===========================================================================
  1958.     //
  1959.     // G_PlayerFinishLevel
  1960.     // Called when a player completes a level.
  1961.     //
  1962.     // flags is checked for RESETINVENTORY and RESETHEALTH only.
  1963.     //
  1964.     //===========================================================================
  1965.  
  1966.     void PlayerFinishLevel (int mode, int flags)
  1967.     {
  1968.         Inventory item, next;
  1969.         let p = player;
  1970.  
  1971.         if (p.morphTics != 0)
  1972.         { // Undo morph
  1973.             Unmorph(self, 0, true);
  1974.         }
  1975.         // 'self' will be no longer valid from here on in case of an unmorph
  1976.         let me = p.mo;
  1977.  
  1978.         // Strip all current powers, unless moving in a hub and the power is okay to keep.
  1979.         item = me.Inv;
  1980.         while (item != NULL)
  1981.         {
  1982.             next = item.Inv;
  1983.             if (item is 'Powerup')
  1984.             {
  1985.                 if (deathmatch || ((mode != FINISH_SameHub || !item.bHUBPOWER) && !item.bPERSISTENTPOWER)) // Keep persistent powers in non-deathmatch games
  1986.                 {
  1987.                     item.Destroy ();
  1988.                 }
  1989.             }
  1990.             item = next;
  1991.         }
  1992.         let ReadyWeapon = p.ReadyWeapon;
  1993.         if (ReadyWeapon != NULL && ReadyWeapon.bPOWERED_UP && p.PendingWeapon == ReadyWeapon.SisterWeapon)
  1994.         {
  1995.             // Unselect powered up weapons if the unpowered counterpart is pending
  1996.             p.ReadyWeapon = p.PendingWeapon;
  1997.         }
  1998.         // reset invisibility to default
  1999.         me.RestoreRenderStyle();
  2000.         p.extralight = 0;                   // cancel gun flashes
  2001.         p.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;   // cancel ir goggles
  2002.         p.fixedlightlevel = -1;
  2003.         p.damagecount = 0;              // no palette changes
  2004.         p.bonuscount = 0;
  2005.         p.poisoncount = 0;
  2006.         p.inventorytics = 0;
  2007.  
  2008.         if (mode != FINISH_SameHub)
  2009.         {
  2010.             // Take away flight and keys (and anything else with IF_INTERHUBSTRIP set)
  2011.             item = me.Inv;
  2012.             while (item != NULL)
  2013.             {
  2014.                 next = item.Inv;
  2015.                 if (item.InterHubAmount < 1)
  2016.                 {
  2017.                     item.Destroy ();
  2018.                 }
  2019.                 item = next;
  2020.             }
  2021.         }
  2022.  
  2023.         if (mode == FINISH_NoHub && !level.KEEPFULLINVENTORY)
  2024.         { // Reduce all owned (visible) inventory to defined maximum interhub amount
  2025.             Array<Inventory> todelete;
  2026.             for (item = me.Inv; item != NULL; item = item.Inv)
  2027.             {
  2028.                 // If the player is carrying more samples of an item than allowed, reduce amount accordingly
  2029.                 if (item.bINVBAR && item.Amount > item.InterHubAmount)
  2030.                 {
  2031.                     item.Amount = item.InterHubAmount;
  2032.                     if (level.REMOVEITEMS && !item.bUNDROPPABLE && !item.bUNCLEARABLE)
  2033.                     {
  2034.                         todelete.Push(item);
  2035.                     }
  2036.                 }
  2037.             }
  2038.             for (int i = 0; i < toDelete.Size(); i++)
  2039.             {
  2040.                 let it = toDelete[i];
  2041.                 if (!it.bDestroyed)
  2042.                 {
  2043.                     item.DepleteOrDestroy();
  2044.                 }
  2045.             }
  2046.         }
  2047.  
  2048.         // Resets player health to default if not dead.
  2049.         if ((flags & CHANGELEVEL_RESETHEALTH) && p.playerstate != PST_DEAD)
  2050.         {
  2051.             p.health = me.health = me.SpawnHealth();
  2052.         }
  2053.  
  2054.         // Clears the entire inventory and gives back the defaults for starting a game
  2055.         if ((flags & CHANGELEVEL_RESETINVENTORY) && p.playerstate != PST_DEAD)
  2056.         {
  2057.             me.ClearInventory();
  2058.             me.GiveDefaultInventory();
  2059.         }
  2060.     }
  2061.    
  2062.     //===========================================================================
  2063.     //
  2064.     // FWeaponSlot :: PickWeapon
  2065.     //
  2066.     // Picks a weapon from this slot. If no weapon is selected in this slot,
  2067.     // or the first weapon in this slot is selected, returns the last weapon.
  2068.     // Otherwise, returns the previous weapon in this slot. This means
  2069.     // precedence is given to the last weapon in the slot, which by convention
  2070.     // is probably the strongest. Does not return weapons you have no ammo
  2071.     // for or which you do not possess.
  2072.     //
  2073.     //===========================================================================
  2074.  
  2075.     virtual Weapon PickWeapon(int slot, bool checkammo)
  2076.     {
  2077.         int i, j;
  2078.  
  2079.         let player = self.player;
  2080.         int Size = player.weapons.SlotSize(slot);
  2081.         // Does this slot even have any weapons?
  2082.         if (Size == 0)
  2083.         {
  2084.             return player.ReadyWeapon;
  2085.         }
  2086.         let ReadyWeapon = player.ReadyWeapon;
  2087.         if (ReadyWeapon != null)
  2088.         {
  2089.             for (i = 0; i < Size; i++)
  2090.             {
  2091.                 let weapontype = player.weapons.GetWeapon(slot, i);
  2092.                 if (weapontype == ReadyWeapon.GetClass() ||
  2093.                     (ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeapon != null && ReadyWeapon.SisterWeapon.GetClass() == weapontype))
  2094.                 {
  2095.                     for (j = (i == 0 ? Size - 1 : i - 1);
  2096.                         j != i;
  2097.                         j = (j == 0 ? Size - 1 : j - 1))
  2098.                     {
  2099.                         let weapontype2 = player.weapons.GetWeapon(slot, j);
  2100.                         let weap = Weapon(player.mo.FindInventory(weapontype2));
  2101.  
  2102.                         if (weap != null)
  2103.                         {
  2104.                             if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false))
  2105.                             {
  2106.                                 return weap;
  2107.                             }
  2108.                         }
  2109.                     }
  2110.                 }
  2111.             }
  2112.         }
  2113.         for (i = Size - 1; i >= 0; i--)
  2114.         {
  2115.             let weapontype = player.weapons.GetWeapon(slot, i);
  2116.             let weap = Weapon(player.mo.FindInventory(weapontype));
  2117.  
  2118.             if (weap != null)
  2119.             {
  2120.                 if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false))
  2121.                 {
  2122.                     return weap;
  2123.                 }
  2124.             }
  2125.         }
  2126.         return ReadyWeapon;
  2127.     }
  2128.  
  2129.     //===========================================================================
  2130.     //
  2131.     // FindMostRecentWeapon
  2132.     //
  2133.     // Locates the slot and index for the most recently selected weapon. If the
  2134.     // player is in the process of switching to a new weapon, that is the most
  2135.     // recently selected weapon. Otherwise, the current weapon is the most recent
  2136.     // weapon.
  2137.     //
  2138.     //===========================================================================
  2139.  
  2140.     bool, int, int FindMostRecentWeapon()
  2141.     {
  2142.         let player = self.player;
  2143.         let ReadyWeapon = player.ReadyWeapon;
  2144.         if (player.PendingWeapon != WP_NOCHANGE)
  2145.         {
  2146.             // Workaround for the current inability
  2147.             bool found;
  2148.             int slot;
  2149.             int index;
  2150.             [found, slot, index] = player.weapons.LocateWeapon(player.PendingWeapon.GetClass());
  2151.             return found, slot, index;
  2152.         }
  2153.         else if (ReadyWeapon != null)
  2154.         {
  2155.             bool found;
  2156.             int slot;
  2157.             int index;
  2158.             [found, slot, index] = player.weapons.LocateWeapon(ReadyWeapon.GetClass());
  2159.             if (!found)
  2160.             {
  2161.                 // If the current weapon wasn't found and is powered up,
  2162.                 // look for its non-powered up version.
  2163.                 if (ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeaponType != null)
  2164.                 {
  2165.                     [found, slot, index] = player.weapons.LocateWeapon(ReadyWeapon.SisterWeaponType);
  2166.                     return found, slot, index;
  2167.                 }
  2168.                 return false, 0, 0;
  2169.             }
  2170.             return true, slot, index;
  2171.         }
  2172.         else
  2173.         {
  2174.             return false, 0, 0;
  2175.         }
  2176.     }
  2177.  
  2178.     //===========================================================================
  2179.     //
  2180.     // FWeaponSlots :: PickNextWeapon
  2181.     //
  2182.     // Returns the "next" weapon for this player. If the current weapon is not
  2183.     // in a slot, then it just returns that weapon, since there's nothing to
  2184.     // consider it relative to.
  2185.     //
  2186.     //===========================================================================
  2187.     const NUM_WEAPON_SLOTS = 10;
  2188.  
  2189.     virtual Weapon PickNextWeapon()
  2190.     {
  2191.         let player = self.player;
  2192.         bool found;
  2193.         int startslot, startindex;
  2194.         int slotschecked = 0;
  2195.  
  2196.         [found, startslot, startindex] = FindMostRecentWeapon();
  2197.         let ReadyWeapon = player.ReadyWeapon;
  2198.         if (ReadyWeapon == null || found)
  2199.         {
  2200.             int slot;
  2201.             int index;
  2202.  
  2203.             if (ReadyWeapon == null)
  2204.             {
  2205.                 startslot = NUM_WEAPON_SLOTS - 1;
  2206.                 startindex = player.weapons.SlotSize(startslot) - 1;
  2207.             }
  2208.  
  2209.             slot = startslot;
  2210.             index = startindex;
  2211.             do
  2212.             {
  2213.                 if (++index >= player.weapons.SlotSize(slot))
  2214.                 {
  2215.                     index = 0;
  2216.                     slotschecked++;
  2217.                     if (++slot >= NUM_WEAPON_SLOTS)
  2218.                     {
  2219.                         slot = 0;
  2220.                     }
  2221.                 }
  2222.                 let type = player.weapons.GetWeapon(slot, index);
  2223.                 let weap = Weapon(FindInventory(type));
  2224.                 if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false))
  2225.                 {
  2226.                     return weap;
  2227.                 }
  2228.             } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
  2229.         }
  2230.         return ReadyWeapon;
  2231.     }
  2232.  
  2233.     //===========================================================================
  2234.     //
  2235.     // FWeaponSlots :: PickPrevWeapon
  2236.     //
  2237.     // Returns the "previous" weapon for this player. If the current weapon is
  2238.     // not in a slot, then it just returns that weapon, since there's nothing to
  2239.     // consider it relative to.
  2240.     //
  2241.     //===========================================================================
  2242.  
  2243.     virtual Weapon PickPrevWeapon()
  2244.     {
  2245.         let player = self.player;
  2246.         int startslot, startindex;
  2247.         bool found;
  2248.         int slotschecked = 0;
  2249.  
  2250.         [found, startslot, startindex] = FindMostRecentWeapon();
  2251.         if (player.ReadyWeapon == null || found)
  2252.         {
  2253.             int slot;
  2254.             int index;
  2255.  
  2256.             if (player.ReadyWeapon == null)
  2257.             {
  2258.                 startslot = 0;
  2259.                 startindex = 0;
  2260.             }
  2261.  
  2262.             slot = startslot;
  2263.             index = startindex;
  2264.             do
  2265.             {
  2266.                 if (--index < 0)
  2267.                 {
  2268.                     slotschecked++;
  2269.                     if (--slot < 0)
  2270.                     {
  2271.                         slot = NUM_WEAPON_SLOTS - 1;
  2272.                     }
  2273.                     index = player.weapons.SlotSize(slot) - 1;
  2274.                 }
  2275.                 let type = player.weapons.GetWeapon(slot, index);
  2276.                 let weap = Weapon(FindInventory(type));
  2277.                 if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false))
  2278.                 {
  2279.                     return weap;
  2280.                 }
  2281.             } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
  2282.         }
  2283.         return player.ReadyWeapon;
  2284.     }
  2285.  
  2286.     //============================================================================
  2287.     //
  2288.     // P_BobWeapon
  2289.     //
  2290.     // [RH] Moved this out of A_WeaponReady so that the weapon can bob every
  2291.     // tic and not just when A_WeaponReady is called. Not all weapons execute
  2292.     // A_WeaponReady every tic, and it looks bad if they don't bob smoothly.
  2293.     //
  2294.     // [XA] Added new bob styles and exposed bob properties. Thanks, Ryan Cordell!
  2295.     // [SP] Added new user option for bob speed
  2296.     //
  2297.     //============================================================================
  2298.  
  2299.     virtual Vector2 BobWeapon (double ticfrac)
  2300.     {
  2301.         Vector2 p1, p2, r;
  2302.         Vector2 result;
  2303.  
  2304.         float bobtarget;
  2305.  
  2306.         let player = self.player;
  2307.         if (!player) return (0, 0);
  2308.         let weapon = player.ReadyWeapon;
  2309.  
  2310.         if (weapon == null || weapon.bDontBob)
  2311.         {
  2312.             return (0, 0);
  2313.         }
  2314.  
  2315.         // [XA] Get the current weapon's bob properties.
  2316.         int bobstyle = weapon.BobStyle;
  2317.         double BobSpeed = (weapon.BobSpeed * 128);
  2318.         double Rangex = weapon.BobRangeX;
  2319.         double Rangey = weapon.BobRangeY;
  2320.  
  2321.         for (int i = 0; i < 2; i++)
  2322.         {
  2323.             // Bob the weapon based on movement speed. ([SP] And user's bob speed setting)
  2324.             double angle = (BobSpeed * player.GetWBobSpeed() * 35 / TICRATE*(Level.maptime - 1 + i)) * (360. / 8192.);
  2325.  
  2326.             // [RH] Smooth transitions between bobbing and not-bobbing frames.
  2327.             // This also fixes the bug where you can "stick" a weapon off-center by
  2328.             // shooting it when it's at the peak of its swing.
  2329.             bobtarget = double((player.WeaponState & WF_WEAPONBOBBING) ? player.bob : 0.);
  2330.             if (curbob != bobtarget)
  2331.             {
  2332.                 if (abs(bobtarget - curbob) <= 1)
  2333.                 {
  2334.                     curbob = bobtarget;
  2335.                 }
  2336.                 else
  2337.                 {
  2338.                     double zoom = MAX(1., abs(curbob - bobtarget) / 40);
  2339.                     if (curbob > bobtarget)
  2340.                     {
  2341.                         curbob -= zoom;
  2342.                     }
  2343.                     else
  2344.                     {
  2345.                         curbob += zoom;
  2346.                     }
  2347.                 }
  2348.             }
  2349.  
  2350.             if (curbob != 0)
  2351.             {
  2352.                 //[SP] Added in decorate player.viewbob checks
  2353.                 double bobx = (player.bob * Rangex * ViewBob);
  2354.                 double boby = (player.bob * Rangey * ViewBob);
  2355.                 switch (bobstyle)
  2356.                 {
  2357.                 case Bob_Normal:
  2358.                     r.X = bobx * cos(angle);
  2359.                     r.Y = boby * abs(sin(angle));
  2360.                     break;
  2361.  
  2362.                 case Bob_Inverse:
  2363.                     r.X = bobx*cos(angle);
  2364.                     r.Y = boby * (1. - abs(sin(angle)));
  2365.                     break;
  2366.  
  2367.                 case Bob_Alpha:
  2368.                     r.X = bobx * sin(angle);
  2369.                     r.Y = boby * abs(sin(angle));
  2370.                     break;
  2371.  
  2372.                 case Bob_InverseAlpha:
  2373.                     r.X = bobx * sin(angle);
  2374.                     r.Y = boby * (1. - abs(sin(angle)));
  2375.                     break;
  2376.  
  2377.                 case Bob_Smooth:
  2378.                     r.X = bobx*cos(angle);
  2379.                     r.Y = 0.5f * (boby * (1. - (cos(angle * 2))));
  2380.                     break;
  2381.  
  2382.                 case Bob_InverseSmooth:
  2383.                     r.X = bobx*cos(angle);
  2384.                     r.Y = 0.5f * (boby * (1. + (cos(angle * 2))));
  2385.                 }
  2386.             }
  2387.             else
  2388.             {
  2389.                 r = (0, 0);
  2390.             }
  2391.             if (i == 0) p1 = r; else p2 = r;
  2392.         }
  2393.         return p1 * (1. - ticfrac) + p2 * ticfrac;
  2394.     }
  2395.    
  2396.     //----------------------------------------------------------------------------
  2397.     //
  2398.     //
  2399.     //
  2400.     //----------------------------------------------------------------------------
  2401.  
  2402.     virtual clearscope color GetPainFlash() const
  2403.     {
  2404.         Color painFlash = GetPainFlashForType(DamageTypeReceived);
  2405.         if (painFlash == 0) painFlash = DamageFade;
  2406.         return painFlash;
  2407.     }
  2408.        
  2409.     //===========================================================================
  2410.     //
  2411.     // PlayerPawn :: ResetAirSupply
  2412.     //
  2413.     // Gives the player a full "tank" of air. If they had previously completely
  2414.     // run out of air, also plays the *gasp sound. Returns true if the player
  2415.     // was drowning.
  2416.     //
  2417.     //===========================================================================
  2418.  
  2419.     virtual bool ResetAirSupply (bool playgasp = true)
  2420.     {
  2421.         let player = self.player;
  2422.         bool wasdrowning = (player.air_finished < Level.maptime);
  2423.  
  2424.         if (playgasp && wasdrowning)
  2425.         {
  2426.             A_StartSound("*gasp", CHAN_VOICE);
  2427.         }
  2428.         if (Level.airsupply > 0 && AirCapacity > 0) player.air_finished = Level.maptime + int(Level.airsupply * AirCapacity);
  2429.         else player.air_finished = int.max;
  2430.         return wasdrowning;
  2431.     }
  2432.  
  2433.     //----------------------------------------------------------------------------
  2434.     //
  2435.     //
  2436.     //
  2437.     //----------------------------------------------------------------------------
  2438.  
  2439.     native clearscope static String GetPrintableDisplayName(Class<Actor> cls);
  2440.     native void CheckMusicChange();
  2441.     native void CheckEnvironment();
  2442.     native void CheckUse();
  2443.     native void CheckWeaponButtons();
  2444.     native void MarkPlayerSounds();
  2445.     private native int SetupCrouchSprite(int c);
  2446.     private native clearscope Color GetPainFlashForType(Name type);
  2447. }
Add Comment
Please, Sign In to add comment