From 3ce699bf9b465dc222a81e1dcdd8d80bca7368e4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 19 Nov 2016 01:23:56 +0100 Subject: [PATCH] - implemented pass-by-reference arguments - so far only for memory based variables. - changed Dehacked weapon function lookup to check the symbol table instead of directly referencing the VM functions. Once scriptified these pointers will no longer be available. - removed all special ATAGs from the VM. While well intentioned any pointer tagged with them is basically unusable because it'd trigger asserts all over the place. - scriptified A_Punch for testing pass-by-reference parameters and stack variables. --- src/d_dehacked.cpp | 44 ++++++------ src/d_player.h | 9 ++- src/dobjtype.h | 1 + src/g_doom/a_doomweaps.cpp | 42 ------------ src/g_shared/a_pickups.h | 1 + src/g_shared/a_weapons.cpp | 34 ++++++++- src/namedef.h | 12 ++++ src/p_mobj.cpp | 8 +++ src/scripting/codegeneration/codegen.cpp | 30 ++++++-- src/scripting/thingdef.cpp | 2 +- src/scripting/thingdef_data.cpp | 8 ++- src/scripting/vm/vm.h | 4 +- src/scripting/vm/vmexec.h | 10 +-- src/scripting/zscript/zcc_compile.cpp | 1 + wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor.txt | 2 +- wadsrc/static/zscript/constants.txt | 2 +- wadsrc/static/zscript/doom/doomweapons.txt | 38 ---------- wadsrc/static/zscript/doom/weaponfist.txt | 80 ++++++++++++++++++++++ wadsrc/static/zscript/shared/inventory.txt | 2 + 20 files changed, 205 insertions(+), 126 deletions(-) create mode 100644 wadsrc/static/zscript/doom/weaponfist.txt diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 8d3a13580..7f546b1ee 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -166,36 +166,27 @@ static TArray MBFCodePointers; struct AmmoPerAttack { - VMNativeFunction **func; + ENamedName func; int ammocount; + VMFunction *ptr; }; DECLARE_ACTION(A_Punch) -DECLARE_ACTION(A_FirePistol) -DECLARE_ACTION(A_FireShotgun) -DECLARE_ACTION(A_FireShotgun2) -DECLARE_ACTION(A_FireCGun) -DECLARE_ACTION(A_FireMissile) -DECLARE_ACTION(A_Saw) -DECLARE_ACTION(A_FirePlasma) -DECLARE_ACTION(A_FireBFG) -DECLARE_ACTION(A_FireOldBFG) -DECLARE_ACTION(A_FireRailgun) // Default ammo use of the various weapon attacks static AmmoPerAttack AmmoPerAttacks[] = { - { &AActor_A_Punch_VMPtr, 0}, - { &AActor_A_FirePistol_VMPtr, 1}, - { &AActor_A_FireShotgun_VMPtr, 1}, - { &AActor_A_FireShotgun2_VMPtr, 2}, - { &AActor_A_FireCGun_VMPtr, 1}, - { &AActor_A_FireMissile_VMPtr, 1}, - { &AActor_A_Saw_VMPtr, 0}, - { &AActor_A_FirePlasma_VMPtr, 1}, - { &AActor_A_FireBFG_VMPtr, -1}, // uses deh.BFGCells - { &AActor_A_FireOldBFG_VMPtr, 1}, - { &AActor_A_FireRailgun_VMPtr, 1}, - { NULL, 0} + { NAME_A_Punch, 0}, + { NAME_A_FirePistol, 1}, + { NAME_A_FireShotgun, 1}, + { NAME_A_FireShotgun2, 2}, + { NAME_A_FireCGun, 1}, + { NAME_A_FireMissile, 1}, + { NAME_A_Saw, 0}, + { NAME_A_FirePlasma, 1}, + { NAME_A_FireBFG, -1}, // uses deh.BFGCells + { NAME_A_FireOldBFG, 1}, + { NAME_A_FireRailgun, 1}, + { NAME_None, 0} }; @@ -3095,7 +3086,12 @@ void FinishDehPatch () StateVisited[state] = true; for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++) { - if (state->ActionFunc == *AmmoPerAttacks[j].func) + if (AmmoPerAttacks[i].ptr == nullptr) + { + auto p = dyn_cast(RUNTIME_CLASS(AStateProvider)->Symbols.FindSymbol(AmmoPerAttacks[i].func, true)); + if (p != nullptr) AmmoPerAttacks[i].ptr = p->Variants[0].Implementation; + } + if (state->ActionFunc == AmmoPerAttacks[j].ptr) { found = true; int use = AmmoPerAttacks[j].ammocount; diff --git a/src/d_player.h b/src/d_player.h index 7f57837c7..20a78c932 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -253,11 +253,10 @@ enum WF_USER4OK = 1 << 11, }; -#define WPIECE1 1 -#define WPIECE2 2 -#define WPIECE3 4 - -#define WP_NOCHANGE ((AWeapon*)~0) +// The VM cannot deal with this as an invalid pointer because it performs a read barrier on every object pointer read. +// This doesn't have to point to a valid weapon, though, because WP_NOCHANGE is never dereferenced, but it must point to a valid object +// and the class descriptor just works fine for that. +#define WP_NOCHANGE ((AWeapon*)RUNTIME_CLASS_CASTLESS(AWeapon)) #define MAXPLAYERNAME 15 diff --git a/src/dobjtype.h b/src/dobjtype.h index bf4608c7b..bd6483258 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -32,6 +32,7 @@ enum VARF_Static = (1<<13), // static class data (by necessity read only.) VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. VARF_Override = (1<<15), // overrides a virtual function from the parent class. + VARF_Ref = (1<<16), // argument is passed by reference. }; // Symbol information ------------------------------------------------------- diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 108a730c4..95ae4dfb6 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -15,7 +15,6 @@ #include "doomstat.h" */ -static FRandom pr_punch ("Punch"); static FRandom pr_saw ("Saw"); static FRandom pr_fireshotgun2 ("FireSG2"); static FRandom pr_fireplasma ("FirePlasma"); @@ -23,47 +22,6 @@ static FRandom pr_firerail ("FireRail"); static FRandom pr_bfgspray ("BFGSpray"); static FRandom pr_oldbfg ("OldBFG"); -// -// A_Punch -// -DEFINE_ACTION_FUNCTION(AActor, A_Punch) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - int damage; - DAngle pitch; - FTranslatedLineTarget t; - - if (self->player != NULL) - { - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL && !(weapon->WeaponFlags & WIF_DEHAMMO) && ACTION_CALL_FROM_PSPRITE()) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - } - - damage = (pr_punch()%10+1)<<1; - - if (self->FindInventory()) - damage *= 10; - - angle = self->Angles.Yaw + pr_punch.Random2() * (5.625 / 256); - pitch = P_AimLineAttack (self, angle, MELEERANGE); - - P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, LAF_ISMELEEATTACK, &t); - - // turn to face target - if (t.linetarget) - { - S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); - self->Angles.Yaw = t.angleFromSource; - } - return 0; -} - // // A_FirePistol // diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 6dc2d0bc5..e50c7f3d8 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -289,6 +289,7 @@ public: class AWeapon : public AStateProvider { DECLARE_CLASS_WITH_META(AWeapon, AStateProvider, PClassWeapon) + HAS_FIELDS HAS_OBJECT_POINTERS public: DWORD WeaponFlags; diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 4a947cbee..9af5c6de2 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -17,10 +17,13 @@ #include "g_level.h" #include "d_net.h" #include "serializer.h" +#include "thingdef.h" #define BONUSADD 6 -IMPLEMENT_CLASS(AWeapon, false, true, false, false) +extern FFlagDef WeaponFlagDefs[]; + +IMPLEMENT_CLASS(AWeapon, false, true, true, false) IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(Ammo1) @@ -28,6 +31,26 @@ IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTERS_END +void AWeapon::InitNativeFields() +{ + auto meta = RUNTIME_CLASS(AWeapon); + + meta->AddNativeField("bAltFire", TypeBool, myoffsetof(AWeapon, bAltFire)); + + + // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. + for (size_t i = 0; WeaponFlagDefs[i].flagbit != 0xffffffff; i++) + { + if (WeaponFlagDefs[i].structoffset > 0) + { + meta->AddNativeField(FStringf("b%s", WeaponFlagDefs[i].name), (WeaponFlagDefs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), WeaponFlagDefs[i].structoffset, WeaponFlagDefs[i].varflags, WeaponFlagDefs[i].flagbit); + } + } + // This flag is not accessible through actor definitions. + meta->AddNativeField("bDehAmmo", TypeSInt32, myoffsetof(AWeapon, WeaponFlags), VARF_ReadOnly, WIF_DEHAMMO); + +} + FString WeaponSection; TArray KeyConfWeapons; FWeaponSlots *PlayingKeyConf; @@ -651,6 +674,15 @@ bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough, int ammouse) return true; } +DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo) +{ + PARAM_SELF_PROLOGUE(AWeapon); + PARAM_BOOL(altfire); + PARAM_BOOL_DEF(checkenough); + PARAM_INT_DEF(ammouse); + ACTION_RETURN_BOOL(self->DepleteAmmo(altfire, checkenough, ammouse)); +} + //=========================================================================== // diff --git a/src/namedef.h b/src/namedef.h index 01859d01a..d4206e2bb 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -738,3 +738,15 @@ xx(Length) xx(Unit) xx(StateLabel) xx(Overlay) + +xx(A_Punch) +xx(A_FirePistol) +xx(A_FireShotgun) +xx(A_FireShotgun2) +xx(A_FireCGun) +xx(A_FireMissile) +xx(A_Saw) +xx(A_FirePlasma) +xx(A_FireBFG) +xx(A_FireOldBFG) +xx(A_FireRailgun) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c998bcf91..09d850af5 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1035,6 +1035,14 @@ AInventory *AActor::FindInventory (FName type) return FindInventory(PClass::FindActor(type)); } +DEFINE_ACTION_FUNCTION(AActor, FindInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AInventory); + PARAM_BOOL_DEF(subclass); + ACTION_RETURN_OBJECT(self->FindInventory(type, subclass)); +} + //============================================================================ // // AActor :: GiveInventoryType diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index a5f6a3bc7..c58167bab 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -3547,7 +3547,7 @@ ExpEmit FxShift::Emit(VMFunctionBuilder *build) if (!op1.Konst) { op1.Free(build); - instr = InstrMap[index][op2.Konst? 0:2]; + instr = InstrMap[index][op2.Konst? 2:0]; } else { @@ -7067,7 +7067,8 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) SAFE_RESOLVE_OPT(Self, ctx); bool failed = false; auto proto = Function->Variants[0].Proto; - auto argtypes = proto->ArgumentTypes; + auto &argtypes = proto->ArgumentTypes; + auto &argflags = Function->Variants[0].ArgFlags; int implicit = Function->GetImplicitArgs(); @@ -7100,8 +7101,29 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) } assert(type != nullptr); - FxExpression *x = new FxTypeCast(ArgList[i], type, false); - x = x->Resolve(ctx); + FxExpression *x; + if (!(argflags[i + implicit] & VARF_Ref)) + { + x = new FxTypeCast(ArgList[i], type, false); + x = x->Resolve(ctx); + } + else + { + bool writable; + ArgList[i] = ArgList[i]->Resolve(ctx); // nust be resolved before the address is requested. + ArgList[i]->RequestAddress(ctx, &writable); + ArgList[i]->ValueType = NewPointer(ArgList[i]->ValueType); + // For a reference argument the types must match 100%. + if (type != ArgList[i]->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in reference argument", Function->SymbolName.GetChars()); + x = nullptr; + } + else + { + x = ArgList[i]; + } + } failed |= (x == nullptr); ArgList[i] = x; } diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 3abce216d..7f0d98846 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -131,7 +131,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayPush(NewPointer(cls)); } - args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!! + args->Push(NewPointer(NewStruct("FStateParamInfo", nullptr))); } if (argflags != nullptr) { diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 95dc0bdce..8c4220b0e 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -388,7 +388,7 @@ static FFlagDef InventoryFlagDefs[] = DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP), }; -static FFlagDef WeaponFlagDefs[] = +FFlagDef WeaponFlagDefs[] = { // Weapon flags DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags), @@ -411,12 +411,14 @@ static FFlagDef WeaponFlagDefs[] = DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags), - - DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags), + + DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), }; + + static FFlagDef PlayerPawnFlagDefs[] = { // PlayerPawn flags diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index deca91a80..407080f2c 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -157,13 +157,15 @@ enum ATAG_OBJECT, // pointer to an object; will be followed by GC // The following are all for documentation during debugging and are - // functionally no different than ATAG_GENERIC. + // functionally no different than ATAG_GENERIC (meaning they are useless because they trigger asserts all over the place.) + /* ATAG_FRAMEPOINTER, // pointer to extra stack frame space for this function ATAG_DREGISTER, // pointer to a data register ATAG_FREGISTER, // pointer to a float register ATAG_SREGISTER, // pointer to a string register ATAG_AREGISTER, // pointer to an address register + */ ATAG_RNG, // pointer to FRandom ATAG_STATE = ATAG_GENERIC, // pointer to FState (cannot have its own type because there's no means to track inside the VM.) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 918dc6949..e4ab95946 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -87,7 +87,7 @@ begin: OP(LFP): ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0); reg.a[a] = f->GetExtra(); - reg.atag[a] = ATAG_FRAMEPOINTER; + reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts. NEXTOP; OP(LB): @@ -461,7 +461,7 @@ begin: break; case REGT_INT | REGT_ADDROF: assert(C < f->NumRegD); - ::new(param) VMValue(®.d[C], ATAG_DREGISTER); + ::new(param) VMValue(®.d[C], ATAG_GENERIC); break; case REGT_INT | REGT_KONST: assert(C < sfunc->NumKonstD); @@ -473,7 +473,7 @@ begin: break; case REGT_STRING | REGT_ADDROF: assert(C < f->NumRegS); - ::new(param) VMValue(®.s[C], ATAG_SREGISTER); + ::new(param) VMValue(®.s[C], ATAG_GENERIC); break; case REGT_STRING | REGT_KONST: assert(C < sfunc->NumKonstS); @@ -485,7 +485,7 @@ begin: break; case REGT_POINTER | REGT_ADDROF: assert(C < f->NumRegA); - ::new(param) VMValue(®.a[C], ATAG_AREGISTER); + ::new(param) VMValue(®.a[C], ATAG_GENERIC); break; case REGT_POINTER | REGT_KONST: assert(C < sfunc->NumKonstA); @@ -512,7 +512,7 @@ begin: break; case REGT_FLOAT | REGT_ADDROF: assert(C < f->NumRegF); - ::new(param) VMValue(®.f[C], ATAG_FREGISTER); + ::new(param) VMValue(®.f[C], ATAG_GENERIC); break; case REGT_FLOAT | REGT_KONST: assert(C < sfunc->NumKonstF); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ebe70c340..5dd430f9a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2160,6 +2160,7 @@ void ZCCCompiler::InitFunctions() if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3)) { type = NewPointer(type); + flags |= VARF_Ref; } else if (type == TypeVector2) { diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 42f32a447..14493875a 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -48,6 +48,7 @@ zscript/doom/cyberdemon.txt zscript/doom/spidermaster.txt zscript/doom/keen.txt zscript/doom/bossbrain.txt +zscript/doom/weaponfist.txt zscript/doom/deadthings.txt zscript/doom/doomammo.txt diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 137fedbec..744128cdd 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -133,6 +133,7 @@ class Actor : Thinker native // DECORATE compatible functions native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); + native Inventory FindInventory(class itemtype, bool subclass = false); native int CountInv(class itemtype, int ptr_select = AAPTR_DEFAULT); native float GetDistance(bool checkz, int ptr = AAPTR_TARGET); native float GetAngle(int flags, int ptr = AAPTR_DEFAULT); @@ -471,7 +472,6 @@ class Actor : Thinker native native void A_DeQueueCorpse(); native void A_ClearLastHeard(); native bool A_SelectWeapon(class whichweapon, int flags = 0); - action native void A_Punch(); native void A_Feathers(); native void A_ClassBossHealth(); native void A_ShootGun(); diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 08eee789e..cf0cbdc5c 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -829,7 +829,7 @@ enum EStateType struct FStateParamInfo { state mCallingState; - EStateType mStateType; + /*EStateType*/int mStateType; int mPSPIndex; } diff --git a/wadsrc/static/zscript/doom/doomweapons.txt b/wadsrc/static/zscript/doom/doomweapons.txt index a6eecd189..b75cfa967 100644 --- a/wadsrc/static/zscript/doom/doomweapons.txt +++ b/wadsrc/static/zscript/doom/doomweapons.txt @@ -12,44 +12,6 @@ class DoomWeapon : Weapon } } -// -------------------------------------------------------------------------- -// -// Fist -// -// -------------------------------------------------------------------------- - -class Fist : Weapon -{ - Default - { - Weapon.SelectionOrder 3700; - Weapon.Kickback 100; - Obituary "$OB_MPFIST"; - Tag "$TAG_FIST"; - +WEAPON.WIMPY_WEAPON - +WEAPON.MELEEWEAPON - } - States - { - Ready: - PUNG A 1 A_WeaponReady; - Loop; - Deselect: - PUNG A 1 A_Lower; - Loop; - Select: - PUNG A 1 A_Raise; - Loop; - Fire: - PUNG B 4; - PUNG C 4 A_Punch; - PUNG D 5; - PUNG C 4; - PUNG B 5 A_ReFire; - Goto Ready; - } -} - // -------------------------------------------------------------------------- // diff --git a/wadsrc/static/zscript/doom/weaponfist.txt b/wadsrc/static/zscript/doom/weaponfist.txt new file mode 100644 index 000000000..3c1cc992f --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponfist.txt @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------- +// +// Fist +// +// -------------------------------------------------------------------------- + +class Fist : Weapon +{ + Default + { + Weapon.SelectionOrder 3700; + Weapon.Kickback 100; + Obituary "$OB_MPFIST"; + Tag "$TAG_FIST"; + +WEAPON.WIMPY_WEAPON + +WEAPON.MELEEWEAPON + } + States + { + Ready: + PUNG A 1 A_WeaponReady; + Loop; + Deselect: + PUNG A 1 A_Lower; + Loop; + Select: + PUNG A 1 A_Raise; + Loop; + Fire: + PUNG B 4; + PUNG C 4 A_Punch; + PUNG D 5; + PUNG C 4; + PUNG B 5 A_ReFire; + Goto Ready; + } +} + + +//=========================================================================== +// +// Code (must be attached to Actor) +// +//=========================================================================== + +extend class Actor +{ + action void A_Punch() + { + FTranslatedLineTarget t; + + if (player != null) + { + Weapon weap = player.ReadyWeapon; + if (weap != null && !weap.bDehAmmo && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire)) + return; + } + } + + int damage = random[Punch](1, 10) << 1; + + if (FindInventory("PowerStrength")) + damage *= 10; + + double ang = angle + Random2[Punch]() * (5.625 / 256); + double pitch = AimLineAttack (ang, MELEERANGE); + + LineAttack (ang, MELEERANGE, pitch, damage, 'Melee', "BulletPuff", LAF_ISMELEEATTACK, t); + + // turn to face target + if (t.linetarget) + { + A_PlaySound ("*fist", CHAN_WEAPON); + angle = t.angleFromSource; + } + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index 12f30c484..8337c5399 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -473,6 +473,8 @@ class Weapon : StateProvider native Stop; } + native bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1); + native action void A_ZoomFactor(float scale = 1, int flags = 0); native action void A_SetCrosshair(int xhair); const ZOOM_INSTANT = 1;