mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-01-18 23:52:02 +00:00
- scriptified A_Saw.
- implemented multiple-return-value assignment. Due to some grammar conflicts the originally intended Lua-inspired syntax of 'a, b = Function()' could not be done, so it's '[a, b] = Function()'
This commit is contained in:
parent
74c5659fc5
commit
af34d82888
14 changed files with 426 additions and 231 deletions
|
@ -16,167 +16,12 @@
|
|||
*/
|
||||
|
||||
void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int index);
|
||||
static FRandom pr_saw ("Saw");
|
||||
static FRandom pr_fireshotgun2 ("FireSG2");
|
||||
static FRandom pr_fireplasma ("FirePlasma");
|
||||
static FRandom pr_firerail ("FireRail");
|
||||
static FRandom pr_bfgspray ("BFGSpray");
|
||||
static FRandom pr_oldbfg ("OldBFG");
|
||||
|
||||
//
|
||||
// A_Saw
|
||||
//
|
||||
enum SAW_Flags
|
||||
{
|
||||
SF_NORANDOM = 1,
|
||||
SF_RANDOMLIGHTMISS = 2,
|
||||
SF_RANDOMLIGHTHIT = 4,
|
||||
SF_NOUSEAMMOMISS = 8,
|
||||
SF_NOUSEAMMO = 16,
|
||||
SF_NOPULLIN = 32,
|
||||
SF_NOTURN = 64,
|
||||
SF_STEALARMOR = 128,
|
||||
};
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_Saw)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SOUND_DEF (fullsound)
|
||||
PARAM_SOUND_DEF (hitsound)
|
||||
PARAM_INT_DEF (damage)
|
||||
PARAM_CLASS_DEF (pufftype, AActor)
|
||||
PARAM_INT_DEF (flags)
|
||||
PARAM_FLOAT_DEF (range)
|
||||
PARAM_ANGLE_DEF (spread_xy)
|
||||
PARAM_ANGLE_DEF (spread_z)
|
||||
PARAM_FLOAT_DEF (lifesteal)
|
||||
PARAM_INT_DEF (lifestealmax)
|
||||
PARAM_CLASS_DEF (armorbonustype, ABasicArmorBonus)
|
||||
|
||||
DAngle angle;
|
||||
DAngle slope;
|
||||
player_t *player;
|
||||
FTranslatedLineTarget t;
|
||||
int actualdamage;
|
||||
|
||||
if (NULL == (player = self->player))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pufftype == NULL)
|
||||
{
|
||||
pufftype = PClass::FindActor(NAME_BulletPuff);
|
||||
}
|
||||
if (damage == 0)
|
||||
{
|
||||
damage = 2;
|
||||
}
|
||||
if (!(flags & SF_NORANDOM))
|
||||
{
|
||||
damage *= (pr_saw()%10+1);
|
||||
}
|
||||
if (range == 0)
|
||||
{
|
||||
range = SAWRANGE;
|
||||
}
|
||||
|
||||
angle = self->Angles.Yaw + spread_xy * (pr_saw.Random2() / 255.);
|
||||
slope = P_AimLineAttack (self, angle, range, &t) + spread_z * (pr_saw.Random2() / 255.);
|
||||
|
||||
AWeapon *weapon = self->player->ReadyWeapon;
|
||||
if ((weapon != NULL) && !(flags & SF_NOUSEAMMO) && !(!t.linetarget && (flags & SF_NOUSEAMMOMISS)) && !(weapon->WeaponFlags & WIF_DEHAMMO) && ACTION_CALL_FROM_PSPRITE())
|
||||
{
|
||||
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
||||
return 0;
|
||||
}
|
||||
|
||||
P_LineAttack (self, angle, range, slope, damage, NAME_Melee, pufftype, false, &t, &actualdamage);
|
||||
|
||||
if (!t.linetarget)
|
||||
{
|
||||
if ((flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64))
|
||||
{
|
||||
player->extralight = !player->extralight;
|
||||
}
|
||||
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags & SF_RANDOMLIGHTHIT)
|
||||
{
|
||||
int randVal = pr_saw();
|
||||
if (randVal < 64)
|
||||
{
|
||||
player->extralight = 0;
|
||||
}
|
||||
else if (randVal < 160)
|
||||
{
|
||||
player->extralight = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->extralight = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (lifesteal && !(t.linetarget->flags5 & MF5_DONTDRAIN))
|
||||
{
|
||||
if (flags & SF_STEALARMOR)
|
||||
{
|
||||
if (armorbonustype == NULL)
|
||||
{
|
||||
armorbonustype = dyn_cast<ABasicArmorBonus::MetaClass>(PClass::FindClass("ArmorBonus"));
|
||||
}
|
||||
if (armorbonustype != NULL)
|
||||
{
|
||||
assert(armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus)));
|
||||
ABasicArmorBonus *armorbonus = static_cast<ABasicArmorBonus *>(Spawn(armorbonustype));
|
||||
armorbonus->SaveAmount = int(armorbonus->SaveAmount * actualdamage * lifesteal);
|
||||
armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax;
|
||||
armorbonus->flags |= MF_DROPPED;
|
||||
armorbonus->ClearCounters();
|
||||
|
||||
if (!armorbonus->CallTryPickup (self))
|
||||
{
|
||||
armorbonus->Destroy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
P_GiveBody (self, int(actualdamage * lifesteal), lifestealmax);
|
||||
}
|
||||
}
|
||||
|
||||
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
|
||||
|
||||
// turn to face target
|
||||
if (!(flags & SF_NOTURN))
|
||||
{
|
||||
DAngle anglediff = deltaangle(self->Angles.Yaw, t.angleFromSource);
|
||||
|
||||
if (anglediff < 0.0)
|
||||
{
|
||||
if (anglediff < -4.5)
|
||||
self->Angles.Yaw = angle + 90.0 / 21;
|
||||
else
|
||||
self->Angles.Yaw -= 4.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anglediff > 4.5)
|
||||
self->Angles.Yaw = angle - 90.0 / 21;
|
||||
else
|
||||
self->Angles.Yaw += 4.5;
|
||||
}
|
||||
}
|
||||
if (!(flags & SF_NOPULLIN))
|
||||
self->flags |= MF_JUSTATTACKED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// A_FireShotgun2
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
#include "g_level.h"
|
||||
#include "d_player.h"
|
||||
#include "serializer.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
IMPLEMENT_CLASS(AArmor, false, false, false, false)
|
||||
IMPLEMENT_CLASS(ABasicArmor, false, false, false, false)
|
||||
IMPLEMENT_CLASS(ABasicArmorPickup, false, false, false, false)
|
||||
IMPLEMENT_CLASS(ABasicArmorBonus, false, false, false, false)
|
||||
IMPLEMENT_CLASS(ABasicArmorBonus, false, false, true, false)
|
||||
IMPLEMENT_CLASS(AHexenArmor, false, false, false, false)
|
||||
|
||||
//===========================================================================
|
||||
|
@ -278,6 +279,19 @@ bool ABasicArmorPickup::Use (bool pickup)
|
|||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ABasicArmorBonus :: InitNativeFields
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void ABasicArmorBonus::InitNativeFields()
|
||||
{
|
||||
auto meta = RUNTIME_CLASS(ABasicArmorBonus);
|
||||
meta->AddNativeField("SaveAmount", TypeSInt32, myoffsetof(ABasicArmorBonus, SaveAmount));
|
||||
meta->AddNativeField("MaxSaveAmount", TypeSInt32, myoffsetof(ABasicArmorBonus, MaxSaveAmount));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ABasicArmorBonus :: Serialize
|
||||
|
|
|
@ -344,6 +344,14 @@ bool P_GiveBody (AActor *actor, int num, int max)
|
|||
return false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GiveBody)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(num);
|
||||
PARAM_INT_DEF(max);
|
||||
ACTION_RETURN_BOOL(P_GiveBody(self, num, max));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_RestoreSpecialThing1
|
||||
|
@ -352,12 +360,12 @@ bool P_GiveBody (AActor *actor, int num, int max)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing1)
|
||||
DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialThing1)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AInventory);
|
||||
|
||||
self->renderflags &= ~RF_INVISIBLE;
|
||||
if (static_cast<AInventory *>(self)->DoRespawn ())
|
||||
if (self->DoRespawn ())
|
||||
{
|
||||
S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
|
||||
}
|
||||
|
@ -370,9 +378,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing1)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing2)
|
||||
DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialThing2)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AInventory);
|
||||
|
||||
self->flags |= MF_SPECIAL;
|
||||
if (!(self->GetDefault()->flags & MF_NOGRAVITY))
|
||||
|
@ -390,9 +398,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing2)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing)
|
||||
DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialDoomThing)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AInventory);
|
||||
|
||||
self->renderflags &= ~RF_INVISIBLE;
|
||||
self->flags |= MF_SPECIAL;
|
||||
|
@ -400,7 +408,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing)
|
|||
{
|
||||
self->flags &= ~MF_NOGRAVITY;
|
||||
}
|
||||
if (static_cast<AInventory *>(self)->DoRespawn ())
|
||||
if (self->DoRespawn ())
|
||||
{
|
||||
self->SetState (self->SpawnState);
|
||||
S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
|
||||
|
@ -1547,6 +1555,17 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
|
|||
return res;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AInventory, CallTryPickup)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AInventory);
|
||||
PARAM_OBJECT(toucher, AActor);
|
||||
AActor *t_ret;
|
||||
bool res = self->CallTryPickup(toucher, &t_ret);
|
||||
if (numret > 0) ret[0].SetInt(res);
|
||||
if (numret > 1) ret[1].SetPointer(t_ret, ATAG_OBJECT), numret = 2;
|
||||
return numret;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
|
|
@ -500,6 +500,7 @@ public:
|
|||
class ABasicArmorBonus : public AArmor
|
||||
{
|
||||
DECLARE_CLASS (ABasicArmorBonus, AArmor)
|
||||
HAS_FIELDS
|
||||
public:
|
||||
|
||||
virtual void Serialize(FSerializer &arc);
|
||||
|
|
|
@ -6884,6 +6884,12 @@ void AActor::ClearCounters()
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, ClearCounters)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->ClearCounters();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AActor::ApplyDamageFactor(FName damagetype, int damage) const
|
||||
{
|
||||
|
|
|
@ -2331,6 +2331,95 @@ ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxMultiAssign::FxMultiAssign(FArgumentList &base, FxExpression *right, const FScriptPosition &pos)
|
||||
:FxExpression(EFX_MultiAssign, pos)
|
||||
{
|
||||
Base = std::move(base);
|
||||
Right = right;
|
||||
LocalVarContainer = new FxCompoundStatement(ScriptPosition);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxMultiAssign::~FxMultiAssign()
|
||||
{
|
||||
SAFE_DELETE(Right);
|
||||
SAFE_DELETE(LocalVarContainer);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxMultiAssign::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(Right, ctx);
|
||||
if (Right->ExprType != EFX_VMFunctionCall)
|
||||
{
|
||||
Right->ScriptPosition.Message(MSG_ERROR, "Function call expected on right side of multi-assigment");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
auto VMRight = static_cast<FxVMFunctionCall *>(Right);
|
||||
auto rets = VMRight->GetReturnTypes();
|
||||
if (rets.Size() < Base.Size())
|
||||
{
|
||||
Right->ScriptPosition.Message(MSG_ERROR, "Insufficient returns in function %s", VMRight->Function->SymbolName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
// Pack the generated data (temp local variables for the results and necessary type casts and single assignments) into a compound statement for easier management.
|
||||
for (unsigned i = 0; i < Base.Size(); i++)
|
||||
{
|
||||
auto singlevar = new FxLocalVariableDeclaration(rets[i], NAME_None, nullptr, 0, ScriptPosition);
|
||||
LocalVarContainer->Add(singlevar);
|
||||
Base[i] = Base[i]->Resolve(ctx);
|
||||
ABORT(Base[i]);
|
||||
auto varaccess = new FxLocalVariable(singlevar, ScriptPosition);
|
||||
auto assignee = new FxTypeCast(varaccess, Base[i]->ValueType, false);
|
||||
LocalVarContainer->Add(new FxAssign(Base[i], assignee, false));
|
||||
}
|
||||
auto x = LocalVarContainer->Resolve(ctx);
|
||||
LocalVarContainer = nullptr;
|
||||
ABORT(x);
|
||||
LocalVarContainer = static_cast<FxCompoundStatement*>(x);
|
||||
static_cast<FxVMFunctionCall *>(Right)->AssignCount = Base.Size();
|
||||
ValueType = TypeVoid;
|
||||
return this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ExpEmit FxMultiAssign::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
Right->Emit(build);
|
||||
for (unsigned i = 0; i < Base.Size(); i++)
|
||||
{
|
||||
LocalVarContainer->LocalVars[i]->SetReg(static_cast<FxVMFunctionCall *>(Right)->ReturnRegs[i]);
|
||||
}
|
||||
static_cast<FxVMFunctionCall *>(Right)->ReturnRegs.Clear();
|
||||
static_cast<FxVMFunctionCall *>(Right)->ReturnRegs.ShrinkToFit();
|
||||
return LocalVarContainer->Emit(build);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
@ -5398,6 +5487,13 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
if (noglobal)
|
||||
{
|
||||
// This is needed to properly resolve class names on the left side of the member access operator
|
||||
ValueType = TypeError;
|
||||
return this;
|
||||
}
|
||||
|
||||
// now check the global identifiers.
|
||||
if (newex == nullptr && (sym = ctx.FindGlobal(Identifier)) != nullptr)
|
||||
{
|
||||
|
@ -6710,11 +6806,20 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
bool staticonly = false;
|
||||
bool novirtual = false;
|
||||
|
||||
PClass *ccls = nullptr;
|
||||
|
||||
if (Self->ExprType == EFX_Identifier)
|
||||
{
|
||||
ccls = PClass::FindClass(static_cast<FxIdentifier *>(Self)->Identifier);
|
||||
// If the left side is a class name for a static member function call it needs to be resolved manually
|
||||
// because the resulting value type would cause problems in nearly every other place where identifiers are being used.
|
||||
PClass *ccls = PClass::FindClass(static_cast<FxIdentifier *>(Self)->Identifier);
|
||||
if (ccls != nullptr)static_cast<FxIdentifier *>(Self)->noglobal = true;
|
||||
}
|
||||
|
||||
SAFE_RESOLVE(Self, ctx);
|
||||
|
||||
if (Self->ValueType == TypeError)
|
||||
{
|
||||
if (ccls != nullptr)
|
||||
{
|
||||
if (ccls->bExported)
|
||||
|
@ -6729,7 +6834,6 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
// Todo: static struct members need to work as well.
|
||||
}
|
||||
}
|
||||
SAFE_RESOLVE(Self, ctx);
|
||||
|
||||
if (Self->ExprType == EFX_Super)
|
||||
{
|
||||
|
@ -7282,10 +7386,8 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
|
||||
{ // Call, expecting one result
|
||||
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
|
||||
build->Emit(OP_CALL_K, funcaddr, count, 1);
|
||||
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
|
||||
return reg;
|
||||
build->Emit(OP_CALL_K, funcaddr, count, MAX(1, AssignCount));
|
||||
goto handlereturns;
|
||||
}
|
||||
else
|
||||
{ // Call, expecting no results
|
||||
|
@ -7307,18 +7409,34 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
|
||||
{ // Call, expecting one result
|
||||
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
|
||||
build->Emit(OP_CALL, funcreg.RegNum, count, 1);
|
||||
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
|
||||
return reg;
|
||||
build->Emit(OP_CALL, funcreg.RegNum, count, MAX(1, AssignCount));
|
||||
goto handlereturns;
|
||||
}
|
||||
else
|
||||
{ // Call, expecting no results
|
||||
build->Emit(OP_CALL, funcreg.RegNum, count, 0);
|
||||
return ExpEmit();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
handlereturns:
|
||||
if (AssignCount == 0)
|
||||
{
|
||||
// Regular call, will not write to ReturnRegs
|
||||
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
|
||||
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
|
||||
return reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-Assignment call, this must fill in the ReturnRegs array so that the multi-assignment operator can dispatch the return values.
|
||||
assert((unsigned)AssignCount <= vmfunc->Proto->ReturnTypes.Size());
|
||||
for (int i = 0; i < AssignCount; i++)
|
||||
{
|
||||
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount());
|
||||
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
|
||||
ReturnRegs.Push(reg);
|
||||
}
|
||||
return ExpEmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9018,13 +9136,19 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
|
|||
return this;
|
||||
}
|
||||
|
||||
void FxLocalVariableDeclaration::SetReg(ExpEmit emit)
|
||||
{
|
||||
assert(ValueType->GetRegType() == emit.RegType && ValueType->GetRegCount() == emit.RegCount);
|
||||
RegNum = emit.RegNum;
|
||||
}
|
||||
|
||||
ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
if (ValueType->RegType != REGT_NIL)
|
||||
{
|
||||
if (Init == nullptr)
|
||||
{
|
||||
RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount);
|
||||
if (RegNum == -1) RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -68,6 +68,7 @@ struct FScriptPosition;
|
|||
class FxLoopStatement;
|
||||
class FxCompoundStatement;
|
||||
class FxLocalVariableDeclaration;
|
||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||
|
||||
struct FCompileContext
|
||||
{
|
||||
|
@ -280,6 +281,7 @@ enum EFxType
|
|||
EFX_GlobalVariable,
|
||||
EFX_Super,
|
||||
EFX_StackVariable,
|
||||
EFX_MultiAssign,
|
||||
EFX_COUNT
|
||||
};
|
||||
|
||||
|
@ -343,6 +345,7 @@ class FxIdentifier : public FxExpression
|
|||
{
|
||||
public:
|
||||
FName Identifier;
|
||||
bool noglobal = false;
|
||||
|
||||
FxIdentifier(FName i, const FScriptPosition &p);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
@ -782,6 +785,28 @@ public:
|
|||
ExpEmit Address;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxAssign
|
||||
//
|
||||
//==========================================================================
|
||||
class FxCompoundStatement;
|
||||
|
||||
class FxMultiAssign : public FxExpression
|
||||
{
|
||||
FxCompoundStatement *LocalVarContainer; // for handling the temporary variables of the results, which may need type casts.
|
||||
FArgumentList Base;
|
||||
FxExpression *Right;
|
||||
bool AddressRequested = false;
|
||||
bool AddressWritable = false;
|
||||
|
||||
public:
|
||||
FxMultiAssign(FArgumentList &base, FxExpression *right, const FScriptPosition &pos);
|
||||
~FxMultiAssign();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxAssignSelf
|
||||
|
@ -1344,8 +1369,6 @@ public:
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||
|
||||
class FxFunctionCall : public FxExpression
|
||||
{
|
||||
FName MethodName;
|
||||
|
@ -1448,11 +1471,16 @@ public:
|
|||
|
||||
class FxVMFunctionCall : public FxExpression
|
||||
{
|
||||
friend class FxMultiAssign;
|
||||
|
||||
bool EmitTail;
|
||||
bool NoVirtual;
|
||||
FxExpression *Self;
|
||||
PFunction *Function;
|
||||
FArgumentList ArgList;
|
||||
// for multi assignment
|
||||
int AssignCount = 0;
|
||||
TArray<ExpEmit> ReturnRegs;
|
||||
|
||||
public:
|
||||
FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual);
|
||||
|
@ -1462,6 +1490,10 @@ public:
|
|||
VMFunction *GetDirectFunction();
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit ®);
|
||||
TArray<PType*> &GetReturnTypes() const
|
||||
{
|
||||
return Function->Variants[0].Proto->ReturnTypes;
|
||||
}
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
@ -1497,6 +1529,7 @@ class FxCompoundStatement : public FxSequence
|
|||
FxCompoundStatement *Outer = nullptr;
|
||||
|
||||
friend class FxLocalVariableDeclaration;
|
||||
friend class FxMultiAssign;
|
||||
|
||||
public:
|
||||
FxCompoundStatement(const FScriptPosition &pos) : FxSequence(pos) {}
|
||||
|
@ -1822,6 +1855,7 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
void Release(VMFunctionBuilder *build);
|
||||
void SetReg(ExpEmit reginfo);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1556,7 +1556,7 @@ statement(X) ::= expression_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/
|
|||
statement(X) ::= selection_statement(X).
|
||||
statement(X) ::= iteration_statement(X).
|
||||
statement(X) ::= jump_statement(X).
|
||||
//statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
statement(X) ::= local_var(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
statement(X) ::= error SEMICOLON. { X = NULL; }
|
||||
|
||||
|
@ -1767,11 +1767,11 @@ labeled_statement(X) ::= DEFAULT(T) COLON.
|
|||
|
||||
/*----- Assignment Statements -----*/
|
||||
|
||||
/* This is no longer being used, in favor of handling assignments as expressions, just like C and C++ do.
|
||||
Keep this here in case some other parts require assignment syntax or Lua-style multi-assignments become a thing.
|
||||
%type assign_statement{ZCC_AssignStmt *}
|
||||
|
||||
assign_statement(X) ::= expr_list(A) EQ expr_list(B). [EQ]
|
||||
// The grammar won't let this pass without some syntactic help.
|
||||
// Parentheses and braces aren't accepted either so brackets are the only way to get this through the parser without a conflict.
|
||||
assign_statement(X) ::= LBRACKET expr_list(A) RBRACKET EQ expr(B). [EQ]
|
||||
{
|
||||
NEW_AST_NODE(AssignStmt,stmt,A);
|
||||
stmt->AssignOp = ZCC_EQ;
|
||||
|
@ -1779,7 +1779,6 @@ assign_statement(X) ::= expr_list(A) EQ expr_list(B). [EQ]
|
|||
stmt->Sources = B;
|
||||
X = stmt;
|
||||
}
|
||||
*/
|
||||
|
||||
/*----- Local Variable Definition "Statements" -----*/
|
||||
|
||||
|
|
|
@ -2262,10 +2262,18 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
|
||||
if (!(f->Flags & ZCC_Native))
|
||||
{
|
||||
auto code = ConvertAST(c->Type(), f->Body);
|
||||
if (code != nullptr)
|
||||
if (f->Body == nullptr)
|
||||
{
|
||||
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
||||
Error(f, "Empty function %s", FName(f->Name).GetChars());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto code = ConvertAST(c->Type(), f->Body);
|
||||
if (code != nullptr)
|
||||
{
|
||||
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
|
||||
|
@ -3164,6 +3172,21 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
|
|||
} while (node != compound->Content);
|
||||
return x;
|
||||
}
|
||||
|
||||
case AST_AssignStmt:
|
||||
{
|
||||
auto ass = static_cast<ZCC_AssignStmt *>(ast);
|
||||
FArgumentList args;
|
||||
ConvertNodeList(args, ass->Dests);
|
||||
assert(ass->Sources->SiblingNext == ass->Sources); // right side should be a single function call - nothing else
|
||||
if (ass->Sources->NodeType != AST_ExprFuncCall)
|
||||
{
|
||||
// don't let this through to the code generator. This node is only used to assign multiple returns of a function to more than one variable.
|
||||
Error(ass, "Right side of multi-assignment must be a function call");
|
||||
return new FxNop(*ast); // allow compiler to continue looking for errors.
|
||||
}
|
||||
return new FxMultiAssign(args, ConvertNode(ass->Sources), *ast);
|
||||
}
|
||||
}
|
||||
// only for development. I_Error is more convenient here than a normal error.
|
||||
I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType);
|
||||
|
|
|
@ -52,6 +52,7 @@ zscript/doom/weaponfist.txt
|
|||
zscript/doom/weaponpistol.txt
|
||||
zscript/doom/weaponshotgun.txt
|
||||
zscript/doom/weaponchaingun.txt
|
||||
zscript/doom/weaponchainsaw.txt
|
||||
|
||||
zscript/doom/deadthings.txt
|
||||
zscript/doom/doomammo.txt
|
||||
|
|
|
@ -71,6 +71,9 @@ class Actor : Thinker native
|
|||
native void RemoveFromHash();
|
||||
native string GetTag(string defstr = "");
|
||||
native float GetBobOffset(float frac = 0);
|
||||
native void ClearCounters();
|
||||
native bool GiveBody (int num, int max=0);
|
||||
|
||||
native void SetDamage(int dmg);
|
||||
native static bool isDehState(state st);
|
||||
native double Distance2D(Actor other);
|
||||
|
@ -469,6 +472,7 @@ class Actor : Thinker native
|
|||
native int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class<Actor> pufftype = "BulletPuff", name damagetype = "none");
|
||||
native void A_Stop();
|
||||
native void A_Respawn(int flags = 1);
|
||||
native void A_RestoreSpecialPosition();
|
||||
native void A_QueueCorpse();
|
||||
native void A_DeQueueCorpse();
|
||||
native void A_ClearLastHeard();
|
||||
|
|
|
@ -13,47 +13,6 @@ class DoomWeapon : Weapon
|
|||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// Chainsaw
|
||||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
class Chainsaw : Weapon
|
||||
{
|
||||
Default
|
||||
{
|
||||
Weapon.Kickback 0;
|
||||
Weapon.SelectionOrder 2200;
|
||||
Weapon.UpSound "weapons/sawup";
|
||||
Weapon.ReadySound "weapons/sawidle";
|
||||
Inventory.PickupMessage "$GOTCHAINSAW";
|
||||
Obituary "$OB_MPCHAINSAW";
|
||||
Tag "$TAG_CHAINSAW";
|
||||
+WEAPON.MELEEWEAPON
|
||||
}
|
||||
States
|
||||
{
|
||||
Ready:
|
||||
SAWG CD 4 A_WeaponReady;
|
||||
Loop;
|
||||
Deselect:
|
||||
SAWG C 1 A_Lower;
|
||||
Loop;
|
||||
Select:
|
||||
SAWG C 1 A_Raise;
|
||||
Loop;
|
||||
Fire:
|
||||
SAWG AB 4 A_Saw;
|
||||
SAWG B 0 A_ReFire;
|
||||
Goto Ready;
|
||||
Spawn:
|
||||
CSAW A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// Shotgun
|
||||
|
|
166
wadsrc/static/zscript/doom/weaponchainsaw.txt
Normal file
166
wadsrc/static/zscript/doom/weaponchainsaw.txt
Normal file
|
@ -0,0 +1,166 @@
|
|||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// Chainsaw
|
||||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
class Chainsaw : Weapon
|
||||
{
|
||||
Default
|
||||
{
|
||||
Weapon.Kickback 0;
|
||||
Weapon.SelectionOrder 2200;
|
||||
Weapon.UpSound "weapons/sawup";
|
||||
Weapon.ReadySound "weapons/sawidle";
|
||||
Inventory.PickupMessage "$GOTCHAINSAW";
|
||||
Obituary "$OB_MPCHAINSAW";
|
||||
Tag "$TAG_CHAINSAW";
|
||||
+WEAPON.MELEEWEAPON
|
||||
}
|
||||
States
|
||||
{
|
||||
Ready:
|
||||
SAWG CD 4 A_WeaponReady;
|
||||
Loop;
|
||||
Deselect:
|
||||
SAWG C 1 A_Lower;
|
||||
Loop;
|
||||
Select:
|
||||
SAWG C 1 A_Raise;
|
||||
Loop;
|
||||
Fire:
|
||||
SAWG AB 4 A_Saw;
|
||||
SAWG B 0 A_ReFire;
|
||||
Goto Ready;
|
||||
Spawn:
|
||||
CSAW A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extend class StateProvider
|
||||
{
|
||||
action void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus")
|
||||
{
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pufftype == null)
|
||||
{
|
||||
pufftype = 'BulletPuff';
|
||||
}
|
||||
if (damage == 0)
|
||||
{
|
||||
damage = 2;
|
||||
}
|
||||
if (!(flags & SF_NORANDOM))
|
||||
{
|
||||
damage *= random[Saw](1, 10);
|
||||
}
|
||||
if (range == 0)
|
||||
{
|
||||
range = SAWRANGE;
|
||||
}
|
||||
|
||||
double ang = angle + spread_xy * (Random2[Saw]() / 255.);
|
||||
double slope = AimLineAttack (ang, range, t) + spread_z * (Random2[Saw]() / 255.);
|
||||
|
||||
Weapon weap = player.ReadyWeapon;
|
||||
if (weap != null && !(flags & SF_NOUSEAMMO) && !(!t.linetarget && (flags & SF_NOUSEAMMOMISS)) && !weap.bDehAmmo &&
|
||||
invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite)
|
||||
{
|
||||
if (!weap.DepleteAmmo (weap.bAltFire))
|
||||
return;
|
||||
}
|
||||
|
||||
Actor puff;
|
||||
int actualdamage;
|
||||
[puff, actualdamage] = LineAttack (ang, range, slope, damage, 'Melee', pufftype, false, t);
|
||||
|
||||
if (!t.linetarget)
|
||||
{
|
||||
if ((flags & SF_RANDOMLIGHTMISS) && (Random[Saw]() > 64))
|
||||
{
|
||||
player.extralight = !player.extralight;
|
||||
}
|
||||
A_PlaySound (fullsound, CHAN_WEAPON);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & SF_RANDOMLIGHTHIT)
|
||||
{
|
||||
int randVal = Random[Saw]();
|
||||
if (randVal < 64)
|
||||
{
|
||||
player.extralight = 0;
|
||||
}
|
||||
else if (randVal < 160)
|
||||
{
|
||||
player.extralight = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.extralight = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (lifesteal && !t.linetarget.bDontDrain)
|
||||
{
|
||||
if (flags & SF_STEALARMOR)
|
||||
{
|
||||
if (armorbonustype == null)
|
||||
{
|
||||
armorbonustype = "ArmorBonus";
|
||||
}
|
||||
if (armorbonustype != null)
|
||||
{
|
||||
BasicArmorBonus armorbonus = BasicArmorBonus(Spawn(armorbonustype));
|
||||
armorbonus.SaveAmount = int(armorbonus.SaveAmount * actualdamage * lifesteal);
|
||||
armorbonus.MaxSaveAmount = lifestealmax <= 0 ? armorbonus.MaxSaveAmount : lifestealmax;
|
||||
armorbonus.bDropped = true;
|
||||
armorbonus.ClearCounters();
|
||||
|
||||
if (!armorbonus.CallTryPickup (self))
|
||||
{
|
||||
armorbonus.Destroy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
GiveBody (int(actualdamage * lifesteal), lifestealmax);
|
||||
}
|
||||
}
|
||||
|
||||
A_PlaySound (hitsound, CHAN_WEAPON);
|
||||
|
||||
// turn to face target
|
||||
if (!(flags & SF_NOTURN))
|
||||
{
|
||||
double anglediff = deltaangle(angle, t.angleFromSource);
|
||||
|
||||
if (anglediff < 0.0)
|
||||
{
|
||||
if (anglediff < -4.5)
|
||||
angle = ang + 90.0 / 21;
|
||||
else
|
||||
angle -= 4.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anglediff > 4.5)
|
||||
angle = ang - 90.0 / 21;
|
||||
else
|
||||
angle += 4.5;
|
||||
}
|
||||
}
|
||||
if (!(flags & SF_NOPULLIN))
|
||||
bJustAttacked = true;
|
||||
}
|
||||
}
|
|
@ -11,11 +11,12 @@ class Inventory : Actor native
|
|||
}
|
||||
|
||||
// These are regular functions for the item itself.
|
||||
private native void A_RestoreSpecialPosition();
|
||||
private native void A_RestoreSpecialDoomThing();
|
||||
private native void A_RestoreSpecialThing1();
|
||||
private native void A_RestoreSpecialThing2();
|
||||
|
||||
native bool, Actor CallTryPickup(Actor toucher);
|
||||
|
||||
States(Actor, Overlay, Weapon, Item)
|
||||
{
|
||||
HideDoomish:
|
||||
|
@ -70,7 +71,6 @@ class StateProvider : Inventory native
|
|||
action native void A_CheckReload();
|
||||
action native void A_GunFlash(statelabel flash = null, int flags = 0);
|
||||
action native void A_FireAssaultGun();
|
||||
action native void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus");
|
||||
action native state A_CheckForReload(int counter, statelabel label, bool dontincrement = false);
|
||||
action native void A_ResetReloadCounter();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue