From 7d20ea008726acb9da33a0e8b71a79ec77b7471d Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sun, 28 Apr 2024 15:04:13 -0300
Subject: [PATCH] Reorder specials Implement Teleport Implement SetViewpoint
 Implement Thing_TrackAngle Implement Thing_StopTrackingAngle Rename
 Player_DisableControls Rename HaveUnlockable Remove tid from mapthing_t Fix
 typo in CallFunc_ThingSound

---
 extras/acs/srb2special.acs |  37 ++++----
 src/acs/call-funcs.cpp     | 189 +++++++++++++++++++++++++++++++------
 src/acs/call-funcs.hpp     |   6 +-
 src/acs/environment.cpp    |  29 +++---
 src/doomdata.h             |   1 -
 src/f_finale.h             |   8 ++
 src/p_mobj.c               |   2 +-
 src/p_setup.c              |  11 +--
 8 files changed, 215 insertions(+), 68 deletions(-)

diff --git a/extras/acs/srb2special.acs b/extras/acs/srb2special.acs
index 5bdd779475..bb76e4c1d3 100644
--- a/extras/acs/srb2special.acs
+++ b/extras/acs/srb2special.acs
@@ -13,21 +13,26 @@ special
 	-101:strcasecmp(2,3),
 	-300:CountEnemies(2),
 	-301:CountPushables(2),
-	-303:HaveUnlockable(1),
+	// -302:SkinAvailable(1),
+	-303:HasUnlockable(1),
 	-304:PlayerSkin(0),
-	-305:GetObjectDye(0),
-	-306:PlayerEmeralds(0),
-	-307:PlayerLap(0),
-	-308:LowestLap(0),
+	-305:PlayerEmeralds(0),
+	-306:PlayerBot(0),
+	-307:PlayerExiting(0),
+	-308:PlayerLap(0),
 	-309:RingSlingerMode(0),
 	-310:TeamGame(0),
-	-311:RecordAttack(0),
-	-312:Thing_Dye(1),
-	-313:CaptureTheFlagMode(0),
-	-315:PlayerBot(0),
-	-316:ModeAttacking(0),
-	-317:NiGHTSAttack(0),
-	-320:PlayerExiting(0),
+	-311:CaptureTheFlagMode(0),
+	-312:RecordAttack(0),
+	-313:NiGHTSAttack(0),
+	-314:ModeAttacking(0),
+	-315:LowestLap(0),
+	-320:GetActorDye(0),
+	-321:Thing_Dye(1),
+	-322:Teleport(2,3),
+	-323:SetViewpoint(1,2),
+	-324:Thing_TrackAngle(4,6),
+	-325:Thing_StopTrackingAngle(1),
 	-500:CameraWait(1),
 	-503:SetLineRenderStyle(3),
 	-504:MapWarp(2),
@@ -47,14 +52,12 @@ special
 	404:Ceiling_Move(2),
 	409:Sector_ChangeTag(3),
 	411:Plane_Stop(1),
-	// 412:Teleport(2,5), // to reimplement
 	416:Light_StartFlickering(3,5),
 	417:Light_StartPulsating(3,5),
 	418:Light_StartBlinking(3,6),
 	419:Light_StartBlinkingSynchronized(3,6),
 	420:Light_Fade(3,6),
 	421:StopLightingEffect(1),
-	// 422:SwitchToCutAwayView(2), // should be reimplemented to search by TID
 	424:Weather_Change(1,2),
 	425:Thing_ChangeState(1),
 	426:Thing_Stop(0,1),
@@ -66,7 +69,7 @@ special
 	// 434:AwardPowerUp(2), // to reimplement
 	435:Plane_ChangeScrollerDirection(2),
 	436:FOF_Shatter(2),
-	437:Player_DisableControls(1,2),
+	437:LockPlayer(1,2),
 	438:Thing_ChangeSize(1),
 	439:Line_ChangeTextures(2,4),
 	440:StartMetalSonicRace(0),
@@ -85,11 +88,9 @@ special
 	454:FOF_StopFading(2),
 	455:Colormap_Fade(3,4),
 	456:Colormap_StopFading(1),
-	// 457:Thing_TrackAngle(4,5), // should be reimplemented to search by TID
-	// 458:Thing_StopTrackingAngle(0),
 	// 459:StartConversation(), // to reimplement
 	460:AwardRings(1,2),
-	// 461:Thing_Spawn(2), // to reimplement
+	// 461:Thing_Spawn(2), // to implement (use the actor names; SRB2 doesn't have spawn numbers)
 	462:StopTimer(0),
 	464:TriggerEggCapsule(1,2),
 	466:SetLevelFailureState(1),
diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp
index 234be2093d..45cfbce6f5 100644
--- a/src/acs/call-funcs.cpp
+++ b/src/acs/call-funcs.cpp
@@ -25,6 +25,7 @@
 #include "../d_think.h"
 #include "../p_mobj.h"
 #include "../p_tick.h"
+#include "../f_finale.h"
 #include "../w_wad.h"
 #include "../m_misc.h"
 #include "../m_random.h"
@@ -549,12 +550,13 @@ bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::
 		if (success == false)
 		{
 			// Exit early.
-
 			CONS_Alert(CONS_WARNING,
 				"Couldn't find object type \"%s\" for ThingCount.\n",
 				className
 			);
 
+			thread->dataStk.push(0);
+
 			return false;
 		}
 	}
@@ -908,12 +910,13 @@ bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:
 		if (success == false)
 		{
 			// Exit early.
-
 			CONS_Alert(CONS_WARNING,
 				"Couldn't find sfx named \"%s\" for SectorSound.\n",
 				sfxName
 			);
 
+			thread->dataStk.push(0);
+
 			return false;
 		}
 	}
@@ -966,12 +969,13 @@ bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
 		if (success == false)
 		{
 			// Exit early.
-
 			CONS_Alert(CONS_WARNING,
 				"Couldn't find sfx named \"%s\" for AmbientSound.\n",
 				sfxName
 			);
 
+			thread->dataStk.push(0);
+
 			return false;
 		}
 	}
@@ -1106,11 +1110,12 @@ bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS
 --------------------------------------------------*/
 bool CallFunc_ChangeSky(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	P_SetupLevelSky(argV[0], argV[1]);
 
+	thread->dataStk.push(0);
+
 	return false;
 }
 
@@ -1152,12 +1157,13 @@ bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::
 		if (success == false)
 		{
 			// Exit early.
-
 			CONS_Alert(CONS_WARNING,
-				"Couldn't find sfx named \"%s\" for AmbientSound.\n",
+				"Couldn't find sfx named \"%s\" for ThingSound.\n",
 				sfxName
 			);
 
+			thread->dataStk.push(0);
+
 			return false;
 		}
 	}
@@ -1467,11 +1473,11 @@ bool CallFunc_CountPushables(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS
 }
 
 /*--------------------------------------------------
-	bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+	bool CallFunc_HasUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 
-		Returns if an unlockable has been gotten.
+		Returns if something was unlocked.
 --------------------------------------------------*/
-bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+bool CallFunc_HasUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
 	UINT32 id = 0;
 	bool unlocked = false;
@@ -1535,7 +1541,6 @@ bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
 		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 		&& (info->mo->player != NULL))
 	{
-
 		thread->dataStk.push(info->mo->player->bot);
 		return false;
 	}
@@ -1616,6 +1621,138 @@ bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
 	return false;
 }
 
+/*--------------------------------------------------
+	bool CallFunc_Teleport(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+
+		Teleports the activating actor.
+--------------------------------------------------*/
+bool CallFunc_Teleport(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+{
+	mobj_t *mobj = P_FindMobjFromTID(argV[0], NULL, NULL);
+	mobj_t *dest = P_FindMobjFromTID(argV[1], NULL, NULL);
+
+	if (mobj != NULL && dest != mobj)
+	{
+		boolean silent = argC >= 3 ? (argV[2] != 0) : false;
+
+		P_Teleport(mobj, dest->x, dest->y, dest->z, dest->angle, !silent, false);
+
+		if (!silent)
+			S_StartSound(dest, sfx_mixup); // Play the 'bowrwoosh!' sound
+	}
+
+	thread->dataStk.push(0);
+
+	return false;
+}
+
+/*--------------------------------------------------
+	bool CallFunc_SetViewpoint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+
+		Switches the current viewpoint to another actor.
+--------------------------------------------------*/
+bool CallFunc_SetViewpoint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+{
+	auto info = &static_cast<Thread *>(thread)->info;
+
+	if ((info != NULL) && (info->mo != NULL && P_MobjWasRemoved(info->mo) == false))
+	{
+		mobj_t *altview = P_FindMobjFromTID(argV[0], NULL, info->mo);
+		if (!altview)
+		{
+			thread->dataStk.push(0);
+
+			return false;
+		}
+
+		// If titlemap, set the camera ref for title's thinker
+		// This is not revoked until overwritten; awayviewtics is ignored
+		if (titlemapinaction)
+		{
+			titlemapcameraref = altview;
+			titlemapcameraref->cusval = altview->pitch;
+		}
+		else
+		{
+			player_t *player = info->mo->player;
+			if (!player)
+			{
+				thread->dataStk.push(0);
+
+				return false;
+			}
+
+			if (!player->awayviewtics || player->awayviewmobj != altview)
+			{
+				P_SetTarget(&player->awayviewmobj, altview);
+
+				if (player == &players[displayplayer])
+					P_ResetCamera(player, &camera); // reset p1 camera on p1 getting an awayviewmobj
+				else if (splitscreen && player == &players[secondarydisplayplayer])
+					P_ResetCamera(player, &camera2);  // reset p2 camera on p2 getting an awayviewmobj
+			}
+
+			player->awayviewaiming = altview->pitch;
+			player->awayviewtics = argC > 1 ? argV[1] : -1;
+		}
+	}
+
+	thread->dataStk.push(0);
+
+	return false;
+}
+
+/*--------------------------------------------------
+	bool CallFunc_TrackObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+
+		Implementation of linedef type 457.
+--------------------------------------------------*/
+bool CallFunc_TrackObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+{
+	mobj_t *mobj = P_FindMobjFromTID(argV[0], NULL, NULL);
+	mobj_t *anchormo = P_FindMobjFromTID(argV[1], NULL, NULL);
+	if (mobj != NULL && anchormo != NULL && P_MobjWasRemoved(mobj) == false && P_MobjWasRemoved(anchormo) == false)
+	{
+		INT32 failureangle = FixedAngle((std::min(std::max(abs((int)argV[2]), 0), 360))*FRACUNIT);
+		INT32 failureexectag = argV[3];
+		INT32 failuredelay = argC >= 5 ? abs((int)argV[4]) : 0;
+		boolean persist = argC >= 6 ? (argV[5] == 0) : false;
+
+		mobj->eflags |= MFE_TRACERANGLE;
+		P_SetTarget(&mobj->tracer, anchormo);
+		mobj->lastlook = persist; // don't disable behavior after first failure
+		mobj->extravalue1 = failureangle; // angle to exceed for failure state
+		mobj->extravalue2 = failureexectag; // exec tag for failure state (angle is not within range)
+		mobj->cusval = mobj->cvmem = failuredelay; // cusval = tics to allow failure before line trigger; cvmem = decrement timer
+	}
+
+	thread->dataStk.push(0);
+
+	return false;
+}
+
+/*--------------------------------------------------
+	bool CallFunc_StopTrackingObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+
+		Implementation of linedef type 458.
+--------------------------------------------------*/
+bool CallFunc_StopTrackingObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
+{
+	(void)argC;
+
+	mobj_t *mobj = P_FindMobjFromTID(argV[0], NULL, NULL);
+	if (mobj != NULL && P_MobjWasRemoved(mobj) == false && (mobj->eflags & MFE_TRACERANGLE))
+	{
+		mobj->eflags &= ~MFE_TRACERANGLE;
+		P_SetTarget(&mobj->tracer, NULL);
+		mobj->lastlook = mobj->cvmem = mobj->cusval = mobj->extravalue1 = mobj->extravalue2 = 0;
+	}
+
+	thread->dataStk.push(0);
+
+	return false;
+}
+
 /*--------------------------------------------------
 	bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 
@@ -1786,7 +1923,6 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV,
 
 	INT32 lineId = -1;
 
-	(void)thread;
 	(void)argC;
 
 	tag = argV[0];
@@ -1866,16 +2002,17 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor
 	if (nextmap == 0)
 	{
 		CONS_Alert(CONS_WARNING, "MapWarp level %s is not valid or loaded.\n", levelName);
-		return false;
 	}
+	else
+	{
+		nextmapoverride = nextmap;
 
-	nextmapoverride = nextmap;
-
-	if (argV[1] == 0)
-		skipstats = 1;
+		if (argV[1] == 0)
+			skipstats = 1;
 
-	if (server)
-		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+		if (server)
+			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+	}
 
 	thread->dataStk.push(0);
 
@@ -1942,7 +2079,6 @@ bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word
 --------------------------------------------------*/
 bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argV;
 	(void)argC;
 
@@ -1954,6 +2090,8 @@ bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
 	if (server)
 		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
 
+	thread->dataStk.push(0);
+
 	return false;
 }
 
