Advertisement
Guest User

Broken adopted Blades of Agony tank code.

a guest
Nov 24th, 2020
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 77.35 KB | None | 0 0
  1. //------------------------------------------------------------------------------
  2. //MAIN
  3. //------------------------------------------------------------------------------
  4.  
  5. /*
  6. Code taken from Blades of Agony
  7. */
  8.  
  9. //
  10. // Vehicle base class - handles trample effect and slope aware terrain following
  11. class VehicleBase : MT_Actor
  12. {
  13.     double trampleradius;
  14.  
  15.     Property TrampleRadius:trampleradius;
  16.  
  17.     Default
  18.     {
  19.         VehicleBase.TrampleRadius -1;
  20.     }
  21.  
  22.     // Handling for terrain-based pitch/roll calculations...
  23.     static double SetPitchRoll(Actor mo, double xoffset = 0, double yoffset = 0, int cap = 80, bool force = false, double centerxoffset = 0, double centeryoffset = 0)
  24.     {
  25.         if (!mo) { return 0; }
  26.  
  27.         double testwidth = mo.radius + yoffset;
  28.         double testlength;
  29.  
  30.         if (mo is "TankTreadsBase") { testlength = TankTreadsBase(mo).length; }
  31.         if (!testlength) { testlength = mo.radius + xoffset; }
  32.  
  33.         // Account for current pitch/roll when measuring corner heights
  34.         testwidth *= abs(cos(mo.roll));
  35.         testlength *= abs(cos(mo.pitch));
  36.  
  37.         double points[4], minz = 0x7FFFFFFF, maxz = -0x7FFFFFFF;
  38.  
  39.         // Get the relative z-height at the four corners of the tank
  40.         points[0] = mo.GetZAt(centerxoffset + testlength, centeryoffset + testwidth);
  41.         points[1] = mo.GetZAt(centerxoffset + testlength, centeryoffset - testwidth);
  42.         points[2] = mo.GetZAt(centerxoffset - testlength, centeryoffset + testwidth);
  43.         points[3] = mo.GetZAt(centerxoffset - testlength, centeryoffset - testwidth);
  44.  
  45.         for (int i = 0; i < 4; i++)
  46.         {
  47.             double maxstep = mo.master ? mo.master.MaxStepHeight : mo.MaxStepHeight;
  48.             double maxdrop = mo.master ? mo.master.MaxDropoffHeight : mo.MaxDropoffHeight;
  49.  
  50.             if (points[i] > mo.pos.z + maxstep) { points[i] = 0; } // Ignore the point if you can't climb that high
  51.             else if (points[i] < mo.pos.z - maxdrop) { points[i] = 0; } // Ignore the point if it's a dropoff
  52.             else { points[i] -= mo.floorz; }
  53.         }
  54.  
  55.         // Use those values to calculate the pitch.roll amounts
  56.         double pitchinput = (points[0] + points[1]) / 2 - (points[2] + points[3]) / 2;
  57.         double rollinput = (points[1] + points[3]) / 2 - (points[0] + points[2]) / 2;
  58.  
  59.         pitchinput = atan(pitchinput / (testlength * 2));
  60.         rollinput = atan(rollinput / (testwidth * 2));
  61.  
  62.         // Interpolate to the new values
  63.         if (force || level.time && level.time < 15)
  64.         {
  65.             mo.pitch = clamp(-pitchinput, -cap, cap);
  66.             mo.roll = clamp(rollinput, -cap, cap);
  67.         }
  68.         else
  69.         {
  70.             if (mo.pitch > -pitchinput) { mo.pitch = max(mo.pitch - 1, -pitchinput); }
  71.             if (mo.pitch < -pitchinput) { mo.pitch = min(mo.pitch + 1, -pitchinput); }
  72.  
  73.             if (mo.roll > rollinput) { mo.roll = max(mo.roll - 1, rollinput); }
  74.             if (mo.roll < rollinput) { mo.roll = min(mo.roll + 1, rollinput); }
  75.  
  76.             mo.pitch = clamp(mo.pitch, -cap, cap);
  77.             mo.roll = clamp(mo.roll, -cap, cap);
  78.         }
  79.  
  80.  
  81.         if (mo.master)
  82.         {
  83.             // Return the amount that you need to adjust the model z position by in order to keep it looking like it's actually on the ground
  84.             double deltaz = testlength * sin(abs(mo.pitch)) + testwidth * sin(abs(mo.roll));
  85.  
  86.             return deltaz;
  87.         }
  88.  
  89.         return 0;
  90.     }
  91.  
  92.     override bool CanCollideWith(Actor other, bool passive)
  93.     {
  94.         //if (other is "FlattenableProp") { return false; }
  95.         //if (other is "Trample") { return false; }
  96.         return true;
  97.     }
  98.  
  99.     override void Tick()
  100.     {
  101.         SetPitchRoll(self);
  102.         DoTrample();
  103.  
  104.         Actor.Tick();
  105.     }
  106.  
  107.     void DoTrample(bool always = false)
  108.     {
  109.         double movespeed = vel.length();
  110.  
  111.         if (trampleradius == -1)
  112.         {
  113.             if (self is "TankBase" && TankTreadsBase(TankBase(self).treads))
  114.             {
  115.                 trampleradius = TankBase(self).treads.radius;
  116.             }
  117.             else { trampleradius = radius; }
  118.         }
  119.  
  120.         if (movespeed >= 1 || always)
  121.         {
  122.             double dmg = always ? 1 : movespeed * 0.625;
  123.             let treads = TankTreadsBase(TankBase(self).treads);
  124.  
  125.             bool sp;
  126.             Actor t;
  127.  
  128.             // The explosion should only stick out 16 units in front of the tank
  129.             double offset = trampleradius - radius + 16;
  130.             if (self is "TankBase" && treads) { offset = max(treads.length, radius) - trampleradius + 16; }
  131.  
  132.             double angleoffset = treads ? treads.angle : 0;
  133.  
  134.             [sp, t] = A_SpawnItemEx("Trample", offset, 0, 0, 0, 0, 0, angleoffset, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE | SXF_ABSOLUTEANGLE);
  135.  
  136.             if (t)
  137.             {
  138.                 t.target = self; // Set the vehicle as the Trample actor's target, because Trample uses A_Explode, which defaults to treating the actor's target as the originator (and doesn't damage it)
  139.                 t.A_SetSize(trampleradius, Height / 3);
  140.                 Trample(t).dmg = int(dmg);
  141.                 if (treads) { t.angle = treads.angle; }
  142.             }
  143.         }
  144.     }
  145. }
  146.  
  147. // Trample actor - basically an explosion that simulates the spawning actor running over actors within its radius
  148. // Used by tanks and by rail sequences
  149. class Trample : MT_Actor
  150. {
  151.     int dmg;
  152.     int flags;
  153.  
  154.     Default
  155.     {
  156.         DamageType "Trample";
  157.         Damage 5;
  158.         Height 56;
  159.         Radius 56;
  160.         +NOBLOCKMAP
  161.         +NOGRAVITY
  162.         +NOCLIP
  163.     }
  164.  
  165.     States
  166.     {
  167.         Spawn:
  168.             TNT1 A 0;
  169.         Death:
  170.             "####" # 20 {
  171.                 A_Explode(dmg == 0 ? damage : dmg, int(Radius), flags, FALSE, int(Radius));
  172.             }
  173.             Stop;
  174.     }
  175. }
  176.  
  177. // Target for tanks to aim at
  178. class TankInterceptTarget : Actor
  179. {
  180.     Default
  181.     {
  182.         +BRIGHT
  183.         +INVISIBLE
  184.         +NOINTERACTION
  185.         Alpha 0.5;
  186.         RenderStyle "Translucent";
  187.     }
  188.  
  189.     States
  190.     {
  191.     Spawn:
  192.         EXCL A -1;
  193.         Stop;
  194.     }
  195.  
  196.     override void Tick()
  197.     {
  198.         Super.Tick();
  199.         if (developer >= 1)
  200.         {
  201.             bInvisible = false;
  202.         }
  203.         else
  204.         {
  205.             bInvisible = true;
  206.         }
  207.     }
  208. }
  209.  
  210. // Base tank enemy actor.  Follows terrain, tramples.
  211. //  Calls:
  212. //   DoTurretTarget (when turret is aimed at the target)
  213. //   DoTurret (when all conditions are right to fire a turret missile)
  214. //   DoFrontWeapon (when conditions are right to fire straight ahead)
  215. //
  216. // See FriendlySherman below for use
  217. class TankBase : VehicleBase
  218. {
  219.     Actor ally, treads, turret, gun, gunspot, frontgunspot;
  220.     Class<Actor> body;
  221.     Class<Actor> TurretProjectile;
  222.     Class<Actor> FrontGunProjectile;
  223.     double minpitch, maxpitch, minp, maxp;
  224.     int missilecount, missiletimeout, bulletcount, bullettimeout, chasetimeout, turrettimeout, reversetimeout, targettimeout, turretdelay;
  225.     sound movesound, idlesound;
  226.     bool missiletargeted, wheeled;
  227.     double zoffset, turretanglerange, turretspeed;
  228.     bool user_static;
  229.     bool intercept;
  230.     Actor InterceptTarget; // Target for tank to aim at
  231.    
  232.     Property TankActor:body;
  233.     Property MoveSound:movesound;
  234.     Property IdleSound:idlesound;
  235.     Property TurretAngleRange:turretanglerange;
  236.     Property TurretSpeed:turretspeed;
  237.     Property WheeledMovement:wheeled;
  238.     Property MaxGunPitch:maxpitch;
  239.     Property MinGunPitch:minpitch;
  240.     Property TurretProjectile:TurretProjectile;
  241.     Property FrontGunProjectile:FrontGunProjectile;
  242.     Property TurretDelay:turretdelay;
  243.  
  244.     Default
  245.     {
  246.         MaxDropoffHeight 24;
  247.         MaxStepHeight 24;
  248.         Height 20;
  249.         Radius 16;
  250.         Mass 0x7ffffff;
  251.         BloodType "TankSpark";
  252.         DamageFactor "Melee", 0.0;  // Knife, boot, etc.
  253.         DamageFactor "Bullet", 0.125;   // Most guns
  254.         DamageFactor "Pellet", 0.125;   // Shotguns
  255.         DamageFactor "Rifle", 0.5;  // Kar98, sniper rifle, chaingun
  256.         DamageFactor "Fire", 0.5;   // Flamer
  257.         DamageFactor "Trample", 0.1;    // Collision with another Tank/Vehicle
  258.         Monster;
  259.  
  260.         Obituary "$TANKGUN";
  261.         Species "NaziTank";
  262.         MaxTargetRange 2048;
  263.         RenderStyle "None";
  264.         +AVOIDMELEE
  265.         +BOSS
  266.         +BOSSDEATH
  267.         +DONTMORPH
  268.         +FLOORCLIP
  269.         +ISMONSTER
  270.         +LOOKALLAROUND
  271.         +NOBLOOD
  272.         +NOBLOODDECALS
  273.         +NODROPOFF
  274.         +NOINFIGHTING
  275.         +PAINLESS
  276.         +SLIDESONWALLS
  277.         +SOLID
  278.         TankBase.TankActor "US_Sherman";
  279.         TankBase.MoveSound "TKMOVE";
  280.         TankBase.IdleSound "TKIDLE";
  281.         TankBase.MinGunPitch -10;
  282.         TankBase.MaxGunPitch 25;
  283.         TankBase.TurretSpeed 1.5;
  284.         TankBase.TurretProjectile "Rocket";
  285.         //TankBase.TurretProjectile "TankMissile";
  286.         TankBase.FrontGunProjectile "PlasmaBall";
  287.         //TankBase.FrontGunProjectile "EnemyChaingunTracer";
  288.         TankBase.TurretDelay 9;
  289.     }
  290.  
  291.     States
  292.     {
  293.         Spawn:
  294.             MDLA A 35 {
  295.                 target = FindEnemy();
  296.                 if (target) { SetStateLabel("See"); }
  297.             }
  298.             Loop;
  299.         See:
  300.             TNT1 A 1 A_TankChase();
  301.             Goto See;
  302.         Death:
  303.             TNT1 A -1 DoDeath();
  304.             Stop;
  305.     }
  306.  
  307.     override void PostBeginPlay()
  308.     {
  309.         //A_GiveInventory("Z_DontShadeMe");
  310.  
  311.         // Spawn everything in neutral orientation
  312.         pitch = 0;
  313.         roll = 0;
  314.  
  315.         if (bFriendly) { Species = "AllyTank"; }
  316.         else { Species = "NaziTank"; }
  317.  
  318.         bool sp;
  319.         while (!treads) { [sp, treads] = A_SpawnItemEx(body, 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  320.         treads.master = self;
  321.         treads.bSolid = false;
  322.         treads.Species = Species;
  323.         treads.bFriendly = bFriendly;
  324.  
  325.         if (!user_static) { A_StartSound(idlesound, CHAN_6, CHANF_LOOPING, 0.25); }
  326.         else { level.total_monsters--; }
  327.  
  328.         targettimeout = Random(0, 35);
  329.         // Disable intercepting missiles on lower skill levels
  330.         intercept = G_SkillPropertyInt(SKILLP_ACSReturn) > 2;
  331.         InterceptTarget = Spawn("TankInterceptTarget", Pos, ALLOW_REPLACE);
  332.  
  333.         Super.PostBeginPlay();
  334.     }
  335.  
  336.     void DoDeath()
  337.     {
  338.         if (turret) { turret.Destroy(); }
  339.         InterceptTarget.Destroy();
  340.  
  341.         A_StopSound(CHAN_6); //the idle sound
  342.         A_StopSound(CHAN_7);
  343.         A_StartSound("weapons/explode", CHAN_AUTO, 0, 1.0, ATTN_IDLE);
  344.         A_Scream();
  345.         A_NoBlocking();
  346.  
  347.         if (treads)
  348.         {
  349.             Actor hulk = Spawn(TankTreadsBase(treads).deadclass, pos);
  350.  
  351.             hulk.angle = treads.angle;
  352.             hulk.pitch = treads.pitch;
  353.             hulk.roll = treads.roll;
  354.  
  355.             for (int i = 0; i < 30; i++)
  356.             {
  357.                 A_SpawnItemEx(TankTreadsBase(treads).debris, random(88, 96), random(88, 96), random(88, 112), random(1, 3), random(1, 3), random(1, 3), random(0, 360), SXF_CLIENTSIDE);
  358.                 //A_SpawnItemEx("Debris_GlassShard_Large", random(88, 96), random(88, 96), random(96, 128), random(1, 3), random(1, 3), random(1, 3), random(0, 360), SXF_CLIENTSIDE);
  359.             }
  360.         }
  361.  
  362.         A_RemoveChildren(true, RMVF_EVERYTHING);
  363.        
  364.         //A_SpawnItemEx("Nuke", 0, 0, 5, 0, 0, 0, 0, SXF_TRANSFERPOINTERS|SXF_NOCHECKPOSITION);
  365.         //A_SpawnItemEx("KaBoomer", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPOINTERS);
  366.         //A_SpawnItemEx("GeneralExplosion_Large", 56, 0, 32);
  367.     }
  368.  
  369.     override void Tick()
  370.     {
  371.         Actor.Tick();
  372.  
  373.         if (globalfreeze || level.Frozen || user_static) { return; }
  374.  
  375.         if (missiletimeout > 0) { missiletimeout--; }
  376.         if (bullettimeout > 0) { bullettimeout--; }
  377.         if (chasetimeout > 0) { chasetimeout--; }
  378.         if (turrettimeout > 0) { turrettimeout--; }
  379.         if (reversetimeout > 0) { reversetimeout--; }
  380.         if (targettimeout > 0) { targettimeout--; }
  381.  
  382.         if (!turret)
  383.         {
  384.             if (TankTreadsBase(treads))
  385.             {
  386.                 turret = TankTreadsBase(treads).turret;
  387.  
  388.                 if (turret)
  389.                 {
  390.                     turret.master = self;
  391.                     turret.angle = angle;
  392.                     turret.bSolid = false;
  393.                     turret.Species = Species;
  394.                 }
  395.             }
  396.         }
  397.        
  398.  
  399.         if (treads && turret)
  400.         {
  401.             double zoffset = VehicleBase.SetPitchRoll(treads);
  402.             double newz = pos.z - zoffset > GetZAt(0, 0) + MaxDropoffHeight ? pos.z - zoffset : GetZAt(0, 0);
  403.             treads.SetOrigin((pos.xy, newz), true);
  404.  
  405.             double delta = deltaangle(treads.angle, turret.angle);
  406.  
  407.             minp = treads.pitch * cos(delta) - maxpitch;
  408.             maxp = treads.pitch * cos(delta) - minpitch;
  409.  
  410.             turret.SetOrigin(treads.pos + (RotateVector((treads.radius * sin(treads.pitch), -treads.radius * sin(treads.roll)), treads.angle), treads.height * cos(treads.roll)), true);
  411.  
  412.             turret.pitch = treads.roll * sin(-delta) + treads.pitch * cos(delta);
  413.             turret.roll = treads.roll * cos(-delta) + treads.pitch * sin(delta);
  414.  
  415.             if (!gun) { gun = TankTurretBase(turret).gun; }
  416.  
  417.             if (gun)
  418.             {
  419.                 gun.A_Face(target, 0, turretspeed, flags:FAF_MIDDLE);
  420.  
  421.                 // Force pitch clamping
  422.                 if (turret.pitch + gun.pitch >= maxp) { gun.pitch = maxp; }
  423.                 else if (turret.pitch + gun.pitch < minp)  { gun.pitch = minp; }
  424.  
  425.                 gun.roll = turret.roll;
  426.             }
  427.  
  428.             if (!gunspot) { gunspot = TankTurretBase(turret).gunspot; }
  429.         }
  430.  
  431.         if (!frontgunspot) { frontgunspot = TankTreadsBase(treads).gunspot; }
  432.  
  433.         //if (bFriendly && (!ally || ally.health <= 0)) { ally = FindClosestPlayer(self, 360, 0, false); }
  434.  
  435.         if (target && bAvoidMelee) { bFrightened = Distance2D(target) < 256; } // Keep distance from the target
  436.     }
  437.  
  438.     override bool CanCollideWith(Actor other, bool passive)
  439.     {
  440.         if (IsFriend(other) || (other is "TankBlocker" && other.Species == Species)) { return false; }
  441.  
  442.         if (!treads || (other.pos.z + height > treads.pos.z && other.pos.z < treads.pos.z + treads.height))
  443.         {
  444.             other.DamageMobj(self, self, int(vel.length() * 0.625), "Trample");
  445.         }
  446.  
  447.         return Super.CanCollideWith(other, passive);
  448.     }
  449.  
  450.     Actor FindEnemy()
  451.     {
  452.         Actor enemy = null;
  453.  
  454.         if (bFriendly)
  455.         {
  456.             if (ally && ally.player.attacker && ally.player.attacker != ally && ally.player.attacker.health > 0)
  457.             {
  458.                 if (Distance3D(ally.player.attacker) < MaxTargetRange) { enemy = ally.player.attacker; }
  459.             }
  460.         }
  461.         else
  462.         {
  463.             for (int p = 0; p < MAXPLAYERS; p++)
  464.             { // Iterate through all of the players and find the closest one
  465.                 Actor pl = players[p].mo;
  466.  
  467.                 if (pl)
  468.                 {
  469.                     if (!pl.bShootable || pl.health <= 0) { continue; }
  470.                     if (players[p].cheats & CF_NOTARGET) { continue; }
  471.                     if (isFriend(pl)) { continue; }
  472.                     if (Distance3D(pl) > MaxTargetRange) { continue; }
  473.                     if (enemy && Distance3d(pl) > Distance3d(enemy)) { continue; }
  474.  
  475.                     enemy = pl;
  476.                 }
  477.             }
  478.         }
  479.  
  480.         ThinkerIterator Finder;
  481.         //Base mo;
  482.         Actor mo;
  483.  
  484.         // Only target sneakable actors if you are a friendly tank (so that enemy tanks won't target sneakable enemies)
  485.         /*
  486.         if (bFriendly)
  487.         {
  488.             Finder = ThinkerIterator.Create("Base", STAT_DEFAULT - 5);
  489.  
  490.             while ( (mo = Base(Finder.Next())) )
  491.             {
  492.                 if (!IsValidTarget(mo)) { continue; }
  493.                 if (enemy && Distance3d(mo) > Distance3d(enemy)) { continue; }
  494.  
  495.                 enemy = mo;
  496.             }
  497.         }
  498.         */
  499.  
  500.         // A Second iterator to account for the non-sneakables...
  501.         //Finder = ThinkerIterator.Create("Base", STAT_DEFAULT);
  502.         Finder = ThinkerIterator.Create("MT_Actor", STAT_DEFAULT);
  503.  
  504.         /*
  505.         while ( (mo = Base(Finder.Next())) )
  506.         {
  507.             if (!IsValidTarget(mo)) { continue; }
  508.             if (enemy && Distance3d(mo) > Distance3d(enemy)) { continue; }
  509.  
  510.             enemy = mo;
  511.         }
  512.         */
  513.  
  514.         // A third iterator to account for the player followers...
  515.         /*
  516.         Finder = ThinkerIterator.Create("PlayerFollower", STAT_DEFAULT);
  517.         PlayerFollower pf;
  518.  
  519.         while ( (pf = PlayerFollower(Finder.Next())) )
  520.         {
  521.             if (!IsValidTarget(pf)) { continue; }
  522.             if (enemy && Distance3d(pf) > Distance3d(enemy)) { continue; }
  523.  
  524.             enemy = pf;
  525.         }
  526.         */
  527.  
  528.         return enemy;
  529.     }
  530.  
  531.     bool IsValidTarget(Actor mo)
  532.     {
  533.         if (mo is "TankBase" && TankBase(mo).treads) { mo = TankBase(mo).treads; }
  534.  
  535.         if (
  536.             !mo.bIsMonster ||
  537.             mo.health <= 0 ||
  538.             mo.bDormant ||
  539.             !IsHostile(mo) ||
  540.             mo == self ||
  541.             !CheckSight(mo) ||
  542.             Distance3D(mo) > MaxTargetRange //||
  543.             //mo is "StatistBarkeeper" || // Ignore NPCs...
  544.             //mo is "Camp_PrisonerBald" || // Ignore Camp Prisoners...
  545.             //(!bFriendly && Nazi(mo) && Nazi(mo).user_sneakable) // Ignore sneakables who are set via editor property
  546.         ) { return false; }
  547.  
  548.         return true;
  549.     }
  550.  
  551.     virtual void A_TankChase(int flags = 0)
  552.     {
  553.         if (!treads || user_static) { return; }
  554.  
  555.         if (bStandStill) { flags |= CHF_DONTMOVE | CHF_DONTTURN; }
  556.  
  557.         if (!targettimeout && (!target || target.health <= 0 || !CheckLOF(CLOFF_JUMPENEMY | CLOFF_SKIPOBJECT, maxtargetrange) || !IsValidTarget(target))) { target = FindEnemy(); targettimeout = 70; }
  558.  
  559.         if ((!chasetimeout || !wheeled) && !(flags & CHF_DONTTURN))
  560.         {
  561.             double diff = deltaangle(treads.angle, angle);
  562.  
  563.             double turnamt = speed / (wheeled ? 3 : 5);
  564.  
  565.             if (reversetimeout) { turnamt *= -1; }
  566.  
  567.             if (abs(diff) > abs(turnamt) + 2)
  568.             {
  569.                 if (diff < turnamt)
  570.                 {
  571.                     treads.angle -= turnamt;
  572.                     flags |= CHF_DONTMOVE;
  573.                     if (InStateSequence(treads.CurState, treads.SpawnState) && treads.FindState("Move")) { treads.SetStateLabel("Move"); }
  574.                 }
  575.                 else if (diff > -turnamt)
  576.                 {
  577.                     treads.angle += turnamt;
  578.                     flags |= CHF_DONTMOVE;
  579.                     if (InStateSequence(treads.CurState, treads.SpawnState) && treads.FindState("Move")) { treads.SetStateLabel("Move"); }
  580.                 }
  581.                 else
  582.                 {
  583.                     treads.angle = angle;
  584.                 }
  585.             }
  586.  
  587.             if (wheeled) { DoMove(); }
  588.         }
  589.  
  590.         if (!target || !CheckLOF(CLOFF_JUMPENEMY | CLOFF_SKIPOBJECT, maxtargetrange, offsetforward: treads.radius))
  591.         {
  592.             missiletimeout = 70;
  593.             bullettimeout = 35;
  594.         }
  595.  
  596.         Actor cannon = gunspot ? gunspot : (gun ? gun : turret);
  597.         if (turret && !missiletimeout && !turrettimeout && missiletargeted && CheckLOF(CLOFF_JUMPENEMY | CLOFF_SKIPOBJECT, maxtargetrange, offsetforward: treads.radius))
  598.         {
  599.             flags |= CHF_DONTMOVE | CHF_DONTTURN;
  600.  
  601.             DoTurret(cannon);
  602.             missiletargeted = false;
  603.         }
  604.  
  605.         if (turret && !turrettimeout)
  606.         {
  607.             if (target)
  608.             {
  609.                 Actor aimAt = target;
  610.                 if (intercept)
  611.                 {
  612.                     double interceptTime;
  613.                     //interceptTime = ZScriptTools.GetInterceptTime4(Pos, Target.Pos, Target.Vel, GetDefaultSpeed(TurretProjectile));
  614.                     interceptTime += turretdelay; // Account for pre-fire delay
  615.                     Vector3 interceptPos = Target.Pos + Target.Vel * interceptTime;
  616.                     InterceptTarget.SetOrigin(interceptPos, true);
  617.                     aimAt = InterceptTarget;
  618.                 }
  619.                 if (turretanglerange > 0)
  620.                 {
  621.                     double delta = deltaangle(treads.angle, treads.AngleTo(aimAt));
  622.  
  623.                     if (abs(delta) <= turretanglerange) { turret.A_Face(aimAt, turretspeed); A_StartSound("MACHINE_LOOP_3", 8, CHANF_NOSTOP, 1.0); }
  624.                     else { turret.angle = clamp(turret.angle, treads.angle - turretanglerange, treads.angle + turretanglerange); A_StartSound("MACHINE_LOOP_3", 8, CHANF_NOSTOP, 1.0); }
  625.                 }
  626.                 else { turret.A_Face(aimAt, turretspeed); }
  627.  
  628.                 if (int(turret.angle) == int(turret.AngleTo(aimAt)))
  629.                 {
  630.                     A_StopSound(8);
  631.                     if (CheckLOF(CLOFF_JUMPENEMY | CLOFF_SKIPOBJECT, maxtargetrange, offsetforward: treads.radius) && (!gun || (gun.pitch - turret.pitch >= minp && gun.pitch - turret.pitch <= maxp)))
  632.                     {
  633.                         DoTurretTarget(cannon);
  634.                     }
  635.                 }
  636.             }
  637.             else
  638.             {
  639.                 turret.angle = clamp(treads.angle, turret.angle - turretspeed, turret.angle + turretspeed);
  640.             }
  641.         }
  642.  
  643.         if (!bullettimeout && frontgunspot && abs(deltaangle(frontgunspot.angle, frontgunspot.AngleTo(target))) <= 5 && CheckLOF(CLOFF_JUMPENEMY | CLOFF_MUSTBESOLID, maxtargetrange * 2 / 3, offsetforward: Radius * 2))
  644.         {
  645.             DoFrontWeapon(frontgunspot ? frontgunspot : treads);
  646.         }
  647.  
  648.         if (chasetimeout)
  649.         {
  650.             flags |= CHF_DONTMOVE | CHF_DONTTURN;
  651.             A_StopSound(CHAN_7);
  652.         }
  653.         else if (!bStandStill)
  654.         {
  655.             DoMove();
  656.         }
  657.  
  658.         A_Chase(null, null, flags);
  659.     }
  660.  
  661.     void DoMove()
  662.     {
  663.         if (!treads) { return; }
  664.  
  665.         if (reversetimeout <= 0 && speed != default.speed) { speed = Default.speed; }
  666.  
  667.         if (!CheckPosition(pos.xy + RotateVector((reversetimeout ? -32 : 32, 0), treads.angle)))
  668.         {
  669.             if (wheeled || !BlockingMobj)
  670.             {
  671.                 speed *= -0.5;
  672.                 if (speed < 0) { reversetimeout = int((15 + Random(0, 35)) * Default.Speed); }
  673.                 else { reversetimeout = 0; }
  674.             }
  675.         }
  676.  
  677.         A_ChangeVelocity(cos(treads.angle) * speed, sin(treads.angle) * speed, vel.z, CVF_REPLACE);
  678.  
  679.         if (InStateSequence(treads.CurState, treads.SpawnState) && treads.FindState("Move"))
  680.         {
  681.              treads.SetStateLabel("Move");
  682.         }
  683.  
  684.         DoTrample(true);
  685.  
  686.         A_StartSound(movesound, CHAN_7, CHANF_LOOPING, 1.0);
  687.     }
  688.  
  689.     // Fire the main cannon at the target. The main cannon is assumed to be
  690.     // already aiming at its target.
  691.     // origin - The main cannon actor
  692.     virtual void DoTurret(Actor origin)
  693.     {
  694.         missiletargeted = false;
  695.         bullettimeout = 20;
  696.         turrettimeout = 40;
  697.         chasetimeout = 5;
  698.     }
  699.  
  700.     // Fire the turret at the target. The turret is not assumed to be aiming at
  701.     // its target, so you will need to make the turret aim at the target when.
  702.     // you override this method.
  703.     // origin - The turret actor
  704.     virtual void DoTurretTarget(Actor origin)
  705.     {
  706.         missiletargeted = true;
  707.         chasetimeout = 40;
  708.         turrettimeout = turretdelay;
  709.     }
  710.  
  711.     // Fire the front weapon at the target. The front weapon is assumed to be
  712.     // aiming at the target.
  713.     // origin - The front weapon
  714.     virtual void DoFrontWeapon(Actor origin)
  715.     {
  716.         chasetimeout = 20;
  717.         missiletimeout = 70;
  718.     }
  719.  
  720.     override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
  721.     {
  722.         if (mod == 'Trample')
  723.         {
  724.             return "$TRAMPLE";
  725.         }
  726.         else if (mod == 'Rocket')
  727.         {
  728.             return "$TANKSHELL";
  729.         }
  730.  
  731.         return Super.GetObituary(victim, inflictor, mod, playerattack);
  732.     }
  733.  
  734.     override void OnDestroy()
  735.     {
  736.         if (user_static) { level.total_monsters++; }
  737.  
  738.         Super.OnDestroy();
  739.     }
  740. }
  741.  
  742. // Blocker object for the front and back of the tank used to keep actors from walking inside the model
  743. class TankBlocker : Actor
  744. {
  745.     Vector3 spawnoffset;
  746.     Vector3 offset;
  747.     double oldpitch;
  748.     double oldroll;
  749.     double oldangle;
  750.  
  751.     Default
  752.     {
  753.         +CANPASS
  754.         +NOGRAVITY
  755.         +SOLID
  756.         +SHOOTABLE
  757.         +NOBLOOD
  758.         +NODAMAGE
  759.         +NOTAUTOAIMED
  760.         +DONTTHRUST
  761.         Painchance 255;
  762.         Radius 32;
  763.         Height 64;
  764.     }
  765.     States
  766.     {
  767.         Spawn:
  768.             TNT1 A -1;
  769.             Stop;
  770.     }
  771.  
  772.     override void PostBeginPlay()
  773.     {
  774.         if (scale.x == 1.0 && scale.y == 1.0)
  775.         {
  776.             scale.x = Radius * 2;
  777.             scale.y = Height;
  778.         }
  779.  
  780.         if (master)
  781.         {
  782.             spawnoffset = pos - master.pos;
  783.  
  784.             Vector2 temp = RotateVector((spawnoffset.x, spawnoffset.y), -master.angle);
  785.             spawnoffset = (temp.x, temp.y, spawnoffset.z);
  786.  
  787.             offset = spawnoffset;
  788.         }
  789.     }
  790.  
  791.     override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
  792.     {
  793.         if (master && master.master) // Ugh...
  794.         {
  795.             if (source != master.master)
  796.             {
  797.                 master.master.DamageMobj(inflictor, source, damage, mod, flags, angle);  // Inflict the damage on the owner of the tank
  798.             }
  799.         }
  800.  
  801.         return Super.DamageMobj(inflictor, source, damage, mod, flags, angle);
  802.     }
  803.  
  804.     override bool Used(Actor user)
  805.     {
  806.         if (master) { return master.Used(user); }
  807.         return false;
  808.     }
  809.  
  810.     override bool CanCollideWith(Actor other, bool passive)
  811.     {
  812.         if (master && master.master && other != master && other != master.master && !master.master.IsFriend(other))
  813.         {
  814.             other.DamageMobj(self, master.master, int(master.master.vel.length() * 0.625), "Trample");
  815.  
  816.             return master.CanCollideWith(other, passive);
  817.         }
  818.  
  819.         return Super.CanCollideWith(other, passive);
  820.     }
  821.  
  822.  
  823.     override void Tick()
  824.     {
  825.         Super.Tick();
  826.  
  827.         if (master)
  828.         {
  829.             scale.x *= master.scale.x;
  830.             scale.y *= master.scale.y;
  831.  
  832.             SetTag(master.GetTag());
  833.  
  834.             Species = master.Species;
  835.         }
  836.  
  837.         Rotate();
  838.     }
  839.  
  840.     void Rotate()
  841.     {
  842.         Vector2 temp;
  843.  
  844.         // Keep the blocks in the correct position, regardless of pitch/roll of the master actor
  845.         // Obviously not perfect, because the blocks are square, but close enough when you can't see them.
  846.         if (master && spawnoffset != (0, 0, 0)) {
  847.             temp = RotateVector((spawnoffset.y, spawnoffset.z), master.roll);
  848.             offset = (spawnoffset.x, temp.x, temp.y);
  849.  
  850.             temp = RotateVector((offset.x, offset.z), 360 - master.pitch);
  851.             offset = (temp.x, offset.y, temp.y);
  852.  
  853.             temp = RotateVector((offset.x, offset.y), master.angle);
  854.             offset = (temp.x, temp.y, offset.z);
  855.  
  856.             offset.x *= master.scale.x;
  857.             offset.y *= master.scale.x;
  858.             offset.z *= master.scale.y;
  859.  
  860.             SetOrigin(master.pos + offset, true);
  861.  
  862.             angle = master.angle;
  863.  
  864.             oldpitch = master.pitch;
  865.             oldroll = master.roll;
  866.             oldangle = master.angle;
  867.         }
  868.     }
  869. }
  870.  
  871. // Base actor for tank treads
  872. class TankTreadsBase : Actor
  873. {
  874.     Actor turret, gunspot;
  875.     Class<Actor> turretclass, deadclass, debris;
  876.     Class<PowerMorph> morphpowerup;
  877.     int savedhealth;
  878.     int usetimeout;
  879.     double length;
  880.     Vector3 gunspotoffset;
  881.     double gunspotoffsetx, gunspotoffsety, gunspotoffsetz;
  882.  
  883.     Property TurretClass:turretclass;
  884.     Property Length:length;
  885.     Property MorphPowerup:morphpowerup;
  886.     Property DeadActor:deadclass;
  887.     Property DebrisActor:debris;
  888.     Property GunSpotX:gunspotoffsetx;
  889.     Property GunSpotY:gunspotoffsety;
  890.     Property GunSpotZ:gunspotoffsetz;
  891.  
  892.     Default
  893.     {
  894.         Radius 64; // Also used as half-width of model for slope tilt calculations
  895.         Height 64;
  896.         Mass 0x7ffffff;
  897.         Species "NaziTank";
  898.         +CANPASS
  899.         +SOLID
  900.         TankTreadsBase.DeadActor "";
  901.         TankTreadsBase.DebrisActor "";
  902.         //TankTreadsBase.DebrisActor "Debris_Tank";
  903.         TankTreadsBase.Length 96; // Half-length of model for slope calculation
  904.         TankTreadsBase.MorphPowerup "";
  905.         TankTreadsBase.GunSpotX 0;
  906.         TankTreadsBase.GunSpotY 0;
  907.         TankTreadsBase.GunSpotZ 0;
  908.     }
  909.  
  910.     States
  911.     {
  912.         Spawn:
  913.             MDLA A -1;
  914.             Stop;
  915.         Move:
  916.             MDLA AB 5;
  917.             Goto Spawn;
  918.         Death:
  919.             TNT1 A 1 DoDeath();
  920.             Stop;
  921.     }
  922.  
  923.     override void PostBeginPlay()
  924.     {
  925.         bool sp = false;
  926.  
  927.         while (!turret) { [sp, turret] = A_SpawnItemEx(turretclass, 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL); }
  928.         //while (!turret) { [sp, turret] = A_SpawnItemEx(turretclass, 0, 0, Height, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  929.  
  930.         double r = radius / 2;
  931.  
  932.         /*
  933.         A_SpawnItemEx("TankBlocker", length - r, -r, flags: SXF_SETMASTER);
  934.         A_SpawnItemEx("TankBlocker", length - r, r, flags: SXF_SETMASTER);
  935.         A_SpawnItemEx("TankBlocker", -(length - r), -r, flags: SXF_SETMASTER);
  936.         A_SpawnItemEx("TankBlocker", -(length - r), r, flags: SXF_SETMASTER);
  937.         */
  938.  
  939.         gunspotoffset = (gunspotoffsetx, gunspotoffsety, gunspotoffsetz);
  940.         while (!gunspot) { [sp, gunspot] = A_SpawnItemEx("WeaponSpot", gunspotoffset.x, gunspotoffset.y, gunspotoffset.z, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  941.         gunspot.master = self;
  942.  
  943.         //if (morphpowerup) { BoACompass.Add(self, "TANKICO"); }
  944.  
  945.         Super.PostBeginPlay();
  946.     }
  947.  
  948.     override void Tick()
  949.     {
  950.         if (usetimeout) { usetimeout--; }
  951.  
  952.         if (master && master.player)
  953.         {
  954.             double moving = master.player.cmd.forwardmove;
  955.  
  956.             if (moving != 0)
  957.             {
  958.                 double movespeed = master.vel.length();
  959.  
  960.                 int dir = moving > 0 ? 1 : -1; // Trample both forward and backwards, depending on which direction you're moving
  961.                 double dmg = movespeed * 0.625; // Top speed gives ~100 damage
  962.  
  963.                 bool sp;
  964.                 Actor t;
  965.  
  966.                 [sp, t] = A_SpawnItemEx("Trample", scale.x * 128 * dir, 0, height / 2 - 16, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE);
  967.  
  968.                 if (t)
  969.                 {
  970.                     t.target = master; // Set the player as the Trample actor's target, because it uses A_Explode, which defaults to treating the actor's target as the originator (and doesn't damage it)
  971.                     Trample(t).dmg = int(dmg);
  972.                 }
  973.             }
  974.         }
  975.  
  976.         if (turret)
  977.         {
  978.             turret.bSolid = bSolid;
  979.             turret.bInvisible = bInvisible;
  980.         }
  981.  
  982.         Super.Tick();
  983.     }
  984.  
  985.     override bool Used(Actor user)
  986.     {
  987.         if (!morphpowerup) { return false; }
  988.  
  989.         if (usetimeout || bDormant || master && (master.player || master.bIsMonster)) { return false; }
  990.  
  991.         let p = user.player;
  992.  
  993.         if (p)
  994.         {
  995.             bSolid = False;
  996.  
  997.             p.mo.SetOrigin(pos, false);
  998.  
  999.             if (turret) { p.mo.angle = turret.angle; }
  1000.             else { p.mo.angle = angle; }
  1001.  
  1002.             p.mo.tracer = self;
  1003.  
  1004.             user.GiveInventory(morphpowerup, 1);
  1005.             return true;
  1006.         }
  1007.         return false;
  1008.     }
  1009.  
  1010.     override bool CanCollideWith(Actor other, bool passive)
  1011.     {
  1012.         if (master && other != master && !master.IsFriend(other))
  1013.         {
  1014.             other.DamageMobj(self, master, int(0.5 * master.vel.xy.length()), "Trample");
  1015.  
  1016.             return master.CanCollideWith(other, passive);
  1017.         }
  1018.  
  1019.         return Super.CanCollideWith(other, passive);
  1020.     }
  1021.  
  1022.     void DoDeath()
  1023.     {
  1024.         if (turret) { turret.Destroy(); }
  1025.  
  1026.         A_StartSound("weapons/explode", CHAN_AUTO, 0, 1.0, ATTN_IDLE);
  1027.  
  1028.         Actor dead = Spawn(deadclass, pos);
  1029.  
  1030.         dead.angle = angle;
  1031.         dead.pitch = pitch;
  1032.         dead.roll = roll;
  1033.  
  1034.         A_RemoveChildren(true, RMVF_EVERYTHING);
  1035.  
  1036.         for (int i = 0; i < 20; i++)
  1037.         {
  1038.             //A_SpawnItemEx(debris, random(88, 96), random(88, 96), random(88, 112), random(1, 3), random(1, 3), random(1, 3), random(0, 360), SXF_CLIENTSIDE);
  1039.             //A_SpawnItemEx("Debris_GlassShard_Large", random(88, 96), random(88, 96), random(96, 128), random(1, 3), random(1, 3), random(1, 3), random(0, 360), SXF_CLIENTSIDE);
  1040.         }
  1041.        
  1042.         //A_SpawnItemEx("Nuke", 0, 0, 5, 0, 0, 0, 0, SXF_TRANSFERPOINTERS|SXF_NOCHECKPOSITION);
  1043.         //A_SpawnItemEx("KaBoomer", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPOINTERS);
  1044.         //A_SpawnItemEx("GeneralExplosion_Large", 56, 0, 32);
  1045.     }      
  1046. }
  1047.  
  1048. class PairedTire : Actor
  1049. {
  1050.     Vector3 spawnoffset;
  1051.     bool turning;
  1052.     int rotated;
  1053.  
  1054.     Default
  1055.     {
  1056.         +NOGRAVITY
  1057.         +NOINTERACTION
  1058.     }
  1059.  
  1060.     States
  1061.     {
  1062.         Spawn:
  1063.             MDLA A -1;
  1064.             Stop;
  1065.     }
  1066.  
  1067.     override void PostBeginPlay()
  1068.     {
  1069.         if (!master) { Destroy(); return; }
  1070.  
  1071.         spawnoffset = pos - master.pos;
  1072.  
  1073.         spawnoffset = (RotateVector(spawnoffset.xy, -master.angle), spawnoffset.z);
  1074.  
  1075.         bSolid = false;
  1076.         Species = master.Species;
  1077.         bFriendly = master.bFriendly;
  1078.         angle = master.angle + rotated;
  1079.     }
  1080.  
  1081.     override bool CanCollideWith(Actor other, bool passive)
  1082.     {
  1083.         if (master && other != master) { return master.CanCollideWith(other, passive); }
  1084.  
  1085.         return Super.CanCollideWith(other, passive);
  1086.     }
  1087.  
  1088.     override void Tick()
  1089.     {
  1090.         Super.Tick();
  1091.  
  1092.         if (globalfreeze || level.Frozen) { return; }
  1093.  
  1094.         scale.x = default.scale.x / level.pixelstretch; // Forceably correct skewed models introduced by pixel ratio settings
  1095.  
  1096.         if (master)
  1097.         {
  1098.             Vector3 offset;
  1099.  
  1100.             if (spawnoffset != (0, 0, 0))
  1101.             {
  1102.                 Vector2 temp = RotateVector((spawnoffset.y, spawnoffset.z), master.roll);
  1103.                 offset = (spawnoffset.x, temp.x, temp.y);
  1104.  
  1105.                 temp = RotateVector((offset.x, offset.z), 360 - master.pitch);
  1106.                 offset = (temp.x, offset.y, temp.y);
  1107.  
  1108.                 temp = RotateVector((offset.x, offset.y), master.angle);
  1109.                 offset = (temp.x, temp.y, offset.z);
  1110.             }
  1111.  
  1112.             SetOrigin(master.pos + offset, true);
  1113.  
  1114.             if (master.InStateSequence(master.CurState, master.FindState("Move")))
  1115.             {
  1116.                 let base = master.master;
  1117.                 double speed = base ? base.speed : 1;
  1118.  
  1119.                 speed *= 2;
  1120.  
  1121.                 if (rotated >= 180) { speed *= -1; }
  1122.  
  1123.                 pitch += speed;
  1124.             }
  1125.  
  1126.             if (master.master && turning)
  1127.             {
  1128.                 let base = master.master;
  1129.                 double dir = base ? base.speed / abs(base.speed) : 1;
  1130.  
  1131.                 double delta = deltaangle(master.angle, base.angle);
  1132.  
  1133.                 double destangle = clamp(master.angle + rotated + delta * dir, master.angle - 30 + rotated, master.angle + 30 + rotated);
  1134.  
  1135.                 angle = clamp(destangle, angle - 5, angle + 5);
  1136.             }
  1137.             else { angle = master.angle + rotated; }
  1138.         }
  1139.         else { Destroy(); }
  1140.     }
  1141. }
  1142.  
  1143. // Base actor for tank turret
  1144. class TankTurretBase : Actor
  1145. {
  1146.     Actor gun, gunspot;
  1147.     Class<Actor> gunclass;
  1148.     Vector3 spawnoffset;
  1149.     Vector3 offset;
  1150.     double gunoffsetx, gunoffsetz, turretoffsetx;
  1151.     Vector3 gunspotoffset;
  1152.     double gunspotoffsetx, gunspotoffsety, gunspotoffsetz;
  1153.  
  1154.     Property TurretOffsetX:turretoffsetx;
  1155.     Property GunClass:gunclass;
  1156.     Property GunOffsetX:gunoffsetx;
  1157.     Property GunOffsetZ:gunoffsetz;
  1158.     Property GunSpotX:gunspotoffsetx;
  1159.     Property GunSpotY:gunspotoffsety;
  1160.     Property GunSpotZ:gunspotoffsetz;
  1161.  
  1162.     Default
  1163.     {
  1164.         Height 0;
  1165.         Species "NaziTank";
  1166.         +DONTSPLASH
  1167.         +NOGRAVITY
  1168.         +SOLID
  1169.         TankTurretBase.GunOffsetX 32;
  1170.         TankTurretBase.GunOffsetZ 18;
  1171.         TankTurretBase.GunSpotX 0;
  1172.         TankTurretBase.GunSpotY 0;
  1173.         TankTurretBase.GunSpotZ 0;
  1174.     }
  1175.  
  1176.     States
  1177.     {
  1178.         Spawn:
  1179.             MDLA A -1;
  1180.             Stop;
  1181.     }
  1182.  
  1183.     override void PostBeginPlay()
  1184.     {
  1185.         bool sp = false;
  1186.  
  1187.         if (gunclass)
  1188.         {
  1189.             while (!gun) { [sp, gun] = A_SpawnItemEx(gunclass, gunoffsetx, 0, gunoffsetz, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  1190.             gun.Species = Species;
  1191.  
  1192.             spawnoffset = gun.pos - pos;
  1193.  
  1194.             spawnoffset = (RotateVector(spawnoffset.xy, -angle), spawnoffset.z);
  1195.         }
  1196.  
  1197.         gunspotoffset = (gunspotoffsetx, gunspotoffsety, gunspotoffsetz);
  1198.         while (!gunspot) { [sp, gunspot] = A_SpawnItemEx("WeaponSpot", gunspotoffset.x, gunspotoffset.y, gunspotoffset.z, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  1199.         if (gun) { gunspot.master = gun; }
  1200.         else { gunspot.master = self; }
  1201.  
  1202.         Super.PostBeginPlay();
  1203.     }
  1204.  
  1205.     override bool CanCollideWith(Actor other, bool passive)
  1206.     {
  1207.         if (master && other != master) { master.CanCollideWith(other, passive); }
  1208.  
  1209.         return true;
  1210.     }
  1211.  
  1212.     override void Tick()
  1213.     {
  1214.         if (gun)
  1215.         {
  1216.             gun.angle = angle;
  1217.  
  1218.             Vector2 temp = RotateVector((spawnoffset.y, spawnoffset.z), roll);
  1219.             offset = (spawnoffset.x, temp.x, temp.y);
  1220.  
  1221.             temp = RotateVector((offset.x, offset.z), 360 - pitch);
  1222.             offset = (temp.x, offset.y, temp.y);
  1223.  
  1224.             temp = RotateVector((offset.x, offset.y), angle);
  1225.             offset = (temp.x, temp.y, offset.z);
  1226.  
  1227.             gun.SetOrigin(self.pos + offset, true);
  1228.  
  1229.             gun.bSolid = bSolid;
  1230.             gun.bInvisible = bInvisible;
  1231.         }
  1232.  
  1233.         Actor.Tick();
  1234.     }
  1235.  
  1236.     override void OnDestroy()
  1237.     {
  1238.         if (gun) { gun.Destroy(); }
  1239.     }
  1240. }
  1241.  
  1242. class TankCannonBase : Actor
  1243. {
  1244.     Default
  1245.     {
  1246.         +DONTSPLASH
  1247.         +NOGRAVITY
  1248.         Species "Tank";
  1249.     }
  1250.  
  1251.     States
  1252.     {
  1253.         Spawn:
  1254.             MDLA A -1;
  1255.             Stop;
  1256.     }
  1257. }
  1258.  
  1259. class TankDeadBase : Actor
  1260. {
  1261.     Default
  1262.     {
  1263.         Radius 88;
  1264.         Height 96;
  1265.         Mass 0x7ffffff;
  1266.         +FLOORCLIP
  1267.         +NOBLOOD
  1268.         +NOBLOODDECALS
  1269.         +NODAMAGE
  1270.         +NOTAUTOAIMED
  1271.         +SHOOTABLE
  1272.         +SOLID
  1273.         BloodType "TankSpark";
  1274.     }
  1275.    
  1276.     States
  1277.     {
  1278.     Spawn:
  1279.         MDLA A 12; //A_SpawnProjectile("TankSmoke",58,0,random(0,360),2,random(70,130));
  1280.         Loop;
  1281.     }
  1282. }
  1283.  
  1284. class ActorPositionable : Actor
  1285. {
  1286.     enum RotationFlags
  1287.     {
  1288.         ROT_MatchAngle = 1,
  1289.         ROT_MatchPitch = 2,
  1290.         ROT_MatchRoll = 4,
  1291.     };
  1292.  
  1293.     Vector3 spawnoffset;
  1294.     Vector3 offset;
  1295.  
  1296.     override void PostBeginPlay()
  1297.     {
  1298.         if (!master) { Destroy(); return; }
  1299.  
  1300.         spawnoffset = pos - master.pos;
  1301.  
  1302.         Vector2 temp = RotateVector((spawnoffset.x, spawnoffset.y), -master.angle);
  1303.         spawnoffset = (temp.x, temp.y, spawnoffset.z);
  1304.  
  1305.         Species = master.Species;
  1306.  
  1307.         Super.PostBeginPlay();
  1308.     }
  1309.  
  1310.     void RotateWithMaster(int flags = ROT_MatchAngle | ROT_MatchPitch | ROT_MatchRoll)
  1311.     {
  1312.         Vector2 temp;
  1313.  
  1314.         // Keep the actor in the correct position, regardless of pitch/roll of the master actor
  1315.         if (master)
  1316.         {
  1317.             if (spawnoffset != (0, 0, 0))
  1318.             {
  1319.                 temp = RotateVector((spawnoffset.y, spawnoffset.z), master.roll);
  1320.                 offset = (spawnoffset.x, temp.x, temp.y);
  1321.  
  1322.                 temp = RotateVector((offset.x, offset.z), 360 - master.pitch);
  1323.                 offset = (temp.x, offset.y, temp.y);
  1324.  
  1325.                 temp = RotateVector((offset.x, offset.y), master.angle);
  1326.                 offset = (temp.x, temp.y, offset.z);
  1327.             }
  1328.  
  1329.             SetOrigin(master.pos + offset, true);
  1330.  
  1331.             if (flags & ROT_MatchAngle) { angle = master.angle; }
  1332.  
  1333.             double delta = deltaangle(master.angle, angle);
  1334.  
  1335.             if (flags & ROT_MatchPitch) { pitch = master.roll * sin(-delta) + master.pitch * cos(delta); }
  1336.             if (flags & ROT_MatchRoll) { roll = master.roll * cos(-delta) + master.pitch * sin(delta); }
  1337.         }
  1338.     }
  1339.  
  1340.     override void Tick()
  1341.     {
  1342.         RotateWithMaster();
  1343.  
  1344.         Super.Tick();
  1345.     }
  1346. }
  1347.  
  1348. class WeaponSpot : ActorPositionable
  1349. {
  1350.     Default
  1351.     {
  1352.         Height 2;
  1353.         Radius 1;
  1354.         +NOBLOCKMAP
  1355.         +NOGRAVITY
  1356.         +NOINTERACTION
  1357.         +INVISIBLE
  1358.     }
  1359.  
  1360.     States
  1361.     {
  1362.         Spawn:
  1363.             AMRK A -1;
  1364.             Stop;
  1365.     }
  1366.  
  1367.     override void PostBeginPlay()
  1368.     {
  1369.         if (master) { Species = master.Species; }
  1370.  
  1371.         Super.PostBeginPlay();
  1372.     }
  1373. }
  1374. //------------------------------------------------------------------------------
  1375. //TEMPORARY
  1376. //------------------------------------------------------------------------------
  1377. // Sherman Tank components (used by both FriendlySherman and ShermanPlayer)
  1378. class US_Sherman : TankTreadsBase
  1379. {
  1380.     Default
  1381.     {
  1382.         Height 20;
  1383.         Radius 16;
  1384.         MaxStepHeight 1;
  1385.         MaxDropoffHeight 1;
  1386.         TankTreadsBase.DeadActor "US_ShermanDead";
  1387.         TankTreadsBase.DebrisActor "";
  1388.         //TankTreadsBase.DebrisActor "Debris_Tank2";
  1389.         TankTreadsBase.MorphPowerup "Sherman";
  1390.         TankTreadsBase.TurretClass "US_ShermanTurret";
  1391.         TankTreadsBase.Length 32;
  1392.         TankTreadsBase.GunSpotX 0;
  1393.         TankTreadsBase.GunSpotY 0;
  1394.         TankTreadsBase.GunSpotZ 0;
  1395.         +NOGRAVITY
  1396.     }
  1397. }
  1398.  
  1399. class US_ShermanTurret : TankTurretBase
  1400. {
  1401.     Actor gunspot2;
  1402.  
  1403.     Default
  1404.     {
  1405.         TankTurretBase.GunClass "US_ShermanCannon";
  1406.         TankTurretBase.GunSpotX 0;
  1407.         TankTurretBase.GunSpotY 0;
  1408.         TankTurretBase.GunSpotZ 0;
  1409.         TankTurretBase.TurretOffsetX 0;
  1410.         Height 20;
  1411.         Radius 16;
  1412.         +NOGRAVITY
  1413.     }
  1414.  
  1415.     override void PostBeginPlay()
  1416.     {
  1417.         bool sp = false;
  1418.  
  1419.         Vector3 gunspot2offset = (0, 0, 0);
  1420.         //Vector3 gunspot2offset = (16, 20, 18);
  1421.         while (!gunspot2) { [sp, gunspot2] = A_SpawnItemEx("WeaponSpot", gunspot2offset.x, gunspot2offset.y, gunspot2offset.z, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  1422.         gunspot2.master = self;
  1423.  
  1424.         Super.PostBeginPlay();
  1425.     }
  1426. }
  1427.  
  1428. class US_ShermanCannon : TankCannonBase {}
  1429. class US_ShermanDead : TankDeadBase {}
  1430.  
  1431. // Same as NebNuke, but does not spawn NebBoom and cause explosion damage; moved from panzers.txt --N00b
  1432. class NebNukeHarmless: Actor
  1433. {
  1434.     Actor a, b;
  1435.     default
  1436.     {
  1437.         +NOBLOCKMAP
  1438.         +NOGRAVITY
  1439.         +NOINTERACTION
  1440.         Radius 0;
  1441.         Height 0;
  1442.     }
  1443.     States
  1444.     {
  1445.     Spawn:
  1446.         TNT1 A 0 Radius_Quake(3,8,0,24,0);
  1447.         /*
  1448.         "####" A 0 { a = Spawn("NebFlare", Pos, ALLOW_REPLACE); a.vel += self.vel; }
  1449.         "####" A 3
  1450.         {
  1451.             a = Spawn("NebFloor", Pos, ALLOW_REPLACE); b = Spawn("NebSmokeFloor", Pos, ALLOW_REPLACE);
  1452.             if (a) a.vel += self.vel; if (b) b.vel += self.vel;
  1453.         }
  1454.         "####" A 6 {
  1455.             a = Spawn("NebSmokePillar", Pos, ALLOW_REPLACE); b = Spawn("NebSmokeMushroom", Pos, ALLOW_REPLACE);
  1456.             if (a) a.vel += (self.vel + (0.0,0.0,2.0)); if (b) b.vel += self.vel;
  1457.         }
  1458.         */
  1459.         Stop;
  1460.     }
  1461. }
  1462.  
  1463. Class BoAFindHitPointTracer : LineTracer
  1464. {
  1465.     Name skipspecies;
  1466.     Actor skipactor;
  1467.  
  1468.     override ETraceStatus TraceCallback() // Doesn't handle 3d Floors :-/
  1469.     {
  1470.         if (Results.HitType == TRACE_HitActor)
  1471.         {
  1472.             if (
  1473.                 Results.HitActor != skipactor && // Skip the player
  1474.                 !(Results.HitActor is "ShermanPlayer") &&
  1475.                 (!Results.HitActor.master || Results.HitActor.master != skipactor) && // And any children
  1476.                 Results.HitActor.species != skipspecies && // And any of the skipped species
  1477.                 (Results.HitActor.bSolid || Results.HitActor.bShootable) // And only return shootable actors
  1478.             ) { return TRACE_Continue; } // Fall through, but remember the actor that you hit
  1479.  
  1480.             return TRACE_Skip;
  1481.         }
  1482.         else if (Results.HitType == TRACE_HitFloor || Results.HitType == TRACE_HitCeiling)
  1483.         {
  1484.             return TRACE_Stop;
  1485.         }
  1486.         else if (Results.HitType == TRACE_HitWall)
  1487.         {
  1488.             if (Results.HitLine.flags & (Line.ML_BLOCKING | Line.ML_BLOCKEVERYTHING)) { return TRACE_Stop; }
  1489.             if (Results.HitTexture)
  1490.             {
  1491.                 if (Results.Tier != TIER_Middle || Results.HitLine.flags & Line.ML_3DMIDTEX) // 3D Midtex check still isn't perfect...
  1492.                 {
  1493.                     return TRACE_Stop;
  1494.                 }
  1495.                 return TRACE_Skip;
  1496.             }
  1497.             return TRACE_Skip;
  1498.         }
  1499.  
  1500.         return TRACE_Stop;
  1501.     }
  1502. }
  1503.  
  1504. class Nothing : Actor
  1505. {
  1506.     Default
  1507.     {
  1508.         +NOBLOCKMAP
  1509.     }
  1510.  
  1511.     States
  1512.     {
  1513.         Spawn:
  1514.             TNT1 A 1;
  1515.             Stop;
  1516.     }
  1517. }
  1518.  
  1519. class Sherman : PowerMorph
  1520. {
  1521.     int armor;
  1522.     int premorphhealth;
  1523.     double savepercent;
  1524.  
  1525.     Default
  1526.     {
  1527.         PowerMorph.MorphStyle MRF_LOSEACTUALWEAPON | MRF_NEWTIDBEHAVIOUR | MRF_UNDOBYDEATHSAVES;
  1528.         PowerMorph.MorphFlash "Nothing"; // Why isn't there an option to NOT spawn fog at all?
  1529.         PowerMorph.UnMorphFlash "Nothing";
  1530.         PowerMorph.PlayerClass "ShermanPlayer";
  1531.         Powerup.Duration 0x7FFFFFFF;
  1532.     }
  1533.  
  1534.     override void InitEffect()
  1535.     {
  1536.         if (owner && owner.player)
  1537.         {
  1538.             owner.player.cheats |= CF_CHASECAM;
  1539.  
  1540.             // Save the standard Doom-style armor values.  Doesn't support Hexen armor.
  1541.             BasicArmor a = BasicArmor(owner.FindInventory("BasicArmor"));
  1542.             if (a)
  1543.             {
  1544.                 armor = a.Amount;
  1545.                 savepercent = a.SavePercent;
  1546.             }
  1547.  
  1548.             premorphhealth = owner.health;
  1549.         }
  1550.  
  1551.         Super.InitEffect();
  1552.     }
  1553.  
  1554.     override void EndEffect()
  1555.     {
  1556.         if (MorphedPlayer && MorphedPlayer.mo)
  1557.         {
  1558.             let tank = ShermanPlayer(MorphedPlayer.mo);
  1559.  
  1560.             if (tank)
  1561.             {
  1562.                 if  (tank.treads)
  1563.                 {
  1564.                     tank.treads.bSolid = true;
  1565.                     tank.treads.master = null;
  1566.                     if (US_Sherman(tank.treads))
  1567.                     {
  1568.                         US_Sherman(tank.treads).savedhealth = tank.health;
  1569.                         US_Sherman(tank.treads).usetimeout = 35;
  1570.                     }
  1571.                 }
  1572.                 if  (tank.turretcamera) { tank.turretcamera.Destroy(); }
  1573.                 if  (tank.povcamera) { tank.povcamera.Destroy(); }
  1574.                 MorphedPlayer.cheats &= ~CF_CHASECAM;
  1575.  
  1576.                 // Restore pitch clamping, since this doesn't get reset otherwise
  1577.                 MorphedPlayer.MinPitch = -90;
  1578.                 MorphedPlayer.MaxPitch = 90;
  1579.  
  1580.                 if (tank.health <= 0) { tank.treads.SetStateLabel("Death"); }
  1581.  
  1582.                 if (tank.turret) { MorphedPlayer.mo.SetOrigin(tank.turret.pos + (0, 0, tank.turret.height), false); } // Dump the player on top of the turret
  1583.                 else { MorphedPlayer.mo.SetOrigin(tank.pos + (0, 0, tank.height), false); } // Dump the player on top of the body of the tank
  1584.  
  1585.                 // Restore armor amount and savepercent
  1586.                 MorphedPlayer.mo.SetInventory("BasicArmor", armor);
  1587.  
  1588.                 BasicArmor a = BasicArmor(MorphedPlayer.mo.FindInventory("BasicArmor"));
  1589.                 if (a) { a.SavePercent = savepercent; }
  1590.             }
  1591.         }
  1592.  
  1593.         Super.EndEffect();
  1594.     }
  1595. }
  1596.  
  1597.  
  1598. //------------------------------------------------------------------------------
  1599. //NEW MAIN
  1600. //------------------------------------------------------------------------------
  1601. Class MT_BaseTank_old : PlayerPawn
  1602. {
  1603. const chassis_spawnflag = SXF_ABSOLUTEVELOCITY + SXF_ABSOLUTEANGLE + SXF_TRANSFERPITCH;
  1604. const t_turnrate = 45; //tics turnrate
  1605. const t_turnrate_move = 50; //tics turn rate + accel
  1606. const turnrate = 4;
  1607. const turnrate_move = 3;
  1608. const accelerate = 1;
  1609. const reverse = 1;
  1610. int user_chassisangle;
  1611. Default
  1612. {
  1613. player.DisplayName "BASE TANK";
  1614. player.viewheight 25;
  1615. player.attackzoffset 13;
  1616. player.forwardmove 0.0;
  1617. player.sidemove 0;
  1618. player.jumpz 0;
  1619. player.face "TKF";
  1620. player.maxhealth 1600;
  1621. health 1600;
  1622. Speed 0.0;
  1623. Species "TankPlayer";
  1624. Player.SoundClass "mt_vehicle";
  1625. MaxStepHeight 24;
  1626. Radius 16;
  1627. Height 20;
  1628. Mass 50000;
  1629. PainChance 128;
  1630. BloodType "TankBlood", "TankBlood", "TankBlood";
  1631. -FLOORCLIP;
  1632. +FIXMAPTHINGPOS;
  1633. +DONTRIP;
  1634. Player.ColorRange 112, 127;
  1635. //Internal stuff, very important
  1636. Player.StartItem "MT_ClassToken"              ,1;
  1637. Player.StartItem "MHTA1_Token"                ,1;
  1638. Player.StartItem "MTU_SupplyBox_2"            ,1;
  1639. Player.StartItem "MT_Subgun_Slot"             ,1;
  1640. Player.StartItem "MT_AmmoSwitcher"            ,1;
  1641. Player.StartItem "MT_AmmoSwitcher_Reverse"    ,1;
  1642. Player.StartItem "MT_SubgunSwitcher"          ,1;
  1643. Player.Startitem "MT_SubgunSwitcher_Reverse"  ,1;
  1644. Player.Startitem "MT_Searchlight"             ,1;
  1645. Player.StartItem "MT_Nightvision_MK1"         ,1;
  1646. Player.StartItem "MT_ChaseCamera_MK1"         ,1;
  1647. Player.StartItem "MT_PeriscopeItem"           ,1;
  1648. //Lockers
  1649. //Weapons
  1650. Player.StartItem "MT_105mmCannon"           ,1;
  1651. Player.StartItem "Multi_Purpose_Device"       ,1;
  1652. //Ammo
  1653. Player.Startitem "MT_75x500mmHE"                ,20;
  1654. Player.Startitem "MT_75x500mmAP"                ,40;
  1655. Player.Startitem "A_7u62x54mmR"                 ,2000;
  1656. //Items
  1657. //  Player.startitem "Item_GrenadePod_Smoke"        ,8
  1658. Player.startitem "Item_GrenadePod_SSS"          ,1;
  1659. Player.startitem "Item_GrenadePod_Illuminating" ,2;
  1660. Player.startitem "MT_Item_GrenadePod_Flare"     ,30;
  1661. Player.Startitem "MT_GrenadePod_Explosive"      ,20;
  1662.  
  1663. //MT Damage types
  1664. DamageFactor "PiercingExplosive" ,0.75;
  1665. DamageFactor "Piercing"          ,0.8;
  1666. DamageFactor "Frag"              ,0.05;
  1667. DamageFactor "Bullet"            ,0.15;
  1668. DamageFactor "Acid"              ,0.5;
  1669. DamageFactor "Chemical"          ,0.5;
  1670. DamageFactor "Build"             ,0;
  1671. //Other Damage types
  1672. DamageFactor "Normal"            ,0.8;
  1673. DamageFactor "Extreme"           ,0.8;
  1674. DamageFactor "Drowning"          ,0.0;
  1675. DamageFactor "Falling"           ,0.1;
  1676. DamageFactor "Ice"               ,0.2;
  1677. DamageFactor "Fire"              ,0.1;
  1678. Damagefactor "stomp"             ,0;
  1679. DamageFactor "Energy"            ,0.85;
  1680. DamageFactor "Radiation"         ,0.0;
  1681. DamageFactor "Nuclear"           ,0.9;
  1682. DamageFactor "Explosive"         ,0.25;
  1683. DamageFactor "Buckshot"          ,0;
  1684. DamageFactor "Reclaim"           ,0.9;
  1685. DamageFactor "Poison"            ,0;
  1686. DamageFactor "PoisonCloud"       ,0;
  1687. DamageFactor "Electric"          ,0.75;
  1688. }
  1689.     States
  1690.     {
  1691.     Pain:
  1692.         "####" A 0;
  1693.         "####" A 1 A_Pain;
  1694.         Goto CheckIfStillMoves;
  1695.  
  1696.     Blank:
  1697.         TNT1 A 0;
  1698.         Goto Stay;
  1699.  
  1700.     //
  1701.     //INITALIZATION
  1702.     //
  1703.     Spawn:
  1704.         TNK1 A 0;
  1705.         TNK1 A 0; //A_SetUserVar("user_chassisangle", CallACS("MTACS_Chassis_Init"))
  1706.         TNK1 A 0 ThrustThingZ(0, 100, -1, 1);
  1707.         TNK1 A 0 A_PlaySound("TankEngine/Start", 0, 0.8, 0, ATTN_IDLE);
  1708.         TNK1 A 0
  1709.         {
  1710.             if ( GetCVAR("mtccvar_startup_noise") == 1 )
  1711.             {
  1712.                 A_PlaySound("GCrew/MissionStart");
  1713.             }
  1714.         }
  1715.         TNK1 A 1;
  1716.     Spawn2:
  1717.         TNK1 A 0; //A_SetUserVar("user_chassisangle", CallACS("MTACS_Chassis_Init"))
  1718.         TNK1 A 0; //A_SetUserVar("user_chassisangle", CallACS("MT_SetVehicleRotation"))
  1719.         TNK1 A 0 {int user_chassisangle = 0;}
  1720.         //TNK1 A 0 A_SpawnItemEx ("MT_HeavyTank_Chassis" , 0, 0, 0, 0, 0, 0, user_chassisangle, chassis_spawnflag);
  1721.         TNK1 A 1;
  1722.         Goto Stay;
  1723.    
  1724.     DoNothing:
  1725.         "####" A 1;
  1726.         Goto Stay;
  1727.        
  1728.     //
  1729.     //CHANGE MODEL/VISUAL
  1730.     //
  1731.  
  1732.  
  1733.     //
  1734.     //DRIVE
  1735.     //
  1736.     CheckIfStillMoves:
  1737.         "####" A 0;
  1738.         "####" A 0 A_JumpIfInventory("ImCrafting", 1, "DoNothing");
  1739.         "####" A 0 A_JumpIfInventory("Accelerate", 1, "Accelerate");
  1740.         "####" A 0 A_JumpIfInventory("Reverse", 1, "Reverse");
  1741.         "####" A 0 A_JumpIfInventory("TurnLeft", 1, "TurnLeft");
  1742.         "####" A 0 A_JumpIfInventory("TurnRight", 1, "TurnRight");
  1743.         "####" AAA 1;
  1744.         "####" A 0 A_StopSound(5);
  1745.         "####" A 0 A_PlaySound("Treads/Mid", 2);
  1746.         Goto Stay; 
  1747.  
  1748.     CheckIfStillMoves2:
  1749.         "####" A 0;
  1750.         "####" A 0 A_JumpIfInventory("ImCrafting", 1, "DoNothing");
  1751.         "####" A 0 A_JumpIfInventory("Accelerate", 1, "Accelerate");
  1752.         "####" A 0 A_JumpIfInventory("Reverse", 1, "Reverse");
  1753.         "####" A 0 A_JumpIfInventory("TurnLeft", 1, "TurnLeft");
  1754.         "####" A 0 A_JumpIfInventory("TurnRight", 1, "TurnRight");
  1755.         "####" AAA 1;
  1756.         "####" A 0 A_StopSound(5);
  1757.         "####" A 0 A_PlaySound("Treads/Left", 2);
  1758.         Goto Stay; 
  1759.    
  1760.     Stay:
  1761.         "####" A 0 A_JumpIfInventory("Reverse", 1, "Reverse");
  1762.         "####" A 0 A_JumpIfInventory("Accelerate", 1, "Accelerate");
  1763.         "####" A 0 A_JumpIfInventory("TurnLeft", 1, "TurnLeft");
  1764.         "####" A 0 A_JumpIfInventory("TurnRight", 1, "TurnRight");
  1765.         "####" A 1;
  1766.         Goto Stay;
  1767.        
  1768.     Accelerate:
  1769.         "####" A 0 {user_chassisangle = CallACS("MT_GetVehicleRotation");}
  1770.         "####" A 0 A_PlaySound("Treads/Forward", 5, 1, 1);
  1771.         "####" A 0 A_JumpIf(vel.z < 0, "Falling");
  1772.         "####" A 1;
  1773.         Goto CheckIfStillMoves;
  1774.  
  1775.     Reverse:
  1776.         "####" A 0 {user_chassisangle = CallACS("MT_GetVehicleRotation");}
  1777.         "####" A 0 A_PlaySound("Treads/Backward", 5, 1, 1);
  1778.         "####" A 0 A_JumpIf(vel.z < 0, "Falling");
  1779.         "####" A 1;
  1780.         Goto CheckIfStillMoves;
  1781.        
  1782.     TurnRight:
  1783.         "####" A 0 {user_chassisangle = CallACS("MT_GetVehicleRotation");}
  1784.         "####" A 0 A_PlaySound("Treads/Left", 5, 1, 1);
  1785.         "####" A 0 A_TakeInventory("TurnLeft", 999);
  1786.         "####" A 0 A_TakeInventory("TurnRight", 999);
  1787.         "####" A 0 A_JumpIf(vel.z < 0, "Falling");
  1788.         "####" A 1;
  1789.         Goto CheckIfStillMoves2;
  1790.  
  1791.     TurnLeft:      
  1792.         "####" A 0 {user_chassisangle = CallACS("MT_GetVehicleRotation");}
  1793.         "####" A 0 A_PlaySound("Treads/Right", 5, 1, 1);
  1794.         "####" A 0 A_TakeInventory("TurnLeft", 999);
  1795.         "####" A 0 A_TakeInventory("TurnRight", 999);
  1796.         "####" A 0 A_JumpIf(vel.z < 0, "Falling");
  1797.         "####" A 1;
  1798.         Goto CheckIfStillMoves2;
  1799.  
  1800.  
  1801.     //
  1802.     //FALLING
  1803.     //         
  1804.     Falling:
  1805.         "####" A 0;
  1806.         "####" A 0 A_CheckFloor("CheckIfStillMoves");
  1807.    
  1808.     FallingForReal:
  1809.         "####" A 0;
  1810.         "####" A 1;
  1811.         "####" A 0 A_JumpIF(vel.z == 0, "CrashIntoGround");
  1812.         "####" A 0 A_GiveInventory("VehicleFallingCount", 1);
  1813.         Loop;
  1814.        
  1815.     CrashIntoGround:
  1816.         "####" A 0;
  1817.         "####" A 0 A_JumpIfinventory("VehicleFallingCount", 10, "CrashIntoGroundViolently");
  1818.         "####" A 1;
  1819.         //"####" A 0 A_SpawnItem("CrashTankIntotheGround1");
  1820.         //"####" A 0 A_SpawnItem("CrashTankIntotheGround2");
  1821.         "####" A 0; //A_SpawnItemEx ("LargeMassWaterImpact", 0, 0, -10)
  1822.         "####" A 0; //Radius_Quake(1, 6, 0, 4, 0)
  1823.         "####" A 0 A_PlaySound("V_LightCrash", 2);
  1824.         "####" A 0 A_TakeInventory("VehicleFallingCount", 9999);
  1825.         Goto Stay;
  1826.        
  1827.     CrashIntoGroundViolently:
  1828.         "####" A 0;
  1829.         "####" A 1;
  1830.         //"####" A 0 A_SpawnItem("CrashTankIntotheGround1");
  1831.         //"####" A 0 A_SpawnItem("CrashTankIntotheGround2");
  1832.        
  1833.         "####" A 0; //A_SpawnItemEx ("ExplosionSplashSpawner", 0, 0, -10)
  1834.        
  1835.         "####" A 0; //Radius_Quake(8, 24, 0, 4, 0)
  1836.         "####" A 0 A_PlaySound("V_LightCrash", 1);
  1837.         "####" A 0 A_PlaySound("V_MediumCrash", 2);
  1838.         "####" AAAA 0; //A_CustomMissile ("ExplosionSmoke", 0, 0, random (0, 360), 2, random (0, 360))
  1839.         "####" A 0 A_TakeInventory("VehicleFallingCount", 9999);
  1840.         "####" A 0 A_SetPitch(-8.0 + pitch);
  1841.         "####" A 1;
  1842.         "####" A 0 A_SetPitch(4.0 + pitch);
  1843.         "####" A 1;
  1844.         "####" A 0 A_SetPitch(4.0 + pitch);
  1845.         Goto Stay;     
  1846.     //
  1847.     //KILLED/DESTROYED
  1848.     // 
  1849.     Death: 
  1850.     XDeath:
  1851.         "####" A 1;
  1852.         "####" A 0; //A_SpawnItem("BigExplosion1112")
  1853.         "####" A 0 A_PlaySound("weapons/explode");
  1854.         "####" A 0 A_PlaySound("EXPLOSIO", 3);
  1855.         "####" A 0 A_Scream;
  1856.         "####" A 0 A_NoBlocking;
  1857.         "####" AAAAAA 0; // A_CustomMissile ("MetalTrashParticle1", 96, 0, random (0, 360), 2, random (0, 180))
  1858.         "####" AAAAAA 0; // A_CustomMissile ("MetalTrashParticle2", 96, 0, random (0, 360), 2, random (0, 180))
  1859.         "####" AAAAAAAAA 0; // A_CustomMissile ("GlassShard", 96, 0, random (0, 360), 2, random (0, 360))
  1860.         "####" A 0 A_Explode(100, 250);
  1861.         "####" A 5;
  1862.         "####" A 100;
  1863.         "####" A -1;
  1864.         Stop;
  1865.     }  
  1866.    
  1867. }
  1868. //------------------------------------------------------------------------------
  1869. //MODIFY
  1870. //------------------------------------------------------------------------------
  1871. //Common base tank for anything player related.
  1872. class MT_CommonTank_New : PlayerPawn
  1873. {
  1874. Actor treads, turret, turretcamera, povcamera, gun;
  1875. Actor ForcedHealthBar;
  1876. transient FLineTraceData cameratrace;
  1877. BoAFindHitPointTracer hittracer;
  1878. double sndvol;
  1879. Vector3 cameralocation;
  1880. int useholdtime;
  1881. Vector3 CrosshairPos;
  1882. Actor CrosshairActor;
  1883. double CrosshairDist;
  1884. int altrefire;
  1885.  
  1886. Default
  1887. {
  1888. //Internal stuff, very important
  1889. Player.StartItem "MT_ClassToken"              ,1;
  1890. Player.StartItem "MHTA1_Token"                ,1;
  1891. Player.StartItem "MTU_SupplyBox_2"            ,1;
  1892. Player.StartItem "MT_Subgun_Slot"             ,1;
  1893. Player.StartItem "MT_AmmoSwitcher"            ,1;
  1894. Player.StartItem "MT_AmmoSwitcher_Reverse"    ,1;
  1895. Player.StartItem "MT_SubgunSwitcher"          ,1;
  1896. Player.Startitem "MT_SubgunSwitcher_Reverse"  ,1;
  1897. Player.Startitem "MT_Searchlight"             ,1;
  1898. Player.StartItem "MT_Nightvision_MK1"         ,1;
  1899. Player.StartItem "MT_ChaseCamera_MK1"         ,1;
  1900. Player.StartItem "MT_PeriscopeItem"           ,1;
  1901. //Lockers
  1902. //Weapons
  1903. Player.StartItem "MT_105mmCannon"           ,1;
  1904. Player.StartItem "Multi_Purpose_Device"       ,1;
  1905. //Ammo
  1906. Player.Startitem "MT_75x500mmHE"                ,20;
  1907. Player.Startitem "MT_75x500mmAP"                ,40;
  1908. Player.Startitem "A_7u62x54mmR"                 ,2000;
  1909. //Items
  1910. //Player.startitem "Item_GrenadePod_Smoke"        ,8
  1911. Player.startitem "Item_GrenadePod_SSS"          ,1;
  1912. Player.startitem "Item_GrenadePod_Illuminating" ,2;
  1913. Player.startitem "MT_Item_GrenadePod_Flare"     ,30;
  1914. Player.Startitem "MT_GrenadePod_Explosive"      ,20;
  1915.  
  1916. player.DisplayName "BASE TANK";
  1917. player.viewheight 25;
  1918. player.attackzoffset 13;
  1919. Health 1600;
  1920. Height 20;
  1921. Radius 16;
  1922. //Height 96;
  1923. //Radius 64;
  1924. Mass 0x7ffffff;
  1925. Speed 1.25;
  1926. BloodType "TankSpark";
  1927. //MaxStepHeight 48;
  1928. //MaxDropoffHeight 48;
  1929. MaxStepHeight 24;
  1930. MaxDropoffHeight 24;
  1931. Species "Tank";
  1932. +NOBLOOD
  1933. }
  1934.  
  1935.     States
  1936.     {
  1937.         Spawn:
  1938.             TNT1 A -1;
  1939.             Stop;
  1940.         Pain:
  1941.             TNT1 A 1 A_Pain;
  1942.             Goto Spawn;
  1943.         Death:
  1944.             TNT1 A 1 {
  1945.                 A_Scream();
  1946.                 A_StopSound(30);
  1947.                 A_StopSound(31);
  1948.                 A_StopSound(32);
  1949.             }
  1950.             TNT1 A 0;
  1951.             Stop;
  1952.        
  1953.     }
  1954.  
  1955.     override void MovePlayer ()
  1956.     {
  1957.         let player = self.player;
  1958.         UserCmd cmd = player.cmd;
  1959.  
  1960.         double moveangle = angle;
  1961.  
  1962.         if (cmd.yaw) { angle += cmd.yaw * (360./65536.); }
  1963.         if (treads) { moveangle = treads.angle; }
  1964.  
  1965.         player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
  1966.  
  1967.         double newsndvol = 0;
  1968.  
  1969.         if (cmd.forwardmove | cmd.sidemove)
  1970.         {
  1971.             double forwardmove, sidemove;
  1972.             double friction, movefactor;
  1973.             double fm, sm;
  1974.  
  1975.             [friction, movefactor] = GetFriction();
  1976.  
  1977.             if (!player.onground && !bNoGravity && !waterlevel)
  1978.             {
  1979.                 // [RH] allow very limited movement if not on ground.
  1980.                 movefactor *= level.aircontrol;
  1981.             }
  1982.  
  1983.             fm = cmd.forwardmove;
  1984.             sm = cmd.sidemove;
  1985.             [fm, sm] = TweakSpeeds (fm, sm);
  1986.             fm *= Speed / 256;
  1987.             sm *= Speed / 256;
  1988.  
  1989.             // The tank always moves at normal speed, regardless of cl_run setting or shift being held
  1990.             // This basically cancels out the run speed additions handled in g_game.cpp.
  1991.             if (cl_run && player.cmd.buttons & BT_SPEED)
  1992.             {
  1993.                 sm /= 1.25;
  1994.                 fm /= 1.25;
  1995.             }
  1996.             else if (cl_run ^ player.cmd.buttons & BT_SPEED)
  1997.             {
  1998.                 sm /= 2;
  1999.                 fm /= 2;
  2000.             }
  2001.  
  2002.             forwardmove = fm * movefactor * (35 / TICRATE);
  2003.             sidemove = sm * movefactor * (35 / TICRATE);
  2004.  
  2005.             CVar boa_autosteer = CVar.FindCVar('boa_autosteer');
  2006.             bool shift = !(player.cmd.buttons & BT_SPEED) != !boa_autosteer.GetInt(); // If shift is being held or boa_autosteer cvar is true
  2007.  
  2008.             // Handle "auto-steering" the tank to wherever you are aiming
  2009.             if (forwardmove && shift && treads && turret && treads.angle != turret.angle) // If holding SHIFT, auto-steer the tracks to align with the turret
  2010.             {
  2011.                 double diff = deltaangle(treads.angle, angle);
  2012.  
  2013.                 if (diff < -1)
  2014.                 {
  2015.                     treads.angle -= 2;
  2016.                     forwardmove /= 5;
  2017.                     A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2018.                 }
  2019.                 else if (diff > 1)
  2020.                 {
  2021.                     treads.angle += 2;
  2022.                     forwardmove /= 5;
  2023.                     A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2024.                 }
  2025.                 else
  2026.                 {
  2027.                     treads.angle = angle;
  2028.                     A_StopSound(30);
  2029.                 }
  2030.  
  2031.                 newsndvol += 0.5;
  2032.             }
  2033.             else if (sidemove) // Only do separate sidemove handling if not auto-steering or not moving forward
  2034.             {
  2035.                 if (forwardmove >= 0) { sidemove *= -1; }
  2036.  
  2037.                 if (!forwardmove) { sidemove *= 2.5; } // Turn faster if you're not moving forward
  2038.  
  2039.                 angle += sidemove;
  2040.  
  2041.                 if (treads)
  2042.                 {
  2043.                     treads.angle += sidemove;
  2044.                     if (turret) { turret.angle += sidemove; }
  2045.                 }
  2046.  
  2047.                 //A_SetInventory("ShakeShaderControl", 1 + int(sidemove));
  2048.  
  2049.                 newsndvol += abs(sidemove);
  2050.             }
  2051.  
  2052.             if (forwardmove)
  2053.             {
  2054.                 ForwardThrust(forwardmove, moveangle);
  2055.             }
  2056.  
  2057.             if (!(player.cheats & CF_PREDICTING) && (forwardmove != 0 || sidemove != 0))
  2058.             {
  2059.                 PlayRunning ();
  2060.                 if (treads)
  2061.                 {
  2062.                     if (InStateSequence(treads.CurState, treads.SpawnState) && treads.FindState("Move"))
  2063.                     {
  2064.                          treads.SetStateLabel("Move");
  2065.                     }
  2066.                 }
  2067.             }
  2068.  
  2069.             if (player.cheats & CF_REVERTPLEASE)
  2070.             {
  2071.                 player.cheats &= ~CF_REVERTPLEASE;
  2072.                 player.camera = player.mo;
  2073.             }
  2074.         }
  2075.  
  2076.         double movespeed = vel.xy.length();
  2077.         if (movespeed)
  2078.         {
  2079.             //A_SetInventory("ShakeShaderControl", 1 + int(movespeed) * 65536);
  2080.             newsndvol += movespeed;
  2081.         }
  2082.  
  2083.         if (sndvol > newsndvol) { sndvol = max(sndvol - 0.25, newsndvol); }
  2084.         if (sndvol < newsndvol) { sndvol = min(sndvol + 0.25, newsndvol); }
  2085.  
  2086.         A_SoundVolume(CHAN_7, sndvol);
  2087.     }
  2088.  
  2089.     override void CheckJump() {} // Tanks can't jump
  2090.  
  2091.    
  2092.     override void CheckPitch()
  2093.     {
  2094.         let player = self.player;
  2095.         // [RH] Look up/down stuff
  2096.         if (!level.IsFreelookAllowed())
  2097.         {
  2098.             Pitch = 0.;
  2099.         }
  2100.         else
  2101.         {
  2102.             // The player's view pitch is clamped between -32 and +56 degrees,
  2103.             // which translates to about half a screen height up and (more than)
  2104.             // one full screen height down from straight ahead when view panning
  2105.             // is used.
  2106.             int clook = player.cmd.pitch;
  2107.             if (clook != 0)
  2108.             {
  2109.                 if (clook == -32768)
  2110.                 { // center view
  2111.                     player.centering = true;
  2112.                 }
  2113.                 else if (!player.centering)
  2114.                 {
  2115.                     // no more overflows with floating point. Yay! :)
  2116.                     Pitch = clamp(Pitch - clook * (360. / 65536.), (turret ? -turret.pitch : 0) + player.MinPitch, (turret ? -turret.pitch : 0) + player.MaxPitch);
  2117.                 }
  2118.             }
  2119.         }
  2120.         if (player.centering)
  2121.         {
  2122.             if (abs(Pitch) > 2.)
  2123.             {
  2124.                 Pitch *= (2. / 3.);
  2125.             }
  2126.             else
  2127.             {
  2128.                 Pitch = 0.;
  2129.                 player.centering = false;
  2130.                 if (PlayerNumber() == consoleplayer)
  2131.                 {
  2132.                     LocalViewPitch = 0;
  2133.                 }
  2134.             }
  2135.         }
  2136.     }
  2137.    
  2138.  
  2139.     override bool CanCollideWith(Actor other, bool passive)
  2140.     {
  2141.         if (other is "TankBlocker" && other.Species == Species) { return false; }
  2142.  
  2143.         if (other.bSpecial) { other.Touch(self); }
  2144.  
  2145.         return true;
  2146.     }
  2147.  
  2148.     /*
  2149.     override void FireWeaponAlt (State stat)
  2150.     {
  2151.         let weapn = player.ReadyWeapon;
  2152.         if (weapn == null || weapn.FindState('AltFire') == null || !weapn.CheckAmmo (Weapon.AltFire, true))
  2153.         {
  2154.             return;
  2155.         }
  2156.  
  2157.         player.WeaponState &= ~WF_WEAPONBOBBING;
  2158.         PlayAttacking ();
  2159.         weapn.bAltFire = true;
  2160.  
  2161.         if (stat == null)
  2162.         {
  2163.             int refire = player.refire;
  2164.  
  2165.             if (player.mo is "ShermanPlayer") { refire = ShermanPlayer(player.mo).altrefire; }
  2166.  
  2167.             stat = weapn.GetAltAtkState(!!refire);
  2168.         }
  2169.  
  2170.         player.SetPsprite(PSP_WEAPON, stat);
  2171.         if (!weapn.bNoAlert)
  2172.         {
  2173.             SoundAlert (self, false);
  2174.         }
  2175.     }
  2176.     */
  2177.  
  2178.     override void PostBeginPlay()
  2179.     {
  2180.         //TakeInventory("BoATilt", 1);
  2181.  
  2182.         hittracer = new("BoAFindHitPointTracer");
  2183.  
  2184.         // Spawn everything in neutral orientation
  2185.         pitch = 0;
  2186.         roll = 0;
  2187.  
  2188.         bool sp = false;
  2189.  
  2190.         if (tracer && US_Sherman(tracer))
  2191.         {
  2192.             treads = tracer;
  2193.             tracer = null;
  2194.         }
  2195.  
  2196.         while (!treads)
  2197.         {
  2198.         [sp, treads] = A_SpawnItemEx("US_Sherman", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE);
  2199.         }
  2200.         treads.master = self;
  2201.         treads.bSolid = false;
  2202.         treads.Species = Species;
  2203.  
  2204.         while (!turretcamera) { [sp, turretcamera] = A_SpawnItemEx("SecurityCamera", -2.5 * radius, 0, treads.height + 32, 0, 0, 0, 0, SXF_TRANSFERSCALE); }
  2205.         turretcamera.master = self;
  2206.  
  2207.         sndvol = 0.7;
  2208.  
  2209.         A_StartSound("TKIDLE", 31, CHANF_LOOPING, 0.25);
  2210.         A_StartSound("TNK1LOOP", 32, CHANF_LOOPING, 0.7);
  2211.  
  2212.         Super.PostBeginPlay();
  2213.     }
  2214.  
  2215.     override void Tick()
  2216.     {
  2217.         Super.Tick();
  2218.  
  2219.         bool chasecam = player.cheats & CF_CHASECAM;
  2220.  
  2221.         if (health == Default.Health && US_Sherman(treads).savedhealth)
  2222.         {
  2223.             A_SetHealth(US_Sherman(treads).savedhealth);
  2224.             US_Sherman(treads).savedhealth = 0;
  2225.         }
  2226.  
  2227.         if (!turret) // Since turret is spawned by treads actor, check for it here since it'll be spawned after PostBeginPlay is called
  2228.         {
  2229.             if (US_Sherman(treads))
  2230.             {
  2231.                 turret = US_Sherman(treads).turret;
  2232.  
  2233.                 if (turret)
  2234.                 {
  2235.                     turret.master = self;
  2236.                     turret.angle = angle;
  2237.                     turret.bSolid = false;
  2238.  
  2239.                     bool sp;
  2240.                     while (!povcamera) { [sp, povcamera] = turret.A_SpawnItemEx("SecurityCamera", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  2241.                     povcamera.master = self;
  2242.                 }
  2243.             }
  2244.         }
  2245.        
  2246.  
  2247.         if (Level.time % (35 * 5) == 0) { ForcedHealthBar = GetClosestForcedHealthBar(); } // Only run this check occasionally
  2248.  
  2249.         if (player && turretcamera && treads && turret && povcamera)
  2250.         {
  2251.             double zoffset = VehicleBase.SetPitchRoll(treads, 10, 10, 80, true);
  2252.             double newz = pos.z - zoffset > GetZAt(0, 0) + MaxDropoffHeight ? pos.z - zoffset : GetZAt(0, 0);
  2253.             treads.SetOrigin((pos.xy, newz), true);
  2254.  
  2255.             double delta = deltaangle(treads.angle, turret.angle);
  2256.  
  2257.  
  2258.             Player.MinPitch = treads.pitch * cos(delta) - 80;
  2259.             Player.MaxPitch = treads.pitch * cos(delta) + 80;
  2260.  
  2261.             turret.SetOrigin(treads.pos, true);
  2262.             //turret.SetOrigin(treads.pos + (RotateVector((treads.radius * sin(treads.pitch), -treads.radius * sin(treads.roll)), treads.angle), treads.height * cos(treads.roll)), true);
  2263.  
  2264.             turret.pitch = treads.roll * sin(-delta) + treads.pitch * cos(delta);
  2265.             turret.roll = treads.roll * cos(-delta) + treads.pitch * sin(delta);
  2266.  
  2267.             if (turret.angle != angle)
  2268.             {
  2269.                 turret.angle = !chasecam ? angle : clamp(angle, turret.angle - 1.5, turret.angle + 1.5);
  2270.                 A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2271.             }
  2272.             else
  2273.             {
  2274.                 A_StopSound(30);
  2275.             }
  2276.  
  2277.             // Force pitch clamping
  2278.             if (turret.pitch + pitch >= player.MaxPitch || turret.pitch + pitch <= player.MinPitch)
  2279.             {
  2280.                 player.cmd.pitch = 0;
  2281.             }
  2282.  
  2283.             if (!gun) { gun = US_ShermanTurret(turret).gun; }
  2284.  
  2285.             if (gun)
  2286.             {
  2287.                 DoTrace(gun, turret.angle, 2048, turret.pitch + pitch, 0, 0, hittracer);
  2288.  
  2289.                 CrosshairPos = hittracer.Results.HitPos;
  2290.                 CrosshairActor = hittracer.Results.HitActor;
  2291.                 CrosshairDist = hittracer.Results.Distance;
  2292.  
  2293.                 gun.pitch = turret.pitch + pitch;
  2294.                 gun.roll = turret.roll;
  2295.             }
  2296.  
  2297.             /*
  2298.             if (!chasecam)
  2299.             {
  2300.                 if (povcamera)
  2301.                 {
  2302.                     //Vector3 povoffset = (48, -24, 48);
  2303.                     Vector3 povoffset = (48, -24, 48);
  2304.  
  2305.                     povcamera.angle = turret.angle;
  2306.                     povcamera.pitch = gun.pitch;
  2307.  
  2308.                     Vector2 temp = RotateVector((povoffset.y, povoffset.z), turret.roll);
  2309.                     Vector3 offset = (povoffset.x, temp.x, temp.y);
  2310.  
  2311.                     temp = RotateVector((offset.x, offset.z), 360 - turret.pitch);
  2312.                     offset = (temp.x, offset.y, temp.y);
  2313.  
  2314.                     temp = RotateVector((offset.x, offset.y), turret.angle);
  2315.                     offset = (temp.x, temp.y, offset.z);
  2316.  
  2317.                     povcamera.SetOrigin(turret.pos + offset, true);
  2318.  
  2319.                     povcamera.roll = turret.roll;
  2320.  
  2321.                     povcamera.CameraFOV = player.fov * 1.2;
  2322.  
  2323.                     player.camera = povcamera;
  2324.                 }
  2325.             }
  2326.             else
  2327.             {
  2328.                 double modifier, pitchmodifier;
  2329.  
  2330.                 if (pitch < 0) { modifier = 48 * sin(-pitch); } // Aiming up moves the camera up so you can keep the aimpoint on screen
  2331.                 else { modifier = 96 * sin(pitch * 3); } // Aiming down actually moves the camera *up*, so that you can see the aimpoint over the top of the turret
  2332.  
  2333.                 modifier += (turret.pitch > 0 ? 96 : 128) * sin(turret.pitch); // Move the camera up/down when going up/downhill
  2334.                 pitchmodifier = (turret.pitch > 0 ? -15 : 10) * sin(turret.pitch); // Pitch the camera up/down when going up/downhill
  2335.  
  2336.                 turret.LineTrace(angle + 180, 2.5 * radius, pitchmodifier, TRF_THRUHITSCAN | TRF_THRUACTORS, turret.height + 32 + modifier, 0.0, 0.0, cameratrace);
  2337.  
  2338.                 turretcamera.SetOrigin(cameratrace.HitLocation + AngleToVector(angle, 2), true);
  2339.                 turretcamera.angle = angle;
  2340.                 turretcamera.pitch = turret.pitch;
  2341.  
  2342.                 turretcamera.CameraFOV = player.fov;
  2343.  
  2344.                 player.camera = turretcamera;
  2345.             }
  2346.             */
  2347.        
  2348.         }
  2349.         else
  2350.         {
  2351.             player.camera = player.mo;
  2352.         }
  2353.  
  2354.         if (player.usedown)
  2355.         {
  2356.             useholdtime++;
  2357.             if (useholdtime == 1) { Level.ExecuteSpecial(80, self, null, false, -int(Name("TankExitMessage")), 0); }
  2358.         }
  2359.         else { useholdtime = 0; }
  2360.  
  2361.  
  2362.         if (useholdtime >= 35)
  2363.         {
  2364.             //A_SetInventory("ShakeShaderControl", 1);
  2365.             if (treads && US_Sherman(treads)) { US_Sherman(treads).usetimeout = 35; }
  2366.             TakeInventory("Sherman", 1);
  2367.         }
  2368.     }
  2369.  
  2370.     Actor GetClosestForcedHealthBar()
  2371.     {
  2372.         ThinkerIterator Finder = ThinkerIterator.Create("MT_Actor", Thinker.STAT_DEFAULT - 3);
  2373.         //ThinkerIterator Finder = ThinkerIterator.Create("Base", Thinker.STAT_DEFAULT - 3);
  2374.         //Base it;
  2375.         MT_Actor it;
  2376.         Actor mo;
  2377.  
  2378.         /*
  2379.         while ( it = Base(Finder.Next()) )
  2380.         {
  2381.             if (!it.user_DrawHealthBar == True) { continue; } // Only process actors with the AlwaysDrawHealthBar flag set
  2382.             if (
  2383.                 it.health <= 0 ||
  2384.                 !it.bShootable ||
  2385.                 it.bDormant
  2386.             ) { continue; }
  2387.             if (mo && Distance3D(it) > Distance3D(mo)) { continue; } // Only draw health bar for the closest one
  2388.  
  2389.             mo = it;
  2390.         }
  2391.         */
  2392.  
  2393.         return mo;
  2394.     }
  2395.  
  2396.     void DoTrace(Actor origin, double angle, double dist, double pitch, int flags, double zoffset, BoAFindHitPointTracer thistracer)
  2397.     {
  2398.         if (!origin) { origin = self; }
  2399.  
  2400.         thistracer.skipspecies = origin.species;
  2401.         thistracer.skipactor = origin;
  2402.         Vector3 tracedir = (cos(angle) * cos(pitch), sin(angle) * cos(pitch), -sin(pitch));
  2403.         thistracer.Trace(origin.pos + (0, 0, zoffset), origin.CurSector, tracedir, dist, 0);
  2404.     }
  2405.  
  2406.     override bool UndoPlayerMorph(playerinfo activator, int unmorphflag, bool force)
  2407.     {
  2408.         if (!activator || !activator.mo) { return false; }
  2409.  
  2410.         int premorphhealth;
  2411.  
  2412.         for (Inventory i = activator.mo.inv; i != null; i = i.Inv)
  2413.         {
  2414.             if (i is "Sherman")
  2415.             {
  2416.                 if (Sherman(i).premorphhealth) { premorphhealth = Sherman(i).premorphhealth; }
  2417.                 break;
  2418.             }
  2419.         }
  2420.  
  2421.         bool ret = Super.UndoPlayerMorph(activator, unmorphflag, force); // This call forces the player health to full Spawn Health
  2422.  
  2423.         activator.health = activator.mo.health = premorphhealth;
  2424.  
  2425.         return ret;
  2426.     }
  2427. }
  2428.  
  2429.  
  2430. // Sherman Tank Player Class
  2431. class ShermanPlayer : PlayerPawn
  2432. {
  2433.     Actor treads, turret, turretcamera, povcamera, gun;
  2434.     Actor ForcedHealthBar;
  2435.     transient FLineTraceData cameratrace;
  2436.     BoAFindHitPointTracer hittracer;
  2437.     double sndvol;
  2438.     Vector3 cameralocation;
  2439.     int useholdtime;
  2440.     Vector3 CrosshairPos;
  2441.     Actor CrosshairActor;
  2442.     double CrosshairDist;
  2443.     int altrefire;
  2444.  
  2445.     Default
  2446.     {
  2447.         Health 10000;
  2448.         Height 96;
  2449.         Mass 0x7ffffff;
  2450.         Radius 64;
  2451.         Speed 1;
  2452.         BloodType "TankSpark";
  2453.         DamageFactor "Electric", 0.8;
  2454.         DamageFactor "Fire", 0.5;
  2455.         DamageFactor "MutantPoison", 0.1;
  2456.         DamageFactor "Normal", 0.5;
  2457.         DamageFactor "Rocket", 0.5;
  2458.         DamageFactor "Rocket2", 1.5; //enemypanzerrocket only, for panzerguards  - ozy81
  2459.         DamageFactor "UndeadPoison", 0.1;
  2460.         MaxStepHeight 48;
  2461.         MaxDropoffHeight 48;
  2462.         Species "Tank";
  2463.         Player.MorphWeapon "Cannon75mm";
  2464.         Player.StartItem "Cannon75mm";
  2465.         Player.UseRange 0;
  2466.         Player.WeaponSlot 1, "Cannon75mm";
  2467.         Renderstyle 'None'; // Ensure that the actual player actor stays invisible, just in case it reverts to PLAYA0 sprites
  2468.         +NOBLOOD
  2469.         -PICKUP // Don't pick up powerups while in the tank
  2470.     }
  2471.  
  2472.     States
  2473.     {
  2474.         Spawn:
  2475.             TNT1 A -1;
  2476.             Stop;
  2477.         Pain:
  2478.             TNT1 A 1 A_Pain;
  2479.             Goto Spawn;
  2480.         Death:
  2481.             TNT1 A 1 {
  2482.                 A_Scream();
  2483.                 A_StopSound(30);
  2484.                 A_StopSound(31);
  2485.                 A_StopSound(32);
  2486.             }
  2487.             TNT1 A -1;
  2488.             Stop;
  2489.     }
  2490.  
  2491.     override void MovePlayer ()
  2492.     {
  2493.         let player = self.player;
  2494.         UserCmd cmd = player.cmd;
  2495.  
  2496.         double moveangle = angle;
  2497.  
  2498.         if (cmd.yaw) { angle += cmd.yaw * (360./65536.); }
  2499.         if (treads) { moveangle = treads.angle; }
  2500.  
  2501.         player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
  2502.  
  2503.         double newsndvol = 0;
  2504.  
  2505.         if (cmd.forwardmove | cmd.sidemove)
  2506.         {
  2507.             double forwardmove, sidemove;
  2508.             double friction, movefactor;
  2509.             double fm, sm;
  2510.  
  2511.             [friction, movefactor] = GetFriction();
  2512.  
  2513.             if (!player.onground && !bNoGravity && !waterlevel)
  2514.             {
  2515.                 // [RH] allow very limited movement if not on ground.
  2516.                 movefactor *= level.aircontrol;
  2517.             }
  2518.  
  2519.             fm = cmd.forwardmove;
  2520.             sm = cmd.sidemove;
  2521.             [fm, sm] = TweakSpeeds (fm, sm);
  2522.             fm *= Speed / 256;
  2523.             sm *= Speed / 256;
  2524.  
  2525.             // The tank always moves at normal speed, regardless of cl_run setting or shift being held
  2526.             // This basically cancels out the run speed additions handled in g_game.cpp.
  2527.             if (cl_run && player.cmd.buttons & BT_SPEED)
  2528.             {
  2529.                 sm /= 1.25;
  2530.                 fm /= 1.25;
  2531.             }
  2532.             else if (cl_run ^ player.cmd.buttons & BT_SPEED)
  2533.             {
  2534.                 sm /= 2;
  2535.                 fm /= 2;
  2536.             }
  2537.  
  2538.             forwardmove = fm * movefactor * (35 / TICRATE);
  2539.             sidemove = sm * movefactor * (35 / TICRATE);
  2540.  
  2541.             CVar boa_autosteer = CVar.FindCVar('boa_autosteer');
  2542.             bool shift = !(player.cmd.buttons & BT_SPEED) != !boa_autosteer.GetInt(); // If shift is being held or boa_autosteer cvar is true
  2543.  
  2544.             // Handle "auto-steering" the tank to wherever you are aiming
  2545.             if (forwardmove && shift && treads && turret && treads.angle != turret.angle) // If holding SHIFT, auto-steer the tracks to align with the turret
  2546.             {
  2547.                 double diff = deltaangle(treads.angle, angle);
  2548.  
  2549.                 if (diff < -1)
  2550.                 {
  2551.                     treads.angle -= 2;
  2552.                     forwardmove /= 5;
  2553.                     A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2554.                 }
  2555.                 else if (diff > 1)
  2556.                 {
  2557.                     treads.angle += 2;
  2558.                     forwardmove /= 5;
  2559.                     A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2560.                 }
  2561.                 else
  2562.                 {
  2563.                     treads.angle = angle;
  2564.                     A_StopSound(30);
  2565.                 }
  2566.  
  2567.                 newsndvol += 0.5;
  2568.             }
  2569.             else if (sidemove) // Only do separate sidemove handling if not auto-steering or not moving forward
  2570.             {
  2571.                 if (forwardmove >= 0) { sidemove *= -1; }
  2572.  
  2573.                 if (!forwardmove) { sidemove *= 2.5; } // Turn faster if you're not moving forward
  2574.  
  2575.                 angle += sidemove;
  2576.  
  2577.                 if (treads)
  2578.                 {
  2579.                     treads.angle += sidemove;
  2580.                     if (turret) { turret.angle += sidemove; }
  2581.                 }
  2582.  
  2583.                 //A_SetInventory("ShakeShaderControl", 1 + int(sidemove));
  2584.  
  2585.                 newsndvol += abs(sidemove);
  2586.             }
  2587.  
  2588.             if (forwardmove)
  2589.             {
  2590.                 ForwardThrust(forwardmove, moveangle);
  2591.             }
  2592.  
  2593.             if (!(player.cheats & CF_PREDICTING) && (forwardmove != 0 || sidemove != 0))
  2594.             {
  2595.                 PlayRunning ();
  2596.                 if (treads)
  2597.                 {
  2598.                     if (InStateSequence(treads.CurState, treads.SpawnState) && treads.FindState("Move"))
  2599.                     {
  2600.                          treads.SetStateLabel("Move");
  2601.                     }
  2602.                 }
  2603.             }
  2604.  
  2605.             if (player.cheats & CF_REVERTPLEASE)
  2606.             {
  2607.                 player.cheats &= ~CF_REVERTPLEASE;
  2608.                 player.camera = player.mo;
  2609.             }
  2610.         }
  2611.  
  2612.         double movespeed = vel.xy.length();
  2613.         if (movespeed)
  2614.         {
  2615.             //A_SetInventory("ShakeShaderControl", 1 + int(movespeed) * 65536);
  2616.             newsndvol += movespeed;
  2617.         }
  2618.  
  2619.         if (sndvol > newsndvol) { sndvol = max(sndvol - 0.25, newsndvol); }
  2620.         if (sndvol < newsndvol) { sndvol = min(sndvol + 0.25, newsndvol); }
  2621.  
  2622.         A_SoundVolume(CHAN_7, sndvol);
  2623.     }
  2624.  
  2625.     override void CheckJump() {} // Tanks can't jump
  2626.  
  2627.     override void CheckPitch()
  2628.     {
  2629.         let player = self.player;
  2630.         // [RH] Look up/down stuff
  2631.         if (!level.IsFreelookAllowed())
  2632.         {
  2633.             Pitch = 0.;
  2634.         }
  2635.         else
  2636.         {
  2637.             // The player's view pitch is clamped between -32 and +56 degrees,
  2638.             // which translates to about half a screen height up and (more than)
  2639.             // one full screen height down from straight ahead when view panning
  2640.             // is used.
  2641.             int clook = player.cmd.pitch;
  2642.             if (clook != 0)
  2643.             {
  2644.                 if (clook == -32768)
  2645.                 { // center view
  2646.                     player.centering = true;
  2647.                 }
  2648.                 else if (!player.centering)
  2649.                 {
  2650.                     // no more overflows with floating point. Yay! :)
  2651.                     Pitch = clamp(Pitch - clook * (360. / 65536.), (turret ? -turret.pitch : 0) + player.MinPitch, (turret ? -turret.pitch : 0) + player.MaxPitch);
  2652.                 }
  2653.             }
  2654.         }
  2655.         if (player.centering)
  2656.         {
  2657.             if (abs(Pitch) > 2.)
  2658.             {
  2659.                 Pitch *= (2. / 3.);
  2660.             }
  2661.             else
  2662.             {
  2663.                 Pitch = 0.;
  2664.                 player.centering = false;
  2665.                 if (PlayerNumber() == consoleplayer)
  2666.                 {
  2667.                     LocalViewPitch = 0;
  2668.                 }
  2669.             }
  2670.         }
  2671.     }
  2672.  
  2673.     /*
  2674.     override bool UseInventory(Inventory item)
  2675.     {
  2676.         if (
  2677.             item is "RepairKit" ||
  2678.             item is "BoACompass"
  2679.         )
  2680.         {
  2681.             return Super.UseInventory(item);
  2682.         }
  2683.  
  2684.         return false;
  2685.     }
  2686.     */
  2687.  
  2688.     override bool CanCollideWith(Actor other, bool passive)
  2689.     {
  2690.         if (other is "TankBlocker" && other.Species == Species) { return false; }
  2691.  
  2692.         if (other.bSpecial) { other.Touch(self); }
  2693.  
  2694.         return true;
  2695.     }
  2696.  
  2697.     override void FireWeaponAlt (State stat)
  2698.     {
  2699.         let weapn = player.ReadyWeapon;
  2700.         if (weapn == null || weapn.FindState('AltFire') == null || !weapn.CheckAmmo (Weapon.AltFire, true))
  2701.         {
  2702.             return;
  2703.         }
  2704.  
  2705.         player.WeaponState &= ~WF_WEAPONBOBBING;
  2706.         PlayAttacking ();
  2707.         weapn.bAltFire = true;
  2708.  
  2709.         if (stat == null)
  2710.         {
  2711.             int refire = player.refire;
  2712.  
  2713.             if (player.mo is "ShermanPlayer") { refire = ShermanPlayer(player.mo).altrefire; }
  2714.  
  2715.             stat = weapn.GetAltAtkState(!!refire);
  2716.         }
  2717.  
  2718.         player.SetPsprite(PSP_WEAPON, stat);
  2719.         if (!weapn.bNoAlert)
  2720.         {
  2721.             SoundAlert (self, false);
  2722.         }
  2723.     }
  2724.  
  2725.     override void PostBeginPlay()
  2726.     {
  2727.         //TakeInventory("BoATilt", 1);
  2728.  
  2729.         hittracer = new("BoAFindHitPointTracer");
  2730.  
  2731.         // Spawn everything in neutral orientation
  2732.         pitch = 0;
  2733.         roll = 0;
  2734.  
  2735.         bool sp = false;
  2736.  
  2737.         if (tracer && US_Sherman(tracer))
  2738.         {
  2739.             treads = tracer;
  2740.             tracer = null;
  2741.         }
  2742.  
  2743.         while (!treads) { [sp, treads] = A_SpawnItemEx("US_Sherman", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  2744.         treads.master = self;
  2745.         treads.bSolid = false;
  2746.         treads.Species = Species;
  2747.  
  2748.         while (!turretcamera) { [sp, turretcamera] = A_SpawnItemEx("SecurityCamera", -2.5 * radius, 0, treads.height + 128, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  2749.         turretcamera.master = self;
  2750.  
  2751.         sndvol = 0.7;
  2752.  
  2753.         A_StartSound("TKIDLE", 31, CHANF_LOOPING, 0.25);
  2754.         A_StartSound("TNK1LOOP", 32, CHANF_LOOPING, 0.7);
  2755.  
  2756.         Super.PostBeginPlay();
  2757.     }
  2758.  
  2759.     override void Tick()
  2760.     {
  2761.         Super.Tick();
  2762.  
  2763.         bool chasecam = player.cheats & CF_CHASECAM;
  2764.  
  2765.         if (health == Default.Health && US_Sherman(treads).savedhealth)
  2766.         {
  2767.             A_SetHealth(US_Sherman(treads).savedhealth);
  2768.             US_Sherman(treads).savedhealth = 0;
  2769.         }
  2770.  
  2771.         if (!turret) // Since turret is spawned by treads actor, check for it here since it'll be spawned after PostBeginPlay is called
  2772.         {
  2773.             if (US_Sherman(treads))
  2774.             {
  2775.                 turret = US_Sherman(treads).turret;
  2776.  
  2777.                 if (turret)
  2778.                 {
  2779.                     turret.master = self;
  2780.                     turret.angle = angle;
  2781.                     turret.bSolid = false;
  2782.  
  2783.                     bool sp;
  2784.                     while (!povcamera) { [sp, povcamera] = turret.A_SpawnItemEx("SecurityCamera", 0, 0, 0, 0, 0, 0, 0, SXF_TRANSFERPITCH | SXF_TRANSFERROLL | SXF_TRANSFERSCALE); }
  2785.                     povcamera.master = turret;
  2786.                 }
  2787.             }
  2788.         }
  2789.  
  2790.         if (Level.time % (35 * 5) == 0) { ForcedHealthBar = GetClosestForcedHealthBar(); } // Only run this check occasionally
  2791.  
  2792.         if (player && turretcamera && treads && turret && povcamera)
  2793.         {
  2794.             double zoffset = VehicleBase.SetPitchRoll(treads);
  2795.             double newz = pos.z - zoffset > GetZAt(0, 0) + MaxDropoffHeight ? pos.z - zoffset : GetZAt(0, 0);
  2796.             treads.SetOrigin((pos.xy, newz), true);
  2797.  
  2798.             double delta = deltaangle(treads.angle, turret.angle);
  2799.  
  2800.             Player.MinPitch = treads.pitch * cos(delta) - 25;
  2801.             Player.MaxPitch = treads.pitch * cos(delta) + 10;
  2802.  
  2803.             turret.SetOrigin(treads.pos + (RotateVector((treads.radius * sin(treads.pitch), -treads.radius * sin(treads.roll)), treads.angle), treads.height * cos(treads.roll)), true);
  2804.  
  2805.             turret.pitch = treads.roll * sin(-delta) + treads.pitch * cos(delta);
  2806.             turret.roll = treads.roll * cos(-delta) + treads.pitch * sin(delta);
  2807.  
  2808.             if (turret.angle != angle)
  2809.             {
  2810.                 turret.angle = !chasecam ? angle : clamp(angle, turret.angle - 1.5, turret.angle + 1.5);
  2811.                 A_StartSound("MACHINE_LOOP_3", 30, CHANF_NOSTOP, 0.5);
  2812.             }
  2813.             else
  2814.             {
  2815.                 A_StopSound(30);
  2816.             }
  2817.  
  2818.             // Force pitch clamping
  2819.             if (turret.pitch + pitch >= player.MaxPitch || turret.pitch + pitch <= player.MinPitch)
  2820.             {
  2821.                 player.cmd.pitch = 0;
  2822.             }
  2823.  
  2824.             if (!gun) { gun = US_ShermanTurret(turret).gun; }
  2825.  
  2826.             if (gun)
  2827.             {
  2828.                 DoTrace(gun, turret.angle, 2048, turret.pitch + pitch, 0, 0, hittracer);
  2829.  
  2830.                 CrosshairPos = hittracer.Results.HitPos;
  2831.                 CrosshairActor = hittracer.Results.HitActor;
  2832.                 CrosshairDist = hittracer.Results.Distance;
  2833.  
  2834.                 gun.pitch = turret.pitch + pitch;
  2835.                 gun.roll = turret.roll;
  2836.             }
  2837.  
  2838.             if (!chasecam)
  2839.             {
  2840.                 if (povcamera)
  2841.                 {
  2842.                     Vector3 povoffset = (48, -24, 48);
  2843.  
  2844.                     povcamera.angle = turret.angle;
  2845.                     povcamera.pitch = gun.pitch;
  2846.  
  2847.                     Vector2 temp = RotateVector((povoffset.y, povoffset.z), turret.roll);
  2848.                     Vector3 offset = (povoffset.x, temp.x, temp.y);
  2849.  
  2850.                     temp = RotateVector((offset.x, offset.z), 360 - turret.pitch);
  2851.                     offset = (temp.x, offset.y, temp.y);
  2852.  
  2853.                     temp = RotateVector((offset.x, offset.y), turret.angle);
  2854.                     offset = (temp.x, temp.y, offset.z);
  2855.  
  2856.                     povcamera.SetOrigin(turret.pos + offset, true);
  2857.  
  2858.                     povcamera.roll = turret.roll;
  2859.  
  2860.                     povcamera.CameraFOV = player.fov * 1.2;
  2861.  
  2862.                     player.camera = povcamera;
  2863.                 }
  2864.             }
  2865.             else
  2866.             {
  2867.                 double modifier, pitchmodifier;
  2868.  
  2869.                 if (pitch < 0) { modifier = 48 * sin(-pitch); } // Aiming up moves the camera up so you can keep the aimpoint on screen
  2870.                 else { modifier = 96 * sin(pitch * 3); } // Aiming down actually moves the camera *up*, so that you can see the aimpoint over the top of the turret
  2871.  
  2872.                 modifier += (turret.pitch > 0 ? 96 : 128) * sin(turret.pitch); // Move the camera up/down when going up/downhill
  2873.                 pitchmodifier = (turret.pitch > 0 ? -15 : 10) * sin(turret.pitch); // Pitch the camera up/down when going up/downhill
  2874.  
  2875.                 turret.LineTrace(angle + 180, 2.5 * radius, pitchmodifier, TRF_THRUHITSCAN | TRF_THRUACTORS, turret.height + 32 + modifier, 0.0, 0.0, cameratrace);
  2876.  
  2877.                 turretcamera.SetOrigin(cameratrace.HitLocation + AngleToVector(angle, 2), true);
  2878.                 turretcamera.angle = angle;
  2879.                 turretcamera.pitch = turret.pitch;
  2880.  
  2881.                 turretcamera.CameraFOV = player.fov;
  2882.  
  2883.                 player.camera = turretcamera;
  2884.             }
  2885.         }
  2886.         else
  2887.         {
  2888.             player.camera = player.mo;
  2889.         }
  2890.  
  2891.         if (player.usedown)
  2892.         {
  2893.             useholdtime++;
  2894.             if (useholdtime == 1) { Level.ExecuteSpecial(80, self, null, false, -int(Name("TankExitMessage")), 0); }
  2895.         }
  2896.         else { useholdtime = 0; }
  2897.  
  2898.  
  2899.         if (useholdtime >= 35)
  2900.         {
  2901.             //A_SetInventory("ShakeShaderControl", 1);
  2902.             if (treads && US_Sherman(treads)) { US_Sherman(treads).usetimeout = 35; }
  2903.             TakeInventory("Sherman", 1);
  2904.         }
  2905.     }
  2906.  
  2907.     Actor GetClosestForcedHealthBar()
  2908.     {
  2909.         ThinkerIterator Finder = ThinkerIterator.Create("MT_Actor", Thinker.STAT_DEFAULT - 3);
  2910.         MT_Actor it;
  2911.         Actor mo;
  2912.  
  2913.         /*
  2914.         while ( it = MT_Actor(Finder.Next()) )
  2915.         {
  2916.             if (!it.user_DrawHealthBar == True) { continue; } // Only process actors with the AlwaysDrawHealthBar flag set
  2917.             if (
  2918.                 it.health <= 0 ||
  2919.                 !it.bShootable ||
  2920.                 it.bDormant
  2921.             ) { continue; }
  2922.             if (mo && Distance3D(it) > Distance3D(mo)) { continue; } // Only draw health bar for the closest one
  2923.  
  2924.             mo = it;
  2925.         }
  2926.         */
  2927.  
  2928.         return mo;
  2929.     }
  2930.  
  2931.     void DoTrace(Actor origin, double angle, double dist, double pitch, int flags, double zoffset, BoAFindHitPointTracer thistracer)
  2932.     {
  2933.         if (!origin) { origin = self; }
  2934.  
  2935.         thistracer.skipspecies = origin.species;
  2936.         thistracer.skipactor = origin;
  2937.         Vector3 tracedir = (cos(angle) * cos(pitch), sin(angle) * cos(pitch), -sin(pitch));
  2938.         thistracer.Trace(origin.pos + (0, 0, zoffset), origin.CurSector, tracedir, dist, 0);
  2939.     }
  2940.  
  2941.     override bool UndoPlayerMorph(playerinfo activator, int unmorphflag, bool force)
  2942.     {
  2943.         if (!activator || !activator.mo) { return false; }
  2944.  
  2945.         int premorphhealth;
  2946.  
  2947.         for (Inventory i = activator.mo.inv; i != null; i = i.Inv)
  2948.         {
  2949.             if (i is "Sherman")
  2950.             {
  2951.                 if (Sherman(i).premorphhealth) { premorphhealth = Sherman(i).premorphhealth; }
  2952.                 break;
  2953.             }
  2954.         }
  2955.  
  2956.         bool ret = Super.UndoPlayerMorph(activator, unmorphflag, force); // This call forces the player health to full Spawn Health
  2957.  
  2958.         activator.health = activator.mo.health = premorphhealth;
  2959.  
  2960.         return ret;
  2961.     }
  2962. }
  2963.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement