Author Topic: NESS spawn system  (Read 644 times)

Legacy_Surek

  • Full Member
  • ***
  • Posts: 169
  • Karma: +0/-0
NESS spawn system
« on: June 28, 2013, 03:49:56 am »


                Does any one know where I could get a copy of ness spawn system now that the vault is down? not sure where to go. '<img'>
               
               

               
            

Legacy_Fester Pot

  • Hero Member
  • *****
  • Posts: 1698
  • Karma: +0/-0
NESS spawn system
« Reply #1 on: June 28, 2013, 05:18:22 am »


               You can still get it yourself.

Simply modify your hosts file and add the following -

69.10.25.55 nwvault.ign.com

C:\\Windows\\system32\\drivers\\etc\\hosts

FP!
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #2 on: June 28, 2013, 06:17:55 am »


               I am sharing the ERF and tutorial for NESS on my google drive temporarily.

https://docs.google....kZVcklka203WHM/
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #3 on: November 07, 2014, 05:22:52 pm »


               

Due to the thread's title I am co-opting it for a question:


 


How many of you have extensively modified NESS for your purposes?


 


I have. And I am wondering whether others have, and what those modifications are.


 


 


I have made very good use of NESS over the years, and extended it as I needed. I suspect others have had similar needs and made similar modifications. But I might be wrong in that. Perhaps many of you have had different needs and made different modifications.


 


One example, I store many local variables on a spawn point in order to customize the spawn. Any local data that can be put on a creature blueprint, I also can setup on a spawn point.  Alternate name, alternate tag, appearance changing, equipment initialization, special AI flags and so on. I just migrate the data from the spawn point to the creature when it is created. And then its OnSpawn event does the rest.


 


I'd like to hear what others have done. And see where the conversation goes from there.



               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
NESS spawn system
« Reply #4 on: November 08, 2014, 07:09:10 pm »


               

As we discussed a while back I added the NT (new tag) flag which I use a lot. The tag of the child is either following the flag or a local variable on the spawn point. I then use the spawn script feature of NESS to do the rest of the setup of the creature since that runs before the onspawn script. Since the tag has been set right it's usually based on the tag (or tag and area etc).


 


The other thing that I've done is made the spawn hours wrap over days so you can spawn from, say 22 to 2.  And added spawn month support. 


 


Not really extensively modified, just a few hacks...


               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #5 on: November 09, 2014, 12:19:32 am »


               

This week I added a code block in NESS's custom check script which prevents a spawn unless the NPC is currently "located" at the spawn point. "NPC Location" is a state variable that I stick in the database, and manipulate with schedules and other scripts, that is also placed on a spawn point as a kind of "location flag" to be matched with the NPC's location state.  I thought using a NESS check script would cover me for this feature, but while I can check whether to spawn, I found that there was no method in NESS to check whether it was time to despawn on the NPC's schedule (outside of the rather limited and static schedule tags that NESS offers). So I added some code at the top of ProcessSpawn which determines if the NPC has now (according to their schedule or some other thing) moved on to be associated with a different spawn point and if the NPC has moved on it then despawns them.


 


Another thing that I was surprised to see missing was a flag that allows you to specify use of the appear and disappear animations with a spawn. I added that one too.


 


The more I play with these systems, the more I find I need to extend them. And then the more I do that, the more I am tempted to rip the whole thing apart and rewrite it. But in this case its much more of a task than I am motivated to do. If it was 2004 or 2005 I'd rewrite NESS to work more efficiently and be better organized, but these days - as I think the silence in this thread shows - it doesn't seem to be worth it to others. So all changes I guess should only be pursued if it is worth it to your own project.


 


Anyway, just rambling about this. I am always hoping for more people to chime in here and there to talk about this stuff.



               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
NESS spawn system
« Reply #6 on: November 09, 2014, 01:22:48 am »


               

If that NPC location code is fairly self contained I would not mind seeing a patch of it. I'm starting to poke at how to keep unique NPCs unique if they have several possible locations but have not given it too much thought yet, Sounds like a reasonable approach. Could you PM or email a diff?


 


As to the last part, I agree completely. I have rewritten most of the code I've imported mainly for cleanliness of code, efficiency, extensions and to make them fit with other systems. Much of the time these end up being more starting points. NESS is one I have not done that to either since it's such a big thing and the testing of a rewrite would take forever.



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #7 on: November 09, 2014, 03:58:25 am »


               

well... i don't think what i have is elegant, but at this point i just want to get it working so this is how the code works now:

 

 


This is what I have for despawning in ProcessSpawn near the top (or at the top if you wish):

 



string sNPCSchedule = GetLocalString(oSpawn,"NPC_SCHEDULE_SCRIPT")

as you can see this is a local string which I set on the spawn point. The string is the "schedule" script that I run for the NPC. It has all the logic that is needed to determine how the creature moves on its own. Stuff like: if creature location is 1, and if the creature has been there for 3 days change the location to #2. Or whatever it is that you want to do.

 



Then later on in ProcessSpawn (note that this happens in the loop which iterates through the child creatures of the spawn point):



        if (nSpawnChild == TRUE)
        {
            // Add to Count Total
            nSpawnCount++;
            //SpawnCountDebug("+ spawn count to " + IntToString(nSpawnCount));
            nSpawnBlock = FALSE;

            // Check Despawning
            nDespawning = GetLocalInt(oCreature, "Despawning");

            // NEW CODE BLOCK FOR SCHEDULE HERE - henesua
            // check NPC schedule to determine whether NPC should despawn
            if(sNPCSchedule!="")
            {
                // first check NPC's schedule and possibly update location
                ExecuteScript(sNPCSchedule,oSpawn);

                // next check the creature's scheduled location versus the location ID on the spawn point
                // I am using integers for this but you could use any identifier for this as all you need is a match
                if(GetCreatureLocationState(GetPCIdentifier(oCreature))!=GetLocalInt(oSpawn,"NPC_LOCATION_ID"))
                {
                    // if the scheduled location is no longer the same as the present despawn the NPC
                    nDespawning = TRUE;
                    nSpawnDespawn = TRUE;
                }
            }

For the spawn check - "spawn_chk_custom"



    // CHECK NPC LOCATION STATUS PRIOR TO SPAWN
    // intention is to enable an NPC to move their spawn point between locations
    // example: a PC can spawn at either location 1, 2, or 3 depending on a quest state.
    // this script runs a custom schedule script (if the NPC has one)
    // then it checks the database for the NPCs location state (location state is always an integer)
    else if (nSpawnCheckCustom == 2)
    {
        string sNPCID       = GetLocalString(oSpawn, "PCID");
        if(sNPCID=="")
        {
            object oTemp    = CreateObject(OBJECT_TYPE_CREATURE,GetLocalString(oSpawn,"NESS_TAG"),GetLocation(GetWaypointByTag("wp_temp_location")));
            sNPCID          = GetPCIdentifier(oTemp);
            SetLocalString(oSpawn, "PCID",sNPCID);
            DestroyObject(oTemp);
        }

        string sNPCSchedule = GetLocalString(oSpawn,"NPC_SCHEDULE_SCRIPT");
        if(sNPCSchedule!="")
            ExecuteScript(sNPCSchedule,oSpawn);

        int nLocationID = GetLocalInt(oSpawn,"NPC_LOCATION_ID");
        if(GetCreatureLocationState(sNPCID)==nLocationID)
            nProcessSpawn = TRUE;
    }

You'll note that I have to get a special ID string sNPCID from the creature (temporarily created and then destroyed), and I store this ID on the spawn point so that I never need to create the creature again. This is because I need a unique string that identifies the NPC across server resets, and spawns and despawns. I use the resref, tag, and name from NPCs which I have flagged as unique individuals. Right now I only use this functionality for blueprint NPCs which I have flagged as Unique.


 


GetCreatureLocationState() is simply a wrapper function for a database call using the unique ID string to look up the NPC's location.


 


 


What I don't like about this is that I need an entire script for the NPC's schedule update. I had wanted to use a userdefined AI script on the creature and signal events to it, but that doesn't work because I need to update the schedule when the creature isn't spawned. This hacked together job is what I came up with instead. The advantage to this over other systems I have seen is that it doesn't require all creatures to be spawned and directed by module heartbeats or anything like that. The creatures move around in the abstract and only update their locations when a PC enters an area that has one of their spawn points.



               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
NESS spawn system
« Reply #8 on: November 09, 2014, 10:04:40 pm »


               

Thanks Henesua. That looks like it might do the trick. I'll borrow that basic mechanism if you don't mind. It good not to rely on the AI so the NPC does not have to be spawned all the time. You can of course use the same script for lots of NPCs, just branch by tag or name or whatever you need to.



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #9 on: November 10, 2014, 05:24:34 pm »


               

I have no problem with anyone borrowing any of this stuff for use in NWN.


 


The more we all share with each other the better because it helps us all learn. Thats why I still stick around NWN land. Its fun to share with and learn from others in the community.



               
               

               
            

Legacy_BelowTheBelt

  • Hero Member
  • *****
  • Posts: 699
  • Karma: +0/-0
NESS spawn system
« Reply #10 on: November 11, 2014, 05:09:35 am »


               

I moved loot creation from onspawn to ondeath, since not all creatures who are spawned are killed and it always seemed like a waste of resources to create loot that might not be needed. 


 


I also moved all the spawning to SQL tables rather than using lists in the code, allowing an infinite number of tables without hindering performance


 


I've also done some other work on camps and have added a flag that enhances the use of those per my module's needs.


 


 


 


One difficulty I've been having is spawning something outside of NESS and then wanting to add that creature to the spawn point so that NESS handles it from that point on.  Anyone have any experience with that?



               
               

               
            

Legacy_Tchos

  • Sr. Member
  • ****
  • Posts: 454
  • Karma: +0/-0
NESS spawn system
« Reply #11 on: November 11, 2014, 05:20:38 am »


               


I moved loot creation from onspawn to ondeath, since not all creatures who are spawned are killed and it always seemed like a waste of resources to create loot that might not be needed.




 


I think it's usually in On Spawn for pickpocketing reasons.



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
NESS spawn system
« Reply #12 on: November 11, 2014, 03:29:21 pm »


               

I've also done some other work on camps and have added a flag that enhances the use of those per my module's needs.


Sounds interesting. Are you up for sharing the code here?
 
 

One difficulty I've been having is spawning something outside of NESS and then wanting to add that creature to the spawn point so that NESS handles it from that point on.  Anyone have any experience with that?


I have been thinking about doing this, but not tackled it yet.
               
               

               
            

Legacy_BelowTheBelt

  • Hero Member
  • *****
  • Posts: 699
  • Karma: +0/-0
NESS spawn system
« Reply #13 on: November 11, 2014, 08:59:35 pm »


               


Sounds interesting. Are you up for sharing the code here?




 


Sure, see below, though I'm no longer sure what is mine and what is original, other than specific comment code.  I've also gone through the code and commented out unnecessary variable declarations through NESS.  There are quite a bit of declarations that are never called in many of the most frequently-used functions.


 


Essentially, my camps are set up as random encounters.  I set area variables that set the type and % chance that a camp will spawn.  If one does spawn, the type of camp is determined from a list (e.g. an underwater camp could be kuo-toa or suahagin, or sea hags, etc...).  It then creates a camp with placeables and banners.  A boss creature is created and upgraded appropriately to be challenging to the party who spawned it.  The flag I added was the SS flag, which calls custom spawn script flags (the SS flag is a regular NESS flag, but wasn't being checked for in the camp code).  Much of this information is set dynamically on the spawnpoint as the camp is being constructed by the code.


 


from Spawn_main:


Object CampSpawn



object CampSpawn(object oSpawn, string sCamp, location lCamp)
{
    // Spawn in Camp Placeholder
    object oCamp = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_invisobj", lCamp, FALSE);
    SetPlotFlag(oCamp, TRUE);
    SetLocalObject(oCamp, "ParentSpawn", oSpawn);
    SetCampSpawn(oCamp, sCamp, lCamp);

    // Initialize
    int nCampNumP = GetLocalInt(oCamp, "CampNumP");
    int nCampNumC = GetLocalInt(oCamp, "CampNumC");
    float fSpawnRadius = GetLocalFloat(oCamp, "CampRadius");
    vector vCamp = GetPositionFromLocation(lCamp);

    object oSpawned;
    int iCount;
    //int nRandomWalk, nSpawnFacing;
    int nSpawnGroup;   //nLootTable, nTrapDisabled,, nDeathScript
    //float fCorpseDecay;
    int nCampCenter; //nCorpseDecayType,
    string sObject, sTemplate, sFlags, sCampCenter;

    // Get Camp Center
    sCampCenter = GetLocalString(oCamp, "CampCenter");

    // Spawn Placeables
    for (iCount = 1; iCount <= nCampNumP; iCount++)
    {
        // Initialize Values
        sObject = "CampP" + IntToString(iCount - 1);
        sTemplate = GetLocalString(oCamp, sObject);
        nCampCenter = FALSE;

        // Check Flags
        sFlags = GetLocalString(oCamp, sObject + "_Flags");
        nSpawnGroup = IsFlagPresent(sFlags, "SG");

        // Spawn Group
        if (nSpawnGroup )   //== TRUE
        {
            sTemplate = SpawnGroup(oSpawn, sTemplate);
        }

        // Check Camp Center
        if (sCampCenter != "")
        {
            if (sCampCenter == "P" + IntToString(iCount - 1))
            {
                nCampCenter = TRUE;
            }
        }
        // If no CampCenter set, Use first Placeable
        else if (iCount == 1)
        {
            nCampCenter = TRUE;
        }

        oSpawned = DoCampSpawn(oCamp, lCamp, fSpawnRadius, sTemplate, TRUE, iCount, nCampCenter);
        // Run the Spawn Script
        SetLocalObject(oCamp, sObject, oSpawned);
        SetupCampSpawned(oSpawn, oSpawned, vCamp, GetLocation(oSpawned), sFlags);

    }

    // Spawn Creatures
    for (iCount = 1; iCount <= nCampNumC; iCount++)
    {
        // Initialize Values
        sObject = "CampC" + IntToString(iCount - 1);
        sTemplate = GetLocalString(oCamp, sObject);

        // Check Flags
        sFlags = GetLocalString(oCamp, sObject + "_Flags");
        nSpawnGroup = IsFlagPresent(sFlags, "SG");

        // Spawn Group
        if (nSpawnGroup )  //== TRUE
        {
            sTemplate = SpawnGroup(oSpawn, sTemplate);
        }

        // Check Camp Center
        if (sCampCenter != "")
        {
            if (sCampCenter == "C" + IntToString(iCount - 1))
            {
                nCampCenter = TRUE;
            }
        }

        oSpawned = DoCampSpawn(oCamp, lCamp, fSpawnRadius, sTemplate, FALSE, iCount, nCampCenter);
        //Added for Arenthyor - to be able to run spawn scripts on camp spawned creatures, primarily for level-up of camp mobs
        if (IsFlagPresent(sFlags, "SS") )   //==TRUE
            {
            //int iSpawnScriptValue = GetFlagValue(sFlags, "SS",4);
            //SendMessageToPC(GetFirstPC(), "DEBUG:  spawn_main:  iSpawnScriptFlag found.  Spawnscript :" +IntToString(iSpawnScriptValue));
            SetLocalInt(oSpawned, "SpawnScript", GetFlagValue(sFlags, "SS",4));
            ExecuteScript("spawn_sc_spawn", oSpawned);
            }
        SetLocalObject(oCamp, sObject, oSpawned);
        SetupCampSpawned(oSpawn, oSpawned, vCamp, GetLocation(oSpawned), sFlags);
    }

    // Return Placeholder
    return oCamp;
}

Spawn_main




// This Function Spawns the Camp Members
object DoCampSpawn(object oCamp, location lCamp, float fSpawnRadius,
   string sTemplate, int nPlaceable, int nSpawnNumber, int nCampCenter)
{
    object oCampSpawned;
    vector vCamp, vRadius;
    float fRadius,  fAngle; //fRadiusX, fRadiusY,

    // Set up Location
    if (!nCampCenter )   //== FALSE
    {
        //vCamp = GetPositionFromLocation(lCamp);
        fAngle = IntToFloat(Random(361));
        fRadius = IntToFloat(Random(FloatToInt(fSpawnRadius)) + 1);
        //fRadiusX = fRadius * cos(fAngle);
        //fRadiusY = fRadius * sin(fAngle);
        vRadius = Vector(fRadius * cos(fAngle), fRadius * sin(fAngle));
        lCamp = Location(OBJECT_SELF, GetPositionFromLocation(lCamp) + vRadius, 0.0);
    }

    // Spawn Camp Object
    if (nPlaceable ) //== TRUE
    {
        oCampSpawned = CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lCamp, FALSE);
        //debug("created placeable at " + LocationToString(lCamp));
    }
    else
    {
        oCampSpawned = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCamp, FALSE);
    }

    // Return Camp Object
    return oCampSpawned;
}

