- 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.
This commit is contained in:
Christoph Oelckers 2016-11-19 01:23:56 +01:00
parent 7ff5069617
commit 3ce699bf9b
20 changed files with 205 additions and 126 deletions

View file

@ -166,36 +166,27 @@ static TArray<CodePointerAlias> MBFCodePointers;
struct AmmoPerAttack struct AmmoPerAttack
{ {
VMNativeFunction **func; ENamedName func;
int ammocount; int ammocount;
VMFunction *ptr;
}; };
DECLARE_ACTION(A_Punch) 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 // Default ammo use of the various weapon attacks
static AmmoPerAttack AmmoPerAttacks[] = { static AmmoPerAttack AmmoPerAttacks[] = {
{ &AActor_A_Punch_VMPtr, 0}, { NAME_A_Punch, 0},
{ &AActor_A_FirePistol_VMPtr, 1}, { NAME_A_FirePistol, 1},
{ &AActor_A_FireShotgun_VMPtr, 1}, { NAME_A_FireShotgun, 1},
{ &AActor_A_FireShotgun2_VMPtr, 2}, { NAME_A_FireShotgun2, 2},
{ &AActor_A_FireCGun_VMPtr, 1}, { NAME_A_FireCGun, 1},
{ &AActor_A_FireMissile_VMPtr, 1}, { NAME_A_FireMissile, 1},
{ &AActor_A_Saw_VMPtr, 0}, { NAME_A_Saw, 0},
{ &AActor_A_FirePlasma_VMPtr, 1}, { NAME_A_FirePlasma, 1},
{ &AActor_A_FireBFG_VMPtr, -1}, // uses deh.BFGCells { NAME_A_FireBFG, -1}, // uses deh.BFGCells
{ &AActor_A_FireOldBFG_VMPtr, 1}, { NAME_A_FireOldBFG, 1},
{ &AActor_A_FireRailgun_VMPtr, 1}, { NAME_A_FireRailgun, 1},
{ NULL, 0} { NAME_None, 0}
}; };
@ -3095,7 +3086,12 @@ void FinishDehPatch ()
StateVisited[state] = true; StateVisited[state] = true;
for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++) for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++)
{ {
if (state->ActionFunc == *AmmoPerAttacks[j].func) if (AmmoPerAttacks[i].ptr == nullptr)
{
auto p = dyn_cast<PFunction>(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; found = true;
int use = AmmoPerAttacks[j].ammocount; int use = AmmoPerAttacks[j].ammocount;

View file

@ -253,11 +253,10 @@ enum
WF_USER4OK = 1 << 11, WF_USER4OK = 1 << 11,
}; };
#define WPIECE1 1 // The VM cannot deal with this as an invalid pointer because it performs a read barrier on every object pointer read.
#define WPIECE2 2 // 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
#define WPIECE3 4 // and the class descriptor just works fine for that.
#define WP_NOCHANGE ((AWeapon*)RUNTIME_CLASS_CASTLESS(AWeapon))
#define WP_NOCHANGE ((AWeapon*)~0)
#define MAXPLAYERNAME 15 #define MAXPLAYERNAME 15

View file

@ -32,6 +32,7 @@ enum
VARF_Static = (1<<13), // static class data (by necessity read only.) VARF_Static = (1<<13), // static class data (by necessity read only.)
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
VARF_Override = (1<<15), // overrides a virtual function from the parent class. VARF_Override = (1<<15), // overrides a virtual function from the parent class.
VARF_Ref = (1<<16), // argument is passed by reference.
}; };
// Symbol information ------------------------------------------------------- // Symbol information -------------------------------------------------------

View file

@ -15,7 +15,6 @@
#include "doomstat.h" #include "doomstat.h"
*/ */
static FRandom pr_punch ("Punch");
static FRandom pr_saw ("Saw"); 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");
@ -23,47 +22,6 @@ 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_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<APowerStrength>())
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 // A_FirePistol
// //

View file

@ -289,6 +289,7 @@ public:
class AWeapon : public AStateProvider class AWeapon : public AStateProvider
{ {
DECLARE_CLASS_WITH_META(AWeapon, AStateProvider, PClassWeapon) DECLARE_CLASS_WITH_META(AWeapon, AStateProvider, PClassWeapon)
HAS_FIELDS
HAS_OBJECT_POINTERS HAS_OBJECT_POINTERS
public: public:
DWORD WeaponFlags; DWORD WeaponFlags;

View file

@ -17,10 +17,13 @@
#include "g_level.h" #include "g_level.h"
#include "d_net.h" #include "d_net.h"
#include "serializer.h" #include "serializer.h"
#include "thingdef.h"
#define BONUSADD 6 #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_POINTERS_START(AWeapon)
IMPLEMENT_POINTER(Ammo1) IMPLEMENT_POINTER(Ammo1)
@ -28,6 +31,26 @@ IMPLEMENT_POINTERS_START(AWeapon)
IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTER(SisterWeapon)
IMPLEMENT_POINTERS_END 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; FString WeaponSection;
TArray<FString> KeyConfWeapons; TArray<FString> KeyConfWeapons;
FWeaponSlots *PlayingKeyConf; FWeaponSlots *PlayingKeyConf;
@ -651,6 +674,15 @@ bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough, int ammouse)
return true; 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));
}
//=========================================================================== //===========================================================================
// //

View file

@ -738,3 +738,15 @@ xx(Length)
xx(Unit) xx(Unit)
xx(StateLabel) xx(StateLabel)
xx(Overlay) 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)

View file

@ -1035,6 +1035,14 @@ AInventory *AActor::FindInventory (FName type)
return FindInventory(PClass::FindActor(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 // AActor :: GiveInventoryType

View file

@ -3547,7 +3547,7 @@ ExpEmit FxShift::Emit(VMFunctionBuilder *build)
if (!op1.Konst) if (!op1.Konst)
{ {
op1.Free(build); op1.Free(build);
instr = InstrMap[index][op2.Konst? 0:2]; instr = InstrMap[index][op2.Konst? 2:0];
} }
else else
{ {
@ -7067,7 +7067,8 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
SAFE_RESOLVE_OPT(Self, ctx); SAFE_RESOLVE_OPT(Self, ctx);
bool failed = false; bool failed = false;
auto proto = Function->Variants[0].Proto; auto proto = Function->Variants[0].Proto;
auto argtypes = proto->ArgumentTypes; auto &argtypes = proto->ArgumentTypes;
auto &argflags = Function->Variants[0].ArgFlags;
int implicit = Function->GetImplicitArgs(); int implicit = Function->GetImplicitArgs();
@ -7100,8 +7101,29 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
} }
assert(type != nullptr); assert(type != nullptr);
FxExpression *x = new FxTypeCast(ArgList[i], type, false); FxExpression *x;
x = x->Resolve(ctx); 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); failed |= (x == nullptr);
ArgList[i] = x; ArgList[i] = x;
} }

View file

@ -131,7 +131,7 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
{ {
args->Push(NewPointer(cls)); args->Push(NewPointer(cls));
} }
args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!! args->Push(NewPointer(NewStruct("FStateParamInfo", nullptr)));
} }
if (argflags != nullptr) if (argflags != nullptr)
{ {

View file

@ -388,7 +388,7 @@ static FFlagDef InventoryFlagDefs[] =
DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP), DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),
}; };
static FFlagDef WeaponFlagDefs[] = FFlagDef WeaponFlagDefs[] =
{ {
// Weapon flags // Weapon flags
DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags),
@ -411,12 +411,14 @@ static FFlagDef WeaponFlagDefs[] =
DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags),
DEFINE_DUMMY_FLAG(NOLMS, false),
DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags),
DEFINE_DUMMY_FLAG(NOLMS, false),
DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false),
}; };
static FFlagDef PlayerPawnFlagDefs[] = static FFlagDef PlayerPawnFlagDefs[] =
{ {
// PlayerPawn flags // PlayerPawn flags

View file

@ -157,13 +157,15 @@ enum
ATAG_OBJECT, // pointer to an object; will be followed by GC ATAG_OBJECT, // pointer to an object; will be followed by GC
// The following are all for documentation during debugging and are // 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_FRAMEPOINTER, // pointer to extra stack frame space for this function
ATAG_DREGISTER, // pointer to a data register ATAG_DREGISTER, // pointer to a data register
ATAG_FREGISTER, // pointer to a float register ATAG_FREGISTER, // pointer to a float register
ATAG_SREGISTER, // pointer to a string register ATAG_SREGISTER, // pointer to a string register
ATAG_AREGISTER, // pointer to an address register ATAG_AREGISTER, // pointer to an address register
*/
ATAG_RNG, // pointer to FRandom 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.) ATAG_STATE = ATAG_GENERIC, // pointer to FState (cannot have its own type because there's no means to track inside the VM.)

View file

@ -87,7 +87,7 @@ begin:
OP(LFP): OP(LFP):
ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0); ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0);
reg.a[a] = f->GetExtra(); reg.a[a] = f->GetExtra();
reg.atag[a] = ATAG_FRAMEPOINTER; reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts.
NEXTOP; NEXTOP;
OP(LB): OP(LB):
@ -461,7 +461,7 @@ begin:
break; break;
case REGT_INT | REGT_ADDROF: case REGT_INT | REGT_ADDROF:
assert(C < f->NumRegD); assert(C < f->NumRegD);
::new(param) VMValue(&reg.d[C], ATAG_DREGISTER); ::new(param) VMValue(&reg.d[C], ATAG_GENERIC);
break; break;
case REGT_INT | REGT_KONST: case REGT_INT | REGT_KONST:
assert(C < sfunc->NumKonstD); assert(C < sfunc->NumKonstD);
@ -473,7 +473,7 @@ begin:
break; break;
case REGT_STRING | REGT_ADDROF: case REGT_STRING | REGT_ADDROF:
assert(C < f->NumRegS); assert(C < f->NumRegS);
::new(param) VMValue(&reg.s[C], ATAG_SREGISTER); ::new(param) VMValue(&reg.s[C], ATAG_GENERIC);
break; break;
case REGT_STRING | REGT_KONST: case REGT_STRING | REGT_KONST:
assert(C < sfunc->NumKonstS); assert(C < sfunc->NumKonstS);
@ -485,7 +485,7 @@ begin:
break; break;
case REGT_POINTER | REGT_ADDROF: case REGT_POINTER | REGT_ADDROF:
assert(C < f->NumRegA); assert(C < f->NumRegA);
::new(param) VMValue(&reg.a[C], ATAG_AREGISTER); ::new(param) VMValue(&reg.a[C], ATAG_GENERIC);
break; break;
case REGT_POINTER | REGT_KONST: case REGT_POINTER | REGT_KONST:
assert(C < sfunc->NumKonstA); assert(C < sfunc->NumKonstA);
@ -512,7 +512,7 @@ begin:
break; break;
case REGT_FLOAT | REGT_ADDROF: case REGT_FLOAT | REGT_ADDROF:
assert(C < f->NumRegF); assert(C < f->NumRegF);
::new(param) VMValue(&reg.f[C], ATAG_FREGISTER); ::new(param) VMValue(&reg.f[C], ATAG_GENERIC);
break; break;
case REGT_FLOAT | REGT_KONST: case REGT_FLOAT | REGT_KONST:
assert(C < sfunc->NumKonstF); assert(C < sfunc->NumKonstF);

