Fully port the ACS integration to C++

This commit is contained in:
Sally Coolatta 2022-12-23 04:38:05 -05:00
parent 88ffab9f3c
commit 8eef5efa95
20 changed files with 1164 additions and 1269 deletions

View file

@ -130,8 +130,6 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
k_profiles.c k_profiles.c
k_specialstage.c k_specialstage.c
k_roulette.c k_roulette.c
k_acs.c
k_acs-func.c
) )
if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows") if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
@ -515,6 +513,7 @@ endif()
add_subdirectory(sdl) add_subdirectory(sdl)
add_subdirectory(objects) add_subdirectory(objects)
add_subdirectory(acs)
add_subdirectory(tests) add_subdirectory(tests)
# strip debug symbols into separate file when using gcc. # strip debug symbols into separate file when using gcc.

10
src/acs/CMakeLists.txt Normal file
View file

@ -0,0 +1,10 @@
target_sources(SRB2SDL2 PRIVATE
environment.cpp
environment.hpp
thread.cpp
thread.hpp
call-funcs.cpp
call-funcs.hpp
interface.cpp
interface.h
)

View file

@ -8,49 +8,56 @@
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details. // See the 'LICENSE' file for more details.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/// \file k_acs-func.c /// \file call-funcs.cpp
/// \brief ACS CallFunc definitions /// \brief Action Code Script: CallFunc instructions
#include "k_acs.h" extern "C" {
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
#include "doomtype.h" #include "../d_think.h"
#include "doomdef.h" #include "../p_mobj.h"
#include "doomstat.h" #include "../p_tick.h"
#include "d_think.h" #include "../w_wad.h"
#include "p_mobj.h" #include "../m_random.h"
#include "p_tick.h" #include "../g_game.h"
#include "w_wad.h" #include "../d_player.h"
#include "m_random.h" #include "../r_defs.h"
#include "g_game.h" #include "../r_state.h"
#include "d_player.h" #include "../p_polyobj.h"
#include "r_defs.h" #include "../taglist.h"
#include "r_state.h" #include "../p_local.h"
#include "p_polyobj.h" #include "../deh_tables.h"
#include "taglist.h" #include "../fastcmp.h"
#include "p_local.h" #include "../hu_stuff.h"
#include "deh_tables.h" #include "../s_sound.h"
#include "fastcmp.h" #include "../r_textures.h"
#include "hu_stuff.h"
#include "s_sound.h"
#include "r_textures.h"
ACSVM_String *ACSVM_MapScope_GetString(ACSVM_MapScope *map, ACSVM_Word index)
{
(void)map;
(void)index;
return NULL;
} }
ACSVM_ThreadInfo *ACSVM_AllocThreadInfo(void *activator) #include <ACSVM/Code.hpp>
{ #include <ACSVM/CodeData.hpp>
(void)activator; #include <ACSVM/Environment.hpp>
return NULL; #include <ACSVM/Error.hpp>
} #include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <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) static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
Helper function for ACS_CF_ThingCount. Gets Helper function for CallFunc_ThingCount. Gets
an object type from a string. an object type from a string.
Input Arguments:- Input Arguments:-
@ -62,15 +69,13 @@ ACSVM_ThreadInfo *ACSVM_AllocThreadInfo(void *activator)
--------------------------------------------------*/ --------------------------------------------------*/
static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type) static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
{ {
mobjtype_t i;
if (fastncmp("MT_", word, 3)) if (fastncmp("MT_", word, 3))
{ {
// take off the MT_ // take off the MT_
word += 3; word += 3;
} }
for (i = 0; i < NUMMOBJFREESLOTS; i++) for (int i = 0; i < NUMMOBJFREESLOTS; i++)
{ {
if (!FREE_MOBJS[i]) if (!FREE_MOBJS[i])
{ {
@ -79,16 +84,16 @@ static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
if (fastcmp(word, FREE_MOBJS[i])) if (fastcmp(word, FREE_MOBJS[i]))
{ {
*type = MT_FIRSTFREESLOT+i; *type = static_cast<mobjtype_t>(static_cast<int>(MT_FIRSTFREESLOT) + i);
return true; return true;
} }
} }
for (i = 0; i < MT_FIRSTFREESLOT; i++) for (int i = 0; i < MT_FIRSTFREESLOT; i++)
{ {
if (fastcmp(word, MOBJTYPE_LIST[i]+3)) if (fastcmp(word, MOBJTYPE_LIST[i] + 3))
{ {
*type = i; *type = static_cast<mobjtype_t>(i);
return true; return true;
} }
} }
@ -111,8 +116,6 @@ static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type)
--------------------------------------------------*/ --------------------------------------------------*/
static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type) static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type)
{ {
sfxenum_t i;
if (fastncmp("SFX_", word, 4)) if (fastncmp("SFX_", word, 4))
{ {
// take off the SFX_ // take off the SFX_
@ -124,11 +127,11 @@ static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type)
word += 2; word += 2;
} }
for (i = 0; i < NUMSFX; i++) for (int i = 0; i < NUMSFX; i++)
{ {
if (S_sfx[i].name && fasticmp(word, S_sfx[i].name)) if (S_sfx[i].name && fasticmp(word, S_sfx[i].name))
{ {
*type = i; *type = static_cast<sfxenum_t>(i);
return true; return true;
} }
} }
@ -139,7 +142,7 @@ static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type)
/*-------------------------------------------------- /*--------------------------------------------------
static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type) static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type)
Helper function for ACS_CF_ThingCount. Helper function for CallFunc_ThingCount.
Returns whenever or not to add this thing Returns whenever or not to add this thing
to the thing count. to the thing count.
@ -172,7 +175,7 @@ static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type)
} }
/*-------------------------------------------------- /*--------------------------------------------------
static bool ACS_ActivatorIsLocal(ACSVM_Thread *thread) static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread)
Helper function for many print functions. Helper function for many print functions.
Returns whenever or not the activator of the Returns whenever or not the activator of the
@ -185,9 +188,9 @@ static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type)
true if it's for a display player, true if it's for a display player,
otherwise false. otherwise false.
--------------------------------------------------*/ --------------------------------------------------*/
static bool ACS_ActivatorIsLocal(ACSVM_Thread *thread) static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread)
{ {
activator_t *info = (activator_t *)ACSVM_Thread_GetInfo(thread); auto info = &static_cast<Thread *>(thread)->info;
if ((info != NULL) if ((info != NULL)
&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false) && (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
@ -200,11 +203,11 @@ static bool ACS_ActivatorIsLocal(ACSVM_Thread *thread)
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
ACS wrapper for P_RandomRange. ACS wrapper for P_RandomRange.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
INT32 low = 0; INT32 low = 0;
INT32 high = 0; INT32 high = 0;
@ -214,22 +217,22 @@ bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC
low = argV[0]; low = argV[0];
high = argV[1]; high = argV[1];
ACSVM_Thread_DataStk_Push(thread, P_RandomRange(PR_ACS, low, high)); thread->dataStk.push(P_RandomRange(PR_ACS, low, high));
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Counts the number of things of a particular Counts the number of things of a particular
type and tid. Both fields are optional; type and tid. Both fields are optional;
no type means indescriminate against type, no type means indescriminate against type,
no tid means search thru all thinkers. no tid means search thru all thinkers.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = NULL;
ACSVM_String *str = NULL; ACSVM::String *str = NULL;
const char *className = NULL; const char *className = NULL;
size_t classLen = 0; size_t classLen = 0;
@ -240,11 +243,11 @@ bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
(void)argC; (void)argC;
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[0]); str = map->getString(argV[0]);
className = ACSVM_String_GetStr(str); className = str->str;
classLen = ACSVM_String_GetLen(str); classLen = str->len;
if (classLen > 0) if (classLen > 0)
{ {
@ -270,6 +273,9 @@ bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
// TODO: Even in SRB2 next's UDMF, tag lists // TODO: Even in SRB2 next's UDMF, tag lists
// still aren't attached to mobj_t, only // still aren't attached to mobj_t, only
// mapthing_t. Fix this. // mapthing_t. Fix this.
CONS_Alert(CONS_WARNING,
"ThingCount TID field not implemented -- waiting for mobj tags.\n"
);
} }
else else
{ {
@ -293,60 +299,58 @@ bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
} }
} }
ACSVM_Thread_DataStk_Push(thread, count); thread->dataStk.push(count);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_TagWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pauses the thread until the tagged Pauses the thread until the tagged
sector stops moving. sector stops moving.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_TagWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_ThreadState st = {0};
(void)argC; (void)argC;
st.state = ACSVM_ThreadState_WaitTag; thread->state = {
st.data = argV[0]; ACSVM::ThreadState::WaitTag,
st.type = ACS_TAGTYPE_SECTOR; argV[0],
ACS_TAGTYPE_SECTOR
};
ACSVM_Thread_SetState(thread, st);
return true; // Execution interrupted return true; // Execution interrupted
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_PolyWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pauses the thread until the tagged Pauses the thread until the tagged
polyobject stops moving. polyobject stops moving.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_PolyWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_ThreadState st = {0};
(void)argC; (void)argC;
st.state = ACSVM_ThreadState_WaitTag; thread->state = {
st.data = argV[0]; ACSVM::ThreadState::WaitTag,
st.type = ACS_TAGTYPE_POLYOBJ; argV[0],
ACS_TAGTYPE_POLYOBJ
};
ACSVM_Thread_SetState(thread, st);
return true; // Execution interrupted return true; // Execution interrupted
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Changes a floor texture. Changes a floor texture.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = nullptr;
ACSVM_String *str = NULL; ACSVM::String *str = nullptr;
const char *texName = NULL; const char *texName = nullptr;
INT32 secnum = -1; INT32 secnum = -1;
mtag_t tag = 0; mtag_t tag = 0;
@ -355,9 +359,9 @@ bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
tag = argV[0]; tag = argV[0];
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[1]); str = map->getString(argV[1]);
texName = ACSVM_String_GetStr(str); texName = str->str;
TAG_ITER_SECTORS(tag, secnum) TAG_ITER_SECTORS(tag, secnum)
{ {
@ -369,14 +373,14 @@ bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Changes a ceiling texture. Changes a ceiling texture.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = NULL;
ACSVM_String *str = NULL; ACSVM::String *str = NULL;
const char *texName = NULL; const char *texName = NULL;
INT32 secnum = -1; INT32 secnum = -1;
@ -386,9 +390,9 @@ bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Wo
tag = argV[0]; tag = argV[0];
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[1]); str = map->getString(argV[1]);
texName = ACSVM_String_GetStr(str); texName = str->str;
TAG_ITER_SECTORS(tag, secnum) TAG_ITER_SECTORS(tag, secnum)
{ {
@ -400,31 +404,31 @@ bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Wo
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_LineSide(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pushes which side of the linedef was Pushes which side of the linedef was
activated. activated.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_LineSide(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
activator_t *info = (activator_t *)ACSVM_Thread_GetInfo(thread); auto info = &static_cast<Thread *>(thread)->info;
(void)argV; (void)argV;
(void)argC; (void)argC;
ACSVM_Thread_DataStk_Push(thread, info->side); thread->dataStk.push(info->side);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_ClearLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ClearLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
If there is an activating linedef, set its If there is an activating linedef, set its
special to 0. special to 0.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_ClearLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ClearLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
activator_t *info = (activator_t *)ACSVM_Thread_GetInfo(thread); auto info = &static_cast<Thread *>(thread)->info;
(void)argV; (void)argV;
(void)argC; (void)argC;
@ -439,38 +443,33 @@ bool ACS_CF_ClearLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
One of the ACS wrappers for CEcho. This One of the ACS wrappers for CEcho. This
version only prints if the activator is a version only prints if the activator is a
display player. display player.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_PrintBuf *buf = NULL;
(void)argV; (void)argV;
(void)argC; (void)argC;
buf = ACSVM_Thread_GetPrintBuf(thread);
if (ACS_ActivatorIsLocal(thread) == true) if (ACS_ActivatorIsLocal(thread) == true)
{ {
HU_SetCEchoDuration(5); HU_SetCEchoDuration(5);
HU_DoCEcho(ACSVM_PrintBuf_GetData(buf)); HU_DoCEcho(thread->printBuf.data());
} }
ACSVM_PrintBuf_Drop(buf); thread->printBuf.drop();
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_PlayerCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_PlayerCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pushes the number of players to ACS. Pushes the number of players to ACS.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_PlayerCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_PlayerCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
UINT8 numPlayers = 0; UINT8 numPlayers = 0;
UINT8 i; UINT8 i;
@ -497,78 +496,78 @@ bool ACS_CF_PlayerCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
numPlayers++; numPlayers++;
} }
ACSVM_Thread_DataStk_Push(thread, numPlayers); thread->dataStk.push(numPlayers);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_GameType(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_GameType(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pushes the current gametype to ACS. Pushes the current gametype to ACS.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_GameType(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_GameType(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
(void)argV; (void)argV;
(void)argC; (void)argC;
ACSVM_Thread_DataStk_Push(thread, gametype); thread->dataStk.push(gametype);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_GameSpeed(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_GameSpeed(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pushes the current game speed to ACS. Pushes the current game speed to ACS.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_GameSpeed(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_GameSpeed(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
(void)argV; (void)argV;
(void)argC; (void)argC;
ACSVM_Thread_DataStk_Push(thread, gamespeed); thread->dataStk.push(gamespeed);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_Timer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pushes leveltime to ACS. Pushes leveltime to ACS.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_Timer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
(void)argV; (void)argV;
(void)argC; (void)argC;
ACSVM_Thread_DataStk_Push(thread, leveltime); thread->dataStk.push(leveltime);
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_SectorSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Plays a point sound effect from a sector. Plays a point sound effect from a sector.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_SectorSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
activator_t *info = (activator_t *)ACSVM_Thread_GetInfo(thread); auto info = &static_cast<Thread *>(thread)->info;
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = nullptr;
ACSVM_String *str = NULL; ACSVM::String *str = nullptr;
const char *sfxName = NULL; const char *sfxName = nullptr;
size_t sfxLen = 0; size_t sfxLen = 0;
sfxenum_t sfxId = sfx_None; sfxenum_t sfxId = sfx_None;
INT32 vol = 0; INT32 vol = 0;
mobj_t *origin = NULL; mobj_t *origin = nullptr;
(void)argC; (void)argC;
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[0]); str = map->getString(argV[0]);
sfxName = ACSVM_String_GetStr(str); sfxName = str->str;
sfxLen = ACSVM_String_GetLen(str); sfxLen = str->len;
if (sfxLen > 0) if (sfxLen > 0)
{ {
@ -589,15 +588,15 @@ bool ACS_CF_SectorSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
vol = argV[1]; vol = argV[1];
if (info->sector != NULL) if (info->sector != nullptr)
{ {
// New to Ring Racers: Use activating sector directly. // New to Ring Racers: Use activating sector directly.
origin = (void *)&info->sector->soundorg; origin = static_cast<mobj_t *>(static_cast<void *>(&info->sector->soundorg));
} }
else if (info->line != NULL) else if (info->line != nullptr)
{ {
// Original Hexen behavior: Use line's frontsector. // Original Hexen behavior: Use line's frontsector.
origin = (void *)&info->line->frontsector->soundorg; origin = static_cast<mobj_t *>(static_cast<void *>(&info->line->frontsector->soundorg));
} }
S_StartSoundAtVolume(origin, sfxId, vol); S_StartSoundAtVolume(origin, sfxId, vol);
@ -605,16 +604,16 @@ bool ACS_CF_SectorSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_AmbientSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Plays a sound effect globally. Plays a sound effect globally.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_AmbientSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = nullptr;
ACSVM_String *str = NULL; ACSVM::String *str = nullptr;
const char *sfxName = NULL; const char *sfxName = nullptr;
size_t sfxLen = 0; size_t sfxLen = 0;
sfxenum_t sfxId = sfx_None; sfxenum_t sfxId = sfx_None;
@ -622,11 +621,11 @@ bool ACS_CF_AmbientSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Wor
(void)argC; (void)argC;
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[0]); str = map->getString(argV[0]);
sfxName = ACSVM_String_GetStr(str); sfxName = str->str;
sfxLen = ACSVM_String_GetLen(str); sfxLen = str->len;
if (sfxLen > 0) if (sfxLen > 0)
{ {
@ -652,7 +651,7 @@ bool ACS_CF_AmbientSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Wor
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_SetLineTexture(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Plays a sound effect globally. Plays a sound effect globally.
--------------------------------------------------*/ --------------------------------------------------*/
@ -663,14 +662,14 @@ enum
SLT_POS_BOTTOM SLT_POS_BOTTOM
}; };
bool ACS_CF_SetLineTexture(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
mtag_t tag = 0; mtag_t tag = 0;
UINT8 sideId = 0; UINT8 sideId = 0;
UINT8 texPos = 0; UINT8 texPos = 0;
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = NULL;
ACSVM_String *str = NULL; ACSVM::String *str = NULL;
const char *texName = NULL; const char *texName = NULL;
INT32 texId = LUMPERROR; INT32 texId = LUMPERROR;
@ -682,9 +681,9 @@ bool ACS_CF_SetLineTexture(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_W
sideId = (argV[1] & 1); sideId = (argV[1] & 1);
texPos = argV[2]; texPos = argV[2];
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[4]); str = map->getString(argV[4]);
texName = ACSVM_String_GetStr(str); texName = str->str;
texId = R_TextureNumForName(texName); texId = R_TextureNumForName(texName);
@ -718,13 +717,13 @@ bool ACS_CF_SetLineTexture(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_W
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_SetLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Changes a linedef's special and arguments. Changes a linedef's special and arguments.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_SetLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
activator_t *info = (activator_t *)ACSVM_Thread_GetInfo(thread); auto info = &static_cast<Thread *>(thread)->info;
mtag_t tag = 0; mtag_t tag = 0;
INT32 spec = 0; INT32 spec = 0;
@ -735,14 +734,14 @@ bool ACS_CF_SetLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_W
tag = argV[0]; tag = argV[0];
spec = argV[1]; spec = argV[1];
numArgs = min(max(argC - 2, 0), NUMLINEARGS); numArgs = std::min(std::max((signed)(argC - 2), 0), NUMLINEARGS);
TAG_ITER_LINES(tag, lineId) TAG_ITER_LINES(tag, lineId)
{ {
line_t *line = &lines[lineId]; line_t *line = &lines[lineId];
size_t i; size_t i;
if (info->line != NULL && line == info->line) if (info->line != nullptr && line == info->line)
{ {
continue; continue;
} }
@ -759,16 +758,16 @@ bool ACS_CF_SetLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_W
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_ThingSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Plays a sound effect for a tagged object. Plays a sound effect for a tagged object.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_ThingSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_MapScope *map = NULL; ACSVM::MapScope *map = nullptr;
ACSVM_String *str = NULL; ACSVM::String *str = nullptr;
const char *sfxName = NULL; const char *sfxName = nullptr;
size_t sfxLen = 0; size_t sfxLen = 0;
mtag_t tag = 0; mtag_t tag = 0;
@ -779,11 +778,11 @@ bool ACS_CF_ThingSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
tag = argV[0]; tag = argV[0];
map = ACSVM_Thread_GetScopeMap(thread); map = thread->scopeMap;
str = ACSVM_MapScope_GetString(map, argV[1]); str = map->getString(argV[1]);
sfxName = ACSVM_String_GetStr(str); sfxName = str->str;
sfxLen = ACSVM_String_GetLen(str); sfxLen = str->len;
if (sfxLen > 0) if (sfxLen > 0)
{ {
@ -823,50 +822,40 @@ bool ACS_CF_ThingSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_EndPrintBold(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
One of the ACS wrappers for CEcho. This One of the ACS wrappers for CEcho. This
version prints for all players. version prints for all players.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_EndPrintBold(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_PrintBuf *buf = NULL;
(void)argV; (void)argV;
(void)argC; (void)argC;
buf = ACSVM_Thread_GetPrintBuf(thread);
HU_SetCEchoDuration(5); HU_SetCEchoDuration(5);
HU_DoCEcho(ACSVM_PrintBuf_GetData(buf)); HU_DoCEcho(thread->printBuf.data());
ACSVM_PrintBuf_Drop(buf);
thread->printBuf.drop();
return false; return false;
} }
/*-------------------------------------------------- /*--------------------------------------------------
bool ACS_CF_EndLog(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndLog(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
One of the ACS wrappers for CONS_Printf. One of the ACS wrappers for CONS_Printf.
This version only prints if the activator This version only prints if the activator
is a display player. is a display player.
--------------------------------------------------*/ --------------------------------------------------*/
bool ACS_CF_EndLog(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) bool CallFunc_EndLog(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
ACSVM_PrintBuf *buf = NULL;
(void)argV; (void)argV;
(void)argC; (void)argC;
buf = ACSVM_Thread_GetPrintBuf(thread);
if (ACS_ActivatorIsLocal(thread) == true) if (ACS_ActivatorIsLocal(thread) == true)
{ {
CONS_Printf("%s\n", ACSVM_PrintBuf_GetData(buf)); CONS_Printf("%s\n", thread->printBuf.data());
} }
ACSVM_PrintBuf_Drop(buf); thread->printBuf.drop();
return false; return false;
} }

70
src/acs/call-funcs.hpp Normal file
View file

@ -0,0 +1,70 @@
// 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.hpp
/// \brief Action Code Script: CallFunc instructions
#ifndef __SRB2_ACS_CALL_FUNCS_HPP__
#define __SRB2_ACS_CALL_FUNCS_HPP__
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
/*--------------------------------------------------
bool CallFunc_???(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
These are the actual CallFuncs ran when ACS
is executed. Which CallFuncs are executed
is based on the indices from the compiled
data. ACS_EnvConstruct is where the link
between the byte code and the actual function
is made.
Input Arguments:-
thread: The ACS execution thread this action
is running on.
argV: An array of the action's arguments.
argC: The length of the argument array.
Return:-
Returns true if this function pauses the
thread's execution. Otherwise returns false.
--------------------------------------------------*/
bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ClearLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_GameType(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_GameSpeed(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_Timer(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_SectorSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_AmbientSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_SetLineSpecial(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ThingSound(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_EndLog(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
#endif // __SRB2_ACS_CALL_FUNCS_HPP__

307
src/acs/environment.cpp Normal file
View file

@ -0,0 +1,307 @@
// 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 environment.cpp
/// \brief Action Code Script: Environment definition
extern "C" {
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
#include "../r_defs.h"
#include "../r_state.h"
#include "../g_game.h"
#include "../p_spec.h"
#include "../w_wad.h"
#include "../z_zone.h"
}
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
#include "environment.hpp"
#include "thread.hpp"
#include "call-funcs.hpp"
#include "../cxxutil.hpp"
using namespace srb2::acs;
Environment ACSEnv;
Environment::Environment()
{
ACSVM::GlobalScope *global = getGlobalScope(0);
// Activate global scope immediately, since we don't want it off.
// Not that we're adding any modules to it, though. :p
global->active = true;
// Add the data & function pointers.
// Starting with raw ACS0 codes. I'm using this classic-style
// format here to have a blueprint for what needs implementing,
// but it'd also be fine to move these to new style.
// See also:
// - https://doomwiki.org/wiki/ACS0_instruction_set
// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeData.hpp
// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeList.hpp
// 0 to 56: Implemented by ACSVM
addCodeDataACS0( 57, {"", 2, addCallFunc(CallFunc_Random)});
addCodeDataACS0( 58, {"WW", 0, addCallFunc(CallFunc_Random)});
addCodeDataACS0( 59, {"", 2, addCallFunc(CallFunc_ThingCount)});
addCodeDataACS0( 60, {"WW", 0, addCallFunc(CallFunc_ThingCount)});
addCodeDataACS0( 61, {"", 1, addCallFunc(CallFunc_TagWait)});
addCodeDataACS0( 62, {"W", 0, addCallFunc(CallFunc_TagWait)});
addCodeDataACS0( 63, {"", 1, addCallFunc(CallFunc_PolyWait)});
addCodeDataACS0( 64, {"W", 0, addCallFunc(CallFunc_PolyWait)});
addCodeDataACS0( 65, {"", 2, addCallFunc(CallFunc_ChangeFloor)});
addCodeDataACS0( 66, {"WWS", 0, addCallFunc(CallFunc_ChangeFloor)});
addCodeDataACS0( 67, {"", 2, addCallFunc(CallFunc_ChangeCeiling)});
addCodeDataACS0( 68, {"WWS", 0, addCallFunc(CallFunc_ChangeCeiling)});
// 69 to 79: Implemented by ACSVM
addCodeDataACS0( 80, {"", 0, addCallFunc(CallFunc_LineSide)});
// 81 to 82: Implemented by ACSVM
addCodeDataACS0( 83, {"", 0, addCallFunc(CallFunc_ClearLineSpecial)});
// 84 to 85: Implemented by ACSVM
addCodeDataACS0( 86, {"", 0, addCallFunc(CallFunc_EndPrint)});
// 87 to 89: Implemented by ACSVM
addCodeDataACS0( 90, {"", 0, addCallFunc(CallFunc_PlayerCount)});
addCodeDataACS0( 91, {"", 0, addCallFunc(CallFunc_GameType)});
addCodeDataACS0( 92, {"", 0, addCallFunc(CallFunc_GameSpeed)});
addCodeDataACS0( 93, {"", 0, addCallFunc(CallFunc_Timer)});
addCodeDataACS0( 94, {"", 2, addCallFunc(CallFunc_SectorSound)});
addCodeDataACS0( 95, {"", 2, addCallFunc(CallFunc_AmbientSound)});
addCodeDataACS0( 97, {"", 4, addCallFunc(CallFunc_SetLineTexture)});
addCodeDataACS0( 99, {"", 7, addCallFunc(CallFunc_SetLineSpecial)});
addCodeDataACS0(100, {"", 3, addCallFunc(CallFunc_ThingSound)});
addCodeDataACS0(101, {"", 0, addCallFunc(CallFunc_EndPrintBold)});
// 136 to 137: Implemented by ACSVM
// 157: Implemented by ACSVM
// 167 to 173: Implemented by ACSVM
addCodeDataACS0(174, {"BB", 0, addCallFunc(CallFunc_Random)});
// 175 to 179: Implemented by ACSVM
// 181 to 189: Implemented by ACSVM
// 203 to 217: Implemented by ACSVM
// 225 to 243: Implemented by ACSVM
// 253: Implemented by ACSVM
// 256 to 257: Implemented by ACSVM
// 263: Implemented by ACSVM
addCodeDataACS0(270, {"", 0, addCallFunc(CallFunc_EndLog)});
// 273 to 275: Implemented by ACSVM
// 291 to 325: Implemented by ACSVM
// 330: Implemented by ACSVM
// 349 to 361: Implemented by ACSVM
// 363 to 380: Implemented by ACSVM
}
ACSVM::Thread *Environment::allocThread()
{
return new Thread(this);
}
void Environment::loadModule(ACSVM::Module *module)
{
ACSVM::ModuleName *const name = &module->name;
size_t lumpLen = 0;
std::vector<ACSVM::Byte> data;
if (name->i == (size_t)LUMPERROR)
{
// No lump given for module.
CONS_Alert(CONS_ERROR, "Bad lump for ACS module '%s'\n", name->s->str);
throw ACSVM::ReadError("file open failure");
}
lumpLen = W_LumpLength(name->i);
if (W_IsLumpWad(name->i) == true || lumpLen == 0)
{
// The lump given is a virtual resource.
// Try to grab a BEHAVIOR lump from inside of it.
virtres_t *vRes = vres_GetMap(name->i);
virtlump_t *vLump = vres_Find(vRes, "BEHAVIOR");
CONS_Printf("Attempting to load ACS module from map's virtual resource...\n");
if (vLump != nullptr && vLump->size > 0)
{
data.insert(data.begin(), vLump->data, vLump->data + vLump->size);
CONS_Printf("Successfully found BEHAVIOR lump.\n");
}
else
{
CONS_Printf("No BEHAVIOR lump found.\n");
}
}
else
{
// It's a real lump.
ACSVM::Byte *lump = static_cast<ACSVM::Byte *>(Z_Calloc(lumpLen, PU_STATIC, nullptr));
auto _ = srb2::finally([lump]() { Z_Free(lump); });
W_ReadLump(name->i, lump);
data.insert(data.begin(), lump, lump + lumpLen);
CONS_Printf("Loading ACS module directly from lump.\n");
}
if (data.empty() == false)
{
try
{
module->readBytecode(data.data(), data.size());
}
catch (const ACSVM::ReadError &e)
{
CONS_Printf("Failed to load ACS module '%s': %s\n", name->s->str, e.what());
throw ACSVM::ReadError("failed import");
}
}
else
{
// Unlike Hexen, a BEHAVIOR lump is not required.
// Simply ignore in this instance.
CONS_Printf("No data received, ignoring...\n");
}
}
bool Environment::checkTag(ACSVM::Word type, ACSVM::Word tag)
{
switch (type)
{
case ACS_TAGTYPE_SECTOR:
{
INT32 secnum = -1;
TAG_ITER_SECTORS(tag, secnum)
{
sector_t *sec = &sectors[secnum];
if (sec->floordata != nullptr || sec->ceilingdata != nullptr)
{
return false;
}
}
return true;
}
case ACS_TAGTYPE_POLYOBJ:
{
const polyobj_t *po = Polyobj_GetForNum(tag);
return (po == nullptr || po->thinker == nullptr);
}
}
return true;
}
ACSVM::Word Environment::callSpecImpl
(
ACSVM::Thread *thread, ACSVM::Word spec,
const ACSVM::Word *argV, ACSVM::Word argC
)
{
auto info = &static_cast<Thread *>(thread)->info;
ACSVM::MapScope *const map = thread->scopeMap;
int arg = 0;
int numStringArgs = 0;
INT32 args[NUMLINEARGS] = {0};
char *stringargs[NUMLINESTRINGARGS] = {0};
auto _ = srb2::finally(
[stringargs]()
{
for (int i = 0; i < NUMLINESTRINGARGS; i++)
{
Z_Free(stringargs[i]);
}
}
);
activator_t *activator = static_cast<activator_t *>(Z_Calloc(sizeof(activator_t), PU_LEVEL, nullptr));
auto __ = srb2::finally([activator]() { Z_Free(activator); });
// This needs manually set, as ACS just uses indicies in the
// compiled string table and not actual strings, and SRB2 has
// separate args and stringargs, so there's no way to
// properly distinguish them.
switch (spec)
{
case 442:
numStringArgs = 2;
break;
case 413:
case 414:
case 415:
case 423:
case 425:
case 443:
case 459:
case 461:
case 463:
case 469:
numStringArgs = 1;
break;
default:
break;
}
for (; arg < numStringArgs; arg++)
{
ACSVM::String *strPtr = map->getString(argV[arg]);
stringargs[arg] = static_cast<char *>(Z_Malloc(strPtr->len + 1, PU_STATIC, nullptr));
M_Memcpy(stringargs[arg], strPtr->str, strPtr->len + 1);
}
for (; arg < std::min((signed)argC, NUMLINEARGS); arg++)
{
args[arg - numStringArgs] = argV[arg];
}
P_SetTarget(&activator->mo, info->mo);
activator->line = info->line;
activator->side = info->side;
activator->sector = info->sector;
activator->po = info->po;
activator->fromLineSpecial = false;
P_ProcessSpecial(activator, spec, args, stringargs);
return 1;
}

52
src/acs/environment.hpp Normal file
View file

@ -0,0 +1,52 @@
// 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 environment.hpp
/// \brief Action Code Script: Environment definition
#ifndef __SRB2_ACS_ENVIRONMENT_HPP__
#define __SRB2_ACS_ENVIRONMENT_HPP__
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
namespace srb2::acs {
class Environment : public ACSVM::Environment
{
public:
Environment();
virtual bool checkTag(ACSVM::Word type, ACSVM::Word tag);
virtual ACSVM::Word callSpecImpl(
ACSVM::Thread *thread, ACSVM::Word spec,
const ACSVM::Word *argV, ACSVM::Word argC
);
virtual ACSVM::Thread *allocThread();
protected:
virtual void loadModule(ACSVM::Module *module);
};
}
extern srb2::acs::Environment ACSEnv;
#endif // __SRB2_ACS_ENVIRONMENT_HPP__

232
src/acs/interface.cpp Normal file
View file

@ -0,0 +1,232 @@
// 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 interface.cpp
/// \brief Action Code Script: Interface for the rest of SRB2's game logic
extern "C" {
#include "interface.h"
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
#include "../r_defs.h"
#include "../g_game.h"
#include "../i_system.h"
}
#include <cmath>
#include <memory>
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
#include "environment.hpp"
#include "thread.hpp"
#include "../cxxutil.hpp"
using namespace srb2::acs;
using std::size_t;
/*--------------------------------------------------
void ACS_Init(void)
See header file for description.
--------------------------------------------------*/
void ACS_Init(void)
{
#if 0
// Initialize ACS on engine start-up.
ACSEnv = new Environment();
I_AddExitFunc(ACS_Shutdown);
#endif
}
/*--------------------------------------------------
void ACS_Shutdown(void)
See header file for description.
--------------------------------------------------*/
void ACS_Shutdown(void)
{
#if 0
// Delete ACS environment.
delete ACSEnv;
ACSEnv = nullptr;
#endif
}
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID)
See header file for description.
--------------------------------------------------*/
void ACS_LoadLevelScripts(size_t mapID)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *hub = NULL;
ACSVM::MapScope *map = NULL;
std::vector<ACSVM::Module *> modules;
// Just some notes on how Hexen's scopes work, if anyone
// intends to implement proper hub logic:
// The integer is an ID for which hub / map it is,
// and instead sets active according to which ones
// should run, since you can go between them.
// But I didn't intend on implementing these features,
// since hubs aren't planned for Ring Racers (although
// they might be useful for SRB2), and I intentionally
// avoided implementing global ACS (since Lua would be
// a better language to do that kind of code).
// Since we literally only are using map scope, we can
// just free everything between every level. But if
// hubs are to be implemented, this logic would need
// to be far more sophisticated.
// Reset hub scope, even if we are not using it.
hub = global->getHubScope(0);
hub->reset();
hub->active = true;
// Start up new map scope.
map = hub->getMapScope(0); // This is where you'd put in mapID if you add hub support.
map->reset();
map->active = true;
// Insert BEHAVIOR lump into the list.
{
ACSVM::ModuleName name = ACSVM::ModuleName(
env->getString( mapheaderinfo[mapID]->lumpname ),
nullptr,
mapheaderinfo[mapID]->lumpnum
);
modules.push_back(env->getModule(name));
}
if (modules.empty() == false)
{
// Register the modules with map scope.
map->addModules(modules.data(), modules.size());
}
}
/*--------------------------------------------------
void ACS_RunPlayerEnterScript(player_t *player)
See header file for description.
--------------------------------------------------*/
void ACS_RunPlayerEnterScript(player_t *player)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
ACSVM::MapScope::ScriptStartInfo scriptInfo;
ThreadInfo info;
P_SetTarget(&info.mo, player->mo);
scriptInfo.info = &info;
map->scriptStartTypeForced(ACS_ST_ENTER, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunLevelStartScripts(void)
See header file for description.
--------------------------------------------------*/
void ACS_RunLevelStartScripts(void)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
map->scriptStartType(ACS_ST_OPEN, {});
for (int i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
ACS_RunPlayerEnterScript(player);
}
}
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line)
See header file for description.
--------------------------------------------------*/
void ACS_RunLapScript(mobj_t *mo, line_t *line)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
ACSVM::MapScope::ScriptStartInfo scriptInfo;
ThreadInfo info;
P_SetTarget(&info.mo, mo);
info.line = line;
scriptInfo.info = &info;
map->scriptStartTypeForced(ACS_ST_LAP, scriptInfo);
}
/*--------------------------------------------------
void ACS_Tick(void)
See header file for description.
--------------------------------------------------*/
void ACS_Tick(void)
{
Environment *env = &ACSEnv;
if (env->hasActiveThread() == true)
{
env->exec();
}
}

116
src/acs/interface.h Normal file
View file

@ -0,0 +1,116 @@
// 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 interface.h
/// \brief Action Code Script: Interface for the rest of SRB2's game logic
#ifndef __SRB2_ACS_INTERFACE_H__
#define __SRB2_ACS_INTERFACE_H__
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
/*--------------------------------------------------
void ACS_Init(void);
Initializes the ACS environment. Handles creating
the VM, initializing its hooks, storing the
pointer for future reference, and adding the
shutdown function.
--------------------------------------------------*/
void ACS_Init(void);
/*--------------------------------------------------
void ACS_Shutdown(void);
Frees the ACS environment, for when the game
is exited.
--------------------------------------------------*/
void ACS_Shutdown(void);
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID);
Resets the ACS hub and map scopes to remove
existing running scripts, and inserts the new
level's ACS modules (BEHAVIOR lump) into
the environment.
Input Arguments:-
mapID: The map's number to read the BEHAVIOR
lump of.
Return:-
None
--------------------------------------------------*/
void ACS_LoadLevelScripts(size_t mapID);
/*--------------------------------------------------
void ACS_RunPlayerEnterScript(player_t *player);
Runs the map's special script for a player
entering the game.
Input Arguments:-
player: The player to run the script for.
Return:-
None
--------------------------------------------------*/
void ACS_RunPlayerEnterScript(player_t *player);
/*--------------------------------------------------
void ACS_RunLevelStartScripts(void);
Runs the map's special scripts for opening
the level, and for all players to enter
the game.
--------------------------------------------------*/
void ACS_RunLevelStartScripts(void);
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line);
Runs the map's special script for a player
crossing the finish line.
Input Arguments:-
player: The player to run the script for.
line: The finish line's linedef.
Return:-
None
--------------------------------------------------*/
void ACS_RunLapScript(mobj_t *mo, line_t *line);
/*--------------------------------------------------
void ACS_Tick(void);
Executes all of the ACS environment's
currently active threads.
--------------------------------------------------*/
void ACS_Tick(void);
#endif // __SRB2_ACS_INTERFACE_H__

57
src/acs/thread.cpp Normal file
View file

@ -0,0 +1,57 @@
// 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 thread.cpp
/// \brief Action Code Script: Thread definition
#include "thread.hpp"
extern "C" {
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
}
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
using namespace srb2::acs;
void Thread::start(
ACSVM::Script *script, ACSVM::MapScope *map,
const ACSVM::ThreadInfo *infoPtr, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::Thread::start(script, map, infoPtr, argV, argC);
if (infoPtr != nullptr)
{
info = *static_cast<const ThreadInfo *>(infoPtr);
}
else
{
info = {};
}
result = 1;
}
void Thread::stop()
{
ACSVM::Thread::stop();
info = {};
}

125
src/acs/thread.hpp Normal file
View file

@ -0,0 +1,125 @@
// 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 thread.hpp
/// \brief Action Code Script: Thread definition
#ifndef __SRB2_ACS_THREAD_HPP__
#define __SRB2_ACS_THREAD_HPP__
extern "C" {
#include "../doomtype.h"
#include "../doomdef.h"
#include "../doomstat.h"
#include "../p_tick.h"
}
#include <ACSVM/Code.hpp>
#include <ACSVM/CodeData.hpp>
#include <ACSVM/Environment.hpp>
#include <ACSVM/Error.hpp>
#include <ACSVM/Module.hpp>
#include <ACSVM/Scope.hpp>
#include <ACSVM/Script.hpp>
#include <ACSVM/Serial.hpp>
#include <ACSVM/Thread.hpp>
#include <Util/Floats.hpp>
namespace srb2::acs {
//
// Special global script types.
//
enum acs_scriptType_e
{
ACS_ST_OPEN = 1, // OPEN: Runs once when the level starts.
ACS_ST_RESPAWN = 2, // RESPAWN: Runs when a player respawns.
ACS_ST_DEATH = 3, // DEATH: Runs when a player dies.
ACS_ST_ENTER = 4, // ENTER: Runs when a player enters the game; both on start of the level, and when un-spectating.
ACS_ST_LAP = 5, // LAP: Runs when a player's lap increases from crossing the finish line.
};
//
// Script "waiting on tag" types.
//
enum acs_tagType_e
{
ACS_TAGTYPE_POLYOBJ,
ACS_TAGTYPE_SECTOR,
};
class ThreadInfo : public ACSVM::ThreadInfo
{
public:
mobj_t *mo; // Object that activated this thread.
line_t *line; // Linedef that activated this thread.
UINT8 side; // Front / back side of said linedef.
sector_t *sector; // Sector that activated this thread.
polyobj_t *po; // Polyobject that activated this thread.
bool fromLineSpecial; // Called from P_ProcessLineSpecial.
ThreadInfo() :
mo{ nullptr },
line{ nullptr },
side{ 0 },
sector{ nullptr },
po{ nullptr },
fromLineSpecial{ false }
{
}
ThreadInfo(const ThreadInfo &info) :
mo{ nullptr },
line{ info.line },
side{ info.side },
sector{ info.sector },
po{ info.po },
fromLineSpecial{ info.fromLineSpecial }
{
P_SetTarget(&mo, info.mo);
}
~ThreadInfo()
{
P_SetTarget(&mo, nullptr);
}
ThreadInfo &operator = (const ThreadInfo &info)
{
P_SetTarget(&mo, info.mo);
line = info.line;
side = info.side;
po = info.po;
return *this;
}
};
class Thread : public ACSVM::Thread
{
public:
ThreadInfo info;
explicit Thread(ACSVM::Environment *env_) : ACSVM::Thread{env_} {}
virtual ACSVM::ThreadInfo const *getInfo() const { return &info; }
virtual void start(
ACSVM::Script *script, ACSVM::MapScope *map,
const ACSVM::ThreadInfo *info, const ACSVM::Word *argV, ACSVM::Word argC
);
virtual void stop();
};
}
#endif // __SRB2_ACS_THREAD_HPP__

View file

@ -76,7 +76,7 @@
#include "doomstat.h" #include "doomstat.h"
#include "m_random.h" // P_ClearRandom #include "m_random.h" // P_ClearRandom
#include "k_specialstage.h" #include "k_specialstage.h"
#include "k_acs.h" #include "acs/interface.h"
#ifdef CMAKECONFIG #ifdef CMAKECONFIG
#include "config.h" #include "config.h"

View file

@ -61,7 +61,7 @@
#include "k_specialstage.h" #include "k_specialstage.h"
#include "k_bot.h" #include "k_bot.h"
#include "doomstat.h" #include "doomstat.h"
#include "k_acs.h" #include "acs/interface.h"
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
#include "discord.h" #include "discord.h"

View file

@ -1,833 +0,0 @@
// 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 k_acs.c
/// \brief Action Code Script implementation using ACSVM
#include "k_acs.h"
#include "doomtype.h"
#include "doomdef.h"
#include "doomstat.h"
#include "z_zone.h"
#include "w_wad.h"
#include "i_system.h"
#include "r_defs.h"
#include "r_state.h"
#include "p_polyobj.h"
#include "taglist.h"
#include "d_player.h"
#include "g_game.h"
#include "p_tick.h"
#include "p_local.h"
#include <CAPI/BinaryIO.h>
#include <CAPI/Environment.h>
#include <CAPI/Module.h>
#include <CAPI/PrintBuf.h>
#include <CAPI/Scope.h>
#include <CAPI/String.h>
#include <CAPI/Thread.h>
static ACSVM_Environment *ACSenv = NULL;
/*--------------------------------------------------
ACSVM_Environment *ACS_GetEnvironment(void)
See header file for description.
--------------------------------------------------*/
ACSVM_Environment *ACS_GetEnvironment(void)
{
return ACSenv;
}
/*--------------------------------------------------
static void ACS_EnvBadAlloc(ACSVM_Environment *env, char const *what)
ACSVM Environment hook. Runs in case of a memory
allocation failure occuring. Environment state
afterwards is unusable; the only thing safe to do
is using ACSVM_FreeEnvironment.
Input Arguments:-
env - The ACS environment data.
what - Error string.
Return:-
N/A
--------------------------------------------------*/
static void ACS_EnvBadAlloc(ACSVM_Environment *env, char const *what)
{
(void)env;
CONS_Alert(CONS_ERROR, "Error allocating memory for ACS (%s)\n", what);
if (env == ACSenv)
{
// Restart the main environment.
ACS_Shutdown();
I_RemoveExitFunc(ACS_Shutdown); // Since ACS_Init will add it again.
ACS_Init();
}
}
/*--------------------------------------------------
static void ACS_EnvReadError(ACSVM_Environment *env, char const *what)
ACSVM Environment hook. Runs when an ACS module
fails to read. Environment state should be safe
afterwards.
Input Arguments:-
env - The ACS environment data.
what - Error string.
Return:-
N/A
--------------------------------------------------*/
static void ACS_EnvReadError(ACSVM_Environment *env, char const *what)
{
(void)env;
CONS_Alert(CONS_WARNING, "Error reading ACS module (%s)\n", what);
}
/*--------------------------------------------------
static void ACS_EnvSerialError(ACSVM_Environment *env, char const *what)
ACSVM Environment hook. Runs when the ACS state
fails to save or load. Environment state is
safe in that it shouldn't be causing crashes,
but it is indeterminate.
Input Arguments:-
env - The ACS environment data.
what - Error string.
Return:-
N/A
--------------------------------------------------*/
static void ACS_EnvSerialError(ACSVM_Environment *env, char const *what)
{
(void)env;
CONS_Alert(CONS_WARNING, "Error serializing ACS state (%s)\n", what);
}
/*--------------------------------------------------
static void ACS_EnvThreadKilled(ACSVM_Environment *env, char const *what)
ACSVM Environment hook. Runs when the thread
has been killed for whatever reason, so that
the console can warn the user about it.
Input Arguments:-
env - The ACS environment the thread is from.
thread - The thread that was stopped.
reason - The reason the thread was stopped. See ACSVM_KillType.
data - Bytecode data at time of stopping.
Return:-
N/A
--------------------------------------------------*/
static void ACS_EnvThreadKilled(ACSVM_Environment const *env, ACSVM_Thread *thread, ACSVM_Word reason, ACSVM_Word data)
{
static const char *strings[] = {
"Just for fun", // ACSVM_KillType_None
"Out of bounds", // ACSVM_KillType_OutOfBounds
"Unknown code", // ACSVM_KillType_UnknownCode
"Unknown function", // ACSVM_KillType_UnknownFunc
"Reached recursion limit" // ACSVM_KillType_BranchLimit
};
(void)env;
(void)thread;
(void)data;
CONS_Alert(CONS_ERROR, "ACS thread killed (%s)\n", strings[reason]);
}
/*--------------------------------------------------
static void ACS_AddCodeDataCallFunc(
ACSVM_Environment *env, ACSVM_Word code,
char const *args, ACSVM_Word argc, ACSVM_CallFunc func)
Shortcut function to simplify adding
CallFuncs. These are for code data ones;
accessible in all ACS formats.
Input Arguments:-
env - The ACS environment data to add this to.
code - The byte code for this function.
args - A string of arguments to use. The
letters represent different operations
to preform on the stack.
argc - Number of arguments to pull from
the stack, if not using args.
func - The function to add.
Return:-
N/A
--------------------------------------------------*/
static inline void ACS_AddCodeDataCallFunc(ACSVM_Environment *env, ACSVM_Word code, char const *args, ACSVM_Word argc, ACSVM_CallFunc func)
{
ACSVM_Word funcId = ACSVM_Environment_AddCallFunc(env, func);
ACSVM_Code tCode = (argc != 0 ? ACSVM_Code_CallFunc : ACSVM_Code_CallFunc_Lit);
ACSVM_Environment_AddCodeDataACS0(
env, code,
args, tCode, argc,
funcId
);
}
/*--------------------------------------------------
static void ACS_EnvConstruct(ACSVM_Environment *env)
ACSVM Environment hook. Runs when the
environment is initally created.
Input Arguments:-
env - The ACS environment data to construct.
Return:-
N/A
--------------------------------------------------*/
static void ACS_EnvConstruct(ACSVM_Environment *env)
{
ACSVM_GlobalScope *global = ACSVM_Environment_GetGlobalScope(env, 0);
// Activate global scope immediately, since we don't want it off.
// Not that we're adding any modules to it, though. :p
ACSVM_GlobalScope_SetActive(global, true);
// Add the data & function pointers.
// Starting with raw ACS0 codes. I'm using this classic-style
// format here to have a blueprint for what needs implementing,
// but it'd also be fine to move these to new style.
// See also:
// - https://doomwiki.org/wiki/ACS0_instruction_set
// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeData.hpp
// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeList.hpp
// 0 to 56: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 57, "", 2, ACS_CF_Random);
ACS_AddCodeDataCallFunc(env, 58, "WW", 0, ACS_CF_Random);
ACS_AddCodeDataCallFunc(env, 59, "", 2, ACS_CF_ThingCount);
ACS_AddCodeDataCallFunc(env, 60, "WW", 0, ACS_CF_ThingCount);
ACS_AddCodeDataCallFunc(env, 61, "", 1, ACS_CF_TagWait);
ACS_AddCodeDataCallFunc(env, 62, "W", 0, ACS_CF_TagWait);
ACS_AddCodeDataCallFunc(env, 63, "", 1, ACS_CF_PolyWait);
ACS_AddCodeDataCallFunc(env, 64, "W", 0, ACS_CF_PolyWait);
ACS_AddCodeDataCallFunc(env, 65, "", 2, ACS_CF_ChangeFloor);
ACS_AddCodeDataCallFunc(env, 66, "WWS", 0, ACS_CF_ChangeFloor);
ACS_AddCodeDataCallFunc(env, 67, "", 2, ACS_CF_ChangeCeiling);
ACS_AddCodeDataCallFunc(env, 68, "WWS", 0, ACS_CF_ChangeCeiling);
// 69 to 79: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 80, "", 0, ACS_CF_LineSide);
// 81 to 82: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 83, "", 0, ACS_CF_ClearLineSpecial);
// 84 to 85: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 86, "", 0, ACS_CF_EndPrint);
// 87 to 89: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 90, "", 0, ACS_CF_PlayerCount);
ACS_AddCodeDataCallFunc(env, 91, "", 0, ACS_CF_GameType);
ACS_AddCodeDataCallFunc(env, 92, "", 0, ACS_CF_GameSpeed);
ACS_AddCodeDataCallFunc(env, 93, "", 0, ACS_CF_Timer);
ACS_AddCodeDataCallFunc(env, 94, "", 2, ACS_CF_SectorSound);
ACS_AddCodeDataCallFunc(env, 95, "", 2, ACS_CF_AmbientSound);
ACS_AddCodeDataCallFunc(env, 97, "", 4, ACS_CF_SetLineTexture);
ACS_AddCodeDataCallFunc(env, 99, "", 7, ACS_CF_SetLineSpecial);
ACS_AddCodeDataCallFunc(env, 100, "", 3, ACS_CF_ThingSound);
ACS_AddCodeDataCallFunc(env, 101, "", 0, ACS_CF_EndPrintBold);
// 136 to 137: Implemented by ACSVM
// 157: Implemented by ACSVM
// 167 to 173: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 174, "BB", 0, ACS_CF_Random);
// 175 to 179: Implemented by ACSVM
// 181 to 189: Implemented by ACSVM
// 203 to 217: Implemented by ACSVM
// 225 to 243: Implemented by ACSVM
// 253: Implemented by ACSVM
// 256 to 257: Implemented by ACSVM
// 263: Implemented by ACSVM
ACS_AddCodeDataCallFunc(env, 270, "", 0, ACS_CF_EndLog);
// 273 to 275: Implemented by ACSVM
// 291 to 325: Implemented by ACSVM
// 330: Implemented by ACSVM
// 349 to 361: Implemented by ACSVM
// 363 to 380: Implemented by ACSVM
}
/*--------------------------------------------------
static void ACS_EnvLoadModule(ACSVM_Environment *env, ACSVM_Module *module)
ACSVM Environment hook. Runs when a ACS
module is being loaded.
Input Arguments:-
env - The ACS environment data.
module - The ACS module being loaded.
Return:-
true when successful, otherwise false.
Returning false will also call the
ACS_EnvReadError hook.
--------------------------------------------------*/
static bool ACS_EnvLoadModule(ACSVM_Environment *env, ACSVM_Module *module)
{
ACSVM_ModuleName name = ACSVM_Module_GetName(module);
const char *str = ACSVM_String_GetStr(name.s);
size_t lumpLen = 0;
ACSVM_Byte *data = NULL;
size_t size = 0;
bool ret = false;
(void)env;
if (name.i == (size_t)LUMPERROR)
{
// No lump given for module.
CONS_Alert(CONS_ERROR, "Bad lump for ACS module \"%s\"\n", str);
return false;
}
lumpLen = W_LumpLength(name.i);
if (W_IsLumpWad(name.i) == true || lumpLen == 0)
{
// The lump given is a virtual resource.
// Try to grab it from there.
virtres_t *vRes = vres_GetMap(name.i);
virtlump_t *vLump = vres_Find(vRes, "BEHAVIOR");
CONS_Printf("Attempting to load ACS module from map's virtual resource...\n");
if (vLump != NULL)
{
data = Z_Calloc(vLump->size, PU_STATIC, NULL);
memcpy(data, vLump->data, vLump->size);
size = vLump->size;
CONS_Printf("Successfully found BEHAVIOR lump.\n");
}
else
{
CONS_Printf("No BEHAVIOR lump found.\n");
}
}
else
{
// It's a real lump.
data = Z_Calloc(lumpLen, PU_STATIC, NULL);
W_ReadLump(name.i, data);
size = lumpLen;
CONS_Printf("Loading ACS module directly from lump.\n");
}
if (data != NULL && size > 0)
{
CONS_Printf("Reading bytecode of ACS module...\n");
ret = ACSVM_Module_ReadBytecode(module, data, size);
}
else
{
// Unlike Hexen, BEHAVIOR is not required.
// Simply ignore in this instance.
CONS_Printf("No data received, ignoring...\n");
ret = true;
}
Z_Free(data);
return ret;
}
/*--------------------------------------------------
static bool ACS_EnvCheckTag(ACSVM_Environment const *env, ACSVM_Word type, ACSVM_Word tag)
ACSVM Environment hook. Ran to determine
whenever or not a thread should still be
waiting on a tag movement.
See: TagWait, PolyWait.
Input Arguments:-
env - The ACS environment data.
type - The kind of level data we're waiting on. See also: acs_tagType_e.
tag - The tag of said level data.
Return:-
true when the tag is done moving and
execution can continue, or false to keep
the thread paused.
--------------------------------------------------*/
static bool ACS_EnvCheckTag(ACSVM_Environment const *env, ACSVM_Word type, ACSVM_Word tag)
{
(void)env;
switch (type)
{
case ACS_TAGTYPE_SECTOR:
{
INT32 secnum = -1;
TAG_ITER_SECTORS(tag, secnum)
{
sector_t *sec = &sectors[secnum];
if (sec->floordata != NULL || sec->ceilingdata != NULL)
{
return false;
}
}
return true;
}
case ACS_TAGTYPE_POLYOBJ:
{
const polyobj_t *po = Polyobj_GetForNum(tag);
return (po == NULL || po->thinker == NULL);
}
}
return true;
}
/*--------------------------------------------------
static ACSVM_Word ACS_EnvCallSpecial(ACSVM_Environment const *env, ACSVM_Thread *thread, ACSVM_Word spec, ACSVM_Word const *argV, ACSVM_Word argC)
ACSVM Environment hook. Activates a special
function straight from the script rather than
a linedef.
Input Arguments:-
env - The ACS environment data.
thread - The thread that is executing the special.
spec - The special ID to execute.
argV - Array containing the arguments given from the script.
argC - The number of entries in argV.
Return:-
1 when successful, otherwise 0.
--------------------------------------------------*/
static ACSVM_Word ACS_EnvCallSpecial(ACSVM_Environment *env, ACSVM_Thread *thread, ACSVM_Word spec, ACSVM_Word const *argV, ACSVM_Word argC)
{
activator_t *info = ACSVM_Thread_GetInfo(thread);
ACSVM_MapScope *map = ACSVM_Thread_GetScopeMap(thread);
INT32 args[NUMLINEARGS] = {0};
char *stringargs[NUMLINESTRINGARGS] = {0};
size_t numStringArgs = 0;
size_t i = 0;
(void)env;
// This needs manually set, as ACS just uses indicies in the
// compiled string table and not actual strings, and SRB2 has
// separate args and stringargs, so there's no way to
// properly distinguish them.
switch (spec)
{
case 442:
numStringArgs = 2;
break;
case 413:
case 414:
case 415:
case 423:
case 425:
case 443:
case 459:
case 461:
case 463:
case 469:
numStringArgs = 1;
break;
default:
break;
}
for (; i < numStringArgs; i++)
{
ACSVM_String *strPtr = ACSVM_MapScope_GetString(map, argV[i]);
const char *str = ACSVM_String_GetStr(strPtr);
size_t strLen = ACSVM_String_GetLen(strPtr);
stringargs[i] = Z_Malloc(strLen + 1, PU_STATIC, NULL);
M_Memcpy(stringargs[i], str, strLen + 1);
}
for (; i < min(argC, NUMLINEARGS); i++)
{
args[i - numStringArgs] = argV[i];
}
P_ProcessSpecial(info, spec, args, stringargs);
for (i = 0; i < numStringArgs; i++)
{
Z_Free(stringargs[i]);
}
return 1;
}
/*--------------------------------------------------
static void ACS_ThrDestruct(ACSVM_Thread *thread)
ACSVM Thread hook. Runs as the thread
is in the process of being destroyed.
Input Arguments:-
thread - The ACS thread data to destroy.
Return:-
N/A
--------------------------------------------------*/
static void ACS_ThrDestruct(ACSVM_Thread *thread)
{
activator_t *info = ACSVM_Thread_GetInfo(thread);
if (info != NULL)
{
Z_Free(info);
}
}
/*--------------------------------------------------
static void ACS_ThrStart(ACSVM_Thread *thread, void *data)
ACSVM Thread hook. Runs immediately after
an ACS thread has been started.
Input Arguments:-
thread - The ACS thread data.
data - ACS thread info, as a raw pointer.
Return:-
The newly created ACS thread.
--------------------------------------------------*/
static void ACS_ThrStart(ACSVM_Thread *thread, void *data)
{
activator_t *activator = NULL;
ACSVM_Thread_SetResult(thread, 1);
if (data == NULL)
{
// Create an empty one, to reduce NULL checks.
// Might not be necessary.
activator = Z_Calloc(sizeof(activator_t), PU_STATIC, NULL);
}
else
{
activator = (activator_t *)data;
}
ACSVM_Thread_SetInfo(thread, activator);
}
/*--------------------------------------------------
static ACSVM_Thread *ACS_EnvAllocThread(ACSVM_Environment *env)
ACSVM Environment hook. Runs when an ACS
thread is being created. This is where
our own thread hooks are supposed to
be loaded.
Input Arguments:-
env - The ACS environment data.
Return:-
The newly created ACS thread.
--------------------------------------------------*/
static ACSVM_Thread *ACS_EnvAllocThread(ACSVM_Environment *env)
{
ACSVM_ThreadFuncs funcs = {0};
funcs.dtor = ACS_ThrDestruct;
funcs.start = ACS_ThrStart;
return ACSVM_AllocThread(env, &funcs, NULL);
}
/*--------------------------------------------------
void ACS_Init(void)
See header file for description.
--------------------------------------------------*/
void ACS_Init(void)
{
// Initialize ACS on engine start-up.
ACSVM_EnvironmentFuncs funcs = {0};
funcs.bad_alloc = ACS_EnvBadAlloc;
funcs.readError = ACS_EnvReadError;
funcs.serialError = ACS_EnvSerialError;
funcs.printKill = ACS_EnvThreadKilled;
funcs.ctor = ACS_EnvConstruct;
funcs.loadModule = ACS_EnvLoadModule;
funcs.checkTag = ACS_EnvCheckTag;
funcs.callSpecImpl = ACS_EnvCallSpecial;
funcs.allocThread = ACS_EnvAllocThread;
ACSenv = ACSVM_AllocEnvironment(&funcs, NULL);
I_AddExitFunc(ACS_Shutdown);
}
/*--------------------------------------------------
void ACS_Shutdown(void)
See header file for description.
--------------------------------------------------*/
void ACS_Shutdown(void)
{
// Delete ACS environment.
ACSVM_FreeEnvironment(ACSenv);
ACSenv = NULL;
}
/*--------------------------------------------------
static void ACS_ResetHub(ACSVM_GlobalScope *global)
Shortcut function to quickly free the
only hub scope Ring Racers uses.
Input Arguments:-
global - The global scope to free the hub from.
Return:-
N/A
--------------------------------------------------*/
static void ACS_ResetHub(ACSVM_GlobalScope *global)
{
ACSVM_HubScope *hub = ACSVM_GlobalScope_GetHubScope(global, 0);
ACSVM_GlobalScope_FreeHubScope(global, hub);
}
/*--------------------------------------------------
static void ACS_ResetMap(ACSVM_HubScope *hub)
Shortcut function to quickly free the
only map scope Ring Racers uses.
Input Arguments:-
hub - The hub scope to free the map from.
Return:-
N/A
--------------------------------------------------*/
static void ACS_ResetMap(ACSVM_HubScope *hub)
{
ACSVM_MapScope *map = ACSVM_HubScope_GetMapScope(hub, 0);
ACSVM_HubScope_FreeMapScope(hub, map);
}
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID)
See header file for description.
--------------------------------------------------*/
void ACS_LoadLevelScripts(size_t mapID)
{
ACSVM_Environment *env = ACSenv;
ACSVM_StringTable *strTab = ACSVM_Environment_GetStringTable(env);
ACSVM_GlobalScope *global = NULL;
ACSVM_HubScope *hub = NULL;
ACSVM_MapScope *map = NULL;
ACSVM_Module **modules = NULL;
size_t modules_len = 0;
size_t modules_size = 4;
global = ACSVM_Environment_GetGlobalScope(ACSenv, 0);
// Just some notes on how Hexen's scopes work, if anyone
// intends to implement proper hub logic:
// The integer is an ID for which hub / map it is,
// and instead sets active according to which ones
// should run, since you can go between them.
// But I didn't intend on implementing these features,
// since hubs aren't planned for Ring Racers (although
// they might be useful for SRB2), and I intentionally
// avoided implementing global ACS (since Lua would be
// a better language to do that kind of code).
// Since we literally only are using map scope, we can
// just free everything between every level. But if
// hubs are to be implemented, this logic would need
// to be far more sophisticated.
// Reset hub scope, even if we are not using it.
ACS_ResetHub(global);
hub = ACSVM_GlobalScope_GetHubScope(global, 0);
ACSVM_HubScope_SetActive(hub, true);
// Start up new map scope.
ACS_ResetMap(hub);
map = ACSVM_HubScope_GetMapScope(hub, 0); // This is where you'd put in mapID if you add hub support.
ACSVM_MapScope_SetActive(map, true);
// Allocate module list.
modules = Z_Calloc(modules_size * sizeof(ACSVM_Module *), PU_STATIC, NULL);
// Insert BEHAVIOR lump into the list.
{
char const *str = mapheaderinfo[mapID]->lumpname;
size_t len = strlen(str);
size_t hash = ACSVM_StrHash(str, len);
ACSVM_ModuleName name = {0};
name.s = ACSVM_StringTable_GetStringByData(strTab, str, len, hash);
name.i = mapheaderinfo[mapID]->lumpnum;
if (modules_len >= modules_size)
{
modules_size *= 2;
modules = Z_Realloc(modules, modules_size * sizeof(ACSVM_Module *), PU_STATIC, &modules);
}
modules[modules_len] = ACSVM_Environment_GetModule(env, name);
modules_len++;
}
if (modules_len > 0)
{
// Register the modules with map scope.
ACSVM_MapScope_AddModules(map, modules, modules_len);
}
}
/*--------------------------------------------------
void ACS_RunPlayerEnterScript(player_t *player)
See header file for description.
--------------------------------------------------*/
void ACS_RunPlayerEnterScript(player_t *player)
{
ACSVM_GlobalScope *global = NULL;
ACSVM_HubScope *hub = NULL;
ACSVM_MapScope *map = NULL;
activator_t *activator = NULL;
global = ACSVM_Environment_GetGlobalScope(ACSenv, 0);
hub = ACSVM_GlobalScope_GetHubScope(global, 0);
map = ACSVM_HubScope_GetMapScope(hub, 0);
activator = Z_Calloc(sizeof(activator_t), PU_STATIC, NULL);
P_SetTarget(&activator->mo, player->mo);
ACSVM_MapScope_ScriptStartTypeForced(map, ACS_ST_ENTER, NULL, 0, ACSVM_AllocThreadInfo(activator), NULL);
}
/*--------------------------------------------------
void ACS_RunLevelStartScripts(void)
See header file for description.
--------------------------------------------------*/
void ACS_RunLevelStartScripts(void)
{
ACSVM_GlobalScope *global = NULL;
ACSVM_HubScope *hub = NULL;
ACSVM_MapScope *map = NULL;
UINT8 i;
global = ACSVM_Environment_GetGlobalScope(ACSenv, 0);
hub = ACSVM_GlobalScope_GetHubScope(global, 0);
map = ACSVM_HubScope_GetMapScope(hub, 0);
// Start OPEN scripts.
ACSVM_MapScope_ScriptStartType(map, ACS_ST_OPEN, NULL, 0, NULL, NULL);
// Start ENTER scripts.
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
ACS_RunPlayerEnterScript(player);
}
}
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line)
See header file for description.
--------------------------------------------------*/
void ACS_RunLapScript(mobj_t *mo, line_t *line)
{
ACSVM_GlobalScope *global = NULL;
ACSVM_HubScope *hub = NULL;
ACSVM_MapScope *map = NULL;
activator_t *activator = NULL;
global = ACSVM_Environment_GetGlobalScope(ACSenv, 0);
hub = ACSVM_GlobalScope_GetHubScope(global, 0);
map = ACSVM_HubScope_GetMapScope(hub, 0);
activator = Z_Calloc(sizeof(activator_t), PU_STATIC, NULL);
P_SetTarget(&activator->mo, mo);
activator->line = line;
ACSVM_MapScope_ScriptStartTypeForced(map, ACS_ST_LAP, NULL, 0, ACSVM_AllocThreadInfo(activator), NULL);
}
/*--------------------------------------------------
void ACS_Tick(void)
See header file for description.
--------------------------------------------------*/
void ACS_Tick(void)
{
ACSVM_Environment *env = ACSenv;
if (ACSVM_Environment_HasActiveThread(env) == false)
{
return;
}
ACSVM_Environment_Exec(env);
}

View file

@ -1,244 +0,0 @@
// 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 k_acs.h
/// \brief Action Code Script implementation using ACSVM
#ifndef __K_ACS__
#define __K_ACS__
#include "doomtype.h"
#include "doomdef.h"
#include "p_mobj.h"
#include "r_defs.h"
#include "p_polyobj.h"
#include "d_player.h"
#include <CAPI/BinaryIO.h>
#include <CAPI/Environment.h>
#include <CAPI/Module.h>
#include <CAPI/PrintBuf.h>
#include <CAPI/Scope.h>
#include <CAPI/String.h>
#include <CAPI/Thread.h>
// Temp
ACSVM_String *ACSVM_MapScope_GetString(ACSVM_MapScope *map, ACSVM_Word index);
ACSVM_ThreadInfo *ACSVM_AllocThreadInfo(void *activator);
//
// Special global script types.
//
typedef enum
{
ACS_ST_OPEN = 1, // OPEN: Runs once when the level starts.
ACS_ST_RESPAWN = 2, // RESPAWN: Runs when a player respawns.
ACS_ST_DEATH = 3, // DEATH: Runs when a player dies.
ACS_ST_ENTER = 4, // ENTER: Runs when a player enters the game; both on start of the level, and when un-spectating.
ACS_ST_LAP = 5, // LAP: Runs when a player's lap increases from crossing the finish line.
} acs_scriptType_e;
//
// Script "waiting on tag" types.
//
typedef enum
{
ACS_TAGTYPE_POLYOBJ,
ACS_TAGTYPE_SECTOR,
} acs_tagType_e;
//
// Thread activator info
//
typedef struct
{
mobj_t *mo; // Object that activated this thread.
line_t *line; // Linedef that activated this thread.
UINT8 side; // Front / back side of said linedef.
sector_t *sector; // Sector that activated this thread.
polyobj_t *po; // Polyobject that activated this thread.
boolean fromLineSpecial; // Called from P_ProcessLineSpecial.
} activator_t;
/*--------------------------------------------------
void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
Runs a numbered special action. Can be called
by both linedefs and ACS scripts.
Input Arguments:-
activator - Struct containing information on what activated this special.
special - Special ID.
args - Array of the args.
stringargs - Array of the string args.
Return:-
N/A
--------------------------------------------------*/
void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
/*--------------------------------------------------
ACSVM_Environment *ACS_GetEnvironment(void);
Returns the global ACS environment. This contains
all of the information about the ACS VM state.
Input Arguments:-
None
Return:-
The ACS environment object.
--------------------------------------------------*/
ACSVM_Environment *ACS_GetEnvironment(void);
/*--------------------------------------------------
void ACS_Init(void);
Initializes the ACS environment. Handles creating
the VM, initializing its hooks, storing the
pointer for future reference, and adding the
shutdown function.
--------------------------------------------------*/
void ACS_Init(void);
/*--------------------------------------------------
void ACS_Shutdown(void);
Frees the ACS environment, for when the game
is exited.
--------------------------------------------------*/
void ACS_Shutdown(void);
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID);
Resets the ACS hub and map scopes to remove
existing running scripts, and inserts the new
level's ACS modules (BEHAVIOR lump) into
the environment.
Input Arguments:-
mapID: The map's number to read the BEHAVIOR
lump of.
Return:-
None
--------------------------------------------------*/
void ACS_LoadLevelScripts(size_t mapID);
/*--------------------------------------------------
void ACS_RunPlayerEnterScript(player_t *player);
Runs the map's special script for a player
entering the game.
Input Arguments:-
player: The player to run the script for.
Return:-
None
--------------------------------------------------*/
void ACS_RunPlayerEnterScript(player_t *player);
/*--------------------------------------------------
void ACS_RunLevelStartScripts(void);
Runs the map's special scripts for opening
the level, and for all players to enter
the game.
--------------------------------------------------*/
void ACS_RunLevelStartScripts(void);
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line);
Runs the map's special script for a player
crossing the finish line.
Input Arguments:-
player: The player to run the script for.
line: The finish line's linedef.
Return:-
None
--------------------------------------------------*/
void ACS_RunLapScript(mobj_t *mo, line_t *line);
/*--------------------------------------------------
void ACS_Tick(void);
Executes all of the ACS environment's
currently active threads.
--------------------------------------------------*/
void ACS_Tick(void);
/*--------------------------------------------------
bool ACS_CF_???(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
These are the actual CallFuncs ran when ACS
is executed. Which CallFuncs are executed
is based on the indices from the compiled
data. ACS_EnvConstruct is where the link
between the byte code and the actual function
is made.
Input Arguments:-
thread: The ACS execution thread this action
is running on.
argV: An array of the action's arguments.
argC: The length of the argument array.
Return:-
Returns true if this function pauses the
thread's execution. Otherwise returns false.
--------------------------------------------------*/
bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_TagWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_PolyWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_LineSide(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_ClearLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_PlayerCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_GameType(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_GameSpeed(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_SectorSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_AmbientSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_SetLineTexture(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_SetLineSpecial(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_ThingSound(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_EndPrintBold(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
bool ACS_CF_EndLog(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC);
#endif // __K_ACS__

View file

@ -98,7 +98,7 @@
#include "k_brightmap.h" #include "k_brightmap.h"
#include "k_director.h" // K_InitDirector #include "k_director.h" // K_InitDirector
#include "k_specialstage.h" #include "k_specialstage.h"
#include "k_acs.h" #include "acs/interface.h"
// Replay names have time // Replay names have time
#if !defined (UNDER_CE) #if !defined (UNDER_CE)

View file

@ -46,7 +46,7 @@
#include "console.h" // CON_LogMessage #include "console.h" // CON_LogMessage
#include "k_respawn.h" #include "k_respawn.h"
#include "k_terrain.h" #include "k_terrain.h"
#include "k_acs.h" #include "acs/interface.h"
#ifdef HW3SOUND #ifdef HW3SOUND
#include "hardware/hw3sound.h" #include "hardware/hw3sound.h"

View file

@ -559,6 +559,21 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);
void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing); void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing);
//
// Special activation info
//
struct activator_t
{
mobj_t *mo;
line_t *line;
UINT8 side;
sector_t *sector;
polyobj_t *po;
boolean fromLineSpecial; // Backwards compat for ACS
};
void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
void P_SetupSignExit(player_t *player); void P_SetupSignExit(player_t *player);
boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec); boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec);

View file

@ -40,7 +40,7 @@
#include "k_waypoint.h" #include "k_waypoint.h"
#include "k_director.h" #include "k_director.h"
#include "k_specialstage.h" #include "k_specialstage.h"
#include "k_acs.h" #include "acs/interface.h"
tic_t leveltime; tic_t leveltime;

View file

@ -295,6 +295,7 @@ TYPEDEF (disappear_t);
TYPEDEF (fade_t); TYPEDEF (fade_t);
TYPEDEF (fadecolormap_t); TYPEDEF (fadecolormap_t);
TYPEDEF (planedisplace_t); TYPEDEF (planedisplace_t);
TYPEDEF (activator_t);
// r_data.h // r_data.h
TYPEDEF (lumplist_t); TYPEDEF (lumplist_t);

View file

@ -634,7 +634,6 @@ if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
add_library(acsvm "${SRB2_INTERNAL_LIBRARY_TYPE}" ${acsvm_SOURCES}) add_library(acsvm "${SRB2_INTERNAL_LIBRARY_TYPE}" ${acsvm_SOURCES})
target_compile_features(acsvm PRIVATE cxx_std_11) target_compile_features(acsvm PRIVATE cxx_std_11)
#target_compile_definitions(ACSVM_SHARED="${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
target_include_directories(acsvm INTERFACE "${acsvm_SOURCE_DIR}") target_include_directories(acsvm INTERFACE "${acsvm_SOURCE_DIR}")