From 8c465df44e49204c393f36107175d4e5cf3e57de Mon Sep 17 00:00:00 2001
From: Randy Heit <rheit@zdoom.fake>
Date: Wed, 1 Aug 2012 03:12:43 +0000
Subject: [PATCH] - Added ACS function IsTIDUsed(tid): It returns whether any
 actors using the given TID exist. This   is more efficient than
 ThingCount(tid, T_NONE), because it only needs to check for one actor   with
 the TID and not all of them. It also makes no distinction between dead things
 and live   things like ThingCount does. - Added ACS function UniqueTID(tid,
 limit): It returns a new TID that is not currently used by   any actors. It
 has two modes of operation. If tid is non-zero, then it checks TIDs
 one-by-one   starting at the given tid until if finds a free one. If tid is
 zero, then it returns a completely   random TID. If limit is non-zero, then
 it will only check that many times for a free TID, so   it might not find a
 free one. If no free TID is found, 0 is returned. If limit is zero, then  
 the search is effectively unlimited.

SVN r3798 (trunk)
---
 src/actor.h    |  4 +++
 src/p_acs.cpp  | 10 ++++++
 src/p_mobj.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 103 insertions(+), 4 deletions(-)

diff --git a/src/actor.h b/src/actor.h
index 13ddf91fa..a0fd4578e 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -976,6 +976,7 @@ private:
 	static FSharedStringArena mStringPropertyData;
 
 	friend class FActorIterator;
+	friend bool P_IsTIDUsed(int tid);
 
 	sector_t *LinkToWorldForMapThing ();
 
@@ -1070,6 +1071,9 @@ public:
 	}
 };
 
+bool P_IsTIDUsed(int tid);
+int P_FindUniqueTID(int start_tid, int limit);
+
 inline AActor *Spawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
 {
 	return AActor::StaticSpawn (type, x, y, z, allowreplacement);
diff --git a/src/p_acs.cpp b/src/p_acs.cpp
index 8ab158d48..fe2001411 100644
--- a/src/p_acs.cpp
+++ b/src/p_acs.cpp
@@ -3363,6 +3363,8 @@ enum EACSFunctions
 	ACSF_ACS_NamedLockedExecuteDoor,
 	ACSF_ACS_NamedExecuteWithResult,
 	ACSF_ACS_NamedExecuteAlways,
+	ACSF_UniqueTID,
+	ACSF_IsTIDUsed,
 
 	// ZDaemon
 	ACSF_GetTeamScore = 19620,
@@ -3879,6 +3881,14 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args)
 			}
 			break;
 
+		case ACSF_UniqueTID:
+			return P_FindUniqueTID(argCount > 0 ? args[0] : 0, argCount > 1 ? args[1] : 0);
+			break;
+
+		case ACSF_IsTIDUsed:
+			return P_IsTIDUsed(args[0]);
+			break;
+
 		default:
 			break;
 	}
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index 666cb2612..938c976f3 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -37,6 +37,7 @@
 #include "doomstat.h"
 #include "v_video.h"
 #include "c_cvars.h"
+#include "c_dispatch.h"
 #include "b_bot.h"	//Added by MC:
 #include "stats.h"
 #include "a_hexenglobal.h"
@@ -107,6 +108,7 @@ static FRandom pr_missiledamage ("MissileDamage");
  FRandom pr_slam ("SkullSlam");
 static FRandom pr_multiclasschoice ("MultiClassChoice");
 static FRandom pr_rockettrail("RocketTrail");
+static FRandom pr_uniquetid("UniqueTID");
 
 // PUBLIC DATA DEFINITIONS -------------------------------------------------
 
@@ -2612,10 +2614,7 @@ AActor *AActor::TIDHash[128];
 
 void AActor::ClearTIDHashes ()
 {
-	int i;
-
-	for (i = 0; i < 128; i++)
-		TIDHash[i] = NULL;
+	memset(TIDHash, NULL, sizeof(TIDHash));
 }
 
 //
@@ -2666,6 +2665,92 @@ void AActor::RemoveFromHash ()
 	tid = 0;
 }
 
+//==========================================================================
+//
+// P_IsTIDUsed
+//
+// Returns true if there is at least one actor with the specified TID
+// (dead or alive).
+//
+//==========================================================================
+
+bool P_IsTIDUsed(int tid)
+{
+	AActor *probe = AActor::TIDHash[tid & 127];
+	while (probe != NULL)
+	{
+		if (probe->tid == tid)
+		{
+			return true;
+		}
+		probe = probe->inext;
+	}
+	return false;
+}
+
+//==========================================================================
+//
+// P_FindUniqueTID
+//
+// Returns an unused TID. If start_tid is 0, then a random TID will be
+// chosen. Otherwise, it will perform a linear search starting from
+// start_tid. If limit is non-0, then it will not check more than <limit>
+// number of TIDs. Returns 0 if no suitable TID was found.
+//
+//==========================================================================
+
+int P_FindUniqueTID(int start_tid, int limit)
+{
+	int tid;
+
+	if (start_tid != 0)
+	{ // Do a linear search.
+		limit = start_tid + limit - 1;
+		if (limit < start_tid)
+		{ // If it overflowed, clamp to INT_MAX
+			limit = INT_MAX;
+		}
+		for (tid = start_tid; tid <= limit; ++tid)
+		{
+			if (tid != 0 && !P_IsTIDUsed(tid))
+			{
+				return tid;
+			}
+		}
+		// Nothing free found.
+		return 0;
+	}
+	// Do a random search. To try and be a bit more performant, this
+	// actually does several linear searches. In the case of *very*
+	// dense TID usage, this could potentially perform worse than doing
+	// a complete linear scan starting at 1. However, you would need
+	// to use an absolutely ridiculous number of actors before this
+	// becomes a real concern.
+	if (limit == 0)
+	{
+		limit = INT_MAX;
+	}
+	for (int i = 0; i < limit; i += 5)
+	{
+		// Use a positive starting TID.
+		tid = pr_uniquetid.GenRand32() & INT_MAX;
+		tid = P_FindUniqueTID(tid == 0 ? 1 : tid, 5);
+		if (tid != 0)
+		{
+			return tid;
+		}
+	}
+	// Nothing free found.
+	return 0;
+}
+
+CCMD(utid)
+{
+	Printf("%d\n",
+		P_FindUniqueTID(argv.argc() > 1 ? atoi(argv[1]) : 0,
+		argv.argc() > 2 ? atoi(argv[2]) : 0));
+}
+
 //==========================================================================
 //
 // AActor :: GetMissileDamage