diff --git a/extras/gdcc/rrspecial.acs b/extras/gdcc/rrspecial.acs index e4de1c9c4..6a1e23e09 100644 --- a/extras/gdcc/rrspecial.acs +++ b/extras/gdcc/rrspecial.acs @@ -205,7 +205,7 @@ special void { 55, 56}:Delay(int), int { 57, 58}:Random(int, int), fixed { 57, 58}:RandomFixed(fixed, fixed), - int { 59, 60}:ThingCount(int, int), + int { 59, 60}:ThingCount(str, int), void { 61, 62}:TagWait(int), void { 63, 64}:PolyWait(int), void { 65, 66}:ChangeFloor(int, str), diff --git a/src/k_acs-func.c b/src/k_acs-func.c index 4fc18d581..7bdfea482 100644 --- a/src/k_acs-func.c +++ b/src/k_acs-func.c @@ -27,6 +27,93 @@ #include "r_state.h" #include "p_polyobj.h" #include "taglist.h" +#include "p_local.h" +#include "deh_tables.h" +#include "fastcmp.h" + +/*-------------------------------------------------- + static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type) + + Helper function for ACS_CF_ThingCount. Gets + an object type from a string. + + Input Arguments:- + word: The mobj class string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetMobjTypeFromString(const char *word, mobjtype_t *type) +{ + mobjtype_t i; + + if (fastncmp("MT_", word, 3)) + { + // take off the MT_ + word += 3; + } + + for (i = 0; i < NUMMOBJFREESLOTS; i++) + { + if (!FREE_MOBJS[i]) + { + break; + } + + if (fastcmp(word, FREE_MOBJS[i])) + { + *type = MT_FIRSTFREESLOT+i; + return true; + } + } + + for (i = 0; i < MT_FIRSTFREESLOT; i++) + { + if (fastcmp(word, MOBJTYPE_LIST[i]+3)) + { + *type = i; + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type) + + Helper function for ACS_CF_ThingCount. + Returns whenever or not to add this thing + to the thing count. + + Input Arguments:- + mobj: The mobj we want to count. + type: Type exclusion. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type) +{ + if (type == MT_NULL || mobj->type == type) + { + // Don't count dead monsters + if (mobj->info->spawnhealth > 0 && mobj->health <= 0) + { + // Note: Hexen checks for COUNTKILL. + // SRB2 does not have an equivalent, so I'm checking + // spawnhealth. Feel free to replace this condition + // with literally anything else. + return false; + } + + // Count this object. + return true; + } + + return false; +} /*-------------------------------------------------- bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) @@ -40,13 +127,92 @@ bool ACS_CF_Random(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC (void)argC; - low = (INT32)argV[0]; - high = (INT32)argV[1]; + low = argV[0]; + high = argV[1]; ACSVM_Thread_DataStk_Push(thread, P_RandomRange(PR_ACS, low, high)); return false; } +/*-------------------------------------------------- + bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) + + Counts the number of things of a particular + type and tid. Both fields are optional; + no type means indescriminate against type, + no tid means search thru all thinkers. +--------------------------------------------------*/ +bool ACS_CF_ThingCount(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) +{ + ACSVM_MapScope *map = NULL; + ACSVM_String *str = NULL; + const char *className = NULL; + size_t classLen = 0; + + mobjtype_t type = MT_NULL; + mtag_t tid = 0; + + size_t count = 0; + + (void)argC; + + map = ACSVM_Thread_GetScopeMap(thread); + str = ACSVM_MapScope_GetString(map, argV[0]); + + className = ACSVM_String_GetStr(str); + classLen = ACSVM_String_GetLen(str); + + if (classLen > 0) + { + bool success = ACS_GetMobjTypeFromString(className, &type); + + if (success == false) + { + // Exit early. + + CONS_Alert(CONS_WARNING, + "Couldn't find object type \"%s\" for ThingCount.\n", + className + ); + + return false; + } + } + + tid = argV[1]; + + if (tid != 0) + { + // TODO: Even in SRB2 next's UDMF, tag lists + // still aren't attached to mobj_t, only + // mapthing_t. Fix this. + } + else + { + // Search thinkers instead of tag lists. + thinker_t *th = NULL; + mobj_t *mobj = NULL; + + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + { + continue; + } + + mobj = (mobj_t *)th; + + if (ACS_CountThing(mobj, type) == true) + { + ++count; + } + } + } + + ACSVM_Thread_DataStk_Push(thread, count); + return false; +} + /*-------------------------------------------------- bool ACS_CF_TagWait(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word argC) @@ -99,7 +265,7 @@ bool ACS_CF_ChangeFloor(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Word const char *texName = NULL; INT32 secnum = -1; - INT32 tag = 0; + mtag_t tag = 0; (void)argC; @@ -130,7 +296,7 @@ bool ACS_CF_ChangeCeiling(ACSVM_Thread *thread, ACSVM_Word const *argV, ACSVM_Wo const char *texName = NULL; INT32 secnum = -1; - INT32 tag = 0; + mtag_t tag = 0; (void)argC; diff --git a/src/k_acs.c b/src/k_acs.c index def8a5db6..e06ddf824 100644 --- a/src/k_acs.c +++ b/src/k_acs.c @@ -202,7 +202,12 @@ static void ACS_EnvConstruct(ACSVM_Environment *env) // Not that we're adding any modules to it, though. :p ACSVM_GlobalScope_SetActive(global, true); - // Add the data & function pointers + // 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 @@ -211,7 +216,8 @@ static void ACS_EnvConstruct(ACSVM_Environment *env) // 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); diff --git a/src/k_acs.h b/src/k_acs.h index cf9ad0555..1e88d9fb1 100644 --- a/src/k_acs.h +++ b/src/k_acs.h @@ -127,6 +127,7 @@ void ACS_Tick(void); --------------------------------------------------*/ 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);