Advertisement
inkoalawetrust

Main battle tank code (ZScript)

Jul 21st, 2021
1,563
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 23.31 KB | None | 0 0
  1. //The code used for my GZDoom MBT NPC, seen here (https://www.youtube.com/watch?v=5fwT7eAKW0I) and here (https://www.realm667.com/index.php/en/beastiary-mainmenu-136-69621/doom-style-mainmenu-105-73113/2392-main-battle-tank).
  2. //I may come back to this one day, to rewrite the code and hopefully make it more easily readable, and also make it so the tank
  3. //does not generate like 5-8 times as much overhead as a normal monster/NPC.
  4.  
  5. #Include "Decorations.zsc"
  6.  
  7. Class MainBattleTank : Actor
  8. {
  9.     Default
  10.     {
  11.         //$Sprite MBTSA1
  12.         //$Category Vehicles
  13.         //$Title Main Battle Tank
  14.         Tag "Main Battle Tank";
  15.         Health 5000;
  16.         Radius 90;
  17.         Height 78;
  18.         DeathHeight 72;
  19.         Speed 8;
  20.         FastSpeed 12;
  21.         Mass 50000;
  22.         XScale 1.2;
  23.         DamageFactor "RailGun", 0.5;
  24.         DamageFactor "TankShell", 0.75;
  25.         DamageFactor "TankCrush", 0.0;
  26.         DamageFactor "AutocannonShell", 0.25;
  27.         RipLevelMin 5;
  28.         Species "Tank";
  29.         Species "MainBattleTank"; //So the tank can't harm itself.
  30.         Obituary "%o was obliterated by a tank shell"; //So the proper obituary will appear instead of the generic "(Player Name) died".
  31.         FriendlySeeBlocks 256;
  32.         Monster;
  33.         -ActivateMCross;
  34.         -CanUseWalls;
  35.         -CanPushWalls;
  36.         +NoRadiusDmg;
  37.         +LookAllAround;
  38.         +NeverRespawn; //Doesn't make sense for a tank to magically reappear.
  39.         +Telestomp;
  40.         +NoTelefrag;
  41.         +Frightening; //Turn this on again.
  42.         +NoFear;
  43.         +DontSquash;
  44.         +DontHarmClass;
  45.         +DontDrain;
  46.         +NoBlood;
  47.         +NoPain;
  48.         +PuffOnActors;
  49.         +AvoidMelee;
  50.         +NoInfightSpecies;
  51.         +DontMorph;
  52.     }
  53.     enum MBTSoundChannels
  54.     {
  55.         CHAN_IDLING = 10396,
  56.         CHAN_MOVING = 10397
  57.     }
  58.     Bool A_IsPlayerClose (Double Mindist = 0) //Custom function by Agent_Ash cause' lord knows I have no idea what this black speech means to be able to type it out on my own.
  59.     {
  60.         For(Int pn = 0; pn < MAXPLAYERS; pn++)
  61.         {
  62.             If(!PlayerInGame[pn])
  63.                 Continue;
  64.             PlayerInfo plr = Players[pn];
  65.             If(plr && plr.mo && Distance3D(plr.mo) <= MinDist)
  66.             {
  67.                 Return True;
  68.                 Break;
  69.             }
  70.         }
  71.         Return False;
  72.     }
  73.     Actor Turret; //Pointer to the tank turret.
  74.     Actor FrontDamage; //Pointer to the actor that performs damage in front of the chassis.
  75.     Int TankFallSpeed; //Measures how fast the tank fell to the floor.
  76.     Bool User_NoCrushing; //If set to true, the tank won't spawn the actor that crushes anything in front of it.
  77.     Bool User_NoSmartAim; //If set to true, the tank will not predict the trajectory of its' target when firing.
  78.     Bool User_Stationary; //If set to true, the tank will not move and stand still and shoot, like the tanks in Half-Life.
  79.     Bool User_Fearless;   //If set to true, the tank will not run away from it's target if it's too big or is another tank.
  80.     Override Void Tick()
  81.     {
  82.         Super.Tick();
  83.         //This is here to fake/emulate the whole tank being frozen, since I can't force the turret to
  84.         //an ice death of its own due to it transferring all the damage it takes to the chassis.
  85.         If (Self.bIceCorpse == True)
  86.         {
  87.             A_SetSize (96,96);
  88.             If (Turret) //If this null check wasn't here, the game would abort every time.
  89.             {
  90.                 Turret.A_SetTranslation ("Ice"); //Gives the turret the ice translation that actors get when frozen with "MDK ice".
  91.             }
  92.         }
  93.         //Stop all sounds if the game is frozen.
  94.         If (IsFrozen())
  95.         {
  96.             A_StopSound (CHAN_IDLING);
  97.             A_StopSound (CHAN_MOVING);
  98.             Return;
  99.         }
  100.         //If the tank fell from a high enough height, cause a small earthquake whose intensity increases with the tanks' speed and play a metal slamming sound.
  101.         If (Self.vel.z < 0) {TankFallSpeed++;}
  102.         //This code to make the tank actually crush stuff it falls on breaks sometimes so I won't be using it unless I can fix it.
  103.         //If (tankfallspeed >= 8 && CheckBlock (CBF_NOLINES|CBF_DROPOFF,zofs:Self.Height+2))
  104.         //{A_Explode (1024,192,XF_NOTMISSILE|XF_EXPLICITDAMAGETYPE,True,192,damagetype:"TankCrush");}
  105.         If (Self.pos.z - Self.GetZAt() <= 0 && TankFallSpeed >= 16)
  106.         {
  107.             A_QuakeEx (4,4,3,35*2,0,768,flags:QF_SCALEDOWN);
  108.             A_StartSound ("MBT/GroundSlam",158371);
  109.             TankFallSpeed = 0; //Reset the falling speed variable.
  110.         }
  111.         //=========================================================
  112.         //If the tank is in its' spawn state, begin playing the idle sound on a loop.
  113.         If (InStateSequence (CurState, ResolveState ("Spawn")))
  114.         {
  115.             A_StartSound ("MBT/TankIdle",CHAN_IDLING,CHANF_LOOPING,attenuation:0.75);
  116.         }
  117.         //If the tank is in its' see state, stop the idle sound and beging the moving one.
  118.         If (InStateSequence (CurState, ResolveState ("See"))) //This works better than the ActiveSound property because it plays the moving sound constantly when the tank moves.
  119.         {
  120.             A_StopSound (CHAN_IDLING); //Stop the tanks' idle sound if it's moving.
  121.             A_StartSound ("MBT/TankMove",CHAN_MOVING,CHANF_LOOPING,attenuation:0.75);
  122.         }
  123.     }
  124.     //Initializes the tanks' turret and front crushing damage.
  125.     Override Void PostBeginPlay()
  126.     {
  127.         Super.PostBeginPlay();
  128.  
  129.         Turret = Spawn ("MainBattleTankTurret",self.pos); //Spawn the turret at the chassis' position.
  130.         Turret.Master = Self; //Make the chassis the turrets' master, so the turret can warp to the chassis.
  131.         Turret.Angle = Self.Angle; //Set the turrets' angle to that of the chassis.
  132.         If (User_NoSmartAim) {MainBattleTankTurret(Turret).NoSmartAim = True;} //If the NoSmartAim user variable is on, then set the similarly named internal variable that the turret has on.
  133.  
  134.         If (User_NoCrushing) {Return;} //If the NoCrushing user variable is enabled, don't spawn the FrontDamage actor
  135.         FrontDamage = Spawn ("MBTFrontDamage",Self.pos); //Spawn the actor that applies the crushing damage in front of the tank when it's moving.
  136.         FrontDamage.Master = Self; //Make the tank chassis the actors' master.
  137.  
  138.         //If the chassis is set to ambush the player and/or is dormant, then set said flags to the turret as well.
  139.         If (Self.bAmbush && Turret) {Turret.bAmbush = True;}
  140.         If (Self.bDormant && Turret) {Turret.bDormant = True;}
  141.         If (Self.GetRenderStyle() == STYLE_None && Turret) {Turret.A_SetRenderStyle (1.0,STYLE_None);}
  142.     }
  143.     Override Void OnDestroy() //Removes the turret and actor that performs damage in front of the tank if the chassis is removed.
  144.     {
  145.         Super.OnDestroy();
  146.         If (Turret){Turret.Destroy();}
  147.         If (FrontDamage){FrontDamage.Destroy();}
  148.     }
  149.     //This should make it so the chassis doesn't collide with its' turret, that way the tank will be able to move up and down.
  150.     Override Bool CanCollideWith (Actor Other,Bool Passive)
  151.     {
  152.         Super.CanCollideWith(Other,Passive);
  153.         If (Turret && Other && Other == Turret) {Return False;}
  154.         Return True;
  155.     }
  156.     //Uses the FrontDamage actors' obituary if the player was killed by it, otherwise it uses the tanks' own obituary.
  157.     Override String GetObituary (Actor Victim, Actor Inflictor, Name MOD, Bool PlayerAttack)
  158.     {
  159.         String Message;
  160.         If (Inflictor){Message = Inflictor.GetObituary (Victim, Inflictor, MOD, PlayerAttack);}
  161.         If (!Message){Message = Super.GetObituary (Victim, Inflictor, MOD, PlayerAttack);}
  162.         Return Message;
  163.     }
  164.     States
  165.     {
  166.         Spawn:
  167.             MBCM A 8 A_LookEx (maxseedist:10240+2048,maxheardist:3072);
  168.             Loop;
  169.         See:
  170.             MBCM AABB 2
  171.             {
  172.                 A_Chase();
  173.                 A_QuakeEx (1,1,1,2,0,384); //So the ground shakes a bit as it moves across it.
  174.                 //In theory the below code should make the tank keep some distance from any nearby players if it's friendly, in practive it does nothing for some reason.
  175.                 If (A_IsPlayerClose (768) && Self.bFriendly == True) {bFrightened = True; A_Stop();}
  176.                 Else {bFrightened = False;}
  177.                 If (Target && User_Fearless == False) //Null check, also checks if User_Fearless is on to run the code inside the brackets.
  178.                 {
  179.                     //Retreat if the target is close and about as big if not bigger than a Cyberdemon or if it's another tank. Or if the target is too close and has over 600 health.
  180.                     If (Distance3D (Target) <= 1024 && Target.Height >= 100 && Target.Radius >= 24 || Distance3D (Target) <= 1024 && Target.GetClassName() == 'MainBattleTank' ||Distance3D (Target) <= 512 && Target.Health >= 600)
  181.                     {
  182.                         SetStateLabel ("MoveAway");
  183.                     }
  184.                 }
  185.             }
  186.             Loop;
  187.         MoveAway:
  188.             TNT1 A 0
  189.             {
  190.                 A_FaceTarget();
  191.                 Speed = -8;
  192.                 Angle = Angle-180;
  193.             }
  194.             MBCM AA 2
  195.             {
  196.                 A_Chase (Null,Null,CHF_NODIRECTIONTURN);
  197.                 A_QuakeEx (1,1,1,2,0,384);
  198.             }
  199.             TNT1 A 0
  200.             {
  201.                 A_FaceTarget();
  202.                 Angle = Angle-180;
  203.             }
  204.             MBCM BB 2
  205.             {
  206.                 A_Chase (Null,Null,CHF_NODIRECTIONTURN|CHF_NORANDOMTURN);
  207.                 A_QuakeEx (1,1,1,2,0,384);
  208.             }
  209.             TNT1 A 0 {Speed = 8;} //Put the tanks' speed back to normal again.
  210.             Goto See;
  211.         Death:
  212.             MBCD A 1
  213.             {
  214.                 A_SpawnItemEx ("MBTExplosion",flags:SXF_SETMASTER);
  215.                 A_StopSound (CHAN_MOVING); //Stops the sound of the tank moving.
  216.                 A_StopSound (CHAN_IDLING); //Stop the idle engine sound if it's playing.
  217.                 bSolid = True;
  218.                 bShootable = True;
  219.                 bNoDamage = True;
  220.                 bNeverTarget = True;
  221.                 bNotAutoAimed = True;
  222.                 A_SetSize (Radius,72); //There's a small gap you can fire through between the turret and chassis once the tank is dead otherwise
  223.             }
  224.             MBCD A -1;
  225.             Stop;
  226.     }
  227. }
  228.  
  229. Class MainBattleTankTurret : Actor
  230. {
  231.     Default
  232.     {
  233.         Radius 55;
  234.         Height 36;
  235.         DeathHeight 36;
  236.         Mass 10000;
  237.         FriendlySeeBlocks 256;
  238.         MaxTargetRange 10240;
  239.         RipLevelMin 5;
  240.         MinMissileChance 100;
  241.         XScale 1.2;
  242.         Obituary "%o was obliterated by a tank shell";
  243.         Species "MainBattleTank";
  244.         Monster;
  245.         -ActivateMCross;
  246.         -CanUseWalls;
  247.         -CanPushWalls;
  248.         -IsMonster;
  249.         -CountKill;
  250.         +LookAllAround;
  251.         +MissileMore;
  252.         +MissileEvenMore;
  253.         +NeverRespawn; //Doesnt make sense for a tank to magically reappear.
  254.         +Telestomp;
  255.         +NoTelefrag;
  256.         +NoFear;
  257.         +DontSquash;
  258.         +DontHarmClass;
  259.         +DontDrain;
  260.         +NoBlood;
  261.         +NoPain;
  262.         +PuffOnActors;
  263.         +NotAutoAimed;
  264.         +NoDamage;
  265.         +NoInfightSpecies;
  266.         +DontMorph;
  267.     }
  268.     enum TurretSoundChannels {CHAN_TURRET = 183745, CHAN_BURNING = 19384}
  269.     Actor Shell;
  270.     Bool NoSmartAim; //If set on by the chassis having User_NoSmartAim set to true, then it will make the tank shell projectile not account for its' targets' movement.
  271.     Double FireRadius; //The radius of the light for when the turret is ablaze.
  272.     Double LightDuration; //For the random pulse light duration when turret is ablaze.
  273.     Bool ShellLOFInvalid;
  274.     //Variables for the particle spawning once the turret is dead.
  275.     Int RNGLife;
  276.     Double RNGSize;
  277.     Double RNGVel;
  278.     Double RNGXOfs;
  279.     Double RNGYOfs;
  280.     //==============
  281.     Override Void Tick()
  282.     {
  283.         Super.Tick();
  284.         //This is how the turret actually stays attached to the chassis.
  285.         If (Master) //Only warp to the master actor if it actually exists.
  286.         {
  287.             A_CopyFriendliness(); //Also copy whatever the chassis' affiliation is.
  288.             //This check is here because for some reason the turret slightly floats above the chassis when the chassis is destroyed
  289.             If (Self.InStateSequence (Self.CurState, Self.ResolveState ("Death")))
  290.             {
  291.                 Warp (Master,zofs:74,flags:WARPF_NOCHECKPOSITION|WARPF_STOP|WARPF_INTERPOLATE|WARPF_USECALLERANGLE|WARPF_COPYPITCH);
  292.                 Return;
  293.             }
  294.             Warp (Master,zofs:78,flags:WARPF_NOCHECKPOSITION|WARPF_STOP|WARPF_INTERPOLATE|WARPF_USECALLERANGLE|WARPF_COPYPITCH);
  295.         }
  296.         //Die if the chassis is dead and spawn a flickering dynamic light.
  297.         If (Master)
  298.         {
  299.             If (Self.InStateSequence (Self.CurState, Self.ResolveState ("Death"))) {Return;} //So the rest of the turrets' death state can continue.
  300.             If (Master.InStateSequence (Master.CurState, Master.ResolveState ("Death")))
  301.             {
  302.                 FireRadius = FRandom (24,64);
  303.                 LightDuration = FRandom (0.1,0.6);
  304.                 Self.SetStateLabel ("Death");
  305.                 Self.bCorpse = True;
  306.                 Self.A_AttachLight ("TurretFire",DynamicLight.PulseLight,"DFD100",FireRadius,FireRadius-6,DynamicLight.LF_ATTENUATE,(0,0,12),LightDuration);
  307.             }
  308.         }
  309.         //If the game is frozen, stop the sound of the turret rotating or burning if it's playing.
  310.         If (IsFrozen())
  311.         {
  312.             A_StopSound (CHAN_TURRET);
  313.             A_StopSound (CHAN_BURNING);
  314.             Return;
  315.         }
  316.         //If the turret is spinning, then play the rotating sound.
  317.         If (InStateSequence (CurState, ResolveState ("FaceTarget")))
  318.         {
  319.             A_StartSound ("MBT/TurretRotate",CHAN_TURRET,CHANF_LOOPING);
  320.         }
  321.     }
  322.     //Transfers any damage it takes to the chassis.
  323.     Override Int DamageMobj (Actor Inflictor, Actor Source, Int Damage, Name MOD, Int Flags, Double Angle)
  324.     {
  325.         If (Master)
  326.         {
  327.             Master.DamageMobj (Inflictor,Source,Damage,MOD,Flags,Angle);
  328.         }
  329.         Return 0;
  330.     }
  331.     Void A_RandomizeParticleValues()
  332.     {
  333.         //Random values for the particle spawning.
  334.         RNGXOfs = FRandom (-48,48);
  335.         RNGYOfs = FRandom (-48,48);
  336.         RNGVel = FRandom (2,8);
  337.         RNGSize = FRandom (2,6);
  338.         RNGLife = 35* RandomPick (1,2);
  339.         //=================================
  340.     }
  341.     States
  342.     {
  343.         Spawn:
  344.             TNT1 A 0 {If (Target){Target = Null;}} //Sometimes the turret may begin attacking the player even if friendly, this should prevent that.
  345.             MBTI A 8
  346.             {
  347.                 A_LookEx (LOF_NOJUMP,maxseedist:10240+2048,maxheardist:3072,label:"Alert");
  348.                 //Replicates the behaviour of A_LookEx without the LOF_NOJUMP flag on.
  349.                 If (Self.Target != Null && bAmbush == False){SetStateLabel ("Alert");}
  350.                 If (Master) {Self.Angle = Master.Angle;}
  351.                 If (Master && bAmbush == False)
  352.                 {
  353.                     //If the chassis' has a target, the transfer that target pointer to the turret as well.
  354.                     If (Master.Target != Null)
  355.                     {
  356.                         Self.Target = Master.Target; //Read the below comment, I had to put it here as well so it only doesn't have the same angles as the chassis when it has to turn to fire at a target.
  357.                         SetStateLabel ("FaceTarget");
  358.                     }
  359.                 }
  360.                 //This has to be done for the Ambush flag to work properly on the turret for some reason.
  361.                 //If the turret has a target, the Ambush flag is on, and there is a valid line of sight to the target, then begin chasing it.
  362.                 If (Self.Target != Null && bAmbush && CheckIfTargetInLOS()) {SetStateLabel ("Alert");}
  363.             }
  364.             Goto Spawn+1;
  365.         Alert:
  366.             MBTI A 8
  367.             {
  368.                 A_StopSound (CHAN_TURRET); //Stop the rotating sound if the turret is simply alert.
  369.                 If (Master) {Self.Angle = Master.Angle;} //Sets the turrets' angle to that of the chassis when it's not firing.
  370.                 A_Chase (null,"FaceTarget",CHF_DONTMOVE|CHF_DONTTURN);
  371.                 //Copied over from the code below, so the turret forgets its' target if it is dead, this is basically here just in case.
  372.                 If ( (Target && Target.Health <= 0) || (Target && Target.Health <= 0) )
  373.                 {
  374.                     Self.Target = Null;
  375.                 }
  376.                 If (Master) {Self.Angle = Master.Angle;}
  377.             }
  378.             Loop;
  379.         FaceTarget:
  380.             TNT1 A 0
  381.             {
  382.                 //So the turret doesn't move at all when dormant.
  383.                 If (bDormant) {SetStateLabel ("Spawn");}
  384.             }
  385.             MBTI A 1
  386.             {
  387.                 //If the target is dead while the tank is rotating at it, then go back to the Alert state. This was originally here to fix a weird bug that now doesn't occur, but I'm keeping it because it fixes another bug that fixates the turret on a dead target.
  388.                 If ( (Target && Target.Health <= 0) || (Target && Target.Health <= 0) )
  389.                 {
  390.                     Self.Target = Null;
  391.                     Self.SetStateLabel ("Alert");
  392.                 }
  393.                 A_FaceTarget (3,5,flags:FAF_MIDDLE);
  394.                 //The below code fires a ray every time the turret rotates to face the target, if the ray hits the target, that means that it has a line of sight to it, so it'll fire.
  395.                 FLineTraceData CanHitTarget;
  396.                 LineTrace (Self.Angle,10240,Clamp(Self.Pitch, -45, 30),TRF_THRUHITSCAN|TRF_NOSKY|TRF_SOLIDACTORS,16,250,data:CanHitTarget);
  397.                 If (CanHitTarget.HitActor && Target && CanHitTarget.HitActor == Target)
  398.                 {
  399.                     SetStateLabel ("FireCannon");
  400.                 }
  401.                 Return null;
  402.             }
  403.             Loop;
  404.         FireCannon: //Each MBTI frame is about a second
  405.             MBTI AAAAAAAAAAAAAAAA 2 A_FaceTarget (3,5,flags:FAF_MIDDLE);
  406.             MBTI AAAAAAAAAAAAAAAA 2 A_FaceTarget (3,5,flags:FAF_MIDDLE);
  407.             MBTI AAAAAAAAAAAAAAAA 2 A_FaceTarget (3,5,flags:FAF_MIDDLE);
  408.             MBTF A 4 Bright
  409.             {
  410.                 Shell = A_SpawnProjectile ("MBTShell",-1);
  411.                 If (Target){Shell.Tracer = Self.Target;} //Makes the turrets' target the shells' tracer so the shell can hit the target with VelIntercept
  412.                 If (Shell) {Shell.Target = Self.Master;} //Makes the shells' target be the chassis, so monsters target it instead of the turret.
  413.                 If (NoSmartAim == True) {MBTShell(Shell).NoSmartAim = True;} //If NoSmartAim is on for the turret, then set it to the shell itself too so it doesn't predict its' targets' movement.
  414.                 A_StopSound (CHAN_TURRET); //Stop the rotating sound.
  415.                 A_StartSound ("MBT/TankShot",CHAN_TURRET,attenuation:0.25);
  416.                 A_QuakeEx (3,3,2,35*1,0,768);
  417.             }
  418.             MBTF B 4 Bright;
  419.             MBTF CDEFGH 4 Bright;
  420.             MBTI A 35;
  421.             Goto Spawn;
  422.         Death:
  423.             MBTD ABCD 4 Bright
  424.             {
  425.                 A_StopSound (CHAN_TURRET);
  426.                 A_StartSound ("MBT/burning",CHAN_BURNING,CHANF_LOOPING,attenuation:0.8);
  427.                 If (IsFrozen()) {Return;} //Don't do so if the game is frozen.
  428.                 A_SpawnParticle ("FFE50B",SPF_FULLBRIGHT,lifetime:RNGLife,size:RNGSize,xoff:RNGXOfs,yoff:RNGYOfs,zoff:32,velz:RNGVel,accelz:0.1,sizestep:0.1);
  429.                 A_RandomizeParticleValues();
  430.                 A_SpawnParticle ("FFE50B",SPF_FULLBRIGHT,lifetime:RNGLife,size:RNGSize,xoff:RNGXOfs,yoff:RNGYOfs,zoff:32,velz:RNGVel,accelz:0.1,sizestep:0.1);
  431.                 A_RandomizeParticleValues();
  432.                 A_SpawnParticle ("FFE50B",SPF_FULLBRIGHT,lifetime:RNGLife,size:RNGSize,xoff:RNGXOfs,yoff:RNGYOfs,zoff:32,velz:RNGVel,accelz:0.1,sizestep:0.1);
  433.                 A_RandomizeParticleValues();
  434.                 A_SpawnParticle ("FFE50B",SPF_FULLBRIGHT,lifetime:RNGLife,size:RNGSize,xoff:RNGXOfs,yoff:RNGYOfs,zoff:32,velz:RNGVel,accelz:0.1,sizestep:0.1);
  435.                 A_RandomizeParticleValues();
  436.             }
  437.             Loop;
  438.     }
  439. }
  440.  
  441. //$GZDB_SKIP
  442.  
  443. Class MBTShell : FastProjectile
  444. {
  445.     Default
  446.     {
  447.         Radius 8;
  448.         Height 8;
  449.         DamageFunction 1000; //No RNG that makes the shell do several times the damage randomly.
  450.         Speed 160;
  451.         FastSpeed 200;
  452.         MissileHeight 8;
  453.         MissileType "MBTShellTrail";
  454.         Decal "TankShellScorch";
  455.         DamageType "TankShell";
  456.         Species "MainBattleTank";
  457.         Projectile;
  458.         +Bright;
  459.         +CausePain;
  460.         +MThruSpecies;
  461.     }
  462.     Bool NoSmartAim; //Don't account for the targets' velocity if set to true.
  463.     Override Void PostBeginPlay()
  464.     {
  465.         Super.PostBeginPlay();
  466.         Warp (Self,60,zofs:4,flags:WARPF_NOCHECKPOSITION); //So the shell looks like it's being fired off the end of the tanks' barrel.
  467.         If (Tracer && NoSmartAim == False){VelIntercept (Tracer);}
  468.     }
  469.     Override Void Tick()
  470.     {
  471.         Super.Tick();
  472.         //If the shell is on the floor, then enter the FloorDeath state.
  473.         If ((pos.z - FloorZ) <= 1)
  474.         {
  475.             If (InStateSequence (CurState, ResolveState ("FloorDeath"))) {Return;}
  476.             SetStateLabel ("FloorDeath");
  477.         }
  478.         //If the shell is at the ceiling, then enter the CeilingDeath state.
  479.         If ((CeilingZ - pos.z + Height) <= 64)
  480.         {
  481.             If (InStateSequence (CurState, ResolveState ("CeilingDeath"))) {Return;}
  482.             SetStateLabel ("CeilingDeath");
  483.         }
  484.         //If the shell is within 128 MU of its' tracer (Its' target) and its' tracer is the chassis or turret of another tank, and that chassis or turret are hostile, turn MThruSpecies off.
  485.         If (Tracer && Target && Distance3D (Tracer) <= 128 && Tracer.GetClassName() == 'MainBattleTank' && Tracer.IsHostile (Self.Target)||
  486.         Tracer && Target && Distance3D (Tracer) <= 128 && Tracer.GetClassName() == 'MainBattleTankTurret' && Tracer.IsHostile (Self.Target))
  487.         {bMThruSpecies = False;}
  488.         //If the above conditions aren't met e.g if the shell misses the targetted enemy tank and flies away, the turn the flag back on to avoid it hitting a friendly tank.
  489.         Else {bMThruSpecies = True;}
  490.     }
  491.     Override Int SpecialMissileHit(Actor Victim)
  492.     {
  493.         Super.SpecialMissileHit(Victim);
  494.         //This makes any tank hit by an opposing tank turn on the opposing tank that fired at it. This includes making both the chassis and turet set the tank that fired at them as their target.
  495.         If (Victim && Target && Victim.GetClassName() == 'MainBattleTank' && Victim.IsHostile(Self.Target) ||
  496.         Victim && Target && Victim.GetClassName() == 'MainBattleTankTurret' && Victim.IsHostile(Self.Target))
  497.         {
  498.             Victim.Target = Self.Target;
  499.             //I need to figure out a way to make the whole tank attack whatever other tank shot it.
  500.             If (Victim && Victim.Master && Self.Target){Victim.Master.Target = Self.Target;}
  501.             Return -1;
  502.         }
  503.         Return -1;
  504.     }
  505.     enum ShellSoundSlot {CHAN_IMPACT = 19328} //Because using CHAN_BODY doesn't work for some reason.
  506.     States
  507.     {
  508.         Spawn:
  509.             TNT1 A 1;
  510.             TSHL A 1;
  511.             Goto Spawn+1;
  512.         Death:
  513.             DSHL A 0
  514.             {
  515.                 A_Stop();
  516.                 //If the shell exploded close enough to the floor, go to the floor death state instead.
  517.                 If (pos.Z - FloorZ <= 48) {SetStateLabel ("FloorDeath");}
  518.             }
  519.             DSHL A 4
  520.             {
  521.                 A_StartSound ("MBT/ShellImpact",CHAN_IMPACT,attenuation:0.6);
  522.                 A_SetScale (2.0);
  523.                 A_SetRenderStyle (1.0,STYLE_Add);
  524.                 A_QuakeEx (4,4,3,35*3,0,360*2,flags:QF_SCALEDOWN);
  525.                 A_Explode (256,500,alert:True,64);
  526.             }
  527.             DSHL BCDEFGHIJKLMNOPQRSTUVWXY 4;
  528.             Stop;
  529.         CeilingDeath:
  530.             PSHL A 0 A_Stop();
  531.             PSHL A 4
  532.             {
  533.                 A_StartSound ("MBT/ShellImpact",CHAN_IMPACT,attenuation:0.6);
  534.                 A_SetScale (-4.0);
  535.                 A_SetRenderStyle (1.0,STYLE_Add);
  536.                 A_QuakeEx (4,4,3,35*3,0,360*2,flags:QF_SCALEDOWN);
  537.                 A_Explode (256,500,alert:True,64);
  538.             }
  539.             PSHL BCDEFGHIJKLMNO 4;
  540.             Stop;
  541.         FloorDeath:
  542.             PSHL A 0 A_Stop();
  543.             PSHL A 6
  544.             {
  545.                 A_StartSound ("MBT/ShellImpact",CHAN_IMPACT,attenuation:0.6);
  546.                 A_SetScale (4.0);
  547.                 A_SetRenderStyle (1.0,STYLE_Add);
  548.                 A_QuakeEx (4,4,3,35+35/2,0,360*2,flags:QF_SCALEDOWN);
  549.                 A_Explode (256,500,alert:True,64);
  550.             }
  551.             PSHL BCDEFGHIJKLMNO 6;
  552.             Stop;
  553.     }
  554. }
  555.  
  556. Class MBTShellTrail : Actor
  557. {
  558.     Default
  559.     {
  560.         RenderStyle "Add";
  561.         +NoBlockmap;
  562.         +NoInteraction;
  563.         Scale 0.20;
  564.     }
  565.     Override Void PostBeginPlay()
  566.     {
  567.         Super.PostBeginPlay();
  568.         Warp (Self,-36.0);
  569.     }
  570.     States
  571.     {
  572.         Spawn:
  573.             TSHL X 1
  574.             {
  575.                 A_FadeOut (0.1);
  576.                 A_SetScale (Scale.X+0.05);
  577.             }
  578.             Loop;
  579.     }
  580. }
  581.  
  582. Class MBTExplosion : Actor
  583. {
  584.     Default
  585.     {
  586.         +NoInteraction;
  587.         RenderStyle "Add";
  588.         Scale 4.0;
  589.     }
  590.     Override Void Tick()
  591.     {
  592.         Super.Tick();
  593.         //So the explosion sticks to the tank if it plays while the tanks' wreckage is moving.
  594.         If (Master)
  595.         {
  596.             Warp (Master,flags:WARPF_INTERPOLATE);
  597.         }
  598.     }
  599.     States
  600.     {
  601.         Spawn:
  602.             ZTXP A 6 Bright NoDelay
  603.             {
  604.                 A_StartSound ("MBT/TankExplode",847271,attenuation:0.4); //Doesn't work without an explicit sound slot.
  605.                 A_Explode (256,384,alert:True);
  606.                 A_QuakeEx (4,4,2,35*4,0,512+128,flags:QF_SCALEDOWN);
  607.             }
  608.             ZTXP BCDEFGHIJKLMNOPQRST 6 Bright;
  609.             Stop;
  610.     }
  611. }
  612.  
  613. Class MBTFrontDamage : Actor
  614. {
  615.     Default
  616.     {
  617.         +NoBlockmap;
  618.         +NoInteraction;
  619.         +DontHarmSpecies;
  620.         DamageType "TankCrush";
  621.         Species "Tank";
  622.         Species "MainBattleTank";
  623.         Obituary "%o was flattened by a tank";
  624.     }
  625.     Override Void Tick()
  626.     {
  627.         Super.Tick();
  628.         //Moves the actor that performs the crushing damage of the tank in front of the chassis every tick, if it spawns with no master, it simply vanishes.
  629.         If (Master)
  630.         {
  631.             Warp (Master,72,flags:WARPF_NOCHECKPOSITION);
  632.         }
  633.         Else
  634.         {
  635.             Destroy();
  636.         }
  637.     }
  638.     States
  639.     {
  640.         Spawn:
  641.             //This makes the Front Damage actors' target the tank itself, so if it kills something, the tank itself will be the source of the damage.
  642.             TNT1 A 0 NoDelay {If (Master){Self.Target = Master;}}
  643.             Goto ConstantDamage;
  644.         ConstantDamage:
  645.             TNT1 A 12
  646.             {
  647.                 //Performs damage as long as the chassis isn't in it's Spawn or Death state.
  648.                 If (Master.InStateSequence (Master.CurState, Master.ResolveState ("Death"))) {Return;}
  649.                 If (Master.InStateSequence (Master.CurState, Master.ResolveState ("Spawn"))) {Return;}
  650.                 A_Explode (10,96,fulldamagedistance:96);
  651.             }
  652.             Loop;
  653.     }
  654. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement