Random Encounters
-
Was working on this for my own purposes, but figured I'd share in case folks were looking for something similar to CoA's CARES.
/////////////Escape From Underdark Randomized-Encounters (EFURY) /////////////Author : RavenBlackRose/Seth Carter /////////////Date: 07/10/2005 /////////////Updated for Isles of Elysium //////////////////////////////////////////////////// /////////////New Version Akylonor 2010, d100 Odds. /////////////Called From:Area OnEnter void SpawnEncounter(string sSpawn, location lSpawn) { string sDebugSpawn = "Encounter String:'" + sSpawn + "', "; string sSubstring; string sCritter; //string reading function int iPositionInString = 0; int iStringLength = GetStringLength(sSpawn); int iPositionInSubString = 0; int iSubStringLength; int iLastStop = 0; int iMultipleCreatures; int iCounter; int iFoundInSubstring; //read through the string while(iPositionInString != iStringLength) { //stop at | if(GetSubString(sSpawn, iPositionInString, 1) == "|") { //get the creature substring sSubstring = GetSubString(sSpawn, iLastStop, iPositionInString-iLastStop); sDebugSpawn += "Creature Substring: '" + sSubstring +"', "; iLastStop = iPositionInString+1; //move the stop to the character after "|" iMultipleCreatures = 1; //reset the check for a multiple creatures //read through the substring iPositionInSubString = 0; iSubStringLength = GetStringLength(sSubstring); iFoundInSubstring = 0; while(iPositionInSubString != iSubStringLength) { if(GetSubString(sSubstring, iPositionInSubString, 1) == "*") { sCritter = GetSubString(sSubstring, 0, iPositionInSubString); iMultipleCreatures = StringToInt(GetSubString(sSubstring, GetStringLength(sCritter)+1, GetStringLength(sSubstring)-(GetStringLength(sCritter)+1))); iFoundInSubstring =1; } else { if(iFoundInSubstring != 1) { sCritter = sSubstring; } } iPositionInSubString++; } //spawning function (complex, ain't it) if(iMultipleCreatures == 1) { CreateObject(OBJECT_TYPE_CREATURE, sCritter, lSpawn, FALSE); } else { for(iCounter = 0; iCounter < iMultipleCreatures; iCounter++) { CreateObject(OBJECT_TYPE_CREATURE, sCritter, lSpawn, FALSE); } } } iPositionInString ++; } WriteTimestampedLogEntry(sDebugSpawn); } void main() { string sLocationDebug; //load area, and dice object oArea = OBJECT_SELF; object oPC = GetEnteringObject(); int iLocationDice = GetLocalInt(oArea, "efury_spawnpoints"); int iSpawnDice = d100(1); int iCondition = iLocationDice; int iCounter = 0; int iEncounterOdds; int nOddsCounter; int iEncounterChosen = 0; //avoid bombarding parties if(GetLocalInt(oArea, "RecentSpawned") == 0) { if(GetIsPC(oPC)) //prevents multispawns from companions, other monsters, DM's wandering about, etc { //run for as many times as there are possible spawnpoints iCounter = 1; while(iCounter <= iLocationDice) { string sLocation = "efury_spawnpoint_" + IntToString(iCounter); sLocationDebug = "Area:'" + GetName(oArea) + "', " + "Location: '" + sLocation + "', "; object oSomethingInArea = GetFirstObjectInArea(oArea); object oSpawnPoint = GetNearestObjectByTag(sLocation, oPC); location lToSpawn = GetLocation(oSpawnPoint); //check around for creatures already spawned or PCs, so we don't doublespawn or drop on their heads object oNearby = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lToSpawn, TRUE, OBJECT_TYPE_CREATURE); int iAnyoneAround = 0; if(oNearby == OBJECT_INVALID) //no creatures/PCs in radius { nOddsCounter = 1; iEncounterOdds = GetLocalInt (oArea, "efury_enc_chance_" + IntToString(nOddsCounter)); iEncounterChosen = 0; iSpawnDice = d100(1); WriteTimestampedLogEntry("Spawn Roll =" + IntToString(iSpawnDice)); while(iEncounterOdds > 0 && iEncounterOdds < 101 && iEncounterChosen != 1) { if(iSpawnDice <= iEncounterOdds) { string sEncounter = "efury_encounter_" + IntToString(nOddsCounter); sEncounter = GetLocalString(oArea, sEncounter); if(sEncounter != "null" && sEncounter != "") { SpawnEncounter(sEncounter, lToSpawn); } else { sLocationDebug += "Encounter '" + sEncounter + "' is null, blank, or missing"; if(sEncounter == "null") { iEncounterChosen = 1; } } iEncounterChosen = 1; } nOddsCounter = nOddsCounter +1; iEncounterOdds = GetLocalInt (oArea, "efury_enc_chance_" + IntToString(nOddsCounter)); } } else { sLocationDebug += "Encounter not spawned due to creatures already present."; } iCounter = iCounter +1; WriteTimestampedLogEntry(sLocationDebug); SetLocalInt(oArea, "RecentSpawned", 1); //2 minute delay before it will spawn another DelayCommand(120.0, ActionDoCommand(SetLocalInt(oArea, "RecentSpawned", 0))); } } } }
The below picture shows set area variables for 12 possible spawn points, with a trio of bugbears (25% 1-25 roll) guarding a shaman, a pair of ravens (35%, 26-60 roll), a family of wyverns (10%, 61-70), and a lone mountain giant (10%, 71-80) as possibles (and a 20% chance of nothing spawning).
The syntax is```
(RESREF)*(Number of them)|(next creature type in encounter)|(next creature)|Waypoints tagged "efury_spawnpoint_XX" where XX is relevant number serve as spawn positions. ![](http://img715.imageshack.us/img715/8021/efury.jpg) Modifying it to look at 2das isn't too hard if one wishes broad category tables over specific area dynamics either.
-
Yeah, I advanced this further, later on. (2 posts here, doesn't all fit in one)
I'll put the script block in a second post, since it's a relatively large one. It's a system I made (as can be seen in the notes) for Escape from the Underdark eons and eons ago, though its altered vastly from that primitive work.
Essentially, you can either tie the system to an Area OnEnter, or a Trigger OnEnter (or both, if you really want). When these are fired, it goes through waypoints in the area tagged as "WP_spawnpoint_" with a number on the end (1 to whatever your system can handle before timing out, or the end of integers I suppose, but its unlikely any area has enough spawns for either case)
On each of these waypoints, are stored a variable list, as shown in this picture (This may seem a lot of work, but you can cut and paste them through an area fast if its similar, or even make Pre-Made Tables by creating them in the pallette for use all over the place)
To dissect this down.
"respawn_time" is the time in seconds it will take before the script will zap through again. Keeps parties from flooding over and such. Although its on the waypoints (for future use), it presently affects the entire area (the last waypoint will be the used setting).
"encounter_#" is a defined encounter. The semi-gibberish there is four orcs with a shaman, 3 wolves, a mixed spiders/ettercap, a great wyvern, and "null" denotes a blank encounter, used for odds of nothing whatsoever spawning. The syntax used is
<resref>*<number to="" spawn="">| ^ A resref is not a tag. You can find it under the Advanced Tab of creature properties (for Default NWN monsters, put one down in an area, then check, as Edit Copy will alter it)</number></resref> ```. Even for singles you need the *1, and the | has to follow each creature, even the last. ""enc_chance_#" is the odds assigned to an encounter. For efficiencies sake, you start with your largest chance encounter (you don't have to order them at all, but its helpful). The odds aren't percantile, due to the script logic, but rather reflect a range on a d100\. 1-(enc_chance_1) will give you encounter_1\. enc_chance_1 plus 1 through to enc_chance_2's odds will give you encounter_2, and so on. In the sample case, the orcs spawn on a roll of 1-25 (25%), the wolves on a roll of 26-50(25%), the spider group on a roll of 51-70,(20%), the wyvern on a roll of 71-80 (10%), and no monsters on a roll of 81-100 (20%) For efficiency and organization, you should cover all 100 odds, lead with the largest chance (since that'll avoid extra script loops, though I've tried it with 25 encounters on a spawn point to no noticeable lag) and work your way down to the minute ones, but the script has limits throughout (denoted in comments) to avoid nasty TMIs from that double loop used) You also don't need to have a null encounter at all, if you want to cover all 100 possibilities with various monsters, something will always spawn.
-
/////////////Seth's Random Encounters /////////////Author : BlackRose/Seth Carter /////////////Date: 07/10/2005, Update May 2007, Update August 2010, Update Jan 2011 /////////////If this wasn't posted or directly distributed to you, it ain't yours. To date including Escape From The Underdark, Isles of Elysium, Bid For Transcendence, Akrylonor, Cormyr & Dales /////////////New Version 2010, d100 Odds. /////////////Called From:Area or Trigger OnEnter void SpawnEncounter(string sSpawn, location lSpawn) { string sDebugSpawn = "Encounter String:'" + sSpawn + "', "; string sSubstring; string sCritter; //string reading function int iPositionInString = 0; int iStringLength = GetStringLength(sSpawn); int iPositionInSubString = 0; int iSubStringLength; int iLastStop = 0; int iMultipleCreatures; int iCounter; int iFoundInSubstring; //read through the string while(iPositionInString != iStringLength) { //stop at | if(GetSubString(sSpawn, iPositionInString, 1) == "|") { //get the creature substring sSubstring = GetSubString(sSpawn, iLastStop, iPositionInString-iLastStop); sDebugSpawn += "Creature Substring: '" + sSubstring +"', "; iLastStop = iPositionInString+1; //move the stop to the character after "|" iMultipleCreatures = 1; //reset the check for a multiple creatures //read through the substring iPositionInSubString = 0; iSubStringLength = GetStringLength(sSubstring); iFoundInSubstring = 0; while(iPositionInSubString != iSubStringLength) { if(GetSubString(sSubstring, iPositionInSubString, 1) == "*") { sCritter = GetSubString(sSubstring, 0, iPositionInSubString); iMultipleCreatures = StringToInt(GetSubString(sSubstring, GetStringLength(sCritter)+1, GetStringLength(sSubstring)-(GetStringLength(sCritter)+1))); iFoundInSubstring =1; } else { if(iFoundInSubstring != 1) { sCritter = sSubstring; } } iPositionInSubString++; } //spawning function (complex, ain't it) if(iMultipleCreatures == 1) { CreateObject(OBJECT_TYPE_CREATURE, sCritter, lSpawn, FALSE); } else { for(iCounter = 0; iCounter < iMultipleCreatures; iCounter++) { CreateObject(OBJECT_TYPE_CREATURE, sCritter, lSpawn, FALSE); } } } iPositionInString ++; } WriteTimestampedLogEntry(sDebugSpawn); } void main() { /// string sLocationDebug; DEBUG ONLY //load area, and initialize dice object oArea; //// If I'm a Trigger, get my area, else I should be the area if(GetObjectType(OBJECT_SELF) == OBJECT_TYPE_TRIGGER) { oArea = GetArea(OBJECT_SELF); } else { oArea = OBJECT_SELF; } object oPC = GetEnteringObject(); int iLocationDice = GetLocalInt(oArea, "Number_Of_Spawnpoints"); // Not strictly necessary, but putting this variable on the area will stop it at that many points, and avoid a very rare TMI int iSpawnDice = d100(1); int iCondition = iLocationDice; int iCounter = 0; int iEncounterOdds; int nOddsCounter; int iEncounterChosen = 0; //avoid bombarding the place when a group enters and don't spawn stuff if a DM turned encounters off if(GetLocalInt(oArea, "RecentSpawned") == 0 && GetLocalInt(oArea, "DMENCOFF") == 0) { if(GetIsPC(oPC) && GetIsDM(oPC) == FALSE) //prevents multispawns and potential TMIS and massive lags from companions, other monsters, DM's wandering about, etc //////Strangely, a DM will register as a PC without this second check { //run for as many times as there are possible spawnpoints iCounter = 1; // Initialize Counter string sLocation = "WP_spawnpoint_" + IntToString(iCounter); // Start with the first spawnpoint object oSpawnPoint = GetNearestObjectByTag(sLocation, oPC); // Grab our first waypoint, for added accuracy (cause nwn is occasionally random about it, start from the PC triggering) while(iCounter <= iLocationDice) { sLocation = "WP_spawnpoint_" + IntToString(iCounter); //Redundant the first time around, but increments to the next spawnpoint //////sLocationDebug = "Area:'" + GetName(oArea) + "', " + "Location: '" + sLocation + "', "; ////MORE DEBUGGING //////object oSomethingInArea = GetFirstObjectInArea(oArea); I Have no clue what this is for. I think its from a prior iteration and was used to confirm the area oSpawnPoint = GetNearestObjectByTag(sLocation, oPC); ///Grabbing the relavent spawn point location lToSpawn = GetLocation(oSpawnPoint); ///// Where to spawn stuff //check around for creatures already spawned or PCs, so we don't doublespawn or drop on their heads object oNearby = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lToSpawn, TRUE, OBJECT_TYPE_CREATURE); ///Check for existing spawns, or PCs where we're looking to throw stuff ///// int iAnyoneAround = 0; Another Legacy line I'm pretty sure. if(oNearby == OBJECT_INVALID) //IF the spawns are still there from a prior firing, or if a PC's there, don't spawn (Avoid doubling, and infamous MONSTERONHEAD Syndrome { nOddsCounter = 1; iEncounterOdds = GetLocalInt (oSpawnPoint, "enc_chance_" + IntToString(nOddsCounter)); // Get the odds assigned to enc_chance_1, or enc_chance_2, or enc_chance_3, etc iEncounterChosen = 0; // We have not yet chosen an ecnounter iSpawnDice = d100(1); /////WriteTimestampedLogEntry("Spawn Roll =" + IntToString(iSpawnDice)); DEBUG STUFF while(iEncounterOdds > 0 && iEncounterOdds < 101 && iEncounterChosen != 1 && GetLocalString(oSpawnPoint, "encounter_" + IntToString(nOddsCounter)) != "") /// IF it's below 0, somethings really weird, and probably missing variables. Odds only go to 100.Stop once we've found the encounter we want. ////If it hits a sudden blank encounter string stop cause its probably gone past the encounter list. { if(iSpawnDice <= iEncounterOdds) ///If the dice roll is lower then the odds of the encounter (The logic is a bit mathematical and confusing, but is explained in external documentation) { string sEncounter = "encounter_" + IntToString(nOddsCounter); //Get the encounter string with the corresponding number sEncounter = GetLocalString(oSpawnPoint, sEncounter); // if(sEncounter != "null" && sEncounter != "") /// If it's "" it's an invalid string, if it's null, it's a blank spawn { SpawnEncounter(sEncounter, lToSpawn); /// Use that giant block of code way up there to decode that gibberish of resrefs } else { //// sLocationDebug += "Encounter '" + sEncounter + "' is null, blank, or missing"; /// MORE DEBUGGING if(sEncounter == "null") { iEncounterChosen = 1; //// null indicates a deliberate blank encounter, and is thus an chosen } }