then this one:




// This Function Spawns the Camp Members
object DoCampSpawn(object oCamp, location lCamp, float fSpawnRadius,
   string sTemplate, int nPlaceable, int nSpawnNumber, int nCampCenter)
{
    object oCampSpawned;
    vector vCamp, vRadius;
    float fRadius,  fAngle; //fRadiusX, fRadiusY,

    // Set up Location
    if (!nCampCenter )   //== FALSE
    {
        //vCamp = GetPositionFromLocation(lCamp);
        fAngle = IntToFloat(Random(361));
        fRadius = IntToFloat(Random(FloatToInt(fSpawnRadius)) + 1);
        //fRadiusX = fRadius * cos(fAngle);
        //fRadiusY = fRadius * sin(fAngle);
        vRadius = Vector(fRadius * cos(fAngle), fRadius * sin(fAngle));
        lCamp = Location(OBJECT_SELF, GetPositionFromLocation(lCamp) + vRadius, 0.0);
    }

    // Spawn Camp Object
    if (nPlaceable ) //== TRUE
    {
        oCampSpawned = CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lCamp, FALSE);
        //debug("created placeable at " + LocationToString(lCamp));
    }
    else
    {
        oCampSpawned = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCamp, FALSE);
    }

    // Return Camp Object
    return oCampSpawned;
}

