blankart/src/acs/call-funcs.cpp
Sally Coolatta 1f6574e9a3 Copy first mapthing tag to their mobjs
Allows the ThingCount and ThingSound ACS functions to fully work now, and adds significantly more possibilities for scripting later.
2024-10-15 15:13:50 -04:00

1258 lines
29 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
extern "C" {
#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_battle.h"
}
#include "ACSVM/ACSVM/Code.hpp"
#include "ACSVM/ACSVM/CodeData.hpp"
#include "ACSVM/ACSVM/Environment.hpp"
#include "ACSVM/ACSVM/Error.hpp"
#include "ACSVM/ACSVM/Module.hpp"
#include "ACSVM/ACSVM/Scope.hpp"
#include "ACSVM/ACSVM/Script.hpp"
#include "ACSVM/ACSVM/Serial.hpp"
#include "ACSVM/ACSVM/Thread.hpp"
#include "ACSVM/Util/Floats.hpp"
#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_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, 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.
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, bool (*filter)(mobj_t *))
{
msecnode_t *node = sec->touching_thinglist; // things touching this sector
UINT32 count = 0;
while (node)
{
mobj_t *mo = node->m_thing;
if (mo->z > sec->ceilingheight
|| mo->z + mo->height < sec->floorheight)
{
continue;
}
if (filter(mo) == true)
{
count++;
}
node = node->m_thinglist_next;
}
return count;
}
/*--------------------------------------------------
static UINT32 ACS_SectorTagThingCounter(mtag_t tag, bool (*filter)(mobj_t *))
Helper function for CallFunc_CountEnemies
and CallFunc_CountPushables. Counts a number
of things in the tagged sectors.
Input Arguments:-
tag: The sector tag to search in.
filter: Filter function, total count is increased when
this function returns true.
Return:-
Numbers of things matching the filter found.
--------------------------------------------------*/
static UINT32 ACS_SectorTagThingCounter(mtag_t tag, bool (*filter)(mobj_t *))
{
INT32 secnum = -1;
UINT32 count = 0;
size_t i;
TAG_ITER_SECTORS(tag, secnum)
{
sector_t *sec = &sectors[secnum];
boolean FOFsector = false;
// 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 = &sectors[targetsecnum];
count += ACS_SectorThingCounter(targetsec, filter);
}
}
if (FOFsector == false)
{
count += ACS_SectorThingCounter(sec, 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(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.
--------------------------------------------------*/
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;
size_t count = 0;
(void)argC;
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 (tid != 0)
{
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_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 = &sectors[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 = &sectors[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;
if (ACS_ActivatorIsLocal(thread) == true)
{
HU_SetCEchoDuration(5);
HU_DoCEcho(thread->printBuf.data());
}
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_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[4]);
texName = str->str;
texId = R_TextureNumForName(texName);
TAG_ITER_LINES(tag, lineId)
{
line_t *line = &lines[lineId];
side_t *side = &sides[line->sidenum[sideId]];
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), NUMLINEARGS);
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_SetCEchoDuration(5);
HU_DoCEcho(thread->printBuf.data());
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 = 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 ring count.
--------------------------------------------------*/
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_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;
if (ACS_ActivatorIsLocal(thread) == true)
{
CONS_Printf("%s\n", thread->printBuf.data());
}
thread->printBuf.drop();
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)
{
mtag_t tag = 0;
UINT32 count = 0;
(void)argC;
tag = argV[0];
count = ACS_SectorTagThingCounter(tag, 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)
{
mtag_t tag = 0;
UINT32 count = 0;
(void)argC;
tag = argV[0];
count = ACS_SectorTagThingCounter(tag, 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;
(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
{
unlocked = (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)
{
UINT8 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_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;
}