- Added Martin Howe's morph system enhancement.

SVN r889 (trunk)
This commit is contained in:
Christoph Oelckers 2008-04-08 08:53:42 +00:00
commit a424a2f2a9
16 changed files with 497 additions and 195 deletions

View file

@ -16,6 +16,7 @@
#include "m_random.h"
#include "v_video.h"
#include "templates.h"
#include "a_morph.h"
static FRandom pr_torch ("Torch");
@ -287,10 +288,27 @@ bool APowerup::HandlePickup (AInventory *item)
AInventory *APowerup::CreateCopy (AActor *other)
{
// Get the effective effect time.
EffectTics = abs (EffectTics);
// Abuse the Owner field to tell the
// InitEffect method who started it;
// this should be cleared afterwards,
// as this powerup instance is not
// properly attached to anything yet.
Owner = other;
// Actually activate the powerup.
InitEffect ();
Owner = NULL;
// Clear the Owner field, unless it was
// changed by the activation, for example,
// if this instance is a morph powerup;
// the flag tells the caller that the
// ownership has changed so that they
// can properly handle the situation.
if (!(ItemFlags & IF_CREATECOPYMOVED))
{
Owner = NULL;
}
// All done.
return this;
}
@ -1704,3 +1722,73 @@ void APowerHighJump::EndEffect( )
}
}
// Morph powerup ------------------------------------------------------
IMPLEMENT_STATELESS_ACTOR( APowerMorph, Any, -1, 0 )
PROP_Powerup_EffectTics( MORPHTICS )
END_DEFAULTS
//===========================================================================
//
// APowerMorph :: Serialize
//
//===========================================================================
void APowerMorph::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash;
arc << player;
}
//===========================================================================
//
// APowerMorph :: InitEffect
//
//===========================================================================
void APowerMorph::InitEffect( )
{
if (Owner != NULL && Owner->player != NULL && PlayerClass != NAME_None)
{
player_t *realplayer = Owner->player; // Remember the identity of the player
const PClass *morph_flash = PClass::FindClass (MorphFlash);
const PClass *unmorph_flash = PClass::FindClass (UnMorphFlash);
const PClass *player_class = PClass::FindClass (PlayerClass);
if (P_MorphPlayer(realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
{
Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor)
player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
}
}
}
//===========================================================================
//
// APowerMorph :: EndEffect
//
//===========================================================================
void APowerMorph::EndEffect( )
{
if (Owner != NULL && player != NULL)
{
int savedMorphTics = player->morphTics;
P_UndoPlayerMorph (player);
if (player->morphTics /*failed*/)
{
// Transfer retry timeout
// to the powerup's timer.
EffectTics = player->morphTics;
// Reload negative morph tics;
// use actual value; it may
// be in use for animation.
player->morphTics = savedMorphTics;
}
else // unmorph succeeded
{
player = NULL;
}
}
}

View file

@ -254,9 +254,20 @@ protected:
void EndEffect( );
};
class APowerMorph : public APowerup
{
DECLARE_STATELESS_ACTOR( APowerMorph, APowerup )
public:
void Serialize (FArchive &arc);
FNameNoInit PlayerClass, MorphFlash, UnMorphFlash;
int MorphStyle;
class player_s;
protected:
void InitEffect ();
void EndEffect ();
// Variables
player_s *player;
};
#endif //__A_ARTIFACTS_H__

View file

