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)
This commit is contained in:
Marisa Heit 2023-01-15 22:58:38 -06:00 committed by Christoph Oelckers
parent dd7cb8649f
commit 258f4b6786

View file

@ -748,7 +748,7 @@ protected:
int DoSpawnSpot(int type, int spot, int tid, int angle, bool forced); int DoSpawnSpot(int type, int spot, int tid, int angle, bool forced);
int DoSpawnSpotFacing(int type, int spot, int tid, bool forced); int DoSpawnSpotFacing(int type, int spot, int tid, bool forced);
int DoClassifyActor(int tid); 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 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); 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; 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; AActor *actor;
switch(funcIndex) switch(funcIndex)
{ {
case ACSF_GetLineUDMFInt: case ACSF_GetLineUDMFInt:
MIN_ARG_COUNT(2);
return GetUDMFInt(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1])); return GetUDMFInt(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1]));
case ACSF_GetLineUDMFFixed: case ACSF_GetLineUDMFFixed:
MIN_ARG_COUNT(2);
return DoubleToACS(GetUDMFFloat(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1]))); return DoubleToACS(GetUDMFFloat(Level, UDMF_Line, LineFromID(args[0]), Level->Behaviors.LookupString(args[1])));
case ACSF_GetThingUDMFInt: case ACSF_GetThingUDMFInt:
case ACSF_GetThingUDMFFixed: case ACSF_GetThingUDMFFixed:
MIN_ARG_COUNT(2);
return 0; // Not implemented yet return 0; // Not implemented yet
case ACSF_GetSectorUDMFInt: case ACSF_GetSectorUDMFInt:
MIN_ARG_COUNT(2);
return GetUDMFInt(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1])); return GetUDMFInt(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1]));
case ACSF_GetSectorUDMFFixed: case ACSF_GetSectorUDMFFixed:
MIN_ARG_COUNT(2);
return DoubleToACS(GetUDMFFloat(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1]))); return DoubleToACS(GetUDMFFloat(Level, UDMF_Sector, Level->FindFirstSectorFromTag(args[0]), Level->Behaviors.LookupString(args[1])));
case ACSF_GetSideUDMFInt: case ACSF_GetSideUDMFInt:
MIN_ARG_COUNT(3);
return GetUDMFInt(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2])); return GetUDMFInt(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2]));
case ACSF_GetSideUDMFFixed: case ACSF_GetSideUDMFFixed:
MIN_ARG_COUNT(3);
return DoubleToACS(GetUDMFFloat(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2]))); return DoubleToACS(GetUDMFFloat(Level, UDMF_Side, SideFromID(args[0], args[1]), Level->Behaviors.LookupString(args[2])));
case ACSF_GetActorVelX: case ACSF_GetActorVelX:
MIN_ARG_COUNT(1);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
return actor != NULL? DoubleToACS(actor->Vel.X) : 0; return actor != NULL? DoubleToACS(actor->Vel.X) : 0;
case ACSF_GetActorVelY: case ACSF_GetActorVelY:
MIN_ARG_COUNT(1);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
return actor != NULL? DoubleToACS(actor->Vel.Y) : 0; return actor != NULL? DoubleToACS(actor->Vel.Y) : 0;
case ACSF_GetActorVelZ: case ACSF_GetActorVelZ:
MIN_ARG_COUNT(1);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
return actor != NULL? DoubleToACS(actor->Vel.Z) : 0; return actor != NULL? DoubleToACS(actor->Vel.Z) : 0;
case ACSF_SetPointer: case ACSF_SetPointer:
MIN_ARG_COUNT(2);
if (activator) if (activator)
{ {
AActor *ptr = Level->SingleActorFromTID(args[1], activator); AActor *ptr = Level->SingleActorFromTID(args[1], activator);
@ -5377,6 +5391,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
return 0; return 0;
case ACSF_SetActivator: case ACSF_SetActivator:
MIN_ARG_COUNT(1);
if (argCount > 1 && args[1] != AAPTR_DEFAULT) // condition (x != AAPTR_DEFAULT) is essentially condition (x). 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]); 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; return activator != NULL;
case ACSF_SetActivatorToTarget: case ACSF_SetActivatorToTarget:
MIN_ARG_COUNT(1);
// [KS] I revised this a little bit // [KS] I revised this a little bit
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
if (actor != NULL) if (actor != NULL)
@ -5411,6 +5427,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
return 0; return 0;
case ACSF_GetActorViewHeight: case ACSF_GetActorViewHeight:
MIN_ARG_COUNT(1);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
if (actor != NULL) if (actor != NULL)
{ {
@ -5426,6 +5443,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
else return 0; else return 0;
case ACSF_GetChar: case ACSF_GetChar:
MIN_ARG_COUNT(2);
{ {
const char *p = Level->Behaviors.LookupString(args[0]); const char *p = Level->Behaviors.LookupString(args[0]);
if (p != NULL && args[1] >= 0 && args[1] < int(strlen(p))) 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: case ACSF_GetAirSupply:
MIN_ARG_COUNT(1);
{ {
if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0])) 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: case ACSF_SetAirSupply:
MIN_ARG_COUNT(2);
{ {
if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0])) 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: case ACSF_SetSkyScrollSpeed:
MIN_ARG_COUNT(2);
{ {
if (args[0] == 1) Level->skyspeed1 = ACSToFloat(args[1]); if (args[0] == 1) Level->skyspeed1 = ACSToFloat(args[1]);
else if (args[0] == 2) Level->skyspeed2 = 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: case ACSF_GetArmorType:
MIN_ARG_COUNT(2);
{ {
if (args[1] < 0 || args[1] >= MAXPLAYERS || !Level->PlayerInGame(args[1])) 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: case ACSF_GetArmorInfo:
MIN_ARG_COUNT(1);
{ {
if (activator == NULL || activator->player == NULL) return 0; 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: case ACSF_SpawnSpotForced:
MIN_ARG_COUNT(4);
return DoSpawnSpot(args[0], args[1], args[2], args[3], true); return DoSpawnSpot(args[0], args[1], args[2], args[3], true);
case ACSF_SpawnSpotFacingForced: case ACSF_SpawnSpotFacingForced:
MIN_ARG_COUNT(3);
return DoSpawnSpotFacing(args[0], args[1], args[2], true); return DoSpawnSpotFacing(args[0], args[1], args[2], true);
case ACSF_CheckActorProperty: case ACSF_CheckActorProperty:
MIN_ARG_COUNT(3);
return (CheckActorProperty(args[0], args[1], args[2])); return (CheckActorProperty(args[0], args[1], args[2]));
case ACSF_SetActorVelocity: case ACSF_SetActorVelocity:
MIN_ARG_COUNT(6);
{ {
DVector3 vel(ACSToDouble(args[1]), ACSToDouble(args[2]), ACSToDouble(args[3])); DVector3 vel(ACSToDouble(args[1]), ACSToDouble(args[2]), ACSToDouble(args[3]));
if (args[0] == 0) if (args[0] == 0)
@ -5549,6 +5576,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
} }
case ACSF_SetUserVariable: case ACSF_SetUserVariable:
MIN_ARG_COUNT(3);
{ {
int cnt = 0; int cnt = 0;
FName varname(Level->Behaviors.LookupString(args[1]), true); 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: case ACSF_GetUserVariable:
MIN_ARG_COUNT(2);
{ {
FName varname(Level->Behaviors.LookupString(args[1]), true); FName varname(Level->Behaviors.LookupString(args[1]), true);
if (varname != NAME_None) if (varname != NAME_None)
@ -5588,6 +5617,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
} }
case ACSF_SetUserArray: case ACSF_SetUserArray:
MIN_ARG_COUNT(4);
{ {
int cnt = 0; int cnt = 0;
FName varname(Level->Behaviors.LookupString(args[1]), true); 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: case ACSF_GetUserArray:
MIN_ARG_COUNT(3);
{ {
FName varname(Level->Behaviors.LookupString(args[1]), true); FName varname(Level->Behaviors.LookupString(args[1]), true);
if (varname != NAME_None) if (varname != NAME_None)
@ -5627,22 +5658,26 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
} }
case ACSF_Radius_Quake2: 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]))); P_StartQuake(Level, activator, args[0], (double)args[1], args[2], args[3], args[4], S_FindSound(Level->Behaviors.LookupString(args[5])));
break; break;
case ACSF_CheckActorClass: case ACSF_CheckActorClass:
MIN_ARG_COUNT(2);
{ {
AActor *a = Level->SingleActorFromTID(args[0], activator); AActor *a = Level->SingleActorFromTID(args[0], activator);
return a == NULL ? false : a->GetClass()->TypeName == FName(Level->Behaviors.LookupString(args[1])); return a == NULL ? false : a->GetClass()->TypeName == FName(Level->Behaviors.LookupString(args[1]));
} }
case ACSF_GetActorClass: case ACSF_GetActorClass:
MIN_ARG_COUNT(1);
{ {
AActor *a = Level->SingleActorFromTID(args[0], activator); AActor *a = Level->SingleActorFromTID(args[0], activator);
return GlobalACSStrings.AddString(a == NULL ? "None" : a->GetClass()->TypeName.GetChars()); return GlobalACSStrings.AddString(a == NULL ? "None" : a->GetClass()->TypeName.GetChars());
} }
case ACSF_SoundSequenceOnActor: case ACSF_SoundSequenceOnActor:
MIN_ARG_COUNT(2);
{ {
const char *seqname = Level->Behaviors.LookupString(args[1]); const char *seqname = Level->Behaviors.LookupString(args[1]);
if (seqname != NULL) if (seqname != NULL)
@ -5669,6 +5704,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
break; break;
case ACSF_SoundSequenceOnSector: case ACSF_SoundSequenceOnSector:
MIN_ARG_COUNT(3);
{ {
const char *seqname = Level->Behaviors.LookupString(args[1]); const char *seqname = Level->Behaviors.LookupString(args[1]);
int space = args[2] < CHAN_FLOOR || args[2] > CHAN_INTERIOR ? CHAN_FULLHEIGHT : args[2]; 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; break;
case ACSF_SoundSequenceOnPolyobj: case ACSF_SoundSequenceOnPolyobj:
MIN_ARG_COUNT(2);
{ {
const char *seqname = Level->Behaviors.LookupString(args[1]); const char *seqname = Level->Behaviors.LookupString(args[1]);
if (seqname != NULL) if (seqname != NULL)
@ -5699,6 +5736,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
break; break;
case ACSF_GetPolyobjX: case ACSF_GetPolyobjX:
MIN_ARG_COUNT(1);
{ {
FPolyObj *poly = Level->GetPolyobj(args[0]); FPolyObj *poly = Level->GetPolyobj(args[0]);
if (poly != NULL) if (poly != NULL)
@ -5709,6 +5747,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
return FIXED_MAX; return FIXED_MAX;
case ACSF_GetPolyobjY: case ACSF_GetPolyobjY:
MIN_ARG_COUNT(1);
{ {
FPolyObj *poly = Level->GetPolyobj(args[0]); FPolyObj *poly = Level->GetPolyobj(args[0]);
if (poly != NULL) if (poly != NULL)
@ -5719,6 +5758,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
return FIXED_MAX; return FIXED_MAX;
case ACSF_CheckSight: case ACSF_CheckSight:
MIN_ARG_COUNT(3);
{ {
AActor *source; AActor *source;
AActor *dest; AActor *dest;
@ -5764,7 +5804,8 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
} }
case ACSF_SpawnForced: 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_NamedExecute:
case ACSF_ACS_NamedSuspend: 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_NamedLockedExecuteDoor:
case ACSF_ACS_NamedExecuteWithResult: case ACSF_ACS_NamedExecuteWithResult:
case ACSF_ACS_NamedExecuteAlways: case ACSF_ACS_NamedExecuteAlways:
MIN_ARG_COUNT(1);
{ {
int scriptnum = -FName(Level->Behaviors.LookupString(args[0])).GetIndex(); int scriptnum = -FName(Level->Behaviors.LookupString(args[0])).GetIndex();
int arg1 = argCount > 1 ? args[1] : 0; 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); return Level->FindUniqueTID(argCount > 0 ? args[0] : 0, (argCount > 1 && args[1] >= 0) ? args[1] : 0);
case ACSF_IsTIDUsed: case ACSF_IsTIDUsed:
MIN_ARG_COUNT(1);
return Level->IsTIDUsed(args[0]); return Level->IsTIDUsed(args[0]);
case ACSF_Sqrt: case ACSF_Sqrt:
MIN_ARG_COUNT(1);
return xs_FloorToInt(g_sqrt(double(args[0]))); return xs_FloorToInt(g_sqrt(double(args[0])));
case ACSF_FixedSqrt: case ACSF_FixedSqrt:
MIN_ARG_COUNT(1);
return DoubleToACS(g_sqrt(ACSToDouble(args[0]))); return DoubleToACS(g_sqrt(ACSToDouble(args[0])));
case ACSF_VectorLength: case ACSF_VectorLength:
MIN_ARG_COUNT(2);
return DoubleToACS(DVector2(ACSToDouble(args[0]), ACSToDouble(args[1])).Length()); return DoubleToACS(DVector2(ACSToDouble(args[0]), ACSToDouble(args[1])).Length());
case ACSF_SetHUDClipRect: case ACSF_SetHUDClipRect:
@ -5814,56 +5860,36 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
break; break;
case ACSF_GetCVarString: case ACSF_GetCVarString:
if (argCount == 1) MIN_ARG_COUNT(1);
{ return DoGetCVar(GetCVar(activator && activator->player ? int(activator->player - players) : -1, Level->Behaviors.LookupString(args[0])), true);
return DoGetCVar(GetCVar(activator && activator->player ? int(activator->player - players) : -1, Level->Behaviors.LookupString(args[0])), true);
}
break;
case ACSF_SetCVar: case ACSF_SetCVar:
if (argCount == 2) MIN_ARG_COUNT(2);
{ return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], false);
return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], false);
}
break;
case ACSF_SetCVarString: case ACSF_SetCVarString:
if (argCount == 2) MIN_ARG_COUNT(2);
{ return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], true);
return SetCVar(activator, Level->Behaviors.LookupString(args[0]), args[1], true);
}
break;
case ACSF_GetUserCVar: case ACSF_GetUserCVar:
if (argCount == 2) MIN_ARG_COUNT(2);
{ return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), false);
return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), false);
}
break;
case ACSF_GetUserCVarString: case ACSF_GetUserCVarString:
if (argCount == 2) MIN_ARG_COUNT(2);
{ return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), true);
return DoGetCVar(G_GetUserCVar(args[0], Level->Behaviors.LookupString(args[1])), true);
}
break;
case ACSF_SetUserCVar: case ACSF_SetUserCVar:
if (argCount == 3) MIN_ARG_COUNT(3);
{ return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], false);
return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], false);
}
break;
case ACSF_SetUserCVarString: case ACSF_SetUserCVarString:
if (argCount == 3) MIN_ARG_COUNT(3);
{ return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], true);
return SetUserCVar(args[0], Level->Behaviors.LookupString(args[1]), args[2], true);
}
break;
//[RC] A bullet firing function for ACS. Thanks to DavidPH. //[RC] A bullet firing function for ACS. Thanks to DavidPH.
case ACSF_LineAttack: case ACSF_LineAttack:
MIN_ARG_COUNT(4);
{ {
DAngle angle = ACSToAngle(args[1]); DAngle angle = ACSToAngle(args[1]);
DAngle pitch = ACSToAngle(args[2]); DAngle pitch = ACSToAngle(args[2]);
@ -5906,6 +5932,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
case ACSF_PlaySound: case ACSF_PlaySound:
case ACSF_PlayActorSound: case ACSF_PlayActorSound:
// PlaySound(tid, "SoundName", channel, volume, looping, attenuation, local) // PlaySound(tid, "SoundName", channel, volume, looping, attenuation, local)
MIN_ARG_COUNT(2);
{ {
FSoundID sid = NO_SOUND; FSoundID sid = NO_SOUND;
@ -5952,6 +5979,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_StopSound: case ACSF_StopSound:
MIN_ARG_COUNT(1);
{ {
int chan = argCount > 1 ? args[1] : CHAN_BODY; int chan = argCount > 1 ? args[1] : CHAN_BODY;
@ -5974,6 +6002,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
case ACSF_SoundVolume: case ACSF_SoundVolume:
// SoundVolume(int tid, int channel, fixed volume) // SoundVolume(int tid, int channel, fixed volume)
MIN_ARG_COUNT(3);
{ {
int chan = args[1]; int chan = args[1];
double volume = ACSToDouble(args[2]); double volume = ACSToDouble(args[2]);
@ -5997,7 +6026,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
case ACSF_strcmp: case ACSF_strcmp:
case ACSF_stricmp: case ACSF_stricmp:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ {
const char *a, *b; const char *a, *b;
// If the string indicies are the same, then they are the same string. // 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_StrLeft:
case ACSF_StrRight: case ACSF_StrRight:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ {
const char *oldstr = Level->Behaviors.LookupString(args[0]); const char *oldstr = Level->Behaviors.LookupString(args[0]);
if (oldstr == NULL || *oldstr == '\0') if (oldstr == NULL || *oldstr == '\0')
@ -6046,7 +6075,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_StrMid: case ACSF_StrMid:
if (argCount >= 3) MIN_ARG_COUNT(3);
{ {
const char *oldstr = Level->Behaviors.LookupString(args[0]); const char *oldstr = Level->Behaviors.LookupString(args[0]);
if (oldstr == NULL || *oldstr == '\0') if (oldstr == NULL || *oldstr == '\0')
@ -6083,6 +6112,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
case ACSF_SpawnDecal: case ACSF_SpawnDecal:
// int SpawnDecal(int tid, str decalname, int flags, fixed angle, int|fixed zoffset, int|fixed distance) // int SpawnDecal(int tid, str decalname, int flags, fixed angle, int|fixed zoffset, int|fixed distance)
// Returns number of decals spawned (not including spreading) // Returns number of decals spawned (not including spreading)
MIN_ARG_COUNT(2);
{ {
int count = 0; int count = 0;
const FDecalTemplate *tpl = DecalLibrary.GetDecalByName(Level->Behaviors.LookupString(args[1])); const FDecalTemplate *tpl = DecalLibrary.GetDecalByName(Level->Behaviors.LookupString(args[1]));
@ -6117,9 +6147,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
case ACSF_CheckFont: case ACSF_CheckFont:
// bool CheckFont(str fontname) // bool CheckFont(str fontname)
MIN_ARG_COUNT(1);
return V_GetFont(Level->Behaviors.LookupString(args[0])) != NULL; return V_GetFont(Level->Behaviors.LookupString(args[0])) != NULL;
case ACSF_DropItem: case ACSF_DropItem:
MIN_ARG_COUNT(2);
{ {
const char *type = Level->Behaviors.LookupString(args[1]); const char *type = Level->Behaviors.LookupString(args[1]);
int amount = argCount >= 3? args[2] : -1; int amount = argCount >= 3? args[2] : -1;
@ -6153,6 +6185,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_DropInventory: case ACSF_DropInventory:
MIN_ARG_COUNT(2);
{ {
const char *type = Level->Behaviors.LookupString(args[1]); const char *type = Level->Behaviors.LookupString(args[1]);
AActor *inv; AActor *inv;
@ -6189,6 +6222,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_CheckFlag: case ACSF_CheckFlag:
MIN_ARG_COUNT(2);
{ {
AActor *actor = Level->SingleActorFromTID(args[0], activator); AActor *actor = Level->SingleActorFromTID(args[0], activator);
if (actor != NULL) if (actor != NULL)
@ -6199,6 +6233,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_QuakeEx: 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])), 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, argCount > 8 ? args[8] : 0,
@ -6212,11 +6247,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_SetLineActivation: case ACSF_SetLineActivation:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ {
int line; int line;
auto itr = Level->GetLineIdIterator(args[0]); 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) while ((line = itr.Next()) >= 0)
{ {
Level->lines[line].activation = args[1]; Level->lines[line].activation = args[1];
@ -6227,7 +6262,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_GetLineActivation: case ACSF_GetLineActivation:
if (argCount > 0) MIN_ARG_COUNT(1);
{ {
int line = Level->FindFirstLineFromID(args[0]); int line = Level->FindFirstLineFromID(args[0]);
return line >= 0 ? Level->lines[line].activation : 0; return line >= 0 ? Level->lines[line].activation : 0;
@ -6235,7 +6270,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_GetActorPowerupTics: case ACSF_GetActorPowerupTics:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ {
PClassActor *powerupclass = PClass::FindActor(Level->Behaviors.LookupString(args[1])); PClassActor *powerupclass = PClass::FindActor(Level->Behaviors.LookupString(args[1]));
if (powerupclass == NULL || !powerupclass->IsDescendantOf(NAME_Powerup)) if (powerupclass == NULL || !powerupclass->IsDescendantOf(NAME_Powerup))
@ -6256,32 +6291,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_ChangeActorAngle: case ACSF_ChangeActorAngle:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ SetActorAngle(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
SetActorAngle(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
}
break; break;
case ACSF_ChangeActorPitch: case ACSF_ChangeActorPitch:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ SetActorPitch(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
SetActorPitch(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
}
break; break;
case ACSF_SetActorTeleFog: case ACSF_SetActorTeleFog:
if (argCount >= 3) MIN_ARG_COUNT(3);
{ SetActorTeleFog(activator, args[0], Level->Behaviors.LookupString(args[1]), Level->Behaviors.LookupString(args[2]));
SetActorTeleFog(activator, args[0], Level->Behaviors.LookupString(args[1]), Level->Behaviors.LookupString(args[2]));
}
break; break;
case ACSF_SwapActorTeleFog: case ACSF_SwapActorTeleFog:
if (argCount >= 1) MIN_ARG_COUNT(1);
{ return SwapActorTeleFog(activator, args[0]);
return SwapActorTeleFog(activator, args[0]);
}
break;
case ACSF_PickActor: case ACSF_PickActor:
if (argCount >= 5) MIN_ARG_COUNT(5);
{ {
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
if (actor == NULL) if (actor == NULL)
@ -6326,6 +6355,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_IsPointerEqual: case ACSF_IsPointerEqual:
MIN_ARG_COUNT(2);
{ {
int tid1 = 0, tid2 = 0; int tid1 = 0, tid2 = 0;
switch (argCount) switch (argCount)
@ -6342,7 +6372,8 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_CanRaiseActor: case ACSF_CanRaiseActor:
if (argCount >= 1) { MIN_ARG_COUNT(1);
{
if (args[0] == 0) { if (args[0] == 0) {
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
if (actor != NULL) { if (actor != NULL) {
@ -6354,7 +6385,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
bool canraiseall = true; bool canraiseall = true;
while ((actor = iterator.Next())) while ((actor = iterator.Next()))
{ {
canraiseall = P_Thing_CanRaise(actor) & canraiseall; canraiseall &= P_Thing_CanRaise(actor);
} }
return canraiseall; return canraiseall;
@ -6363,22 +6394,23 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
// [Nash] Actor roll functions. Let's roll! // [Nash] Actor roll functions. Let's roll!
case ACSF_SetActorRoll: case ACSF_SetActorRoll:
MIN_ARG_COUNT(2);
SetActorRoll(activator, args[0], args[1], false); SetActorRoll(activator, args[0], args[1], false);
return 0; break;
case ACSF_ChangeActorRoll: case ACSF_ChangeActorRoll:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
}
break; break;
case ACSF_GetActorRoll: case ACSF_GetActorRoll:
MIN_ARG_COUNT(1);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
return actor != NULL? AngleToACS(actor->Angles.Roll) : 0; return actor != NULL? AngleToACS(actor->Angles.Roll) : 0;
// [ZK] A_Warp in ACS // [ZK] A_Warp in ACS
case ACSF_Warp: case ACSF_Warp:
MIN_ARG_COUNT(6);
{ {
if (nullptr == activator) if (nullptr == activator)
{ {
@ -6430,6 +6462,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
return true; return true;
} }
case ACSF_GetMaxInventory: case ACSF_GetMaxInventory:
MIN_ARG_COUNT(2);
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
if (actor != NULL) if (actor != NULL)
{ {
@ -6438,7 +6471,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_SetSectorDamage: case ACSF_SetSectorDamage:
if (argCount >= 2) MIN_ARG_COUNT(2);
{ {
auto it = Level->GetSectorTagIterator(args[0]); auto it = Level->GetSectorTagIterator(args[0]);
int s; int s;
@ -6455,7 +6488,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_SetSectorTerrain: case ACSF_SetSectorTerrain:
if (argCount >= 3) MIN_ARG_COUNT(3);
{ {
if (args[1] == sector_t::floor || args[1] == sector_t::ceiling) if (args[1] == sector_t::floor || args[1] == sector_t::ceiling)
{ {
@ -6471,6 +6504,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_SpawnParticle: case ACSF_SpawnParticle:
MIN_ARG_COUNT(1);
{ {
PalEntry color = args[0]; PalEntry color = args[0];
bool fullbright = argCount > 1 ? !!args[1] : false; bool fullbright = argCount > 1 ? !!args[1] : false;
@ -6503,10 +6537,12 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
case ACSF_SetMusicVolume: case ACSF_SetMusicVolume:
MIN_ARG_COUNT(1);
Level->SetMusicVolume(ACSToFloat(args[0])); Level->SetMusicVolume(ACSToFloat(args[0]));
break; break;
case ACSF_CheckProximity: case ACSF_CheckProximity:
MIN_ARG_COUNT(3);
{ {
// [zombie] ACS version of A_CheckProximity // [zombie] ACS version of A_CheckProximity
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
@ -6519,6 +6555,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_CheckActorState: case ACSF_CheckActorState:
MIN_ARG_COUNT(2);
{ {
actor = Level->SingleActorFromTID(args[0], activator); actor = Level->SingleActorFromTID(args[0], activator);
const char *statename = Level->Behaviors.LookupString(args[1]); const char *statename = Level->Behaviors.LookupString(args[1]);
@ -6531,12 +6568,14 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_CheckClass: case ACSF_CheckClass:
MIN_ARG_COUNT(1);
{ {
const char *clsname = Level->Behaviors.LookupString(args[0]); const char *clsname = Level->Behaviors.LookupString(args[0]);
return !!PClass::FindActor(clsname); return !!PClass::FindActor(clsname);
} }
case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj
MIN_ARG_COUNT(6);
{ {
// (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype)
AActor* target = COPY_AAPTREX(Level, Level->SingleActorFromTID(args[0], activator), args[1]); AActor* target = COPY_AAPTREX(Level, Level->SingleActorFromTID(args[0], activator), args[1]);
@ -6546,6 +6585,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_SetActorFlag: case ACSF_SetActorFlag:
MIN_ARG_COUNT(3);
{ {
int tid = args[0]; int tid = args[0];
FString flagname = Level->Behaviors.LookupString(args[1]); FString flagname = Level->Behaviors.LookupString(args[1]);
@ -6574,6 +6614,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_SetTranslation: case ACSF_SetTranslation:
MIN_ARG_COUNT(2);
{ {
int tid = args[0]; int tid = args[0];
const char *trname = Level->Behaviors.LookupString(args[1]); const char *trname = Level->Behaviors.LookupString(args[1]);
@ -6595,6 +6636,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
// OpenGL exclusive functions // OpenGL exclusive functions
case ACSF_SetSectorGlow: case ACSF_SetSectorGlow:
MIN_ARG_COUNT(6);
{ {
int which = !!args[1]; int which = !!args[1];
PalEntry color(args[2], args[3], args[4]); PalEntry color(args[2], args[3], args[4]);
@ -6612,6 +6654,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_SetFogDensity: case ACSF_SetFogDensity:
MIN_ARG_COUNT(2);
{ {
auto it = Level->GetSectorTagIterator(args[0]); auto it = Level->GetSectorTagIterator(args[0]);
int s; int s;
@ -6624,6 +6667,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_GetActorFloorTexture: case ACSF_GetActorFloorTexture:
MIN_ARG_COUNT(1);
{ {
auto a = Level->SingleActorFromTID(args[0], activator); auto a = Level->SingleActorFromTID(args[0], activator);
if (a != nullptr) if (a != nullptr)
@ -6638,6 +6682,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_GetActorFloorTerrain: case ACSF_GetActorFloorTerrain:
MIN_ARG_COUNT(1);
{ {
auto a = Level->SingleActorFromTID(args[0], activator); auto a = Level->SingleActorFromTID(args[0], activator);
if (a != nullptr) if (a != nullptr)
@ -6652,25 +6697,32 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_StrArg: case ACSF_StrArg:
MIN_ARG_COUNT(1);
return -FName(Level->Behaviors.LookupString(args[0])).GetIndex(); return -FName(Level->Behaviors.LookupString(args[0])).GetIndex();
case ACSF_Floor: case ACSF_Floor:
MIN_ARG_COUNT(1);
return args[0] & ~0xffff; return args[0] & ~0xffff;
case ACSF_Ceil: case ACSF_Ceil:
MIN_ARG_COUNT(1);
return (args[0] & ~0xffff) + 0x10000; return (args[0] & ~0xffff) + 0x10000;
case ACSF_Round: case ACSF_Round:
MIN_ARG_COUNT(1);
return (args[0] + 32768) & ~0xffff; return (args[0] + 32768) & ~0xffff;
case ACSF_ScriptCall: case ACSF_ScriptCall:
MIN_ARG_COUNT(1);
return ScriptCall(activator, argCount, args); return ScriptCall(activator, argCount, args);
case ACSF_StartSlideshow: case ACSF_StartSlideshow:
MIN_ARG_COUNT(1);
G_StartSlideshow(Level, FName(Level->Behaviors.LookupString(args[0]))); G_StartSlideshow(Level, FName(Level->Behaviors.LookupString(args[0])));
break; break;
case ACSF_GetSectorHealth: case ACSF_GetSectorHealth:
MIN_ARG_COUNT(2);
{ {
int part = args[1]; int part = args[1];
auto it = Level->GetSectorTagIterator(args[0]); auto it = Level->GetSectorTagIterator(args[0]);
@ -6698,6 +6750,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_GetLineHealth: case ACSF_GetLineHealth:
MIN_ARG_COUNT(1);
{ {
auto it = Level->GetLineIdIterator(args[0]); auto it = Level->GetLineIdIterator(args[0]);
int l = it.Next(); int l = it.Next();
@ -6715,6 +6768,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
case ACSF_GetLineX: case ACSF_GetLineX:
case ACSF_GetLineY: case ACSF_GetLineY:
MIN_ARG_COUNT(3);
{ {
auto it = Level->GetLineIdIterator(args[0]); auto it = Level->GetLineIdIterator(args[0]);
int lineno = it.Next(); int lineno = it.Next();
@ -6730,22 +6784,20 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
case ACSF_SetSubtitleNumber: 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 int logNum = args[0];
if (activator != nullptr && activator->player != nullptr) FSoundID sid = NO_SOUND;
const char* lookup = Level->Behaviors.LookupString(args[1]);
if (lookup != nullptr)
{ {
int logNum = args[0]; sid = S_FindSound(lookup);
FSoundID sid = NO_SOUND;
const char* lookup = Level->Behaviors.LookupString(args[1]);
if (lookup != nullptr)
{
sid = S_FindSound(lookup);
}
activator->player->SetSubtitle(logNum, sid);
} }
activator->player->SetSubtitle(logNum, sid);
} }
break; break;
@ -6754,6 +6806,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
} }
return 0; return 0;
} }
#undef MIN_ARG_COUNT
enum enum
{ {
@ -7167,7 +7220,12 @@ int DLevelScript::RunScript()
int argCount = NEXTBYTE; int argCount = NEXTBYTE;
int funcIndex = NEXTSHORT; 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; sp -= argCount-1;
STACK(1) = retval; STACK(1) = retval;
} }