From cb8dddbedcca7704a038c31143862d90d73cb7c9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Oct 2022 03:23:17 -0400 Subject: [PATCH] ACS basic implementation - BEHAVIOR lumps successfully load & run from maps. Currently they do not get unloaded between maps, though. - Print and Timer are the only implemented CallFuncs. All of the base language functions (Delay, etc) are already implemented by the VM though. - ACS compiler files are included, for use with GDCC. (Picked instead of ACC because it's less ZDoom-centric) - Additionally, also added the configs for Zone Builder to be able to compile it in editor. Syntax highlighting is very incomplete atm. --- extras/conf/RingRacers_ACS.cfg | 116 +++++++++++ extras/conf/gdcc.cfg | 16 ++ extras/gdcc/rrcommon.acs | 23 ++ extras/gdcc/rrdefs.acs | 41 ++++ extras/gdcc/rrspecial.acs | 324 +++++++++++++++++++++++++++++ libs/ACSVM/include/CAPI/Module.cpp | 6 +- libs/ACSVM/include/CAPI/Module.h | 2 +- src/Sourcefile | 2 + src/console.c | 1 + src/console.h | 1 + src/d_main.c | 5 + src/k_acs-func.c | 51 +++++ src/k_acs.c | 279 +++++++++++++++++++++++-- src/k_acs.h | 23 +- src/m_perfstats.c | 5 +- src/m_perfstats.h | 1 + src/p_setup.c | 7 + src/p_tick.c | 5 + 18 files changed, 882 insertions(+), 26 deletions(-) create mode 100644 extras/conf/RingRacers_ACS.cfg create mode 100644 extras/conf/gdcc.cfg create mode 100644 extras/gdcc/rrcommon.acs create mode 100644 extras/gdcc/rrdefs.acs create mode 100644 extras/gdcc/rrspecial.acs create mode 100644 src/k_acs-func.c diff --git a/extras/conf/RingRacers_ACS.cfg b/extras/conf/RingRacers_ACS.cfg new file mode 100644 index 000000000..fe2d177d2 --- /dev/null +++ b/extras/conf/RingRacers_ACS.cfg @@ -0,0 +1,116 @@ +/*******************************************************************\ + Doom Builder Script highlighting definitions for ACS +\*******************************************************************/ + +// Compiler settings +compiler = "ringracers_gdcc"; +parameters = "-I \"%PT\" -I \"%PS\" %FI %FO"; +resultlump = "BEHAVIOR"; + +// Editor settings +description = "Ring Racers ACS script"; +codepage = 0; +extensions = "acs"; +casesensitive = false; +insertcase = 0; // 0=Normal, 1=Lowercase, 2=Uppercase +lexer = 35; // CPP-style, case-insensitive +functionopen = "("; +functionclose = ")"; +codeblockopen = "{"; +codeblockclose = "}"; +arrayopen = "["; +arrayclose = "]"; +argumentdelimiter = ","; +terminator = ";"; +extrawordchars = "#"; // Extra characters to be treated as a part of a word by the Script Editor +//keywordhelp = "http://www.zdoom.org/wiki/index.php?title=%K"; +snippetsdir = "acs"; +scripttype = 1; //0 = unknown script, 1 = acc, 2 = modeldef, 3 = decorate + +keywords +{ + #define = "#Define identifier expression"; + #encryptstrings = "#EncryptStrings"; + #import = "#Import"; + #include = "#Include"; + #libdefine = "#LibDefine identifier expression"; + #library = "#Library"; + #NoCompact = "#NoCompact"; + #NoWadAuthor = "#NoWadAuthor"; + #WadAuthor = "#WadAuthor"; + #region = "#region block"; + #endregion = "end of #region block"; + ACS_Execute = "ACS_Execute(int script, int arg1, int arg2, int arg3)"; + ACS_ExecuteAlways = "ACS_ExecuteAlways(int script, int arg1, int arg2, int arg3)"; + ACS_ExecuteWait = "ACS_ExecuteWait(int script, int arg1, int arg2, int arg3)"; + ACS_ExecuteWithResult = "ACS_ExecuteWithResult(int script, int arg1, int arg2, int arg3)"; + ACS_Suspend = "ACS_Suspend(int script)"; + ACS_Terminate = "ACS_Terminate(int script)"; + ACS_NamedExecute = "ACS_NamedExecute(str script, int arg1, int arg2, int arg3)"; + ACS_NamedExecuteAlways = "ACS_NamedExecuteAlways(str script, int arg1, int arg2, int arg3)"; + ACS_NamedExecuteWait = "ACS_NamedExecuteWait(str script, int arg1, int arg2, int arg3)"; + ACS_NamedExecuteWithResult = "ACS_ExecuteWithResult(str script, int arg1, int arg2, int arg3)"; + ACS_NamedSuspend = "ACS_NamedSuspend(str script)"; + ACS_NamedTerminate = "ACS_NamedTerminate(str script)"; + ActivatorSound = "ActivatorSound(str name, int volume)"; + ActivatorTID = "ActivatorTID()"; + Bool = "Bool expression"; + Break = "Break"; + Return = "Return"; + Case = "Case expression:"; + Const = "Const"; + Continue = "Continue"; + Death = "Script expression Death"; + Default = "Default:"; + Delay = "void Delay(int tics)"; + Disconnect = "Script expression Disconnect"; + Do = "Do"; + Else = "Else"; + Enter = "Script expression Enter"; + For = "For(initialization, condition, iteration)"; + Function = "Function Void expression (Void)"; + If = "if(expression)"; + Int = "int expression"; + Open = "Script expression Open"; + PolyWait = "void PolyWait(int polyid)"; + Print = "void Print(type:expression)\nPrint will print something to the screen.\nPrint will only display for the activator of the script\nFor printing to all players, use PrintBold."; + PrintBold = "void PrintBold(type:expression)\nThis is exactly the same as Print, except all players will see the printed text\non the screen instead of just the activator of the script."; + Random = "int Random(int min, int max)"; + Restart = "Restart"; + Script = "Script expression"; + ScriptWait = "void ScriptWait(int script)"; + Special = "Special"; + str = "str expression"; + Suspend = "Suspend"; + Switch = "Switch(expression)"; + TagWait = "void TagWait(int tag)"; + Terminate = "Terminate"; + Timer = "int Timer(void)"; + Unloading = "Script expression Unloading"; + Until = "Until(expression)"; + Void = "Void"; + While = "While(expression)"; + World = "World Int expression:identifier"; +} + +constants +{ + TRUE; + FALSE; + ON; + OFF; + YES; + NO; + LINE_FRONT; + LINE_BACK; + SIDE_FRONT; + SIDE_BACK; + TEXTURE_TOP; + TEXTURE_MIDDLE; + TEXTURE_BOTTOM; + GAMETYPE_RACE; + GAMETYPE_BATTLE; + GAMESPEED_EASY; + GAMESPEED_NORMAL; + GAMESPEED_HARD; +} diff --git a/extras/conf/gdcc.cfg b/extras/conf/gdcc.cfg new file mode 100644 index 000000000..17bb7246c --- /dev/null +++ b/extras/conf/gdcc.cfg @@ -0,0 +1,16 @@ + +compilers +{ + // This defines what files a compiler uses + // The setting named "program" defines what .exe to run + // The "interface" setting defines what interal interface to use for processing and error feedback + // All others are the required files (the setting names do not matter) + ringracers_gdcc + { + interface = "AccCompiler"; + program = "gdcc-acc.exe"; + zcommon = "rrcommon.acs"; + zdefs = "rrdefs.acs"; + zspecial = "rrspecial.acs"; + } +} diff --git a/extras/gdcc/rrcommon.acs b/extras/gdcc/rrcommon.acs new file mode 100644 index 000000000..8261b73bd --- /dev/null +++ b/extras/gdcc/rrcommon.acs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2015 David Hill +// Copyright(C) 2022 Sally Cochenour +// +// See COPYLIB for license information. +// +//----------------------------------------------------------------------------- +// +// Header for target library libacs. +// +// Defines for Dr. Robotnik's Ring Racers' ACS. +// +//----------------------------------------------------------------------------- + +#ifndef __GDCC_Header__ACS__rrcommon_acs__ +#define __GDCC_Header__ACS__rrcommon_acs__ + +#include "rrspecial.acs" +#include "rrdefs.acs" + +#endif//__GDCC_Header__ACS__rrcommon_acs__ + diff --git a/extras/gdcc/rrdefs.acs b/extras/gdcc/rrdefs.acs new file mode 100644 index 000000000..c6a3707d4 --- /dev/null +++ b/extras/gdcc/rrdefs.acs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// +// Header for target library libacs. +// +// Defines for Dr. Robotnik's Ring Racers' ACS. +// +//----------------------------------------------------------------------------- + +#ifndef __GDCC_Header__ACS__rrdefs_acs__ +#define __GDCC_Header__ACS__rrdefs_acs__ + +//----------------------------------------------------------------------------| +// Macros | +// + +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 +#define YES 1 +#define NO 0 + +#define LINE_FRONT 0 +#define LINE_BACK 1 + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 + +#define TEXTURE_TOP 0 +#define TEXTURE_MIDDLE 1 +#define TEXTURE_BOTTOM 2 + +#define GAMETYPE_RACE 0 +#define GAMETYPE_BATTLE 1 + +#define GAMESPEED_EASY 0 +#define GAMESPEED_NORMAL 1 +#define GAMESPEED_HARD 2 + +#endif//__GDCC_Header__ACS__rrdefs_acs__ + diff --git a/extras/gdcc/rrspecial.acs b/extras/gdcc/rrspecial.acs new file mode 100644 index 000000000..e4de1c9c4 --- /dev/null +++ b/extras/gdcc/rrspecial.acs @@ -0,0 +1,324 @@ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2015-2017 David Hill +// Copyright(C) 2022 Sally Cochenour +// +// See COPYLIB for license information. +// +//----------------------------------------------------------------------------- +// +// Header for target library libacs. +// +// Defines for Dr. Robotnik's Ring Racers' ACS. +// +//----------------------------------------------------------------------------- + +#ifndef __GDCC_Header__ACS__rrspecial_acs__ +#define __GDCC_Header__ACS__rrspecial_acs__ + + +//----------------------------------------------------------------------------| +// Macros | +// + +#define ACS_ExecuteWait(num, ...) \ + do \ + { \ + int __execute_wait_num = (num); \ + ACS_Execute(__execute_wait_num, __VA_ARGS__); \ + ScriptWait(__execute_wait_num); \ + } \ + while(0) + +#define ACS_NamedExecuteWait(name, ...) \ + do \ + { \ + str __execute_wait_name = (name); \ + ACS_NamedExecute(__execute_wait_name, __VA_ARGS__); \ + NamedScriptWait(__execute_wait_name); \ + } \ + while(0) + +#define ACS_PrintPropertyBase() \ + (char): PrintChar, \ + (str): PrintString, \ + \ + a(global): PrintGlobalCharArray, \ + a(global): PrintGlobalCharRange, \ + a(local): PrintLocalCharArray, \ + a(local): PrintLocalCharRange, \ + a(module): PrintModuleCharArray, \ + a(module): PrintModuleCharRange, \ + a(world): PrintWorldCharArray, \ + a(world): PrintWorldCharRange, \ + \ + b: PrintBinary, \ + c: PrintChar, \ + d: PrintInt, \ + f: PrintFixed, \ + i: PrintInt, \ + s: PrintString, \ + x: PrintHex + +#pragma state save +#pragma define raw ON +#define suspend do (__suspend()); while(0) +#pragma state restore + + +//----------------------------------------------------------------------------| +// Functions | +// + +// [type] [ID]:[function name]([required args], [optional args]) + +// Currently just implements linedef executors. +// Ideally would implement as many as possible. + +special + int 400:Floor_SetHeightTexture(2,3), // tag, height[, texture] + int 401:Ceiling_SetHeightTexture(2,3), // tag, height[, texture] + int 402:Light_ChangeToValue(2), // tag, value + int 403:Floor_Move(3,5), // tag, height, speed[, texture, script] + int 404:Ceiling_Move(3,5), // tag, height, speed[, texture, script] + int 405:Floor_MoveByOffset(3,4), // tag, offset, speed[, instant] + + int 407:Ceiling_MoveByOffset(3,4), // tag, offset, speed[, instant] + + int 409:Sector_ChangeTag(2), // old tag, new tag + int 410:Line_ChangeFrontSectorTag(2), // line tag, new tag + + int 411:Sector_StopMovement(1), // tag + int 412:Thing_Teleport(2, 5), // thing tag, sector tag[, silent, keep angle, keep speed]. Relative teleport is now a separate function. + + // TODO: split all these damn features up into separate functions, instead of optional variables + int 413:Level_SetMusic(1, 12), // name[, track, loop, local, reload reset, force reset, position, position is offset, fade out time, fade in time, fade out vol, fade in vol] + + int 414:Thing_PlaySound(1, 3), // name[, local, origin enum]. Doing it from a sector is now a separate function. +// int 415:Console_Execute(1), // script id + int 416:Light_Flicker(2, 4), // tag, value a[, value b, frequency] + int 417:Light_Pulse(2, 4), // tag, value a[, value b, frequency] + int 418:Light_BlinkUnsynced(4, 5), // tag, low time, hi time, value a[, value b] + int 419:Light_Blink(4, 5), // tag, low time, hi time, value a[, value b] + int 420:Light_Fade(3, 5), // tag, value, speed[, speed is tics, no interrupt] + int 421:Light_Stop(1), // tag + int 422:Player_CutAwayView(2, 3), // tid, tics[, pitch] + int 423:Level_SetSky(1, 2), // texture[, global] + int 424:Level_SetWeather(1, 2), // effect[, global] + int 425:Thing_SetState(1), // string + int 426:Thing_Stop(0, 1), // [sector] + int 427:Player_AddScore(1), // amount + int 428:FOF_StartMovement(8, 9), // tag, bottom low, bottom hi, top low, top hi, speed, start delay, swap delay[, invert] + int 429:Ceiling_Crush(2, 3), // tag, speed[, constant] + int 430:Floor_Crush(2, 3), // tag, speed[, constant] + int 431:Sector_Crush(2, 3), // tag, speed[, constant] +// int 432:Thing_Set2D(1), // bool + int 433:Thing_SetFlip(1), // bool +// int 434:Player_CustomPower(?), + int 435:Scroll_Change(3), // tag, dir, speed + int 436:FOF_Shatter(2), // target, control + int 437:Player_DisableControl(1, 2), // time[, allowjump] + int 438:Thing_SetScale(1), // size + int 439:Line_CopyTextures(1, 2), // tag[, existing] +// int 440:Level_StartMetalSonicRace(0), + int 441:SetUnlockableTrigger(1), // ID + int 442:Sector_NextThingState(2, 3), // tag, type[, state] + int 443:Lua_Execute(1), // script + int 444:Earthquake(1, 2), // tics[, intensity] + int 445:FOF_SetExists(2, 3), // target, control[, exists] + int 446:FOF_Crumble(2, 3), // target, control[, options] + int 447:Sector_SetColormap(2, 8), // tag, light[, fade, extra, flags, force light, force fade, force extra] + int 448:SetSkyboxViewpoint(1, 2), // viewpoint ID[, global]. Center point is now a separate function + int 449:SetBossActive(1, 2), // boss ID[, active?] + int 450:ACS_ExecuteAlways(1,4), // script[, arg1, arg2, arg3] + int 451:ACS_ExecuteRandomAlways(2,5), // script low, script high[, arg1, arg2, arg3] + int 452:FOF_SetAlpha(3, 5), // target, control, alpha[, add, update flags] + int 453:FOF_Fade(4, 5), // target, control, alpha, time[, options] + int 454:FOF_StopFade(2, 3), // target, control[, interrupt] +// int 455:Sector_FadeColormap(?), // TODO + int 456:Sector_StopColormapFade(1), // tag + int 457:Thing_StartTracking(5), // tag, tolerance, time, script, continue + int 458:Thing_StopTracking(0), + int 459:Prompt_Open(1, 3), // prompt[, allow control, close script]. Closing is now a separate function. + int 460:Player_AddRings(1, 2), // amount[, freq] + int 461:Thing_Spawn(4, 5), // type, x, y, z[, angle]. Randomized coordinates can be handled by code. + int 462:Level_Stopwatch(0), + int 463:Thing_Dye(1), // color +// int 464:TriggerEggCapsule(1, 2), // capsule ID[, end level] + + int 466:Level_SetFailed(0, 1), // [success?] + + int 480:Polyobj_DoorSlide(5), // po, speed, angle, dist, delay + int 481:Polyobj_DoorSwing(4), // po, speed, angle, delay + int 482:Polyobj_Move(4), // po, speed, angle, dist + int 483:Polyobj_OR_Move(4), // po, speed, angle, dist + int 484:Polyobj_RotateRight(3), // po, speed, angle + int 485:Polyobj_OR_RotateRight(3), // po, speed, angle + int 486:Polyobj_RotateLeft(3), // po, speed, angle + int 487:Polyobj_OR_RotateLeft(3), // po, speed, angle + int 488:Polyobj_MoveByWaypoints(4), // po, speed, sequence[, options] + int 489:Polyobj_InvisibleIntangible(1, 2), // po[, just visibility] + int 490:Polyobj_VisibleTangible(1, 2), // po[, just visibility] + int 491:Polyobj_SetAlpha(2, 3), // po, alpha[, add] + int 492:Polyobj_FadeAlpha(2, 3), // po, alpha, speed[, options] + + int 499:Sector_ToggleWaypoints(0, 1), // [enable?] + + // internal functions have negative values + int -1:GetLineUDMFInt(2, int, str), + fixed -2:GetLineUDMFFixed(2, int, str), + int -3:GetThingUDMFInt(2, int, str), + fixed -4:GetThingUDMFFixed(2, int, str), + int -5:GetSectorUDMFInt(2, int, str), + fixed -6:GetSectorUDMFFixed(2, int, str), + int -7:GetSideUDMFInt(3, int, int, str), + fixed -8:GetSideUDMFFixed(3, int, int, str), + int -9:ACS_Execute(1, int, int, int, int), + int -10:ACS_Suspend(1, int), + int -11:ACS_Terminate(1, int), + int -12:ACS_ExecuteWithResult(1, int, int, int, int), + int -13:ACS_NamedExecute(1, str, int, int, int), + int -14:ACS_NamedSuspend(1, str), + int -15:ACS_NamedTerminate(1, str), + int -16:ACS_NamedExecuteWithResult(1, str, int, int, int), + int -17:ACS_NamedExecuteAlways(1, str, int, int, int), + int -18:UniqueTID(0, int, int), + int -19:IsTIDUsed(1, int), + int -20:Sqrt(1, int), + fixed -21:FixedSqrt(1, fixed), + fixed -22:VectorLength(2, fixed, fixed), + int -23:StrCmp(2, str, str, int), + int -24:StrICmp(2, str, str, int), + int -24:StrCaseCmp(2, str, str, int), + str -25:StrLeft(2, str, int), + str -25:StrRight(2, str, int), + str -26:StrMid(3, str, int, int), + int -27:GetChar(2, str, int), + int -28:StrArg(1, str), + fixed -29:Floor(1, fixed), + fixed -30:Round(1, fixed), + fixed -31:Ceil(1, fixed), + + void { 2 }:__suspend(void), + + // 0 to 56: Implemented by ACSVM + void { 55, 56}:Delay(int), + int { 57, 58}:Random(int, int), + fixed { 57, 58}:RandomFixed(fixed, fixed), + int { 59, 60}:ThingCount(int, int), + void { 61, 62}:TagWait(int), + void { 63, 64}:PolyWait(int), + void { 65, 66}:ChangeFloor(int, str), + void { 67, 68}:ChangeCeiling(int, str), + // 69 to 79: Implemented by ACSVM + int { 80 }:LineSide(void), + void { 81, 82}:ScriptWait(int), // 81 to 82: Implemented by ACSVM + void { 83 }:ClearLineSpecial(void), + // 84 to 85: Implemented by ACSVM + void { 85 }:BeginPrint(void), + void { 85 }:BeginPrintBold(void), + void { 86 }:EndPrint(void), + void { 87 }:PrintString(str), // 87 to 89: Implemented by ACSVM + void { 88 }:PrintInt(int), + void { 88 }:PrintNumber(int), + void { 89 }:PrintChar(int), + int { 90 }:PlayerCount(void), + int { 91 }:GameType(void), + int { 92 }:GameSpeed(void), + int { 93 }:Timer(void), + void { 94 }:SectorSound(str, int), + void { 95 }:AmbientSound(str, int), + + void { 97 }:SetLineTexture(int, int, int, str), + void { 98 }:SetLineBlocking(int, int), + void { 99 }:SetLineSpecial(int, int, int, int, int, int, int), + void {100 }:ThingSound(int, str, int), + void {101 }:EndPrintBold(void), + + int {118 }:IsMultiplayer(void), + int {118 }:IsNetworkGame(void), + + int {135 }:SinglePlayer(void), + int {136 }:FixedMul(int, int), // 136 to 137: Implemented by ACSVM + int {137 }:FixedDiv(int, int), + + void {157 }:PrintFixed(fixed), // 157: Implemented by ACSVM + + // 167 to 173: Implemented by ACSVM + + // 175 to 179: Implemented by ACSVM + + // 181 to 189: Implemented by ACSVM + + // 203 to 217: Implemented by ACSVM + + fixed {220 }:Sin(fixed), + fixed {221 }:Cos(fixed), + fixed {222 }:VectorAngle(fixed, fixed), + + // 225 to 243: Implemented by ACSVM + + int {247 }:PlayerNumber(void), + int {248 }:ActivatorTID(void), + + int {253 }:StrLen(str), // 253: Implemented by ACSVM + + // 256 to 257: Implemented by ACSVM + + // 263: Implemented by ACSVM + + int {267 }:PlayerInGame(int), + int {268 }:PlayerIsBot(int), + + void {349 }:PrintBinary(int), + void {350 }:PrintHex(int), + + void {273 }:PrintModuleCharArray(int, int), // 273 to 275: Implemented by ACSVM + void {274 }:PrintWorldCharArray(int, int), + void {275 }:PrintGlobalCharArray(int, int), + + // 291 to 325: Implemented by ACSVM + + // 330: Implemented by ACSVM + + // 349 to 361: Implemented by ACSVM + + str {352 }:EndStrParam(void), + void {353 }:PrintModuleCharRange(int, int, int, int), + void {354 }:PrintWorldCharRange(int, int, int, int), + void {355 }:PrintGlobalCharRange(int, int, int, int), + int {356 }:StrCpyToModuleCharRange(int, int, int, int, str, int), + int {357 }:StrCpyToWorldCharRange(int, int, int, int, str, int), + int {358 }:StrCpyToGlobalCharRange(int, int, int, int, str, int), + + void {361 }:NamedScriptWait(str), + + // 363 to 380: Implemented by ACSVM + + void {378 }:PrintLocalCharArray(int, int), + void {379 }:PrintLocalCharRange(int, int, int, int), + int {380 }:StrCpyToLocalCharRange(int, int, int, int, str, int); + +print __Print +( + (begin): BeginPrint, + (end): EndPrint, + + ACS_PrintPropertyBase() +); + +print __PrintBold +( + (begin): BeginPrintBold, + (end): EndPrintBold, + + ACS_PrintPropertyBase() +); + +print PrintRaw +( + ACS_PrintPropertyBase() +); + +#endif//__GDCC_Header__ACS__rrspecial_acs__ + diff --git a/libs/ACSVM/include/CAPI/Module.cpp b/libs/ACSVM/include/CAPI/Module.cpp index a270ec3ab..8f427cd2e 100644 --- a/libs/ACSVM/include/CAPI/Module.cpp +++ b/libs/ACSVM/include/CAPI/Module.cpp @@ -27,11 +27,13 @@ extern "C" // // ACSVM_Module_GetName // -ACSVM_ModuleName ACSVM_Module_GetName(ACSVM_Module const *module_) +void ACSVM_Module_GetName(ACSVM_Module const *module_, ACSVM_ModuleName *out) { auto module = reinterpret_cast(module_); - return {reinterpret_cast(module->name.s), module->name.p, module->name.i}; + out->s = reinterpret_cast(module->name.s); + out->p = module->name.p; + out->i = module->name.i; } // diff --git a/libs/ACSVM/include/CAPI/Module.h b/libs/ACSVM/include/CAPI/Module.h index 618cd49bb..577ef1810 100644 --- a/libs/ACSVM/include/CAPI/Module.h +++ b/libs/ACSVM/include/CAPI/Module.h @@ -45,7 +45,7 @@ struct ACSVM_ModuleName // Extern Functions | // -ACSVM_ModuleName ACSVM_Module_GetName(ACSVM_Module const *module); +void ACSVM_Module_GetName(ACSVM_Module const *module, ACSVM_ModuleName *out); // Returns false if reading fails. bool ACSVM_Module_ReadBytecode(ACSVM_Module *module, ACSVM_Byte const *data, size_t size); diff --git a/src/Sourcefile b/src/Sourcefile index 008200473..f0efd658e 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -125,3 +125,5 @@ k_director.c k_follower.c k_profiles.c k_acs.c +k_acs-func.c + diff --git a/src/console.c b/src/console.c index ede2f633a..1d906af71 100644 --- a/src/console.c +++ b/src/console.c @@ -1863,6 +1863,7 @@ static const char *CON_LoadingStrings[LOADED_ALLDONE+1] = "Init rendering daemon...", //LOADED_RINIT "Init audio subsystem...", //LOADED_SINITSFXCHANNELS "Cache HUD...", //LOADED_STINIT + "Init ACSVM...", //LOADED_ACSINIT "Check game status...", //LOADED_DCHECKNETGAME "Now starting..." }; // see also con_loadprogress_t in console.h diff --git a/src/console.h b/src/console.h index 192596ab0..203698d5e 100644 --- a/src/console.h +++ b/src/console.h @@ -44,6 +44,7 @@ typedef enum LOADED_RINIT, LOADED_SINITSFXCHANNELS, LOADED_STINIT, + LOADED_ACSINIT, LOADED_DCHECKNETGAME, LOADED_ALLDONE = LOADED_DCHECKNETGAME, } con_loadprogress_t; diff --git a/src/d_main.c b/src/d_main.c index 5d83c783b..3da1b613a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -75,6 +75,7 @@ #include "k_boss.h" #include "doomstat.h" #include "m_random.h" // P_ClearRandom +#include "k_acs.h" #ifdef CMAKECONFIG #include "config.h" @@ -1612,6 +1613,10 @@ void D_SRB2Main(void) ST_Init(); CON_SetLoadingProgress(LOADED_STINIT); + CONS_Printf("ACS_Init(): Init Action Code Script VM.\n"); + ACS_Init(); + CON_SetLoadingProgress(LOADED_ACSINIT); + //------------------------------------------------ COMMAND LINE PARAMS // this must be done after loading gamedata, diff --git a/src/k_acs-func.c b/src/k_acs-func.c new file mode 100644 index 000000000..dfdc7a7c9 --- /dev/null +++ b/src/k_acs-func.c @@ -0,0 +1,51 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// 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-func.c +/// \brief ACS CallFunc definitions + +#include "k_acs.h" + +#include "doomtype.h" +#include "doomdef.h" +#include "d_think.h" +#include "p_mobj.h" +#include "p_tick.h" +#include "w_wad.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" + +bool ACS_CF_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) +{ + ACSVM_PrintBuf *buf = NULL; + + (void)argV; + (void)argC; + + buf = ACSVM_Thread_GetPrintBuf(thread); + CONS_Printf("%s\n", ACSVM_PrintBuf_GetData(buf)); + ACSVM_PrintBuf_Drop(buf); + + return false; +} + +bool ACS_CF_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) +{ + (void)argV; + (void)argC; + + ACSVM_Thread_DataStk_Push(thread, leveltime); + return false; +} diff --git a/src/k_acs.c b/src/k_acs.c index fc9d3ec4d..d667e8d3a 100644 --- a/src/k_acs.c +++ b/src/k_acs.c @@ -14,6 +14,10 @@ #include "doomtype.h" #include "doomdef.h" +#include "doomstat.h" +#include "z_zone.h" +#include "w_wad.h" +#include "i_system.h" #include "CAPI/BinaryIO.h" #include "CAPI/Environment.h" @@ -23,29 +27,264 @@ #include "CAPI/String.h" #include "CAPI/Thread.h" -static tic_t ExecTime = 0; +static ACSVM_Environment *ACSenv = NULL; -boolean K_ACS_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) +ACSVM_Environment *ACS_GetEnvironment(void) { - (void)thread; - (void)argV; - (void)argC; - -/* - ACSVM_PrintBuf *buf = ACSVM_Thread_GetPrintBuf(thread); - - CONS_Printf("%s\n", ACSVM_PrintBuf_GetData(buf)); - ACSVM_PrintBuf_Drop(buf); -*/ - - return false; + return ACSenv; } -boolean K_ACS_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) +ACSVM_GlobalScope *ACS_GetGlobal(void) { - (void)argV; - (void)argC; - - ACSVM_Thread_DataStk_Push(thread, ExecTime); - return false; + return ACSVM_Environment_GetGlobalScope(ACSenv, 0); +} + +ACSVM_HubScope *ACS_GetHub(void) +{ + ACSVM_GlobalScope *global = ACS_GetGlobal(); + + if (global == NULL) + { + return NULL; + } + + return ACSVM_GlobalScope_GetHubScope(global, 0); +} + +ACSVM_MapScope *ACS_GetMap(void) +{ + ACSVM_HubScope *hub = ACS_GetHub(); + + if (hub == NULL) + { + return NULL; + } + + return ACSVM_HubScope_GetMapScope(hub, 0); +} + +static void ACS_EnvBadAlloc(ACSVM_Environment *env, char const *what) +{ + (void)env; + I_Error("Error allocating memory for ACS (%s)\n", what); +} + +static void ACS_EnvReadError(ACSVM_Environment *env, char const *what) +{ + (void)env; + I_Error("Error reading ACS module (%s)\n", what); +} + +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. + ACSVM_GlobalScope_SetActive(global, true); + + // Add the data & function pointers + // See also: + // https://doomwiki.org/wiki/ACS0_instruction_set + // + // 0 to 56: Implemented by ACSVM + + // 69 to 79: Implemented by ACSVM + + // 81 to 82: Implemented by ACSVM + + // 84 to 85: Implemented by ACSVM + ACSVM_Environment_AddCodeDataACS0(env, 86, "", ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_EndPrint)); + // 87 to 89: Implemented by ACSVM + ACSVM_Environment_AddCodeDataACS0(env, 93, "", ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_Timer)); + // 136 to 137: Implemented by ACSVM + + // 157: Implemented by ACSVM + + // 167 to 173: Implemented by ACSVM + + // 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 + ACSVM_Environment_AddCodeDataACS0(env, 270, "", ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_EndPrint)); + // 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 bool ACS_EnvLoadModule(ACSVM_Environment *env, ACSVM_Module *module) +{ + ACSVM_ModuleName name = {0}; + const char *str = NULL; + + size_t lumpLen = 0; + + ACSVM_Byte *data = NULL; + size_t size = 0; + + bool ret = false; + + (void)env; + + ACSVM_Module_GetName(module, &name); + str = ACSVM_String_GetStr(name.s); + + 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; +} + +void ACS_Init(void) +{ + // Initialize ACS on engine start-up. + ACSVM_EnvironmentFuncs funcs = {0}; + + funcs.bad_alloc = ACS_EnvBadAlloc; + funcs.readError = ACS_EnvReadError; + funcs.ctor = ACS_EnvConstruct; + funcs.loadModule = ACS_EnvLoadModule; + + ACSenv = ACSVM_AllocEnvironment(&funcs, NULL); + + I_AddExitFunc(ACS_Shutdown); +} + +void ACS_Shutdown(void) +{ + // Delete ACS environment. + ACSVM_FreeEnvironment(ACSenv); + ACSenv = NULL; +} + +void ACS_LoadLevelScripts(size_t mapID) +{ + ACSVM_Environment *env = ACSenv; + ACSVM_StringTable *strTab = ACSVM_Environment_GetStringTable(env); + + ACSVM_HubScope *hub = NULL; + ACSVM_MapScope *map = NULL; + + ACSVM_Module **modules = NULL; + size_t modules_len = 0; + size_t modules_size = 4; + + // No hub support. Simply always reset it. + hub = ACS_GetHub(); + ACSVM_HubScope_SetActive(hub, true); + + // Start up map scope. + map = ACSVM_HubScope_GetMapScope(hub, 0); + ACSVM_MapScope_SetActive(map, true); + + // Allocate module list. + modules = Z_Calloc(modules_size * sizeof(ACSVM_Module *), PU_STATIC, NULL); + + // Insert BEHAVIOR lump. + { + 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); + } + + // Start OPEN scripts. + ACSVM_MapScope_ScriptStartType(map, 1, NULL, 0, NULL, NULL); +} + +void ACS_Tick(void) +{ + ACSVM_Environment *env = ACSenv; + + if (ACSVM_Environment_HasActiveThread(env) == false) + { + return; + } + + ACSVM_Environment_Exec(env); } diff --git a/src/k_acs.h b/src/k_acs.h index 5846f5c7a..8264c3652 100644 --- a/src/k_acs.h +++ b/src/k_acs.h @@ -24,7 +24,26 @@ #include "CAPI/String.h" #include "CAPI/Thread.h" -boolean K_ACS_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC); -boolean K_ACS_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC); +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_scriptType_e; + +ACSVM_Environment *ACS_GetEnvironment(void); + +ACSVM_GlobalScope *ACS_GetGlobal(void); +ACSVM_HubScope *ACS_GetHub(void); +ACSVM_MapScope *ACS_GetMap(void); + +void ACS_Init(void); +void ACS_Shutdown(void); +void ACS_LoadLevelScripts(size_t mapID); +void ACS_Tick(void); + +bool ACS_CF_EndPrint(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC); +bool ACS_CF_Timer(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC); #endif // __K_ACS__ diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 0be2eb805..330db5f1f 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -52,6 +52,7 @@ precise_t ps_botticcmd_time = 0; precise_t ps_thinkertime = 0; precise_t ps_thlist_times[NUM_THINKERLISTS]; +precise_t ps_acs_time = 0; int ps_checkposition_calls = 0; @@ -350,7 +351,8 @@ static void M_DrawTickStats(void) ps_tictime - ps_playerthink_time - ps_thinkertime - - ps_lua_thinkframe_time; + ps_lua_thinkframe_time - + ps_acs_time; perfstatrow_t tictime_row[] = { {"logic ", "Game logic: ", &ps_tictime}, @@ -374,6 +376,7 @@ static void M_DrawTickStats(void) perfstatrow_t extra_thinker_time_row[] = { {"lthinkf", "LUAh_ThinkFrame:", &ps_lua_thinkframe_time}, + {"acs ", "ACS_Tick: ", &ps_acs_time}, {"botcmd ", "Bot logic: ", &ps_botticcmd_time}, {"other ", "Other: ", &extratime}, {0} diff --git a/src/m_perfstats.h b/src/m_perfstats.h index 2c448031c..8371e1da7 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -32,6 +32,7 @@ extern precise_t ps_botticcmd_time; extern precise_t ps_thinkertime; extern precise_t ps_thlist_times[]; +extern precise_t ps_acs_time; extern int ps_checkposition_calls; diff --git a/src/p_setup.c b/src/p_setup.c index a4f21ee76..0d67b3753 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -98,6 +98,7 @@ #include "k_brightmap.h" #include "k_terrain.h" // TRF_TRIPWIRE #include "k_director.h" // K_InitDirector +#include "k_acs.h" // Replay names have time #if !defined (UNDER_CE) @@ -4252,6 +4253,12 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // clear special respawning que iquehead = iquetail = 0; + // Initialize ACS scripts + if (!fromnetsave) + { + ACS_LoadLevelScripts(gamemap-1); + } + // Remove the loading shit from the screen if (rendermode != render_none && !titlemapinaction && !reloadinggamestate) F_WipeColorFill(levelfadecol); diff --git a/src/p_tick.c b/src/p_tick.c index ad83abca6..7b9ca67a2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -36,6 +36,7 @@ #include "k_boss.h" #include "k_waypoint.h" #include "k_director.h" +#include "k_acs.h" tic_t leveltime; @@ -359,6 +360,10 @@ static inline void P_RunThinkers(void) if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) K_RunBattleOvertime(); + + ps_acs_time = I_GetPreciseTime(); + ACS_Tick(); + ps_acs_time = I_GetPreciseTime() - ps_acs_time; } //