Advertisement
poosh

StinkyController - AmmoPPPM

Mar 25th, 2025 (edited)
544
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. class StinkyController extends ScriptedController;
  2.  
  3. var FtgGame FtgGame;
  4. var byte TeamIndex; // is set by FtgGame
  5. var StinkyClot StinkyClot;
  6.  
  7. var array<Actor> MoveTargets;
  8.  
  9. var array<KFAmmoPickup> AmmoCandidates;
  10. var transient KFAmmoPickup CurrentAmmoCandidate;
  11. var int AmmoSpawnCount, MaxAmmoSpawnCount;
  12. var transient int AmmoSpawned;
  13. var float AmmoPPPM; // Ammo spawned Per Player Per Minute
  14. var transient float LastAmmoSpawnTime;
  15.  
  16. var transient Actor LastAlternatePathTarget;
  17. var transient NavigationPoint LastAlternatePathPoint;
  18. var transient Actor OldMoveTarget;
  19. var transient Actor TeleportTarget;
  20. var transient int TeleportAttempts;
  21. var transient int ActionMoves;
  22. var int MoveAttempts;
  23. var float GuardianReachTime; // max time to reach the guardian. Gets extended if Stinky cannot see the guardian;
  24. var transient float GuardianReachDeadline;
  25. var int GuardianVisibilityChecks;
  26.  
  27. var localized string BlameStr;
  28.  
  29. function PostBeginPlay()
  30. {
  31.     FtgGame = FtgGame(Level.Game);
  32.     AmmoCandidates = FtgGame.StinkyAmmoPickups;
  33.     LastAmmoSpawnTime = Level.TimeSeconds;
  34.     super.PostBeginPlay();
  35. }
  36.  
  37. function Possess(Pawn aPawn)
  38. {
  39.     super.Possess(aPawn);
  40.     StinkyClot = StinkyClot(Pawn);
  41.     if ( StinkyClot != none && FtgGame != none) {
  42.         FtgGame.StinkyControllerReady(self);
  43.     }
  44. }
  45.  
  46. function TakeControlOf(Pawn aPawn) {}
  47.  
  48. function int GetActionCount()
  49. {
  50.     return MoveTargets.length;
  51. }
  52.  
  53. function Actor GetActionStart()
  54. {
  55.     if ( ActionNum > 0 && ActionNum - 1 < MoveTargets.length )
  56.         return MoveTargets[ActionNum - 1];
  57.     return none;
  58. }
  59.  
  60. function Actor GetActionTarget()
  61. {
  62.     if ( ActionNum < MoveTargets.length )
  63.         return MoveTargets[ActionNum];
  64.     return none;
  65. }
  66.  
  67. function Actor GetMoveTarget()
  68. {
  69.     local Actor result;
  70.  
  71.     result = GetActionTarget();
  72.     if ( result != none && result == LastAlternatePathTarget && LastAlternatePathPoint != none )
  73.         result = LastAlternatePathPoint; // target unreachable -> reroute to closest nav. point
  74.     return result;
  75. }
  76.  
  77. function Stuck()
  78. {
  79.     local NavigationPoint N;
  80.     local string s;
  81.  
  82.     if ( Target == none ) {
  83.         warn("No Target");
  84.         return;
  85.     }
  86.  
  87.     if ( LastAlternatePathTarget == Target && LastAlternatePathPoint != none ) {
  88.         // make sure we don't use this navigation point anymore
  89.         FtgGame.InvalidatePathTarget(LastAlternatePathPoint);
  90.     }
  91.  
  92.     s = "Unreachable actor " $ GetItemName(string(Target)) $ " @ (" $ Target.Location $ ")";
  93.     N = FtgGame.FindClosestPathNode(Target);
  94.  
  95.     if ( N == none ) {
  96.         log(s $ " => abort", class.name);
  97.     }
  98.     else {
  99.         log(s $ " => rerouting to " $ N, class.name);
  100.         MoveTarget = FindPathToward(N, false);
  101.     }
  102.     LastAlternatePathTarget = Target;
  103.     LastAlternatePathPoint = N;
  104. }
  105.  
  106. function CompleteAction()
  107. {
  108.     FtgGame.StinkyControllerCompeledAction(self, ActionNum++);
  109. }
  110.  
  111. function TakeActor(Actor A)
  112. {
  113.     A.SetBase(Pawn);
  114.     Pawn.AttachToBone(A, StinkyClot.GrabBone);
  115. }
  116.  
  117. function int CalcSpeed()
  118. {
  119.     return StinkyClot.OriginalGroundSpeed;
  120. }
  121.  
  122. function AdjustSpeed()
  123. {
  124.     local int spd;
  125.  
  126.     if (StinkyClot == none || StinkyClot.Health <= 0)
  127.         return;
  128.  
  129.     spd = CalcSpeed();
  130.     // if (spd != int(StinkyClot.GroundSpeed)) {
  131.     //     log("Stinky Speed " $ int(StinkyClot.GroundSpeed) $ " => " $ spd, class.name);
  132.     // }
  133.     StinkyClot.GroundSpeed = spd;
  134.     StinkyClot.WaterSpeed = spd;
  135.     StinkyClot.AirSpeed = spd;
  136.     StinkyClot.HiddenGroundSpeed = spd;
  137. }
  138.  
  139. function bool CanSpeedAdjust()
  140. {
  141.     return false;
  142. }
  143.  
  144. function float PlayCompleteAnimation()
  145. {
  146.     if( Pawn.Physics==PHYS_Falling )
  147.     {
  148.         Pawn.SetPhysics(PHYS_Walking);
  149.     }
  150.  
  151.     Pawn.SetAnimAction('KnockDown'); // dunno why but the next anim doesn't work without this
  152.     Pawn.SetAnimAction(StinkyClot.CompleteAnim);
  153.     Pawn.Acceleration = vect(0, 0, 0);
  154.     Pawn.Velocity.X = 0;
  155.     Pawn.Velocity.Y = 0;
  156.     Return 0.8;
  157. }
  158.  
  159. function float AfterCompleteCooldown()
  160. {
  161.     return 2.0;
  162. }
  163.  
  164. function WhatToDoNext()
  165. {
  166.     log("Stuck at state " $ GetStateName(), class.name);
  167. }
  168.  
  169. function DoAdditionalActions()
  170. {
  171. }
  172.  
  173. state LatentDeath
  174. {
  175. Begin:
  176.     sleep(2.0);
  177.     if ( Pawn != none ) {
  178.         Pawn.Suicide();
  179.     }
  180. }
  181.  
  182. state Moving extends Scripting
  183. {
  184.     ignores Timer;
  185.  
  186.     function AbortScript()
  187.     {
  188.         if ( StinkyClot != none ) {
  189.             StinkyClot.Suicide();
  190.         }
  191.         LeaveScripting();
  192.     }
  193.  
  194.     function SetMoveTarget()
  195.     {
  196.         Focus = ScriptedFocus;
  197.         Target = GetMoveTarget();
  198.         if ( Target == None ) {
  199.             Pawn.Suicide();
  200.             //GotoState('Broken');
  201.             return;
  202.         }
  203.         if ( Focus == None )
  204.             Focus = Target;
  205.         MoveTarget = Target;
  206.         TeleportTarget = none;
  207.  
  208.         if ( MoveTarget != none && !ActorReachable(MoveTarget) ) {
  209.             MoveTarget = FindPathToward(MoveTarget, false);
  210.             if ( MoveTarget == none && ActionMoves == 0 ) {
  211.                 // this could be a dead end, like badly placed ammo box or base guardian
  212.                 // teleport one step back and try again
  213.                 log("No path to " $ GetItemName(string(Target)), class.name);
  214.                 TeleportTarget = StinkyClot.MoveHistory[1];
  215.                 ActionMoves++;
  216.                 if ( TeleportTarget != none )
  217.                     return;
  218.             }
  219.  
  220.             if ( MoveTarget == none || (MoveTarget == OldMoveTarget && --MoveAttempts <= 0) ) {
  221.                 log("Stuck @ (" $ Pawn.Location $ ") while navigating to " $ GetItemName(string(MoveTarget))
  222.                         $ " / " $ GetItemName(string(Target)), class.name);
  223.                 StinkyClot.LogPath();
  224.  
  225.                 switch (TeleportAttempts) {
  226.                     case 0:
  227.                         if ( CurrentPath != none && CurrentPath.End != none ) {
  228.                             TeleportTarget = CurrentPath.End;
  229.                             break;
  230.                         }
  231.                         // else fallthrough
  232.                     case 1:
  233.                         if ( NextRoutePath != none && NextRoutePath.End != none ) {
  234.                             TeleportTarget = NextRoutePath.End;
  235.                             break;
  236.                         }
  237.                         // else fallthrough
  238.                     default:
  239.                         Stuck();
  240.                 }
  241.                 if ( TeleportTarget != none )
  242.                     return;
  243.             }
  244.         }
  245.  
  246.         if ( MoveTarget == None ) {
  247.             AbortScript();
  248.             return;
  249.         }
  250.  
  251.         if ( Focus == Target )
  252.             Focus = MoveTarget;
  253.         if ( OldMoveTarget != MoveTarget ) {
  254.             ActionMoves++;
  255.             StinkyClot.OnMoveTarget(MoveTarget);
  256.             OldMoveTarget = MoveTarget;
  257.             MoveAttempts = default.MoveAttempts;
  258.         }
  259.         // Level.GetLocalPlayerController().ClientMessage("Moving to " $ GetItemName(string(MoveTarget)) $ " / " $ GetItemName(string(Target)), 'log');
  260.     }
  261.  
  262.     function CompleteAction()
  263.     {
  264.         global.CompleteAction();
  265.     }
  266.  
  267. Begin:
  268.     Pawn.SetMovementPhysics();
  269.     WaitForLanding();
  270. KeepMoving:
  271.     if ( StinkyClot.TeleportPhase != StinkyClot.TELEPORT_NONE ) {
  272.         // wait for teleportation to finish
  273.         sleep(1.0);
  274.         Goto('Begin');
  275.     }
  276.     DoAdditionalActions();
  277.     SetMoveTarget();
  278. Teleporting:
  279.     if ( TeleportTarget != none ) {
  280.         StinkyClot.TeleportToActor(TeleportTarget);
  281.         TeleportTarget = none;
  282.         TeleportAttempts++;
  283.         Goto('KeepMoving');
  284.     }
  285.     TeleportAttempts = 0;
  286.     AdjustSpeed();
  287.     // MayShootTarget();
  288.     if ( MoveTarget != None && MoveTarget != Pawn ) {
  289.         MoveToward(MoveTarget, Focus,,,Pawn.bIsWalking);
  290.  
  291.         if ( !Pawn.ReachedDestination(GetMoveTarget()) ) {
  292.             Goto('KeepMoving');
  293.         }
  294.  
  295.         ActionMoves = 0;
  296.         MoveTarget = none;
  297.         // make sure the Stinky Clot won't teleport at this phase
  298.         StinkyClot.StuckCounter = 0;
  299.         StinkyClot.NextStuckTestTime = Level.TimeSeconds + 5;
  300.     }
  301. Completing:
  302.     sleep(PlayCompleteAnimation());
  303.     CompleteAction();
  304.     sleep(AfterCompleteCooldown());
  305.     WhatToDoNext();
  306. }
  307.  
  308. state MoveToGuardian extends Moving
  309. {
  310.     function BeginState()
  311.     {
  312.         super.BeginState();
  313.         SetTimer(60, false);
  314.         GuardianReachDeadline = 0;
  315.         GuardianVisibilityChecks = GuardianReachTime;
  316.     }
  317.  
  318.     function EndState()
  319.     {
  320.         super.EndState();
  321.         SetTimer(GuardianReachTime, false);
  322.     }
  323.  
  324.     function Timer()
  325.     {
  326.         if (GuardianReachDeadline == 0) {
  327.             if (GuardianVisibilityChecks <= 0 || LineOfSightTo(Target)) {
  328.                 GuardianReachDeadline = Level.TimeSeconds + GuardianReachTime;
  329.                 SetTimer(GuardianReachTime + 0.1, false);
  330.                 log(FtgGame.ScrnBalanceMut.GameTimeStr() $ " Base Deadline in " $ GuardianReachTime, class.name);
  331.             }
  332.             else {
  333.                 // wait for the line of sight
  334.                 SetTimer(1, false);
  335.                 --GuardianVisibilityChecks;
  336.             }
  337.         }
  338.         else if (Level.TimeSeconds < GuardianReachDeadline) {
  339.             SetTimer(GuardianReachDeadline - Level.TimeSeconds + 0.1, false);
  340.         }
  341.         else {
  342.             log(FtgGame.ScrnBalanceMut.GameTimeStr() $ " Base Deadline reached", class.name);
  343.             SetTimer(GuardianReachTime, false); // shouldn't trigger
  344.             // FtgBaseGuardian(Target).BlameBaseSetter(BlameStr);
  345.             TeleportTarget = GetMoveTarget();
  346.             GotoState(, 'Teleporting');
  347.         }
  348.     }
  349.  
  350.     function Stuck()
  351.     {
  352.         global.Stuck();
  353.  
  354.         if ( MoveTarget == none && LastAlternatePathPoint != none && TeleportAttempts < 3 ) {
  355.             // Most-likely spawned in glitch spot due to map level design
  356.             // teleport next to guardian
  357.             TeleportTarget = LastAlternatePathPoint;
  358.         }
  359.         // else if ( FtgBaseGuardian(Target) != none ) {
  360.         //     FtgBaseGuardian(Target).BlameBaseSetter(BlameStr);
  361.         // }
  362.     }
  363.  
  364.     function int CalcSpeed()
  365.     {
  366.         if ( FtgGame.bWaveBossInProgress )
  367.             return StinkyClot.MaxBoostSpeed;
  368.  
  369.         return min( Pawn.GroundSpeed + 2, StinkyClot.MaxBoostSpeed ) ; // each call move faster and faster
  370.     }
  371. }
  372.  
  373. state MoveToShop extends Moving
  374. {
  375.     function AbortScript()
  376.     {
  377.         if ( ActionNum < MoveTargets.length-1 )
  378.             CompleteAction();
  379.         else
  380.             super.AbortScript();
  381.     }
  382.  
  383.     function Actor GetMoveTarget()
  384.     {
  385.         if ( ActionNum < MoveTargets.length-1 && FtgGame.TotalMaxMonsters <= 0
  386.                 && FtgGame.NumMonsters <= 16 + rand(16) )
  387.         {
  388.             ActionNum = MoveTargets.length-1; // end of the wave -> move directly to the last target
  389.         }
  390.         return global.GetMoveTarget();
  391.     }
  392.  
  393.     function Stuck()
  394.     {
  395.         if ( KFAmmoPickup(Target) != none ) {
  396.             AbortScript();
  397.         }
  398.         else {
  399.             global.Stuck();
  400.         }
  401.     }
  402.  
  403.     function DoAdditionalActions()
  404.     {
  405.         local KFAmmoPickup ammo;
  406.         local int i;
  407.  
  408.         if ( FtgGame.TSCGRI.MaxMonsters < 16 )
  409.             return; // no ammo spawning during end of the game
  410.  
  411.         for ( i = AmmoCandidates.length - 1; i >= 0; --i ) {
  412.             ammo = AmmoCandidates[i];
  413.             if ( abs(Pawn.Location.Z - ammo.Location.Z) < 100
  414.                     && VSizeSquared(Pawn.Location - ammo.Location) < 1000000 // 20m
  415.                     && Pawn.FastTrace(Pawn.Location, ammo.Location) )
  416.             {
  417.                 AmmoCandidates.remove(i, 1);
  418.                 if ( ammo.bSleeping ) {
  419.                     CurrentAmmoCandidate = ammo;
  420.                     GotoState( 'MoveToAmmo', 'Begin' ); // go for ammo
  421.                 } // else ammo is already spawned
  422.                 return;
  423.             }
  424.         }
  425.     }
  426.  
  427.     function int CalcSpeed()
  428.     {
  429.         local TSCBaseGuardian gnome;
  430.  
  431.         gnome = FtgGame.TeamBases[TeamIndex];
  432.         if (FtgGame.TotalMaxMonsters <= 0 && !FtgGame.bWaveBossInProgress) {
  433.             if ( FtgGame.NumMonsters < 10 )
  434.                 return StinkyClot.MaxBoostSpeed;
  435.             else if (gnome.SameTeamCounter < gnome.default.SameTeamCounter)
  436.                 return StinkyClot.OutOfBaseSpeed; // slowdown when nobody at the base to give team a chance to reach the base
  437.             else
  438.                 return 2.0 * StinkyClot.OriginalGroundSpeed;
  439.         }
  440.         else if (gnome.SameTeamCounter < gnome.default.SameTeamCounter)
  441.             return StinkyClot.OutOfBaseSpeed; // slowdown when nobody at the base to give team a chance to reach the base
  442.         else if (FtgGame.TotalMaxMonsters < 50  && !FtgGame.bWaveBossInProgress)
  443.             return StinkyClot.OriginalGroundSpeed * (2.0 - FtgGame.TotalMaxMonsters/50.0);
  444.         else
  445.             return StinkyClot.OriginalGroundSpeed;
  446.     }
  447.  
  448.     function CompleteAction()
  449.     {
  450.         AmmoCandidates = FtgGame.StinkyAmmoPickups; // allow respawing ammo boxes
  451.         global.CompleteAction();
  452.     }
  453. }
  454.  
  455. state MoveToAmmo extends Moving
  456. {
  457.     function BeginState()
  458.     {
  459.         super.BeginState();
  460.         SetTimer(30, false);
  461.  
  462.         AmmoSpawned = 0;
  463.         AmmoSpawnCount = default.AmmoSpawnCount;
  464.         if (AmmoPPPM < 1.1 * default.AmmoPPPM) {
  465.             // extra ammo box per 3 players
  466.             AmmoSpawnCount += FtgGame.AliveTeamPlayerCount[TeamIndex] / 3;
  467.             // If AmmoPPPM drastically drops, spawn more ammo even in 1-2 player games
  468.             AmmoSpawnCount = max(AmmoSpawnCount, 1.5 * default.AmmoPPPM / AmmoPPPM);
  469.             AmmoSpawnCount = min(AmmoSpawnCount, MaxAmmoSpawnCount);
  470.         }
  471.     }
  472.  
  473.     function EndState()
  474.     {
  475.         super.EndState();
  476.         SetTimer(0, false);
  477.     }
  478.  
  479.     function Timer()
  480.     {
  481.         AbortScript();
  482.     }
  483.  
  484.     function Actor GetMoveTarget()
  485.     {
  486.         return CurrentAmmoCandidate;
  487.     }
  488.  
  489.     function AbortScript()
  490.     {
  491.         // if can't reach ammo box, then just exit the state intead of aborting the entire script
  492.         CurrentAmmoCandidate = none;
  493.         WhatToDoNext();
  494.     }
  495.  
  496.     function Stuck()
  497.     {
  498.         AbortScript();
  499.     }
  500.  
  501.     function CompleteAction()
  502.     {
  503.         local float dt;
  504.  
  505.         CurrentAmmoCandidate.GotoState('Pickup');
  506.  
  507.         if (++AmmoSpawned >= AmmoSpawnCount) {
  508.             dt = Level.TimeSeconds - LastAmmoSpawnTime;
  509.             class'ScrnFunctions'.static.lpf(AmmoPPPM,
  510.                     AmmoSpawned * 60.0 / dt / fmax(1.0, FtgGame.AliveTeamPlayerCount[TeamIndex]),
  511.                     dt, 60.0);
  512.             class'ScrnFunctions'.static.dbg(self, "Sinky Spawned " $ AmmoSpawned " ammo in " $ dt
  513.                     $ "s. AmmoPPPM="$AmmoPPPM);
  514.             LastAmmoSpawnTime = Level.TimeSeconds;
  515.             WhatToDoNext();
  516.         }
  517.     }
  518.  
  519.     function WhatToDoNext()
  520.     {
  521.         if (AmmoSpawnCount <= 0 || CurrentAmmoCandidate == none || CurrentAmmoCandidate.IsInState('Pickup')) {
  522.             // get back to the mision
  523.             CurrentAmmoCandidate = none;
  524.             GotoState('MoveToShop', 'Begin');
  525.         }
  526.         else {
  527.             // spawn ammo once again
  528.             GotoState(, 'Completing');
  529.         }
  530.     }
  531. }
  532.  
  533. defaultproperties
  534. {
  535.     GuardianReachTime=45
  536.     MoveAttempts=5
  537.     TeamIndex=1
  538.     BlameStr="%p blamed for setting the base in a glitch spot!"
  539.     AmmoSpawnCount=1
  540.     MaxAmmoSpawnCount=5
  541.     AmmoPPPM=1.0
  542. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement