- scriptified the pistol to test if struct member functions work.

- made APlayerPawn::PlayAttacking(2) virtual script functions so that mods have better control over player animations. Note that these have no native base so they skip the templated interface for managing virtual functions.
This commit is contained in:
Christoph Oelckers 2016-11-19 13:56:29 +01:00
parent de8cacc465
commit d50da34664
15 changed files with 214 additions and 126 deletions

View file

@ -2535,7 +2535,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArra
assert(proto->ArgumentTypes.Size() > 0);
auto selftypeptr = dyn_cast<PPointer>(proto->ArgumentTypes[0]);
assert(selftypeptr != nullptr);
variant.SelfClass = dyn_cast<PClass>(selftypeptr->PointedType);
variant.SelfClass = dyn_cast<PStruct>(selftypeptr->PointedType);
assert(variant.SelfClass != nullptr);
}
else

View file

@ -22,40 +22,6 @@ static FRandom pr_firerail ("FireRail");
static FRandom pr_bfgspray ("BFGSpray");
static FRandom pr_oldbfg ("OldBFG");
//
// A_FirePistol
//
DEFINE_ACTION_FUNCTION(AActor, A_FirePistol)
{
PARAM_ACTION_PROLOGUE(AActor);
bool accurate;
if (self->player != nullptr)
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != nullptr && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
P_SetPsprite(self->player, PSP_FLASH, weapon->FindState(NAME_Flash), true);
}
self->player->mo->PlayAttacking2 ();
accurate = !self->player->refire;
}
else
{
accurate = true;
}
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
P_GunShot (self, accurate, PClass::FindActor(NAME_BulletPuff), P_BulletSlope (self));
return 0;
}
//
// A_Saw
//
@ -71,6 +37,29 @@ enum SAW_Flags
SF_STEALARMOR = 128,
};
static FRandom pr_gunshot("GunShot");
//
// P_GunShot
//
void P_GunShot(AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch)
{
DAngle angle;
int damage;
damage = 5 * (pr_gunshot() % 3 + 1);
angle = mo->Angles.Yaw;
if (!accurate)
{
angle += pr_gunshot.Random2() * (5.625 / 256);
}
P_LineAttack(mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
}
DEFINE_ACTION_FUNCTION(AActor, A_Saw)
{
PARAM_ACTION_PROLOGUE(AActor);

View file

@ -79,7 +79,6 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static FRandom pr_wpnreadysnd ("WpnReadySnd");
static FRandom pr_gunshot ("GunShot");
static const FGenericButtons ButtonChecks[] =
{
@ -184,6 +183,16 @@ void P_SetPsprite(player_t *player, PSPLayers id, FState *state, bool pending)
player->GetPSprite(id)->SetState(state, pending);
}
DEFINE_ACTION_FUNCTION(_Player, SetPSprite) // the underscore is needed to get past the name mangler which removes the first clas name character to match the class representation (needs to be fixed in a later commit)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_INT(id);
PARAM_POINTER(state, FState);
PARAM_BOOL_DEF(pending);
P_SetPsprite(self, (PSPLayers)id, state, pending);
return 0;
}
DPSprite *player_t::GetPSprite(PSPLayers layer)
{
AActor *oldcaller = nullptr;
@ -1381,6 +1390,15 @@ DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget, int aimfla
return pitch;
}
DEFINE_ACTION_FUNCTION(AActor, BulletSlope)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER_DEF(t, FTranslatedLineTarget);
PARAM_INT_DEF(aimflags);
ACTION_RETURN_FLOAT(P_BulletSlope(self, t, aimflags).Degrees);
}
AActor *P_AimTarget(AActor *mo)
{
FTranslatedLineTarget t;
@ -1395,25 +1413,6 @@ DEFINE_ACTION_FUNCTION(AActor, AimTarget)
}
//
// P_GunShot
//
void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch)
{
DAngle angle;
int damage;
damage = 5*(pr_gunshot()%3+1);
angle = mo->Angles.Yaw;
if (!accurate)
{
angle += pr_gunshot.Random2 () * (5.625 / 256);
}
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
}
DEFINE_ACTION_FUNCTION(AActor, A_Light)
{
PARAM_SELF_PROLOGUE(AActor);

View file

@ -111,8 +111,6 @@ void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac);
DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget = NULL, int aimflags = 0);
AActor *P_AimTarget(AActor *mo);
void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch);
void DoReadyWeapon(AActor *self);
void DoReadyWeaponToBob(AActor *self);
void DoReadyWeaponToFire(AActor *self, bool primary = true, bool secondary = true);

View file

