Skip to content

Heartbeat script question

How taxing would this script be on an area?
// Tinmans Heartbeat area respawner test script
// Created 12\8\2023
void main()
{
// Get the object type of the object that runs the script
int nObjectType = GetObjectType(OBJECT_SELF);

// Check if the object is a placeable
if (nObjectType == OBJECT_TYPE_PLACEABLE)
{
    // Use the Tag of a Standard Creature
    // Use the resref of a Custom Creature
    string sTag = GetTag(OBJECT_SELF);

    // Check if the object has a local variable named “DO_ONCE”
    if (GetLocalInt(OBJECT_SELF, "DO_ONCE") == 0)
    {
        // Set the variable to 1, so the script will not fire again
        SetLocalInt(OBJECT_SELF, "DO_ONCE", 1);

        // We run this section only once
        CreateObject (OBJECT_TYPE_CREATURE, sTag, GetLocation (OBJECT_SELF), FALSE);
    }

// Check if the object is an area
if (nObjectType == OBJECT_TYPE_PLACEABLE)
{
     nObjectType = GetObjectType(OBJECT_SELF);

    // Get the area object
    object oArea = GetArea(OBJECT_SELF);

    // Get the first creature in the area
    object oCreature = GetFirstObjectInArea(oArea);

    // The delay time for the spawner script
    float fDelay = 20.0;

    // Loop through all creatures in the area
    while (GetIsObjectValid(oCreature))
    {
        // Check if the creature is dead
        if (GetIsDead(oCreature))
        {
            // Increment the delay time by 7 seconds
            //fDelay += 7.0;//Not sure i will use this option

            // Get the tag of the creature
            string sTag = GetTag(oCreature);

            // Find the closest placeable with the same tag as the creature
            object oClosestPlaceable = GetNearestObjectByTag(sTag, oCreature);

            // Check if the placeable is valid
            if (GetIsObjectValid(oClosestPlaceable))
            {
                // Reset the variable of the closest placeable to 0
                DelayCommand(fDelay, SetLocalInt(oClosestPlaceable, "DO_ONCE", 0));
            }
        }

        // Get the next creature in the area
        oCreature = GetNextObjectInArea(oArea);
    }
}

}
}

Comments

  • ForSeriousForSerious Member Posts: 446
    Noticing a few things that don't look correct. I'm not really sure what you intend them for.
    void main()
    {
        int nObjectType = GetObjectType(OBJECT_SELF);
        if (nObjectType == OBJECT_TYPE_PLACEABLE)
        {
            string sTag = GetTag(OBJECT_SELF);
            if (GetLocalInt(OBJECT_SELF, "DO_ONCE") == 0)
            {
                SetLocalInt(OBJECT_SELF, "DO_ONCE", 1);
                CreateObject (OBJECT_TYPE_CREATURE, sTag, GetLocation (OBJECT_SELF), FALSE);
            }
        // Check if the object is an area
        // I don't think this should be used on an area. And that's another OBJECT_TYPE_PLACEABLE check.
        // This should be in the first if statement or a check for a different type. (or !=)
        if (nObjectType == OBJECT_TYPE_PLACEABLE)
        {
        // GetObjectType(OBJECT_SELF) will return the same thing it already is.
             nObjectType = GetObjectType(OBJECT_SELF);
            object oArea = GetArea(OBJECT_SELF);
            // Get the first creature in the area
            // This can return placeables. Probably need to check if it's a creature.
            object oCreature = GetFirstObjectInArea(oArea);
            float fDelay = 20.0;
            while (GetIsObjectValid(oCreature))
            {
                if (GetIsDead(oCreature))
                {
                    string sTag = GetTag(oCreature);
                    object oClosestPlaceable = GetNearestObjectByTag(sTag, oCreature);
                    if (GetIsObjectValid(oClosestPlaceable))
                    {
                        DelayCommand(fDelay, SetLocalInt(oClosestPlaceable, "DO_ONCE", 0));
                    }
                }
                oCreature = GetNextObjectInArea(oArea);
            }
        }
    }
    

    To me it looks like you will need one placeable for every one creature you want to respawn.
    Would it be easier to make a waypoint with a tag and a variable and the do_once variable?
    On heartbeat go through all the waypoints with that tag. If not do_once, spawn the creature stored in the variable. I suppose you would need to modify the onDeath script of the creature to reset the do_once variable of the correct waypoint. I've done a very similar thing but with placeables. They don't move, so I can with confidence call GetNearestObjectByTag when the placeable gets destroyed.
  • meaglynmeaglyn Member Posts: 149
    That script as written won't be taxing at all run as an area HB. It will only get the object type, see that it's not PLACEABLE and be done.
  • TheTinmanTheTinman Member Posts: 72
    edited December 2023
    I tested it on one placeable object and it worked as intended. Haven't had time to test it on the areas heartbeat script yet. This is just my first draft. :D. Will test it more when I get off work.
    To me it looks like you will need one placeable for every one creature you want to respawn
    Yes it is set to run this way. That is why I asked my question. If multiple placeable run this in one area...who knows. :#:D
    Post edited by TheTinman on
  • meaglynmeaglyn Member Posts: 149
    If this is run on a placeable then yeah, it could cause some load. Each placeable you have this on will cycle through every object in the area, every hb. Also, some creatures may be gone and get missed (after they decay). And there is nothing that says the nearest placeable is the thing that spawned the creature (unless the don't move in which case it is pretty likely).

    You might consider storing the spawning placeable object on the spawned creature. You'd know exactly which placeable the creature needs to reset. Then you could clear the do_once thing in the on death handler of the creature and not have to loop through everything (actually... assign the delay command to the placeable since the creature will likely be gone and so the delayed command will go away).

    If you want to do it in the placeable HB, have the placeable store the creature object so it can easily check its creature (since you seem to have this 1:1).
  • MelkiorMelkior Member Posts: 181
    What I'd probably do with something like this is to incorporate it into the module heartbeat, but I'd cycle through all PCs instead of through all tagged waypoints, and for each PC I'd find which area the PC is in and then run a void function on the area which does all of the hard work of cycling through the waypoints in the area and performing all the other functions of creating NPCs or waiting before resetting the timer to create a new NPC.
    That way, you minimise how many items the script has to deal with and therefore how long it takes to run.
  • meaglynmeaglyn Member Posts: 149
    Sure, there are other ways (not the least of which is just to use NESS). I was trying to go in the direction the OP had started :)
  • TheTinmanTheTinman Member Posts: 72
    edited December 2023
    Here is what i came up with after a little bit of testing. Havent tested it with a heavy load of placeables as of yet.
    // Tinmans Heartbeat area respawner test script
    // Created 12\8\2023
    
    // This script runs on the areas heartbeat
    void main()
    {
    
    // Get the area object that is running this script
    object oArea = OBJECT_SELF;
    
    // Get the first object in the area
    object oObject = GetFirstObjectInArea(oArea);
    
    // Loop through all the objects in the area
    while (GetIsObjectValid(oObject))
    {
    
        // Check if the object is a placeable object
        if (GetObjectType(oObject) == OBJECT_TYPE_PLACEABLE)
        {
    
            // Get the tag of the placeable object
            string sTag = GetTag(oObject);
    
            // Check if the placeable object has not spawned a creature yet
            if (GetLocalInt(oObject, "DO_ONCE") == 0)
            {
    
                // Set a local variable on the placeable object to prevent multiple spawns
                SetLocalInt(oObject, "DO_ONCE", 1);
    
                // Create a creature object with the same tag as the placeable object at its location
                object oCreature = CreateObject (OBJECT_TYPE_CREATURE, sTag, GetLocation (oObject), FALSE);
    
                // Set a local variable on the creature object to store a reference to the placeable object
                SetLocalObject(oCreature, "SPAWNER", oObject);
            }
        }
    
        // Check if the object is a creature object and if it is dead
        if (GetObjectType(oObject) == OBJECT_TYPE_CREATURE && GetIsDead(oObject))
        {
    
             // Set the current hit points of the creature object to 1 to prevent it from disappearing
            // Used to offset the creature decay time and the area heartbeat 
            SetCurrentHitPoints(oObject, 1);
    
            // Get the reference to the placeable object that spawned the creature object
            object oSpawner = GetLocalObject(oObject, "SPAWNER");
    
            // Check if the reference is valid
            if (GetIsObjectValid(oSpawner))
            {
    
                // Set a delay of 20 seconds
                float fDelay = 20.0;
    
                // Add 7 seconds to the delay
               // fDelay += 7.0;  // Not sure I will use this feature
    
                // After the delay, reset the local variable on the placeable object to allow another spawn
                DelayCommand(fDelay, SetLocalInt(oSpawner, "DO_ONCE", 0));
            }
        }
    
        // Get the next object in the area
        oObject = GetNextObjectInArea(oArea);
    }
    
    }
    
  • MelkiorMelkior Member Posts: 181
    That script is "assuming" that every placeable in the area is a monster spawn placeable. You never check the tag of the placeable to ensure that it's a monster spawn placeable.
  • TheTinmanTheTinman Member Posts: 72
    The tag of the placeable is the tag of the creature it is spawning. If it is not a creature tag it is skipped. It would still have to check every placeable in the area anyway.
  • MelkiorMelkior Member Posts: 181
    TheTinman wrote: »
    The tag of the placeable is the tag of the creature it is spawning. If it is not a creature tag it is skipped. It would still have to check every placeable in the area anyway.

    object GetObjectByTag(string, int);
    Finds objects based on the object's tag.
    You'd be better off if you gave all of the spawn objects the same tag, searched only for the objects with that tag, then used a string variable on the spawn object as the ResRef of the creature to spawn. The script would have a lot less work to do, that way, taking up a lot less time to run due to not having to run as many instructions.

    The following is just to illustrate what I was thinking of. It doesn't do the delay you wanted (except that it would delay until the body of the creature vanishes, so you could "fudge it" by making the body decay time be the time you wanted until respawn). This is a module heartbeat script.
    void main()
    {
      string sFind="creaturespawner";// The tag of all of the creature spawn placeables
      object oThing=GetObjectByTag(sFind,0);// Find the first one
      string sMakeIt=GetLocalString(oThing,"SPAWN");// Get the tag of what to spawn
      object oWhat=GetLocalObject(oThing,"MADE");// check for an object ID in case it's already spawned
      int iIndex=1;// Point to the next creature spawn placeable
      while(GetIsObjectValid(oThing))// Loop until no more creature spawn placeables
      {
        if(!GetIsObjectValid(oWhat))// Only do the next two instructions if the creature does not exist
        {
          oWhat=CreateObject(OBJECT_TYPE_CREATURE,sMakeIt,GetLocation(oThing));// Create the creature where the placeable is
          SetLocalObject(oThing,"MADE",oWhat);// Set the object pointer to the created creature so we can try to find it later
        }
        oThing=GetObjectByTag(sFind,iIndex++);// Find the next creature spawn placeable, if any
        sMakeIt=GetLocalString(oThing,"SPAWN");// Find out what to spawn there
        oWhat=GetLocalObject(oThing,"MADE");// Find the creature of the last creature spawned here, if any was.
      }// Loop until there are no more spawn placeables left to look through
    }
    
    I expect you would be able to modify this code to match exactly what you want it to do.
  • TheTinmanTheTinman Member Posts: 72
    edited December 2023
    I thought about doing it with variables like this, but that seemed like a lot of setup. I liked the idea of placing this heartbeat script, renaming some placeables(every area has them) and being done. :D
    Post edited by TheTinman on
  • MelkiorMelkior Member Posts: 181
    TheTinman wrote: »
    I thought about doing it with variables like this, but that seemed like a lot of setup. I liked the idea of placing this heartbeat script, renaming some placeables(every area has them) and being done. :D

    In any game of respectable length, you'll quickly run into the dreaded "Too Many Instructions" error. If you override that with the system setting, every heartbeat will cause your game to pause which will make it unplayable. It will only work if your game is just a few areas with hardly any placeable objects.
  • TheTinmanTheTinman Member Posts: 72
    That makes sense. One last question about this script. Because i search for dead creatures with a heartbeat so they can respawn, sometimes the variable to respawn is not set and the creature doesnt respawn. I like the randomness this creates.
    Once the variable is not reset on the placeable will it take some of the load off of this script?
  • MelkiorMelkior Member Posts: 181
    edited December 2023
    I'm not sure which version of script you're talking about, but personally I wouldn't trust a random error like that. I'd be more inclined to deliberately program a random respawn interval (within limits). My first guess would be that you're already hitting the dreaded TMI error from time to time, which is causing the script to abort and so some creatures don't get respawned.
    My second guess would be that the body has despawned sometimes by the time your script gets around to that creature. That's why I use the object pointer instead of the actual creature in order to detect whether or not it's dead.
    I'll see if I can come up with something which will do everything you want, although I'm a little puzzled about why you aren't just using an encounter, since you can spawn a single creature reliably that way, and set it to infinite respawn, and set the respawn delay?
  • ForSeriousForSerious Member Posts: 446
    …And put the trigger area to the whole area. And scale up what creature gets spawned with the player levels.
Sign In or Register to comment.