From 78d6832d14ac76c2de4a6730f06e563bacb4050a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 08:17:30 +0100 Subject: [PATCH] - P_UndoPlayerMorph scriptified. Not tested yet and still missing a new native interface. --- src/d_player.h | 1 - src/g_inventory/a_weapons.cpp | 68 ++++--- src/g_inventory/a_weapons.h | 4 +- src/g_shared/a_morph.cpp | 18 +- src/p_saveg.cpp | 2 +- src/p_user.cpp | 36 +--- src/scripting/thingdef.cpp | 1 + wadsrc/static/zscript/inventory/weapons.txt | 28 +++ wadsrc/static/zscript/shared/morph.txt | 192 ++++++++++++++++++++ wadsrc/static/zscript/shared/player.txt | 18 +- 10 files changed, 301 insertions(+), 67 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index ecb3a71ea..9341310e6 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -105,7 +105,6 @@ public: void GiveDeathmatchInventory (); void FilterCoopRespawnInventory (APlayerPawn *oldplayer); - void SetupWeaponSlots (); void GiveDefaultInventory (); // These are virtual on the script side only. diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index a72a10d7e..9cef1686e 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -382,31 +382,6 @@ DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo) } -//=========================================================================== -// -// AWeapon :: PostMorphWeapon -// -// Bring this weapon up after a player unmorphs. -// -//=========================================================================== - -void AWeapon::PostMorphWeapon () -{ - DPSprite *pspr; - if (Owner == nullptr) - { - return; - } - Owner->player->PendingWeapon = WP_NOCHANGE; - Owner->player->ReadyWeapon = this; - Owner->player->refire = 0; - - pspr = Owner->player->GetPSprite(PSP_WEAPON); - pspr->y = WEAPONBOTTOM; - pspr->ResetInterpolation(); - pspr->SetState(GetUpState()); -} - //=========================================================================== // // AWeapon :: GetUpState @@ -1382,6 +1357,49 @@ void P_PlaybackKeyConfWeapons(FWeaponSlots *slots) PlayingKeyConf = nullptr; } +//=========================================================================== +// +// APlayerPawn :: SetupWeaponSlots +// +// Sets up the default weapon slots for this player. If this is also the +// local player, determines local modifications and sends those across the +// network. Ignores voodoo dolls. +// +//=========================================================================== + +void FWeaponSlots::SetupWeaponSlots(APlayerPawn *pp) +{ + auto player = pp->player; + if (player != nullptr && player->mo == pp) + { + player->weapons.StandardSetup(pp->GetClass()); + // If we're the local player, then there's a bit more work to do. + // This also applies if we're a bot and this is the net arbitrator. + if (player - players == consoleplayer || + (player->Bot != nullptr && consoleplayer == Net_Arbitrator)) + { + FWeaponSlots local_slots(player->weapons); + if (player->Bot != nullptr) + { // Bots only need weapons from KEYCONF, not INI modifications. + P_PlaybackKeyConfWeapons(&local_slots); + } + else + { + local_slots.LocalSetup(pp->GetClass()); + } + local_slots.SendDifferences(int(player - players), player->weapons); + } + } +} + +DEFINE_ACTION_FUNCTION(FWeaponSlots, SetupWeaponSlots) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(pawn, APlayerPawn); + FWeaponSlots::SetupWeaponSlots(pawn); + return 0; +} + //=========================================================================== // // P_SetupWeapons_ntohton diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 1e0634ddd..3f5d34dd8 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -3,6 +3,7 @@ #include "a_pickups.h" class PClassActor; class AWeapon; +class APlayerPawn; class FWeaponSlot { @@ -71,6 +72,7 @@ struct FWeaponSlots void SendDifferences(int playernum, const FWeaponSlots &other); int RestoreSlots (FConfigFile *config, const char *section); void PrintSettings(); + static void SetupWeaponSlots(APlayerPawn *pp); void AddSlot(int slot, PClassActor *type, bool feedback); void AddSlotDefault(int slot, PClassActor *type, bool feedback); @@ -126,8 +128,6 @@ public: void Finalize(FStateDefinitions &statedef) override; void Serialize(FSerializer &arc) override; - void PostMorphWeapon(); - // scripted virtuals. FState *GetUpState (); FState *GetDownState (); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index c97e3111d..a5216a226 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -38,6 +38,18 @@ static FRandom pr_morphmonst ("MorphMonster"); + +bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) +{ + return false; +} + +bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) +{ + return false; +} + +#if 0 void EndAllPowerupEffects(AInventory *item); void InitAllPowerupEffects(AInventory *item); @@ -336,7 +348,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, beastweap = player->ReadyWeapon; if (player->PremorphWeapon != nullptr) { - player->PremorphWeapon->PostMorphWeapon (); + player->PremorphWeapon-> P ostMorphWeapon (); } else { @@ -386,9 +398,11 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph) PARAM_POINTER_NOT_NULL(player, player_t); PARAM_INT(unmorphflag); PARAM_BOOL(force); - ACTION_RETURN_BOOL(P_UndoPlayerMorph(self, player, unmorphflag, force)); + ACTION_RETURN_BOOL(P_UndoPlayerMorph(player, self, unmorphflag, force)); } +#endif + //--------------------------------------------------------------------------- // // FUNC P_MorphMonster diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index e5c111a8f..fa1e6f105 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -1025,7 +1025,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) { if (playeringame[i] && players[i].mo != NULL) { - players[i].mo->SetupWeaponSlots(); + FWeaponSlots::SetupWeaponSlots(players[i].mo); } } } diff --git a/src/p_user.cpp b/src/p_user.cpp index fef2d51d5..ea583dde3 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -916,7 +916,7 @@ void APlayerPawn::Tick() void APlayerPawn::PostBeginPlay() { Super::PostBeginPlay(); - SetupWeaponSlots(); + FWeaponSlots::SetupWeaponSlots(this); // Voodoo dolls: restore original floorz/ceilingz logic if (player == NULL || player->mo != this) @@ -931,40 +931,6 @@ void APlayerPawn::PostBeginPlay() } } -//=========================================================================== -// -// APlayerPawn :: SetupWeaponSlots -// -// Sets up the default weapon slots for this player. If this is also the -// local player, determines local modifications and sends those across the -// network. Ignores voodoo dolls. -// -//=========================================================================== - -void APlayerPawn::SetupWeaponSlots() -{ - if (player != NULL && player->mo == this) - { - player->weapons.StandardSetup(GetClass()); - // If we're the local player, then there's a bit more work to do. - // This also applies if we're a bot and this is the net arbitrator. - if (player - players == consoleplayer || - (player->Bot != NULL && consoleplayer == Net_Arbitrator)) - { - FWeaponSlots local_slots(player->weapons); - if (player->Bot != NULL) - { // Bots only need weapons from KEYCONF, not INI modifications. - P_PlaybackKeyConfWeapons(&local_slots); - } - else - { - local_slots.LocalSetup(GetClass()); - } - local_slots.SendDifferences(int(player - players), player->weapons); - } - } -} - //=========================================================================== // // APlayerPawn :: AddInventory diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d7869c26a..95206801e 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -186,6 +186,7 @@ PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *func auto cls_target = funcsym ? PType::toClass(funcsym->OwningClass) : nullptr; if (funcsym == nullptr) { + if (PClass::FindClass(name)) return nullptr; // Special case when a class's member variable hides a global class name. This should still work. sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); } else if ((funcsym->Variants[0].Flags & VARF_Private) && symtable != &funccls->Symbols) diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 4786e44d7..8b9b71ff7 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -677,6 +677,33 @@ class Weapon : StateProvider native } } + + //=========================================================================== + // + // Weapon :: PostMorphWeapon + // + // Bring this weapon up after a player unmorphs. + // + //=========================================================================== + + void PostMorphWeapon () + { + if (Owner == null) + { + return; + } + let p = owner.player; + p.PendingWeapon = WP_NOCHANGE; + p.ReadyWeapon = self; + p.refire = 0; + + let pspr = p.GetPSprite(PSP_WEAPON); + pspr.y = WEAPONBOTTOM; + pspr.ResetInterpolation(); + pspr.SetState(GetUpState()); + } + + } @@ -746,4 +773,5 @@ class WeaponGiver : Weapon struct WeaponSlots native { native bool, int, int LocateWeapon(class weap); + native static void SetupWeaponSlots(PlayerPawn pp); } diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index a2eed3dc0..137fd9c0e 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -214,6 +214,198 @@ extend class PlayerPawn return true; } + //---------------------------------------------------------------------------- + // + // FUNC P_UndoPlayerMorph + // + //---------------------------------------------------------------------------- + + virtual bool UndoPlayerMorph(playerinfo activator, int unmorphflag = 0, bool force = false) + { + if (alternative == null) + { + return false; + } + + let player = self.player; + bool DeliberateUnmorphIsOkay = !!(MRF_STANDARDUNDOING & unmorphflag); + + if ((bInvulnerable) // If the player is invulnerable + && ((player != activator) // and either did not decide to unmorph, + || (!((player.MorphStyle & MRF_WHENINVULNERABLE) // or the morph style does not allow it + || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), + { // Then the player is immune to the unmorph. + return false; + } + + let altmo = PlayerPawn(alternative); + altmo.SetOrigin (Pos, false); + altmo.bSolid = true; + bSolid = false; + if (!force && !altmo.TestMobjLocation()) + { // Didn't fit + altmo.bSolid = false; + bSolid = true; + player.morphTics = 2*TICRATE; + return false; + } + // No longer using tracer as morph storage. That is what 'alternative' is for. + // If the tracer has changed on the morph, change the original too. + altmo.target = target; + altmo.tracer = tracer; + self.player = null; + altmo.alternative = alternative = null; + + // Remove the morph power if the morph is being undone prematurely. + for (Inventory item = Inv; item != null;) + { + let next = item.Inv; + if (item is "PowerMorph") + { + item.Destroy(); + } + item = next; + } + EndAllPowerupEffects(); + altmo.ObtainInventory (self); + Substitute(altmo); + if ((tid != 0) && (player.MorphStyle & MRF_NEWTIDBEHAVIOUR)) + { + altmo.ChangeTid(tid); + } + altmo.Angle = Angle; + altmo.player = player; + altmo.reactiontime = 18; + altmo.bSolid = !!(special2 & 2); + altmo.bShootable = !!(special2 & 4); + altmo.bInvisible = !!(special2 & 0x40); + altmo.Vel.XY = (0, 0); + player.Vel = (0, 0); + altmo.Vel.Z = Vel.Z; + altmo.floorz = floorz; + altmo.bShadow = bShadow; + altmo.bNoGravity = bNoGravity; + altmo.bGhost = bGhost; + altmo.Score = Score; + altmo.InitAllPowerupEffects(); + + let exit_flash = player.MorphExitFlash; + bool correctweapon = !!(player.MorphStyle & MRF_LOSEACTUALWEAPON); + bool undobydeathsaves = !!(player.MorphStyle & MRF_UNDOBYDEATHSAVES); + + player.morphTics = 0; + player.MorphedPlayerClass = null; + player.MorphStyle = 0; + player.MorphExitFlash = null; + player.viewheight = altmo.ViewHeight; + Inventory level2 = altmo.FindInventory("PowerWeaponLevel2", true); + if (level2 != null) + { + level2.Destroy (); + } + + if ((player.health > 0) || undobydeathsaves) + { + player.health = altmo.health = altmo.SpawnHealth(); + } + else // killed when morphed so stay dead + { + altmo.health = player.health; + } + + player.mo = altmo; + if (player.camera == self) + { + player.camera = altmo; + } + + // [MH] + // If the player that was morphed is the one + // taking events, reset up the face, if any; + // this is only needed for old-skool skins + // and for the original DOOM status bar. + if (player == players[consoleplayer]) + { + if (face != 'None') + { + // Assume root-level base skin to begin with + let skinindex = 0; + let skin = player.GetSkin(); + // If a custom skin was in use, then reload it + // or else the base skin for the player class. + if (skin >= PlayerClasses.Size () && skin < PlayerSkins.Size()) + { + skinindex = skin; + } + else if (PlayerClasses.Size () > 1) + { + let whatami = altmo.GetClass(); + for (int i = 0; i < PlayerClasses.Size (); ++i) + { + if (PlayerClasses[i].Type == whatami) + { + skinindex = i; + break; + } + } + } + } + } + + Actor eflash = null; + if (exit_flash != null) + { + eflash = Spawn(exit_flash, Vec3Angle(20., altmo.Angle, gameinfo.telefogheight), ALLOW_REPLACE); + if (eflash) eflash.target = altmo; + } + WeaponSlots.SetupWeaponSlots(altmo); // Use original class's weapon slots. + let beastweap = player.ReadyWeapon; + if (player.PremorphWeapon != null) + { + player.PremorphWeapon.PostMorphWeapon (); + } + else + { + player.ReadyWeapon = player.PendingWeapon = null; + } + if (correctweapon) + { // Better "lose morphed weapon" semantics + class morphweaponcls = MorphWeapon; + if (morphweaponcls != null && morphweaponcls is 'Weapon') + { + let OriginalMorphWeapon = Weapon(altmo.FindInventory (morphweapon)); + if ((OriginalMorphWeapon != null) && (OriginalMorphWeapon.GivenAsMorphWeapon)) + { // You don't get to keep your morphed weapon. + if (OriginalMorphWeapon.SisterWeapon != null) + { + OriginalMorphWeapon.SisterWeapon.Destroy (); + } + OriginalMorphWeapon.Destroy (); + } + } + } + else // old behaviour (not really useful now) + { // Assumptions made here are no longer valid + if (beastweap != null) + { // You don't get to keep your morphed weapon. + if (beastweap.SisterWeapon != null) + { + beastweap.SisterWeapon.Destroy (); + } + beastweap.Destroy (); + } + } + Destroy (); + // Restore playerclass armor to its normal amount. + let hxarmor = HexenArmor(altmo.FindInventory('HexenArmor')); + if (hxarmor != null) + { + hxarmor.Slots[4] = altmo.HexenArmor[0]; + } + return true; + } + + } class MorphProjectile : Actor diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 4d47552b9..dc5d92512 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1358,6 +1358,13 @@ class PSprite : Object native play } } } + + void ResetInterpolation() + { + oldx = x; + oldy = y; + } + } enum EPlayerState @@ -1480,7 +1487,16 @@ struct PlayerInfo native play // this is what internally is known as player_t return false; } - native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false); + // This somehow got its arguments mixed up. 'self' should have been the player to be unmorphed, not the activator + bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) + { + if (player.mo != null) + { + return player.mo.UndoPlayerMorph(self, unmorphflag, force); + } + return false; + } + native bool PoisonPlayer(Actor poisoner, Actor source, int poison); native void PoisonDamage(Actor source, int damage, bool playPainSound); native void SetPsprite(int id, State stat, bool pending = false);