Spawning Conditions

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!)
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Spawning Conditions

Post by Misery »

Can you create a conditional for an item to spawn only when the player isn't "looking," i.e. ammo that respawns every minute or so, but if the player is looking, or is in the sector, the item will wait until the player leaves, or isn't looking, or both?
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

The actor class has a [wiki=CheckIfSeen]CheckIfSeen()[/wiki] function that tells if any player can see it.
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

So I would have to create a copy of the items with that particular ZScript function? And if so, how would I implement that to prevent spawning on the condition that the player can see the item?
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

Misery wrote:So I would have to create a copy of the items with that particular ZScript function? And if so, how would I implement that to prevent spawning on the condition that the player can see the item?
You'd probably want to create a subclass, yes.

It's pretty simple. I'm guessing your spawner does something like this:
  1. Spawn an item
  2. wait for player to pick up item
  3. start countdown
  4. at the end of countdown go to #1
You just change it to this:
  1. Spawn an item
  2. wait for player to pick up item
  3. start countdown
  4. at the end of countdown check if player is looking
  5. if so, go to #3
  6. else go to #1
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

I couldn't find any examples to confirm the syntax, so this is as close as I got. Also, it won't have a sprite, so I put NONE, 'cause I don't know what to put if there is no sprite.

Code: Select all

Class DemonSpawner : RandomSpawner

{

	bool CheckIfSeen()
	{
		if (CheckSight (target) == true)
		{
			return;
		}
	}

	Default
	{
		//$Category Monsters/Special Monsters
		//$Title Demon Spawner
		DropItem "Demon"
	}

	States
	{
		Spawn:
			NONE A 10;
			Loop;
		See:
			NONE A 1 CheckIfSeen();
			NONE A 70 A_SpawnItemEx ("Demon");
			Loop;
	}
}
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

My bad, I've never actually used spawners before. Looking at the code of RandomSpawner, I completely misunderstood how they work. Let me try something and I'll post it when it works, if someone else doesn't beat me to it
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

Ok, threw this together copying code from RandomSpawner. Stripped out some code, so don't use it to spawn items directly, instead use it to a spawn another spawner which has all the appropriate code.

Code: Select all

class ShySpawner : actor
{
    const MAX_RANDOMSPAWNERS_RECURSION = 32; // Should be largely more than enough, honestly.
    
    default
    {
        SpawnerWhenSeen.ClassToSpawn "unknown";
    }
    
    string ClassToSpawn;
    
    property ClassToSpawn : ClassToSPawn;

    override void tick()
    {
        super.tick();
    
        if (!CheckIfSeen()) return;
        
        DoSpawn();
    }
    
    override Activate(actor activator)
    {
        DoSpawn();
    }
    
    actor DoSpawn()
    {
        if (ClassToSpawn ~== "Unknown") // Spawn error markers immediately.
        {
            Spawn(ClassToSpawn, Pos, NO_REPLACE);
            Destroy();
            return null;
        }
        if (ClassToSpawn ~== "None") // ChooseSpawn chose to spawn nothing.
        {
            Destroy();
            return null;
        }

        Class<Actor> cls = ClassToSpawn;
        if (cls != null)
        {
            Class<Actor> rep = GetReplacement(cls);
            if (rep != null)
            {
                cls = rep;
            }
        }
        if (cls == null)
        {
            A_Log(TEXTCOLOR_RED .. "Unknown item class ".. ClassToSpawn .." to drop from SpawnerWhenSeen\n");
            Destroy();
            return null;
        }

        if (bouncecount >= MAX_RANDOMSPAWNERS_RECURSION)    // Prevents infinite recursions
        {
            Spawn("Unknown", Pos, NO_REPLACE);        // Show that there's a problem.
            Destroy();
            return null;
        }

        Actor newmobj = Spawn(cls, Pos, NO_REPLACE);
        bool boss = false;

        if (newmobj != null)
        {
            // copy everything relevant
            newmobj.SpawnAngle = SpawnAngle;
            newmobj.Angle        = Angle;
            newmobj.Pitch        = Pitch;
            newmobj.Roll        = Roll;
            newmobj.SpawnPoint = SpawnPoint;
            newmobj.special    = special;
            newmobj.args[0]    = args[0];
            newmobj.args[1]    = args[1];
            newmobj.args[2]    = args[2];
            newmobj.args[3]    = args[3];
            newmobj.args[4]    = args[4];
            newmobj.special1   = special1;
            newmobj.special2   = special2;
            newmobj.SpawnFlags = SpawnFlags & ~MTF_SECRET;    // MTF_SECRET needs special treatment to avoid incrementing the secret counter twice. It had already been processed for the spawner itself.
            newmobj.HandleSpawnFlags();
            newmobj.SpawnFlags = SpawnFlags;
            newmobj.bCountSecret = SpawnFlags & MTF_SECRET;    // "Transfer" count secret flag to spawned actor
            newmobj.ChangeTid(tid);
            newmobj.Vel    = Vel;
            newmobj.master = master;    // For things such as DamageMaster/DamageChildren, transfer mastery.
            newmobj.target = target;
            newmobj.tracer = tracer;
            newmobj.CopyFriendliness(self, false);
            // This handles things such as projectiles with the MF4_SPECTRAL flag that have
            // a health set to -2 after spawning, for internal reasons.
            if (health != SpawnHealth()) newmobj.health = health;
            if (!bDropped) newmobj.bDropped = false;
            // Handle special altitude flags
            if (newmobj.bSpawnCeiling)
            {
                newmobj.SetZ(newmobj.ceilingz - newmobj.Height - SpawnPoint.Z);
            }
            else if (newmobj.bSpawnFloat) 
            {
                double space = newmobj.ceilingz - newmobj.Height - newmobj.floorz;
                if (space > 48)
                {
                    space -= 40;
                    newmobj.SetZ((space * random[randomspawn]()) / 256. + newmobj.floorz + 40);
                }
                newmobj.AddZ(SpawnPoint.Z);
            }
            if (newmobj.bMissile && !(newmobj is 'SpawnerWhenSeen'))
                newmobj.CheckMissileSpawn(0);
            // Bouncecount is used to count how many recursions we're in.
            if (newmobj is 'SpawnerWhenSeen')
                newmobj.bouncecount = ++bouncecount;
        }
        Destroy();
        return newmobj;
    }
}
It will spawn the specified object when not seen, and also if activated. So you can give it a TID and activate it with a line trigger if you'd prefer it that way.

