From d50da34664d40b4789d53d7146e422c13b1390e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 19 Nov 2016 13:56:29 +0100 Subject: [PATCH] - 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. --- src/dobjtype.cpp | 2 +- src/g_doom/a_doomweaps.cpp | 57 +++++------ src/p_pspr.cpp | 39 ++++---- src/p_pspr.h | 2 - src/p_user.cpp | 14 ++- src/scripting/codegeneration/codegen.cpp | 29 ++++-- src/scripting/vm/vm.h | 6 +- src/virtual.h | 20 ++-- wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor.txt | 1 + wadsrc/static/zscript/constants.txt | 1 + wadsrc/static/zscript/doom/doomweapons.txt | 47 --------- wadsrc/static/zscript/doom/weaponpistol.txt | 100 ++++++++++++++++++++ wadsrc/static/zscript/shared/inventory.txt | 1 - wadsrc/static/zscript/shared/player.txt | 20 ++++ 15 files changed, 214 insertions(+), 126 deletions(-) create mode 100644 wadsrc/static/zscript/doom/weaponpistol.txt diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 682cc0fd5..5154d1689 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2535,7 +2535,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArra assert(proto->ArgumentTypes.Size() > 0); auto selftypeptr = dyn_cast(proto->ArgumentTypes[0]); assert(selftypeptr != nullptr); - variant.SelfClass = dyn_cast(selftypeptr->PointedType); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); assert(variant.SelfClass != nullptr); } else diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 95ae4dfb6..77085f5b5 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -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); diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 1ba452b2b..d47156d16 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -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); diff --git a/src/p_pspr.h b/src/p_pspr.h index d75f08b06..3c8f0097e 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -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); diff --git a/src/p_user.cpp b/src/p_user.cpp index 00e58e5ef..6f58f3cc8 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -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 () diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 4a8f4b55b..3577ac1ec 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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,11 +6714,19 @@ 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(Self)->Identifier); - if (cls != nullptr && cls->bExported) + PClass *ccls = PClass::FindClass(static_cast(Self)->Identifier); + if (ccls != nullptr) { - staticonly = true; - goto isresolved; + if (ccls->bExported) + { + cls = ccls; + staticonly = true; + goto isresolved; + } + } + else + { + // Todo: static struct members need to work as well. } } SAFE_RESOLVE(Self, ctx); @@ -6755,9 +6763,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { auto ptype = static_cast(Self->ValueType)->PointedType; - if (ptype->IsKindOf(RUNTIME_CLASS(PClass))) + if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) { - cls = static_cast(ptype); + cls = static_cast(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(ctx.Class); - if (clstype == nullptr || !clstype->IsDescendantOf(cls)) + auto ccls = dyn_cast(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; } diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 407080f2c..670c5a982 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -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; diff --git a/src/virtual.h b/src/virtual.h index d3a2bfa3a..e0679df74 100644 --- a/src/virtual.h +++ b/src/virtual.h @@ -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(cls->Symbols.FindSymbol(funcname, false)); + assert(sym != nullptr); + auto VIndex = sym->Variants[0].Implementation->VirtualIndex; + assert(VIndex >= 0); + return VIndex; +} + template 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(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; diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 14493875a..2f5a420a3 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -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 diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 744128cdd..0fd6d2282 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -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); diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index cf0cbdc5c..cfdc98425 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -711,6 +711,7 @@ enum EPSpriteFlags // Default psprite layers enum EPSPLayers { + PSP_STRIFEHANDS = -1, PSP_WEAPON = 1, PSP_FLASH = 1000, }; diff --git a/wadsrc/static/zscript/doom/doomweapons.txt b/wadsrc/static/zscript/doom/doomweapons.txt index b75cfa967..60b5ba157 100644 --- a/wadsrc/static/zscript/doom/doomweapons.txt +++ b/wadsrc/static/zscript/doom/doomweapons.txt @@ -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 diff --git a/wadsrc/static/zscript/doom/weaponpistol.txt b/wadsrc/static/zscript/doom/weaponpistol.txt new file mode 100644 index 000000000..45a1ebecd --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponpistol.txt @@ -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 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 ()); + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index 8337c5399..d66843593 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -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(); diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 224011acd..e2bbefbea 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -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); +} \ No newline at end of file