@ -9,8 +9,7 @@
#include "m_random.h"
#include "a_sharedglobal.h"
#include "sbar.h"
#define MORPHTICS (40*TICRATE)
#include "a_morph.h"
static FRandom pr_morphmonst ("MorphMonster");
@ -22,7 +21,7 @@ static FRandom pr_morphmonst ("MorphMonster");
//
//---------------------------------------------------------------------------
bool P_MorphPlayer (player_t *p, const PClass *spawntype)
bool P_MorphPlayer (player_t *p, const PClass *spawntype, int duration, int style, const PClass *enter_flash, const PClass *exit_flash)
{
AInventory *item;
APlayerPawn *morphed;
@ -77,12 +76,12 @@ bool P_MorphPlayer (player_t *p, const PClass *spawntype)
morphed->flags |= actor->flags & (MF_SHADOW|MF_NOGRAVITY);
morphed->flags2 |= actor->flags2 & MF2_FLY;
morphed->flags3 |= actor->flags3 & MF3_GHOST;
Spawn<ATeleportFog> (actor->x, actor->y, actor->z + TELEFOGHEIGHT, ALLOW_REPLACE);
Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->x, actor->y, actor->z + TELEFOGHEIGHT, ALLOW_REPLACE);
actor->player = NULL;
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
actor->renderflags |= RF_INVISIBLE;
p->morphTics = MORPHTICS;
p->morphTics = (duration) ? duration : MORPHTICS;
// [MH] Used by SBARINFO to speed up face drawing
p->MorphedPlayerClass = 0;
@ -94,7 +93,9 @@ bool P_MorphPlayer (player_t *p, const PClass *spawntype)
break;
}
}
p->MorphStyle = style;
p->MorphExitFlash = (exit_flash) ? exit_flash : RUNTIME_CLASS(ATeleportFog);
p->health = morphed->health;
p->mo = morphed;
p->momx = p->momy = 0;
@ -198,8 +199,12 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY);
mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST);
const PClass *exit_flash = player->MorphExitFlash;
player->morphTics = 0;
player->MorphedPlayerClass = 0;
player->MorphStyle = 0;
player->MorphExitFlash = NULL;
player->viewheight = mo->ViewHeight;
AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2));
if (level2 != NULL)
@ -246,8 +251,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
}
angle = mo->angle >> ANGLETOFINESHIFT;
Spawn<ATeleportFog> (pmo->x + 20*finecosine[angle],
pmo->y + 20*finesine[angle], pmo->z + TELEFOGHEIGHT, ALLOW_REPLACE);
Spawn(exit_flash, pmo->x + 20*finecosine[angle], pmo->y + 20*finesine[angle], pmo->z + TELEFOGHEIGHT, ALLOW_REPLACE);
beastweap = player->ReadyWeapon;
if (player->PremorphWeapon != NULL)
{
@ -284,7 +288,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
//
//---------------------------------------------------------------------------
bool P_MorphMonster (AActor *actor, const PClass *spawntype)
bool P_MorphMonster (AActor *actor, const PClass *spawntype, int duration, int style, const PClass *enter_flash, const PClass *exit_flash)
{
AMorphedMonster *morphed;
@ -304,7 +308,9 @@ bool P_MorphMonster (AActor *actor, const PClass *spawntype)
morphed->alpha = actor->alpha;
morphed->RenderStyle = actor->RenderStyle;
morphed->UnmorphTime = level.time + MORPHTICS + pr_morphmonst();
morphed->UnmorphTime = level.time + ((duration) ? duration : MORPHTICS) + pr_morphmonst();
morphed->MorphStyle = style;
morphed->MorphExitFlash = (exit_flash) ? exit_flash : RUNTIME_CLASS(ATeleportFog);
morphed->FlagsSave = actor->flags & ~MF_JUSTHIT;
//morphed->special = actor->special;
//memcpy (morphed->args, actor->args, sizeof(actor->args));
@ -321,7 +327,7 @@ bool P_MorphMonster (AActor *actor, const PClass *spawntype)
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
actor->renderflags |= RF_INVISIBLE;
Spawn<ATeleportFog> (actor->x, actor->y, actor->z + TELEFOGHEIGHT, ALLOW_REPLACE);
Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->x, actor->y, actor->z + TELEFOGHEIGHT, ALLOW_REPLACE);
return true;
}
@ -375,8 +381,9 @@ bool P_UpdateMorphedMonster (AMorphedMonster *beast)
actor->AddToHash ();
beast->UnmorphedMe = NULL;
DObject::StaticPointerSubstitution (beast, actor);
const PClass *exit_flash = beast->MorphExitFlash;
beast->Destroy ();
Spawn<ATeleportFog> (beast->x, beast->y, beast->z + TELEFOGHEIGHT, ALLOW_REPLACE);
Spawn(exit_flash, beast->x, beast->y, beast->z + TELEFOGHEIGHT, ALLOW_REPLACE);
return true;
}
@ -390,13 +397,17 @@ END_DEFAULTS
int AMorphProjectile::DoSpecialDamage (AActor *target, int damage)
{
const PClass *morph_flash = PClass::FindClass (MorphFlash);
const PClass *unmorph_flash = PClass::FindClass (UnMorphFlash);
if (target->player)
{
P_MorphPlayer (target->player, PClass::FindClass (PlayerClass));
const PClass *player_class = PClass::FindClass (PlayerClass);
P_MorphPlayer (target->player, player_class, Duration, MorphStyle, morph_flash, unmorph_flash);
}
else
{
P_MorphMonster (target, PClass::FindClass (MonsterClass));
const PClass *monster_class = PClass::FindClass (MonsterClass);
P_MorphMonster (target, monster_class, Duration, MorphStyle, morph_flash, unmorph_flash);
}
return -1;
}
@ -404,7 +415,7 @@ int AMorphProjectile::DoSpecialDamage (AActor *target, int damage)
void AMorphProjectile::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << PlayerClass << MonsterClass;
arc << PlayerClass << MonsterClass << Duration << MorphStyle << MorphFlash << UnMorphFlash;
}
@ -423,7 +434,7 @@ END_DEFAULTS
void AMorphedMonster::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << UnmorphedMe << UnmorphTime << FlagsSave;
arc << UnmorphedMe << UnmorphTime << MorphStyle << MorphExitFlash << FlagsSave;
}
void AMorphedMonster::Destroy ()

36
src/g_shared/a_morph.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef __A_MORPH__
#define __A_MORPH__
#define MORPHTICS (40*TICRATE)
#define MAXMORPHHEALTH 30
// Morph style states how morphing affects health and
// other effects in the game; only valid for players.
// Default should be the old Heretic/HeXen behaviour,
// so the (int) value of MORPH_RAVEN *must* be zero.
enum
{
MORPH_OLDEFFECTS = 0, // Default to old Heretic/HeXen behaviour unless flags given
MORPH_ADDSTAMINA = 1, // Power instead of curse (add stamina instead of limiting to health)
MORPH_FULLHEALTH = 2, // New health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour)
MORPH_UNDOBYTOMEOFPOWER = 4,
MORPH_UNDOBYCHAOSDEVICE = 8,
};
class PClass;
class AActor;
class player_s;
//
// A_MORPH
//
bool P_MorphPlayer (player_s *player, const PClass *morphclass, int duration = 0, int style = 0,
const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
bool P_UndoPlayerMorph (player_s *player, bool force = false);
bool P_MorphMonster (AActor *actor, const PClass *morphclass, int duration = 0, int style = 0,
const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
bool P_UpdateMorphedMonster (AActor *actor);
#endif //__A_MORPH__

View file

@ -13,6 +13,7 @@
#include "gstrings.h"
#include "templates.h"
#include "a_strifeglobal.h"
#include "a_morph.h"
static FRandom pr_restore ("RestorePos");
@ -193,10 +194,25 @@ bool P_GiveBody (AActor *actor, int num)
if (player != NULL)
{
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->stamina;
if (player->morphTics)
{
max = MAXMORPHHEALTH;
}
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->stamina;
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->stamina;
}
}
}
// [RH] For Strife: A negative body sets you up with a percentage
// of your full health.
if (num < 0)
@ -1163,6 +1179,8 @@ void AInventory::GiveQuest (AActor *toucher)
bool AInventory::TryPickup (AActor *toucher)
{
AActor *newtoucher = toucher; // in case changed by the powerup
// If HandlePickup() returns true, it will set the IF_PICKUPGOOD flag
// to indicate that this item has been picked up. If the item cannot be
// picked up, then it leaves the flag cleared.
@ -1209,7 +1227,15 @@ bool AInventory::TryPickup (AActor *toucher)
{
return false;
}
copy->AttachToOwner (toucher);
// Handle owner-changing powerups
if (copy->ItemFlags & IF_CREATECOPYMOVED)
{
newtoucher = copy->Owner;
copy->Owner = NULL;
copy->ItemFlags &= ~IF_CREATECOPYMOVED;
}
// Continue onwards with the rest
copy->AttachToOwner (newtoucher);
if (ItemFlags & IF_AUTOACTIVATE)
{
if (copy->Use (true))
@ -1223,7 +1249,7 @@ bool AInventory::TryPickup (AActor *toucher)
}
}
GiveQuest(toucher);
GiveQuest(newtoucher);
return true;
}
@ -1380,9 +1406,24 @@ bool AHealth::TryPickup (AActor *other)
if (max == 0)
{
max = static_cast<APlayerPawn*>(other)->GetMaxHealth() + player->stamina;
if (player->morphTics)
{
max = MAXMORPHHEALTH;
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->stamina;
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->stamina;
}
}
}
}
if (player->health >= max)

View file

@ -96,6 +96,7 @@ enum
IF_BIGPOWERUP = 1<<12, // Affected by RESPAWN_SUPER dmflag
IF_KEEPDEPLETED = 1<<13, // Items with this flag are retained even when they run out.
IF_IGNORESKILL = 1<<14, // Ignores any skill related multiplicators when giving this item.
IF_CREATECOPYMOVED = 1<<15 // CreateCopy changed the owner (copy's Owner field holds new owner).
};
struct vissprite_t;
@ -210,7 +211,7 @@ public:
int Kickback;
fixed_t YAdjust; // For viewing the weapon fullscreen
WORD UpSound, ReadySound; // Sounds when coming up and idle
const PClass *SisterWeaponType; // Another weapon to pick up with this one
const PClass *SisterWeaponType; // Another weapon to pick up with this one
const PClass *ProjectileType; // Projectile used by primary attack
const PClass *AltProjectileType; // Projectile used by alternate attack
int SelectionOrder; // Lower-numbered weapons get picked first

View file

@ -11,14 +11,6 @@ struct side_t;
extern void P_SpawnDirt (AActor *actor, fixed_t radius);
bool P_MorphPlayer (player_s *player);
bool P_UndoPlayerMorph (player_s *player, bool force);
bool P_MorphMonster (AActor *actor, const PClass *morphClass);
bool P_UpdateMorphedMonster (AActor *actor);
class DBaseDecal : public DThinker
{
DECLARE_CLASS (DBaseDecal, DThinker)
@ -182,7 +174,8 @@ public:
int DoSpecialDamage (AActor *target, int damage);
void Serialize (FArchive &arc);
FNameNoInit PlayerClass, MonsterClass;
FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash;
int Duration, MorphStyle;
};
class AMorphedMonster : public AActor
@ -196,7 +189,8 @@ public:
void Destroy ();
TObjPtr<AActor> UnmorphedMe;
int UnmorphTime;
int UnmorphTime, MorphStyle;
const PClass *MorphExitFlash;
DWORD FlagsSave;
};