- P_UndoPlayerMorph scriptified.

Not tested yet and still missing a new native interface.
This commit is contained in:
Christoph Oelckers 2018-11-24 08:17:30 +01:00
parent 192104aea2
commit 78d6832d14
10 changed files with 301 additions and 67 deletions

View file

@ -105,7 +105,6 @@ public:
void GiveDeathmatchInventory ();
void FilterCoopRespawnInventory (APlayerPawn *oldplayer);
void SetupWeaponSlots ();
void GiveDefaultInventory ();
// These are virtual on the script side only.

View file

@ -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

View file

@ -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 ();

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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)

View file

@ -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<Weapon> weap);
native static void SetupWeaponSlots(PlayerPawn pp);
}

View file

@ -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<Actor> 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

View file

@ -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);