- 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:
Christoph Oelckers 2016-11-20 00:25:38 +01:00
parent 74c5659fc5
commit af34d82888
14 changed files with 426 additions and 231 deletions

View file

@ -16,167 +16,12 @@
*/ */
void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int index); 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_fireshotgun2 ("FireSG2");
static FRandom pr_fireplasma ("FirePlasma"); static FRandom pr_fireplasma ("FirePlasma");
static FRandom pr_firerail ("FireRail"); static FRandom pr_firerail ("FireRail");
static FRandom pr_bfgspray ("BFGSpray"); static FRandom pr_bfgspray ("BFGSpray");
static FRandom pr_oldbfg ("OldBFG"); 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 // A_FireShotgun2

View file

@ -7,11 +7,12 @@
#include "g_level.h" #include "g_level.h"
#include "d_player.h" #include "d_player.h"
#include "serializer.h" #include "serializer.h"
#include "cmdlib.h"
IMPLEMENT_CLASS(AArmor, false, false, false, false) IMPLEMENT_CLASS(AArmor, false, false, false, false)
IMPLEMENT_CLASS(ABasicArmor, false, false, false, false) IMPLEMENT_CLASS(ABasicArmor, false, false, false, false)
IMPLEMENT_CLASS(ABasicArmorPickup, 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) IMPLEMENT_CLASS(AHexenArmor, false, false, false, false)
//=========================================================================== //===========================================================================
@ -278,6 +279,19 @@ bool ABasicArmorPickup::Use (bool pickup)
return true; 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 // ABasicArmorBonus :: Serialize

View file

@ -344,6 +344,14 @@ bool P_GiveBody (AActor *actor, int num, int max)
return false; 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 // 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; self->renderflags &= ~RF_INVISIBLE;
if (static_cast<AInventory *>(self)->DoRespawn ()) if (self->DoRespawn ())
{ {
S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE); 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; self->flags |= MF_SPECIAL;
if (!(self->GetDefault()->flags & MF_NOGRAVITY)) 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->renderflags &= ~RF_INVISIBLE;
self->flags |= MF_SPECIAL; self->flags |= MF_SPECIAL;
@ -400,7 +408,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing)
{ {
self->flags &= ~MF_NOGRAVITY; self->flags &= ~MF_NOGRAVITY;
} }
if (static_cast<AInventory *>(self)->DoRespawn ()) if (self->DoRespawn ())
{ {
self->SetState (self->SpawnState); self->SetState (self->SpawnState);
S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE); S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
@ -1547,6 +1555,17 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
return res; 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;
}
//=========================================================================== //===========================================================================
// //

View file

@ -500,6 +500,7 @@ public:
class ABasicArmorBonus : public AArmor class ABasicArmorBonus : public AArmor
{ {
DECLARE_CLASS (ABasicArmorBonus, AArmor) DECLARE_CLASS (ABasicArmorBonus, AArmor)
HAS_FIELDS
public: public:
virtual void Serialize(FSerializer &arc); virtual void Serialize(FSerializer &arc);

View file

@ -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 int AActor::ApplyDamageFactor(FName damagetype, int damage) const
{ {

View file

@ -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. // now check the global identifiers.
if (newex == nullptr && (sym = ctx.FindGlobal(Identifier)) != nullptr) if (newex == nullptr && (sym = ctx.FindGlobal(Identifier)) != nullptr)
{ {
@ -6710,11 +6806,20 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
bool staticonly = false; bool staticonly = false;
bool novirtual = false; bool novirtual = false;
PClass *ccls = nullptr;
if (Self->ExprType == EFX_Identifier) 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 // 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. // 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 != nullptr)
{ {
if (ccls->bExported) if (ccls->bExported)
@ -6729,7 +6834,6 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
// Todo: static struct members need to work as well. // Todo: static struct members need to work as well.
} }
} }
SAFE_RESOLVE(Self, ctx);
if (Self->ExprType == EFX_Super) if (Self->ExprType == EFX_Super)
{ {
@ -7282,10 +7386,8 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
} }
else if (vmfunc->Proto->ReturnTypes.Size() > 0) else if (vmfunc->Proto->ReturnTypes.Size() > 0)
{ // Call, expecting one result { // Call, expecting one result
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); build->Emit(OP_CALL_K, funcaddr, count, MAX(1, AssignCount));
build->Emit(OP_CALL_K, funcaddr, count, 1); goto handlereturns;
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
return reg;
} }
else else
{ // Call, expecting no results { // Call, expecting no results
@ -7307,18 +7409,34 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
} }
else if (vmfunc->Proto->ReturnTypes.Size() > 0) else if (vmfunc->Proto->ReturnTypes.Size() > 0)
{ // Call, expecting one result { // Call, expecting one result
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); build->Emit(OP_CALL, funcreg.RegNum, count, MAX(1, AssignCount));
build->Emit(OP_CALL, funcreg.RegNum, count, 1); goto handlereturns;
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
return reg;
} }
else else
{ // Call, expecting no results { // Call, expecting no results
build->Emit(OP_CALL, funcreg.RegNum, count, 0); build->Emit(OP_CALL, funcreg.RegNum, count, 0);
return ExpEmit(); 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; return this;
} }
void FxLocalVariableDeclaration::SetReg(ExpEmit emit)
{
assert(ValueType->GetRegType() == emit.RegType && ValueType->GetRegCount() == emit.RegCount);
RegNum = emit.RegNum;
}
ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
{ {
if (ValueType->RegType != REGT_NIL) if (ValueType->RegType != REGT_NIL)
{ {
if (Init == nullptr) if (Init == nullptr)
{ {
RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount); if (RegNum == -1) RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount);
} }
else else
{ {

View file

@ -68,6 +68,7 @@ struct FScriptPosition;
class FxLoopStatement; class FxLoopStatement;
class FxCompoundStatement; class FxCompoundStatement;
class FxLocalVariableDeclaration; class FxLocalVariableDeclaration;
typedef TDeletingArray<FxExpression*> FArgumentList;
struct FCompileContext struct FCompileContext
{ {
@ -280,6 +281,7 @@ enum EFxType
EFX_GlobalVariable, EFX_GlobalVariable,
EFX_Super, EFX_Super,
EFX_StackVariable, EFX_StackVariable,
EFX_MultiAssign,
EFX_COUNT EFX_COUNT
}; };
@ -343,6 +345,7 @@ class FxIdentifier : public FxExpression
{ {
public: public:
FName Identifier; FName Identifier;
bool noglobal = false;
FxIdentifier(FName i, const FScriptPosition &p); FxIdentifier(FName i, const FScriptPosition &p);
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
@ -782,6 +785,28 @@ public:
ExpEmit Address; 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 // FxAssignSelf
@ -1344,8 +1369,6 @@ public:
// //
//========================================================================== //==========================================================================
typedef TDeletingArray<FxExpression*> FArgumentList;
class FxFunctionCall : public FxExpression class FxFunctionCall : public FxExpression
{ {
FName MethodName; FName MethodName;
@ -1448,11 +1471,16 @@ public:
class FxVMFunctionCall : public FxExpression class FxVMFunctionCall : public FxExpression
{ {
friend class FxMultiAssign;
bool EmitTail; bool EmitTail;
bool NoVirtual; bool NoVirtual;
FxExpression *Self; FxExpression *Self;
PFunction *Function; PFunction *Function;
FArgumentList ArgList; FArgumentList ArgList;
// for multi assignment
int AssignCount = 0;
TArray<ExpEmit> ReturnRegs;
public: public:
FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual); FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual);
@ -1462,6 +1490,10 @@ public:
VMFunction *GetDirectFunction(); VMFunction *GetDirectFunction();
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit &reg); bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit &reg);
TArray<PType*> &GetReturnTypes() const
{
return Function->Variants[0].Proto->ReturnTypes;
}
}; };
//========================================================================== //==========================================================================
@ -1497,6 +1529,7 @@ class FxCompoundStatement : public FxSequence
FxCompoundStatement *Outer = nullptr; FxCompoundStatement *Outer = nullptr;
friend class FxLocalVariableDeclaration; friend class FxLocalVariableDeclaration;
friend class FxMultiAssign;
public: public:
FxCompoundStatement(const FScriptPosition &pos) : FxSequence(pos) {} FxCompoundStatement(const FScriptPosition &pos) : FxSequence(pos) {}
@ -1822,6 +1855,7 @@ public:
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
void Release(VMFunctionBuilder *build); void Release(VMFunctionBuilder *build);
void SetReg(ExpEmit reginfo);
}; };

View file

@ -1556,7 +1556,7 @@ statement(X) ::= expression_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/
statement(X) ::= selection_statement(X). statement(X) ::= selection_statement(X).
statement(X) ::= iteration_statement(X). statement(X) ::= iteration_statement(X).
statement(X) ::= jump_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) ::= local_var(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
statement(X) ::= error SEMICOLON. { X = NULL; } statement(X) ::= error SEMICOLON. { X = NULL; }
@ -1767,11 +1767,11 @@ labeled_statement(X) ::= DEFAULT(T) COLON.
/*----- Assignment Statements -----*/ /*----- 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 *} %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); NEW_AST_NODE(AssignStmt,stmt,A);
stmt->AssignOp = ZCC_EQ; stmt->AssignOp = ZCC_EQ;
@ -1779,7 +1779,6 @@ assign_statement(X) ::= expr_list(A) EQ expr_list(B). [EQ]
stmt->Sources = B; stmt->Sources = B;
X = stmt; X = stmt;
} }
*/
/*----- Local Variable Definition "Statements" -----*/ /*----- Local Variable Definition "Statements" -----*/

View file

@ -2262,10 +2262,18 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (!(f->Flags & ZCC_Native)) if (!(f->Flags & ZCC_Native))
{ {
auto code = ConvertAST(c->Type(), f->Body); if (f->Body == nullptr)
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); 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. if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
@ -3149,7 +3157,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
case AST_CaseStmt: case AST_CaseStmt:
{ {
auto cases = static_cast<ZCC_CaseStmt *>(ast); auto cases = static_cast<ZCC_CaseStmt *>(ast);
return new FxCaseStatement(ConvertNode(cases->Condition), *ast); return new FxCaseStatement(ConvertNode(cases->Condition), *ast);
} }
case AST_CompoundStmt: case AST_CompoundStmt:
@ -3164,6 +3172,21 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
} while (node != compound->Content); } while (node != compound->Content);
return x; 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. // only for development. I_Error is more convenient here than a normal error.
I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType); I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType);

View file

@ -52,6 +52,7 @@ zscript/doom/weaponfist.txt
zscript/doom/weaponpistol.txt zscript/doom/weaponpistol.txt
zscript/doom/weaponshotgun.txt zscript/doom/weaponshotgun.txt
zscript/doom/weaponchaingun.txt zscript/doom/weaponchaingun.txt
zscript/doom/weaponchainsaw.txt
zscript/doom/deadthings.txt zscript/doom/deadthings.txt
zscript/doom/doomammo.txt zscript/doom/doomammo.txt

View file

@ -71,6 +71,9 @@ class Actor : Thinker native
native void RemoveFromHash(); native void RemoveFromHash();
native string GetTag(string defstr = ""); native string GetTag(string defstr = "");
native float GetBobOffset(float frac = 0); native float GetBobOffset(float frac = 0);
native void ClearCounters();
native bool GiveBody (int num, int max=0);
native void SetDamage(int dmg); native void SetDamage(int dmg);
native static bool isDehState(state st); native static bool isDehState(state st);
native double Distance2D(Actor other); 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 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_Stop();
native void A_Respawn(int flags = 1); native void A_Respawn(int flags = 1);
native void A_RestoreSpecialPosition();
native void A_QueueCorpse(); native void A_QueueCorpse();
native void A_DeQueueCorpse(); native void A_DeQueueCorpse();
native void A_ClearLastHeard(); native void A_ClearLastHeard();

View file

@ -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 // Shotgun

View 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;
}
}

View file

@ -11,10 +11,11 @@ class Inventory : Actor native
} }
// These are regular functions for the item itself. // These are regular functions for the item itself.
private native void A_RestoreSpecialPosition();
private native void A_RestoreSpecialDoomThing(); private native void A_RestoreSpecialDoomThing();
private native void A_RestoreSpecialThing1(); private native void A_RestoreSpecialThing1();
private native void A_RestoreSpecialThing2(); private native void A_RestoreSpecialThing2();
native bool, Actor CallTryPickup(Actor toucher);
States(Actor, Overlay, Weapon, Item) States(Actor, Overlay, Weapon, Item)
{ {
@ -70,7 +71,6 @@ class StateProvider : Inventory native
action native void A_CheckReload(); action native void A_CheckReload();
action native void A_GunFlash(statelabel flash = null, int flags = 0); action native void A_GunFlash(statelabel flash = null, int flags = 0);
action native void A_FireAssaultGun(); 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 state A_CheckForReload(int counter, statelabel label, bool dontincrement = false);
action native void A_ResetReloadCounter(); action native void A_ResetReloadCounter();
} }