- fixed: The morph actors stored their required classes as names, not as class pointers.

This prevented any kind of error check on them.
Unfortunately, due to backwards compatibility needs, on DECORATE the missing class may not be fatal so a workaround had to be added to clear those bogus pointers later if they are discovered to be broken.
For ZScript, though, this will result in a compile error, which was the intention behind this change.
This commit is contained in:
Christoph Oelckers 2016-11-23 22:34:17 +01:00
parent 5e67cf79d3
commit 7527141ad4
7 changed files with 64 additions and 40 deletions

View File

@ -3298,6 +3298,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
// see if we can reuse the existing class. This is only possible if the inheritance is identical. Otherwise it needs to be replaced.
if (this == existclass->ParentClass)
{
existclass->ObjectFlags &= OF_Transient;
return existclass;
}
}

View File

@ -1910,13 +1910,10 @@ void APowerMorph::InitEffect( )
{
Super::InitEffect();
if (Owner != NULL && Owner->player != NULL && PlayerClass != NAME_None)
if (Owner != nullptr && Owner->player != nullptr && PlayerClass != nullptr)
{
player_t *realplayer = Owner->player; // Remember the identity of the player
PClassActor *morph_flash = PClass::FindActor(MorphFlash);
PClassActor *unmorph_flash = PClass::FindActor(UnMorphFlash);
PClassPlayerPawn *player_class = dyn_cast<PClassPlayerPawn>(PClass::FindClass (PlayerClass));
if (P_MorphPlayer(realplayer, realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
if (P_MorphPlayer(realplayer, realplayer, PlayerClass, -1/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
{
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)
@ -1960,7 +1957,7 @@ void APowerMorph::EndEffect( )
}
// Unmorph if possible
if (!bNoCallUndoMorph)
if (!bInUndoMorph)
{
int savedMorphTics = Player->morphTics;
P_UndoPlayerMorph (Player, Player, 0, !!(Player->MorphStyle & MORPH_UNDOALWAYS));

View File

@ -280,13 +280,14 @@ class APowerMorph : public APowerup
public:
virtual void Serialize(FSerializer &arc);
void SetNoCallUndoMorph() { bNoCallUndoMorph = true; }
void SetNoCallUndoMorph() { bInUndoMorph = true; }
// Variables
FNameNoInit PlayerClass, MorphFlash, UnMorphFlash;
PClassPlayerPawn *PlayerClass;
PClassActor *MorphFlash, *UnMorphFlash;
int MorphStyle;
player_t *Player;
bool bNoCallUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
bool bInUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
protected:
void InitEffect ();

View File

@ -620,17 +620,13 @@ IMPLEMENT_CLASS(AMorphProjectile, false, false, false, false)
int AMorphProjectile::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
PClassActor *morph_flash = PClass::FindActor(MorphFlash);
PClassActor *unmorph_flash = PClass::FindActor(UnMorphFlash);
if (target->player)
{
PClassPlayerPawn *player_class = dyn_cast<PClassPlayerPawn>(PClass::FindClass(PlayerClass));
P_MorphPlayer (NULL, target->player, player_class, Duration, MorphStyle, morph_flash, unmorph_flash);
P_MorphPlayer (NULL, target->player, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash);
}
else
{
PClassActor *monster_class = PClass::FindActor(MonsterClass);
P_MorphMonster (target, monster_class, Duration, MorphStyle, morph_flash, unmorph_flash);
P_MorphMonster (target, MonsterClass, Duration, MorphStyle, MorphFlash, UnMorphFlash);
}
return -1;
}

View File

@ -192,7 +192,8 @@ public:
void Serialize(FSerializer &arc);
FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash;
PClassPlayerPawn *PlayerClass;
PClassActor *MonsterClass, *MorphFlash, *UnMorphFlash;
int Duration, MorphStyle;
};

View File

@ -68,6 +68,7 @@
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void InitThingdef();
TArray<PClassActor **> OptionalClassPtrs;
// STATIC FUNCTION PROTOTYPES --------------------------------------------
PClassActor *QuestItemClasses[31];
@ -390,8 +391,21 @@ void LoadActors ()
{
if (ti->Size == TentativeClass)
{
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
FScriptPosition::ErrorCounter++;
if (ti->ObjectFlags & OF_Transient)
{
Printf(TEXTCOLOR_ORANGE "Class %s referenced but not defined\n", ti->TypeName.GetChars());
FScriptPosition::WarnCounter++;
DObject::StaticPointerSubstitution(ti, nullptr);
for (auto op : OptionalClassPtrs)
{
if (*op == ti) *op = nullptr;
}
}
else
{
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
FScriptPosition::ErrorCounter++;
}
continue;
}
@ -431,4 +445,6 @@ void LoadActors ()
QuestItemClasses[i] = PClass::FindActor(fmt);
}
StateSourceLines.Clear();
OptionalClassPtrs.Clear();
OptionalClassPtrs.ShrinkToFit();
}

View File

@ -71,13 +71,14 @@
#include "r_data/colormaps.h"
#include "vmbuilder.h"
extern TArray<PClassActor **> OptionalClassPtrs;
//==========================================================================
//
// Gets a class pointer and performs an error check for correct type
//
//==========================================================================
static PClassActor *FindClassTentative(const char *name, PClass *ancestor)
static PClassActor *FindClassTentative(const char *name, PClass *ancestor, bool optional = false)
{
// "" and "none" mean 'no class'
if (name == NULL || *name == 0 || !stricmp(name, "none"))
@ -91,23 +92,27 @@ static PClassActor *FindClassTentative(const char *name, PClass *ancestor)
{
I_Error("%s does not inherit from %s\n", name, ancestor->TypeName.GetChars());
}
if (cls->Size == TentativeClass && optional)
{
cls->ObjectFlags |= OF_Transient; // since this flag has no meaning in class types, let's use it for marking the type optional.
}
return static_cast<PClassActor *>(cls);
}
static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name)
static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name, bool optional = false)
{
return static_cast<AAmmo::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(AAmmo)));
return static_cast<AAmmo::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(AAmmo), optional));
}
static AWeapon::MetaClass *FindClassTentativeWeapon(const char *name)
static AWeapon::MetaClass *FindClassTentativeWeapon(const char *name, bool optional = false)
{
return static_cast<AWeapon::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(AWeapon)));
return static_cast<AWeapon::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(AWeapon), optional));
}
static APowerup::MetaClass *FindClassTentativePowerup(const char *name)
static APowerup::MetaClass *FindClassTentativePowerup(const char *name, bool optional = false)
{
return static_cast<APowerup::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(APowerup)));
return static_cast<APowerup::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(APowerup), optional));
}
static APlayerPawn::MetaClass *FindClassTentativePlayerPawn(const char *name)
static APlayerPawn::MetaClass *FindClassTentativePlayerPawn(const char *name, bool optional = false)
{
return static_cast<APlayerPawn::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(APlayerPawn)));
return static_cast<APlayerPawn::MetaClass *>(FindClassTentative(name, RUNTIME_CLASS(APlayerPawn), optional));
}
//==========================================================================
@ -2984,21 +2989,23 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, viewbob, F, PlayerPawn)
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(playerclass, S, MorphProjectile)
{
PROP_STRING_PARM(str, 0);
defaults->PlayerClass = FName(str);
defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push((PClassActor**)&defaults->PlayerClass);
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(monsterclass, S, MorphProjectile)
{
PROP_STRING_PARM(str, 0);
defaults->MonsterClass = FName(str);
defaults->MonsterClass = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MonsterClass);
}
//==========================================================================
@ -3020,12 +3027,13 @@ DEFINE_CLASS_PROPERTY(morphstyle, M, MorphProjectile)
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(morphflash, S, MorphProjectile)
{
PROP_STRING_PARM(str, 0);
defaults->MorphFlash = FName(str);
defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MorphFlash);
}
//==========================================================================
@ -3034,16 +3042,18 @@ DEFINE_CLASS_PROPERTY(morphflash, S, MorphProjectile)
DEFINE_CLASS_PROPERTY(unmorphflash, S, MorphProjectile)
{
PROP_STRING_PARM(str, 0);
defaults->UnMorphFlash = FName(str);
defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->UnMorphFlash);
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(playerclass, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->PlayerClass = FName(str);
defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push((PClassActor**)&defaults->PlayerClass);
}
//==========================================================================
@ -3056,21 +3066,23 @@ DEFINE_CLASS_PROPERTY(morphstyle, M, PowerMorph)
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(morphflash, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->MorphFlash = FName(str);
defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MorphFlash);
}
//==========================================================================
//
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(unmorphflash, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->UnMorphFlash = FName(str);
defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->UnMorphFlash);
}