@ -1280,12 +1280,22 @@ void APlayerPawn::PlayRunning ()
void APlayerPawn::PlayAttacking ()
{
if (MissileState != NULL) SetState (MissileState);
static int VIndex = -1;
if (VIndex < 0) VIndex = GetVirtualIndex(RUNTIME_CLASS(APlayerPawn), "PlayAttacking");
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
stack.Call(this->GetClass()->Virtuals[VIndex], params, 1, nullptr, 0, nullptr);
}
void APlayerPawn::PlayAttacking2 ()
{
if (MeleeState != NULL) SetState (MeleeState);
static int VIndex = -1;
if (VIndex < 0) VIndex = GetVirtualIndex(RUNTIME_CLASS(APlayerPawn), "PlayAttacking2");
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
stack.Call(this->GetClass()->Virtuals[VIndex], params, 1, nullptr, 0, nullptr);
}
void APlayerPawn::ThrowPoisonBag ()

View file

@ -6706,7 +6706,7 @@ FxMemberFunctionCall::~FxMemberFunctionCall()
FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
{
ABORT(ctx.Class);
PClass *cls;
PStruct *cls;
bool staticonly = false;
bool novirtual = false;
@ -6714,13 +6714,21 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
{
// 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.
cls = PClass::FindClass(static_cast<FxIdentifier *>(Self)->Identifier);
if (cls != nullptr && cls->bExported)
PClass *ccls = PClass::FindClass(static_cast<FxIdentifier *>(Self)->Identifier);
if (ccls != nullptr)
{
if (ccls->bExported)
{
cls = ccls;
staticonly = true;
goto isresolved;
}
}
else
{
// Todo: static struct members need to work as well.
}
}
SAFE_RESOLVE(Self, ctx);
if (Self->ExprType == EFX_Super)
@ -6755,9 +6763,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
{
auto ptype = static_cast<PPointer *>(Self->ValueType)->PointedType;
if (ptype->IsKindOf(RUNTIME_CLASS(PClass)))
if (ptype->IsKindOf(RUNTIME_CLASS(PStruct)))
{
cls = static_cast<PClass *>(ptype);
cls = static_cast<PStruct *>(ptype);
}
else
{
@ -6773,6 +6781,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
return nullptr;
}
// Todo: handle member calls from instantiated structs.
isresolved:
bool error = false;
PFunction *afd = FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error);
@ -6792,7 +6802,8 @@ isresolved:
if (staticonly && (afd->Variants[0].Flags & VARF_Method))
{
auto clstype = dyn_cast<PClass>(ctx.Class);
if (clstype == nullptr || !clstype->IsDescendantOf(cls))
auto ccls = dyn_cast<PClass>(cls);
if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls))
{
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
@ -7067,7 +7078,7 @@ VMFunction *FxVMFunctionCall::GetDirectFunction()
if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual))
{
unsigned imp = Function->GetImplicitArgs();
if (Function->Variants[0].ArgFlags.Size() <= imp || !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr;
if (Function->Variants[0].ArgFlags.Size() > imp && !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr;
return Function->Variants[0].Implementation;
}

View file

@ -1032,8 +1032,6 @@ struct AFuncDesc
// change every single use in case the parameters change.
#define DECLARE_ACTION(name) extern VMNativeFunction *AActor_##name##_VMPtr;
// This distinction is here so that CALL_ACTION produces errors when trying to
// access a function that requires parameters.
#define DEFINE_ACTION_FUNCTION(cls, name) \
static int AF_##cls##_##name(VM_ARGS); \
VMNativeFunction *cls##_##name##_VMPtr; \
@ -1077,6 +1075,10 @@ void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self);
PARAM_PROLOGUE; \
PARAM_OBJECT(self, type);
// for structs we need to check for ATAG_GENERIC instead of ATAG_OBJECT
#define PARAM_SELF_STRUCT_PROLOGUE(type) \
PARAM_PROLOGUE; \
PARAM_POINTER(self, type);
class PFunction;

View file

@ -37,6 +37,17 @@ VMEXPORTED_NATIVES_START
VMEXPORTED_NATIVES_FUNC(PostBeginPlay)
VMEXPORTED_NATIVES_END
inline int GetVirtualIndex(PClass *cls, const char *funcname)
{
// Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override.
auto sym = dyn_cast<PFunction>(cls->Symbols.FindSymbol(funcname, false));
assert(sym != nullptr);
auto VIndex = sym->Variants[0].Implementation->VirtualIndex;
assert(VIndex >= 0);
return VIndex;
}
template<class T>
class DVMObject : public T
{
@ -79,14 +90,7 @@ public:
else
{
static int VIndex = -1;
if (VIndex < 0)
{
// Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override.
auto sym = dyn_cast<PFunction>(RUNTIME_CLASS(DObject)->Symbols.FindSymbol("Destroy", false));
assert(sym != nullptr);
VIndex = sym->Variants[0].Implementation->VirtualIndex;
assert(VIndex >= 0);
}
if (VIndex < 0) VIndex = GetVirtualIndex(RUNTIME_CLASS(DObject), "Destroy");
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;

View file

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

View file

@ -78,6 +78,7 @@ class Actor : Thinker native
native void SetXYZ(vector3 newpos);
native Actor GetPointer(int aaptr);
native void FaceMovementDirection();
native double BulletSlope(out FTranslatedLineTarget pLineTarget = null, int aimflags = 0);
native Actor AimTarget();
native bool CheckMissileSpawn(double maxdist);
native bool CheckPosition(Vector2 pos, bool actorsonly = false);

View file

@ -711,6 +711,7 @@ enum EPSpriteFlags
// Default psprite layers
enum EPSPLayers
{
PSP_STRIFEHANDS = -1,
PSP_WEAPON = 1,
PSP_FLASH = 1000,
};

View file

@ -13,53 +13,6 @@ class DoomWeapon : Weapon
}
// --------------------------------------------------------------------------
//
// Pistol
//
// --------------------------------------------------------------------------
class Pistol : DoomWeapon
{
Default
{
Weapon.SelectionOrder 1900;
Weapon.AmmoUse 1;
Weapon.AmmoGive 20;
Weapon.AmmoType "Clip";
Obituary "$OB_MPPISTOL";
+WEAPON.WIMPY_WEAPON
Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED";
Tag "$TAG_PISTOL";
}
States
{
Ready:
PISG A 1 A_WeaponReady;
Loop;
Deselect:
PISG A 1 A_Lower;
Loop;
Select:
PISG A 1 A_Raise;
Loop;
Fire:
PISG A 4;
PISG B 6 A_FirePistol;
PISG C 4;
PISG B 5 A_ReFire;
Goto Ready;
Flash:
PISF A 7 Bright A_Light1;
Goto LightDone;
PISF A 7 Bright A_Light1;
Goto LightDone;
Spawn:
PIST A -1;
Stop;
}
}
// --------------------------------------------------------------------------
//
// Chainsaw

View file

@ -0,0 +1,100 @@
// --------------------------------------------------------------------------
//
// Pistol
//
// --------------------------------------------------------------------------
class Pistol : DoomWeapon
{
Default
{
Weapon.SelectionOrder 1900;
Weapon.AmmoUse 1;
Weapon.AmmoGive 20;
Weapon.AmmoType "Clip";
Obituary "$OB_MPPISTOL";
+WEAPON.WIMPY_WEAPON
Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED";
Tag "$TAG_PISTOL";
}
States
{
Ready:
PISG A 1 A_WeaponReady;
Loop;
Deselect:
PISG A 1 A_Lower;
Loop;
Select:
PISG A 1 A_Raise;
Loop;
Fire:
PISG A 4;
PISG B 6 A_FirePistol;
PISG C 4;
PISG B 5 A_ReFire;
Goto Ready;
Flash:
PISF A 7 Bright A_Light1;
Goto LightDone;
PISF A 7 Bright A_Light1;
Goto LightDone;
Spawn:
PIST A -1;
Stop;
}
}
//===========================================================================
//
// Code (must be attached to StateProvider)
//
//===========================================================================
extend class StateProvider
{
//===========================================================================
// This is also used by the shotgun and chaingun
//===========================================================================
protected action void GunShot(bool accurate, Class<Actor> pufftype, double pitch)
{
int damage = 5 * random[GunShot](1, 3);
double ang = angle;
if (!accurate)
{
ang += Random2[GunShot]() * (5.625 / 256);
}
LineAttack(ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', pufftype);
}
//===========================================================================
action void A_FirePistol()
{
bool accurate;
if (player != null)
{
Weapon weap = player.ReadyWeapon;
if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite)
{
if (!weap.DepleteAmmo (weap.bAltFire, true, 1))
return;
player.SetPsprite(PSP_FLASH, weap.FindState('Flash'), true);
}
player.mo.PlayAttacking2 ();
accurate = !player.refire;
}
else
{
accurate = true;
}
A_PlaySound ("weapons/pistol", CHAN_WEAPON);
GunShot (accurate, "BulletPuff", BulletSlope ());
}
}

View file

@ -49,7 +49,6 @@ class StateProvider : Inventory native
action native void A_WeaponReady(int flags = 0);
action native void A_Lower();
action native void A_Raise();
action native void A_FirePistol();
action native void A_FireShotgun();
action native void A_FireShotgun2();

View file

@ -38,6 +38,17 @@ class PlayerPawn : Actor native
Player.ViewBob 1;
Obituary "$OB_MPDEFAULT";
}
virtual void PlayAttacking ()
{
if (MissileState != null) SetState (MissileState);
}
virtual void PlayAttacking2 ()
{
if (MeleeState != null) SetState (MeleeState);
}
}
class PlayerChunk : PlayerPawn native
@ -57,3 +68,12 @@ class PlayerChunk : PlayerPawn native
-TELESTOMP
}
}
class PSprite : Object native
{
}
struct Player native
{
native void SetPsprite(int id, State stat, bool pending = false);
}