CheckSolidFooting

From ZDoom Wiki
Jump to navigation Jump to search

bool CheckSolidFooting (int tid, int flags)

Usage

Returns true if the actor with the specified tid is standing on the ground, an actor or a 3D floor. If tid is 0, the function uses the activator.

This custom function is a ZDoom 2.8-compatible ACS equivalent of A_CheckSolidFooting

Parameters

  • tid: TID of the actor.
  • flags: The following flags can be combined by using | between the constant names:
    • CSF_SOLIDGROUND — Checks if the actor is standing on solid ground
    • CSF_SOLIDACTORS — Checks if the actor is standing on a shootable actor
    • CSF_ALL — Combination of CSF_SOLIDGROUND and CSF_SOLIDACTORS

Implementation

Define the following actors in DECORATE:

actor CheckSolidFooting
{
    +PUFFONACTORS
    +BLOODLESSIMPACT
    +HITTARGET
    +PAINLESS
    
    Height 0
    
    states
    {
        Spawn:
            TNT1 A 0
            stop
    }
}

actor UnSetShootable : CustomInventory
{
    states
    {
        Pickup:
            TNT1 A 0 A_ChangeFlag("SHOOTABLE", false)
            stop
    }
}

actor SetShootable : CustomInventory
{
    states
    {
        Pickup:
            TNT1 A 0 A_ChangeFlag("SHOOTABLE", true)
            stop
    }
}

If using standard ACC, define the following constants:

#define CSF_SOLIDGROUND 1
#define CSF_SOLIDACTORS 2
#define CSF_ALL (CSF_SOLIDGROUND|CSF_SOLIDACTORS)

If using GDCC-ACC, define the following enum:

enum ECheckSolidFootingFlags
{
    CSF_SOLIDGROUND = 1,
    CSF_SOLIDACTORS = 2,
    
    CSF_ALL = CSF_SOLIDGROUND|CSF_SOLIDACTORS,
}

If using standard ACC, then use the following version of the function:

function bool CheckSolidFooting(int tid, int flags)
{
    int x = GetActorX(0);
    int y = GetActorY(0);
    int z = GetActorZ(0);
    int fz = GetActorFloorZ(0);
    int vz = GetActorVelZ(0);
    int radius = GetActorProperty(0, APROP_Radius);
    
    // we need to remember the activator
    if (!tid)
    {
        Thing_ChangeTID(tid, UniqueTID());
        tid = ActivatorTID();
    }
    
    // remember this for later
    bool shootable = CheckFlag(0, "SHOOTABLE");
    
    // unset shootable on actor
    GiveInventory("UnSetShootable", 1);
    
    for (int px = -radius; px <= radius; px += radius)
        for (int py = -radius; py <= radius; py += radius)
    {
        int point = UniqueTID();
        SpawnForced("CheckSolidFooting", x + px, y + py, z, point);
        
        int check = UniqueTID();
        LineAttack(point, 0, 0.25, 0, "CheckSolidFooting", "", 9.0, FHF_NORANDOMPUFFZ|FHF_NOIMPACTDECAL, check);
        
        if (ThingCount(T_NONE, check) > 0)
        {
            if ((flags & CSF_SOLIDACTORS) && SetActivatorToTarget(check))
            {
                int height  = GetActorProperty(0, APROP_Height);
                int hitzpos = GetActorZ(0);
                int hitzvel = GetActorVelZ(0);
                
                // restore activator
                SetActivator(tid);
                
                // restore shootable flag
                if (shootable)
                    GiveInventory("SetShootable", 1);
                
                if (z == hitzpos + height && vz == hitzvel)
                    return true;
                else
                    return false;
            }
            
            // restore shootable flag
            if (shootable)
                GiveInventory("SetShootable", 1);
            
            if (flags & CSF_SOLIDGROUND)
                return true;
        }
    }
    
    // restore shootable flag
    if (shootable)
        GiveInventory("SetShootable", 1);
    
    if (flags & CSF_SOLIDGROUND)
        return z <= fz;
    return false;
}

If using GDCC-ACC with #pragma fixed on, use this version of the function:

function bool CheckSolidFooting(int tid, int flags)
{
    fixed x = GetActorX(0);
    fixed y = GetActorY(0);
    fixed z = GetActorZ(0);
    fixed fz = GetActorFloorZ(0);
    fixed vz = GetActorVelZ(0);
    fixed radius = GetActorPropertyFixed(0, APROP_Radius);
    
    // we need to remember the activator
    if (!tid)
    {
        Thing_ChangeTID(tid, UniqueTID());
        tid = ActivatorTID();
    }
    
    // remember this for later
    bool shootable = CheckFlag(0, "SHOOTABLE");
    
    // unset shootable on actor
    GiveInventory("UnSetShootable", 1);
    
    for (fixed px = -radius; px <= radius; px += radius)
        for (fixed py = -radius; py <= radius; py += radius)
    {
        int point = UniqueTID();
        SpawnForced("CheckSolidFooting", x + px, y + py, z, point);
        
        int check = UniqueTID();
        LineAttack(point, 0, 0.25, 0, "CheckSolidFooting", "", 9.0, FHF_NORANDOMPUFFZ|FHF_NOIMPACTDECAL, check);
        
        if (ThingCount(T_NONE, check) > 0)
        {
            if ((flags & CSF_SOLIDACTORS) && SetActivatorToTarget(check))
            {
                fixed height  = GetActorPropertyFixed(0, APROP_Height);
                fixed hitzpos = GetActorZ(0);
                fixed hitzvel = GetActorVelZ(0);
                
                // restore activator
                SetActivator(tid);
                
                // restore shootable flag
                if (shootable)
                    GiveInventory("SetShootable", 1);
                
                if (z == hitzpos + height && vz == hitzvel)
                    return true;
                else
                    return false;
            }
            
            // restore shootable flag
            if (shootable)
                GiveInventory("SetShootable", 1);
            
            if (flags & CSF_SOLIDGROUND)
                return true;
        }
    }
    
    // restore shootable flag
    if (shootable)
        GiveInventory("SetShootable", 1);
    
    return flags & CSF_SOLIDGROUND ? z <= fz : false;
}

Examples

The following example will force the player into the first frame of their Death state when on the ground, and the first frame of their Spawn state when in the air.

script "CheckSolidFootingExample" enter
{
    for (;;)
    {
        if (CheckSolidFooting(0, CSF_ALL))
            SetActorState(0, "Death");
        else
            SetActorState(0, "Spawn");
        
        Delay(1);
    }
}

See also