From 7527141ad4aab0edc87cd41a970b01971e8fc5b5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 23 Nov 2016 22:34:17 +0100 Subject: [PATCH] - 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. --- src/dobjtype.cpp | 1 + src/g_shared/a_artifacts.cpp | 9 ++--- src/g_shared/a_artifacts.h | 7 ++-- src/g_shared/a_morph.cpp | 8 +--- src/g_shared/a_sharedglobal.h | 3 +- src/scripting/thingdef.cpp | 20 +++++++++- src/scripting/thingdef_properties.cpp | 56 ++++++++++++++++----------- 7 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 11640ec94..52a269873 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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; } } diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 66832c21c..5b03d4a60 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -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(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)); diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 163094d32..bd7e86323 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -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 (); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 506f18b2f..49ea525bb 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -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(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; } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 65f9d90b6..6911c6688 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -192,7 +192,8 @@ public: void Serialize(FSerializer &arc); - FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash; + PClassPlayerPawn *PlayerClass; + PClassActor *MonsterClass, *MorphFlash, *UnMorphFlash; int Duration, MorphStyle; }; diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 9dcf3a16f..d6c53dd8d 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -68,6 +68,7 @@ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void InitThingdef(); +TArray 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(); } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 7faab937b..8968dc1d6 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -71,13 +71,14 @@ #include "r_data/colormaps.h" #include "vmbuilder.h" +extern TArray 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(cls); } -static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name) +static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name, bool optional = false) { - return static_cast(FindClassTentative(name, RUNTIME_CLASS(AAmmo))); + return static_cast(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(FindClassTentative(name, RUNTIME_CLASS(AWeapon))); + return static_cast(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(FindClassTentative(name, RUNTIME_CLASS(APowerup))); + return static_cast(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(FindClassTentative(name, RUNTIME_CLASS(APlayerPawn))); + return static_cast(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); }