1. // anewonexit.nss
  2. // WoG's general area OnExit handler. This handles the cleanup tasks and
  3. // calls the script that lets monsters rest (forces them to, really).
  4.  
  5.  
  6. #include "nw_i0_plot"
  7.  
  8.  
  9. const string LV_SPAWNS_LEFT = "WoG_MySpawnsLeft";
  10.  
  11. // :: Delayed Cleanup for remains of familiars and animal companions ::
  12. // This is the tag the cleanup script will know denotes a fallen familiar/companion.
  13. const string ASSOCIATE_MARKER_TAG = "AssociateRemains"; // must match nw_ch_ac7.nss
  14. // Only reschedule associate cleanup once.
  15. const string ASSOCIATE_CLEANUP_SCHEDULED = "ASSOCIATE_CLEANUP_SCHEDULED";
  16. // The associate cleanup rescheduling delay, in turns (minutes).
  17. // This can also be set as local int ASSOCIATE_CLEANUP_DELAY on the area.
  18. const int DEFAULT_ASSOCIATE_CLEANUP_DELAY = 20; // one game hour
  19.  
  20. // The delayed function that implements the cleanup of oArea.
  21. void DoCleanup(object oArea);
  22. // Destroys an encounter creature, possibly triggering an encounter to reactivate.
  23. void DestroyEncounterCreature(object oCreature);
  24. // Implements an encounter resetting.
  25. void EncounterResetter(object oEncounter);
  26. // Destroys container (like the remains of a familiar or animal companion).
  27. void DestroyContainer(object oRemains);
  28.  
  29.  
  30. void main()
  31. {
  32.     object oArea = OBJECT_SELF;
  33.  
  34.     // Only fire for PCs.
  35.     //if ( !GetIsPC(GetExitingObject()) )
  36.     //    return;
  37.     // Cancel the PC check. The reason I got rid of the player count in a local
  38.     // variable is that there are some cases where OnExit fails to fire (e.g.
  39.     // a logout is reported to cause this in at least some cases). If we miss
  40.     // a PC exiting the area, we should grab the next opportunity to make up
  41.     // for it. --TK
  42.     // But we can still abort if the area is clean.
  43.     if ( !GetLocalInt(oArea, "WoG_Area_Entered") )
  44.         return;
  45.  
  46.  
  47.     // Look for a living PC in the area. (Ignore dead PCs for now -- they do not
  48.     // stop the part where monsters rest.)
  49.     object oPC = GetFirstObjectInArea(oArea);
  50.     if ( !GetIsPlayerCharacter(oPC)  ||  GetIsDead(oPC) )
  51.         oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oPC, 1,
  52.                                  CREATURE_TYPE_IS_ALIVE, TRUE);
  53.  
  54.     // If there is a living PC in the area, do nothing more.
  55.     if ( oPC != OBJECT_INVALID )
  56.         return;
  57.  
  58.     // Schedule a cleanup.
  59.     // (This is measured in seconds.)
  60.     int nTimeout = GetLocalInt(oArea, "CleanupDelay");
  61.     if ( nTimeout == 0 )
  62.         // Default to 30 seconds, to accommodate an accidental out & in.
  63.         nTimeout = 30;
  64.     DelayCommand(IntToFloat(nTimeout), DoCleanup(oArea));
  65.  
  66.     // Also let the monsters rest, particularly the non-encounter creatures.
  67.     ExecuteScript("onexitformonster", oArea);
  68. }
  69.  
  70.  
  71. // -----------------------------------------------------------------------------
  72.  
  73.  
  74. // The delayed function that implements the cleanup of oArea.
  75. void DoCleanup(object oArea)
  76. {
  77.     // Look for a living or dead PC in the area.
  78.     // (This ignores a player who had entered and left during the delay, but
  79.     // tracking that may involve more overhead than it is worth.)
  80.     object oPC = GetFirstObjectInArea(oArea);
  81.     if ( !GetIsPlayerCharacter(oPC) )
  82.     {
  83.         // Remember the first object in the area in case we need to do a second search.
  84.         object oReference = oPC;
  85.         oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oReference, 1,
  86.                                  CREATURE_TYPE_IS_ALIVE, TRUE);
  87.         if ( oPC == OBJECT_INVALID )
  88.             oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oReference, 1,
  89.                                      CREATURE_TYPE_IS_ALIVE, FALSE);
  90.     }
  91.     // No cleanup while a PC is in the area.
  92.     if ( oPC != OBJECT_INVALID )
  93.         return;
  94.  
  95.     // Get the delay in cleaning up associate remains
  96.     int CleanupDelay = GetLocalInt(oArea,"ASSOCIATE_CLEANUP_DELAY");
  97.     if ( CleanupDelay == 0 )
  98.         CleanupDelay = DEFAULT_ASSOCIATE_CLEANUP_DELAY;
  99.  
  100.     // Loop through all objects in the area.
  101.     int    nType;
  102.     string sLowerName;
  103.     object oLoop = GetFirstObjectInArea(oArea);
  104.     while ( oLoop != OBJECT_INVALID )
  105.     {
  106.         nType = GetObjectType(oLoop);
  107.         sLowerName = GetStringLowerCase(GetName(oLoop));
  108.        
  109.         // Determine the type of cleanup.
  110.         if ( nType == OBJECT_TYPE_CREATURE )
  111.         {
  112.             // Destroy encounter creatures; leave placed creatures alone.
  113.             // Avoid destroying encounter creatures dominated by PCs.
  114.             if ( GetIsEncounterCreature(oLoop)  &&  !GetIsPlayerCharacter(GetMaster(oLoop)) )
  115.                 DestroyEncounterCreature(oLoop);
  116.         }
  117.         else if ( nType == OBJECT_TYPE_ENCOUNTER )
  118.         {
  119.             // Check for a reset once the current script is done.
  120.             AssignCommand(oArea, EncounterResetter(oLoop));
  121.         }
  122.         else if ( nType == OBJECT_TYPE_ITEM  ||  sLowerName == "remains"  ||
  123.                   sLowerName == "corpse" )
  124.         {
  125.             // Clean up items and the contents of corpses and remains.
  126.             if ( GetHasInventory(oLoop) )
  127.             {
  128.                 // If cleanup has already been rescheduled, move on
  129.                 if ( GetLocalInt(oLoop, ASSOCIATE_CLEANUP_SCHEDULED) )
  130.                 {
  131.                     oLoop = GetNextObjectInArea(oArea);
  132.                     continue;
  133.                 }
  134.                 // If this is the remains of a fallen familiar, delay cleanup
  135.                 if ( HasItem(oLoop, ASSOCIATE_MARKER_TAG) )
  136.                 {
  137.                     DelayCommand(TurnsToSeconds(CleanupDelay), DestroyContainer(oLoop));
  138.                     SetLocalInt(oLoop, ASSOCIATE_CLEANUP_SCHEDULED, 1);
  139.                     oLoop = GetNextObjectInArea(oArea);
  140.                     continue;
  141.                 }
  142.                 // Destroy the inventory.
  143.                 object oItem = GetFirstItemInInventory(oLoop);
  144.                 while ( GetIsObjectValid(oItem) )
  145.                 {
  146.                     DestroyObject(oItem);
  147.                     oItem = GetNextItemInInventory(oLoop);
  148.                 }
  149.             }
  150.  
  151.             if ( nType == OBJECT_TYPE_ITEM )
  152.             {
  153.                 // Items get destroyed.
  154.                 DestroyObject(oLoop);
  155.             }
  156.         }
  157.  
  158.         // Update the loop.
  159.         oLoop = GetNextObjectInArea(oArea);
  160.     }
  161.  
  162.     // The area is now clean.
  163.     DeleteLocalInt(OBJECT_SELF, "WoG_Area_Entered");
  164. }
  165.  
  166.  
  167. // Destroys an encounter creature, possibly triggering an encounter to reactivate.
  168. void DestroyEncounterCreature(object oCreature)
  169. {
  170.     // See if we can trace this creature back to its encounter.
  171.     object oEncounter = GetLocalObject(oCreature, "WoG_MySpawner");
  172.     if ( oEncounter != OBJECT_INVALID )
  173.         // Increase the encounter's count of creatures left.
  174.         SetLocalInt(oEncounter, LV_SPAWNS_LEFT,
  175.             GetLocalInt(oEncounter, LV_SPAWNS_LEFT) + 1);
  176.  
  177.     // Now destroy the creature.
  178.     DestroyObject(oCreature);
  179. }
  180.  
  181.  
  182. // Implements an encounter resetting.
  183. void EncounterResetter(object oEncounter)
  184. {
  185.     int nTotal = GetLocalInt(oEncounter, "WoG_MySpawns");
  186.     int nRemaining = GetLocalInt(oEncounter, LV_SPAWNS_LEFT);
  187.  
  188.     // See if insufficient creatures had been killed before the cleanup.
  189.     if ( nRemaining > nTotal/4 )
  190.         // Reset this (after the creatures disappear).
  191.         DelayCommand(10.0, SetEncounterActive(TRUE, oEncounter));
  192.  
  193.     // Clean up the local variables.
  194.     DeleteLocalInt(oEncounter, "WoG_MySpawns");
  195.     DeleteLocalInt(oEncounter, LV_SPAWNS_LEFT);
  196. }
  197.  
  198. // Destroys container (like the remains of a familiar or animal companion).
  199. void DestroyContainer(object oContainer)
  200. {
  201.     // Quit if it's already gone
  202.     if ( !GetIsObjectValid(oContainer) )
  203.     {
  204.         return;
  205.     }
  206.     // Destroy the inventory.
  207.     object oItem = GetFirstItemInInventory(oContainer);
  208.     while ( GetIsObjectValid(oItem) )
  209.     {
  210.         DestroyObject(oItem);
  211.         oItem = GetNextItemInInventory(oContainer);
  212.     }
  213.     if ( GetObjectType(oContainer) == OBJECT_TYPE_ITEM )
  214.     {
  215.         DestroyObject(oContainer);
  216.     }
  217. }
  218.