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);
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 ®);
|
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;
|
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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" -----*/
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
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,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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue