Dynamic Enemy Reinforcements tweak (make the spawns friendly)

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
SaucepanSF
Posts: 20
Joined: Thu Dec 30, 2021 7:03 pm

Dynamic Enemy Reinforcements tweak (make the spawns friendly)

Post by SaucepanSF »

I know very little about doom scripting, so am at a roadblock trying to figure this out. If I don't get an answer here, I'll delve deeper into learning how zscript works and maybe figure it out. But I really just wanted to tweak this one thing, so am reaching out to the community as a possible quick solution.

What I want to do:
- Tweak fcas3t's Dynamic Enemy Reinforcement mod to make the spawns FRIENDLY instead of the default (HOSTILE).

Here's his code (and please, tell me if this is not proper to do here). I'd ask him, but he doesn't appear to exist on the forum anymore. Also, this is my first ever thread so be kind. I look up to all of you.

Thanks in advance for reading, and any help you can provide.

Code: Select all

version 4.2

class derSP : Actor  // spawn point
{
    Default { +NOINTERACTION }
}

class DER_EventHandler : EventHandler
{
    private Array< derSP > AllStaticSPs;

    private Array< string > EnemyRoster;
    private int LastRosterIdx;

    private int timer;
    private bool retrying;

    private bool SpawnOneAt( derSP spawnPoint )
    {
        Actor m = Actor.Spawn( EnemyRoster[ random( 0, LastRosterIdx ) ],
                               spawnPoint.pos, ALLOW_REPLACE );
        if ( !m )
        {
            return false;
        }

        Vector2 offset = ( random(-2, 2) * 2, random(-2, 2) * 2 );
        offset *= m.radius;
        Vector3 offsetpos = ( m.pos.xy + offset, m.pos.z );

        Vector2 lineVector = offsetpos.xy - m.pos.xy;
        FLineTraceData ltdata;
        m.LineTrace( VectorAngle( lineVector.x, lineVector.y ),
                     lineVector.length(), 0, TRF_THRUACTORS, data: ltdata );

        m.SetOrigin( m.pos + (ltdata.HitDir).unit() * (ltdata.distance - 8.0), false );

        double nudge = m.radius * 2.0;

        for ( double a = 0.1; a < 359.0; a += 22.5 )
        {
            m.LineTrace( a, nudge, 0, data: ltdata );

            if ( ltdata.distance < nudge )
            {
                m.SetOrigin( m.pos - ltdata.HitDir * (nudge - ltdata.distance), false );
                break;
            }
        }
        m.SetOrigin(( m.pos.xy, m.floorz ), false );

        if ( m.height > ( m.ceilingz - m.floorz )
             ||  !m.TestMobjLocation()  ||  !m.CheckMove( m.pos.xy ))
        {
            m.ClearCounters();
            m.Destroy();
            return false;
        }

        m.angle = spawnPoint.angle + frandom( -45, 45 );

        Actor.Spawn( "TeleportFog", m.pos );

        return true;
    }

    /* This function is invoked both at the start of each map and when loading a
       save file. Because of the second case, LastRosterIdx must not be set here,
       which is fine, since the proper value from the 3rd tick is in the save file.
     */
    override void OnRegister()
    {
        timer = 35 * random( der_min_seconds, der_max_seconds );
        retrying = false;
    }

    override void WorldTick()
    {
        if ( level.time == 2 )
        {
            let iterator = ThinkerIterator.Create( "Actor" );
            Actor m;
            while ( m = Actor( iterator.Next() ))
            {
                if ( GetDefaultByType( Actor.GetReplacee( m.GetClassName() )).bIsMonster
                     &&  !m.bBoss  &&  !m.bBossDeath )
                {
                    derSP sp = derSP( Actor.Spawn( "derSP", m.pos ));
                    sp.angle = m.angle;

                    AllStaticSPs.push( sp );

                    string cName = m.GetClassName();

                    /* Barons, Cyberdemons, Masterminds, Arachnotrons, and Mancubi
                       are already excluded because of their boss flags.
                     */
                    if ( cName != "EVPSpectre"   &&
                         cName != "EVPRevenant"  &&
                         cName != "EVPArchvile"  &&
                         cName != "EVPPainElemental" )
                    {
                        EnemyRoster.push( cName );
                    }
                }
            }

            for ( int j = der_dilute; j > 0; j -= 1 )
            {
                EnemyRoster.push( "Zombieman" );
                EnemyRoster.push( "DoomImp" );
                EnemyRoster.push( "ShotgunGuy" );
            }

            LastRosterIdx = EnemyRoster.size() - 1;
        }

        timer -= 1;
        if ( timer > 0  ||  LastRosterIdx == -1 )
        {
            return;
        }

        if ( !retrying  &&  random( 0, 99 ) >= der_chance )
        {
            timer = 35 * random( der_min_seconds, der_max_seconds );
            return;
        }

        let player = players[ 0 ].mo;

        Array< derSP > validSPs;

        for ( int j = AllStaticSPs.size() - 1;
              j >= 0; j -= 1 )
        {
            double dist = player.Distance2D( AllStaticSPs[ j ] );

            if ( dist >= der_min_distance  &&  dist <= der_max_distance )
            {
                validSPs.push( AllStaticSPs[ j ] );
            }
        }
        if ( validSPs.size() == 0 )
        {
            timer = 3 * 35;
            retrying = true;
            return;
        }

        timer = 35 * random( der_min_seconds, der_max_seconds );
        retrying = false;

        int lastVspIdx = validSPs.size() - 1;

        for ( int j = random( der_min_groups, der_max_groups );
              j > 0; j -= 1 )
        {
            derSP sp = validSPs[ random( 0, lastVspIdx ) ];

            int numFails = 0;

            for ( int k = random( der_min_pergroup, der_max_pergroup );
                  k > 0; k -= 1 )
            {
                if ( SpawnOneAt( sp ) == false )
                {
                    numFails += 1;
                    if ( numFails == 10 )
                    {
                        break;
                    }
                    // try again
                    k += 1;
                }
            }
        }
    }
}
Last edited by SaucepanSF on Tue Mar 21, 2023 8:40 pm, edited 2 times in total.
User avatar
Player701
 
 
Posts: 1710
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: Dynamic Enemy Reinforcements tweak (make the spawns friendly)

Post by Player701 »

Please use code tags when pasting code in the future to make it easier to read (5th button from the left above the message edit form).

Regarding your question. First, find this line:

Code: Select all

m.angle = spawnPoint.angle + frandom( -45, 45 );
and add the following code below it:

Code: Select all

m.ClearCounters();
m.bFriendly = true;
The first line ensures the monster doesn't count towards the kill percentage (you probably don't what that for friendlies), the second line sets the friendly flag.
SaucepanSF
Posts: 20
Joined: Thu Dec 30, 2021 7:03 pm

Re: Dynamic Enemy Reinforcements tweak (make the spawns friendly)

Post by SaucepanSF »

Noted. And thank you so much.
Post Reply

Return to “Scripting”