- started with a ScriptUtil class which will allow moving function implementations for ACS and FraggleScript to zscript.txt

So far 3 functions for testing implemented.
This commit is contained in:
Christoph Oelckers 2018-11-24 13:06:01 +01:00
parent ac77ca6474
commit 6fc63b9b78
10 changed files with 280 additions and 130 deletions

View File

@ -998,6 +998,7 @@ set (PCH_SOURCES
s_sound.cpp
serializer.cpp
sc_man.cpp
scriptutil.cpp
st_stuff.cpp
statistics.cpp
stats.cpp

View File

@ -48,6 +48,7 @@
#include "r_utility.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "scriptutil.h"
static FRandom pr_script("FScript");
@ -283,6 +284,17 @@ static int T_GetPlayerNum(const svalue_t &arg)
return playernum;
}
APlayerPawn *T_GetPlayerActor(const svalue_t &arg)
{
int num = T_GetPlayerNum(arg);
return num == -1 ? nullptr : players[num].mo;
}
PClassActor *T_ClassType(const svalue_t &arg)
{
return PClass::FindActor(stringvalue(arg));
}
//==========================================================================
//
// Finds a sector from a tag. This has been extended to allow looking for
@ -2834,34 +2846,8 @@ void FParser::SF_SetWeapon()
{
if (CheckArgs(2))
{
int playernum=T_GetPlayerNum(t_argv[0]);
if (playernum!=-1)
{
AInventory *item = players[playernum].mo->FindInventory (PClass::FindActor (stringvalue(t_argv[1])));
if (item == NULL || !item->IsKindOf(NAME_Weapon))
{
}
else if (players[playernum].ReadyWeapon == item)
{
// The weapon is already selected, so setweapon succeeds by default,
// but make sure the player isn't switching away from it.
players[playernum].PendingWeapon = WP_NOCHANGE;
t_return.value.i = 1;
}
else
{
auto weap = static_cast<AWeapon *> (item);
if (weap->CheckAmmo (AWeapon::EitherFire, false))
{
// There's enough ammo, so switch to it.
t_return.value.i = 1;
players[playernum].PendingWeapon = weap;
}
}
}
t_return.value.i = 0;
t_return.type = svt_int;
t_return.value.i = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]), ScriptUtil::End);
}
}

View File

@ -983,3 +983,8 @@ xx(snd_output)
xx(snd_output_format)
xx(snd_speakermode)
xx(snd_resampler)
// ScriptUtil entry points
xx(ScriptUtil)
xx(SetMarineWeapon)
xx(SetMarineSprite)

View File