EDIT I forgot that CheckIfSeen() logic is flipped, edited the code to negate it.
Last edited by Sir Robin on Sun Jul 17, 2022 3:03 pm, edited 1 time in total.
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

Sir Robin wrote:Stripped out some code, so don't use it to spawn items directly, instead use it to a spawn another spawner which has all the appropriate code.
Sorry, I don't understand ZScript with that level of complexity. How do I utilize it that way? I jus' needed a spawner that spawned monsters and ammo when the player wasn't looking. If the player not looking wasn't a requirement, I would jus' use a Map Spot. Is it possible to create a subclass of Map Spot that goes dormant when the player is looking? And reactivates after the player turns away?
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

Pretty easy:

Code: Select all

//first create your random spawner:
class MyZombieSpawner : RandomSpawner
{
   default
   {
      DropItem "ZombieMan", 255, 7;
      DropItem "ShotgunGuy", 255, 3;
   }
}

//Then create a shy spawner:
class MyShyZombieSpawner : ShySpawner
{
   default
      {
         SpawnerWhenSeen.ClassToSpawn "MyZombieSpawner";
      }
}
The way it works is that ShySpawner will spawn whatever you tell it as soon as the players aren't looking, so now spawn that instead of the random spawner directly.
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

Is it possible to create the subclass of Map Spot, instead? One that will become dormant when the player can see it, and become active when the player turns away? If I inherited from Map Spot, could I use the bool CheckIfSeen to cause the Actor to become dormant if it returns true? And become active once it returns false?
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

yeah, that's pretty easy to do:

Code: Select all

class ShyMapSpot : MapSpot
{
    override void tick()
    {
        if (CheckIfSeen())
        {
            if (bDormant)
            {
                bDormant = false;
                console.printf("ShyMapSpot Activated");//DEBUG
            }
        }
        else
        {
            if (!bDormant)
            {
                bDormant = true;
                console.printf("ShyMapSpot Deactivated");//DEBUG
            }
        }
        Super.Tick();
    }
}
 
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

Thanks, that one seems much easier to understand. Sorry if I was difficult, I jus' didn't understand the first one. My ZScript skills are fairly rudimentary.
User avatar
Misery
Posts: 157
Joined: Sun Nov 04, 2018 4:57 pm

Re: Spawning Conditions

Post by Misery »

How exactly does CheckIfSeen() work? It seems to affect the Actor only when there's geometry between it and the player. If I'm only facin' away from it, it doesn't change its behavior.
User avatar
Sir Robin
Posts: 392
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Spawning Conditions

Post by Sir Robin »

Misery wrote:Thanks, that one seems much easier to understand. Sorry if I was difficult, I jus' didn't understand the first one. My ZScript skills are fairly rudimentary.
I don't understand everytihng in the code either, I just copied it out of zscript\actors\shared\randomspawner.zs in the gzdoom.pk3 file, then took out anything related to missiles and bosses since that's not what I was designing it to spawn.
Misery wrote:How exactly does CheckIfSeen() work? It seems to affect the Actor only when there's geometry between it and the player. If I'm only facin' away from it, it doesn't change its behavior.
Yeah, it basically draws a line between the actor and each player and if there is no geometry blocking that line then it is considered seen. It doesn't actually check the angle and fov. For that I think you can use [wiki=CheckSight_(ZScript)]CheckSight[/wiki] or one of the other [wiki=ZScript_actor_functions#Checks]checks[/wiki] but I haven't tried that out myself yet.

I actually need that feature in a mod I'm writing, so I'm about to try it out and will post what I find out.
User avatar
Enjay
 
 
Posts: 26403
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland

Re: Spawning Conditions

Post by Enjay »

I wonder if I can get a clarification on this:

"Checks if the Actor is visible to any players or cameras a player is looking through. " (from the Wiki [wiki]CheckIfSeen[/wiki] page)

How does this handle camera textures? I mean, if the map has a camera texture, and the actor concerned is in view of the camera texture, does the CheckIfSeen register as the thing is seen all the time (because a camera that is actively being cast to a texture can see it) or would it only register as seen if a player could see the camera texture?

Reason I ask is that camera textures sometimes seem to reveal parts of a map on the automap even if the player has not yet been to the part of the map where either the camera or the camera texture are.

Return to “Scripting”