mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-13 07:57:52 +00:00
- 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:
parent
ac77ca6474
commit
6fc63b9b78
10 changed files with 280 additions and 130 deletions
|
@ -998,6 +998,7 @@ set (PCH_SOURCES
|
||||||
s_sound.cpp
|
s_sound.cpp
|
||||||
serializer.cpp
|
serializer.cpp
|
||||||
sc_man.cpp
|
sc_man.cpp
|
||||||
|
scriptutil.cpp
|
||||||
st_stuff.cpp
|
st_stuff.cpp
|
||||||
statistics.cpp
|
statistics.cpp
|
||||||
stats.cpp
|
stats.cpp
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "r_utility.h"
|
#include "r_utility.h"
|
||||||
#include "g_levellocals.h"
|
#include "g_levellocals.h"
|
||||||
#include "actorinlines.h"
|
#include "actorinlines.h"
|
||||||
|
#include "scriptutil.h"
|
||||||
|
|
||||||
static FRandom pr_script("FScript");
|
static FRandom pr_script("FScript");
|
||||||
|
|
||||||
|
@ -283,6 +284,17 @@ static int T_GetPlayerNum(const svalue_t &arg)
|
||||||
return playernum;
|
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
|
// 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))
|
if (CheckArgs(2))
|
||||||
{
|
{
|
||||||
int playernum=T_GetPlayerNum(t_argv[0]);
|
t_return.type = svt_int;
|
||||||
if (playernum!=-1)
|
t_return.value.i = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]), ScriptUtil::End);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -983,3 +983,8 @@ xx(snd_output)
|
||||||
xx(snd_output_format)
|
xx(snd_output_format)
|
||||||
xx(snd_speakermode)
|
xx(snd_speakermode)
|
||||||
xx(snd_resampler)
|
xx(snd_resampler)
|
||||||
|
|
||||||
|
// ScriptUtil entry points
|
||||||
|
xx(ScriptUtil)
|
||||||
|
xx(SetMarineWeapon)
|
||||||
|
xx(SetMarineSprite)
|
||||||
|
|
106
src/p_acs.cpp
106
src/p_acs.cpp
|
@ -74,6 +74,7 @@
|
||||||
#include "g_levellocals.h"
|
#include "g_levellocals.h"
|
||||||
#include "actorinlines.h"
|
#include "actorinlines.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "scriptutil.h"
|
||||||
|
|
||||||
// P-codes for ACS scripts
|
// P-codes for ACS scripts
|
||||||
enum
|
enum
|
||||||
|
@ -6963,28 +6964,6 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, FACSStackMemory&
|
||||||
return true;
|
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 ()
|
int DLevelScript::RunScript ()
|
||||||
{
|
{
|
||||||
DACSThinker *controller = DACSThinker::ActiveThinker;
|
DACSThinker *controller = DACSThinker::ActiveThinker;
|
||||||
|
@ -9832,93 +9811,16 @@ scriptwait:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCD_SETWEAPON:
|
case PCD_SETWEAPON:
|
||||||
if (activator == NULL || activator->player == NULL)
|
STACK(1) = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, activator, ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCD_SETMARINEWEAPON:
|
case PCD_SETMARINEWEAPON:
|
||||||
if (STACK(2) != 0)
|
ScriptUtil::Exec(NAME_SetMarineWeapon, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::Int, STACK(1), ScriptUtil::End);
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sp -= 2;
|
sp -= 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCD_SETMARINESPRITE:
|
case PCD_SETMARINESPRITE:
|
||||||
{
|
ScriptUtil::Exec(NAME_SetMarineSprite, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sp -= 2;
|
sp -= 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
class FFont;
|
class FFont;
|
||||||
class FileReader;
|
class FileReader;
|
||||||
struct line_t;
|
struct line_t;
|
||||||
|
class FSerializer;
|
||||||
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -675,6 +675,23 @@ VMFunction *FindVMFunction(PClass *cls, const char *name);
|
||||||
|
|
||||||
FString FStringFormat(VM_ARGS, int offset = 0);
|
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);
|
unsigned GetVirtualIndex(PClass *cls, const char *funcname);
|
||||||
|
|
||||||
|
|
104
src/scriptutil.cpp
Normal file
104
src/scriptutil.cpp
Normal 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, ¶meters[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
27
src/scriptutil.h
Normal 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, ...);
|
||||||
|
};
|
|
@ -260,3 +260,5 @@ version "3.7"
|
||||||
#include "zscript/chex/chexitems.txt"
|
#include "zscript/chex/chexitems.txt"
|
||||||
#include "zscript/chex/chexdecorations.txt"
|
#include "zscript/chex/chexdecorations.txt"
|
||||||
#include "zscript/chex/chexplayer.txt"
|
#include "zscript/chex/chexplayer.txt"
|
||||||
|
|
||||||
|
#include "zscript/scriptutil/scriptutil.txt"
|
||||||
|
|
105
wadsrc/static/zscript/scriptutil/scriptutil.txt
Normal file
105
wadsrc/static/zscript/scriptutil/scriptutil.txt
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue