diff --git a/src/actor.h b/src/actor.h index 824f6d436..10dd4c871 100644 --- a/src/actor.h +++ b/src/actor.h @@ -822,6 +822,7 @@ public: // Return starting health adjusted by skill level int SpawnHealth() const; + virtual int GetMaxHealth(bool withupgrades = false) const; int GetGibHealth() const; double GetCameraHeight() const; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index ad7a3f891..0cc6e6e4c 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -213,6 +213,7 @@ DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha) DEFINE_FIELD_X(DehInfo, DehInfo, NoAutofreeze) DEFINE_FIELD_X(DehInfo, DehInfo, BFGCells) DEFINE_FIELD_X(DehInfo, DehInfo, BlueAC) +DEFINE_FIELD_X(DehInfo, DehInfo, MaxHealth) // Doom identified pickup items by their sprites. ZDoom prefers to use their // class type to identify them instead. To support the traditional Doom diff --git a/src/d_player.h b/src/d_player.h index 500a4098a..dc99255ec 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -92,15 +92,6 @@ public: virtual void BeginPlay () override; virtual bool UpdateWaterLevel (bool splash) override; - int GetMaxHealth(bool withupgrades = false) const; - void GiveDeathmatchInventory (); - - void GiveDefaultInventory (); - - // These are virtual on the script side only. - void PlayIdle(); - - int hasBuddha(); // returns 0 for no buddha, 1 for regular buddha and 2 for strong buddha enum EInvulState { @@ -112,7 +103,6 @@ public: int crouchsprite; - int MaxHealth; int BonusHealth; int MugShotMaxHealth; @@ -132,6 +122,7 @@ public: double UseRange; // [NS] Distance at which player can +use // Everything below this point is only used by scripted code or through the scripted variable interface. + int MaxHealth; int RunHealth; TObjPtr InvFirst; // first inventory item displayed on inventory bar @@ -157,6 +148,9 @@ public: }; +void PlayIdle(AActor *player); + + // // PlayerPawn flags // diff --git a/src/g_game.cpp b/src/g_game.cpp index 6006180ec..2477ed58a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1290,7 +1290,12 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { // [GRB] Give inventory specified in DECORATE - actor->GiveDefaultInventory (); + + IFVIRTUALPTR(actor, APlayerPawn, GiveDefaultInventory) + { + VMValue params[1] = { actor }; + VMCall(func, params, 1, nullptr, 0); + } p->ReadyWeapon = p->PendingWeapon; } diff --git a/src/namedef.h b/src/namedef.h index c905ee6fe..740bf59ad 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -1041,6 +1041,7 @@ xx(Slot) xx(SoundClass) xx(ViewBob) xx(DamageFade) +xx(MaxHealth) xx(BlueCard) xx(YellowCard) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index ab7ca86b8..02a24b93f 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1816,10 +1816,7 @@ int CheckInventory (AActor *activator, const char *type, bool max) { if (max) { - if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn))) - return static_cast(activator)->GetMaxHealth(); - else - return activator->SpawnHealth(); + return activator->GetMaxHealth(); } return activator->health; } @@ -4268,7 +4265,7 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) { - static_cast(actor)->MaxHealth = value; + actor->IntVar(NAME_MaxHealth) = value; } break; @@ -4428,14 +4425,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_Notarget: return !!(actor->flags3 & MF3_NOTARGET); case APROP_Notrigger: return !!(actor->flags6 & MF6_NOTRIGGER); case APROP_Dormant: return !!(actor->flags2 & MF2_DORMANT); - case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) - { - return static_cast(actor)->GetMaxHealth(); - } - else - { - return actor->SpawnHealth(); - } + case APROP_SpawnHealth: return actor->GetMaxHealth(); case APROP_JumpZ: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) { diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 368184329..f2384e832 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1935,34 +1935,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_Burst) return 0; } -//=========================================================================== -// -// A_Stop -// resets all velocity of the actor to 0 -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_Stop) -{ - PARAM_SELF_PROLOGUE(AActor); - self->Vel.Zero(); - if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) - { - self->player->mo->PlayIdle(); - self->player->Vel.Zero(); - } - return 0; -} - -static void CheckStopped(AActor *self) -{ - if (self->player != NULL && - self->player->mo == self && - !(self->player->cheats & CF_PREDICTING) && !self->Vel.isZero()) - { - self->player->mo->PlayIdle(); - self->player->Vel.Zero(); - } -} //=========================================================================== // @@ -2881,89 +2853,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetRoll) return 0; } -//=========================================================================== -// -// A_ScaleVelocity -// -// Scale actor's velocity. -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ScaleVelocity) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_FLOAT(scale); - PARAM_INT(ptr); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (ref == NULL) - { - return 0; - } - - bool was_moving = !ref->Vel.isZero(); - - ref->Vel *= scale; - - // If the actor was previously moving but now is not, and is a player, - // update its player variables. (See A_Stop.) - if (was_moving) - { - CheckStopped(ref); - } - return 0; -} - -//=========================================================================== -// -// A_ChangeVelocity -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ChangeVelocity) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_FLOAT (x) - PARAM_FLOAT (y) - PARAM_FLOAT (z) - PARAM_INT (flags) - PARAM_INT (ptr) - - AActor *ref = COPY_AAPTR(self, ptr); - - if (ref == NULL) - { - return 0; - } - - INTBOOL was_moving = !ref->Vel.isZero(); - - DVector3 vel(x, y, z); - double sina = ref->Angles.Yaw.Sin(); - double cosa = ref->Angles.Yaw.Cos(); - - if (flags & 1) // relative axes - make x, y relative to actor's current angle - { - vel.X = x*cosa - y*sina; - vel.Y = x*sina + y*cosa; - } - if (flags & 2) // discard old velocity - replace old velocity with new velocity - { - ref->Vel = vel; - } - else // add new velocity to old velocity - { - ref->Vel += vel; - } - - if (was_moving) - { - CheckStopped(ref); - } - return 0; -} - //=========================================================================== // // A_SetUserVar diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index c0c8ada83..a02d106c1 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -771,7 +771,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang pc->Vel.Zero(); pc->player->Vel.Zero(); - static_cast(pc)->PlayIdle (); + PlayIdle (pc); pc->player->ConversationPC = pc; pc->player->ConversationNPC = npc; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 75b7c95e7..348aeae8d 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -984,6 +984,25 @@ DEFINE_ACTION_FUNCTION(AActor, TriggerPainChance) ================== */ +//=========================================================================== +// +// APlayerPawn :: hasBuddha +// +//=========================================================================== + +static int hasBuddha(player_t *player) +{ + if (player->playerstate == PST_DEAD) return 0; + if (player->cheats & CF_BUDDHA2) return 2; + + if ((player->cheats & CF_BUDDHA) || + (player->mo->flags7 & MF7_BUDDHA) || + player->mo->FindInventory(PClass::FindActor(NAME_PowerBuddha), true) != nullptr) return 1; + + return 0; +} + + // Returns the amount of damage actually inflicted upon the target, or -1 if // the damage was cancelled. @@ -1312,7 +1331,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. // [MC]Buddha2 absorbs telefrag damage, and anything else thrown their way. - int buddha = player->mo->hasBuddha(); + int buddha = hasBuddha(player); if (flags & DMG_FORCED) buddha = 0; if (telefragDamage && buddha == 1) buddha = 0; if (buddha) @@ -1759,7 +1778,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPain target->health -= damage; if (target->health <= 0) { // Death - int buddha = player->mo->hasBuddha(); + int buddha = hasBuddha(player); if (telefragDamage && buddha == 1) buddha = 0; if (buddha) { // [SP] Save the player... diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 63c8926cb..8661450b6 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2279,7 +2279,7 @@ explode: // Don't affect main player when voodoo dolls stop: if (player && player->mo == mo && !(player->cheats & CF_PREDICTING)) { - player->mo->PlayIdle (); + PlayIdle (player->mo); } mo->Vel.X = mo->Vel.Y = 0; @@ -5077,7 +5077,11 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) if (deathmatch) { // Give all cards in death match mode. - p->mo->GiveDeathmatchInventory (); + IFVIRTUALPTR(p->mo, APlayerPawn, GiveDeathmatchInventory) + { + VMValue params[1] = { p->mo }; + VMCall(func, params, 1, nullptr, 0); + } } else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN) || sv_singleplayerrespawn || !!G_SkillProperty(SKILLP_PlayerRespawn)) && state == PST_REBORN && oldactor != NULL) @@ -7072,6 +7076,18 @@ int AActor::SpawnHealth() const } } +int AActor::GetMaxHealth(bool withupgrades) const +{ + int ret = 100; + IFVIRTUAL(AActor, GetMaxHealth) + { + VMValue param[] = { const_cast(this), withupgrades }; + VMReturn r(&ret); + VMCall(func, param, 2, &r, 1); + } + return ret; +} + FState *AActor::GetRaiseState() { if (!(flags & MF_CORPSE)) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 4938e1860..e8de24ff7 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -406,7 +406,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, int f } if (vx == 0 && vy == 0 && thing->player != NULL && thing->player->mo == thing && !predicting) { - thing->player->mo->PlayIdle (); + PlayIdle (thing->player->mo); } return true; } diff --git a/src/p_user.cpp b/src/p_user.cpp index 29243fb48..0f4216e67 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -919,74 +919,6 @@ void APlayerPawn::PostBeginPlay() } } -//=========================================================================== -// -// APlayerPawn :: GiveDeathmatchInventory -// -// Gives players items they should have in addition to their default -// inventory when playing deathmatch. (i.e. all keys) -// -//=========================================================================== - -void APlayerPawn::GiveDeathmatchInventory() -{ - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - if (PClassActor::AllActorClasses[i]->IsDescendantOf (PClass::FindActor(NAME_Key))) - { - auto key = GetDefaultByType (PClassActor::AllActorClasses[i]); - if (key->special1 != 0) - { - key = Spawn(PClassActor::AllActorClasses[i]); - if (!CallTryPickup (key, this)) - { - key->Destroy (); - } - } - } - } -} - -//=========================================================================== -// -// APlayerPawn :: hasBuddha -// -//=========================================================================== - -int APlayerPawn::hasBuddha() -{ - if (player->playerstate == PST_DEAD) return 0; - if (player->cheats & CF_BUDDHA2) return 2; - - if ((player->cheats & CF_BUDDHA) || - (player->mo->flags7 & MF7_BUDDHA) || - player->mo->FindInventory (PClass::FindActor(NAME_PowerBuddha),true) != nullptr) return 1; - - return 0; -} - -//=========================================================================== -// -// APlayerPawn :: GetMaxHealth -// -// only needed because Boom screwed up Dehacked. -// -//=========================================================================== - -int APlayerPawn::GetMaxHealth(bool withupgrades) const -{ - int ret = MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth); - if (withupgrades) ret += stamina + BonusHealth; - return ret; -} - -DEFINE_ACTION_FUNCTION(APlayerPawn, GetMaxHealth) -{ - PARAM_SELF_PROLOGUE(APlayerPawn); - PARAM_BOOL(withupgrades); - ACTION_RETURN_INT(self->GetMaxHealth(withupgrades)); -} - //=========================================================================== // // APlayerPawn :: UpdateWaterLevel @@ -1023,30 +955,15 @@ bool APlayerPawn::UpdateWaterLevel (bool splash) // //=========================================================================== -void APlayerPawn::PlayIdle () +void PlayIdle (AActor *player) { - IFVIRTUAL(APlayerPawn, PlayIdle) + IFVIRTUALPTR(player, APlayerPawn, PlayIdle) { - VMValue params[1] = { (DObject*)this }; + VMValue params[1] = { (DObject*)player }; VMCall(func, params, 1, nullptr, 0); } } -//=========================================================================== -// -// APlayerPawn :: GiveDefaultInventory -// -//=========================================================================== - -void APlayerPawn::GiveDefaultInventory () -{ - IFVIRTUAL(APlayerPawn, GiveDefaultInventory) - { - VMValue params[1] = { (DObject*)this }; - VMCall(func, params, 1, nullptr, 0); - } -} - //=========================================================================== // // A_PlayerScream diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 6e3a1f08b..6a80ed434 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -321,10 +321,6 @@ struct VMValue { sp = s; } - VMValue(DObject *v) - { - a = v; - } VMValue(void *v) { a = v; diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index e8823609b..fd4cb3d79 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -9,6 +9,7 @@ version "3.7" #include "zscript/actor_checks.txt" #include "zscript/actor_interaction.txt" #include "zscript/actor_inventory.txt" +#include "zscript/actor_actions.txt" #include "zscript/events.txt" #include "zscript/destructible.txt" #include "zscript/level_compatibility.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 3d60e0dc9..e9058c67a 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -615,6 +615,10 @@ class Actor : Thinker native native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false); native void RestoreDamage(); native clearscope int SpawnHealth() const; + virtual clearscope int GetMaxHealth(bool withupgrades = false) const // this only exists to make checks for player health easier so they can avoid the type check and just call this method. + { + return SpawnHealth(); + } native void SetDamage(int dmg); native clearscope double Distance2D(Actor other) const; native clearscope double Distance3D(Actor other) const; @@ -1086,7 +1090,6 @@ class Actor : Thinker native native int GetRadiusDamage(Actor thing, int damage, int distance, int fulldmgdistance = 0, bool oldradiusdmg = false); native int RadiusAttack(Actor bombsource, int bombdamage, int bombdistance, Name bombmod = 'none', int flags = RADF_HURTSOURCE, int fulldamagedistance = 0); - native void A_Stop(); native void A_Respawn(int flags = 1); native void A_RestoreSpecialPosition(); native void A_QueueCorpse(); @@ -1097,8 +1100,6 @@ class Actor : Thinker native native void A_SetAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); - native void A_ScaleVelocity(double scale, int ptr = AAPTR_DEFAULT); - native void A_ChangeVelocity(double x = 0, double y = 0, double z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); deprecated("2.3") native void A_SetUserVar(name varname, int value); deprecated("2.3") native void A_SetUserArray(name varname, int index, int value); deprecated("2.3") native void A_SetUserVarFloat(name varname, double value); diff --git a/wadsrc/static/zscript/actor_actions.txt b/wadsrc/static/zscript/actor_actions.txt new file mode 100644 index 000000000..e3ab15900 --- /dev/null +++ b/wadsrc/static/zscript/actor_actions.txt @@ -0,0 +1,99 @@ +extend class Actor +{ + + private void CheckStopped() + { + let player = self.player; + if (player && player.mo == self && !(player.cheats & CF_PREDICTING) && Vel == (0, 0, 0)) + { + player.mo.PlayIdle(); + player.Vel = (0, 0); + } + } + + //=========================================================================== + // + // A_Stop + // resets all velocity of the actor to 0 + // + //=========================================================================== + void A_Stop() + { + let player = self.player; + Vel = (0, 0, 0); + CheckStopped(); + } + + //=========================================================================== + // + // A_ScaleVelocity + // + // Scale actor's velocity. + // + //=========================================================================== + + void A_ScaleVelocity(double scale, int ptr = AAPTR_DEFAULT) + { + + let ref = GetPointer(ptr); + + if (ref == NULL) + { + return; + } + + bool was_moving = ref.Vel != (0, 0, 0); + + ref.Vel *= scale; + + // If the actor was previously moving but now is not, and is a player, + // update its player variables. (See A_Stop.) + if (was_moving) + { + ref.CheckStopped(); + } + } + + //=========================================================================== + // + // A_ChangeVelocity + // + //=========================================================================== + + void A_ChangeVelocity(double x = 0, double y = 0, double z = 0, int flags = 0, int ptr = AAPTR_DEFAULT) + { + let ref = GetPointer(ptr); + + if (ref == NULL) + { + return; + } + + bool was_moving = ref.Vel != (0, 0, 0); + + let newvel = (x, y, z); + double sina = sin(ref.Angle); + double cosa = cos(ref.Angle); + + if (flags & 1) // relative axes - make x, y relative to actor's current angle + { + newvel.X = x * cosa - y * sina; + newvel.Y = x * sina + y * cosa; + } + if (flags & 2) // discard old velocity - replace old velocity with new velocity + { + ref.Vel = newvel; + } + else // add new velocity to old velocity + { + ref.Vel += newvel; + } + + if (was_moving) + { + ref.CheckStopped(); + } + } + + +} diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 466529aea..ef4fa3bb0 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -724,6 +724,7 @@ struct DehInfo native native readonly int NoAutofreeze; native readonly int BFGCells; native readonly int BlueAC; + native readonly int MaxHealth; } struct State native diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 0484193a8..17b1e26e3 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1847,6 +1847,44 @@ class PlayerPawn : Actor native } } + //=========================================================================== + // + // APlayerPawn :: GiveDeathmatchInventory + // + // Gives players items they should have in addition to their default + // inventory when playing deathmatch. (i.e. all keys) + // + //=========================================================================== + + virtual void GiveDeathmatchInventory() + { + for ( int i = 0; i < AllActorClasses.Size(); ++i) + { + let cls = (class)(AllActorClasses[i]); + if (cls) + { + let keyobj = GetDefaultByType(cls); + if (keyobj.special1 != 0) + { + GiveInventoryType(cls); + } + } + } + } + + //=========================================================================== + // + // + // + //=========================================================================== + + override int GetMaxHealth(bool withupgrades) const + { + int ret = MaxHealth > 0? MaxHealth : (compat_dehhealth? 100 : deh.MaxHealth); + if (withupgrades) ret += stamina + BonusHealth; + return ret; + } + //=========================================================================== // // @@ -2347,7 +2385,6 @@ class PlayerPawn : Actor native // //---------------------------------------------------------------------------- - native clearscope int GetMaxHealth(bool withupgrades = false) const; native clearscope static String GetPrintableDisplayName(Class cls); native void CheckMusicChange(); native void CheckEnvironment();