Legacy:XPawn Priority Bug - Unreal Wiki

# Legacy:XPawn Priority Bug

### From Unreal Wiki, The Unreal Engine Documentation Site

## Overview

### Goal

This page will describe, and will hopefully provide solutions to, the xPawn "priority bug" in UT2003 and UT2004.

### Volume Priority

The `priority` variable found in PhysicsVolume determines which one of several overlapping PhysicsVolumes gets to apply its physics properties (Gravity, GroundFriction, ZoneVelocity and so on) on players and other actors in the overlapping area. The higher the value of `priority`, the higher the Priority. Every PhysicsVolume added, however, even if `priority` is 0, has a higher priority than the DefaultPhysicsVolume.

### The Bug - In Brief

There is a bug in the implementation of the priority scheme in xPawn as it pertains to interactions with PhysicsVolumes. If the player is contained within overlapping volumes and one of the volumes has `bWaterVolume` set to `True`, splashing sounds are likely to be played when a Player walks, dodges, or whatever, regardless of which volume has priority.

## Cause

### How Volume Priority And Splashing Are Handled

#### PlayerReplicationInfo

The following code was extracted from an export of the PlayerReplicationInfo class, UT2004 build 3186

    function UpdatePlayerLocation()
    {
        local Volume V, Best;
        local Pawn P;
        local Controller C;

        C = Controller(Owner);

        if( C != None )
            P = C.Pawn;

        if( P == None )
        {
            PlayerVolume = None;
            PlayerZone = None;
            return;
        }

        if ( PlayerZone != P.Region.Zone )
                    PlayerZone = P.Region.Zone;

        foreach P.TouchingActors( class'Volume', V )
        {
            if( V.LocationName == "")
                continue;

            if( (Best != None) && (V.LocationPriority <= Best.LocationPriority) )
                continue;

            if( V.Encompasses(P) )
                Best = V;
        }
        if ( PlayerVolume != Best )
                    PlayerVolume = Best;
    }

#### xPawn

The following code was extracted from an export of the xPawn class, UT2004 build 3186

    simulated function FootStepping(int Side)
    {
        local int SurfaceNum, i;
            local actor A;
            local material FloorMat;
            local vector HL,HN,Start,End;

        SurfaceNum = 0;

        for ( i=0; i<Touching.Length; i++ )
                    if ( ((PhysicsVolume(Touching[i]) != None) && PhysicsVolume(Touching[i]).bWaterVolume)
                            || (FluidSurfaceInfo(Touching[i]) != None) )
                    {
                            if ( FRand() < 0.5 )
                                    PlaySound(sound'PlayerSounds.FootStepWater2', SLOT_Interact, FootstepVolume );
                            else
                                    PlaySound(sound'PlayerSounds.FootStepWater1', SLOT_Interact, FootstepVolume );
                            return;
                    }

            if ( bIsCrouched || bIsWalking )
                    return;

            if ( (Base!=None) && (!Base.IsA('LevelInfo')) && (Base.SurfaceType!=0) )
            {
                    SurfaceNum = Base.SurfaceType;
            }
            else
            {
                    Start = Location - Vect(0,0,1)*CollisionHeight;
                    End = Start - Vect(0,0,16);
                    A = Trace(hl,hn,End,Start,false,,FloorMat);
                    if (FloorMat !=None)
                            SurfaceNum = FloorMat.SurfaceType;
            }
            PlaySound(SoundFootsteps[SurfaceNum], SLOT_Interact, FootstepVolume,,400 );
    }

#### PhysicsVolume