View file

@ -2160,6 +2160,7 @@ void ZCCCompiler::InitFunctions()
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3)) if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
{ {
type = NewPointer(type); type = NewPointer(type);
flags |= VARF_Ref;
} }
else if (type == TypeVector2) else if (type == TypeVector2)
{ {

View file

@ -48,6 +48,7 @@ zscript/doom/cyberdemon.txt
zscript/doom/spidermaster.txt zscript/doom/spidermaster.txt
zscript/doom/keen.txt zscript/doom/keen.txt
zscript/doom/bossbrain.txt zscript/doom/bossbrain.txt
zscript/doom/weaponfist.txt
zscript/doom/deadthings.txt zscript/doom/deadthings.txt
zscript/doom/doomammo.txt zscript/doom/doomammo.txt

View file

@ -133,6 +133,7 @@ class Actor : Thinker native
// DECORATE compatible functions // DECORATE compatible functions
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false);
native int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT); native int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT);
native float GetDistance(bool checkz, int ptr = AAPTR_TARGET); native float GetDistance(bool checkz, int ptr = AAPTR_TARGET);
native float GetAngle(int flags, int ptr = AAPTR_DEFAULT); native float GetAngle(int flags, int ptr = AAPTR_DEFAULT);
@ -471,7 +472,6 @@ class Actor : Thinker native
native void A_DeQueueCorpse(); native void A_DeQueueCorpse();
native void A_ClearLastHeard(); native void A_ClearLastHeard();
native bool A_SelectWeapon(class<Weapon> whichweapon, int flags = 0); native bool A_SelectWeapon(class<Weapon> whichweapon, int flags = 0);
action native void A_Punch();
native void A_Feathers(); native void A_Feathers();
native void A_ClassBossHealth(); native void A_ClassBossHealth();
native void A_ShootGun(); native void A_ShootGun();

View file

@ -829,7 +829,7 @@ enum EStateType
struct FStateParamInfo struct FStateParamInfo
{ {
state mCallingState; state mCallingState;
EStateType mStateType; /*EStateType*/int mStateType;
int mPSPIndex; int mPSPIndex;
} }

View file

@ -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;
}
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// //

View file

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

View file

@ -473,6 +473,8 @@ class Weapon : StateProvider native
Stop; 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_ZoomFactor(float scale = 1, int flags = 0);
native action void A_SetCrosshair(int xhair); native action void A_SetCrosshair(int xhair);
const ZOOM_INSTANT = 1; const ZOOM_INSTANT = 1;