Skip to content

Spawning a random group within a random group with NESS (change the thread name to fit the solution)

QuilistanQuilistan Member Posts: 186
edited April 2020 in Builders - Scripting
Let me start by saying I can script, but I typically have NO IDEA what I am doing when I do it. With Lots of trial and error a get stuff done ;)

So I have been wanting for a long while to enable the NESS Spawn System to handle Random Encounters. Loudent helped me do one for NWN2, but I have long lost that PW module. Not to mention it was a complex and clunky (though it was also pretty darn cool).

In NWNee using variables on spawn waypoints, I have been successful in getting this to work over server resets. The spawn script selects the Tag of the spawn point from strings on the waypoint its self, and then runs the spawn.

The issue is that the NESS system will "lock in" the creature type chosen on the first spawn and won't release it, so that the server MUST be reset for it to randomize it's choice again.

Is it possible to "reset an area to default" via scripting?

I do understand that this would also reset some things within the system as far as timers and what not. With a delay on the area reset, I think it would work fine though.

any help would be great!
Post edited by Quilistan on

Comments

  • GM_ODAGM_ODA Member Posts: 177
    IIRC spawn groups handles this randomized encounter stuff - our module uses it a lot to make a choice of a long list of possible creatures mostly using Select/Case scripting.
  • QuilistanQuilistan Member Posts: 186
    Thank you for the reply!

    Would you be willing to send me an example?

    It was a long while ago, but I remember having an issue with spawn groups and what I was trying to do.
  • dunahandunahan Member Posts: 139
    edited April 2020
    For you group waypoint NESS needs the extension _SG in the name like "SP_SN5_SA_RW_SG" and in the tag the name of the group, like "wild_near_city".

    My example spawns five random critters the following group out of eigth defined the script "spawn_cfg_group":
    string SpawnGroup(object oSpawn, string sTemplate) 
    ...
      if (sTemplate == "wild_near_city")
      {
        switch(d10())
        {
          case  1: sRetTemplate = "cre_dachs_n";      break;
          case  2: sRetTemplate = "cre_eber_n";       break;
          case  3: sRetTemplate = "cre_fledermaus_n"; break;
          case  4: sRetTemplate = "cre_rabe_n";       break;
          case  5: sRetTemplate = "cre_kitz_n";       break;
          case  6: sRetTemplate = "cre_reh_n";        break;
          case  7: sRetTemplate = "cre_hirsch_n";     break;
          case  8: sRetTemplate = "cre_falke_n";      break;
          case  9: sRetTemplate = "cre_waschbaer";    break;
          case 10: sRetTemplate = "cre_stinktier";    break;
        }
      }
    
    My example name consists of SpawnPoint_SpawnNumber5_SpawnAll_RandomWalk_SpawnGroup.
    Post edited by dunahan on
  • QuilistanQuilistan Member Posts: 186
    Is it possible for each case to spawn a group its self.

    case 1: select all spawns from "group_orc" tables,
    case 2: select all spawns from "group_bandit" tables,
    case 3: select all spawns from "group_bugbear" tables

    then being able to clear or reset after a period of time, to create a randomness of enemy type for that area. One time you travel there you get orc, the next bandits, making travel a bit more unpredictable.

    With a large amount of help from Loudent2, I had something similar figured out in NWN2, but do to a HD crash I lost most of that work. We had to used multiple flags to do it, I remember that.

    I am trying to piece it back together in my head. Any idea are appreciated, or it this possible with any new functions/script stuff that Beamdog has added? I am NOT a programmer, I stumble around a lot to get the scripting done that I need. So help is much appreciated.
  • dunahandunahan Member Posts: 139
    As I know, a table within a table isn't possible with the vanilla NESS. Dunno if that is possible, didn't do that yet. But you can get around when you set up three waypoints with each group. I know there is a flag that sets the spawnpoint to deactivated when activated and with a area exit script (checks about left enemies, and then doesn't fire) you reset this state.
  • QuilistanQuilistan Member Posts: 186
    does setting inactive, and then reactivating a spawn point reset it?
    What lines of script onEnter onExit are needed to set a Spawn Point active or inactive?
  • mlkent22mlkent22 Member Posts: 41
    You can do something like this within the NESS spawn_group. Here I am spawning in different placeables based on the month of the mod year.
    // Huge Pine
    if (sTemplate == "HugePine")
    {
    if (nMonth < 3 || nMonth == 12)
    switch(d2(1))
    {
    case 1:sRetTemplate = "zep_pinetr13"; break;

    }
    else if (nMonth >= 3 && nMonth < 12)
    //case 2:
    switch(d2(1))
    {
    case 1:
    sRetTemplate = "zep_pinetr12";
    break;
    }
    }


    What this does is for the the months 3-11, the green pine tree is spawned in. In months 1,2, and 12 the snowy pine is spawned. This can be done with creatures and items as well. It is also possible to use nDay in stead of nMonth to get different spawns on different days.
  • dunahandunahan Member Posts: 139
    edited April 2020
    [i] // Huge Pine
    if (sTemplate == "HugePine")
    {
    if (nMonth < 3 || nMonth == 12)
    switch(d2(1))
          {
            case 1:sRetTemplate = "zep_pinetr13"; break;
    
          }
       else if (nMonth >= 3 && nMonth < 12)
        //case 2:
          switch(d2(1))
    {
    case 1:
    sRetTemplate = "zep_pinetr12";
    break;
    }
    }
    


    Yeah, your snippet will spawn the right tree for the right season. You are using a switch/case without a second case, which could be deleted...

    The right tweak for him could be...
    if (sTemplate == "OrcBanditsBugbear")
    {
    int nTurn = GetLocalInt(oSpawnPoint, "Turn" );
    if (nTurn == 0)
    {
      switch(d4(1))
        {
          case 1: sRetTemplate = "orc001"; break;
          case 2: sRetTemplate = "orc002"; break;
          case 3: sRetTemplate = "orc003"; break;
          case 4: sRetTemplate = "orc004"; break;
        }
      SetLocalInt(oSpawnPoint, "Turn", 1); 
     } 
    else if (nTurn == 1)
    {
      switch(d4(1))
        {
          case 1: sRetTemplate = "banit001"; break;
          case 2: sRetTemplate = "bandit002"; break;
          case 3: sRetTemplate = "bandit003"; break;
          case 4: sRetTemplate = "bandit004"; break;
        }
      SetLocalInt(oSpawnPoint, "Turn", 2); 
     } 
    else if (nTurn == 2)
    {
      switch(d4(1))
        {
          case 1: sRetTemplate = "bugbear001"; break;
          case 2: sRetTemplate = "bugbear002"; break;
          case 3: sRetTemplate = "bugbear003"; break;
          case 4: sRetTemplate = "bugbear004"; break;
        }
      DeleteLocalInt(oSpawnPoint, "Turn"; 
     } 
    }
    }
    
    Post edited by dunahan on
  • QuilistanQuilistan Member Posts: 186
    I am trying something with the variables on the waypoint. I have a feeling it is getting the wrong waypoint.

    How do I verify the waypoint is in the area? or get the waypoint from a specific area?
  • QuilistanQuilistan Member Posts: 186
    onEnter Script:
    #include "spawn_functions"
    void main()
    {
    object oPC = GetEnteringObject();
    object oArea = GetArea(OBJECT_SELF);
    object oSpawn = GetWaypointByTag("basespawn");
    
    //roll your dice to pick random blueprint for spawn point variables list
    int iSpawn = d4();
    
    //Send a message for testing
    SendMessageToPC(oPC, "You rolled a " + IntToString(iSpawn));
    
    //get blueprint string from waypoint variables list
    string sTag = GetLocalString(oSpawn, "BaseTag"+IntToString(iSpawn));
    
    //set spawn point's Tag and save tag to the Area
    SetTag(oSpawn, sTag);
    SetLocalString(oArea, "randomspawn", sTag);
    
    //Send a message for testing
    SendMessageToPC(oPC, "Setting Tag To " + sTag);
    
    //make sure oSpawn is activated
    NESS_ActivateSpawn(oSpawn);
    
    //NESS spawn stuff
      if ( GetIsAreaAboveGround( OBJECT_SELF ) &&
        ! GetIsAreaNatural( OBJECT_SELF ) )
      {
        // Indoors - no delay on the first HB
        Spawn_OnAreaEnter( "spawn_sample_hb", 10.0 );
      }
    
      else
      {
        // Outdoors or underground - do a 3 second delay on the first HB
        Spawn_OnAreaEnter( "spawn_sample_hb", 10.0, 3.0 );
      }
    }
    

    Then on the spawn waypoint I set variable strings with the blueprints

    BaseTag1 string goblin
    BaseTag2 string orc
    BaseTag3 string bandit
    BaseTag4 string kobold

    onExit Script:
    #include "spawn_functions"
    
    void main()
    {
        Spawn_OnAreaExit();
    
    object oPC = GetExitingObject();
    object oArea = GetArea(OBJECT_SELF);
    
    //Get the spawn point's tag from the area
    string sTag = GetLocalString(oArea, "randomspawn");
    
    //Get the spawn point with the tag
    object oSpawn = GetWaypointByTag(sTag);
    
    //reset the tag of the spawn point
    SetTag(oSpawn, "basespawn");
    SendMessageToPC(oPC, "Setting Tag To basespawn");
    }
    

    I think currently the onExit is grabbing a waypoint from a different area that has the same blueprint. I need to be able to verify it is the way point in the specific area.

    I am trying this with single creatures at the moment, but I think if it works it will work with groups.

    hopefully in embedded the script correctly......
  • dunahandunahan Member Posts: 139
    If you placed more than one spawnpoint with this tag, it will be complicated to get the right one...

    Instead I would add the area tag/resref to the tag, so you could get the right one. If more than one is placed in this specific area, then add another string, like '_a' for the first, and so on.
  • QuilistanQuilistan Member Posts: 186
    yep...

    I tested it with unique blueprints just to make sure, but I fear that NESS won't let go of the tag once it has fired. If one could get NESS to recheck the tag of the spawn point it would work. I am not sure if that is possible.
  • dunahandunahan Member Posts: 139
    edited April 2020
    As I remember, NESS saves the tags of spawnpoints on the area itself. So the script don't have to check every point again.

    I'll look into the scripts again in the evening (actually it is four o'clock here in my time zone).

    Edit:
    As I described earlier, I would rather add some code to the spawn_cfg_group.nss. I didn't tested it, but I think that should do the trick?
    if (sTemplate == "OrcBanditsBugbear")
    {
      int nTurn = GetLocalInt(oSpawn, "Turn" );
      if (nTurn == 0)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "orc001"; break;
          case 2: sRetTemplate = "orc002"; break;
          case 3: sRetTemplate = "orc003"; break;
          case 4: sRetTemplate = "orc004"; break;
        }
        SetLocalInt(oSpawn, "Turn", 1); 
      } 
      else if (nTurn == 1)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "banit001"; break;
          case 2: sRetTemplate = "bandit002"; break;
          case 3: sRetTemplate = "bandit003"; break;
          case 4: sRetTemplate = "bandit004"; break;
        }
        SetLocalInt(oSpawn, "Turn", 2); 
      } 
      else if (nTurn == 2)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "bugbear001"; break;
          case 2: sRetTemplate = "bugbear002"; break;
          case 3: sRetTemplate = "bugbear003"; break;
          case 4: sRetTemplate = "bugbear004"; break;
        }
        DeleteLocalInt(oSpawn, "Turn"; 
      } 
    }
    
    Post edited by dunahan on
  • QuilistanQuilistan Member Posts: 186
    been messing around with the script you posted.
    if (sTemplate == "kagesgroups")
    SetLocalInt(oSpawn, "Turn", d4(1));
    {
       int nTurn = GetLocalInt(oSpawn, "Turn" );
      if (nTurn == 1)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kole"; break;
          case 2: sRetTemplate = "kobold_strongarm"; break;
          case 3: sRetTemplate = "koboldgrunt"; break;
          case 4: sRetTemplate = "kobold007"; break;
        }
        SetLocalInt(oSpawn, "Turn", 2);
      }
      else if (nTurn == 2)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kadyn"; break;
          case 2: sRetTemplate = "mino_hunter"; break;
          case 3: sRetTemplate = "minotaur_warrior"; break;
          case 4: sRetTemplate = "mino_shaman"; break;
        }
        SetLocalInt(oSpawn, "Turn", 3);
      }
      else if (nTurn == 3)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "nick"; break;
          case 2: sRetTemplate = "gobsoldier"; break;
          case 3: sRetTemplate = "gob_elite"; break;
          case 4: sRetTemplate = "gobarcher"; break;
        }
        SetLocalInt(oSpawn, "Turn", 4);
       }
      else if (nTurn == 4)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kage"; break;
          case 2: sRetTemplate = "orc_warrior"; break;
          case 3: sRetTemplate = "orc_fighter"; break;
          case 4: sRetTemplate = "orcarcher"; break;
        }
    DelayCommand(10.0, DeleteLocalInt(oSpawn, "Turn"));
      }
    }
    
    This one fires and spawns random creature, but it picks from all the groups listed in the same spawn. Meaning you can get a kobold, minotaur, goblin, and orc in the same spawn time.

    I had to add the SetLocalInt(oSpawn, "Turn", d4(1)); otherwise it was doing nothing. I was also trying the delay deleting the Int to see if that helped, here it didn't.
    if (sTemplate == "nicksgroups")
    {
        if (GetLocalInt(oSpawn, "Group") == 0)
            {
                SetLocalInt(oSpawn, "Group", d4(1));
            }
    
      int nTurn = GetLocalInt(oSpawn, "Group" );
    
      if (nTurn == 1)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kole"; break;
          case 2: sRetTemplate = "kobold_strongarm"; break;
          case 3: sRetTemplate = "koboldgrunt"; break;
          case 4: sRetTemplate = "kobold007"; break;
        }
        //SetLocalInt(oSpawn, "Turn", 1);
      }
      else if (nTurn == 2)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kadyn"; break;
          case 2: sRetTemplate = "mino_hunter"; break;
          case 3: sRetTemplate = "minotaur_warrior"; break;
          case 4: sRetTemplate = "mino_shaman"; break;
        }
        //SetLocalInt(oSpawn, "Turn", 2);
      }
      else if (nTurn == 3)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "nick"; break;
          case 2: sRetTemplate = "gobsoldier"; break;
          case 3: sRetTemplate = "gob_elite"; break;
          case 4: sRetTemplate = "gobarcher"; break;
        }
    
       }
      else if (nTurn == 4)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kage"; break;
          case 2: sRetTemplate = "orc_warrior"; break;
          case 3: sRetTemplate = "orc_fighter"; break;
          case 4: sRetTemplate = "orcarcher"; break;
        }
        DelayCommand(30.0, SetLocalInt(oSpawn, "Group", d4(1)));
        DelayCommand(30.1, DeleteLocalInt(oSpawn, "Group"));
      }
    }
    
    In this one it is picking from ONE group only like I would like, but it picks the same group EVERY single time over a server reset. It can be random between server resets (although it really does like to roll a 3, lol )

    any ideas?
  • dunahandunahan Member Posts: 139
    Ah, I had a typo :( Didn't saw that on the first sight. Wrote that script snipped on the mobile phone :D
    And I hadn't thought about a second problem that could occour (you already found them)...
    If you change your code from the first snippet it should work now as you want.
    // for this group you will need more info about the spawnpoint...
    // int nChildrenSpawned = GetLocalInt(oSpawn, "ChildrenSpawned");	// those data is important to get it 
    // int nSpawnNumber = GetLocalInt(oSpawn, "f_SpawnNumber");		// to work
    if (sTemplate == "kagesgroups")
    {
      int nTurn = GetLocalInt(oSpawn, "Turn" );		// has the variable already set?
      if (nTurn == 0) SetLocalInt(oSpawn, "Turn", 1);	// if not, then set it to 1
    
      if (nTurn == 1)							// var is set to 1 so take all the spawns from this group
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kole"; break;
          case 2: sRetTemplate = "kobold_strongarm"; break;
          case 3: sRetTemplate = "koboldgrunt"; break;
          case 4: sRetTemplate = "kobold007"; break;
        }
        if (nChildrenSpawned == nSpawnNumber)		// that should check if all creatures where spawned
          SetLocalInt(oSpawn, "Turn", 2);				// and jump to spawntable two
      }
      else if (nTurn == 2)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kadyn"; break;
          case 2: sRetTemplate = "mino_hunter"; break;
          case 3: sRetTemplate = "minotaur_warrior"; break;
          case 4: sRetTemplate = "mino_shaman"; break;
        }
        if (nChildrenSpawned == nSpawnNumber)		// that should check if all creatures where spawned
          SetLocalInt(oSpawn, "Turn", 3);				// and jump to spawntable three
      }
      else if (nTurn == 3)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "nick"; break;
          case 2: sRetTemplate = "gobsoldier"; break;
          case 3: sRetTemplate = "gob_elite"; break;
          case 4: sRetTemplate = "gobarcher"; break;
        }
        if (nChildrenSpawned == nSpawnNumber)		// that should check if all creatures where spawned
          SetLocalInt(oSpawn, "Turn", 4);				// and jump to spawntable four
      }
      else if (nTurn == 4)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kage"; break;
          case 2: sRetTemplate = "orc_warrior"; break;
          case 3: sRetTemplate = "orc_fighter"; break;
          case 4: sRetTemplate = "orcarcher"; break;
        }
        if (nChildrenSpawned == nSpawnNumber)		// that should check if all creatures where spawned
          SetLocalInt(oSpawn, "Turn", 1);				// and jump to spawntable one
      }
    }
    
  • QuilistanQuilistan Member Posts: 186
    Sweet, I will give this a shot when I get done with work!

    Question: Does this spawn them in a sequential pattern? or random? if it is random I am failing to understand how? (I like random because I play on my server, and random keeps me guessing instead of knowing patterns.)
  • QuilistanQuilistan Member Posts: 186
    After testing, this is what spawn each round for a spawnpoint with SP_IS1_SG_SN04_SA_SD02_CD60T1R7

    1 round was 3 kobolds
    2 round was 2 kobolds 2 minotaur (I would like this to not happen)
    3 round was 4 minotaur
    4 round was 3 minotaur (I had a previous one dominated)
    5 round was 3 minotaur (I had a previous one dominated)
    6 round was 4 minotaur
    7 round was 4 minotaur

    seems to get stuck or something?
  • QuilistanQuilistan Member Posts: 186
    edited April 2020
    Ok I had a successful test with this:
    if (sTemplate == "nicksgroups")
    {
        if (GetLocalInt(oSpawn, "Group") == 0)
            {
                SetLocalInt(oSpawn, "Group", d4(1));
            }
    
      int nTurn = GetLocalInt(oSpawn, "Group" );
    
      if (nTurn == 1)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kole"; break;
          case 2: sRetTemplate = "kobold_strongarm"; break;
          case 3: sRetTemplate = "koboldgrunt"; break;
          case 4: sRetTemplate = "kobold007"; break;
        }
        SetLocalInt(oSpawn, "Group", 1);
      }
      else if (nTurn == 2)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kadyn"; break;
          case 2: sRetTemplate = "mino_hunter"; break;
          case 3: sRetTemplate = "minotaur_warrior"; break;
          case 4: sRetTemplate = "mino_shaman"; break;
        }
        SetLocalInt(oSpawn, "Group", 2);
      }
      else if (nTurn == 3)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "nick"; break;
          case 2: sRetTemplate = "gobsoldier"; break;
          case 3: sRetTemplate = "gob_elite"; break;
          case 4: sRetTemplate = "gobarcher"; break;
        }
         SetLocalInt(oSpawn, "Group", 3);
       }
      else if (nTurn == 4)
      {
        switch(d4(1))
        {
          case 1: sRetTemplate = "kage"; break;
          case 2: sRetTemplate = "orc_warrior"; break;
          case 3: sRetTemplate = "orc_fighter"; break;
          case 4: sRetTemplate = "orcarcher"; break;
        }
         SetLocalInt(oSpawn, "Group", 4);
      }
        DelayCommand(30.0, SetLocalInt(oSpawn, "Group", 0));
        DelayCommand(30.1, DeleteLocalInt(oSpawn, "Group"));
    }
    
    

    Though I have only done 1 small test, it is the first one to work completely correct.
    Edit: I tested with 2 spawn points in the same area, and they are working perfect also.

    SP_IS1_SG_SN04_SA_SD03_CD90T1R7

    spawning all 4 of the same group
    and spawning each group randomly, so getting all Orcs, or goblins, or minotaur, or kobold!

    Is there anything wrong with doing it this way, or is something not needed?

    Post edited by Quilistan on
  • dunahandunahan Member Posts: 139
    The code is okay, it could be a little bit streamlined, but due this isn't called often, I think that isn't a must.

    I would at least delete this line:
    DelayCommand(30.1, DeleteLocalInt(oSpawn, "Group"));
    You are setting Group already to 0. So you leave the variable on the spawnpoint, but reset it.
  • QuilistanQuilistan Member Posts: 186
    Thank you so much for your help, this is a great tool to use and really opens up the possibilities for me!

    I just took a character and traveled one of the Trade Routes on my PW (Balarand). I had 3 random encounters: Gnolls (archer, witch, and fighters) attacked me near a ruined watch tower, and later on I ran into a pack of wolves ( 3 regular, 2 Dire, and 1 Pack Leader). Next, I was then waylaid by bandits (archers, footmen, and sorcerers). All perfectly handled by NESS!

    The next time I travel the road it will be completely different!

    With more intricate Dice rolls like d100(), I should be able add some rare encounters.

    Needless to say I am excited..... Once again thank you!
  • NR_RainmanNR_Rainman Member Posts: 6
    mlkent22 wrote: »
    You can do something like this within the NESS spawn_group. Here I am spawning in different placeables based on the month of the mod year.
    // Huge Pine
    if (sTemplate == "HugePine")
    {
    if (nMonth < 3 || nMonth == 12)
    switch(d2(1))
    {
    case 1:sRetTemplate = "zep_pinetr13"; break;

    }
    else if (nMonth >= 3 && nMonth < 12)
    //case 2:
    switch(d2(1))
    {
    case 1:
    sRetTemplate = "zep_pinetr12";
    break;
    }
    }


    What this does is for the the months 3-11, the green pine tree is spawned in. In months 1,2, and 12 the snowy pine is spawned. This can be done with creatures and items as well. It is also possible to use nDay in stead of nMonth to get different spawns on different days.

    Sorry how did you get placables to work within a Spawn Group?
Sign In or Register to comment.