ff_u_gc_area

Area garbage collector.

Variables

ENABLE__FF_U_GC_AREA: (string) (opt-out) Set to “N” to disable unit.

GC_AREA_DURATION: (int) Maximum age in module game hours before a corpse or loose item is destroyed in an empty area. Defaults to 24.

Source code

// @code

#include "ff_i_core"


// -----------------------------------------------------------------------------

const string ENABLE__FF_U_GC_AREA = "ENABLE__FF_U_GC_AREA";

const string ARRAY_NAME = "__FF_U_GC_AREA__ARRAY";

const string GC_AREA_TIMESTAMP = "__FF_U_GC_AREA__TIMESTAMP";

const string GC_AREA_DURATION = "GC_AREA_DURATION";

const int DEFAULT_DURATION = 20;


// -----------------------------------------------------------------------------

void _TrackItem(object oObject)
{
  ObjectArrayAdd(ARRAY_NAME, oObject);
  SetLocalInt(oObject, GC_AREA_TIMESTAMP, GetTimeStamp());
}

//! @brief Start tracking oObject for area garbage collection if eligible.
//! @param oObject An item or BodyBag placeable.
void TrackItem(object oObject);
void TrackItem(object oObject)
{
  if (!GetIsObjectValid(oObject))
    return;

  object oPossessor = GetItemPossessor(oObject);
  int nType = GetObjectType(oObject);

  // Keep all items with the tag BodyBag,
  // Keep all items on the ground.
  if (nType == OBJECT_TYPE_ITEM && (GetIsObjectValid(oPossessor) == FALSE))
    _TrackItem(oObject);
  else if (nType == OBJECT_TYPE_PLACEABLE && (GetTag(oObject) == TAG_BODY_BAG))
    _TrackItem(oObject);
  else if (nType == OBJECT_TYPE_ITEM && (GetTag(oPossessor) == TAG_BODY_BAG))
  {
    object oItem = GetFirstItemInInventory(oPossessor);
    while (GetIsObjectValid(oItem))
    {
      _TrackItem(oItem);
      oItem = GetNextItemInInventory(oPossessor);
    }
  }
}


// -----------------------------------------------------------------------------

//! @brief Remove oObject from area GC tracking.
//! @param oObject A tracked object.
void UntrackItem(object oObject);
void UntrackItem(object oObject)
{
  if (!GetIsObjectValid(oObject))
    return;

  ObjectArrayDelete(ARRAY_NAME, oObject);
  DeleteLocalInt(oObject, GC_AREA_TIMESTAMP);
}


// -----------------------------------------------------------------------------

//! @brief Track all placeables and creatures within 5m of lWhere.
//! @param lWhere Centre of the search sphere.
void TrackFromLocation(location lWhere);
void TrackFromLocation(location lWhere)
{
  // Mark the location for later.
  object oObject = GetFirstObjectInShape(SHAPE_SPHERE, 5.0f, lWhere, FALSE, OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_CREATURE);
  while (GetIsObjectValid(oObject))
  {
    TrackItem(oObject);
    oObject = GetNextObjectInShape(SHAPE_SPHERE, 5.0f, lWhere, FALSE, OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_CREATURE);
  }
}


// -----------------------------------------------------------------------------

void OnRealtimeMinute();
void OnRealtimeMinute()
{
  object oModule = GetModule();
  int nNow = GetTimeStamp();

  sqlquery sqlQuery = ObjectArrayIterate(ARRAY_NAME);
  while (SqlStep(sqlQuery))
  {
    string sUUID = SqlGetString(sqlQuery, 0);
    object oItem = GetObjectByUUID(sUUID);

    if (!GetIsObjectValid(oItem))
    {
      ObjectArrayDelete(ARRAY_NAME, oItem);
      continue;
    }

    int nStart = GetLocalInt(oItem, GC_AREA_TIMESTAMP);
    if (nStart == 0)
    {
      SetLocalInt(oItem, GC_AREA_TIMESTAMP, GetTimeStamp());
      continue;
    }

    int nMaxAge = GetRecursiveInt(oItem, GC_AREA_DURATION, DEFAULT_DURATION);
    nMaxAge = max(nMaxAge, 1);
    nMaxAge = nMaxAge * 60; // in minutes

    int nSeconds = nNow - nStart;
    if (nSeconds > 0 && nSeconds >= nMaxAge)
    {
      LogInfo("Area Garbage Collector: Removal of " + GetObjectInfo(oItem)
        + " (age: " + IntToString(nSeconds)
        + ", max age: " + IntToString(nMaxAge) + ")");
      ObjectArrayDelete(ARRAY_NAME, oItem);

      DestroyInventory(oItem, 0.0f);
      DestroyObject(oItem, 0.1f);
    }
  }
}


// -----------------------------------------------------------------------------

void OnConsole();
void OnConsole()
{
  object oModule = GetModule();
  string sCommand = GetLocalString(oModule, CONSOLE_COMMAND);
  string sCommandLine = GetLocalString(oModule, CONSOLE_ARGS);
  struct CLI_ARGUMENTS sArgs = GetArguments(sCommand + " " + sCommandLine);

  int nTime = StringToInt(sArgs.sArg1);
  if (nTime == 0 && (sArgs.sArg1 == ""))
    nTime = -1;
  OnRealtimeMinute();
}


// -----------------------------------------------------------------------------

void main()
{
  object oModule = GetModule();
  if (!GetModuleFlag(ENABLE__FF_U_GC_AREA, TRUE))
    return;

  string sEvent = GetCurrentEvent();
  if (sEvent == ON_REGISTER)
  {
    CreateObjectArray(ARRAY_NAME);

    // RegisterConsoleCommand("gc", __FILE__); FIXME/TODO
    SubscribeToEvent(ON_DEFAULT_OBJECT_DEATH, __FILE__);
    SubscribeToEvent(ON_DEFAULT_ITEM_ACQUIRE, __FILE__);
    SubscribeToEvent(ON_DEFAULT_ITEM_LOST, __FILE__);
    SubscribeToEvent(ON_REALTIME_MINUTE, __FILE__);
    SubscribeToEvent(ON_CONSOLE_COMMAND_HELP, __FILE__);
  }

  else if (sEvent == ON_DEFAULT_ITEM_ACQUIRE)
    UntrackItem(GetModuleItemAcquired());
  else if (sEvent == ON_DEFAULT_ITEM_LOST)
    TrackItem(GetModuleItemLost());
  else if (sEvent == ON_REALTIME_MINUTE)
    OnRealtimeMinute();
  else if (sEvent == ON_DEFAULT_CREATURE_DEATH || sEvent == ON_DEFAULT_OBJECT_DEATH)
  {
    object oObject = OBJECT_SELF; // technically, not a PC
    location lObject = GetLocation(oObject);
    AssignCommand(GetArea(oObject), DelayCommand(10.0f, TrackFromLocation(lObject)));
  }
  //else if (sEvent == ON_CONSOLE_COMMAND_HELP)
  //  NWNX_Util_RawPrint(GetStringRightPad("gc AREA TIME", CONSOLE_HELP_PAD) + "- Run garbage collector");
  else
    OnConsole();
}