mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			4126 lines
		
	
	
	
		
			94 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4126 lines
		
	
	
	
		
			94 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// DR. ROBOTNIK'S RING RACERS
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// Copyright (C) 2016 by James Haley, David Hill, et al. (Team Eternity)
 | 
						|
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
 | 
						|
// Copyright (C) 2022 by Kart Krew
 | 
						|
//
 | 
						|
// This program is free software distributed under the
 | 
						|
// terms of the GNU General Public License, version 2.
 | 
						|
// See the 'LICENSE' file for more details.
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
/// \file  call-funcs.cpp
 | 
						|
/// \brief Action Code Script: CallFunc instructions
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <cctype>
 | 
						|
 | 
						|
#include "acsvm.hpp"
 | 
						|
 | 
						|
#include "../doomtype.h"
 | 
						|
#include "../doomdef.h"
 | 
						|
#include "../doomstat.h"
 | 
						|
 | 
						|
#include "../d_think.h"
 | 
						|
#include "../p_mobj.h"
 | 
						|
#include "../p_tick.h"
 | 
						|
#include "../w_wad.h"
 | 
						|
#include "../m_random.h"
 | 
						|
#include "../g_game.h"
 | 
						|
#include "../d_player.h"
 | 
						|
#include "../r_defs.h"
 | 
						|
#include "../r_state.h"
 | 
						|
#include "../p_polyobj.h"
 | 
						|
#include "../taglist.h"
 | 
						|
#include "../p_local.h"
 | 
						|
#include "../deh_tables.h"
 | 
						|
#include "../fastcmp.h"
 | 
						|
#include "../hu_stuff.h"
 | 
						|
#include "../s_sound.h"
 | 
						|
#include "../r_textures.h"
 | 
						|
#include "../m_cond.h"
 | 
						|
#include "../r_skins.h"
 | 
						|
#include "../k_kart.h"
 | 
						|
#include "../k_battle.h"
 | 
						|
#include "../k_grandprix.h"
 | 
						|
#include "../k_podium.h"
 | 
						|
#include "../k_bot.h"
 | 
						|
#include "../z_zone.h"
 | 
						|
#include "../music.h"
 | 
						|
#include "../r_draw.h"
 | 
						|
#include "../k_dialogue.hpp"
 | 
						|
#include "../k_hud.h"
 | 
						|
 | 
						|
#include "call-funcs.hpp"
 | 
						|
 | 
						|
#include "environment.hpp"
 | 
						|
#include "thread.hpp"
 | 
						|
#include "../cxxutil.hpp"
 | 
						|
 | 
						|
