Here's a chunk from our generic onenter, which goes on every area in the module:
/* spawn anything specified in area locals */
int nSpawns = GetLocalInt(oArea, "Area_Spawns");
string sTask;
struct IntList li;
if (nSpawns > 0) {
int i, nType;
string sKey, sTag, sRes, sLoc, sLocType;
object oSpawn;
location lSpawn;
for (i = 1; i <= nSpawns; i++) {
sKey = "Area_Spawn_" + IntToString(i);
sTag = GetLocalString(oArea, sKey + "_Tag");
/* if the object has already been spawned, skip it */
if (sTag != "") {
if (GetIsObjectValid(GetNearestObjectByTag(sTag, oPC)))
continue;
} else {
if (GetIsObjectValid(GetLocalObject(oArea, sKey + "_Obj")))
continue;
}
sRes = GetLocalString(oArea, sKey + "_Res");
sLoc = GetLocalString(oArea, sKey + "_Loc");
nType = GetLocalInt(oArea, sKey + "_Type");
if (nType == 0)
nType = OBJECT_TYPE_CREATURE;
sLocType = GetStringLeft(sLoc, 1);
if (sLocType == "!")
lSpawn = GetLocation(GetNearestObjectByTag(GetSubString(sLoc, 1, 64), oPC));
else if (sLocType == "@")
lSpawn = GetLocation(GetWaypointByTag(GetSubString(sLoc, 1, 64)));
else if (sLocType == "&") {//designates task creature, only spawn if pc requires (uses waypoint) ex: tsk_cr_1_2
sTask = GetStringRight(sRes, (GetStringLength(sRes) - 7));
li = GetIntList(sTask, "_");
if (!GetPCHasTaskActive(oPC, li.i0) || (GetTaskCompleted(oPC, li.i0) != (li.i1 - 1)))//if they don't have the task active, don't spawn
continue;
else
lSpawn = GetLocation(GetWaypointByTag(GetSubString(sLoc, 1, 64)));
} else
continue;
if (GetAreaFromLocation(lSpawn) == oArea) {
oSpawn = CreateObject(nType, sRes, lSpawn, FALSE, sTag);
SetLocalObject(oArea, sKey + "_Obj", oSpawn);
}
}
}
Code is acaos', and very elegant. You can basically spawn what you want where you want. For example, here are the variables that spawn in two creatures in the area in question:
Area_Spawns int 2 /* indicates two NPC spawnins in this area */
Area_Spawn_1_Loc string @ele_water_m2s /* indicates location of the first spawn, a waypoint named ele_water_m2s */
Area_Spawn_1_Res string ele_portalwater /*indicates resref of the first spawn */
Area_Spawn_1_Tag string ele_water_s2m /* indicates what the tag of the first spawn should be set to - this allows you to spawn in multiple creatures of the same resref at different points without the script mixing them up */
Area_Spawn_2_Loc string @ele_water_m2d /* waypoint location of second spawn */
Area_Spawn_2_Res string ele_portalwater /*resref of second spawn, same as first */
Area_Spawn_2_Tag string ele_water_d2m /* tag to set on second spawn */
This example shows how you can ensure only one of each NPC spawns in, even when using the same resref more than once.
Here's the entire onexit. You won't be able to use it, as there are missing includes, but you can see what gets despawned and what doesn't. The NPCs from the onenter script Have a Despawn int 1 set on them - you could set up a much simpler loop just to eliminate anything with that int set on it, if you liked.
#include "hg_inc"
#include "afx_sfunc"
#include "ac_qstatus_inc"
#include "legendarybab_inc"
#include "gen_interact_inc"
void TrashObject (object oObject) {
if (GetObjectType(oObject) == OBJECT_TYPE_PLACEABLE) {
/* search and destroy contents of placeables */
object oItem = GetFirstItemInInventory(oObject);
while (GetIsObjectValid(oItem)) {
SetPlotFlag(oItem, FALSE);
DestroyObject(oItem);
oItem = GetNextItemInInventory(oObject);
}
}
SetPlotFlag(oObject, FALSE);
DestroyObject(oObject);
}
/* -------------------- remove this section once the new loot system is in place -------------------- */
void RespawnObject(string sTag, int iType, location lLoc, int nLoot = 0) {
object oPlace = CreateObject(iType, sTag, lLoc);
if (nLoot != 0)
SetLocalInt(oPlace, "RLoot", nLoot);
}
void DespawnRespawn(object oObject, float fDelay) {
SetPlotFlag(oObject, FALSE);
TrashObject(oObject);
string sTag = GetResRef(oObject);
int iType = GetObjectType(oObject);
location lLoc = GetLocation(oObject);
/* if this is set to 1, object spawns random loot, tag should be preserved */
int nLoot = GetLocalInt(oObject, "RLoot");
/* added nLoot paramenter to Respawn function to pass locals to respawned
* version (avoids having to set them in the object palettes) */
if (fDelay < 8000.0)
AssignCommand(GetArea(oObject), DelayCommand(fDelay, RespawnObject(sTag, iType, lLoc, nLoot)));
}
/* -------------------- end section to be removed -------------------- */
void main () {
object oPC = GetExitingObject();
object oObject, oArea = OBJECT_SELF;
/* deplot everything leaving an area, to prevent invulnerability exploits -
* exceptions for BBoD, DMs, and intentionally invulnerable summons */
if (GetResRef(oPC) != "x2_s_bblade" && !GetLocalInt(oPC, "FKY_CHAT_INVULN") && !GetLocalInt(oPC, "PlotSummons"))
SetPlotFlag(oPC, FALSE);
/* destroy all dominated creatures unless specifically allowed to transition */
if (!GetIsPC(oPC) &&
GetHasEffectOfType(EFFECT_TYPE_DOMINATED, oPC) &&
!GetLocalInt(oPC, "DomTransition")) {
DestroyObject(oPC);
return;
}
/* only players and DMs are tagged with ID */
if (GetLocalString(oPC, "ID") == "" || GetIsDM(oPC))
return;
/* fire off custom script for area exit if specified */
string sExecuteExit = GetLocalString(oArea, "Area_OnExit");
if (sExecuteExit != "")
ExecuteScript(sExecuteExit, oArea);
/* de-AFK players on any transition */
DeleteLocalInt(oPC, "FKY_CHAT_AFK");
DeleteLocalObject(oPC, "FKY_CHAT_BULK_TARGET");
if (GetHasSpellEffect(HGEFFECT_AREA_PENALTIES, oPC)) {
RemoveEffectsFromSpell(HGEFFECT_AREA_PENALTIES, oPC);
CheckLegendaryBABEffect(oPC);
}
/* remove supernatural darkness from area */
RemoveEffectsOfType(EFFECT_TYPE_DARKNESS, oPC, oArea);
/* removes effects of underwater bubbles if any */
if (GetHasSpellEffect(HGEFFECT_UNDERWATER_BUBBLES, oPC))
RemoveEffectsFromSpell(HGEFFECT_UNDERWATER_BUBBLES, oPC);
/* scan to see if there are any PCs left in the area */
oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (GetArea(oPC) == oArea && oPC != oArea && !GetIsDM(oPC))
return;
oPC = GetNextPC();
}
SetLocalInt(oArea, "Area_Clear", 1);
/* fire off custom script for pc-free area if specififed */
string sExecuteClear = GetLocalString(oArea, "Area_OnClear");
if (sExecuteClear != "")
ExecuteScript(sExecuteClear, oArea);
/* reset any interaction triggers in need of it, and shut off any interactions
marked for area-exit shutoff */
int nTriggerResetCount = GetLocalInt(oArea, "Interact_Triggers_To_Reset");
if (nTriggerResetCount) {
int nX, nY, nZ, nInteractions, nObjMask;
object oTriggerToReset, oLoop;
string sObjTag;
for (nX = 1; nX <= nTriggerResetCount; nX++) {
oTriggerToReset = GetLocalObject(oArea, "Interact_Trigger_Reset_" + IntToString(nX));
nInteractions = GetLocalInt(oTriggerToReset, "Interact_Count");
for (nY = 1; nY <= nInteractions; nY++) {
if (GetLocalInt(oTriggerToReset, "Interact_Trigger_Shutoff_" + IntToString(nY)) == 2)
DeleteLocalInt(oTriggerToReset, "Interact_Off_" + IntToString(nY));
if (GetLocalInt(oTriggerToReset, "Interact_Interaction_Shutoff_" + IntToString(nY)) == 2) {
sObjTag = GetLocalString(OBJECT_SELF, "Interact_Object_Tag_" + IntToString(nY));
if (sObjTag == "")
sObjTag = "gen_interact_obj";
nObjMask = GetLocalInt(OBJECT_SELF, "Interact_Object_Mask_" + IntToString(nY));
nZ = 1;
oLoop = GetNearestObjectByTag(sObjTag, oTriggerToReset, 1);
while (GetIsObjectValid(oLoop)) {
if ((GetLocalInt(oLoop, "Interact_Object_Mask") & nObjMask) &&
(GetLocalInt(OBJECT_SELF, "Interact_Interaction_Shutoff_"
+ IntToString(nY)) == 1))
ShutOffInteraction(oLoop, nY);
nZ++;
oLoop = GetNearestObjectByTag(sObjTag, oTriggerToReset, nZ);
}
}
}
}
}
/* despawn the entire area */
int bArena = GetLocalInt(oArea, "Arena");
int nUptime = GetLocalInt(GetModule(), "uptime");
int nDespawn, bDespawnTracking = GetLocalInt(oArea, "DespawnTrack");
int bNoDespawnCreatures = GetLocalInt(oArea, "NoDespawnCreatures");
string sBoss;
for (oObject = GetFirstObjectInArea(oArea);
GetIsObjectValid(oObject);
oObject = GetNextObjectInArea(oArea)) {
switch (GetObjectType(oObject)) {
case OBJECT_TYPE_PLACEABLE:
/*
if (GetTag(oObject) == "BodyBag" || GetLocalInt(oObject, "Despawn"))
TrashObject(oObject);
*/
/* ---- remove this section once the new loot system is in place ---- */
nDespawn = GetLocalInt(oObject, "fkydespawn");
if (GetTag(oObject) == "BodyBag" || nDespawn < 0)
TrashObject(oObject);
else if (nDespawn)
DespawnRespawn(oObject, IntToFloat(nDespawn));
/* ---- end section to be removed ----------------------------------- */
break;
case OBJECT_TYPE_ITEM:
if (!bArena)
TrashObject(oObject);
break;
case OBJECT_TYPE_CREATURE:
if (GetIsPC(oObject) || GetIsPC(GetMaster(oObject)) || bNoDespawnCreatures)
break;
if ((sBoss = GetLocalString(oObject, "TrapArea_Waypoint")) != "") {
location lSelf = GetLocation(oObject);
object oTrapped, oTrapArea = GetArea(GetWaypointByTag(sBoss));
string sMessage = GetLocalString(oObject, "TrapArea_Escape");
if (GetIsObjectValid(oTrapArea)) {
for (oTrapped = GetFirstPC(); GetIsObjectValid(oTrapped); oTrapped = GetNextPC()) {
if (GetArea(oTrapped) == oTrapArea) {
SendMessageToPC(oTrapped, sMessage);
ForceJump(oTrapped, lSelf);
}
}
}
}
if ((sBoss = GetLocalString(oObject, "QSDeathTag")) != "") {
struct SubString ss = GetFirstSubString(sBoss, "~");
if (ss.first != "")
QSSetQuestStatus(ss.first, StringToInt(ss.rest) * -1);
SetPlotFlag(oObject, FALSE);
DestroyObject(oObject);
}
/* delete NPCs, encounter creatures, and spawned in
* creatures (with Despawn set). Don't delete creatures that
* have a PC as a master, as summons and dominates could be destroyed
* before they can zone out with their master. */
if ((nDespawn = GetLocalInt(oObject, "Despawn")) ||
(GetIsEncounterCreature(oObject) && !GetIsPC(GetMaster(oObject)))) {
if (nDespawn >= 0 && bDespawnTracking) {
AddLocalInt(oArea, "DespawnCount", 1);
SetLocalInt(oArea, "DespawnTime", nUptime);
}
TrashObject(oObject);
}
break;
case OBJECT_TYPE_TRIGGER:
if (GetLocalInt(oObject, "Despawn"))
TrashObject(oObject);
break;
}
}
}
Hope that helps. Let me know if you have questions.
Funky