- 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); assert(proto->ArgumentTypes.Size() > 0);
auto selftypeptr = dyn_cast<PPointer>(proto->ArgumentTypes[0]); auto selftypeptr = dyn_cast<PPointer>(proto->ArgumentTypes[0]);
assert(selftypeptr != nullptr); assert(selftypeptr != nullptr);
variant.SelfClass = dyn_cast<PClass>(selftypeptr->PointedType); variant.SelfClass = dyn_cast<PStruct>(selftypeptr->PointedType);
assert(variant.SelfClass != nullptr); assert(variant.SelfClass != nullptr);
} }
else else

View File

@ -22,40 +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_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 // A_Saw
// //
@ -71,6 +37,29 @@ enum SAW_Flags
SF_STEALARMOR = 128, 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) DEFINE_ACTION_FUNCTION(AActor, A_Saw)
{ {
PARAM_ACTION_PROLOGUE(AActor); PARAM_ACTION_PROLOGUE(AActor);

View File

@ -79,7 +79,6 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
static FRandom pr_wpnreadysnd ("WpnReadySnd"); static FRandom pr_wpnreadysnd ("WpnReadySnd");
static FRandom pr_gunshot ("GunShot");
static const FGenericButtons ButtonChecks[] = 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); 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) DPSprite *player_t::GetPSprite(PSPLayers layer)
{ {
AActor *oldcaller = nullptr; AActor *oldcaller = nullptr;
@ -1381,6 +1390,15 @@ DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget, int aimfla
return pitch; 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) AActor *P_AimTarget(AActor *mo)
{ {
FTranslatedLineTarget t; 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) DEFINE_ACTION_FUNCTION(AActor, A_Light)
{ {
PARAM_SELF_PROLOGUE(AActor); 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); DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget = NULL, int aimflags = 0);
AActor *P_AimTarget(AActor *mo); AActor *P_AimTarget(AActor *mo);
void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch);
void DoReadyWeapon(AActor *self); void DoReadyWeapon(AActor *self);
void DoReadyWeaponToBob(AActor *self); void DoReadyWeaponToBob(AActor *self);
void DoReadyWeaponToFire(AActor *self, bool primary = true, bool secondary = true); void DoReadyWeaponToFire(AActor *self, bool primary = true, bool secondary = true);

View File

@ -1280,12 +1280,22 @@ void APlayerPawn::PlayRunning ()
void APlayerPawn::PlayAttacking () 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 () 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 () void APlayerPawn::ThrowPoisonBag ()

View File

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

View File

@ -1032,8 +1032,6 @@ struct AFuncDesc
// change every single use in case the parameters change. // change every single use in case the parameters change.
#define DECLARE_ACTION(name) extern VMNativeFunction *AActor_##name##_VMPtr; #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) \ #define DEFINE_ACTION_FUNCTION(cls, name) \
static int AF_##cls##_##name(VM_ARGS); \ static int AF_##cls##_##name(VM_ARGS); \
VMNativeFunction *cls##_##name##_VMPtr; \ VMNativeFunction *cls##_##name##_VMPtr; \
@ -1077,6 +1075,10 @@ void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self);
PARAM_PROLOGUE; \ PARAM_PROLOGUE; \
PARAM_OBJECT(self, type); 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; class PFunction;

View File

@ -37,6 +37,17 @@ VMEXPORTED_NATIVES_START
VMEXPORTED_NATIVES_FUNC(PostBeginPlay) VMEXPORTED_NATIVES_FUNC(PostBeginPlay)
VMEXPORTED_NATIVES_END 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> template<class T>
class DVMObject : public T class DVMObject : public T
{ {
@ -79,14 +90,7 @@ public:
else else
{ {
static int VIndex = -1; static int VIndex = -1;
if (VIndex < 0) if (VIndex < 0) VIndex = GetVirtualIndex(RUNTIME_CLASS(DObject), "Destroy");
{
// 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);
}
// Without the type cast this picks the 'void *' assignment... // Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this }; VMValue params[1] = { (DObject*)this };
VMFrameStack stack; VMFrameStack stack;

View File

@ -49,6 +49,7 @@ 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/weaponfist.txt
zscript/doom/weaponpistol.txt
zscript/doom/deadthings.txt zscript/doom/deadthings.txt
zscript/doom/doomammo.txt zscript/doom/doomammo.txt

View File

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

View File

@ -711,6 +711,7 @@ enum EPSpriteFlags
// Default psprite layers // Default psprite layers
enum EPSPLayers enum EPSPLayers
{ {
PSP_STRIFEHANDS = -1,
PSP_WEAPON = 1, PSP_WEAPON = 1,
PSP_FLASH = 1000, 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 // 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_WeaponReady(int flags = 0);
action native void A_Lower(); action native void A_Lower();
action native void A_Raise(); action native void A_Raise();
action native void A_FirePistol();
action native void A_FireShotgun(); action native void A_FireShotgun();
action native void A_FireShotgun2(); action native void A_FireShotgun2();

View File

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