from Spawn_cfg_camp



void SetCampSpawn(object oCamp, string sCamp, location lCamp)
{

//
// Place Custom Camps Here
// -------------------------------------------
//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Success!! camp script firing");

int iNoStructures = d3(); //determine how many structure
int iStructuresLoopCount; //for looping through and setting the structures
int iAltarYN = d2(1); //determine if to use an altar/throne
int iNoPlaceables = d4(); //determine how many placeables to use
int iPlaceableTotal;  //total number of placeables we're going to be putting down across banner, altar, structure, and placeables
int iTotalPlaceableLoopCount; //for keeping track of the total number of placeables we've set
//int iPlaceableLoopCount; //for looping through the placeables we've set
int iCreatures = 4+ d4(); //number of creatures
int iTotalCreatureLoopCount;
int iPCHD;
int iCreatureHD;
int iCreatureNewHD;

float fRadius = IntToFloat(10+d4(3));

object oCampBoss;
object oArea = GetArea (oCamp);
object oPC;

string sBanner;
string sStructure;
string sAltar;
string sPlaceable;
string sCampType = GetLocalString (oArea, "CampType");
string sCampGroup;
string sCampCreature;
string sLT;

if (iAltarYN == 1)//1 = yes, let's have an altar
    {
    iPlaceableTotal = 2 + iNoStructures + iNoPlaceables; //1 banner + 1 altar + number of structures + number of placeables
    //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Altar check successful - should place an altar");
    }
else //2 = no, let's not have an altar
    {
    iPlaceableTotal = 1 +iNoStructures + iNoPlaceables;  //1 banner + number of structures + number of placeables
    //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Altar check failed - will not place an altar");
    }
// Set Number of Placeables
SetLocalInt(oCamp, "CampNumP", iPlaceableTotal);
// Set Number of Creatures
SetLocalInt(oCamp, "CampNumC", iCreatures);
// Set Radius of Camp
SetLocalFloat(oCamp, "CampRadius", fRadius);

//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Total number of placeables should be "+IntToString(iPlaceableTotal));
//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Total number of creatures should be "+IntToString(iCreatures));
//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Float radius of the camp should be "+FloatToString(fRadius));

//Start with the placeables
iTotalPlaceableLoopCount = 1;
iStructuresLoopCount = 1;
//iPlaceableLoopCount =1;
while (iTotalPlaceableLoopCount <= iPlaceableTotal)
    {
    //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Looping for all placeables.  Loop # "+IntToString(iTotalPlaceableLoopCount));
    //first placeable should be for banner, which will be the center of the camp;
    if (iTotalPlaceableLoopCount ==1)
        {
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Placeable Loop Count =1. Initializing GetCampPlaceableBanner ");
        sBanner = GetCampPlaceableBanner();
        // Set Placeable 0 to be Camp Center
        SetLocalString(oCamp, "CampCenter", "P0");
        // Set Placeable 0 and Spawn Flags
        // First Placeable always Spawns at Center of Camp
        // If CampCenter Is Not Set
        SetLocalString(oCamp, "CampP0", sBanner);
        SetLocalString(oCamp, "CampP0_Flags", "SP_SF");
        }
    // on the second loop, if we're to use the altar, then let's get it
    else if (iTotalPlaceableLoopCount ==2 && iAltarYN == 1)
        {
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iPlaceableLoopCount ==2 && iAltarYN == 1 Initializing GetCampPlaceableAltar ");
        sAltar = GetCampPlaceableAltar();
        // Set Placeable 1 and Spawn Flags
        SetLocalString(oCamp, "CampP1", sAltar);  //altar will alway be P1, since it will always immediately follow the banner when it is used
        SetLocalString(oCamp, "CampP1_Flags", "SP_SF");
        }
    else
        {
        //now we need to do a couple of subloops to set the structures
        if (iStructuresLoopCount <= iNoStructures)
            {
            //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iStructuresLoopCount = "+IntToString(iStructuresLoopCount));
            //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Initializing GetCampStructures ");
            sStructure = GetCampStructures(sCampType);
            // Set structures and Spawn Flags
            SetLocalString(oCamp, "CampP"+IntToString(iTotalPlaceableLoopCount-1), sStructure); //i.e, the 3rd loop will put in placeable, 0, 1, and 2, so need to subtract one
            SetLocalString(oCamp, "CampP"+IntToString(iTotalPlaceableLoopCount-1)+"_Flags", "SP_SF");
            iStructuresLoopCount = iStructuresLoopCount +1;
            }
        //now that the structures are done, we can get the placeables
        else
            {
            //if (iPlaceableLoopCount <= iNoPlaceables)
                {
                //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iPlaceableLoopCount = "+IntToString(iPlaceableLoopCount));
                //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Initializing GetCampPlaceables ");
                sPlaceable = GetCampPlaceables(sCampType);
                // Set structures and Spawn Flags
                SetLocalString(oCamp, "CampP"+IntToString(iTotalPlaceableLoopCount-1), sPlaceable); //i.e, the 3rd loop will put in placeable, 0, 1, and 2, so need to subtract one
                SetLocalString(oCamp, "CampP"+IntToString(iTotalPlaceableLoopCount-1)+"_Flags", "SP_SF");
                //iPlaceableLoopCount = iPlaceableLoopCount +1;
                }
            }
        }
    iTotalPlaceableLoopCount = iTotalPlaceableLoopCount+1;
    }

//now let's figure out the HD that the creatures should be, based on the PCs in the area and only have to do loop once
oPC = GetFirstObjectInArea (oArea);
while( GetIsObjectValid(oPC))//  now begin counting faction members and their hitdice
    {
    if (GetIsPC(oPC) && !GetIsDM(oPC) && !GetIsDMPossessed(oPC))
        {
        iPCHD = GetHitDice(oPC);
        if (iCreatureHD < iPCHD)
            {
            iCreatureNewHD = iPCHD ;
            //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Creature Target HD should be "+IntToString(iCreatureNewHD));
            }
        }
    oPC = GetNextObjectInArea(oArea);
    }
SetLocalInt (oArea, "PCHD", iCreatureNewHD); //ar_boss_scale will look at this and levelup with it

//now let's do the creatures
iTotalCreatureLoopCount=1;
//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Initializing sCampGroup ");
sCampGroup = GetCampGroup(sCampType);
//SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Recognizing sCampGroup as "+sCampGroup);
while (iTotalCreatureLoopCount <=iCreatures)
    {
    //first creature should be the boss, so no loot table set, though does use a death tag, which will spawn treasure & xp
    //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iTotalCreatureLoopCount = "+IntToString(iTotalCreatureLoopCount));
    if (iTotalCreatureLoopCount ==1)
        {
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iTotalCreatureLoopCount = "+IntToString(iTotalCreatureLoopCount));
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Setting Boss Creature");
        // Set Creature 0 to be Trigger
        // Script 00 : Kill him and the Camp Despawns - commented out because the whole camp disappears if he's killed
        SetLocalString(oCamp, "CampTrigger", "C0");
        SetLocalInt(oCamp, "CampTriggerScript", 2); //- spawns the chests when the boss dies and destroys the "PCHD" int and the camp waypoint, so camp checks will begin again
        // Set Creature 0 and Spawn Flags
        SetLocalString(oCamp, "CampC0", sCampGroup+"boss");
        SetLocalString(oCamp, "CampC0_Flags", "SP_SS3_CD060"); //SR10M05_SS3_DT1");  //  level up the creature
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Setting Boss Creature string set as "+sCampGroup+"boss");
        }
    //remainder creatures
    else
        {
        sLT= GetLootTable (iPCHD);
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  iTotalCreatureLoopCount = "+IntToString(iTotalCreatureLoopCount));
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Setting minion Creatures");
        sCampCreature = GetCampCreature (sCampGroup);
        // Set Placeable 1+ and Spawn Flags
        SetLocalString(oCamp, "CampC"+IntToString(iTotalCreatureLoopCount-1), sCampCreature);
        SetLocalString(oCamp, "CampC"+IntToString(iTotalCreatureLoopCount-1)+"_Flags", "SP_SS4_LT"+sLT+"_CD060"); //SR10M05_SS4");//
        //SendMessageToPC (GetFirstPC(), "DEBUG:  spawn_cfg_camp:  Setting minion creature string set as "+sCampCreature+" with flag: CampC"+IntToString(iTotalCreatureLoopCount-1)+"_Flags");
        }
     iTotalCreatureLoopCount = iTotalCreatureLoopCount+1;
     }
}

In the above script, there are separate functions that are essentially switch-case statements that select the resrefs for random placeables:


GetCampPlaceableBanner() - returns a resref for a banner for the the creatures to rally aournd.


GetCampPlaceableAltar() - returns the resref for an altar (every camp needs an altar, right?)


GetCampStructures(string sCampType) - returns a "building'/structure resref appropriate to a specific camp type


GetCampPlaceables(string sCampType) - returns a placeable resref appropriate to a specific camp type


GetCampGroup(string sCampType) - determines the type of group given it's general type (if it's an underwater camp, is it sahuagin, kuotoa, or sea hags)


GetCampCreature(string sCampGroup) - determines the resref of creatures that can be spawned given a specific type of camp


GetLootTable(int iHD) - tells NESS what loot table creatures should pull from, based upon the HD of the party that spawned it.


 


Hope that helps, or at least provides some inspiration!