For direct access use https://forums.oldunreal.com
It's been quite a while since oldunreal had an overhaul, but we are moving to another server which require some updates and changes. The biggest change is the migration of our old reliable YaBB forum to phpBB. This system expects you to login with your username and old password known from YaBB.
If you experience any problems there is also the usual "password forgotten" function. Don't forget to clear your browser cache!
If you have any further concerns feel free to contact me: Smirftsch@oldunreal.com

Issue #71. Waking a sitting Titan up may become impossible

Report bugs, read about fixes, new features and ask questions about the Unreal 227 patch here. Place comments and commit suggestions.
Post Reply
User avatar
Masterkent
OldUnreal Member
Posts: 1469
Joined: Fri Apr 05, 2013 12:41 pm

Issue #71. Waking a sitting Titan up may become impossible

Post by Masterkent »

The check for EventInstigator.bIsPlayer in function UnrealI.Titan.Sitting.Trigger doesn't look reasonable and sometimes produces real troubles.

Code: Select all

state Sitting
{
      ignores SeePlayer, HearNoise, Bump, TakeDamage;

      function Trigger( actor Other, pawn EventInstigator )
      {
            if ( EventInstigator.bIsPlayer )
            {
                  AttitudeToPlayer = ATTITUDE_Hate;
                  Enemy = EventInstigator;
                  GotoState('Sitting', 'GetUp');
            }
            Disable('Trigger');
      }

      function BeginState()
      {
            bProjTarget = false;
      }

GetUp:
      bProjTarget = true;
      PlayAnim('TGetUp');
      FinishAnim();
      SetCollisionSize(0, Default.CollisionHeight);
      SetPhysics(PHYS_Walking);
      DesiredSpeed = 1.0;
      Acceleration = vector(Rotation) * AccelRate;
      PlayAnim('TWalk001');
      FinishAnim();
      SetCollisionSize(Default.CollisionRadius, Default.CollisionHeight);
      GotoState('Attacking');

Begin:
      TweenAnim('TSit', 0.05);
      SetPhysics(PHYS_None);
}
Firstly, it can't reliably work in a network game when function Trigger is called by a Dispatcher with a delay, because the player who initiated the event may leave the game and the corresponding PlayerPawn actor may be destroyed and garbage-collected during the pause before the delayed event issued by the Dispatcher, then parameter EventInstigator becomes a none reference and the check for EventInstigator.bIsPlayer fails as an erroneous attempt to access none. Since Disable('Trigger') is invoked, there will be no second chance to wake the Titan up and then kill him. This may lead to a trouble if players have to kill him in order to finish the map.

Secondly, if a non-player pawn triggers a sitting Titan somehow (e.g. by hitting a Trigger with TriggerType == TT_Shoot which then eventually invokes function Titan.Sitting.Trigger), then, again, players don't have a chance to wake the Titan up later.

At this moment I can recall only two maps with sitting Titans: VeloraEnd and Jones-05-TemplePart3. Both maps can't be finished if the check for EventInstigator.bIsPlayer fails for any reason (and I saw how such things happened in online game - this is why I had to find out where the given issue originates from). From my point of view, this check is inherently wrong by design and probably many mappers are not aware of it, so it should be just removed. If we move Disable('Trigger') to the if-block instead, there would still be a big chance that retriggering won't be possible because it would have to be initiated by something that can be triggered at most once (e.g. by a Trigger with bTriggerOnceOnly == true - as in case of the two aforementioned maps).

Mappers who want to prevent triggering by non-players should use more reliable and reasonable methods. In particular, mappers should either make sure that the decision is made before the event instigator could be entirely destroyed or properly handle possible none references. For example, if a TT_Shoot Trigger is supposed to trigger a Dispatcher which is supposed to trigger a sitting Titan, then it's possible to insert an intermediate filter between the Trigger and the Dispatcher so that the Dispatcher would be triggered only when the shooter is known to be a player:

Code: Select all

// Transforms an incoming event designated by Tag to an outcoming event designated by Event.
// The outcoming event is issued only if the instigator of the incoming event is known to be a player.
class PlayerEventTransformer expands Triggers;

var() bool bTriggerOnceOnly;

event Trigger(Actor A, Pawn EventInstigator)
{
      // if the instigator is none/unknown or not a player, then ignore the incoming event
      if (EventInstigator == none || EventInstigator.PlayerReplicationInfo == none)
            return;

      Disable('Trigger'); // recursive calls to Trigger are not allowed
      TriggerEvent(Event, self, EventInstigator);
      if (!bTriggerOnceOnly)
            Enable('Trigger');
}

Code: Select all

Trigger
    Event == 'TriggerIsShot'

PlayerEventTransformer
    Tag == 'TriggerIsShot'
    Event == 'TriggerIsShotByPlayer'

Dispatcher
    Tag == 'TriggerIsShotByPlayer'
    OutEvents[0] == 'WakeTitan'

Titan
    Tag == 'WakeTitan'
Suggested changes to UnrealI.Titan.Sitting.Trigger:

Code: Select all

      function Trigger( actor Other, pawn EventInstigator )
      {
-            if ( EventInstigator.bIsPlayer )
-            {
-                  AttitudeToPlayer = ATTITUDE_Hate;
-                  Enemy = EventInstigator;
-                  GotoState('Sitting', 'GetUp');
-            }
+            AttitudeToPlayer = ATTITUDE_Hate;
+            if (EventInstigator != none && EventInstigator.bIsPlayer)
+                  Enemy = EventInstigator;
+            GotoState('Sitting', 'GetUp');
            Disable('Trigger');
      }
Since the state WalkOut has a similar code, I'd suggest to change it in the same way:

Code: Select all

      function Trigger( actor Other, pawn EventInstigator )
      {
-            if ( EventInstigator.bIsPlayer )
-            {
-                  AttitudeToPlayer = ATTITUDE_Hate;
-                  Enemy = EventInstigator;
-                  GotoState('WalkOut', 'Walk');
-            }
+            AttitudeToPlayer = ATTITUDE_Hate;
+            if (EventInstigator != none && EventInstigator.bIsPlayer)
+                  Enemy = EventInstigator;
+            GotoState('WalkOut', 'Walk');
            Disable('Trigger');
      }
Last edited by Masterkent on Thu May 09, 2019 9:36 am, edited 1 time in total.
User avatar
Smirftsch
Administrator
Posts: 8999
Joined: Wed Apr 29, 1998 10:00 pm
Location: NaPali
Contact:

Re: Issue #71. Waking a sitting Titan up may become impossible

Post by Smirftsch »

great find, indeed explains some things :)
Sometimes you have to lose a fight to win the war.
Post Reply

Return to “Unreal 227”