From 258f4b6786d2da1306615582e5fb583d80f82ef3 Mon Sep 17 00:00:00 2001 From: Marisa Heit Date: Sun, 15 Jan 2023 22:58:38 -0600 Subject: [PATCH] Check all ACS functions for minimum number of arguments. Also fixes SpawnForced not respecting the minimum declared in zspecial.acs. (https://forum.zdoom.org/viewtopic.php?t=77027) --- src/playsim/p_acs.cpp | 230 ++++++++++++++++++++++++++---------------- 1 file changed, 144 insertions(+), 86 deletions(-) diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index 25ccee987..ff97c7484 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -748,7 +748,7 @@ protected: int DoSpawnSpot(int type, int spot, int tid, int angle, bool forced); int DoSpawnSpotFacing(int type, int spot, int tid, bool forced); int DoClassifyActor(int tid); - int CallFunction(int argCount, int funcIndex, int32_t *args); + int CallFunction(int argCount, int funcIndex, int32_t *args, int &needCount); void DoFadeTo(int r, int g, int b, int a, int time); void DoFadeRange(int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2, int time); @@ -5323,46 +5323,60 @@ int DLevelScript::SwapActorTeleFog(AActor *activator, int tid) return retval; } -int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) +// Macro for CallFunction. Checks passed number of arguments with minimum required. Sets needCount and returns if not enough. +#define MIN_ARG_COUNT(minCount) do { if (argCount < minCount) { needCount = minCount; return 0; } } while(0) + +int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args, int &needCount) { AActor *actor; switch(funcIndex) { case ACSF_GetLineUDMFInt: + MIN_ARG_COUNT(2); return GetUDMFInt(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1])); case ACSF_GetLineUDMFFixed: + MIN_ARG_COUNT(2); return DoubleToACS(GetUDMFFloat(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1]))); case ACSF_GetThingUDMFInt: case ACSF_GetThingUDMFFixed: + MIN_ARG_COUNT(2); return 0; // Not implemented yet case ACSF_GetSectorUDMFInt: + MIN_ARG_COUNT(2); return GetUDMFInt(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1])); case ACSF_GetSectorUDMFFixed: + MIN_ARG_COUNT(2); return DoubleToACS(GetUDMFFloat(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1]))); case ACSF_GetSideUDMFInt: + MIN_ARG_COUNT(3); return GetUDMFInt(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2])); case ACSF_GetSideUDMFFixed: + MIN_ARG_COUNT(3); return DoubleToACS(GetUDMFFloat(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2]))); case ACSF_GetActorVelX: + MIN_ARG_COUNT(1); actor = Level->SingleActorFromTID(args[0], activator); return actor != NULL? DoubleToACS(actor->Vel.X) : 0; case ACSF_GetActorVelY: + MIN_ARG_COUNT(1); actor = Level->SingleActorFromTID(args[0], activator); return actor != NULL? DoubleToACS(actor->Vel.Y) : 0; case ACSF_GetActorVelZ: + MIN_ARG_COUNT(1); actor = Level->SingleActorFromTID(args[0], activator); return actor != NULL? DoubleToACS(actor->Vel.Z) : 0; case ACSF_SetPointer: + MIN_ARG_COUNT(2); if (activator) { AActor *ptr = Level->SingleActorFromTID(args[1], activator); @@ -5377,6 +5391,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return 0; case ACSF_SetActivator: + MIN_ARG_COUNT(1); if (argCount > 1 && args[1] != AAPTR_DEFAULT) // condition (x != AAPTR_DEFAULT) is essentially condition (x). { activator = COPY_AAPTREX(Level, Level->SingleActorFromTID(args[0], activator), args[1]); @@ -5388,6 +5403,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return activator != NULL; case ACSF_SetActivatorToTarget: + MIN_ARG_COUNT(1); // [KS] I revised this a little bit actor = Level->SingleActorFromTID(args[0], activator); if (actor != NULL) @@ -5411,6 +5427,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return 0; case ACSF_GetActorViewHeight: + MIN_ARG_COUNT(1); actor = Level->SingleActorFromTID(args[0], activator); if (actor != NULL) { @@ -5426,6 +5443,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) else return 0; case ACSF_GetChar: + MIN_ARG_COUNT(2); { const char *p = Level->Behaviors.LookupString(args[0]); if (p != NULL && args[1] >= 0 && args[1] < int(strlen(p))) @@ -5439,6 +5457,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_GetAirSupply: + MIN_ARG_COUNT(1); { if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0])) { @@ -5451,6 +5470,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SetAirSupply: + MIN_ARG_COUNT(2); { if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0])) { @@ -5464,6 +5484,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SetSkyScrollSpeed: + MIN_ARG_COUNT(2); { if (args[0] == 1) Level->skyspeed1 = ACSToFloat(args[1]); else if (args[0] == 2) Level->skyspeed2 = ACSToFloat(args[1]); @@ -5471,6 +5492,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_GetArmorType: + MIN_ARG_COUNT(2); { if (args[1] < 0 || args[1] >= MAXPLAYERS || !Level->PlayerInGame(args[1])) { @@ -5486,6 +5508,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_GetArmorInfo: + MIN_ARG_COUNT(1); { if (activator == NULL || activator->player == NULL) return 0; @@ -5521,15 +5544,19 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SpawnSpotForced: + MIN_ARG_COUNT(4); return DoSpawnSpot(args[0], args[1], args[2], args[3], true); case ACSF_SpawnSpotFacingForced: + MIN_ARG_COUNT(3); return DoSpawnSpotFacing(args[0], args[1], args[2], true); case ACSF_CheckActorProperty: + MIN_ARG_COUNT(3); return (CheckActorProperty(args[0], args[1], args[2])); case ACSF_SetActorVelocity: + MIN_ARG_COUNT(6); { DVector3 vel(ACSToDouble(args[1]), ACSToDouble(args[2]), ACSToDouble(args[3])); if (args[0] == 0) @@ -5549,6 +5576,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SetUserVariable: + MIN_ARG_COUNT(3); { int cnt = 0; FName varname(Level->Behaviors.LookupString(args[1]), true); @@ -5577,6 +5605,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_GetUserVariable: + MIN_ARG_COUNT(2); { FName varname(Level->Behaviors.LookupString(args[1]), true); if (varname != NAME_None) @@ -5588,6 +5617,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SetUserArray: + MIN_ARG_COUNT(4); { int cnt = 0; FName varname(Level->Behaviors.LookupString(args[1]), true); @@ -5616,6 +5646,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_GetUserArray: + MIN_ARG_COUNT(3); { FName varname(Level->Behaviors.LookupString(args[1]), true); if (varname != NAME_None) @@ -5627,22 +5658,26 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_Radius_Quake2: + MIN_ARG_COUNT(6); P_StartQuake(Level, activator, args[0], (double)args[1], args[2], args[3], args[4], S_FindSound(Level->Behaviors.LookupString(args[5]))); break; case ACSF_CheckActorClass: + MIN_ARG_COUNT(2); { AActor *a = Level->SingleActorFromTID(args[0], activator); return a == NULL ? false : a->GetClass()->TypeName == FName(Level->Behaviors.LookupString(args[1])); } case ACSF_GetActorClass: + MIN_ARG_COUNT(1); { AActor *a = Level->SingleActorFromTID(args[0], activator); return GlobalACSStrings.AddString(a == NULL ? "None" : a->GetClass()->TypeName.GetChars()); } case ACSF_SoundSequenceOnActor: + MIN_ARG_COUNT(2); { const char *seqname = Level->Behaviors.LookupString(args[1]); if (seqname != NULL) @@ -5669,6 +5704,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) break; case ACSF_SoundSequenceOnSector: + MIN_ARG_COUNT(3); { const char *seqname = Level->Behaviors.LookupString(args[1]); int space = args[2] < CHAN_FLOOR || args[2] > CHAN_INTERIOR ? CHAN_FULLHEIGHT : args[2]; @@ -5685,6 +5721,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) break; case ACSF_SoundSequenceOnPolyobj: + MIN_ARG_COUNT(2); { const char *seqname = Level->Behaviors.LookupString(args[1]); if (seqname != NULL) @@ -5699,6 +5736,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) break; case ACSF_GetPolyobjX: + MIN_ARG_COUNT(1); { FPolyObj *poly = Level->GetPolyobj(args[0]); if (poly != NULL) @@ -5709,6 +5747,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return FIXED_MAX; case ACSF_GetPolyobjY: + MIN_ARG_COUNT(1); { FPolyObj *poly = Level->GetPolyobj(args[0]); if (poly != NULL) @@ -5719,6 +5758,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return FIXED_MAX; case ACSF_CheckSight: + MIN_ARG_COUNT(3); { AActor *source; AActor *dest; @@ -5764,7 +5804,8 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) } case ACSF_SpawnForced: - return DoSpawn(args[0], args[1], args[2], args[3], args[4], args[5], true); + MIN_ARG_COUNT(4); + return DoSpawn(args[0], args[1], args[2], args[3], argCount > 4 ? args[4] : 0, argCount > 5 ? args[5] : 0, true); case ACSF_ACS_NamedExecute: case ACSF_ACS_NamedSuspend: @@ -5773,6 +5814,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) case ACSF_ACS_NamedLockedExecuteDoor: case ACSF_ACS_NamedExecuteWithResult: case ACSF_ACS_NamedExecuteAlways: + MIN_ARG_COUNT(1); { int scriptnum = -FName(Level->Behaviors.LookupString(args[0])).GetIndex(); int arg1 = argCount > 1 ? args[1] : 0; @@ -5789,15 +5831,19 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) return Level->FindUniqueTID(argCount > 0 ? args[0] : 0, (argCount > 1 && args[1] >= 0) ? args[1] : 0); case ACSF_IsTIDUsed: + MIN_ARG_COUNT(1); return Level->IsTIDUsed(args[0]); case ACSF_Sqrt: + MIN_ARG_COUNT(1); return xs_FloorToInt(g_sqrt(double(args[0]))); case ACSF_FixedSqrt: + MIN_ARG_COUNT(1); return DoubleToACS(g_sqrt(ACSToDouble(args[0]))); case ACSF_VectorLength: + MIN_ARG_COUNT(2); return DoubleToACS(DVector2(ACSToDouble(args[0]), ACSToDouble(args[1])).Length()); case ACSF_SetHUDClipRect: @@ -5814,56 +5860,36 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) break; case ACSF_GetCVarString: - if (argCount == 1) - { - return DoGetCVar(GetCVar(activator && activator->player ? int(activator->player - players) : -1, Level->Behaviors.LookupString(args[0])), true); - } - break; + MIN_ARG_COUNT(1); + return DoGetCVar(GetCVar(activator && activator->player ? int(activator->player - players) : -1, Level->Behaviors.LookupString(args[0])), true); case ACSF_SetCVar: - if (argCount == 2) - { - return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], false); - } - break; + MIN_ARG_COUNT(2); + return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], false); case ACSF_SetCVarString: - if (argCount == 2) - { - return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], true); - } - break; + MIN_ARG_COUNT(2); + return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], true); case ACSF_GetUserCVar: - if (argCount == 2) - { - return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), false); - } - break; + MIN_ARG_COUNT(2); + return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), false); case ACSF_GetUserCVarString: - if (argCount == 2) - { - return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), true); - } - break; + MIN_ARG_COUNT(2); + return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), true); case ACSF_SetUserCVar: - if (argCount == 3) - { - return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], false); - } - break; + MIN_ARG_COUNT(3); + return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], false); case ACSF_SetUserCVarString: - if (argCount == 3) - { - return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], true); - } - break; + MIN_ARG_COUNT(3); + return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], true); //[RC] A bullet firing function for ACS. Thanks to DavidPH. case ACSF_LineAttack: + MIN_ARG_COUNT(4); { DAngle angle = ACSToAngle(args[1]); DAngle pitch = ACSToAngle(args[2]); @@ -5906,6 +5932,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args) case ACSF_PlaySound: case ACSF_PlayActorSound: // PlaySound(tid, "SoundName", channel, volume, looping, attenuation, local) + MIN_ARG_COUNT(2); { FSoundID sid = NO_SOUND; @@ -5952,6 +5979,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_StopSound: + MIN_ARG_COUNT(1); { int chan = argCount > 1 ? args[1] : CHAN_BODY; @@ -5974,6 +6002,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_SoundVolume: // SoundVolume(int tid, int channel, fixed volume) + MIN_ARG_COUNT(3); { int chan = args[1]; double volume = ACSToDouble(args[2]); @@ -5997,7 +6026,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_strcmp: case ACSF_stricmp: - if (argCount >= 2) + MIN_ARG_COUNT(2); { const char *a, *b; // If the string indicies are the same, then they are the same string. @@ -6026,7 +6055,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_StrLeft: case ACSF_StrRight: - if (argCount >= 2) + MIN_ARG_COUNT(2); { const char *oldstr = Level->Behaviors.LookupString(args[0]); if (oldstr == NULL || *oldstr == '\0') @@ -6046,7 +6075,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_StrMid: - if (argCount >= 3) + MIN_ARG_COUNT(3); { const char *oldstr = Level->Behaviors.LookupString(args[0]); if (oldstr == NULL || *oldstr == '\0') @@ -6083,6 +6112,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_SpawnDecal: // int SpawnDecal(int tid, str decalname, int flags, fixed angle, int|fixed zoffset, int|fixed distance) // Returns number of decals spawned (not including spreading) + MIN_ARG_COUNT(2); { int count = 0; const FDecalTemplate *tpl = DecalLibrary.GetDecalByName(Level->Behaviors.LookupString(args[1])); @@ -6117,9 +6147,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_CheckFont: // bool CheckFont(str fontname) + MIN_ARG_COUNT(1); return V_GetFont(Level->Behaviors.LookupString(args[0])) != NULL; case ACSF_DropItem: + MIN_ARG_COUNT(2); { const char *type = Level->Behaviors.LookupString(args[1]); int amount = argCount >= 3? args[2] : -1; @@ -6153,6 +6185,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_DropInventory: + MIN_ARG_COUNT(2); { const char *type = Level->Behaviors.LookupString(args[1]); AActor *inv; @@ -6189,6 +6222,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_CheckFlag: + MIN_ARG_COUNT(2); { AActor *actor = Level->SingleActorFromTID(args[0], activator); if (actor != NULL) @@ -6199,6 +6233,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_QuakeEx: + MIN_ARG_COUNT(8); { return P_StartQuakeXYZ(Level, activator, args[0], args[1], args[2], args[3], args[4], args[5], args[6], S_FindSound(Level->Behaviors.LookupString(args[7])), argCount > 8 ? args[8] : 0, @@ -6212,11 +6247,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_SetLineActivation: - if (argCount >= 2) + MIN_ARG_COUNT(2); { int line; auto itr = Level->GetLineIdIterator(args[0]); - int repeat = argCount > 2? args[2] : -1; + int repeat = argCount > 2 ? args[2] : -1; while ((line = itr.Next()) >= 0) { Level->lines[line].activation = args[1]; @@ -6227,7 +6262,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_GetLineActivation: - if (argCount > 0) + MIN_ARG_COUNT(1); { int line = Level->FindFirstLineFromID(args[0]); return line >= 0 ? Level->lines[line].activation : 0; @@ -6235,7 +6270,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_GetActorPowerupTics: - if (argCount >= 2) + MIN_ARG_COUNT(2); { PClassActor *powerupclass = PClass::FindActor(Level->Behaviors.LookupString(args[1])); if (powerupclass == NULL || !powerupclass->IsDescendantOf(NAME_Powerup)) @@ -6256,32 +6291,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_ChangeActorAngle: - if (argCount >= 2) - { - SetActorAngle(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); - } + MIN_ARG_COUNT(2); + SetActorAngle(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); break; case ACSF_ChangeActorPitch: - if (argCount >= 2) - { - SetActorPitch(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); - } + MIN_ARG_COUNT(2); + SetActorPitch(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); break; + case ACSF_SetActorTeleFog: - if (argCount >= 3) - { - SetActorTeleFog(activator, args[0], Level->Behaviors.LookupString(args[1]), Level->Behaviors.LookupString(args[2])); - } + MIN_ARG_COUNT(3); + SetActorTeleFog(activator, args[0], Level->Behaviors.LookupString(args[1]), Level->Behaviors.LookupString(args[2])); break; + case ACSF_SwapActorTeleFog: - if (argCount >= 1) - { - return SwapActorTeleFog(activator, args[0]); - } - break; + MIN_ARG_COUNT(1); + return SwapActorTeleFog(activator, args[0]); + case ACSF_PickActor: - if (argCount >= 5) + MIN_ARG_COUNT(5); { actor = Level->SingleActorFromTID(args[0], activator); if (actor == NULL) @@ -6326,6 +6355,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_IsPointerEqual: + MIN_ARG_COUNT(2); { int tid1 = 0, tid2 = 0; switch (argCount) @@ -6342,7 +6372,8 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_CanRaiseActor: - if (argCount >= 1) { + MIN_ARG_COUNT(1); + { if (args[0] == 0) { actor = Level->SingleActorFromTID(args[0], activator); if (actor != NULL) { @@ -6354,7 +6385,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) bool canraiseall = true; while ((actor = iterator.Next())) { - canraiseall = P_Thing_CanRaise(actor) & canraiseall; + canraiseall &= P_Thing_CanRaise(actor); } return canraiseall; @@ -6363,22 +6394,23 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) // [Nash] Actor roll functions. Let's roll! case ACSF_SetActorRoll: + MIN_ARG_COUNT(2); SetActorRoll(activator, args[0], args[1], false); - return 0; + break; case ACSF_ChangeActorRoll: - if (argCount >= 2) - { - SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); - } + MIN_ARG_COUNT(2); + SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); break; case ACSF_GetActorRoll: + MIN_ARG_COUNT(1); actor = Level->SingleActorFromTID(args[0], activator); return actor != NULL? AngleToACS(actor->Angles.Roll) : 0; // [ZK] A_Warp in ACS case ACSF_Warp: + MIN_ARG_COUNT(6); { if (nullptr == activator) { @@ -6430,6 +6462,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return true; } case ACSF_GetMaxInventory: + MIN_ARG_COUNT(2); actor = Level->SingleActorFromTID(args[0], activator); if (actor != NULL) { @@ -6438,7 +6471,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_SetSectorDamage: - if (argCount >= 2) + MIN_ARG_COUNT(2); { auto it = Level->GetSectorTagIterator(args[0]); int s; @@ -6455,7 +6488,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_SetSectorTerrain: - if (argCount >= 3) + MIN_ARG_COUNT(3); { if (args[1] == sector_t::floor || args[1] == sector_t::ceiling) { @@ -6471,6 +6504,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_SpawnParticle: + MIN_ARG_COUNT(1); { PalEntry color = args[0]; bool fullbright = argCount > 1 ? !!args[1] : false; @@ -6503,10 +6537,12 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; case ACSF_SetMusicVolume: + MIN_ARG_COUNT(1); Level->SetMusicVolume(ACSToFloat(args[0])); break; case ACSF_CheckProximity: + MIN_ARG_COUNT(3); { // [zombie] ACS version of A_CheckProximity actor = Level->SingleActorFromTID(args[0], activator); @@ -6519,6 +6555,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_CheckActorState: + MIN_ARG_COUNT(2); { actor = Level->SingleActorFromTID(args[0], activator); const char *statename = Level->Behaviors.LookupString(args[1]); @@ -6531,12 +6568,14 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_CheckClass: + MIN_ARG_COUNT(1); { const char *clsname = Level->Behaviors.LookupString(args[0]); return !!PClass::FindActor(clsname); } case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj + MIN_ARG_COUNT(6); { // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) AActor* target = COPY_AAPTREX(Level, Level->SingleActorFromTID(args[0], activator), args[1]); @@ -6546,6 +6585,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_SetActorFlag: + MIN_ARG_COUNT(3); { int tid = args[0]; FString flagname = Level->Behaviors.LookupString(args[1]); @@ -6574,6 +6614,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_SetTranslation: + MIN_ARG_COUNT(2); { int tid = args[0]; const char *trname = Level->Behaviors.LookupString(args[1]); @@ -6595,6 +6636,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) // OpenGL exclusive functions case ACSF_SetSectorGlow: + MIN_ARG_COUNT(6); { int which = !!args[1]; PalEntry color(args[2], args[3], args[4]); @@ -6612,6 +6654,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_SetFogDensity: + MIN_ARG_COUNT(2); { auto it = Level->GetSectorTagIterator(args[0]); int s; @@ -6624,6 +6667,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_GetActorFloorTexture: + MIN_ARG_COUNT(1); { auto a = Level->SingleActorFromTID(args[0], activator); if (a != nullptr) @@ -6638,6 +6682,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_GetActorFloorTerrain: + MIN_ARG_COUNT(1); { auto a = Level->SingleActorFromTID(args[0], activator); if (a != nullptr) @@ -6652,25 +6697,32 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_StrArg: + MIN_ARG_COUNT(1); return -FName(Level->Behaviors.LookupString(args[0])).GetIndex(); case ACSF_Floor: + MIN_ARG_COUNT(1); return args[0] & ~0xffff; case ACSF_Ceil: + MIN_ARG_COUNT(1); return (args[0] & ~0xffff) + 0x10000; case ACSF_Round: + MIN_ARG_COUNT(1); return (args[0] + 32768) & ~0xffff; case ACSF_ScriptCall: + MIN_ARG_COUNT(1); return ScriptCall(activator, argCount, args); case ACSF_StartSlideshow: + MIN_ARG_COUNT(1); G_StartSlideshow(Level, FName(Level->Behaviors.LookupString(args[0]))); break; case ACSF_GetSectorHealth: + MIN_ARG_COUNT(2); { int part = args[1]; auto it = Level->GetSectorTagIterator(args[0]); @@ -6698,6 +6750,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_GetLineHealth: + MIN_ARG_COUNT(1); { auto it = Level->GetLineIdIterator(args[0]); int l = it.Next(); @@ -6715,6 +6768,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_GetLineX: case ACSF_GetLineY: + MIN_ARG_COUNT(3); { auto it = Level->GetLineIdIterator(args[0]); int lineno = it.Next(); @@ -6730,22 +6784,20 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } case ACSF_SetSubtitleNumber: - if (argCount >= 2) + MIN_ARG_COUNT(2); + // only players allowed as activator + if (activator != nullptr && activator->player != nullptr) { - // only players allowed as activator - if (activator != nullptr && activator->player != nullptr) + int logNum = args[0]; + FSoundID sid = NO_SOUND; + + const char* lookup = Level->Behaviors.LookupString(args[1]); + if (lookup != nullptr) { - int logNum = args[0]; - FSoundID sid = NO_SOUND; - - const char* lookup = Level->Behaviors.LookupString(args[1]); - if (lookup != nullptr) - { - sid = S_FindSound(lookup); - } - - activator->player->SetSubtitle(logNum, sid); + sid = S_FindSound(lookup); } + + activator->player->SetSubtitle(logNum, sid); } break; @@ -6754,6 +6806,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } return 0; } +#undef MIN_ARG_COUNT enum { @@ -7167,7 +7220,12 @@ int DLevelScript::RunScript() int argCount = NEXTBYTE; int funcIndex = NEXTSHORT; - int retval = CallFunction(argCount, funcIndex, &STACK(argCount)); + int retval, minCount = 0; + retval = CallFunction(argCount, funcIndex, &STACK(argCount), minCount); + if (minCount != 0) + { + Printf("Called ACS function index %d with too few args: %d (need %d)\n", funcIndex, argCount, minCount); + } sp -= argCount-1; STACK(1) = retval; }