The following code was extracted from an export of the PhysicsVolume class, UT2004 build 3186

    simulated function PostBeginPlay()
    {
            super.PostBeginPlay();

            BACKUP_Gravity          = Gravity;
            BACKUP_bPainCausing     = bPainCausing;
            if( VolumeEffect == None && bWaterVolume )
                    VolumeEffect = new(None) class'EFFECT_WaterVolume';
    }

    //
    // (...)
    //

    event touch(Actor Other)
    {
            local Pawn P;
            local bool bFoundPawn;

            Super.Touch(Other);
            if ( Other == None )
                    return;
            if ( bNoInventory && (Pickup(Other) != None) && (Other.Owner == None) )
            {
                    Other.LifeSpan = 1.5;
                    return;
            }
            if ( bMoveProjectiles && (ZoneVelocity != vect(0,0,0)) )
            {
                    if ( Other.Physics == PHYS_Projectile )
                            Other.Velocity += ZoneVelocity;
                    else if ( (Other.Base == None) && Other.IsA('Emitter') && (Other.Physics == PHYS_None) )
                    {
                            Other.SetPhysics(PHYS_Projectile);
                            Other.Velocity += ZoneVelocity;
                    }
            }
            if ( bPainCausing )
            {
                    if ( Other.bDestroyInPainVolume )
                    {
                            Other.Destroy();
                            return;
                    }
                    if ( Other.bCanBeDamaged && !Other.bStatic )
                    {
                            CausePainTo(Other);
                            if ( Other == None )
                                    return;
                            if ( PainTimer == None )
                                    PainTimer = Spawn(class'VolumeTimer', self);
                            else if ( Pawn(Other) != None )
                            {
                                    ForEach TouchingActors(class'Pawn', P)
                                            if ( (P != Other) && P.bCanBeDamaged )
                                            {
                                                    bFoundPawn = true;
                                                    break;
                                            }
                                    if ( !bFoundPawn )
                                            PainTimer.SetTimer(1.0,true);
                            }
                    }
            }
            if ( bWaterVolume && Other.CanSplash() )
                    PlayEntrySplash(Other);
    }

    //
    // (...)
    //

    function PlayEntrySplash(Actor Other)
    {
            local float SplashSize;
            local actor splash;

            splashSize = FClamp(0.00003 * Other.Mass * (250 - 0.5 * FMax(-600,Other.Velocity.Z)), 0.1, 1.0 );
            if( EntrySound != None )
            {
                    PlaySound(EntrySound, SLOT_Interact, splashSize);
                    if ( Other.Instigator != None )
                            MakeNoise(SplashSize);
            }
            if( EntryActor != None )
            {
                    splash = Spawn(EntryActor);
                    if ( splash != None )
                            splash.SetDrawScale(splashSize);
            }
    }

    event untouch(Actor Other)
    {
            if ( bWaterVolume && Other.CanSplash() )
                    PlayExitSplash(Other);
    }

    function PlayExitSplash(Actor Other)
    {
            local float SplashSize;
            local actor splash;

            splashSize = FClamp(0.003 * Other.Mass, 0.1, 1.0 );
            if( ExitSound != None )
                    PlaySound(ExitSound, SLOT_Interact, splashSize);
            if( ExitActor != None )
            {
                    splash = Spawn(ExitActor);
                    if ( splash != None )
                            splash.SetDrawScale(splashSize);
            }
    }

### The Problem

The most obvious problem is that the FootStepping() function in xPawn takes it upon itself to check for touching PhysicsVolumes, rather than using the `PlayerVolume` var in PlayerReplicationInfo, in order to determine if a "splash" should be "played". In doing this, it bypasses the priority check made in the UpdatePlayerLocation() function of PlayerReplicationInfo, thus eliminating the priority scheme for the "splashing" functionality.

## Solutions

### Proposal One: Avoid Overlapping Volumes

Note: Obviously, this is just a work-around...

Just like the title says: Avoid overlapping PhysicsVolumes.

Pros:

- You avoid the issue all together

Cons:

- You potentially sacrifice certain senarios–"dynamic" volumes attached to movers interacting with one another, for instance.

### Proposal Two: Modify xPawn

One solution may be to add a new check to the simulated function FootStepping() in xPawn (or in your own new pawn class) that compares touching volumes with `bWaterVolume` set to `True` with `PlayerVolume` in PlayerReplicationInfo. If they are not one in the same, skip the splashing code.

Pros:

- Will likely solve the problem

Cons:

- By extending xPawn, you now essentially are forced into making a mod/mutator in order to impliment your changes. Pawn-type is set in the player class (by default, xPlayer) which is in turn is set by a GameInfo actor like xDeathMatch.

Categories: Legacy Mapping | Legacy Troubleshooting

##### Views

- Legacy
- Discussion
- Current revision

##### Search