From 15dbbc9137dc10018ebac48b2f5e407e2665ad2f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 3 Apr 2015 16:51:45 +0200 Subject: [PATCH] - set editor numbers through MAPINFO. The DECORATE way still works ans will override any definition made in MAPINFO. - use a standard TMap for finding editor numbers --- src/d_main.cpp | 1 + src/g_doomedmap.cpp | 272 ++++++++++++++++++++++++++------------------ src/g_level.h | 1 + src/g_mapinfo.cpp | 12 ++ src/info.cpp | 21 ++-- src/info.h | 32 ++---- src/p_mobj.cpp | 69 ++++++----- 7 files changed, 235 insertions(+), 173 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 8b9e54680..6b5399c00 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2440,6 +2440,7 @@ void D_DoomMain (void) // Create replacements for dehacked pickups FinishDehPatch(); + InitActorNumsFromMapinfo(); FActorInfo::StaticSetActorNums (); //Added by MC: diff --git a/src/g_doomedmap.cpp b/src/g_doomedmap.cpp index 9ee2ce0c5..521b98373 100644 --- a/src/g_doomedmap.cpp +++ b/src/g_doomedmap.cpp @@ -34,20 +34,34 @@ */ #include "info.h" +#include "p_lnspec.h" #include "m_fixed.h" #include "c_dispatch.h" -#include "d_net.h" -#include "v_text.h" - -#include "gi.h" - -#include "actor.h" -#include "r_state.h" -#include "i_system.h" -#include "p_local.h" #include "templates.h" #include "cmdlib.h" #include "g_level.h" +#include "v_text.h" +#include "i_system.h" + +//========================================================================== +// +// Stuff that's only valid during definition time +// +//========================================================================== + +struct MapinfoEdMapItem +{ + FName classname; // DECORATE is read after MAPINFO so we do not have the actual classes available here yet. + int special; + int args[5]; + // These are for error reporting. We must store the file information because it's no longer available when these items get resolved. + FString filename; + int linenum; +}; + +typedef TMap IdMap; + +static IdMap DoomEdFromMapinfo; //========================================================================== // @@ -56,108 +70,20 @@ FDoomEdMap DoomEdMap; -FDoomEdMap::FDoomEdEntry *FDoomEdMap::DoomEdHash[DOOMED_HASHSIZE]; - -FDoomEdMap::~FDoomEdMap() -{ - Empty(); -} - -void FDoomEdMap::AddType (int doomednum, const PClass *type, bool temporary) -{ - unsigned int hash = (unsigned int)doomednum % DOOMED_HASHSIZE; - FDoomEdEntry *entry = DoomEdHash[hash]; - while (entry && entry->DoomEdNum != doomednum) - { - entry = entry->HashNext; - } - if (entry == NULL) - { - entry = new FDoomEdEntry; - entry->HashNext = DoomEdHash[hash]; - entry->DoomEdNum = doomednum; - DoomEdHash[hash] = entry; - } - else if (!entry->temp) - { - Printf (PRINT_BOLD, "Warning: %s and %s both have doomednum %d.\n", - type->TypeName.GetChars(), entry->Type->TypeName.GetChars(), doomednum); - } - entry->temp = temporary; - entry->Type = type; -} - -void FDoomEdMap::DelType (int doomednum) -{ - unsigned int hash = (unsigned int)doomednum % DOOMED_HASHSIZE; - FDoomEdEntry **prev = &DoomEdHash[hash]; - FDoomEdEntry *entry = *prev; - while (entry && entry->DoomEdNum != doomednum) - { - prev = &entry->HashNext; - entry = entry->HashNext; - } - if (entry != NULL) - { - *prev = entry->HashNext; - delete entry; - } -} - -void FDoomEdMap::Empty () -{ - int bucket; - - for (bucket = 0; bucket < DOOMED_HASHSIZE; ++bucket) - { - FDoomEdEntry *probe = DoomEdHash[bucket]; - - while (probe != NULL) - { - FDoomEdEntry *next = probe->HashNext; - delete probe; - probe = next; - } - DoomEdHash[bucket] = NULL; - } -} - -const PClass *FDoomEdMap::FindType (int doomednum) const -{ - unsigned int hash = (unsigned int)doomednum % DOOMED_HASHSIZE; - FDoomEdEntry *entry = DoomEdHash[hash]; - while (entry && entry->DoomEdNum != doomednum) - entry = entry->HashNext; - return entry ? entry->Type : NULL; -} - -struct EdSorting -{ - const PClass *Type; - int DoomEdNum; -}; - static int STACK_ARGS sortnums (const void *a, const void *b) { - return ((const EdSorting *)a)->DoomEdNum - - ((const EdSorting *)b)->DoomEdNum; + return (*(const FDoomEdMap::Pair**)a)->Key - (*(const FDoomEdMap::Pair**)b)->Key; } -void FDoomEdMap::DumpMapThings () +CCMD (dumpmapthings) { - TArray infos (PClass::m_Types.Size()); - int i; + TArray infos(DoomEdMap.CountUsed()); + FDoomEdMap::Iterator it(DoomEdMap); + FDoomEdMap::Pair *pair; - for (i = 0; i < DOOMED_HASHSIZE; ++i) + while (it.NextPair(pair)) { - FDoomEdEntry *probe = DoomEdHash[i]; - - while (probe != NULL) - { - EdSorting sorting = { probe->Type, probe->DoomEdNum }; - infos.Push (sorting); - probe = probe->HashNext; - } + infos.Push(pair); } if (infos.Size () == 0) @@ -166,17 +92,145 @@ void FDoomEdMap::DumpMapThings () } else { - qsort (&infos[0], infos.Size (), sizeof(EdSorting), sortnums); + qsort (&infos[0], infos.Size (), sizeof(FDoomEdMap::Pair*), sortnums); - for (i = 0; i < (int)infos.Size (); ++i) + for (unsigned i = 0; i < infos.Size (); ++i) { Printf ("%6d %s\n", - infos[i].DoomEdNum, infos[i].Type->TypeName.GetChars()); + infos[i]->Key, infos[i]->Value.Type->TypeName.GetChars()); } } } -CCMD (dumpmapthings) + +void FMapInfoParser::ParseDoomEdNums() { - FDoomEdMap::DumpMapThings (); + TMap defined; + int error = 0; + + MapinfoEdMapItem editem; + + editem.filename = sc.ScriptName; + + sc.MustGetStringName("{"); + while (true) + { + if (sc.CheckString("}")) return; + else if (sc.CheckNumber()) + { + int ednum = sc.Number; + sc.MustGetStringName("="); + sc.MustGetString(); + + bool *def = defined.CheckKey(ednum); + if (def != NULL) + { + sc.ScriptMessage("Editor Number %d defined more than once", ednum); + error++; + } + defined[ednum] = true; + if (sc.String[0] == '$') + { + // todo: add special stuff like playerstarts and sound sequence overrides here, too. + editem.classname = NAME_None; + editem.special = 1; // todo: assign proper constants + } + else + { + editem.classname = sc.String; + editem.special = -1; + } + memset(editem.args, 0, sizeof(editem.args)); + + int minargs = 0; + int maxargs = 5; + FString specialname; + if (sc.CheckString(",")) + { + // todo: parse a special or args + editem.special = 0; // mark args as used - if this is done we need to prevent assignment of map args in P_SpawnMapThing. + if (!sc.CheckNumber()) + { + sc.MustGetString(); + specialname = sc.String; // save for later error reporting. + editem.special = P_FindLineSpecial(sc.String, &minargs, &maxargs); + if (editem.special == 0 || minargs == -1) + { + sc.ScriptMessage("Invalid special %s for Editor Number %d", sc.String, ednum); + error++; + minargs = 0; + maxargs = 5; + } + if (!sc.CheckString(",")) + { + // special case: Special without arguments + if (minargs != 0) + { + sc.ScriptMessage("Incorrect number of args for special %s, min = %d, max = %d, found = 0", specialname.GetChars(), minargs, maxargs); + error++; + } + DoomEdFromMapinfo.Insert(ednum, editem); + continue; + } + sc.MustGetStringName(","); + sc.MustGetNumber(); + } + int i = 0; + while (i < 5) + { + editem.args[i++] = sc.Number; + i++; + if (!sc.CheckString(",")) break; + sc.MustGetNumber(); + } + if (specialname.IsNotEmpty() && (i < minargs || i > maxargs)) + { + sc.ScriptMessage("Incorrect number of args for special %s, min = %d, max = %d, found = %d", specialname.GetChars(), minargs, maxargs, i); + error++; + } + } + DoomEdFromMapinfo.Insert(ednum, editem); + } + else + { + sc.ScriptError("Number expected"); + } + } + if (error > 0) + { + sc.ScriptError("%d errors encountered in DoomEdNum definition"); + } +} + +void InitActorNumsFromMapinfo() +{ + DoomEdMap.Clear(); + IdMap::Iterator it(DoomEdFromMapinfo); + IdMap::Pair *pair; + int error = 0; + + while (it.NextPair(pair)) + { + const PClass *cls = NULL; + if (pair->Value.classname != NAME_None) + { + cls = PClass::FindClass(pair->Value.classname); + if (cls == NULL) + { + Printf(TEXTCOLOR_RED "Script error, \"%s\" line %d:\nUnknown actor class %s\n", + pair->Value.filename.GetChars(), pair->Value.linenum, pair->Value.classname.GetChars()); + error++; + } + } + FDoomEdEntry ent; + ent.Type = cls; + ent.Special = pair->Value.special; + memcpy(ent.Args, pair->Value.args, sizeof(ent.Args)); + DoomEdMap.Insert(pair->Key, ent); + } + if (error > 0) + { + I_Error("%d unknown actor classes found", error); + } + DoomEdFromMapinfo.Clear(); // we do not need this any longer } diff --git a/src/g_level.h b/src/g_level.h index f94fefa42..50a661db9 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -104,6 +104,7 @@ struct FMapInfoParser void ParseIntermissionAction(FIntermissionDescriptor *Desc); void ParseIntermission(); + void ParseDoomEdNums(); void ParseAMColors(bool); FName CheckEndSequence(); FName ParseEndGame(); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 306c3645a..a99056eeb 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1876,6 +1876,18 @@ void FMapInfoParser::ParseMapInfo (int lump, level_info_t &gamedefaults, level_i sc.ScriptError("intermission definitions not supported with old MAPINFO syntax"); } } + else if (sc.Compare("doomednums")) + { + if (format_type != FMT_Old) + { + format_type = FMT_New; + ParseDoomEdNums(); + } + else + { + sc.ScriptError("doomednums definitions not supported with old MAPINFO syntax"); + } + } else if (sc.Compare("automap") || sc.Compare("automap_overlay")) { if (format_type != FMT_Old) diff --git a/src/info.cpp b/src/info.cpp index f4852a1d1..7677ac4a7 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -142,7 +142,6 @@ void FActorInfo::StaticInit () void FActorInfo::StaticSetActorNums () { SpawnableThings.Clear(); - DoomEdMap.Empty (); for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i) { @@ -171,7 +170,16 @@ void FActorInfo::RegisterIDs () } if (DoomEdNum != -1) { - DoomEdMap.AddType (DoomEdNum, cls); + FDoomEdEntry *oldent = DoomEdMap.CheckKey(DoomEdNum); + if (oldent != NULL && oldent->Special == -2) + { + Printf(TEXTCOLOR_RED"Editor number %d defined twice for classes '%s' and '%s'\n", DoomEdNum, cls->TypeName.GetChars(), oldent->Type->TypeName.GetChars()); + } + FDoomEdEntry ent; + memset(&ent, 0, sizeof(ent)); + ent.Type = cls; + ent.Special = -2; // use -2 instead of -1 so that we can recognize DECORATE defined entries and print a warning message if duplicates occur. + DoomEdMap.Insert(DoomEdNum, ent); if (cls != Class) { Printf(TEXTCOLOR_RED"Editor number %d refers to hidden class type '%s'\n", DoomEdNum, cls->TypeName.GetChars()); @@ -179,15 +187,6 @@ void FActorInfo::RegisterIDs () } } // Fill out the list for Chex Quest with Doom's actors - if (gameinfo.gametype == GAME_Chex && DoomEdMap.FindType(DoomEdNum) == NULL && - (GameFilter & GAME_Doom)) - { - DoomEdMap.AddType (DoomEdNum, Class, true); - if (cls != Class) - { - Printf(TEXTCOLOR_RED"Editor number %d refers to hidden class type '%s'\n", DoomEdNum, cls->TypeName.GetChars()); - } - } } //========================================================================== diff --git a/src/info.h b/src/info.h index 13a8eb5e1..86f2bbdd5 100644 --- a/src/info.h +++ b/src/info.h @@ -278,34 +278,20 @@ struct FActorInfo TArray ForbiddenToPlayerClass; }; -class FDoomEdMap +struct FDoomEdEntry { -public: - ~FDoomEdMap(); - - const PClass *FindType (int doomednum) const; - void AddType (int doomednum, const PClass *type, bool temporary = false); - void DelType (int doomednum); - void Empty (); - - static void DumpMapThings (); - -private: - enum { DOOMED_HASHSIZE = 256 }; - - struct FDoomEdEntry - { - FDoomEdEntry *HashNext; - const PClass *Type; - int DoomEdNum; - bool temp; - }; - - static FDoomEdEntry *DoomEdHash[DOOMED_HASHSIZE]; + const PClass *Type; + int Special; + int Args[5]; }; +typedef TMap FDoomEdMap; + extern FDoomEdMap DoomEdMap; +void InitActorNumsFromMapinfo(); + + int GetSpriteIndex(const char * spritename, bool add = true); TArray &MakeStateNameList(const char * fname); void AddStateLight(FState *state, const char *lname); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 9dd9f1ddc..fea31ada3 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4600,6 +4600,28 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) if (mthing->type == 0 || mthing->type == -1) return NULL; + // find which type to spawn + FDoomEdEntry *mentry = DoomEdMap.CheckKey(mthing->type); + + if (mentry == NULL) + { + // [RH] Don't die if the map tries to spawn an unknown thing + Printf ("Unknown type %i at (%i, %i)\n", + mthing->type, + mthing->x>>FRACBITS, mthing->y>>FRACBITS); + mentry = DoomEdMap.CheckKey(0); + if (mentry == NULL) // we need a valid entry for the rest of this function so if we can't find a default, let's exit right away. + { + return NULL; + } + } + if (mentry->Type == NULL && mentry->Special <= 0) + { + // has been explicitly set to not spawning anything. + return NULL; + } + + // count deathmatch start positions if (mthing->type == 11) { @@ -4776,39 +4798,25 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mthing->args[0] = mthing->type - 14100; mthing->type = 14165; } - // find which type to spawn - i = DoomEdMap.FindType (mthing->type); - - if (i == NULL) - { - // [RH] Don't die if the map tries to spawn an unknown thing - Printf ("Unknown type %i at (%i, %i)\n", - mthing->type, - mthing->x>>FRACBITS, mthing->y>>FRACBITS); - i = PClass::FindClass("Unknown"); - } // [RH] If the thing's corresponding sprite has no frames, also map // it to the unknown thing. - else + // Handle decorate replacements explicitly here + // to check for missing frames in the replacement object. + i = mentry->Type->GetReplacement(); + + const AActor *defaults = GetDefaultByType (i); + if (defaults->SpawnState == NULL || + sprites[defaults->SpawnState->sprite].numframes == 0) { - // Handle decorate replacements explicitly here - // to check for missing frames in the replacement object. - i = i->GetReplacement(); + // We don't load mods for shareware games so we'll just ignore + // missing actors. Heretic needs this since the shareware includes + // the retail weapons in Deathmatch. + if (gameinfo.flags & GI_SHAREWARE) + return NULL; - const AActor *defaults = GetDefaultByType (i); - if (defaults->SpawnState == NULL || - sprites[defaults->SpawnState->sprite].numframes == 0) - { - // We don't load mods for shareware games so we'll just ignore - // missing actors. Heretic needs this since the shareware includes - // the retail weapons in Deathmatch. - if (gameinfo.flags & GI_SHAREWARE) - return NULL; - - Printf ("%s at (%i, %i) has no frames\n", - i->TypeName.GetChars(), mthing->x>>FRACBITS, mthing->y>>FRACBITS); - i = PClass::FindClass("Unknown"); - } + Printf ("%s at (%i, %i) has no frames\n", + i->TypeName.GetChars(), mthing->x>>FRACBITS, mthing->y>>FRACBITS); + i = PClass::FindClass("Unknown"); } const AActor *info = GetDefaultByType (i); @@ -4898,7 +4906,8 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) P_FindFloorCeiling(mobj, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); } - if (!(mobj->flags2 & MF2_ARGSDEFINED)) + // if the actor got args defined either in DECORATE or MAPINFO we must ignore the map's properties. + if (!(mobj->flags2 & MF2_ARGSDEFINED) && (mentry == NULL || mentry->Special < 0)) { // [RH] Set the thing's special mobj->special = mthing->special;