/** * Generic OnOpen and OnDeath handler for respawning containers. * To use this script: *
    *
  1. Create a new invisible plot object to act as the respawner. Note its * resref, as we'll be using it later.
  2. *
  3. Create a container and set this script as its OnOpen and OnDeath * handler.
  4. *
  5. Create a custom OnUserDefined script for the container.
  6. *
  7. In that script, #include "dmc_inc_contgen"
  8. *
  9. In that script, handle the UE_SET_RESPAWN_VARIABLES event. In it, * set the following local variables: * * Once you've finished setting these variables, call * callbackSetRespawnVariables.
  10. *
  11. In that script, optionally handle the UE_GENERATE_TREASURE event. In it, * generate treasure for the container. You can use the treasure generation * system from the original campaign (found in NW_O2_CONINCLUDE), the * treasure generation system from Shadows of Undrentide (found in * X0_I0_TREASURE), or your own treasure generation system. Prior to the * event being signalled, the CONTAINER_ACTOR_TAG local object is set to * the object that triggered the event (whoever opened or destroyed the * container).
  12. *
  13. In that script, optionally handle the UE_CONTAINER_DESTRUCTION event. In * it, perform any operations needed when the container is destroyed, such * as destroying fragile treasure. This is signalled AFTER the * UE_GENERATE_TREASURE event. Prior to the event being signalled, the * CONTAINER_ACTOR_TAG local object is set to the object that triggered the * event (whoever destroyed the container).
  14. * * Feel free to use or modify this script freely, with the exception that this * paragraph must remain unchanged. Please leave some mention of the original * authors in the comments of the script, and if you have an area in your module * where you give credit to the developers/contributors, please be sure to * include us. * * @author David Carr * @author * R.Lowe * * @version 0.3 */ #include "dmc_inc_contgen" /** * PRIVATE: Returns the value of GetLastOpenedBy() if set, otherwise the value * of GetLastKiller() if set, otherwise OBJECT_INVALID. */ object GetLastOpenerOrKiller() { object oLastOpenedBy = GetLastOpenedBy(); object oLastKiller = GetLastKiller(); if(GetIsObjectValid(oLastOpenedBy)) { return oLastOpenedBy; } else if(GetIsObjectValid(oLastKiller)){ return oLastKiller; } else { return OBJECT_INVALID; } } /** * PRIVATE: Returns if the specified object was killed */ int ObjectWasKilled() { return GetIsObjectValid(GetLastKiller()); } /** * PRIVATE: Alert nearby creatures when a container is disturbed. * Based on code in NW_O2_CONINCLUDE. */ void ShoutDisturbed() { object oContainer = OBJECT_SELF; location lLoc = GetLocation(oContainer); object oAttacker = GetLastOpenerOrKiller(); object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lLoc, TRUE, OBJECT_TYPE_CREATURE); //Cycle through the targets within the spell shape until an //invalid object is captured. while(GetIsObjectValid(oTarget)) { if(GetFactionEqual(oTarget, oContainer)) { //Make anyone who is a member of my faction hostile if I am violated SetIsTemporaryEnemy(oAttacker, oTarget); AssignCommand(oTarget, ActionAttack(oAttacker)); } oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lLoc, TRUE, OBJECT_TYPE_CREATURE); } } /** * PRIVATE: Returns true if it's okay to generate treasure now */ int respawnTimerHasExpired(object oContainer) { return !GetLocalInt(oContainer, RESPAWN_TIMER_TAG); } /** * PRIVATE: Puts a hold on treasure generation until clearRespawnTimer is called */ void setRespawnTimer(object oContainer) { SetLocalInt(oContainer, RESPAWN_TIMER_TAG, TRUE); } /** * PRIVATE: Removes the association between the spawner and a container */ void clearInstance(object oSpawner) { DeleteLocalObject(oSpawner, INSTANCE_TAG); } /** * PRIVATE: Returns the respawner object associated with a container, or * OBJECT_INVALID if one doesn't exist */ object getRespawner(object oContainer) { return GetLocalObject(oContainer, RESPAWNER_TAG); } /** * PRIVATE: Signals the container to set its variables, such as respawn duration * and whether to store the inventory. No arguments are passed. */ void signalSetRespawnVariables(object oContainer) { SignalEvent(oContainer, EventUserDefined(UE_SET_RESPAWN_VARIABLES)); } /** * PRIVATE: Signals the treasure generation event for the container. One * argument is passed, the "actor" who triggered the event. */ void signalTreasureGeneration(object oContainer, object oActor) { //Set the actor on the container so that it can be accessed from user events SetLocalObject(oContainer, CONTAINER_ACTOR_TAG, oActor); SignalEvent(oContainer, EventUserDefined(UE_GENERATE_TREASURE)); } /** * PRIVATE: Signals the container destruction event for the container. One * argument is passed, the "actor" who triggered the event. */ void signalContainerDestruction(object oContainer, object oActor) { //Set the actor on the container so that it can be accessed from user events SetLocalObject(oContainer, CONTAINER_ACTOR_TAG, oActor); SignalEvent(oContainer, EventUserDefined(UE_CONTAINER_DESTRUCTION)); } /** * PRIVATE: If a respawner for the specified container doesn't exist, create * one. If it's a new respawner or the instance was destroyed, update the stored * instance. Then, queue a command to respawn the treasure/container. */ void scheduleRespawn(object oContainer) { if(!GetIsObjectValid(oContainer) || !GetHasInventory(oContainer)) { logError("invalid object passed to scheduleRespawn"); return; } object oSpawner = getRespawner(oContainer); if(!GetIsObjectValid(oSpawner)) {//If there isn't a spawner already signalSetRespawnVariables(oContainer); } else { addRespawnToEventQueue(oSpawner); } } /** * PRIVATE: First, determine whether the container was opened or destroyed. * Then, if it has been destroyed or it is time to generate treasure, set a * timer for a respawn. Then, generate the treasure if appropriate. If the * chest was destroyed, potions may be shattered. Also, alert any nearby * creatures. */ void main() { object oContainer = OBJECT_SELF; object oActor = GetLastOpenerOrKiller(); int bWasKilled = ObjectWasKilled(); if(!GetIsObjectValid(oActor)) { logError("object was neither opened nor destroyed"); return; } if(respawnTimerHasExpired(oContainer)) { setRespawnTimer(oContainer); scheduleRespawn(oContainer); signalTreasureGeneration(oContainer, oActor); } else if(bWasKilled) { //clear the instance so that the respawn event knows to make a new container clearInstance(getRespawner(oContainer)); } if(bWasKilled) { signalContainerDestruction(oContainer, oActor); } ShoutDisturbed(); }