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);
}
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'
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');
}
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');
}