I’ve run into the same issues before, so to simplify my overall life, though create a slightly more complicated setup initially, I set a variable on all of my players, including DMs, when they login (OnClientEnter).
if (GetIsDM(oPC))
{
AddListObject(OBJECT_SELF, oPC, DM_ROSTER, TRUE);
SetLocalInt(oPC, IS_DM, TRUE);
}
else if (GetIsPC(oPC))
{
AddListObject(OBJECT_SELF, oPC, PLAYER_ROSTER, TRUE);
SetLocalInt(oPC, IS_PC, TRUE);
}
The AddListObject command just adds them to a pseudo-array that I can cycle instead of using the whole GetFirst/GetNextPC thing. The important part is the IS_PC and IS_DM portion. Additionally, OnAreaEnter for each area, I use this:
AddListObject(OBJECT_SELF, oPC, AREA_ROSTER, TRUE);
This creates a roster of all players, including DMs, in the area. So if I want to determine if the area is empty, instead of looping every object in the area, I simply call:
if (!CountObjectList(oArea, AREA_ROSTER))
{
//Do stuff here.
}
If I want to cycle the players in the area and determine if one’s a DM, I can do this:
int i, nCount = CountObjectList(oArea, AREA_ROSTER);
for (i = 0; i < nCount; i++)
{
oPC = GetListObject(oArea, i, AREA_ROSTER);
if (_GetIsDM(oPC))
// Do something here
}
And, of course, that couples with my custom _GetIsDM and _GetIsPC functions:
int _GetIsDM(object oPC)
{
return _GetLocalInt(oPC, IS_DM) || (GetIsDM(oPC) || GetIsDMPossessed(oPC));
}
int _GetIsPC(object oPC)
{
return _GetLocalInt(oPC, IS_PC) || (GetIsPC(oPC) && !_GetIsDM(oPC));
}
Don’t worry about the _GetLocal*, those are also custom variable handling functions that are essentially equivalent to the organic GetLocal*. So, yeah, a much more complicated setup, but makes my life a bit easier later on when trying to figure out who is where and what they are. One of the nice bonuses of this setup is the ability to create an OnAreaEmpty event, which is essentially what you’re using (in this case, I start a timer that runs when the AREA_ROSTER is depeleted – this is in the global OnAreaExit):
if (!RemoveListObject(OBJECT_SELF, oPC, AREA_ROSTER) && ENABLE_ON_AREA_EMPTY_EVENT)
{
int nTimerID = CreateTimer(OBJECT_SELF, AREA_EVENT_ON_EMPTY, ON_AREA_EMPTY_EVENT_DELAY, 1);
StartTimer(nTimerID, FALSE);
}
When the timer expires, the cleanup (or other) functions run. If a new player enters the area before the time expires, the timer is destroyed. The IS_DM and IS_PC is a global function for me, so I can use it anywhere for just about anything, such as ensuring a specific item can only be used by a DM:
if (!_GetIsDM(oActivator))
....
Sorry for the long-winded post, just trying to explain all aspects of it. This is probably overkill for small projects, but is a life (and cycle) saver for me on my bigger projects. The nice thing about this setup, though, is it’s all generic and importable into a new module, so once it’s up and running once, you can use it for any project.
Of course, now that I think about all your “is this guy possessed stuff”, I probably need to consider a check for PC possession of their familiars in the _GetIsPC call. Bleh, more work!