//:://///////////////////////////////////////////// //:: Name re_rndenc //:: FileName re_rndenc.nss //:: Copyright (c) 2002 Raymond Miller //::////////////////////////////////////////////// /* This script creates functions called RandomEncounter(), CleanHouse(), and SetRndEncProperties() for use in the NWN scripting language. This script is meant to be used as an #include and is part of the BESIE Random Encounter package by Ray Miller */ //::////////////////////////////////////////////// //:: Created By: Ray Miller //:: Created On: 7/6/2002 //::////////////////////////////////////////////// // Encounter Type Constants int ENCOUNTER_TYPE_AREA = 0; int ENCOUNTER_TYPE_PARTY = 1; int ENCOUNTER_TYPE_IND = 2; // PRIVATE FUNCTION DECLARATIONS // Sets properties for random encounters that are likely to seldom change // - iDifficulty 1 to 10 // - bConsiderCR: If true takes CR of creature into consideration when // choosing an encounter. // - sCreatureTable: "re_***" - where *** is a string of letter and/or numbers to indicate to the function what type // of creatures to spawn. They are as follows: // a - animals // c - construct // d - dragon // e - elemental // g - giant // h - humanoid // i - insect // m - miscellaneous // p - planar // u - undead // b - bandit // 0 through 9 - These are for custom encounter tables (see Custom Encounter Tables, later in this section.) // - iLifeTime: Time in seconds before unengaged encounters decay. // - Mph: Should equal the Minutes Per Hour setting of the Module. // - iEncountertype: // ENCOUNTER_TYPE_PARTY (default) - Takes into consideration the average level of the entire party of the PC who is to // receive the encounter when choosing an encounter of appropriate difficulty level. // ENCOUNTER_TYPE_AREA - Takes into consideration the levels off all PCs and henchmen within a 20m radius of the PC // who is to receive the encounter. // ENCOUNTER_TYPE_IND - Takes into consideration only the levels of the individual PC who is to receive the encounter. // - bLOSCheck: Dependant upon a broken scripting function. (future use!) // Note: This function is best called by the OnModuleLoad or OnAreaLoad handler. void SetRndEncProperties(int iDifficulty = 4, int bConsiderCR = TRUE, string sCreatureTable = "re_ceghimpub", int iLifeTime = 180, int iMph = 2, int iEncounterType = 1, int bLOSCheck = FALSE); // Generates the likelihood of a random encounter. // - fChanceOfEncounter: Odds of encounter spawning when funciton is called. Accurate to two // decimal places. .01 to 100.00 percent chance. // - oEncounterObject: The object about which the encounter will spawn, whose levels, party // or area is considered when determining an appropriate creature. // - sTemplate: When used as the sCreatureTable parameter in the SetRndEncProperties() // function this parameter has higher priority. It can also be set to the tag of a // specific creature, or to "random" to use the default table set by SetRndEncProperties() // - iMinNumberOfCreatures: If > 1, a random number of creatures between this and iMaxNumberOfCreatures // will spawn. If set to 0, then exactly the number of creatures set by iMaxNumberOfCreatures will // spawn. // - iMaxNumberOfCreatures: If this and iMinNumberOfCreatures is set to 0 then the number of Creatures // spawned will be determined by the CR of the creature spawned compared to the levels of the player(s). // - iMinEncounterDistance: If set to 0, encounter distance will always be at the number set by iMaxEncounterDistance. // - iMaxEncounterDistance: Farthest distance the encounter can be from oEncounterObject. // - iOrientation: 0 to 360. Counterclockwise representing the angle from facing where the encounter will spawn. // a value of 0 will spawn the encounter directly in front of oEncounterObject. 360 will generate a random angle. // - iTolerance: The number of degrees by which the angle can randomly be off from iOrientation. // - iLevelOverride: Use this to force the function to base the encounter on a character level other than that // determined by oEncounterObject. // - iDifficulty: Overrides the difficulty setting determined by the SetRndEncProperties() function. object RandomEncounter(float fChanceOfEncounter = 100.0, object oEncounterObject = OBJECT_SELF, string sTemplate = "random", int iMinNumberOfCreatures = 0, int iMaxNumberOfCreatures = 0, int iMinEncounterDistance = 1, int iMaxEncounterDistance = 15, int iOrientation = 360, int iTolerance = 0, int iLevelOverride = 0, int iDifficulty = 0); // Used to "clean up" an area that has become littered by random encounters. // - bDestroyPlotItems - Tells the function whether or not to destroy items with their plot flags set. If set to TRUE, // plot items will be destroyed just like any other item. // - oArea - The area to clean up. // - iSpawnOverride - Overrides the default (set by the SetRndEncProperties() function) time to destroy random encounter // creatures who are not engaged by PCs. // - iItemOverride - Overrides the default time of 30 minutes after which to destroy items dropped by PCs // Note: Only works if the "re_moditemdrop" script included with the BESIE Random Encounter package // is placed in the module OnItemUnacquire handler. // - iBodyBagOverride - Overrides the default time of 5 minutes after which to destroy loot that was dropped by creatures // who were killed. // NOTE: If there is bDestroyPlotItems is FALSE and there is a plot item or items inside a container or body bag, the container // and all non-plot items will decay but the plot item(s) will be left. // NOTE: A value of zero assigned to the override parameters will cause the function to use the default value for that parameter. void CleanHouse(int bDestroyPlotItems = FALSE, object oArea = OBJECT_SELF, int iSpawnOverride = 0, int iItemOverride = 0, int iBodyBagOverride = 0); void SetRndEncProperties(int iDifficulty = 4, int bConsiderCR = TRUE, string sCreatureTable = "re_ceghimpub", int iLifeTime = 180, int iMph = 2, int iEncounterType = 1, int bLOSCheck = FALSE) { // INITIALIZE SOME GLOBAL VARIABLES object oMod = GetModule(); object oPC; if(GetIsObjectValid(oMod)) { SetLocalInt(oMod, "bInitialized", TRUE); SetLocalInt(oMod, "bConsiderCR", bConsiderCR); SetLocalInt(oMod, "iMph", iMph); SetLocalInt(oMod, "iLifeTime", iLifeTime); SetLocalInt(oMod, "bLOSCheck", bLOSCheck); SetLocalInt(oMod, "iDifficulty", iDifficulty); SetLocalInt(oMod, "iEncounterType", iEncounterType); SetLocalString(oMod, "sCreatureTable", sCreatureTable); SetLocalInt(oMod, "iMinNumberOfCreatures", 0); SetLocalInt(oMod, "iMaxNumberOfCreatures", 0); } } #include "re_table" object RandomEncounter(float fChanceOfEncounter = 100.0, object oEncounterObject = OBJECT_SELF, string sTemplate = "random", int iMinNumberOfCreatures = 0, int iMaxNumberOfCreatures = 0, int iMinEncounterDistance = 1, int iMaxEncounterDistance = 15, int iOrientation = 360, int iTolerance = 0, int iLevelOverride = 0, int iDifficulty = 0) { // IF PROPERTIES ARE NOT SET THEN SET THEM WITH DEFAULTS if(!GetLocalInt(GetModule(), "bInitialized")) { SetRndEncProperties(); } // DETERMINE IF ENCOUNTER HAPPENS // The following two lines allow for a chance of encounter with a precision of up to // two decimal places. ie. 100.00. An encounter can have as little as a 0.01 chance // of occuring. int iHappens = Random(10000)+1; int iChanceOfEncounter = FloatToInt(fChanceOfEncounter * 100); if(iChanceOfEncounter < iHappens) { return OBJECT_INVALID; } // DECLARE AND INITIALIZE VARIABLES object oMod = GetModule(); int iLifeTime = GetLocalInt(oMod, "iLifeTime"); int bLOSCheck = GetLocalInt(oMod, "bLOSCheck"); if(!iDifficulty) iDifficulty = GetLocalInt(oMod, "iDifficulty") * 2; int iEncounterType = GetLocalInt(oMod, "iEncounterType"); int iCounter1 = 1; // Used to count the creatures when spawning them. int iCounter2 = 1; // Used in loop to set difficulty level. int iCounter3 = 1; // Used in loop to check line of sight float fEncounterDistance (future use!); int iCounter4; int iNumberOfCreatures; int iEncounterDistance; int iFacingSameWay; int iLevels; int bNumberByLevel = FALSE; int bNoEncounter = FALSE; int bComplete1 = FALSE; int bComplete2 = FALSE; int iMph; float fMinCR; float fMaxCR; float fEncounterDistance; float fNewEncounterDistance; float fCreatureFacing; float fEncounterAngle; float fEncounterVector; float fAngleOffset; float fLevels; float fDifficulty = 0.167; vector vEncounterVector; vector vVectorOffset; vector vCreatureVector; object oAmIAPC; object oCreature; object oArea; if(oEncounterObject == GetModule()) { oAmIAPC = GetFirstPC(); while(GetIsObjectValid(oAmIAPC)) { SetLocalObject(oMod, "oEncounterObject" + IntToString(iCounter4), oAmIAPC); iCounter4++; oAmIAPC = GetNextPC(); } oEncounterObject = GetLocalObject(oMod, "oEncounterObject" + IntToString(Random(iCounter4))); } else if(GetObjectType(oEncounterObject) == 0 && oEncounterObject != GetModule()) { oArea = oEncounterObject; oAmIAPC = GetFirstObjectInArea(oArea); while(GetIsObjectValid(oAmIAPC)) { if(GetIsPC(oAmIAPC)) { SetLocalObject(oArea, "oEncounterObject" + IntToString(iCounter4), oAmIAPC); iCounter4++; } oAmIAPC = GetNextObjectInArea(oArea); } oEncounterObject = GetLocalObject(oArea, "oEncounterObject" + IntToString(Random(iCounter4))); } else { oArea = GetArea(oEncounterObject); } if(!GetIsPC(oEncounterObject)) iEncounterType = ENCOUNTER_TYPE_AREA; location lCreatureLocation; vector vEncounterObjectVector = GetPosition(oEncounterObject); if(!GetLocalInt(GetModule(), "iMph")) { iMph = 2; } else { iMph = GetLocalInt(GetModule(), "iMph"); } int iMin = 60; int iHr = iMin * iMph; int iDay = iHr * 24; int iMth = iDay * 28; int iYr = iMth * 12; if(iDifficulty > 10) { iDifficulty = 10; } if(iDifficulty == 0) { iDifficulty = GetGameDifficulty() * 2; } while(iCounter2 <= iDifficulty) { fDifficulty = fDifficulty * 1.5; iCounter2++; } // FURTHER CHECKS TO DETERMINE IF ENCOUNTER HAPPENS oAmIAPC = GetFirstObjectInShape(SHAPE_SPHERE, 35.0, GetLocation(oEncounterObject), FALSE, OBJECT_TYPE_CREATURE); if(GetIsObjectValid(oAmIAPC)) { while(GetIsObjectValid(oAmIAPC)) { if(GetIsPC(oAmIAPC)) { if(IsInConversation(oAmIAPC)) { return OBJECT_INVALID; } } oAmIAPC = GetNextObjectInShape(SHAPE_SPHERE, 25.0, GetLocation(oEncounterObject), FALSE, OBJECT_TYPE_CREATURE); } } // ERROR CORRECTION if(iMaxNumberOfCreatures < iMinNumberOfCreatures) { iMaxNumberOfCreatures = iMinNumberOfCreatures; } if(iMaxEncounterDistance < iMinEncounterDistance) { iMaxEncounterDistance = iMinEncounterDistance; } if(!GetIsPC(oEncounterObject)) { iEncounterType = ENCOUNTER_TYPE_AREA; } // DETERMINE THE ANGLE OFFSET OF THE SPAWN if(iOrientation == 360) { fEncounterAngle = IntToFloat(Random(360)); } else { fEncounterAngle = GetFacingFromLocation(GetLocation(oEncounterObject)) + IntToFloat(iOrientation); fEncounterAngle = (fEncounterAngle + (IntToFloat(iTolerance) * 0.5)) - (IntToFloat(Random(iTolerance))); } // DETERMINE THE DISTANCE FROM THE SPAWNING OBJECT if(iMinEncounterDistance == 0) { iMinEncounterDistance = iMaxEncounterDistance; fEncounterDistance = IntToFloat(iMaxEncounterDistance); } else { fEncounterDistance = IntToFloat(iMinEncounterDistance + Random((iMaxEncounterDistance - iMinEncounterDistance) + 1)); } iEncounterDistance = FloatToInt(fEncounterDistance); // DETERMINE THE FACING OF THE SPAWN fCreatureFacing = IntToFloat(Random(360)); iFacingSameWay = Random(2); // Note: If there is more than one creature there is a 50% chance they will all be facing the same direction // DETERMINE TOTAL CHARACTER LEVELS TO CONSIDER WHEN CHOOSING A CREATURE // AND/OR DETERMINING THE NUMBER OF CREATURES TO SPAWN. // If the variable iEncounterType is AREA, this routine // determines the total character levels // based upon the character levels of all PCs // in a 20 meter radius around the object that spawned // the encounter. // Later on the total character levels will be compared to // the challenge rating of the creature spawned, and a number // of creatures will be determined from that comparison. if(iEncounterType == ENCOUNTER_TYPE_AREA) { oAmIAPC = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(oEncounterObject), FALSE, OBJECT_TYPE_CREATURE); while(GetIsObjectValid(oAmIAPC)) { if(GetIsPC(oAmIAPC)) { iLevels = iLevels + GetLevelByPosition(1, oAmIAPC) + GetLevelByPosition(2, oAmIAPC) + GetLevelByPosition(3, oAmIAPC); if(GetIsObjectValid(GetHenchman(oAmIAPC))) { iLevels = iLevels + GetLevelByPosition(1, GetHenchman(oAmIAPC)) + GetLevelByPosition(2, GetHenchman(oAmIAPC)) + GetLevelByPosition(3, GetHenchman(oAmIAPC)); } } oAmIAPC = GetNextObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(oEncounterObject), FALSE, OBJECT_TYPE_CREATURE); } } else if(iEncounterType == ENCOUNTER_TYPE_PARTY) { iLevels = GetFactionAverageLevel(oEncounterObject); } else { // If the variable iEncounterType is set to IND, this // routine determines the total character levels based upon the // character level of the object that spawned the encounter. // if the object that spawned the encounter is NOT a PC then // the number of creatures spawned will be one. This shouldn't // happen since the the encounter type sets itself to AREA if // the encounter object is a placeable. if(GetIsPC(oEncounterObject)) { iLevels = GetLevelByPosition(1, oEncounterObject) + GetLevelByPosition(2, oEncounterObject) + GetLevelByPosition(3, oEncounterObject); } } // Modify the float representing the total levels by the difficulty level. if(iLevelOverride) { iLevels = iLevelOverride; } fLevels = IntToFloat(iLevels) * fDifficulty; // CHOOSE A CREATURE TO SPAWN if(GetStringLowerCase(sTemplate) == "random" || GetStringLowerCase(GetStringLeft(sTemplate, 3)) == "re_") { if(GetStringLowerCase(GetStringLeft(sTemplate, 3)) == "re_") { sTemplate = GetStringRight(sTemplate, GetStringLength(sTemplate) - 3); } if(fLevels < 0.25) { fMaxCR = 0.25; } else { fMaxCR = fLevels; } fMinCR = IntToFloat(FloatToInt(fMaxCR * 0.2)); //If there is a definative number of creatures to spawn passed to //the RandomEncounter function when it is called, then do not //allow as much play in the low end, and a little more in the // high end challange ratings. if(iMinNumberOfCreatures == 0 && iMaxNumberOfCreatures > 1) { fMinCR = IntToFloat(FloatToInt(fMaxCR * 0.4)); fMaxCR = fMaxCR * 1.2; } if(iMinNumberOfCreatures == 0 && iMaxNumberOfCreatures == 1) { fMinCR = IntToFloat(FloatToInt(fMaxCR * 0.6)); fMaxCR = fMinCR * 1.2; } if(GetLocalInt(oMod, "bConsiderCR") == FALSE) { fMaxCR = 9999.0; fMinCR = 0.0; } sTemplate = GetRndEncCreature(fMinCR, fMaxCR, sTemplate); } // DETERMINE LOCATION AND SPAWN ONE CREATURE // NOTE: Line Of Sight checks have a bug. Bioware says they are looking // into the bug. I have spent an ungodly amount of hours trying to come // up with an acceptable work-around to the Line Of Sight functionality // of Get**ObjectInShape(). Unless somebody else can come up with a working // LOS check, I have no choice but to disregard LOS checks until they are // fixed. // // if(LOSCheck = TRUE) // { // // } // // note: one creature is spawned in now so its challange rating can be // used to determine if more are needed. (if that option is set) vEncounterVector = AngleToVector(fEncounterAngle); vVectorOffset = vEncounterVector * fEncounterDistance; vCreatureVector = vEncounterObjectVector + vVectorOffset; lCreatureLocation = Location(oArea, vCreatureVector, fCreatureFacing); oCreature = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCreatureLocation, TRUE); // DETERMINE THE NUMBER OF ADDITIONAL CREATURES TO SPAWN. // If the min and max number of creatures in the function call are zero // then get the min and max number from the local variables in the module // object. if(iMinNumberOfCreatures == 0 && iMaxNumberOfCreatures == 0) { iMinNumberOfCreatures = GetLocalInt(oMod, "iMinNumberOfCreatures"); iMaxNumberOfCreatures = GetLocalInt(oMod, "iMaxNumberOfCreatures"); } // Now that we are done with these local integers, we need to clean reset // them to their defaults so we don't accidentally use old numbers later. SetLocalInt(oMod, "iMinNumberOfCreatures", 0); SetLocalInt(oMod, "iMaxNumberOfCreatures", 0); if(iMinNumberOfCreatures == 0 && iMaxNumberOfCreatures != 0) { iNumberOfCreatures = iMaxNumberOfCreatures; } if(iMinNumberOfCreatures != 0 && iMaxNumberOfCreatures != 0) { iNumberOfCreatures = iMinNumberOfCreatures + Random((iMaxNumberOfCreatures - iMinNumberOfCreatures) + 1); } if(iMinNumberOfCreatures == 0 && iMaxNumberOfCreatures == 0) { // This is the routine that sets the number of creatures to spawn // based on their challenge rating and the total character levels. // It chooses a random number between one half (truncated) and 125 // percent (1 for every 4) of the number of creatures ideal for the // difficulty level set. iMaxNumberOfCreatures = FloatToInt(fLevels / GetChallengeRating(oCreature)); iMinNumberOfCreatures = FloatToInt(IntToFloat(iMaxNumberOfCreatures) * 0.75); iMaxNumberOfCreatures = FloatToInt(IntToFloat(iMaxNumberOfCreatures) * 1.25); iNumberOfCreatures = iMinNumberOfCreatures + Random((iMaxNumberOfCreatures - iMinNumberOfCreatures) + 1); if((iNumberOfCreatures < 1) && (iLevels > 0)) { iNumberOfCreatures = 1; } } // SPAWN THOSE SUCKERS! while(iCounter1 <= iNumberOfCreatures) { // Stick some labels on the creature for record keeping and reference (future use!) SetLocalInt(oCreature, "bRandomEncounter", TRUE); SetLocalObject(oCreature, "oRandomEncounterSpawner", oEncounterObject); SetLocalInt(oCreature, "iRandomEncounterCounter", 1); SetLocalInt(oCreature, "iRandomEncounterSpawnTime", (GetCalendarYear() * iYr) + (GetCalendarMonth() * iMth) + (GetCalendarDay()* iDay) + (GetTimeHour()* iHr) + (GetTimeMinute() * iMin) + GetTimeSecond()); SetLocalInt(oCreature, "iRandomEncounterLifeTime", iLifeTime); if(iCounter1 < iNumberOfCreatures) { oCreature = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCreatureLocation, TRUE); } iCounter1++; // Determine the facing of the next creature if(iFacingSameWay == FALSE) { fCreatureFacing = IntToFloat(Random(360)); lCreatureLocation = Location(oArea, vCreatureVector, fCreatureFacing); } } // Stick a lable on the spawning object for record keeping and reference (future use?) SetLocalString(oEncounterObject, "sLastRandomEncounterSpawned", GetTag(oCreature)); return oCreature; } void CleanHouse(int bDestroyPlotItems = FALSE, object oArea = OBJECT_SELF, int iSpawnOverride = 0, int iItemOverride = 0, int iBodyBagOverride = 0) { // IF NOT INITIALIZED THEN DO SO if(!GetLocalInt(GetModule(), "bInitialized")) { SetRndEncProperties(); } int iMph; if(!GetLocalInt(GetModule(), "iMph")) { iMph = 2; } else { iMph = GetLocalInt(GetModule(), "iMph"); } // DECLARE AND INTIALIZE VARIABLES int iMin = 60; int iHr = iMin * iMph; int iDay = iHr * 24; int iMth = iDay * 28; int iYr = iMth * 12; int bShouldIKillHim = TRUE; int iLifeTime; int iItemLifeTime; int iBodyBagLifeTime; int iPresentTime = (GetCalendarYear() * iYr) + (GetCalendarMonth() * iMth) + (GetCalendarDay() * iDay) + (GetTimeHour() * iHr) + (GetTimeMinute() * iMin) + GetTimeSecond(); object oObject; // GET EACH OBJECT IN THE AREA AND TEST FOR VALIDITY object oAmIASpawn = GetFirstObjectInArea(); while(GetIsObjectValid(oAmIASpawn)) { // IS IT A BODY BAG? if(GetTag(oAmIASpawn) == "Body Bag" && !GetLocalInt(oAmIASpawn, "bDroppedItem")) { SetLocalInt(oAmIASpawn, "bDroppedItem", TRUE); SetLocalInt(oAmIASpawn, "iDropTime", iPresentTime); } // IS IT A DROPPED ITEM? if(GetLocalInt(oAmIASpawn, "bDroppedItem")) { // HAS IT BEEN AROUND TOO LONG? if(iItemOverride) { iItemLifeTime = iItemOverride; } else { iItemLifeTime = 1800; } if(iBodyBagOverride) { iBodyBagLifeTime = iBodyBagOverride; } else { iBodyBagLifeTime = 300; } if((iPresentTime - GetLocalInt(oAmIASpawn, "iDropTime") > iItemLifeTime && GetTag(oAmIASpawn) != "Body Bag") || (iPresentTime - GetLocalInt(oAmIASpawn, "iDropTime") > iBodyBagLifeTime && GetTag(oAmIASpawn) == "Body Bag"))// && !GetPlotFlag(oAmIASpawn)) { if(GetHasInventory(oAmIASpawn)) { oObject = GetFirstItemInInventory(oAmIASpawn); while(GetIsObjectValid(oObject)) { if(!GetPlotFlag(oObject) || bDestroyPlotItems) { DestroyObject(oObject, 0.0); } oObject = GetNextItemInInventory(oAmIASpawn); } } if(!GetPlotFlag(oAmIASpawn) || bDestroyPlotItems) { DestroyObject(oAmIASpawn, 0.0); } } } // IS HE IS A RANDOM ENCOUNTER? if(GetLocalInt(oAmIASpawn, "bRandomEncounter")) { // HAS HE BEEN AROUND TOO LONG? if(iSpawnOverride) { iLifeTime = iSpawnOverride; } else { iLifeTime = GetLocalInt(oAmIASpawn, "iRandomEncounterLifeTime"); } if(iPresentTime - GetLocalInt(oAmIASpawn, "iRandomEncounterSpawnTime") > iLifeTime) { // IS HE IN COMBAT? if(!GetIsInCombat(oAmIASpawn)) { // GET EACH PC AND TEST IF THE CREATURE SEES HIM // Note: this is because the creature might be charmed // or influenced not to attack the PCs by other means. object oPC = GetFirstPC(); if(GetIsObjectValid(oPC)) { while(GetIsObjectValid(oPC)) { if(GetObjectSeen(oPC, oAmIASpawn)) { bShouldIKillHim = FALSE; } oPC = GetNextPC(); } } // IF THE CREATURE HAS PASSED ALL OF THESE CHECKS, DESTROY HIM. if(bShouldIKillHim) { DestroyObject(oAmIASpawn, 0.0); } } } } oAmIASpawn = GetNextObjectInArea(); } }