@@ -1972,6 +2110,8 @@ bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
 
 	if (argC > 1 && argV[1] && !ACS_ActivatorIsLocal(thread))
 	{
+		thread->dataStk.push(0);
+
 		return false;
 	}
 
@@ -1994,6 +2134,8 @@ bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
 
 	if (argC > 0 && argV[0] && !ACS_ActivatorIsLocal(thread))
 	{
+		thread->dataStk.push(0);
+
 		return false;
 	}
 
@@ -2028,7 +2170,6 @@ bool CallFunc_MusicRestore(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
 --------------------------------------------------*/
 bool CallFunc_MusicDim(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	// 0: int fade time (ms) - time to fade between full volume and silence
@@ -2091,7 +2232,6 @@ static INT32 NextLine(mtag_t tag, size_t *iterate, INT32 activatorID)
 
 bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2175,7 +2315,6 @@ bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, AC
 
 bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2312,7 +2451,6 @@ enum
 
 bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2418,7 +2556,6 @@ bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, AC
 
 bool CallFunc_SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2663,7 +2800,6 @@ static INT32 NextSector(mtag_t tag, size_t *iterate, INT32 activatorID)
 
 bool CallFunc_GetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2764,7 +2900,6 @@ bool CallFunc_GetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV,
 
 bool CallFunc_SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -2961,7 +3096,6 @@ enum
 
 bool CallFunc_GetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
@@ -3163,7 +3297,6 @@ bool CallFunc_GetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A
 
 bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 {
-	(void)thread;
 	(void)argC;
 
 	auto info = &static_cast<Thread *>(thread)->info;
diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp
index 10e7a4bfdd..5f4c494a8c 100644
--- a/src/acs/call-funcs.hpp
+++ b/src/acs/call-funcs.hpp
@@ -72,12 +72,16 @@ bool CallFunc_strcasecmp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::
 
 bool CallFunc_CountEnemies(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_CountPushables(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
-bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
+bool CallFunc_HasUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_SetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
+bool CallFunc_Teleport(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
+bool CallFunc_SetViewpoint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
+bool CallFunc_TrackObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
+bool CallFunc_StopTrackingObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
 bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp
index 5440fa8ffa..f0a7ca7da0 100644
--- a/src/acs/environment.cpp
+++ b/src/acs/environment.cpp
@@ -160,21 +160,26 @@ Environment::Environment()
 
 	addFuncDataACS0( 300, addCallFunc(CallFunc_CountEnemies));
 	addFuncDataACS0( 301, addCallFunc(CallFunc_CountPushables));
-	addFuncDataACS0( 303, addCallFunc(CallFunc_HaveUnlockable));
+	// addFuncDataACS0( 302, addCallFunc(CallFunc_SkinAvailable));
+	addFuncDataACS0( 303, addCallFunc(CallFunc_HasUnlockable));
 	addFuncDataACS0( 304, addCallFunc(CallFunc_PlayerSkin));
-	addFuncDataACS0( 305, addCallFunc(CallFunc_GetObjectDye));
-	addFuncDataACS0( 306, addCallFunc(CallFunc_PlayerEmeralds));
-	addFuncDataACS0( 307, addCallFunc(CallFunc_PlayerLap));
-	addFuncDataACS0( 308, addCallFunc(CallFunc_LowestLap));
+	addFuncDataACS0( 305, addCallFunc(CallFunc_PlayerEmeralds));
+	addFuncDataACS0( 306, addCallFunc(CallFunc_PlayerBot));
+	addFuncDataACS0( 307, addCallFunc(CallFunc_PlayerExiting));
+	addFuncDataACS0( 308, addCallFunc(CallFunc_PlayerLap));
 	addFuncDataACS0( 309, addCallFunc(CallFunc_RingSlingerMode));
 	addFuncDataACS0( 310, addCallFunc(CallFunc_TeamGame));
-	addFuncDataACS0( 311, addCallFunc(CallFunc_RecordAttack));
-	addFuncDataACS0( 312, addCallFunc(CallFunc_SetObjectDye));
-	addFuncDataACS0( 313, addCallFunc(CallFunc_CaptureTheFlagMode));
-	addFuncDataACS0( 315, addCallFunc(CallFunc_PlayerBot));
-	addFuncDataACS0( 316, addCallFunc(CallFunc_ModeAttacking));
-	addFuncDataACS0( 317, addCallFunc(CallFunc_NiGHTSAttack));
-	addFuncDataACS0( 320, addCallFunc(CallFunc_PlayerExiting));
+	addFuncDataACS0( 311, addCallFunc(CallFunc_CaptureTheFlagMode));
+	addFuncDataACS0( 312, addCallFunc(CallFunc_RecordAttack));
+	addFuncDataACS0( 313, addCallFunc(CallFunc_NiGHTSAttack));
+	addFuncDataACS0( 314, addCallFunc(CallFunc_ModeAttacking));
+	addFuncDataACS0( 315, addCallFunc(CallFunc_LowestLap));
+	addFuncDataACS0( 320, addCallFunc(CallFunc_GetObjectDye));
+	addFuncDataACS0( 321, addCallFunc(CallFunc_SetObjectDye));
+	addFuncDataACS0( 322, addCallFunc(CallFunc_Teleport));
+	addFuncDataACS0( 323, addCallFunc(CallFunc_SetViewpoint));
+	addFuncDataACS0( 324, addCallFunc(CallFunc_TrackObjectAngle));
+	addFuncDataACS0( 325, addCallFunc(CallFunc_StopTrackingObjectAngle));
 
 	addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait));
 	addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle));
diff --git a/src/doomdata.h b/src/doomdata.h
index 785242671b..c90af23e41 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -244,7 +244,6 @@ typedef struct
 	UINT16 options;
 	INT16 z;
 	UINT8 extrainfo;
-	mtag_t tid;
 	taglist_t tags;
 	fixed_t scale;
 	fixed_t spritexscale, spriteyscale;
diff --git a/src/f_finale.h b/src/f_finale.h
index 4b777eb11f..76555f8c79 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -19,6 +19,10 @@
 #include "d_event.h"
 #include "p_mobj.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 //
 // FINALE
 //
@@ -233,4 +237,8 @@ enum
 };
 extern UINT8 wipedefs[NUMWIPEDEFS];
 
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
 #endif
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2f04b17f7b..65adb530b3 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -13474,7 +13474,7 @@ void P_CopyMapThingSpecialFieldsToMobj(const mapthing_t *mthing, mobj_t *mobj)
 {
 	size_t arg = SIZE_MAX;
 
-	P_SetThingTID(mobj, mthing->tid);
+	P_SetThingTID(mobj, Tag_FGet(&mthing->tags));
 
 	mobj->special = mthing->special;
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 67d9d56cb7..0af07dd068 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1544,7 +1544,6 @@ static void P_LoadThings(UINT8 *data)
 		mt->type = READUINT16(data);
 		mt->options = READUINT16(data);
 		mt->extrainfo = (UINT8)(mt->type >> 12);
-		mt->tid = 0;
 		Tag_FSet(&mt->tags, 0);
 		mt->scale = FRACUNIT;
 		mt->spritexscale = mt->spriteyscale = FRACUNIT;
@@ -2120,10 +2119,7 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char
 static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *val)
 {
 	if (fastcmp(param, "id"))
-	{
-		mapthings[i].tid = atol(val);
 		Tag_FSet(&mapthings[i].tags, atol(val));
-	}
 	else if (fastcmp(param, "moreids"))
 	{
 		const char* id = val;
@@ -2318,14 +2314,16 @@ typedef struct
 static void P_WriteTextmap_Things(FILE *f, const mapthing_t *wmapthings)
 {
 	size_t i, j;
+	mtag_t firsttag;
 
 	// Actual writing
 	for (i = 0; i < nummapthings; i++)
 	{
 		fprintf(f, "thing // %s\n", sizeu1(i));
 		fprintf(f, "{\n");
-		if (wmapthings[i].tid != 0)
-			fprintf(f, "id = %d;\n", wmapthings[i].tid);
+		firsttag = Tag_FGet(&wmapthings[i].tags);
+		if (firsttag != 0)
+			fprintf(f, "id = %d;\n", firsttag);
 		if (wmapthings[i].tags.count > 1)
 		{
 			fprintf(f, "moreids = \"");
@@ -3275,7 +3273,6 @@ static void P_LoadTextmap(void)
 		mt->options = 0;
 		mt->z = 0;
 		mt->extrainfo = 0;
-		mt->tid = 0;
 		Tag_FSet(&mt->tags, 0);
 		mt->scale = FRACUNIT;
 		mt->spritexscale = mt->spriteyscale = FRACUNIT;
-- 
GitLab