mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
369 lines
11 KiB
C++
369 lines
11 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 environment.cpp
|
|
/// \brief Action Code Script: Environment definition
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#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>
|
|
|
|
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 "../p_local.h"
|
|
}
|
|
|
|
#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)});
|
|
|
|
addCodeDataACS0(119, {"", 0, addCallFunc(CallFunc_PlayerTeam)});
|
|
addCodeDataACS0(120, {"", 0, addCallFunc(CallFunc_PlayerRings)});
|
|
|
|
addCodeDataACS0(122, {"", 0, addCallFunc(CallFunc_PlayerScore)});
|
|
|
|
// 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
|
|
|
|
// Now for new style functions.
|
|
// This style is preferred for added functions
|
|
// that aren't mimicing one from Hexen's or ZDoom's
|
|
// ACS implementations.
|
|
//addFuncDataACS0( 1, addCallFunc(CallFunc_GetLineUDMFInt));
|
|
//addFuncDataACS0( 2, addCallFunc(CallFunc_GetLineUDMFFixed));
|
|
//addFuncDataACS0( 3, addCallFunc(CallFunc_GetThingUDMFInt));
|
|
//addFuncDataACS0( 4, addCallFunc(CallFunc_GetThingUDMFFixed));
|
|
//addFuncDataACS0( 5, addCallFunc(CallFunc_GetSectorUDMFInt));
|
|
//addFuncDataACS0( 6, addCallFunc(CallFunc_GetSectorUDMFFixed));
|
|
//addFuncDataACS0( 7, addCallFunc(CallFunc_GetSideUDMFInt));
|
|
//addFuncDataACS0( 8, addCallFunc(CallFunc_GetSideUDMFFixed));
|
|
|
|
addFuncDataACS0( 100, addCallFunc(CallFunc_strcmp));
|
|
addFuncDataACS0( 101, addCallFunc(CallFunc_strcasecmp));
|
|
|
|
addFuncDataACS0( 300, addCallFunc(CallFunc_CountEnemies));
|
|
addFuncDataACS0( 301, addCallFunc(CallFunc_CountPushables));
|
|
addFuncDataACS0( 302, addCallFunc(CallFunc_HaveUnlockableTrigger));
|
|
addFuncDataACS0( 303, addCallFunc(CallFunc_HaveUnlockable));
|
|
addFuncDataACS0( 304, addCallFunc(CallFunc_PlayerSkin));
|
|
addFuncDataACS0( 305, addCallFunc(CallFunc_GetObjectDye));
|
|
addFuncDataACS0( 306, addCallFunc(CallFunc_PlayerEmeralds));
|
|
addFuncDataACS0( 307, addCallFunc(CallFunc_PlayerLap));
|
|
addFuncDataACS0( 308, addCallFunc(CallFunc_LowestLap));
|
|
addFuncDataACS0( 309, addCallFunc(CallFunc_EncoreMode));
|
|
addFuncDataACS0( 310, addCallFunc(CallFunc_BreakTheCapsules));
|
|
|
|
addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait));
|
|
addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition));
|
|
addFuncDataACS0( 502, addCallFunc(CallFunc_PodiumFinish));
|
|
addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle));
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
vres_Free(vRes);
|
|
}
|
|
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 = §ors[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);
|
|
}
|
|
|
|
case ACS_TAGTYPE_CAMERA:
|
|
{
|
|
const mobj_t *camera = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, tag);
|
|
if (camera == nullptr || camera->spawnpoint == nullptr)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return (camera->tracer == nullptr || P_MobjWasRemoved(camera->tracer) == true);
|
|
}
|
|
}
|
|
|
|
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]()
|
|
{
|
|
P_SetTarget(&activator->mo, NULL);
|
|
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;
|
|
}
|