@ -74,6 +74,7 @@
#include "g_levellocals.h"
#include "actorinlines.h"
#include "types.h"
#include "scriptutil.h"
// P-codes for ACS scripts
enum
@ -6963,28 +6964,6 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, FACSStackMemory&
return true;
}
static void SetMarineWeapon(AActor *marine, int weapon)
{
static VMFunction *smw = nullptr;
if (smw == nullptr) PClass::FindFunction(&smw, NAME_ScriptedMarine, NAME_SetWeapon);
if (smw)
{
VMValue params[2] = { marine, weapon };
VMCall(smw, params, 2, nullptr, 0);
}
}
static void SetMarineSprite(AActor *marine, PClassActor *source)
{
static VMFunction *sms = nullptr;
if (sms == nullptr) PClass::FindFunction(&sms, NAME_ScriptedMarine, NAME_SetSprite);
if (sms)
{
VMValue params[2] = { marine, source };
VMCall(sms, params, 2, nullptr, 0);
}
}
int DLevelScript::RunScript ()
{
DACSThinker *controller = DACSThinker::ActiveThinker;
@ -9832,93 +9811,16 @@ scriptwait:
break;
case PCD_SETWEAPON:
if (activator == NULL || activator->player == NULL)
{
STACK(1) = 0;
}
else
{
AInventory *item = activator->FindInventory (PClass::FindActor (FBehavior::StaticLookupString (STACK(1))));
if (item == NULL || !item->IsKindOf(NAME_Weapon))
{
STACK(1) = 0;
}
else if (activator->player->ReadyWeapon == item)
{
// The weapon is already selected, so setweapon succeeds by default,
// but make sure the player isn't switching away from it.
activator->player->PendingWeapon = WP_NOCHANGE;
STACK(1) = 1;
}
else
{
AWeapon *weap = static_cast<AWeapon *> (item);
if (weap->CheckAmmo (AWeapon::EitherFire, false))
{
// There's enough ammo, so switch to it.
STACK(1) = 1;
activator->player->PendingWeapon = weap;
}
else
{
STACK(1) = 0;
}
}
}
STACK(1) = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, activator, ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
break;
case PCD_SETMARINEWEAPON:
if (STACK(2) != 0)
{
AActor *marine;
NActorIterator iterator(NAME_ScriptedMarine, STACK(2));
while ((marine = iterator.Next()) != NULL)
{
SetMarineWeapon(marine, STACK(1));
}
}
else
{
if (activator != nullptr && activator->IsKindOf (NAME_ScriptedMarine))
{
SetMarineWeapon(activator, STACK(1));
}
}
ScriptUtil::Exec(NAME_SetMarineWeapon, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::Int, STACK(1), ScriptUtil::End);
sp -= 2;
break;
case PCD_SETMARINESPRITE:
{
PClassActor *type = PClass::FindActor(FBehavior::StaticLookupString (STACK(1)));
if (type != NULL)
{
if (STACK(2) != 0)
{
AActor *marine;
NActorIterator iterator(NAME_ScriptedMarine, STACK(2));
while ((marine = iterator.Next()) != NULL)
{
SetMarineSprite(marine, type);
}
}
else
{
if (activator != nullptr && activator->IsKindOf(NAME_ScriptedMarine))
{
SetMarineSprite(activator, type);
}
}
}
else
{
Printf ("Unknown actor type: %s\n", FBehavior::StaticLookupString (STACK(1)));
}
}
ScriptUtil::Exec(NAME_SetMarineSprite, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
sp -= 2;
break;

View File

@ -43,6 +43,7 @@
class FFont;
class FileReader;
struct line_t;
class FSerializer;
enum

View File

@ -675,6 +675,23 @@ VMFunction *FindVMFunction(PClass *cls, const char *name);
FString FStringFormat(VM_ARGS, int offset = 0);
#define IFVM(cls, funcname) \
static VMFunction * func = nullptr; \
if (func == nullptr) { \
func = dyn_cast<PFunction>(RUNTIME_CLASS(cls)->FindSymbol(#funcname, false)); \
assert(func); \
} \
if (func != nullptr)
#define IFVMNAME(cls, funcname) \
static VMFunction * func = nullptr; \
if (func == nullptr) { \
func = dyn_cast<PFunction>(PClass::FindClass(cls)->FindSymbol(#funcname, false)); \
assert(func); \
} \
if (func != nullptr)
unsigned GetVirtualIndex(PClass *cls, const char *funcname);

104
src/scriptutil.cpp Normal file
View File

@ -0,0 +1,104 @@
//-----------------------------------------------------------------------------
//
// Copyright 2018 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
//
// DESCRIPTION: generalized interface for implementing ACS/FS functions
// in ZScript.
//
//-----------------------------------------------------------------------------
#include "i_system.h"
#include "tarray.h"
#include "dobject.h"
#include "vm.h"
#include "scriptutil.h"
#include "p_acs.h"
static TArray<VMValue> parameters;
static TMap<FName, VMFunction*> functions;
void ScriptUtil::BuildParameters(va_list ap)
{
for(int type = va_arg(ap, int); type != End; type = va_arg(ap, int))
{
switch (type)
{
case Int:
parameters.Push(VMValue(va_arg(ap, int)));
break;
case Pointer:
case Class: // this is just a pointer.
case String: // must be passed by reference to a persistent location!
parameters.Push(VMValue(va_arg(ap, void*)));
break;
case Float:
parameters.Push(VMValue(va_arg(ap, double)));
break;
case ACSClass:
parameters.Push(VMValue(PClass::FindActor(FBehavior::StaticLookupString(va_arg(ap, int)))));
break;
}
}
}
void ScriptUtil::RunFunction(FName functionname, unsigned paramstart, VMReturn &returns)
{
VMFunction *func = nullptr;
auto check = functions.CheckKey(functionname);
if (!check)
{
PClass::FindFunction(&func, NAME_ScriptUtil, functionname);
if (func == nullptr)
{
I_Error("Call to undefined function ScriptUtil.%s", functionname.GetChars());
}
functions.Insert(functionname, func);
}
else func = *check;
VMCall(func, &parameters[paramstart], parameters.Size() - paramstart, &returns, 1);
}
int ScriptUtil::Exec(FName functionname, ...)
{
unsigned paramstart = parameters.Size();
va_list ap;
va_start(ap, functionname);
try
{
BuildParameters(ap);
int ret = 0;
VMReturn returns(&ret);
RunFunction(functionname, paramstart, returns);
va_end(ap);
parameters.Clamp(paramstart);
return ret;
}
catch(...)
{
va_end(ap);
parameters.Clamp(paramstart);
throw;
}
}

27
src/scriptutil.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <stdarg.h>
#include "name.h"
class ScriptUtil
{
static void BuildParameters(va_list ap);
static void RunFunction(FName function, unsigned paramstart, VMReturn &returns);
public:
enum
{
End,
Int,
Pointer,
Float,
String,
Class,
ACSString, // convenience helpers taking an ACS string index instead of a string
ACSClass,
};
static int Exec(FName functionname, ...);
};

View File

@ -260,3 +260,5 @@ version "3.7"
#include "zscript/chex/chexitems.txt"
#include "zscript/chex/chexdecorations.txt"
#include "zscript/chex/chexplayer.txt"
#include "zscript/scriptutil/scriptutil.txt"

View File

@ -0,0 +1,105 @@
// Container for utility functions used by ACS and FraggleScript.
class ScriptUtil play
{
//==========================================================================
//
//
//
//==========================================================================
static int SetWeapon(Actor activator, class<Inventory> cls)
{
if(activator != NULL && activator.player != NULL && cls != null)
{
let item = Weapon(activator.FindInventory(cls));
if(item != NULL)
{
if(activator.player.ReadyWeapon == item)
{
// The weapon is already selected, so setweapon succeeds by default,
// but make sure the player isn't switching away from it.
activator.player.PendingWeapon = WP_NOCHANGE;
return 1;
}
else
{
if(item.CheckAmmo(Weapon.EitherFire, false))
{
// There's enough ammo, so switch to it.
activator.player.PendingWeapon = item;
return 1;
}
}
}
}
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
static void SetMarineWeapon(Actor activator, int tid, int marineweapontype)
{
if (tid != 0)
{
let it = ActorIterator.Create(tid, 'ScriptedMarine');
ScriptedMarine marine;
while ((marine = ScriptedMarine(it.Next())) != NULL)
{
marine.SetWeapon(marineweapontype);
}
}
else
{
let marine = ScriptedMarine(activator);
if (marine != null)
{
marine.SetWeapon(marineweapontype);
}
}
}
//==========================================================================
//
//
//
//==========================================================================
static void SetMarineSprite(Actor activator, int tid, class<Actor> type)
{
if (type != NULL)
{
if (tid != 0)
{
let it = ActorIterator.Create(tid, 'ScriptedMarine');
ScriptedMarine marine;
while ((marine = ScriptedMarine(it.Next())) != NULL)
{
marine.SetSprite(type);
}
}
else
{
let marine = ScriptedMarine(activator);
if (marine != null)
{
marine.SetSprite(type);
}
}
}
else
{
Console.Printf ("Unknown actor type: %s\n", type.GetClassName());
}
}
}