using namespace srb2::acs;
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_ThingCount. Gets
 | 
						|
		an object type from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The mobj class string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
 | 
						|
{
 | 
						|
	if (fastncmp("MT_", word, 3))
 | 
						|
	{
 | 
						|
		// take off the MT_
 | 
						|
		word += 3;
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < NUMMOBJFREESLOTS; i++)
 | 
						|
	{
 | 
						|
		if (!FREE_MOBJS[i])
 | 
						|
		{
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (fastcmp(word, FREE_MOBJS[i]))
 | 
						|
		{
 | 
						|
			*type = static_cast<mobjtype_t>(static_cast<int>(MT_FIRSTFREESLOT) + i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < MT_FIRSTFREESLOT; i++)
 | 
						|
	{
 | 
						|
		if (fastcmp(word, MOBJTYPE_LIST[i] + 3))
 | 
						|
		{
 | 
						|
			*type = static_cast<mobjtype_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type)
 | 
						|
 | 
						|
		Helper function for sound playing functions.
 | 
						|
		Gets a SFX id from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The sound effect string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type)
 | 
						|
{
 | 
						|
	if (fastncmp("SFX_", word, 4))
 | 
						|
	{
 | 
						|
		// take off the SFX_
 | 
						|
		word += 4;
 | 
						|
	}
 | 
						|
	else if (fastncmp("DS", word, 2))
 | 
						|
	{
 | 
						|
		// take off the DS
 | 
						|
		word += 2;
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < NUMSFX; i++)
 | 
						|
	{
 | 
						|
		if (S_sfx[i].name && fasticmp(word, S_sfx[i].name))
 | 
						|
		{
 | 
						|
			*type = static_cast<sfxenum_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetSpriteFromString(const char *word, spritenum_t *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_Get/SetThingProperty.
 | 
						|
		Gets a sprite from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The sprite string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetSpriteFromString(const char *word, spritenum_t *type)
 | 
						|
{
 | 
						|
	if (fastncmp("SPR_", word, 4))
 | 
						|
	{
 | 
						|
		// take off the SPR_
 | 
						|
		word += 4;
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < NUMSPRITES; i++)
 | 
						|
	{
 | 
						|
		if (fastncmp(word, sprnames[i], 4))
 | 
						|
		{
 | 
						|
			*type = static_cast<spritenum_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetSprite2FromString(const char *word, playersprite_t *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_Get/SetThingProperty.
 | 
						|
		Gets a sprite2 from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The sprite2 string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetSprite2FromString(const char *word, playersprite_t *type)
 | 
						|
{
 | 
						|
	if (fastncmp("SPR2_", word, 5))
 | 
						|
	{
 | 
						|
		// take off the SPR2_
 | 
						|
		word += 5;
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < free_spr2; i++)
 | 
						|
	{
 | 
						|
		if (fastcmp(word, spr2names[i]))
 | 
						|
		{
 | 
						|
			*type = static_cast<playersprite_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetStateFromString(const char *word, playersprite_t *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_Get/SetThingProperty.
 | 
						|
		Gets a state from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The state string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetStateFromString(const char *word, statenum_t *type)
 | 
						|
{
 | 
						|
	if (fastncmp("S_", word, 2))
 | 
						|
	{
 | 
						|
		// take off the S_
 | 
						|
		word += 2;
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < NUMMOBJFREESLOTS; i++)
 | 
						|
	{
 | 
						|
		if (!FREE_STATES[i])
 | 
						|
		{
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (fastcmp(word, FREE_STATES[i]))
 | 
						|
		{
 | 
						|
			*type = static_cast<statenum_t>(static_cast<int>(S_FIRSTFREESLOT) + i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for (int i = 0; i < S_FIRSTFREESLOT; i++)
 | 
						|
	{
 | 
						|
		if (fastcmp(word, STATE_LIST[i] + 2))
 | 
						|
		{
 | 
						|
			*type = static_cast<statenum_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetSkinFromString(const char *word, INT32 *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_Get/SetThingProperty.
 | 
						|
		Gets a skin from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The skin string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetSkinFromString(const char *word, INT32 *type)
 | 
						|
{
 | 
						|
	INT32 skin = R_SkinAvailable(word);
 | 
						|
	if (skin == -1)
 | 
						|
		return false;
 | 
						|
 | 
						|
	*type = skin;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_GetColorFromString(const char *word, skincolornum_t *type)
 | 
						|
 | 
						|
		Helper function for CallFunc_Get/SetThingProperty.
 | 
						|
		Gets a color from a string.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		word: The color string.
 | 
						|
		type: Variable to store the result in.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_GetColorFromString(const char *word, skincolornum_t *type)
 | 
						|
{
 | 
						|
	for (int i = 0; i < numskincolors; i++)
 | 
						|
	{
 | 
						|
		if (fastcmp(word, skincolors[i].name))
 | 
						|
		{
 | 
						|
			*type = static_cast<skincolornum_t>(i);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type)
 | 
						|
 | 
						|
		Helper function for CallFunc_ThingCount.
 | 
						|
		Returns whenever or not to add this thing
 | 
						|
		to the thing count.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		mobj: The mobj we want to count.
 | 
						|
		type: Type exclusion.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if successful, otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type)
 | 
						|
{
 | 
						|
	if (type == MT_NULL || mobj->type == type)
 | 
						|
	{
 | 
						|
		// Don't count dead monsters
 | 
						|
		if (mobj->info->spawnhealth > 0 && mobj->health <= 0)
 | 
						|
		{
 | 
						|
			// Note: Hexen checks for COUNTKILL.
 | 
						|
			// SRB2 does not have an equivalent, so I'm checking
 | 
						|
			// spawnhealth. Feel free to replace this condition
 | 
						|
			// with literally anything else.
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		// Count this object.
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread)
 | 
						|
 | 
						|
		Helper function for many print functions.
 | 
						|
		Returns whenever or not the activator of the
 | 
						|
		thread is a display player or not.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		thread: The thread we're exeucting on.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true if it's for a display player,
 | 
						|
		otherwise false.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		return P_IsDisplayPlayer(info->mo->player);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static UINT32 ACS_SectorThingCounter(sector_t *sec, mtag_t thingTag, bool (*filter)(mobj_t *))
 | 
						|
 | 
						|
		Helper function for CallFunc_CountEnemies
 | 
						|
		and CallFunc_CountPushables. Counts a number
 | 
						|
		of things in the specified sector.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		sec: The sector to search in.
 | 
						|
		thingTag: Thing tag to filter for. 0 allows any.
 | 
						|
		filter: Filter function, total count is increased when
 | 
						|
			this function returns true.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		Numbers of things matching the filter found.
 | 
						|
--------------------------------------------------*/
 | 
						|
static UINT32 ACS_SectorThingCounter(sector_t *sec, mtag_t thingTag, bool (*filter)(mobj_t *))
 | 
						|
{
 | 
						|
	UINT32 count = 0;
 | 
						|
 | 
						|
	for (msecnode_t *node = sec->touching_thinglist; node; node = node->m_thinglist_next) // things touching this sector
 | 
						|
	{
 | 
						|
		mobj_t *mo = node->m_thing;
 | 
						|
 | 
						|
		if (thingTag != 0 && mo->tid != thingTag)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (mo->z > sec->ceilingheight
 | 
						|
			|| mo->z + mo->height < sec->floorheight)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (filter(mo) == true)
 | 
						|
		{
 | 
						|
			count++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static UINT32 ACS_SectorTagThingCounter(mtag_t sectorTag, sector_t *activator, mtag_t thingTag, bool (*filter)(mobj_t *))
 | 
						|
 | 
						|
		Helper function for CallFunc_CountEnemies
 | 
						|
		and CallFunc_CountPushables. Counts a number
 | 
						|
		of things in the tagged sectors.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		sectorTag: The sector tag to search in.
 | 
						|
		activator: The activator sector to fall back on when sectorTag is 0.
 | 
						|
		thingTag: Thing tag to filter for. 0 allows any.
 | 
						|
		filter: Filter function, total count is increased when
 | 
						|
			this function returns true.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		Numbers of things matching the filter found.
 | 
						|
--------------------------------------------------*/
 | 
						|
static UINT32 ACS_SectorIterateThingCounter(sector_t *sec, mtag_t thingTag, bool (*filter)(mobj_t *))
 | 
						|
{
 | 
						|
	UINT32 count = 0;
 | 
						|
	boolean FOFsector = false;
 | 
						|
	size_t i;
 | 
						|
 | 
						|
	if (sec == nullptr)
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	// Check the lines of this sector, to see if it is a FOF control sector.
 | 
						|
	for (i = 0; i < sec->linecount; i++)
 | 
						|
	{
 | 
						|
		INT32 targetsecnum = -1;
 | 
						|
 | 
						|
		if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		FOFsector = true;
 | 
						|
 | 
						|
		TAG_ITER_SECTORS(sec->lines[i]->args[0], targetsecnum)
 | 
						|
		{
 | 
						|
			sector_t *targetsec = §ors[targetsecnum];
 | 
						|
			count += ACS_SectorThingCounter(targetsec, thingTag, filter);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (FOFsector == false)
 | 
						|
	{
 | 
						|
		count += ACS_SectorThingCounter(sec, thingTag, filter);
 | 
						|
	}
 | 
						|
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
static UINT32 ACS_SectorTagThingCounter(mtag_t sectorTag, sector_t *activator, mtag_t thingTag, bool (*filter)(mobj_t *))
 | 
						|
{
 | 
						|
	UINT32 count = 0;
 | 
						|
 | 
						|
	if (sectorTag == 0)
 | 
						|
	{
 | 
						|
		count += ACS_SectorIterateThingCounter(activator, thingTag, filter);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		INT32 secnum = -1;
 | 
						|
 | 
						|
		TAG_ITER_SECTORS(sectorTag, secnum)
 | 
						|
		{
 | 
						|
			sector_t *sec = §ors[secnum];
 | 
						|
			count += ACS_SectorIterateThingCounter(sec, thingTag, filter);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		ACS wrapper for P_RandomRange.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	INT32 low = 0;
 | 
						|
	INT32 high = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	low = argV[0];
 | 
						|
	high = argV[1];
 | 
						|
 | 
						|
	thread->dataStk.push(P_RandomRange(PR_ACS, low, high));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Counts the number of things of a particular
 | 
						|
		type and tid. Both fields are optional;
 | 
						|
		no type means indescriminate against type,
 | 
						|
		no tid means search thru all thinkers.
 | 
						|
--------------------------------------------------*/
 | 
						|
static mobjtype_t filter_for_mobjtype = MT_NULL; // annoying but I don't wanna mess with other code
 | 
						|
bool ACS_ThingTypeFilter(mobj_t *mo)
 | 
						|
{
 | 
						|
	return (ACS_CountThing(mo, filter_for_mobjtype));
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
	ACSVM::String *str = NULL;
 | 
						|
	const char *className = NULL;
 | 
						|
	size_t classLen = 0;
 | 
						|
 | 
						|
	mobjtype_t type = MT_NULL;
 | 
						|
	mtag_t tid = 0;
 | 
						|
	mtag_t sectorTag = 0;
 | 
						|
 | 
						|
	size_t count = 0;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[0]);
 | 
						|
 | 
						|
	className = str->str;
 | 
						|
	classLen = str->len;
 | 
						|
 | 
						|
	if (classLen > 0)
 | 
						|
	{
 | 
						|
		bool success = ACS_GetMobjTypeFromString(className, &type);
 | 
						|
 | 
						|
		if (success == false)
 | 
						|
		{
 | 
						|
			// Exit early.
 | 
						|
 | 
						|
			CONS_Alert(CONS_WARNING,
 | 
						|
				"Couldn't find object type \"%s\" for ThingCount.\n",
 | 
						|
				className
 | 
						|
			);
 | 
						|
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	tid = argV[1];
 | 
						|
 | 
						|
	if (argC > 2)
 | 
						|
	{
 | 
						|
		sectorTag = argV[2];
 | 
						|
	}
 | 
						|
 | 
						|
	if (sectorTag != 0)
 | 
						|
	{
 | 
						|
		// Search through sectors.
 | 
						|
		filter_for_mobjtype = type;
 | 
						|
		count = ACS_SectorTagThingCounter(sectorTag, nullptr, tid, ACS_ThingTypeFilter);
 | 
						|
		filter_for_mobjtype = MT_NULL;
 | 
						|
	}
 | 
						|
	else if (tid != 0)
 | 
						|
	{
 | 
						|
		// Search through tag lists.
 | 
						|
		mobj_t *mobj = nullptr;
 | 
						|
 | 
						|
		while ((mobj = P_FindMobjFromTID(tid, mobj, nullptr)) != nullptr)
 | 
						|
		{
 | 
						|
			if (ACS_CountThing(mobj, type) == true)
 | 
						|
			{
 | 
						|
				++count;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		// Search thinkers instead of tag lists.
 | 
						|
		thinker_t *th = nullptr;
 | 
						|
		mobj_t *mobj = nullptr;
 | 
						|
 | 
						|
		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 | 
						|
		{
 | 
						|
			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
 | 
						|
			{
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			mobj = (mobj_t *)th;
 | 
						|
 | 
						|
			if (ACS_CountThing(mobj, type) == true)
 | 
						|
			{
 | 
						|
				++count;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(count);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pauses the thread until the tagged
 | 
						|
		sector stops moving.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->state = {
 | 
						|
		ACSVM::ThreadState::WaitTag,
 | 
						|
		argV[0],
 | 
						|
		ACS_TAGTYPE_SECTOR
 | 
						|
	};
 | 
						|
 | 
						|
	return true; // Execution interrupted
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pauses the thread until the tagged
 | 
						|
		polyobject stops moving.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->state = {
 | 
						|
		ACSVM::ThreadState::WaitTag,
 | 
						|
		argV[0],
 | 
						|
		ACS_TAGTYPE_POLYOBJ
 | 
						|
	};
 | 
						|
 | 
						|
	return true; // Execution interrupted
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pauses the thread until the tagged
 | 
						|
		camera is done moving.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->state = {
 | 
						|
		ACSVM::ThreadState::WaitTag,
 | 
						|
		argV[0],
 | 
						|
		ACS_TAGTYPE_CAMERA
 | 
						|
	};
 | 
						|
 | 
						|
	return true; // Execution interrupted
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool Dialogue_ValidCheck(ACSVM::Thread *thread)
 | 
						|
 | 
						|
		Helper to check if the thread's dialogue
 | 
						|
		context is valid, initialising if not set.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool Dialogue_ValidCheck(ACSVM::Thread *thread)
 | 
						|
{
 | 
						|
	// TODO when we move away from g_dialogue
 | 
						|
	if (netgame)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	if (!info->dialogue_era)
 | 
						|
		info->dialogue_era = g_dialogue.GetNewEra();
 | 
						|
 | 
						|
	return g_dialogue.EraIsValid(info->dialogue_era);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueWaitDismiss(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pauses the thread until the current
 | 
						|
		dialogue box is dismissed.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueWaitDismiss(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	g_dialogue.SetDismissable(true);
 | 
						|
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	thread->state = {
 | 
						|
		ACSVM::ThreadState::WaitTag,
 | 
						|
		info->dialogue_era,
 | 
						|
		ACS_TAGTYPE_DIALOGUE
 | 
						|
	};
 | 
						|
 | 
						|
	return true; // Execution interrupted
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueWaitText(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pauses the thread until the current
 | 
						|
		dialogue box finishes rendering its text.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueWaitText(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	g_dialogue.SetDismissable(false);
 | 
						|
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	thread->state = {
 | 
						|
		ACSVM::ThreadState::WaitTag,
 | 
						|
		info->dialogue_era,
 | 
						|
		ACS_TAGTYPE_DIALOGUE
 | 
						|
	};
 | 
						|
 | 
						|
	return true; // Execution interrupted
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Changes a floor texture.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
	ACSVM::String *str = nullptr;
 | 
						|
	const char *texName = nullptr;
 | 
						|
 | 
						|
	INT32 secnum = -1;
 | 
						|
	mtag_t tag = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[1]);
 | 
						|
	texName = str->str;
 | 
						|
 | 
						|
	TAG_ITER_SECTORS(tag, secnum)
 | 
						|
	{
 | 
						|
		sector_t *sec = §ors[secnum];
 | 
						|
		sec->floorpic = P_AddLevelFlatRuntime(texName);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Changes a ceiling texture.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
	ACSVM::String *str = NULL;
 | 
						|
	const char *texName = NULL;
 | 
						|
 | 
						|
	INT32 secnum = -1;
 | 
						|
	mtag_t tag = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[1]);
 | 
						|
	texName = str->str;
 | 
						|
 | 
						|
	TAG_ITER_SECTORS(tag, secnum)
 | 
						|
	{
 | 
						|
		sector_t *sec = §ors[secnum];
 | 
						|
		sec->ceilingpic = P_AddLevelFlatRuntime(texName);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes which side of the linedef was
 | 
						|
		activated.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(info->side);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ClearLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		If there is an activating linedef, set its
 | 
						|
		special to 0.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ClearLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (info->line != NULL)
 | 
						|
	{
 | 
						|
		// One time only.
 | 
						|
		info->line->special = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		One of the ACS wrappers for CEcho. This
 | 
						|
		version only prints if the activator is a
 | 
						|
		display player.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	auto& info = static_cast<Thread*>(thread)->info;
 | 
						|
 | 
						|
	if (P_MobjWasRemoved(info.mo) == false && info.mo->player != nullptr)
 | 
						|
		HU_DoTitlecardCEcho(info.mo->player, thread->printBuf.data(), true);
 | 
						|
 | 
						|
	thread->printBuf.drop();
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes the number of players to ACS.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	UINT8 numPlayers = 0;
 | 
						|
	UINT8 i;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	for (i = 0; i < MAXPLAYERS; i++)
 | 
						|
	{
 | 
						|
		player_t *player = NULL;
 | 
						|
 | 
						|
		if (playeringame[i] == false)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		player = &players[i];
 | 
						|
 | 
						|
		if (player->spectator == true)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		numPlayers++;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(numPlayers);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_GameType(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes the current gametype to ACS.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GameType(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(gametype);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_GameSpeed(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes the current game speed to ACS.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GameSpeed(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(gamespeed);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Timer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes leveltime to ACS.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_Timer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(leveltime);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_IsNetworkGame(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Pushes netgame status to ACS.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_IsNetworkGame(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(netgame);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Plays a point sound effect from a sector.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
	ACSVM::String *str = nullptr;
 | 
						|
 | 
						|
	const char *sfxName = nullptr;
 | 
						|
	size_t sfxLen = 0;
 | 
						|
 | 
						|
	sfxenum_t sfxId = sfx_None;
 | 
						|
	INT32 vol = 0;
 | 
						|
	mobj_t *origin = nullptr;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[0]);
 | 
						|
 | 
						|
	sfxName = str->str;
 | 
						|
	sfxLen = str->len;
 | 
						|
 | 
						|
	if (sfxLen > 0)
 | 
						|
	{
 | 
						|
		bool success = ACS_GetSFXFromString(sfxName, &sfxId);
 | 
						|
 | 
						|
		if (success == false)
 | 
						|
		{
 | 
						|
			// Exit early.
 | 
						|
 | 
						|
			CONS_Alert(CONS_WARNING,
 | 
						|
				"Couldn't find sfx named \"%s\" for SectorSound.\n",
 | 
						|
				sfxName
 | 
						|
			);
 | 
						|
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	vol = argV[1];
 | 
						|
 | 
						|
	if (info->sector != nullptr)
 | 
						|
	{
 | 
						|
		// New to Ring Racers: Use activating sector directly.
 | 
						|
		origin = static_cast<mobj_t *>(static_cast<void *>(&info->sector->soundorg));
 | 
						|
	}
 | 
						|
	else if (info->line != nullptr)
 | 
						|
	{
 | 
						|
		// Original Hexen behavior: Use line's frontsector.
 | 
						|
		origin = static_cast<mobj_t *>(static_cast<void *>(&info->line->frontsector->soundorg));
 | 
						|
	}
 | 
						|
 | 
						|
	S_StartSoundAtVolume(origin, sfxId, vol);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Plays a sound effect globally.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
	ACSVM::String *str = nullptr;
 | 
						|
 | 
						|
	const char *sfxName = nullptr;
 | 
						|
	size_t sfxLen = 0;
 | 
						|
 | 
						|
	sfxenum_t sfxId = sfx_None;
 | 
						|
	INT32 vol = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[0]);
 | 
						|
 | 
						|
	sfxName = str->str;
 | 
						|
	sfxLen = str->len;
 | 
						|
 | 
						|
	if (sfxLen > 0)
 | 
						|
	{
 | 
						|
		bool success = ACS_GetSFXFromString(sfxName, &sfxId);
 | 
						|
 | 
						|
		if (success == false)
 | 
						|
		{
 | 
						|
			// Exit early.
 | 
						|
 | 
						|
			CONS_Alert(CONS_WARNING,
 | 
						|
				"Couldn't find sfx named \"%s\" for AmbientSound.\n",
 | 
						|
				sfxName
 | 
						|
			);
 | 
						|
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	vol = argV[1];
 | 
						|
 | 
						|
	S_StartSoundAtVolume(NULL, sfxId, vol);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Plays a sound effect globally.
 | 
						|
--------------------------------------------------*/
 | 
						|
enum
 | 
						|
{
 | 
						|
	SLT_POS_TOP,
 | 
						|
	SLT_POS_MIDDLE,
 | 
						|
	SLT_POS_BOTTOM
 | 
						|
};
 | 
						|
 | 
						|
bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	mtag_t tag = 0;
 | 
						|
	UINT8 sideId = 0;
 | 
						|
	UINT8 texPos = 0;
 | 
						|
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
	ACSVM::String *str = NULL;
 | 
						|
	const char *texName = NULL;
 | 
						|
	INT32 texId = LUMPERROR;
 | 
						|
 | 
						|
	INT32 lineId = -1;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	sideId = (argV[1] & 1);
 | 
						|
	texPos = argV[2];
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[3]);
 | 
						|
	texName = str->str;
 | 
						|
 | 
						|
	texId = R_TextureNumForName(texName);
 | 
						|
 | 
						|
	TAG_ITER_LINES(tag, lineId)
 | 
						|
	{
 | 
						|
		line_t *line = &lines[lineId];
 | 
						|
		side_t *side = NULL;
 | 
						|
 | 
						|
		if (line->sidenum[sideId] != 0xffff)
 | 
						|
		{
 | 
						|
			side = &sides[line->sidenum[sideId]];
 | 
						|
		}
 | 
						|
 | 
						|
		if (side == NULL)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		switch (texPos)
 | 
						|
		{
 | 
						|
			case SLT_POS_MIDDLE:
 | 
						|
			{
 | 
						|
				side->midtexture = texId;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case SLT_POS_BOTTOM:
 | 
						|
			{
 | 
						|
				side->bottomtexture = texId;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case SLT_POS_TOP:
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				side->toptexture = texId;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Changes a linedef's special and arguments.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	INT32 spec = 0;
 | 
						|
	size_t numArgs = 0;
 | 
						|
 | 
						|
	INT32 lineId = -1;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	spec = argV[1];
 | 
						|
 | 
						|
	numArgs = std::min(std::max((signed)(argC - 2), 0), NUM_SCRIPT_ARGS);
 | 
						|
 | 
						|
	TAG_ITER_LINES(tag, lineId)
 | 
						|
	{
 | 
						|
		line_t *line = &lines[lineId];
 | 
						|
		size_t i;
 | 
						|
 | 
						|
		if (info->line != nullptr && line == info->line)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		line->special = spec;
 | 
						|
 | 
						|
		for (i = 0; i < numArgs; i++)
 | 
						|
		{
 | 
						|
			line->args[i] = argV[i + 2];
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Plays a sound effect for a tagged object.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
	ACSVM::String *str = nullptr;
 | 
						|
 | 
						|
	const char *sfxName = nullptr;
 | 
						|
	size_t sfxLen = 0;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	sfxenum_t sfxId = sfx_None;
 | 
						|
	INT32 vol = 0;
 | 
						|
 | 
						|
	mobj_t *mobj = nullptr;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
	str = map->getString(argV[1]);
 | 
						|
 | 
						|
	sfxName = str->str;
 | 
						|
	sfxLen = str->len;
 | 
						|
 | 
						|
	if (sfxLen > 0)
 | 
						|
	{
 | 
						|
		bool success = ACS_GetSFXFromString(sfxName, &sfxId);
 | 
						|
 | 
						|
		if (success == false)
 | 
						|
		{
 | 
						|
			// Exit early.
 | 
						|
 | 
						|
			CONS_Alert(CONS_WARNING,
 | 
						|
				"Couldn't find sfx named \"%s\" for AmbientSound.\n",
 | 
						|
				sfxName
 | 
						|
			);
 | 
						|
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	vol = argV[2];
 | 
						|
 | 
						|
	while ((mobj = P_FindMobjFromTID(tag, mobj, info->mo)) != nullptr)
 | 
						|
	{
 | 
						|
		S_StartSoundAtVolume(mobj, sfxId, vol);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		One of the ACS wrappers for CEcho. This
 | 
						|
		version prints for all players.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	HU_DoTitlecardCEcho(nullptr, thread->printBuf.data(), true);
 | 
						|
 | 
						|
	thread->printBuf.drop();
 | 
						|
	return false;
 | 
						|
}
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerTeam(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's team ID.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerTeam(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	UINT8 teamID = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		teamID = info->mo->player->ctfteam;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(teamID);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerRings(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's ring count.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerRings(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	SINT8 rings = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		rings = (gametyperules & GTR_SPHERES)
 | 
						|
			? info->mo->player->spheres
 | 
						|
			: info->mo->player->rings;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(rings);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerScore(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's roundscore.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerScore(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	UINT32 score = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		score = info->mo->player->roundscore;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(score);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerNumber(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's ID.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerNumber(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	INT16 playerID = -1;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		playerID = (info->mo->player - players);
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(playerID);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ActivatorTID(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating object's TID.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ActivatorTID(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	INT16 tid = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false))
 | 
						|
	{
 | 
						|
		tid = info->mo->tid;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(tid);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_EndLog(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		One of the ACS wrappers for CONS_Printf.
 | 
						|
		This version only prints if the activator
 | 
						|
		is a display player.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_EndLog(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	CONS_Printf("%s\n", thread->printBuf.data());
 | 
						|
	thread->printBuf.drop();
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_strcmp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		ACS wrapper for strcmp.
 | 
						|
--------------------------------------------------*/
 | 
						|
static int ACS_strcmp(ACSVM::String *a, ACSVM::String *b)
 | 
						|
{
 | 
						|
	for (char const *sA = a->str, *sB = b->str; ; ++sA, ++sB)
 | 
						|
	{
 | 
						|
		char cA = *sA, cB = *sB;
 | 
						|
 | 
						|
		if (cA != cB)
 | 
						|
		{
 | 
						|
			return (cA < cB) ? -1 : 1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!cA)
 | 
						|
		{
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_strcmp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
 | 
						|
	ACSVM::String *strA = nullptr;
 | 
						|
	ACSVM::String *strB = nullptr;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	strA = map->getString(argV[0]);
 | 
						|
	strB = map->getString(argV[1]);
 | 
						|
 | 
						|
	thread->dataStk.push(ACS_strcmp(strA, strB));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_strcasecmp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		ACS wrapper for strcasecmp / stricmp.
 | 
						|
--------------------------------------------------*/
 | 
						|
static int ACS_strcasecmp(ACSVM::String *a, ACSVM::String *b)
 | 
						|
{
 | 
						|
	for (char const *sA = a->str, *sB = b->str; ; ++sA, ++sB)
 | 
						|
	{
 | 
						|
		char cA = std::tolower(*sA), cB = std::tolower(*sB);
 | 
						|
 | 
						|
		if (cA != cB)
 | 
						|
		{
 | 
						|
			return (cA < cB) ? -1 : 1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!cA)
 | 
						|
		{
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_strcasecmp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
 | 
						|
	ACSVM::String *strA = nullptr;
 | 
						|
	ACSVM::String *strB = nullptr;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	strA = map->getString(argV[0]);
 | 
						|
	strB = map->getString(argV[1]);
 | 
						|
 | 
						|
	thread->dataStk.push(ACS_strcasecmp(strA, strB));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_CountEnemies(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the number of enemies in the tagged sectors.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool ACS_EnemyFilter(mobj_t *mo)
 | 
						|
{
 | 
						|
	return ((mo->flags & (MF_ENEMY|MF_BOSS)) && mo->health > 0);
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_CountEnemies(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	mtag_t tid = 0;
 | 
						|
	UINT32 count = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	tid = argV[1];
 | 
						|
	count = ACS_SectorTagThingCounter(tag, info->sector, tid, ACS_EnemyFilter);
 | 
						|
 | 
						|
	thread->dataStk.push(count);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_CountPushables(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the number of pushables in the tagged sectors.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool ACS_PushableFilter(mobj_t *mo)
 | 
						|
{
 | 
						|
	return ((mo->flags & MF_PUSHABLE)
 | 
						|
		|| ((mo->info->flags & MF_PUSHABLE) && mo->fuse));
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_CountPushables(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	mtag_t tid = 0;
 | 
						|
	UINT32 count = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	tid = argV[1];
 | 
						|
	count = ACS_SectorTagThingCounter(tag, info->sector, tid, ACS_PushableFilter);
 | 
						|
 | 
						|
	thread->dataStk.push(count);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if an unlockable trigger has been gotten.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	UINT8 id = 0;
 | 
						|
	bool unlocked = false;
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	id = argV[0];
 | 
						|
 | 
						|
	if (id < 0 || id > 31) // limited by 32 bit variable
 | 
						|
	{
 | 
						|
		CONS_Printf("Bad unlockable trigger ID %d\n", id);
 | 
						|
	}
 | 
						|
	else if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		unlocked = (info->mo->player->roundconditions.unlocktriggers & (1 << id));
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(unlocked);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if an unlockable has been gotten.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	UINT32 id = 0;
 | 
						|
	bool unlocked = false;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	id = argV[0];
 | 
						|
 | 
						|
	if (id < 0 || id >= MAXUNLOCKABLES)
 | 
						|
	{
 | 
						|
		CONS_Printf("Bad unlockable ID %d\n", id);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		unlocked = M_CheckNetUnlockByID(id);
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(unlocked);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's skin name.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		UINT8 skin = info->mo->player->skin;
 | 
						|
		thread->dataStk.push(~env->getString( skins[skin].name )->idx);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(0);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's bot status.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
 | 
						|
		thread->dataStk.push(info->mo->player->bot);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(false);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's losing status.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		thread->dataStk.push(K_IsPlayerLosing(info->mo->player));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(false);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's exiting status.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		thread->dataStk.push((info->mo->player->exiting != 0));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(false);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating object's current dye.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	UINT16 dye = SKINCOLOR_NONE;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false))
 | 
						|
	{
 | 
						|
		dye = (info->mo->player != NULL) ? info->mo->player->dye : info->mo->color;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(~env->getString( skincolors[dye].name )->idx);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's number of Chaos Emeralds.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	UINT8 count = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		count = K_NumEmeralds(info->mo->player);
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(count);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the activating player's current lap.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	UINT8 laps = 0;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		laps = info->mo->player->laps;
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(laps);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the lowest lap of all of the players in-game.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(P_FindLowestLap());
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if the map is in Encore Mode.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(encoremode);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PrisonBreak(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if the map is in Prison Break mode.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PrisonBreak(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(battleprisons);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_TimeAttack(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if the map is a Time Attack session.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_TimeAttack(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push((modeattacking != ATTACKING_NONE));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_FreePlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if the map is in Free Play.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_FreePlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push((M_NotFreePlay() == false));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if a Grand Prix is active.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push(grandprixinfo.gp);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PositionStart(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns if the map is in POSITION!!
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PositionStart(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	thread->dataStk.push((starttime != 0 && leveltime < starttime));
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the level's associated Spray Can, if grabbed.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (netgame == false // cans are per-player and completely unsyncable
 | 
						|
	&& gamemap-1 < basenummapheaders)
 | 
						|
	{
 | 
						|
		// See also P_SprayCanInit
 | 
						|
		UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan;
 | 
						|
 | 
						|
		if (can_id < gamedata->numspraycans)
 | 
						|
		{
 | 
						|
			UINT16 col = gamedata->spraycans[can_id].col;
 | 
						|
 | 
						|
			thread->dataStk.push(~env->getString( skincolors[col].name )->idx);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if (gamedata->gotspraycans >= gamedata->numspraycans)
 | 
						|
		{
 | 
						|
			thread->dataStk.push(~env->getString( "_Completed" )->idx);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(0);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_CheckTutorialChallenge(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the Tutorial Challenge status, if possible.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_CheckTutorialChallenge(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (netgame == false) // behaviour is not particularly sync-friendly
 | 
						|
	{
 | 
						|
		if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
 | 
						|
		{
 | 
						|
			thread->dataStk.push(~env->getString( "Active" )->idx);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if (tutorialchallenge == TUTORIALSKIP_FAILED)
 | 
						|
		{
 | 
						|
			thread->dataStk.push(~env->getString( "Failed" )->idx);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if (gamedata != nullptr
 | 
						|
		&& gamedata->enteredtutorialchallenge == true
 | 
						|
		&& M_GameTrulyStarted() == false)
 | 
						|
		{
 | 
						|
			thread->dataStk.push(~env->getString( "Locked" )->idx);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(0);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Returns the best position of all non-CPU players.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	UINT8 ret = MAXPLAYERS;
 | 
						|
	INT32 i;
 | 
						|
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	for (i = 0; i < MAXPLAYERS; i++)
 | 
						|
	{
 | 
						|
		player_t *player = NULL;
 | 
						|
 | 
						|
		if (playeringame[i] == false)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		player = &players[i];
 | 
						|
 | 
						|
		if (player->spectator == true)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (player->bot == true)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = std::min(ret, player->position);
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(ret);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Ends the podium sequence. Doesn't do anything
 | 
						|
		outside of podium maps.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	K_FinishCeremony();
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Changes a linedef's blend mode and alpha.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	mtag_t tag = 0;
 | 
						|
	patchalphastyle_t blend = AST_COPY;
 | 
						|
	fixed_t alpha = FRACUNIT;
 | 
						|
 | 
						|
	INT32 lineId = -1;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	switch (argV[1])
 | 
						|
	{
 | 
						|
		case TMB_TRANSLUCENT:
 | 
						|
		default:
 | 
						|
			blend = AST_COPY;
 | 
						|
			break;
 | 
						|
		case TMB_ADD:
 | 
						|
			blend = AST_ADD;
 | 
						|
			break;
 | 
						|
		case TMB_SUBTRACT:
 | 
						|
			blend = AST_SUBTRACT;
 | 
						|
			break;
 | 
						|
		case TMB_REVERSESUBTRACT:
 | 
						|
			blend = AST_REVERSESUBTRACT;
 | 
						|
			break;
 | 
						|
		case TMB_MODULATE:
 | 
						|
			blend = AST_MODULATE;
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	alpha = argV[2];
 | 
						|
	alpha = std::clamp(alpha, 0, FRACUNIT);
 | 
						|
 | 
						|
	TAG_ITER_LINES(tag, lineId)
 | 
						|
	{
 | 
						|
		line_t *line = &lines[lineId];
 | 
						|
 | 
						|
		line->blendmode = blend;
 | 
						|
		line->alpha = alpha;
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Immediately warps to another level.
 | 
						|
--------------------------------------------------*/
 | 
						|
 | 
						|
bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
 | 
						|
	ACSVM::String *str = nullptr;
 | 
						|
 | 
						|
	const char *levelName = NULL;
 | 
						|
	size_t levelLen = 0;
 | 
						|
 | 
						|
	UINT16 nextmap = NEXTMAP_INVALID;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (exitcountdown == 1)
 | 
						|
	{
 | 
						|
		// An exit is already in progress.
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	str = map->getString(argV[0]);
 | 
						|
 | 
						|
	levelName = str->str;
 | 
						|
	levelLen = str->len;
 | 
						|
 | 
						|
	if (!levelLen || !levelName)
 | 
						|
	{
 | 
						|
		CONS_Alert(CONS_WARNING, "MapWarp level name was not provided.\n");
 | 
						|
	}
 | 
						|
 | 
						|
	nextmap = G_MapNumber(levelName);
 | 
						|
 | 
						|
	if (nextmap == NEXTMAP_INVALID)
 | 
						|
	{
 | 
						|
		CONS_Alert(CONS_WARNING, "MapWarp level %s is not valid or loaded.\n", levelName);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	nextmapoverride = (nextmap + 1);
 | 
						|
 | 
						|
	if (argV[1] == 0)
 | 
						|
		skipstats = 1;
 | 
						|
 | 
						|
	G_BeginLevelExit();
 | 
						|
	exitcountdown = 1;
 | 
						|
 | 
						|
	if (server)
 | 
						|
		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Inserts a bot, if there's room for them.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = NULL;
 | 
						|
 | 
						|
	ACSVM::String *skinStr = nullptr;
 | 
						|
	INT32 skin = -1;
 | 
						|
 | 
						|
	UINT8 difficulty = 0;
 | 
						|
	botStyle_e style = BOT_STYLE_NORMAL;
 | 
						|
 | 
						|
	UINT8 newplayernum = 0;
 | 
						|
	bool success = false;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	skinStr = map->getString(argV[0]);
 | 
						|
	if (skinStr->len != 0)
 | 
						|
	{
 | 
						|
		skin = R_SkinAvailable(skinStr->str);
 | 
						|
	}
 | 
						|
 | 
						|
	if (skin == -1)
 | 
						|
	{
 | 
						|
		skin = R_BotDefaultSkin();
 | 
						|
	}
 | 
						|
 | 
						|
	difficulty = std::clamp(static_cast<int>(argV[1]), 1, MAXBOTDIFFICULTY);
 | 
						|
 | 
						|
	style = static_cast<botStyle_e>(argV[2]);
 | 
						|
	if (style < BOT_STYLE_NORMAL || style >= BOT_STYLE__MAX)
 | 
						|
	{
 | 
						|
		style = BOT_STYLE_NORMAL;
 | 
						|
	}
 | 
						|
 | 
						|
	success = K_AddBot(skin, difficulty, style, &newplayernum);
 | 
						|
	thread->dataStk.push(success ? newplayernum : -1);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueSetSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Set the dialogue speaker to a skin.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueSetSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *skinStr = nullptr;
 | 
						|
	const char *skinName = nullptr;
 | 
						|
 | 
						|
	int spriteFrame = 0;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	skinStr = map->getString(argV[0]);
 | 
						|
	skinName = skinStr->str;
 | 
						|
 | 
						|
	spriteFrame = argV[1];
 | 
						|
 | 
						|
	g_dialogue.SetSpeaker(skinName, spriteFrame);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueSetCustomSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Set the dialogue speaker to specific graphics.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueSetCustomSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *nametagStr = nullptr;
 | 
						|
	const char *nametag = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *patchStr = nullptr;
 | 
						|
	const char *patchName = nullptr;
 | 
						|
	patch_t *patch = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *colorStr = nullptr;
 | 
						|
	const char *colorName = nullptr;
 | 
						|
	skincolornum_t colorID = SKINCOLOR_NONE;
 | 
						|
	UINT8 *colormap = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *voiceStr = nullptr;
 | 
						|
	const char *voiceName = nullptr;
 | 
						|
	sfxenum_t voiceID = sfx_ktalk;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	nametagStr = map->getString(argV[0]);
 | 
						|
	nametag = nametagStr->str;
 | 
						|
 | 
						|
	patchStr = map->getString(argV[1]);
 | 
						|
	patchName = patchStr->str;
 | 
						|
 | 
						|
	colorStr = map->getString(argV[2]);
 | 
						|
	colorName = colorStr->str;
 | 
						|
 | 
						|
	if (patchName && patchName[0])
 | 
						|
	{
 | 
						|
		patch = static_cast<patch_t *>( W_CachePatchName(patchName, PU_CACHE) );
 | 
						|
 | 
						|
		if (ACS_GetColorFromString(colorName, &colorID) == true)
 | 
						|
		{
 | 
						|
			colormap = R_GetTranslationColormap(TC_DEFAULT, colorID, GTC_CACHE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	voiceStr = map->getString(argV[3]);
 | 
						|
	voiceName = voiceStr->str;
 | 
						|
	ACS_GetSFXFromString(voiceName, &voiceID);
 | 
						|
 | 
						|
	g_dialogue.SetSpeaker(nametag, patch, colormap, voiceID);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Halts the level exit if it's happening.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	exitcountdown = 0;
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Exits the level.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (exitcountdown == 1)
 | 
						|
	{
 | 
						|
		// An exit is already in progress.
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (argC >= 1)
 | 
						|
	{
 | 
						|
		skipstats = (argV[0] == 0);
 | 
						|
	}
 | 
						|
 | 
						|
	G_BeginLevelExit();
 | 
						|
	exitcountdown = 1;
 | 
						|
 | 
						|
	if (server)
 | 
						|
		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueNewText(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Change the current dialogue text.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueNewText(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = nullptr;
 | 
						|
 | 
						|
	ACSVM::String *textStr = nullptr;
 | 
						|
	const char *text = nullptr;
 | 
						|
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	map = thread->scopeMap;
 | 
						|
 | 
						|
	textStr = map->getString(argV[0]);
 | 
						|
	text = textStr->str;
 | 
						|
 | 
						|
	g_dialogue.NewText(text);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_DialogueAutoDismiss(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Dismiss the current dialogue text.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_DialogueAutoDismiss(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	if (Dialogue_ValidCheck(thread) == false)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	g_dialogue.Dismiss();
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Play a tune. If it's already playing, restart from the
 | 
						|
		beginning.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = thread->scopeMap;
 | 
						|
 | 
						|
	// 0: str tune - id for the tune to play
 | 
						|
	// 1: [bool foractivator] - only do this if the activator is a player and is being viewed
 | 
						|
 | 
						|
	if (argC > 1 && argV[1] && !ACS_ActivatorIsLocal(thread))
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	Music_Play(map->getString(argV[0])->str);
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Stop every tune that is currently playing.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	// 0: [bool foractivator] - only do this if the activator is a player and is being viewed
 | 
						|
 | 
						|
	if (argC > 0 && argV[0] && !ACS_ActivatorIsLocal(thread))
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	Music_StopAll();
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Change the actual song lump that a tune will play.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = thread->scopeMap;
 | 
						|
 | 
						|
	// 0: str tune - id for the tune to play
 | 
						|
	// 1: str song - lump name for the song to map to
 | 
						|
	// 2: [bool foractivator] - only do this if the activator is a player and is being viewed
 | 
						|
 | 
						|
	if (argC > 2 && argV[2] && !ACS_ActivatorIsLocal(thread))
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	Music_Remap(map->getString(argV[0])->str, map->getString(argV[1])->str);
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_MusicDim(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Fade level music into or out of silence.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_MusicDim(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	// 0: int fade time (tics) - time to fade between full volume and silence
 | 
						|
	// 1: [int duration (tics)] - silent duration (not including fade in and fade out), -1 = infinite (default if omitted)
 | 
						|
 | 
						|
	// If a dim is ongoing, do not interrupt it
 | 
						|
	if (g_musicfade.start < leveltime && g_musicfade.end < leveltime)
 | 
						|
	{
 | 
						|
		g_musicfade.start = leveltime;
 | 
						|
	}
 | 
						|
 | 
						|
	tic_t fade = argV[0];
 | 
						|
	tic_t duration = INFTICS;
 | 
						|
 | 
						|
	if (argC > 1 && argV[1] >= 0)
 | 
						|
	{
 | 
						|
		duration = argV[1];
 | 
						|
	}
 | 
						|
 | 
						|
	g_musicfade.end = duration != INFTICS ? leveltime + duration + 2*fade : INFTICS;
 | 
						|
	g_musicfade.fade = fade;
 | 
						|
	g_musicfade.ticked = false;
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Freeze(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Updates level freeze.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_Freeze(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	(void)argV;
 | 
						|
	(void)argC;
 | 
						|
 | 
						|
	P_SetFreezeLevel(argV[0] != 0);
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Generic line property management.
 | 
						|
--------------------------------------------------*/
 | 
						|
enum
 | 
						|
{
 | 
						|
	LINE_PROP_FLAGS,
 | 
						|
	LINE_PROP_ALPHA,
 | 
						|
	LINE_PROP_BLENDMODE,
 | 
						|
	LINE_PROP_ACTIVATION,
 | 
						|
	LINE_PROP_ACTION,
 | 
						|
	LINE_PROP_ARG0,
 | 
						|
	LINE_PROP_ARG1,
 | 
						|
	LINE_PROP_ARG2,
 | 
						|
	LINE_PROP_ARG3,
 | 
						|
	LINE_PROP_ARG4,
 | 
						|
	LINE_PROP_ARG5,
 | 
						|
	LINE_PROP_ARG6,
 | 
						|
	LINE_PROP_ARG7,
 | 
						|
	LINE_PROP_ARG8,
 | 
						|
	LINE_PROP_ARG9,
 | 
						|
	LINE_PROP_ARG0STR,
 | 
						|
	LINE_PROP_ARG1STR,
 | 
						|
	LINE_PROP__MAX
 | 
						|
};
 | 
						|
 | 
						|
static INT32 NextLine(mtag_t tag, size_t *iterate, INT32 activatorID)
 | 
						|
{
 | 
						|
	size_t i = *iterate;
 | 
						|
	*iterate = *iterate + 1;
 | 
						|
 | 
						|
	if (tag == 0)
 | 
						|
	{
 | 
						|
		// 0 grabs the activator.
 | 
						|
 | 
						|
		if (i != 0)
 | 
						|
		{
 | 
						|
			// Don't do more than once.
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		return activatorID;
 | 
						|
	}
 | 
						|
 | 
						|
	return Tag_Iterate_Lines(tag, i);
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	INT32 property = LINE_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
 | 
						|
	if (line != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( line->y ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( line->y )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(LINE_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(LINE_PROP_ALPHA, alpha)
 | 
						|
			PROP_INT(LINE_PROP_BLENDMODE, blendmode)
 | 
						|
			PROP_INT(LINE_PROP_ACTIVATION, activation)
 | 
						|
			PROP_INT(LINE_PROP_ACTION, special)
 | 
						|
			PROP_INT(LINE_PROP_ARG0, args[0])
 | 
						|
			PROP_INT(LINE_PROP_ARG1, args[1])
 | 
						|
			PROP_INT(LINE_PROP_ARG2, args[2])
 | 
						|
			PROP_INT(LINE_PROP_ARG3, args[3])
 | 
						|
			PROP_INT(LINE_PROP_ARG4, args[4])
 | 
						|
			PROP_INT(LINE_PROP_ARG5, args[5])
 | 
						|
			PROP_INT(LINE_PROP_ARG6, args[6])
 | 
						|
			PROP_INT(LINE_PROP_ARG7, args[7])
 | 
						|
			PROP_INT(LINE_PROP_ARG8, args[8])
 | 
						|
			PROP_INT(LINE_PROP_ARG9, args[9])
 | 
						|
			PROP_STR(LINE_PROP_ARG0STR, stringargs[0])
 | 
						|
			PROP_STR(LINE_PROP_ARG1STR, stringargs[1])
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "GetLineProperty type %d out of range (expected 0 - %d).\n", property, LINE_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(value);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	//Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	INT32 property = LINE_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
	value = argV[2];
 | 
						|
 | 
						|
	while (line != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_READONLY(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		CONS_Alert(CONS_WARNING, "SetLineProperty type '%s' cannot be written to.\n", "y"); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		line->y = static_cast< decltype(line->y) >(value); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		ACSVM::String *str = thread->scopeMap->getString( value ); \
 | 
						|
		if (str->len == 0) \
 | 
						|
		{ \
 | 
						|
			Z_Free(line->y); \
 | 
						|
			line->y = NULL; \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			line->y = static_cast<char *>(Z_Realloc(line->y, str->len + 1, PU_LEVEL, NULL)); \
 | 
						|
			M_Memcpy(line->y, str->str, str->len + 1); \
 | 
						|
			line->y[str->len] = '\0'; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(LINE_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(LINE_PROP_ALPHA, alpha)
 | 
						|
			PROP_INT(LINE_PROP_BLENDMODE, blendmode)
 | 
						|
			PROP_INT(LINE_PROP_ACTIVATION, activation)
 | 
						|
			PROP_INT(LINE_PROP_ACTION, special)
 | 
						|
			PROP_INT(LINE_PROP_ARG0, args[0])
 | 
						|
			PROP_INT(LINE_PROP_ARG1, args[1])
 | 
						|
			PROP_INT(LINE_PROP_ARG2, args[2])
 | 
						|
			PROP_INT(LINE_PROP_ARG3, args[3])
 | 
						|
			PROP_INT(LINE_PROP_ARG4, args[4])
 | 
						|
			PROP_INT(LINE_PROP_ARG5, args[5])
 | 
						|
			PROP_INT(LINE_PROP_ARG6, args[6])
 | 
						|
			PROP_INT(LINE_PROP_ARG7, args[7])
 | 
						|
			PROP_INT(LINE_PROP_ARG8, args[8])
 | 
						|
			PROP_INT(LINE_PROP_ARG9, args[9])
 | 
						|
			PROP_STR(LINE_PROP_ARG0STR, stringargs[0])
 | 
						|
			PROP_STR(LINE_PROP_ARG1STR, stringargs[1])
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "SetLineProperty type %d out of range (expected 0 - %d).\n", property, LINE_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
		{
 | 
						|
			line = &lines[ lineID ];
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			line = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
#undef PROP_READONLY
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Get/SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Generic side property management.
 | 
						|
--------------------------------------------------*/
 | 
						|
enum
 | 
						|
{
 | 
						|
	SIDE_FRONT = 0,
 | 
						|
	SIDE_BACK = 1,
 | 
						|
	SIDE_BOTH,
 | 
						|
};
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
	SIDE_PROP_XOFFSET,
 | 
						|
	SIDE_PROP_YOFFSET,
 | 
						|
	SIDE_PROP_TOPTEXTURE,
 | 
						|
	SIDE_PROP_BOTTOMTEXTURE,
 | 
						|
	SIDE_PROP_MIDTEXTURE,
 | 
						|
	SIDE_PROP_REPEATCOUNT,
 | 
						|
	SIDE_PROP__MAX
 | 
						|
};
 | 
						|
 | 
						|
bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	UINT8 sideID = 0;
 | 
						|
	side_t *side = NULL;
 | 
						|
 | 
						|
	INT32 property = SIDE_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	sideID = argV[1];
 | 
						|
	switch (sideID)
 | 
						|
	{
 | 
						|
		default: // Activator
 | 
						|
		case SIDE_BOTH: // Wouldn't make sense for this function.
 | 
						|
		{
 | 
						|
			sideID = info->side;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		case SIDE_FRONT:
 | 
						|
		case SIDE_BACK:
 | 
						|
		{
 | 
						|
			// Keep sideID as is.
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (line != NULL && line->sidenum[sideID] != 0xffff)
 | 
						|
	{
 | 
						|
		side = &sides[line->sidenum[sideID]];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[2];
 | 
						|
 | 
						|
	if (side != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( side->y ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( side->y )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_TEXTURE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( textures[ side->y ]->name )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(SIDE_PROP_XOFFSET, textureoffset)
 | 
						|
			PROP_INT(SIDE_PROP_YOFFSET, rowoffset)
 | 
						|
			PROP_TEXTURE(SIDE_PROP_TOPTEXTURE, toptexture)
 | 
						|
			PROP_TEXTURE(SIDE_PROP_BOTTOMTEXTURE, bottomtexture)
 | 
						|
			PROP_TEXTURE(SIDE_PROP_MIDTEXTURE, midtexture)
 | 
						|
			PROP_INT(SIDE_PROP_REPEATCOUNT, repeatcnt)
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "GetSideProperty type %d out of range (expected 0 - %d).\n", property, SIDE_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_TEXTURE
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(value);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	//Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	UINT8 sideID = 0;
 | 
						|
	side_t *side = NULL;
 | 
						|
	boolean tryBoth = false;
 | 
						|
 | 
						|
	INT32 property = SIDE_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	sideID = argV[1];
 | 
						|
	switch (sideID)
 | 
						|
	{
 | 
						|
		default: // Activator
 | 
						|
		{
 | 
						|
			sideID = info->side;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		case SIDE_BOTH:
 | 
						|
		{
 | 
						|
			sideID = SIDE_FRONT;
 | 
						|
			tryBoth = true;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		case SIDE_FRONT:
 | 
						|
		case SIDE_BACK:
 | 
						|
		{
 | 
						|
			// Keep sideID as is.
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (line != NULL && line->sidenum[sideID] != 0xffff)
 | 
						|
	{
 | 
						|
		side = &sides[line->sidenum[sideID]];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[2];
 | 
						|
	value = argV[3];
 | 
						|
 | 
						|
	while (line != NULL)
 | 
						|
	{
 | 
						|
		if (side != NULL)
 | 
						|
		{
 | 
						|
 | 
						|
#define PROP_READONLY(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		CONS_Alert(CONS_WARNING, "SetSideProperty type '%s' cannot be written to.\n", "y"); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		side->y = static_cast< decltype(side->y) >(value); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		ACSVM::String *str = thread->scopeMap->getString( value ); \
 | 
						|
		if (str->len == 0) \
 | 
						|
		{ \
 | 
						|
			Z_Free(side->y); \
 | 
						|
			side->y = NULL; \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			side->y = static_cast<char *>(Z_Realloc(side->y, str->len + 1, PU_LEVEL, NULL)); \
 | 
						|
			M_Memcpy(side->y, str->str, str->len + 1); \
 | 
						|
			side->y[str->len] = '\0'; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_TEXTURE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		side->y = R_TextureNumForName( thread->scopeMap->getString( value )->str ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
			switch (property)
 | 
						|
			{
 | 
						|
				PROP_INT(SIDE_PROP_XOFFSET, textureoffset)
 | 
						|
				PROP_INT(SIDE_PROP_YOFFSET, rowoffset)
 | 
						|
				PROP_TEXTURE(SIDE_PROP_TOPTEXTURE, toptexture)
 | 
						|
				PROP_TEXTURE(SIDE_PROP_BOTTOMTEXTURE, bottomtexture)
 | 
						|
				PROP_TEXTURE(SIDE_PROP_MIDTEXTURE, midtexture)
 | 
						|
				PROP_INT(SIDE_PROP_REPEATCOUNT, repeatcnt)
 | 
						|
				default:
 | 
						|
				{
 | 
						|
					CONS_Alert(CONS_WARNING, "SetSideProperty type %d out of range (expected 0 - %d).\n", property, SIDE_PROP__MAX-1);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (tryBoth == true && sideID == SIDE_FRONT)
 | 
						|
		{
 | 
						|
			sideID = SIDE_BACK;
 | 
						|
 | 
						|
			if (line->sidenum[sideID] != 0xffff)
 | 
						|
			{
 | 
						|
				side = &sides[line->sidenum[sideID]];
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
		{
 | 
						|
			line = &lines[ lineID ];
 | 
						|
 | 
						|
			if (tryBoth == true)
 | 
						|
			{
 | 
						|
				sideID = SIDE_FRONT;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			line = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		if (line != NULL && line->sidenum[sideID] != 0xffff)
 | 
						|
		{
 | 
						|
			side = &sides[line->sidenum[sideID]];
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			side = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_TEXTURE
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
#undef PROP_READONLY
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Get/SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Generic sector property management.
 | 
						|
--------------------------------------------------*/
 | 
						|
enum
 | 
						|
{
 | 
						|
	SECTOR_PROP_FLOORHEIGHT,
 | 
						|
	SECTOR_PROP_CEILINGHEIGHT,
 | 
						|
	SECTOR_PROP_FLOORPIC,
 | 
						|
	SECTOR_PROP_CEILINGPIC,
 | 
						|
	SECTOR_PROP_LIGHTLEVEL,
 | 
						|
	SECTOR_PROP_FLOORLIGHTLEVEL,
 | 
						|
	SECTOR_PROP_CEILINGLIGHTLEVEL,
 | 
						|
	SECTOR_PROP_FLOORLIGHTABSOLUTE,
 | 
						|
	SECTOR_PROP_CEILINGLIGHTABSOLUTE,
 | 
						|
	SECTOR_PROP_FLAGS,
 | 
						|
	SECTOR_PROP_SPECIALFLAGS,
 | 
						|
	SECTOR_PROP_GRAVITY,
 | 
						|
	SECTOR_PROP_ACTIVATION,
 | 
						|
	SECTOR_PROP_ACTION,
 | 
						|
	SECTOR_PROP_ARG0,
 | 
						|
	SECTOR_PROP_ARG1,
 | 
						|
	SECTOR_PROP_ARG2,
 | 
						|
	SECTOR_PROP_ARG3,
 | 
						|
	SECTOR_PROP_ARG4,
 | 
						|
	SECTOR_PROP_ARG5,
 | 
						|
	SECTOR_PROP_ARG6,
 | 
						|
	SECTOR_PROP_ARG7,
 | 
						|
	SECTOR_PROP_ARG8,
 | 
						|
	SECTOR_PROP_ARG9,
 | 
						|
	SECTOR_PROP_ARG0STR,
 | 
						|
	SECTOR_PROP_ARG1STR,
 | 
						|
	SECTOR_PROP__MAX
 | 
						|
};
 | 
						|
 | 
						|
static INT32 NextSector(mtag_t tag, size_t *iterate, INT32 activatorID)
 | 
						|
{
 | 
						|
	size_t i = *iterate;
 | 
						|
	*iterate = *iterate + 1;
 | 
						|
 | 
						|
	if (tag == 0)
 | 
						|
	{
 | 
						|
		// 0 grabs the activator.
 | 
						|
 | 
						|
		if (i != 0)
 | 
						|
		{
 | 
						|
			// Don't do more than once.
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		return activatorID;
 | 
						|
	}
 | 
						|
 | 
						|
	return Tag_Iterate_Sectors(tag, i);
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_GetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 sectorID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	sector_t *sector = NULL;
 | 
						|
 | 
						|
	INT32 property = SECTOR_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->sector != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->sector - sectors;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		sector = §ors[ sectorID ];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
 | 
						|
	if (sector != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( sector->y ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( sector->y )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_FLAT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( levelflats[ sector->y ].name )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORHEIGHT, floorheight)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGHEIGHT, ceilingheight)
 | 
						|
			PROP_FLAT(SECTOR_PROP_FLOORPIC, floorpic)
 | 
						|
			PROP_FLAT(SECTOR_PROP_CEILINGPIC, ceilingpic)
 | 
						|
			PROP_INT(SECTOR_PROP_LIGHTLEVEL, lightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORLIGHTLEVEL, floorlightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGLIGHTLEVEL, ceilinglightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORLIGHTABSOLUTE, floorlightabsolute)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGLIGHTABSOLUTE, ceilinglightabsolute)
 | 
						|
			PROP_INT(SECTOR_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(SECTOR_PROP_SPECIALFLAGS, specialflags)
 | 
						|
			PROP_INT(SECTOR_PROP_GRAVITY, gravity)
 | 
						|
			PROP_INT(SECTOR_PROP_ACTIVATION, activation)
 | 
						|
			PROP_INT(SECTOR_PROP_ACTION, action)
 | 
						|
			PROP_INT(SECTOR_PROP_ARG0, args[0])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG1, args[1])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG2, args[2])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG3, args[3])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG4, args[4])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG5, args[5])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG6, args[6])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG7, args[7])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG8, args[8])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG9, args[9])
 | 
						|
			PROP_STR(SECTOR_PROP_ARG0STR, stringargs[0])
 | 
						|
			PROP_STR(SECTOR_PROP_ARG1STR, stringargs[1])
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "GetSectorProperty type %d out of range (expected 0 - %d).\n", property, SECTOR_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_FLAT
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(value);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	//Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 sectorID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	sector_t *sector = NULL;
 | 
						|
 | 
						|
	INT32 property = SECTOR_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
 | 
						|
	if (info != NULL && info->sector != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->sector - sectors;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		sector = §ors[ sectorID ];
 | 
						|
	}
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
	value = argV[2];
 | 
						|
 | 
						|
	while (sector != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_READONLY(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		CONS_Alert(CONS_WARNING, "SetSectorProperty type '%s' cannot be written to.\n", "y"); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		sector->y = static_cast< decltype(sector->y) >(value); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		ACSVM::String *str = thread->scopeMap->getString( value ); \
 | 
						|
		if (str->len == 0) \
 | 
						|
		{ \
 | 
						|
			Z_Free(sector->y); \
 | 
						|
			sector->y = NULL; \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			sector->y = static_cast<char *>(Z_Realloc(sector->y, str->len + 1, PU_LEVEL, NULL)); \
 | 
						|
			M_Memcpy(sector->y, str->str, str->len + 1); \
 | 
						|
			sector->y[str->len] = '\0'; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_FLAT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		sector->y = P_AddLevelFlatRuntime( thread->scopeMap->getString( value )->str ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORHEIGHT, floorheight)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGHEIGHT, ceilingheight)
 | 
						|
			PROP_FLAT(SECTOR_PROP_FLOORPIC, floorpic)
 | 
						|
			PROP_FLAT(SECTOR_PROP_CEILINGPIC, ceilingpic)
 | 
						|
			PROP_INT(SECTOR_PROP_LIGHTLEVEL, lightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORLIGHTLEVEL, floorlightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGLIGHTLEVEL, ceilinglightlevel)
 | 
						|
			PROP_INT(SECTOR_PROP_FLOORLIGHTABSOLUTE, floorlightabsolute)
 | 
						|
			PROP_INT(SECTOR_PROP_CEILINGLIGHTABSOLUTE, ceilinglightabsolute)
 | 
						|
			PROP_INT(SECTOR_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(SECTOR_PROP_SPECIALFLAGS, specialflags)
 | 
						|
			PROP_INT(SECTOR_PROP_GRAVITY, gravity)
 | 
						|
			PROP_INT(SECTOR_PROP_ACTIVATION, activation)
 | 
						|
			PROP_INT(SECTOR_PROP_ACTION, action)
 | 
						|
			PROP_INT(SECTOR_PROP_ARG0, args[0])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG1, args[1])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG2, args[2])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG3, args[3])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG4, args[4])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG5, args[5])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG6, args[6])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG7, args[7])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG8, args[8])
 | 
						|
			PROP_INT(SECTOR_PROP_ARG9, args[9])
 | 
						|
			PROP_STR(SECTOR_PROP_ARG0STR, stringargs[0])
 | 
						|
			PROP_STR(SECTOR_PROP_ARG1STR, stringargs[1])
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "SetSectorProperty type %d out of range (expected 0 - %d).\n", property, SECTOR_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1)
 | 
						|
		{
 | 
						|
			sector = §ors[ sectorID ];
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			sector = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_FLAT
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
#undef PROP_READONLY
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Get/SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		Generic thing property management.
 | 
						|
--------------------------------------------------*/
 | 
						|
enum
 | 
						|
{
 | 
						|
	THING_PROP_X,
 | 
						|
	THING_PROP_Y,
 | 
						|
	THING_PROP_Z,
 | 
						|
	THING_PROP_TYPE,
 | 
						|
	THING_PROP_ANGLE,
 | 
						|
	THING_PROP_PITCH,
 | 
						|
	THING_PROP_ROLL,
 | 
						|
	THING_PROP_SPRITEROLL,
 | 
						|
	THING_PROP_FRAME,
 | 
						|
	THING_PROP_SPRITE,
 | 
						|
	THING_PROP_SPRITE2,
 | 
						|
	THING_PROP_RENDERFLAGS,
 | 
						|
	THING_PROP_SPRITEXSCALE,
 | 
						|
	THING_PROP_SPRITEYSCALE,
 | 
						|
	THING_PROP_SPRITEXOFFSET,
 | 
						|
	THING_PROP_SPRITEYOFFSET,
 | 
						|
	THING_PROP_FLOORZ,
 | 
						|
	THING_PROP_CEILINGZ,
 | 
						|
	THING_PROP_RADIUS,
 | 
						|
	THING_PROP_HEIGHT,
 | 
						|
	THING_PROP_MOMX,
 | 
						|
	THING_PROP_MOMY,
 | 
						|
	THING_PROP_MOMZ,
 | 
						|
	THING_PROP_TICS,
 | 
						|
	THING_PROP_STATE,
 | 
						|
	THING_PROP_FLAGS,
 | 
						|
	THING_PROP_FLAGS2,
 | 
						|
	THING_PROP_EFLAGS,
 | 
						|
	THING_PROP_SKIN,
 | 
						|
	THING_PROP_COLOR,
 | 
						|
	THING_PROP_HEALTH,
 | 
						|
	THING_PROP_MOVEDIR,
 | 
						|
	THING_PROP_MOVECOUNT,
 | 
						|
	THING_PROP_REACTIONTIME,
 | 
						|
	THING_PROP_THRESHOLD,
 | 
						|
	THING_PROP_LASTLOOK,
 | 
						|
	THING_PROP_FRICTION,
 | 
						|
	THING_PROP_MOVEFACTOR,
 | 
						|
	THING_PROP_FUSE,
 | 
						|
	THING_PROP_WATERTOP,
 | 
						|
	THING_PROP_WATERBOTTOM,
 | 
						|
	THING_PROP_SCALE,
 | 
						|
	THING_PROP_DESTSCALE,
 | 
						|
	THING_PROP_SCALESPEED,
 | 
						|
	THING_PROP_EXTRAVALUE1,
 | 
						|
	THING_PROP_EXTRAVALUE2,
 | 
						|
	THING_PROP_CUSVAL,
 | 
						|
	THING_PROP_CVMEM,
 | 
						|
	THING_PROP_COLORIZED,
 | 
						|
	THING_PROP_MIRRORED,
 | 
						|
	THING_PROP_SHADOWSCALE,
 | 
						|
	THING_PROP_WHITESHADOW,
 | 
						|
	THING_PROP_WORLDXOFFSET,
 | 
						|
	THING_PROP_WORLDYOFFSET,
 | 
						|
	THING_PROP_WORLDZOFFSET,
 | 
						|
	THING_PROP_HITLAG,
 | 
						|
	THING_PROP_WATERSKIP,
 | 
						|
	THING_PROP_DISPOFFSET,
 | 
						|
	THING_PROP_TARGET,
 | 
						|
	THING_PROP_TRACER,
 | 
						|
	THING_PROP_HNEXT,
 | 
						|
	THING_PROP_HPREV,
 | 
						|
	THING_PROP_ITNEXT,
 | 
						|
	THING_PROP__MAX
 | 
						|
};
 | 
						|
 | 
						|
bool CallFunc_GetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	mobj_t *mobj = NULL;
 | 
						|
 | 
						|
	INT32 property = SECTOR_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	mobj = P_FindMobjFromTID(tag, mobj, info->mo);
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
 | 
						|
	if (mobj != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( mobj->y ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( mobj->y )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_ANGLE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( AngleFixed( mobj->y ) ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_TYPE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		if (mobj->y >= MT_FIRSTFREESLOT) \
 | 
						|
		{ \
 | 
						|
			std::string	prefix = "MT_"; \
 | 
						|
			std::string	full = prefix + FREE_MOBJS[mobj->y - MT_FIRSTFREESLOT]; \
 | 
						|
			value = static_cast<INT32>( ~env->getString( full.c_str() )->idx ); \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			value = static_cast<INT32>( ~env->getString( MOBJTYPE_LIST[ mobj->y ] )->idx ); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SPR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		char crunched[5] = {0}; \
 | 
						|
		strncpy(crunched, sprnames[ mobj->y ], 4); \
 | 
						|
		value = static_cast<INT32>( ~env->getString( crunched )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SPR2(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( spr2names[ mobj->y ] )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STATE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		statenum_t stateID = static_cast<statenum_t>(mobj->y - states); \
 | 
						|
		if (stateID >= S_FIRSTFREESLOT) \
 | 
						|
		{ \
 | 
						|
			std::string	prefix = "S_"; \
 | 
						|
			std::string	full = prefix + FREE_STATES[stateID - S_FIRSTFREESLOT]; \
 | 
						|
			value = static_cast<INT32>( ~env->getString( full.c_str() )->idx ); \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			value = static_cast<INT32>( ~env->getString( STATE_LIST[ stateID ] )->idx ); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SKIN(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		if (mobj->y != NULL) \
 | 
						|
		{ \
 | 
						|
			skin_t *skin = static_cast<skin_t *>(mobj->y); \
 | 
						|
			value = static_cast<INT32>( ~env->getString( skin->name )->idx ); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_COLOR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		value = static_cast<INT32>( ~env->getString( skincolors[ mobj->y ].name )->idx ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_MOBJ(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		if (P_MobjWasRemoved(mobj->y) == false) \
 | 
						|
		{ \
 | 
						|
			value = static_cast<INT32>( mobj->y->tid ); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_INT(THING_PROP_X, x)
 | 
						|
			PROP_INT(THING_PROP_Y, y)
 | 
						|
			PROP_INT(THING_PROP_Z, z)
 | 
						|
			PROP_TYPE(THING_PROP_TYPE, type)
 | 
						|
			PROP_ANGLE(THING_PROP_ANGLE, angle)
 | 
						|
			PROP_ANGLE(THING_PROP_PITCH, pitch)
 | 
						|
			PROP_ANGLE(THING_PROP_ROLL, roll)
 | 
						|
			PROP_ANGLE(THING_PROP_SPRITEROLL, rollangle)
 | 
						|
			PROP_INT(THING_PROP_FRAME, frame)
 | 
						|
			PROP_SPR(THING_PROP_SPRITE, sprite)
 | 
						|
			PROP_SPR2(THING_PROP_SPRITE2, sprite2)
 | 
						|
			PROP_INT(THING_PROP_RENDERFLAGS, renderflags)
 | 
						|
			PROP_INT(THING_PROP_SPRITEXSCALE, spritexscale)
 | 
						|
			PROP_INT(THING_PROP_SPRITEYSCALE, spriteyscale)
 | 
						|
			PROP_INT(THING_PROP_SPRITEXOFFSET, spritexoffset)
 | 
						|
			PROP_INT(THING_PROP_SPRITEYOFFSET, spriteyoffset)
 | 
						|
			PROP_INT(THING_PROP_FLOORZ, floorz)
 | 
						|
			PROP_INT(THING_PROP_CEILINGZ, ceilingz)
 | 
						|
			PROP_INT(THING_PROP_RADIUS, radius)
 | 
						|
			PROP_INT(THING_PROP_HEIGHT, height)
 | 
						|
			PROP_INT(THING_PROP_MOMX, momx)
 | 
						|
			PROP_INT(THING_PROP_MOMY, momy)
 | 
						|
			PROP_INT(THING_PROP_MOMZ, momz)
 | 
						|
			PROP_INT(THING_PROP_TICS, tics)
 | 
						|
			PROP_STATE(THING_PROP_STATE, state)
 | 
						|
			PROP_INT(THING_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(THING_PROP_FLAGS2, flags2)
 | 
						|
			PROP_INT(THING_PROP_EFLAGS, eflags)
 | 
						|
			PROP_SKIN(THING_PROP_SKIN, skin)
 | 
						|
			PROP_COLOR(THING_PROP_COLOR, color)
 | 
						|
			PROP_INT(THING_PROP_HEALTH, health)
 | 
						|
			PROP_INT(THING_PROP_MOVEDIR, movedir)
 | 
						|
			PROP_INT(THING_PROP_MOVECOUNT, movecount)
 | 
						|
			PROP_INT(THING_PROP_REACTIONTIME, reactiontime)
 | 
						|
			PROP_INT(THING_PROP_THRESHOLD, threshold)
 | 
						|
			PROP_INT(THING_PROP_LASTLOOK, lastlook)
 | 
						|
			PROP_INT(THING_PROP_FRICTION, friction)
 | 
						|
			PROP_INT(THING_PROP_MOVEFACTOR, movefactor)
 | 
						|
			PROP_INT(THING_PROP_FUSE, fuse)
 | 
						|
			PROP_INT(THING_PROP_WATERTOP, watertop)
 | 
						|
			PROP_INT(THING_PROP_WATERBOTTOM, waterbottom)
 | 
						|
			PROP_INT(THING_PROP_SCALE, scale)
 | 
						|
			PROP_INT(THING_PROP_DESTSCALE, destscale)
 | 
						|
			PROP_INT(THING_PROP_SCALESPEED, scalespeed)
 | 
						|
			PROP_INT(THING_PROP_EXTRAVALUE1, extravalue1)
 | 
						|
			PROP_INT(THING_PROP_EXTRAVALUE2, extravalue2)
 | 
						|
			PROP_INT(THING_PROP_CUSVAL, cusval)
 | 
						|
			PROP_INT(THING_PROP_CVMEM, cvmem)
 | 
						|
			PROP_INT(THING_PROP_COLORIZED, colorized)
 | 
						|
			PROP_INT(THING_PROP_MIRRORED, mirrored)
 | 
						|
			PROP_INT(THING_PROP_SHADOWSCALE, shadowscale)
 | 
						|
			PROP_INT(THING_PROP_WHITESHADOW, whiteshadow)
 | 
						|
			PROP_INT(THING_PROP_WORLDXOFFSET, sprxoff)
 | 
						|
			PROP_INT(THING_PROP_WORLDYOFFSET, spryoff)
 | 
						|
			PROP_INT(THING_PROP_WORLDZOFFSET, sprzoff)
 | 
						|
			PROP_INT(THING_PROP_HITLAG, hitlag)
 | 
						|
			PROP_INT(THING_PROP_WATERSKIP, waterskip)
 | 
						|
			PROP_INT(THING_PROP_DISPOFFSET, dispoffset)
 | 
						|
			PROP_MOBJ(THING_PROP_TARGET, target)
 | 
						|
			PROP_MOBJ(THING_PROP_TRACER, tracer)
 | 
						|
			PROP_MOBJ(THING_PROP_HNEXT, hnext)
 | 
						|
			PROP_MOBJ(THING_PROP_HPREV, hprev)
 | 
						|
			PROP_MOBJ(THING_PROP_ITNEXT, itnext)
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "GetThingProperty type %d out of range (expected 0 - %d).\n", property, THING_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
#undef PROP_MOBJ
 | 
						|
#undef PROP_COLOR
 | 
						|
#undef PROP_SKIN
 | 
						|
#undef PROP_STATE
 | 
						|
#undef PROP_SPR2
 | 
						|
#undef PROP_SPR
 | 
						|
#undef PROP_TYPE
 | 
						|
#undef PROP_ANGLE
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(value);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	//Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	mobj_t *mobj = NULL;
 | 
						|
 | 
						|
	INT32 property = SECTOR_PROP__MAX;
 | 
						|
	INT32 value = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	mobj = P_FindMobjFromTID(tag, mobj, info->mo);
 | 
						|
 | 
						|
	property = argV[1];
 | 
						|
	value = argV[2];
 | 
						|
 | 
						|
	while (mobj != NULL)
 | 
						|
	{
 | 
						|
 | 
						|
#define PROP_READONLY(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		CONS_Alert(CONS_WARNING, "SetThingProperty type '%s' cannot be written to.\n", "y"); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_INT(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		mobj->y = static_cast< decltype(mobj->y) >(value); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		ACSVM::String *str = thread->scopeMap->getString( value ); \
 | 
						|
		if (str->len == 0) \
 | 
						|
		{ \
 | 
						|
			Z_Free(mobj->y); \
 | 
						|
			mobj->y = NULL; \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			mobj->y = static_cast<char *>(Z_Realloc(mobj->y, str->len + 1, PU_LEVEL, NULL)); \
 | 
						|
			M_Memcpy(mobj->y, str->str, str->len + 1); \
 | 
						|
			mobj->y[str->len] = '\0'; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_ANGLE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		mobj->y = static_cast<angle_t>( FixedAngle(value) ); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_TYPE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		if (mobj->player == NULL) \
 | 
						|
		{ \
 | 
						|
			mobjtype_t newType = mobj->y; \
 | 
						|
			bool success = ACS_GetMobjTypeFromString(thread->scopeMap->getString( value )->str, &newType); \
 | 
						|
			if (success == true) \
 | 
						|
			{ \
 | 
						|
				mobj->y = newType; \
 | 
						|
				mobj->info = &mobjinfo[newType]; \
 | 
						|
				P_SetScale(mobj, mobj->scale); \
 | 
						|
			} \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SPR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		spritenum_t newSprite = mobj->y; \
 | 
						|
		bool success = ACS_GetSpriteFromString(thread->scopeMap->getString( value )->str, &newSprite); \
 | 
						|
		if (success == true) \
 | 
						|
		{ \
 | 
						|
			mobj->y = newSprite; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SPR2(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		playersprite_t newSprite2 = static_cast<playersprite_t>(mobj->y); \
 | 
						|
		bool success = ACS_GetSprite2FromString(thread->scopeMap->getString( value )->str, &newSprite2); \
 | 
						|
		if (success == true) \
 | 
						|
		{ \
 | 
						|
			mobj->y = static_cast< decltype(mobj->y) >(newSprite2); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_STATE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		statenum_t newState = static_cast<statenum_t>(mobj->y - states); \
 | 
						|
		bool success = ACS_GetStateFromString(thread->scopeMap->getString( value )->str, &newState); \
 | 
						|
		if (success == true) \
 | 
						|
		{ \
 | 
						|
			if (mobj->player != NULL) \
 | 
						|
			{ \
 | 
						|
				P_SetPlayerMobjState(mobj, newState); \
 | 
						|
			} \
 | 
						|
			else \
 | 
						|
			{ \
 | 
						|
				P_SetMobjState(mobj, newState); \
 | 
						|
			} \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SKIN(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		INT32 newSkin = (mobj->skin != NULL) ? (static_cast<skin_t *>(mobj->skin)) - skins : -1; \
 | 
						|
		bool success = ACS_GetSkinFromString(thread->scopeMap->getString( value )->str, &newSkin); \
 | 
						|
		if (success == true) \
 | 
						|
		{ \
 | 
						|
			mobj->y = (newSkin >= 0 && newSkin < numskins) ? &skins[ newSkin ] : NULL; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_COLOR(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		skincolornum_t newColor = static_cast<skincolornum_t>(mobj->y); \
 | 
						|
		bool success = ACS_GetColorFromString(thread->scopeMap->getString( value )->str, &newColor); \
 | 
						|
		if (success == true) \
 | 
						|
		{ \
 | 
						|
			mobj->y = static_cast< decltype(mobj->y) >(newColor); \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_MOBJ(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		mobj_t *newTarget = P_FindMobjFromTID(value, NULL, NULL); \
 | 
						|
		P_SetTarget(&mobj->y, newTarget); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_SCALE(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		P_SetScale(mobj, value); \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
#define PROP_FLAGS(x, y) \
 | 
						|
	case x: \
 | 
						|
	{ \
 | 
						|
		if ((value & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (mobj->y & (MF_NOBLOCKMAP|MF_NOSECTOR))) \
 | 
						|
		{ \
 | 
						|
			P_UnsetThingPosition(mobj); \
 | 
						|
			mobj->y = value; \
 | 
						|
			if ((value & MF_NOSECTOR) && sector_list) \
 | 
						|
			{ \
 | 
						|
				P_DelSeclist(sector_list); \
 | 
						|
				sector_list = NULL; \
 | 
						|
			} \
 | 
						|
			mobj->snext = NULL, mobj->sprev = NULL; \
 | 
						|
			mobj->bnext = NULL, mobj->bprev = NULL; \
 | 
						|
			P_SetThingPosition(mobj); \
 | 
						|
		} \
 | 
						|
		else \
 | 
						|
		{ \
 | 
						|
			mobj->y = value; \
 | 
						|
		} \
 | 
						|
		break; \
 | 
						|
	}
 | 
						|
 | 
						|
		switch (property)
 | 
						|
		{
 | 
						|
			PROP_READONLY(THING_PROP_X, x)
 | 
						|
			PROP_READONLY(THING_PROP_Y, y)
 | 
						|
			PROP_READONLY(THING_PROP_Z, z)
 | 
						|
			PROP_TYPE(THING_PROP_TYPE, type)
 | 
						|
			PROP_ANGLE(THING_PROP_ANGLE, angle)
 | 
						|
			PROP_ANGLE(THING_PROP_PITCH, pitch)
 | 
						|
			PROP_ANGLE(THING_PROP_ROLL, roll)
 | 
						|
			PROP_ANGLE(THING_PROP_SPRITEROLL, rollangle)
 | 
						|
			PROP_INT(THING_PROP_FRAME, frame)
 | 
						|
			PROP_SPR(THING_PROP_SPRITE, sprite)
 | 
						|
			PROP_SPR2(THING_PROP_SPRITE2, sprite2)
 | 
						|
			PROP_INT(THING_PROP_RENDERFLAGS, renderflags)
 | 
						|
			PROP_INT(THING_PROP_SPRITEXSCALE, spritexscale)
 | 
						|
			PROP_INT(THING_PROP_SPRITEYSCALE, spriteyscale)
 | 
						|
			PROP_INT(THING_PROP_SPRITEXOFFSET, spritexoffset)
 | 
						|
			PROP_INT(THING_PROP_SPRITEYOFFSET, spriteyoffset)
 | 
						|
			PROP_INT(THING_PROP_FLOORZ, floorz)
 | 
						|
			PROP_INT(THING_PROP_CEILINGZ, ceilingz)
 | 
						|
			PROP_READONLY(THING_PROP_RADIUS, radius)
 | 
						|
			PROP_READONLY(THING_PROP_HEIGHT, height)
 | 
						|
			PROP_INT(THING_PROP_MOMX, momx)
 | 
						|
			PROP_INT(THING_PROP_MOMY, momy)
 | 
						|
			PROP_INT(THING_PROP_MOMZ, momz)
 | 
						|
			PROP_INT(THING_PROP_TICS, tics)
 | 
						|
			PROP_STATE(THING_PROP_STATE, state)
 | 
						|
			PROP_FLAGS(THING_PROP_FLAGS, flags)
 | 
						|
			PROP_INT(THING_PROP_FLAGS2, flags2)
 | 
						|
			PROP_INT(THING_PROP_EFLAGS, eflags)
 | 
						|
			PROP_SKIN(THING_PROP_SKIN, skin)
 | 
						|
			PROP_COLOR(THING_PROP_COLOR, color)
 | 
						|
			PROP_INT(THING_PROP_HEALTH, health)
 | 
						|
			PROP_INT(THING_PROP_MOVEDIR, movedir)
 | 
						|
			PROP_INT(THING_PROP_MOVECOUNT, movecount)
 | 
						|
			PROP_INT(THING_PROP_REACTIONTIME, reactiontime)
 | 
						|
			PROP_INT(THING_PROP_THRESHOLD, threshold)
 | 
						|
			PROP_INT(THING_PROP_LASTLOOK, lastlook)
 | 
						|
			PROP_INT(THING_PROP_FRICTION, friction)
 | 
						|
			PROP_INT(THING_PROP_MOVEFACTOR, movefactor)
 | 
						|
			PROP_INT(THING_PROP_FUSE, fuse)
 | 
						|
			PROP_INT(THING_PROP_WATERTOP, watertop)
 | 
						|
			PROP_INT(THING_PROP_WATERBOTTOM, waterbottom)
 | 
						|
			PROP_SCALE(THING_PROP_SCALE, scale)
 | 
						|
			PROP_INT(THING_PROP_DESTSCALE, destscale)
 | 
						|
			PROP_INT(THING_PROP_SCALESPEED, scalespeed)
 | 
						|
			PROP_INT(THING_PROP_EXTRAVALUE1, extravalue1)
 | 
						|
			PROP_INT(THING_PROP_EXTRAVALUE2, extravalue2)
 | 
						|
			PROP_INT(THING_PROP_CUSVAL, cusval)
 | 
						|
			PROP_INT(THING_PROP_CVMEM, cvmem)
 | 
						|
			PROP_INT(THING_PROP_COLORIZED, colorized)
 | 
						|
			PROP_INT(THING_PROP_MIRRORED, mirrored)
 | 
						|
			PROP_INT(THING_PROP_SHADOWSCALE, shadowscale)
 | 
						|
			PROP_INT(THING_PROP_WHITESHADOW, whiteshadow)
 | 
						|
			PROP_INT(THING_PROP_WORLDXOFFSET, sprxoff)
 | 
						|
			PROP_INT(THING_PROP_WORLDYOFFSET, spryoff)
 | 
						|
			PROP_INT(THING_PROP_WORLDZOFFSET, sprzoff)
 | 
						|
			PROP_INT(THING_PROP_HITLAG, hitlag)
 | 
						|
			PROP_INT(THING_PROP_WATERSKIP, waterskip)
 | 
						|
			PROP_INT(THING_PROP_DISPOFFSET, dispoffset)
 | 
						|
			PROP_MOBJ(THING_PROP_TARGET, target)
 | 
						|
			PROP_MOBJ(THING_PROP_TRACER, tracer)
 | 
						|
			PROP_MOBJ(THING_PROP_HNEXT, hnext)
 | 
						|
			PROP_MOBJ(THING_PROP_HPREV, hprev)
 | 
						|
			PROP_MOBJ(THING_PROP_ITNEXT, itnext)
 | 
						|
			default:
 | 
						|
			{
 | 
						|
				CONS_Alert(CONS_WARNING, "SetThingProperty type %d out of range (expected 0 - %d).\n", property, THING_PROP__MAX-1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		mobj = P_FindMobjFromTID(tag, mobj, info->mo);
 | 
						|
 | 
						|
#undef PROP_FLAGS
 | 
						|
#undef PROP_SCALE
 | 
						|
#undef PROP_MOBJ
 | 
						|
#undef PROP_COLOR
 | 
						|
#undef PROP_SKIN
 | 
						|
#undef PROP_STATE
 | 
						|
#undef PROP_SPR2
 | 
						|
#undef PROP_SPR
 | 
						|
#undef PROP_TYPE
 | 
						|
#undef PROP_ANGLE
 | 
						|
#undef PROP_STR
 | 
						|
#undef PROP_INT
 | 
						|
#undef PROP_READONLY
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	bool CallFunc_Get[x]UserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
 | 
						|
		User-defined property management.
 | 
						|
--------------------------------------------------*/
 | 
						|
bool CallFunc_GetLineUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	const char *key = NULL;
 | 
						|
 | 
						|
	mapUserProperty_t *prop = NULL;
 | 
						|
	INT32 ret = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	key = thread->scopeMap->getString(argV[1])->str;
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	if (line != NULL)
 | 
						|
	{
 | 
						|
		prop = K_UserPropertyFind(&line->user, key);
 | 
						|
	}
 | 
						|
 | 
						|
	if (prop != NULL)
 | 
						|
	{
 | 
						|
		switch (prop->type)
 | 
						|
		{
 | 
						|
			case USER_PROP_BOOL:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueBool);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_INT:
 | 
						|
			{
 | 
						|
				ret = prop->valueInt;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_FIXED:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueFixed);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_STR:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>( ~env->getString( prop->valueStr )->idx );
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(ret);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_GetSideUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 lineID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	line_t *line = NULL;
 | 
						|
 | 
						|
	UINT8 sideID = 0;
 | 
						|
	side_t *side = NULL;
 | 
						|
 | 
						|
	const char *key = NULL;
 | 
						|
 | 
						|
	mapUserProperty_t *prop = NULL;
 | 
						|
	INT32 ret = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	sideID = argV[1];
 | 
						|
	key = thread->scopeMap->getString(argV[2])->str;
 | 
						|
 | 
						|
	if (info != NULL && info->line != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->line - lines;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		line = &lines[ lineID ];
 | 
						|
	}
 | 
						|
 | 
						|
	if (sideID < 0 || sideID > 1)
 | 
						|
	{
 | 
						|
		sideID = info->side;
 | 
						|
	}
 | 
						|
 | 
						|
	if (line != NULL && line->sidenum[sideID] != 0xffff)
 | 
						|
	{
 | 
						|
		side = &sides[line->sidenum[sideID]];
 | 
						|
	}
 | 
						|
 | 
						|
	if (side != NULL)
 | 
						|
	{
 | 
						|
		prop = K_UserPropertyFind(&side->user, key);
 | 
						|
	}
 | 
						|
 | 
						|
	if (prop != NULL)
 | 
						|
	{
 | 
						|
		switch (prop->type)
 | 
						|
		{
 | 
						|
			case USER_PROP_BOOL:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueBool);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_INT:
 | 
						|
			{
 | 
						|
				ret = prop->valueInt;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_FIXED:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueFixed);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_STR:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>( ~env->getString( prop->valueStr )->idx );
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(ret);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_GetSectorUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	size_t tagIt = 0;
 | 
						|
 | 
						|
	INT32 sectorID = 0;
 | 
						|
	INT32 activatorID = -1;
 | 
						|
	sector_t *sector = NULL;
 | 
						|
 | 
						|
	const char *key = NULL;
 | 
						|
 | 
						|
	mapUserProperty_t *prop = NULL;
 | 
						|
	INT32 ret = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	key = thread->scopeMap->getString(argV[1])->str;
 | 
						|
 | 
						|
	if (info != NULL && info->sector != NULL)
 | 
						|
	{
 | 
						|
		activatorID = info->sector - sectors;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1)
 | 
						|
	{
 | 
						|
		sector = §ors[ sectorID ];
 | 
						|
	}
 | 
						|
 | 
						|
	if (sector != NULL)
 | 
						|
	{
 | 
						|
		prop = K_UserPropertyFind(§or->user, key);
 | 
						|
	}
 | 
						|
 | 
						|
	if (prop != NULL)
 | 
						|
	{
 | 
						|
		switch (prop->type)
 | 
						|
		{
 | 
						|
			case USER_PROP_BOOL:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueBool);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_INT:
 | 
						|
			{
 | 
						|
				ret = prop->valueInt;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_FIXED:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueFixed);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_STR:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>( ~env->getString( prop->valueStr )->idx );
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(ret);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_GetThingUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
	Environment *env = &ACSEnv;
 | 
						|
 | 
						|
	mtag_t tag = 0;
 | 
						|
	mobj_t *mobj = NULL;
 | 
						|
 | 
						|
	const char *key = NULL;
 | 
						|
 | 
						|
	mapUserProperty_t *prop = NULL;
 | 
						|
	INT32 ret = 0;
 | 
						|
 | 
						|
	tag = argV[0];
 | 
						|
	key = thread->scopeMap->getString(argV[1])->str;
 | 
						|
 | 
						|
	mobj = P_FindMobjFromTID(tag, mobj, info->mo);
 | 
						|
 | 
						|
	if (mobj != NULL && mobj->spawnpoint != NULL)
 | 
						|
	{
 | 
						|
		prop = K_UserPropertyFind(&mobj->spawnpoint->user, key);
 | 
						|
	}
 | 
						|
 | 
						|
	if (prop != NULL)
 | 
						|
	{
 | 
						|
		switch (prop->type)
 | 
						|
		{
 | 
						|
			case USER_PROP_BOOL:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueBool);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_INT:
 | 
						|
			{
 | 
						|
				ret = prop->valueInt;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_FIXED:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>(prop->valueFixed);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			case USER_PROP_STR:
 | 
						|
			{
 | 
						|
				ret = static_cast<INT32>( ~env->getString( prop->valueStr )->idx );
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thread->dataStk.push(ret);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_AddMessage(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = thread->scopeMap;
 | 
						|
 | 
						|
	K_AddMessage(map->getString(argV[0])->str, argV[1], argV[2]);
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_AddMessageForPlayer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	ACSVM::MapScope *map = thread->scopeMap;
 | 
						|
 | 
						|
	auto info = &static_cast<Thread *>(thread)->info;
 | 
						|
 | 
						|
	if ((info != NULL)
 | 
						|
		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		K_AddMessageForPlayer(info->mo->player, map->getString(argV[0])->str, argV[1], argV[2]);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_ClearPersistentMessages(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
 | 
						|
{
 | 
						|
	K_ClearPersistentMessages();
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CallFunc_ClearPersistentMessageForPlayer(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)
 | 
						|
		&& (info->mo->player != NULL))
 | 
						|
	{
 | 
						|
		K_ClearPersistentMessageForPlayer(info->mo->player);
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 |