mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 13:01:47 +00:00
- 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:
parent
7ff5069617
commit
3ce699bf9b
20 changed files with 205 additions and 126 deletions
|
@ -166,36 +166,27 @@ static TArray<CodePointerAlias> 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<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;
|
||||
int use = AmmoPerAttacks[j].ammocount;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -------------------------------------------------------
|
||||
|
|
|
@ -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<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
|
||||
//
|
||||
|
|
|
@ -289,6 +289,7 @@ public:
|
|||
class AWeapon : public AStateProvider
|
||||
{
|
||||
DECLARE_CLASS_WITH_META(AWeapon, AStateProvider, PClassWeapon)
|
||||
HAS_FIELDS
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
DWORD WeaponFlags;
|
||||
|
|
|
@ -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<FString> 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));
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -133,6 +133,7 @@ class Actor : Thinker native
|
|||
|
||||
// DECORATE compatible functions
|
||||
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 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<Weapon> whichweapon, int flags = 0);
|
||||
action native void A_Punch();
|
||||
native void A_Feathers();
|
||||
native void A_ClassBossHealth();
|
||||
native void A_ShootGun();
|
||||
|
|
|
@ -829,7 +829,7 @@ enum EStateType
|
|||
struct FStateParamInfo
|
||||
{
|
||||
state mCallingState;
|
||||
EStateType mStateType;
|
||||
/*EStateType*/int mStateType;
|
||||
int mPSPIndex;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
|
|
80
wadsrc/static/zscript/doom/weaponfist.txt
Normal file
80
wadsrc/static/zscript/doom/weaponfist.txt
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue