diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 906be04c45..c4daae0241 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -65,6 +65,12 @@ void PClassInventory::ReplaceClassRef(PClass *oldclass, PClass *newclass) } } +void PClassInventory::Finalize(FStateDefinitions &statedef) +{ + Super::Finalize(statedef); + ((AActor*)Defaults)->flags |= MF_SPECIAL; +} + IMPLEMENT_CLASS(PClassAmmo) PClassAmmo::PClassAmmo() diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 9dba52639a..45676a214d 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -139,6 +139,7 @@ public: PClassInventory(); virtual void DeriveData(PClass *newclass); virtual void ReplaceClassRef(PClass *oldclass, PClass *newclass); + void Finalize(FStateDefinitions &statedef); FString PickupMessage; int GiveQuest; // Optionally give one of the quest items. @@ -274,6 +275,7 @@ protected: public: PClassWeapon(); virtual void ReplaceClassRef(PClass *oldclass, PClass *newclass); + void Finalize(FStateDefinitions &statedef); int SlotNumber; int SlotPriority; diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 36ffbc2715..84305b9cdc 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -67,6 +67,38 @@ void PClassWeapon::ReplaceClassRef(PClass *oldclass, PClass *newclass) } } +void PClassWeapon::Finalize(FStateDefinitions &statedef) +{ + Super::Finalize(statedef); + FState *ready = FindState(NAME_Ready); + FState *select = FindState(NAME_Select); + FState *deselect = FindState(NAME_Deselect); + FState *fire = FindState(NAME_Fire); + + // Consider any weapon without any valid state abstract and don't output a warning + // This is for creating base classes for weapon groups that only set up some properties. + if (ready || select || deselect || fire) + { + if (!ready) + { + I_Error("Weapon %s doesn't define a ready state.", TypeName.GetChars()); + } + if (!select) + { + I_Error("Weapon %s doesn't define a select state.", TypeName.GetChars()); + } + if (!deselect) + { + I_Error("Weapon %s doesn't define a deselect state.", TypeName.GetChars()); + } + if (!fire) + { + I_Error("Weapon %s doesn't define a fire state.", TypeName.GetChars()); + } + } +} + + //=========================================================================== // // AWeapon :: Serialize diff --git a/src/info.cpp b/src/info.cpp index 191ed2bd5b..8089e065b8 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -53,7 +53,9 @@ #include "g_level.h" #include "stats.h" #include "vm.h" +#include "thingdef.h" #include "d_player.h" +#include "doomerrors.h" extern void LoadActors (); extern void InitBotStuff(); @@ -380,6 +382,75 @@ void PClassActor::InitializeNativeDefaults() } } +//========================================================================== +// +// PClassActor :: SetReplacement +// +// Sets as a replacement class for another class. +// +//========================================================================== + +bool PClassActor::SetReplacement(FName replaceName) +{ + // Check for "replaces" + if (replaceName != NAME_None) + { + // Get actor name + PClassActor *replacee = PClass::FindActor(replaceName); + + if (replacee == nullptr) + { + return false; + } + if (replacee != nullptr) + { + replacee->Replacement = this; + Replacee = replacee; + } + } + return true; +} + +//========================================================================== +// +// PClassActor :: SetDropItems +// +// Sets a new drop item list +// +//========================================================================== + +void PClassActor::SetDropItems(DDropItem *drops) +{ + DropItems = drops; + GC::WriteBarrier(this, DropItems); +} + + +//========================================================================== +// +// PClassActor :: Finalize +// +// Installs the parsed states and does some sanity checking +// +//========================================================================== + +void PClassActor::Finalize(FStateDefinitions &statedef) +{ + AActor *defaults = (AActor*)Defaults; + + try + { + statedef.FinishStates(this, defaults); + } + catch (CRecoverableError &) + { + statedef.MakeStateDefines(NULL); + throw; + } + statedef.InstallStates(this, defaults); + statedef.MakeStateDefines(NULL); +} + //========================================================================== // // PClassActor :: RegisterIDs diff --git a/src/info.h b/src/info.h index 5ba7eafd9b..0fe479d20e 100644 --- a/src/info.h +++ b/src/info.h @@ -53,6 +53,7 @@ struct Baggage; class FScanner; struct FActorInfo; class FIntCVar; +class FStateDefinitions; enum EStateDefineFlags { @@ -234,6 +235,9 @@ public: void SetPainChance(FName type, int chance); size_t PropagateMark(); void InitializeNativeDefaults(); + bool SetReplacement(FName replaceName); + void SetDropItems(DDropItem *drops); + virtual void Finalize(FStateDefinitions &statedef); FState *FindState(int numnames, FName *names, bool exact=false) const; FState *FindStateByString(const char *name, bool exact=false); diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 5fbe27a9dd..186b269cec 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -57,6 +57,45 @@ #include "m_argv.h" void ParseOldDecoration(FScanner &sc, EDefinitionType def); +EXTERN_CVAR(Bool, strictdecorate); + + +//========================================================================== +// +// DecoDerivedClass +// +// Create a derived class and performs some additional sanity checks +// +//========================================================================== + +PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName) +{ + PClassActor *type = static_cast(parent->CreateDerivedClass(typeName, parent->Size)); + if (type == nullptr) + { + FString newname = typeName.GetChars(); + FString sourcefile = sc.FileName; + + sourcefile.Substitute(":", "@"); + newname << '@' << sourcefile; + if (strictdecorate) + { + sc.Message(MSG_ERROR, "Tried to define class '%s' more than once.", typeName.GetChars()); + } + else + { + // Due to backwards compatibility issues this cannot be an unconditional error. + sc.Message(MSG_WARNING, "Tried to define class '%s' more than once. Renaming class to '%s'", typeName.GetChars(), newname.GetChars()); + } + type = static_cast(parent->CreateDerivedClass(newname, parent->Size)); + if (type == nullptr) + { + // This we cannot handle cleanly anymore. Let's just abort and forget about the odd mod out that was this careless. + sc.Message(MSG_FATAL, "Tried to define class '%s' more than twice in the same file.", typeName.GetChars()); + } + } + return type; +} //========================================================================== // @@ -1126,6 +1165,80 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls) ParseFunctionDef(sc, cls, funcname, rets, VARF_Method | VARF_Action); } +//========================================================================== +// +// Starts a new actor definition +// +//========================================================================== +PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native) +{ + PClassActor *replacee = NULL; + PClassActor *ti = NULL; + + PClassActor *parent = RUNTIME_CLASS(AActor); + + if (parentName != NAME_None) + { + parent = PClass::FindActor(parentName); + + PClassActor *p = parent; + while (p != NULL) + { + if (p->TypeName == typeName) + { + sc.Message(MSG_ERROR, "'%s' inherits from a class with the same name", typeName.GetChars()); + break; + } + p = dyn_cast(p->ParentClass); + } + + if (parent == NULL) + { + sc.Message(MSG_ERROR, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars()); + parent = RUNTIME_CLASS(AActor); + } + else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + sc.Message(MSG_ERROR, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars()); + parent = RUNTIME_CLASS(AActor); + } + } + + if (native) + { + ti = PClass::FindActor(typeName); + if (ti == NULL) + { + extern void DumpTypeTable(); + DumpTypeTable(); + sc.Message(MSG_ERROR, "Unknown native actor '%s'", typeName.GetChars()); + goto create; + } + else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass()) + { + sc.Message(MSG_ERROR, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars()); + parent = RUNTIME_CLASS(AActor); + goto create; + } + else if (ti->Defaults != NULL) + { + sc.Message(MSG_ERROR, "Redefinition of internal class '%s'", typeName.GetChars()); + goto create; + } + ti->InitializeNativeDefaults(); + ti->ParentClass->DeriveData(ti); + } + else + { + create: + ti = DecoDerivedClass(sc, parent, typeName); + } + + ti->Replacee = ti->Replacement = NULL; + ti->DoomEdNum = -1; + return ti; +} + //========================================================================== // // Starts a new actor definition @@ -1216,7 +1329,10 @@ static PClassActor *ParseActorHeader(FScanner &sc, Baggage *bag) info->DoomEdNum = DoomEdNum > 0 ? DoomEdNum : -1; info->SourceLumpName = Wads.GetLumpFullPath(sc.LumpNum); - SetReplacement(sc, info, replaceName); + if (!info->SetReplacement(replaceName)) + { + sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->TypeName.GetChars()); + } ResetBaggage (bag, info == RUNTIME_CLASS(AActor) ? NULL : static_cast(info->ParentClass)); bag->Info = info; @@ -1293,7 +1409,18 @@ static void ParseActor(FScanner &sc) break; } } - FinishActor(sc, info, bag); + if (bag.DropItemSet) + { + bag.Info->SetDropItems(bag.DropItemList); + } + try + { + info->Finalize(bag.statedef); + } + catch (CRecoverableError &err) + { + sc.ScriptError("%s", err.GetMessage()); + } sc.SetCMode (false); } diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 175aa89904..5227009e75 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -76,202 +76,6 @@ void ParseDecorate(FScanner &ctx); // STATIC FUNCTION PROTOTYPES -------------------------------------------- PClassActor *QuestItemClasses[31]; -EXTERN_CVAR(Bool, strictdecorate); - -PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName) -{ - PClassActor *type = static_cast(parent->CreateDerivedClass(typeName, parent->Size)); - if (type == nullptr) - { - FString newname = typeName.GetChars(); - FString sourcefile = sc.FileName; - - sourcefile.Substitute(":", "@"); - newname << '@' << sourcefile; - if (strictdecorate) - { - sc.Message(MSG_ERROR, "Tried to define class '%s' more than once.", typeName.GetChars()); - } - else - { - // Due to backwards compatibility issues this cannot be an unconditional error. - sc.Message(MSG_WARNING, "Tried to define class '%s' more than once. Renaming class to '%s'", typeName.GetChars(), newname.GetChars()); - } - type = static_cast(parent->CreateDerivedClass(newname, parent->Size)); - if (type == nullptr) - { - // This we cannot handle cleanly anymore. Let's just abort and forget about the odd mod out that was this careless. - sc.Message(MSG_FATAL, "Tried to define class '%s' more than twice in the same file.", typeName.GetChars()); - } - } - return type; -} -//========================================================================== -// -// Starts a new actor definition -// -//========================================================================== -PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native) -{ - PClassActor *replacee = NULL; - PClassActor *ti = NULL; - - PClassActor *parent = RUNTIME_CLASS(AActor); - - if (parentName != NAME_None) - { - parent = PClass::FindActor(parentName); - - PClassActor *p = parent; - while (p != NULL) - { - if (p->TypeName == typeName) - { - sc.Message(MSG_ERROR, "'%s' inherits from a class with the same name", typeName.GetChars()); - break; - } - p = dyn_cast(p->ParentClass); - } - - if (parent == NULL) - { - sc.Message(MSG_ERROR, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars()); - parent = RUNTIME_CLASS(AActor); - } - else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor))) - { - sc.Message(MSG_ERROR, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars()); - parent = RUNTIME_CLASS(AActor); - } - } - - if (native) - { - ti = PClass::FindActor(typeName); - if (ti == NULL) - { - extern void DumpTypeTable(); - DumpTypeTable(); - sc.Message(MSG_ERROR, "Unknown native actor '%s'", typeName.GetChars()); - goto create; - } - else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass()) - { - sc.Message(MSG_ERROR, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars()); - parent = RUNTIME_CLASS(AActor); - goto create; - } - else if (ti->Defaults != NULL) - { - sc.Message(MSG_ERROR, "Redefinition of internal class '%s'", typeName.GetChars()); - goto create; - } - ti->InitializeNativeDefaults(); - ti->ParentClass->DeriveData(ti); - } - else - { - create: - ti = DecoDerivedClass(sc, parent, typeName); - } - - ti->Replacee = ti->Replacement = NULL; - ti->DoomEdNum = -1; - return ti; -} - -//========================================================================== -// -// -// -//========================================================================== - -void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName) -{ - // Check for "replaces" - if (replaceName != NAME_None) - { - // Get actor name - PClassActor *replacee = PClass::FindActor(replaceName); - - if (replacee == NULL) - { - sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->TypeName.GetChars()); - return; - } - if (replacee != NULL) - { - replacee->Replacement = info; - info->Replacee = replacee; - } - } - -} - -//========================================================================== -// -// Finalizes an actor definition -// -//========================================================================== - -void FinishActor(const FScriptPosition &sc, PClassActor *info, Baggage &bag) -{ - AActor *defaults = (AActor*)info->Defaults; - - try - { - bag.statedef.FinishStates (info, defaults); - } - catch (CRecoverableError &err) - { - sc.Message(MSG_ERROR, "%s", err.GetMessage()); - bag.statedef.MakeStateDefines(NULL); - return; - } - bag.statedef.InstallStates (info, defaults); - bag.statedef.MakeStateDefines(NULL); - if (bag.DropItemSet) - { - info->DropItems = bag.DropItemList; - GC::WriteBarrier(info, info->DropItems); - } - if (info->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - defaults->flags |= MF_SPECIAL; - } - - // Weapons must be checked for all relevant states. They may crash the game otherwise. - if (info->IsDescendantOf(RUNTIME_CLASS(AWeapon))) - { - FState *ready = info->FindState(NAME_Ready); - FState *select = info->FindState(NAME_Select); - FState *deselect = info->FindState(NAME_Deselect); - FState *fire = info->FindState(NAME_Fire); - - // Consider any weapon without any valid state abstract and don't output a warning - // This is for creating base classes for weapon groups that only set up some properties. - if (ready || select || deselect || fire) - { - if (!ready) - { - sc.Message(MSG_ERROR, "Weapon %s doesn't define a ready state.\n", info->TypeName.GetChars()); - } - if (!select) - { - sc.Message(MSG_ERROR, "Weapon %s doesn't define a select state.\n", info->TypeName.GetChars()); - } - if (!deselect) - { - sc.Message(MSG_ERROR, "Weapon %s doesn't define a deselect state.\n", info->TypeName.GetChars()); - } - if (!fire) - { - sc.Message(MSG_ERROR, "Weapon %s doesn't define a fire state.\n", info->TypeName.GetChars()); - } - } - } -} - //========================================================================== // // Do some postprocessing after everything has been defined @@ -426,14 +230,6 @@ static void FinishThingdef() ActorDamageFuncs.DeleteAndClear(); StateTempCalls.DeleteAndClear(); - - // Since these are defined in DECORATE now the table has to be initialized here. - for(int i = 0; i < 31; i++) - { - char fmt[20]; - mysnprintf(fmt, countof(fmt), "QuestItem%d", i+1); - QuestItemClasses[i] = PClass::FindActor(fmt); - } } @@ -454,10 +250,11 @@ void LoadActors () timer.Reset(); timer.Clock(); ActorDamageFuncs.Clear(); - FScriptPosition::ResetErrorCounter(); InitThingdef(); lastlump = 0; ParseScripts(); + + FScriptPosition::ResetErrorCounter(); while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1) { FScanner sc(lump); @@ -471,4 +268,12 @@ void LoadActors () timer.Unclock(); if (!batchrun) Printf("DECORATE parsing took %.2f ms\n", timer.TimeMS()); // Base time: ~52 ms + + // Since these are defined in DECORATE now the table has to be initialized here. + for (int i = 0; i < 31; i++) + { + char fmt[20]; + mysnprintf(fmt, countof(fmt), "QuestItem%d", i + 1); + QuestItemClasses[i] = PClass::FindActor(fmt); + } } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 84f1f7d068..714657d24d 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -178,10 +178,8 @@ void SetImplicitArgs(TArray *args, TArray *argflags, PClass *cls //========================================================================== PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native); -void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName); void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod); -void FinishActor(const FScriptPosition &sc, PClassActor *info, Baggage &bag); FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 0d9558a527..e8ea8a6e73 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -69,6 +69,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) name << "nodes - " << FName(cnode->NodeName); cls->TreeNodes.SetName(name); + if (!static_cast(cnode->Type)->SetReplacement(cnode->Replaces->Id)) + { + Warn(cnode, "Replaced type '%s' not found for %s", FName(cnode->Replaces->Id).GetChars(), cnode->Type->TypeName.GetChars()); + } // Need to check if the class actually has a body. if (node != nullptr) do @@ -1836,6 +1840,10 @@ void ZCCCompiler::InitDefaults() content = static_cast(content->SiblingNext); } while (content != d->Content); } + if (bag.DropItemSet) + { + bag.Info->SetDropItems(bag.DropItemList); + } } } } @@ -2239,5 +2247,13 @@ void ZCCCompiler::CompileStates() st = static_cast(st->SiblingNext); } while (st != s->Body); } + try + { + static_cast(c->Type())->Finalize(statedef); + } + catch (CRecoverableError &err) + { + Error(c->cls, "%s", err.GetMessage()); + } } } diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 34c1271e02..a15ec893a4 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -371,25 +371,30 @@ class Actor : Thinker native native int ACS_NamedExecuteWithResult(name script, int arg1=0, int arg2=0, int arg3=0, int arg4=0); native void ACS_NamedExecuteAlways(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0); -/* States { Spawn: - TNT1 A -1 - Stop + TNT1 A -1; + Stop; Null: - TNT1 A 1 - Stop + TNT1 A 1; + Stop; GenericFreezeDeath: // Generic freeze death frames. Woo! - "####" "#" 5 A_GenericFreezeDeath - "----" A 1 A_FreezeDeathChunks - Wait + #### # 5 A_GenericFreezeDeath; + ---- A 1 A_FreezeDeathChunks; + Wait; GenericCrush: - POL5 A -1 - Stop + POL5 A -1; + Stop; + Test.State: + &&&& A[\] 5 offset(4,4*2); + &&&& A[\] DEFAULT_HEALTH; + abcd efgh random(3, 6); + lght blahblah 5*3 light("furz", "bläh", "rülps"); + lght blahblah 5 light("boing"); + goto Actor::Spawn+1; } -*/ // Internal functions deprecated private native state __decorate_internal_state__(state s);