2025/09/03 Update
Modified CEP 2DA hak to remove extraneous creatures from palette. Added door closer script to most town doors. Added Amulet of Mighty Fists +1 through +5 to loot table. Fixed names on remainder of creatures. Updated PEPS. Set PEPS to server mode. Set module to server mode. Updated Class Title function in ms_name_inc. Updated dungeon areas to reset, globally & per player. Added "new" weapons to the Blacksmith. Added magical "new" weapons to the treasure tables. Fixed persistent storage.
This commit is contained in:
165
_module/nss/area_onexit.nss
Normal file
165
_module/nss/area_onexit.nss
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
// Clears out non-plot, non-immortal objects from the area
|
||||
void CleanupArea(object oArea)
|
||||
{
|
||||
object oObj = GetFirstObjectInArea(oArea);
|
||||
while (GetIsObjectValid(oObj))
|
||||
{
|
||||
int bDestroy = FALSE;
|
||||
|
||||
// Clean creatures
|
||||
if (GetObjectType(oObj) == OBJECT_TYPE_CREATURE)
|
||||
{
|
||||
if (!GetPlotFlag(oObj) && !GetImmortal(oObj) && !GetIsPC(oObj))
|
||||
{
|
||||
bDestroy = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean items
|
||||
else if (GetObjectType(oObj) == OBJECT_TYPE_ITEM)
|
||||
{
|
||||
if (!GetPlotFlag(oObj) && !GetImmortal(oObj))
|
||||
{
|
||||
bDestroy = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean placeables (note: static placeables can't be destroyed anyway)
|
||||
else if (GetObjectType(oObj) == OBJECT_TYPE_PLACEABLE)
|
||||
{
|
||||
if (!GetPlotFlag(oObj) && !GetImmortal(oObj))
|
||||
{
|
||||
bDestroy = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDestroy)
|
||||
{
|
||||
WriteTimestampedLogEntry("CleanupArea: Destroying object " + GetTag(oObj) + " in " + GetName(oArea));
|
||||
DestroyObject(oObj);
|
||||
}
|
||||
|
||||
oObj = GetNextObjectInArea(oArea);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ResetPlayerDungeon(string sAreaTag, object oPC)
|
||||
{
|
||||
int bDebug = GetLocalInt(GetModule(), "MMD_DEBUG");
|
||||
|
||||
object oArea = GetObjectByTag(sAreaTag);
|
||||
|
||||
// Allow this PC to trigger spawns in this area again
|
||||
DeleteLocalInt(oPC, "DungeonCooldown_" + sAreaTag);
|
||||
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit >> ResetPlayerDungeon: Cooldown expired for area " + GetName(oArea)+" / ("+sAreaTag+").");
|
||||
|
||||
WriteTimestampedLogEntry("area_onexit >> ResetPlayerDungeon: Cooldown expired for PC " + GetName(oPC) + " in area " + GetName(oArea)+" / ("+sAreaTag+").");
|
||||
}
|
||||
|
||||
void ResetDungeonIfEmpty(string sAreaTag)
|
||||
{
|
||||
int bDebug = GetLocalInt(GetModule(), "MMD_DEBUG");
|
||||
|
||||
object oArea = GetObjectByTag(sAreaTag);
|
||||
if (oArea == OBJECT_INVALID) return;
|
||||
|
||||
// check if still empty
|
||||
object oCheck = GetFirstObjectInArea(oArea);
|
||||
while (oCheck != OBJECT_INVALID)
|
||||
{
|
||||
if (GetIsPC(oCheck)) return; // still occupied
|
||||
oCheck = GetNextObjectInArea(oArea);
|
||||
}
|
||||
|
||||
// clear waypoint flags so the dungeon can respawn
|
||||
object oSpawn = GetFirstObjectInArea(oArea);
|
||||
while (oSpawn != OBJECT_INVALID)
|
||||
{
|
||||
if (GetObjectType(oSpawn) == OBJECT_TYPE_WAYPOINT)
|
||||
{
|
||||
DeleteLocalInt(oSpawn, "done");
|
||||
}
|
||||
oSpawn = GetNextObjectInArea(oArea);
|
||||
}
|
||||
|
||||
object oPC = GetFirstPC();
|
||||
while (oPC != OBJECT_INVALID)
|
||||
{
|
||||
if (GetLocalInt(oPC, "DungeonCooldown_" + sAreaTag) == 1)
|
||||
{
|
||||
SetLocalInt(oPC, "DungeonCooldown_" + sAreaTag, 0);
|
||||
WriteTimestampedLogEntry("area_onexit >> ResetDungeonIfEmpty: Cooldown cleared for " + GetName(oPC) + " in area " + GetName(oArea)+" / ("+sAreaTag+").");
|
||||
}
|
||||
oPC = GetNextPC();
|
||||
}
|
||||
|
||||
if (bDebug) SpeakString("area_onexit >> ResetDungeonIfEmpty: Dungeon reset complete for area " + GetName(oPC) + " in area " + GetName(oArea)+" / ("+sAreaTag+").");
|
||||
|
||||
DelayCommand(0.0f, CleanupArea(oArea));
|
||||
|
||||
WriteTimestampedLogEntry("area_onexit >> ResetDungeonIfEmpty: Dungeon area " + GetName(oArea)+" / ("+sAreaTag+") is fully reset (no PCs present).");
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oPC = GetExitingObject();
|
||||
object oDoor = OBJECT_SELF;
|
||||
|
||||
if (!GetIsPC(oPC)) return;
|
||||
|
||||
int bDebug = GetLocalInt(GetModule(), "MMD_DEBUG");
|
||||
|
||||
object oArea = GetArea(oDoor);
|
||||
string sAreaTag = GetTag(oArea);
|
||||
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Exiting area " + sAreaTag);
|
||||
WriteTimestampedLogEntry("area_onexit: PC " + GetName(oPC) + " exited " + sAreaTag);
|
||||
|
||||
// If no timer is running for this PC/area, start one
|
||||
int iCooldownActive = GetLocalInt(oPC, "DungeonCooldownRunning_" + sAreaTag);
|
||||
if (!iCooldownActive)
|
||||
{
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Scheduling cooldown reset for " + sAreaTag + " in 5 minutes.");
|
||||
WriteTimestampedLogEntry("area_onexit: Starting cooldown timer for " + GetName(oPC) + " in area " + sAreaTag);
|
||||
|
||||
SetLocalInt(oPC, "DungeonCooldownRunning_" + sAreaTag, 1);
|
||||
|
||||
// 5-minute cooldown for THIS PC only
|
||||
DelayCommand(300.0, ResetPlayerDungeon(sAreaTag, oPC));
|
||||
DelayCommand(300.0, DeleteLocalInt(oPC, "DungeonCooldownRunning_" + sAreaTag));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Cooldown already active for " + sAreaTag);
|
||||
WriteTimestampedLogEntry("area_onexit: Cooldown already active for " + GetName(oPC) + " in area " + sAreaTag);
|
||||
}
|
||||
|
||||
// check if any PCs remain
|
||||
object oCheck = GetFirstObjectInArea(oArea);
|
||||
while (oCheck != OBJECT_INVALID)
|
||||
{
|
||||
if (GetIsPC(oCheck) && oCheck != oPC)
|
||||
{
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Another PC remains in area " + GetName(oArea)+" / ("+sAreaTag+"). Not resetting.");
|
||||
WriteTimestampedLogEntry("area_onexit: Area " + sAreaTag + " not empty, skipping full reset.");
|
||||
return; // still someone here
|
||||
}
|
||||
oCheck = GetNextObjectInArea(oArea);
|
||||
}
|
||||
|
||||
// no PCs left -> schedule full dungeon reset in 5 minutes
|
||||
if (sAreaTag != "area4_20") // exclude cow level
|
||||
{
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Scheduling dungeon reset for " + GetName(oArea)+" / ("+sAreaTag+") in 5 minutes.");
|
||||
WriteTimestampedLogEntry("area_onexit: Scheduling full reset for area " + GetName(oArea)+" / ("+sAreaTag+") in 5 minutes.");
|
||||
DelayCommand(300.0, ResetDungeonIfEmpty(sAreaTag));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bDebug) SendMessageToPC(oPC, "area_onexit: Cow level excluded from reset.");
|
||||
WriteTimestampedLogEntry("area_onexit: Cow level excluded from reset (" + sAreaTag + ")");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user