- preparations for the next steps.

This commit is contained in:
Christoph Oelckers 2018-11-09 00:00:00 +01:00
parent bbad004e25
commit 01d258910b
2 changed files with 367 additions and 404 deletions

View file

@ -0,0 +1,242 @@
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR(Bool, dumpspawnedthings, false, 0)
AActor *SpawnMapThing(int index, FMapThing *mt, int position)
{
AActor *spawned = P_SpawnMapThing(mt, position);
if (dumpspawnedthings)
{
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
}
T_AddSpawnedThing(spawned);
return spawned;
}
//===========================================================================
//
// SetMapThingUserData
//
//===========================================================================
static void SetMapThingUserData(AActor *actor, unsigned udi)
{
if (actor == NULL)
{
return;
}
while (MapThingsUserData[udi].Key != NAME_None)
{
FName varname = MapThingsUserData[udi].Key;
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
{
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
actor->GetClass()->TypeName.GetChars());
}
else
{ // Set the value of the specified user variable.
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
if (var->Type == TypeString)
{
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
}
else if (var->Type->isFloat())
{
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
}
else if (var->Type->isInt() || var->Type == TypeBool)
{
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
}
}
udi++;
}
}
//===========================================================================
//
// P_LoadThings
//
//===========================================================================
uint16_t MakeSkill(int flags)
{
uint16_t res = 0;
if (flags & 1) res |= 1+2;
if (flags & 2) res |= 4;
if (flags & 4) res |= 8+16;
return res;
}
void P_LoadThings (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthing_t);
char *mtp;
mapthing_t *mt;
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mt = (mapthing_t*)mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
// [RH] ZDoom now uses Hexen-style maps as its native format.
// Since this is the only place where Doom-style Things are ever
// referenced, we translate them into a Hexen-style thing.
for (int i=0 ; i < numthings; i++, mt++)
{
// [RH] At this point, monsters unique to Doom II were weeded out
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
// handle these and more cases better, so we just pass it
// everything and let it decide what to do with them.
// [RH] Need to translate the spawn flags to Hexen format.
short flags = LittleShort(mt->options);
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].Gravity = 1;
mti[i].Conversation = 0;
mti[i].SkillFilter = MakeSkill(flags);
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].pos.X = LittleShort(mt->x);
mti[i].pos.Y = LittleShort(mt->y);
mti[i].angle = LittleShort(mt->angle);
mti[i].EdNum = LittleShort(mt->type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
#ifndef NO_EDATA
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
{
ProcessEDMapthing(&mti[i], flags);
}
else
#endif
{
flags &= ~MTF_SKILLMASK;
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
if (gameinfo.gametype == GAME_Strife)
{
mti[i].flags &= ~MTF_AMBUSH;
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
else
{
if (flags & BTF_BADEDITORCHECK)
{
flags &= 0x1F;
}
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
}
}
delete [] mtp;
}
//===========================================================================
//
// [RH]
// P_LoadThings2
//
// Same as P_LoadThings() except it assumes Things are
// saved Hexen-style. Position also controls which single-
// player start spots are spawned by filtering out those
// whose first parameter don't match position.
//
//===========================================================================
void P_LoadThings2 (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthinghexen_t);
char *mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mapthinghexen_t *mth = (mapthinghexen_t*)mtp;
for(int i = 0; i< numthings; i++)
{
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].thingid = LittleShort(mth[i].thingid);
mti[i].pos.X = LittleShort(mth[i].x);
mti[i].pos.Y = LittleShort(mth[i].y);
mti[i].pos.Z = LittleShort(mth[i].z);
mti[i].angle = LittleShort(mth[i].angle);
mti[i].EdNum = LittleShort(mth[i].type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
mti[i].flags = LittleShort(mth[i].flags);
mti[i].special = mth[i].special;
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
mti[i].SkillFilter = MakeSkill(mti[i].flags);
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
if (level.flags2 & LEVEL2_HEXENHACK)
{
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
}
mti[i].Gravity = 1;
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].friendlyseeblocks = -1;
}
delete[] mtp;
}
//===========================================================================
//
//
//
//===========================================================================
void P_SpawnThings (int position)
{
int numthings = MapThingsConverted.Size();
for (int i=0; i < numthings; i++)
{
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
if (udi != NULL)
{
SetMapThingUserData(actor, *udi);
}
}
}

View file

@ -153,8 +153,6 @@ TArray<FMapThing> MapThingsConverted;
TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
TArray<FUDMFKey> MapThingsUserData; TArray<FUDMFKey> MapThingsUserData;
bool ForceNodeBuild;
static void P_AllocateSideDefs (MapData *map, int count); static void P_AllocateSideDefs (MapData *map, int count);
//=========================================================================== //===========================================================================
@ -245,246 +243,6 @@ void P_FloodZones ()
} }
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR(Bool, dumpspawnedthings, false, 0)
AActor *SpawnMapThing(int index, FMapThing *mt, int position)
{
AActor *spawned = P_SpawnMapThing(mt, position);
if (dumpspawnedthings)
{
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
}
T_AddSpawnedThing(spawned);
return spawned;
}
//===========================================================================
//
// SetMapThingUserData
//
//===========================================================================
static void SetMapThingUserData(AActor *actor, unsigned udi)
{
if (actor == NULL)
{
return;
}
while (MapThingsUserData[udi].Key != NAME_None)
{
FName varname = MapThingsUserData[udi].Key;
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
{
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
actor->GetClass()->TypeName.GetChars());
}
else
{ // Set the value of the specified user variable.
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
if (var->Type == TypeString)
{
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
}
else if (var->Type->isFloat())
{
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
}
else if (var->Type->isInt() || var->Type == TypeBool)
{
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
}
}
udi++;
}
}
//===========================================================================
//
// P_LoadThings
//
//===========================================================================
uint16_t MakeSkill(int flags)
{
uint16_t res = 0;
if (flags & 1) res |= 1+2;
if (flags & 2) res |= 4;
if (flags & 4) res |= 8+16;
return res;
}
void P_LoadThings (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthing_t);
char *mtp;
mapthing_t *mt;
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mt = (mapthing_t*)mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
// [RH] ZDoom now uses Hexen-style maps as its native format.
// Since this is the only place where Doom-style Things are ever
// referenced, we translate them into a Hexen-style thing.
for (int i=0 ; i < numthings; i++, mt++)
{
// [RH] At this point, monsters unique to Doom II were weeded out
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
// handle these and more cases better, so we just pass it
// everything and let it decide what to do with them.
// [RH] Need to translate the spawn flags to Hexen format.
short flags = LittleShort(mt->options);
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].Gravity = 1;
mti[i].Conversation = 0;
mti[i].SkillFilter = MakeSkill(flags);
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].pos.X = LittleShort(mt->x);
mti[i].pos.Y = LittleShort(mt->y);
mti[i].angle = LittleShort(mt->angle);
mti[i].EdNum = LittleShort(mt->type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
#ifndef NO_EDATA
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
{
ProcessEDMapthing(&mti[i], flags);
}
else
#endif
{
flags &= ~MTF_SKILLMASK;
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
if (gameinfo.gametype == GAME_Strife)
{
mti[i].flags &= ~MTF_AMBUSH;
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
else
{
if (flags & BTF_BADEDITORCHECK)
{
flags &= 0x1F;
}
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
}
}
delete [] mtp;
}
//===========================================================================
//
// [RH]
// P_LoadThings2
//
// Same as P_LoadThings() except it assumes Things are
// saved Hexen-style. Position also controls which single-
// player start spots are spawned by filtering out those
// whose first parameter don't match position.
//
//===========================================================================
void P_LoadThings2 (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthinghexen_t);
char *mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mapthinghexen_t *mth = (mapthinghexen_t*)mtp;
for(int i = 0; i< numthings; i++)
{
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].thingid = LittleShort(mth[i].thingid);
mti[i].pos.X = LittleShort(mth[i].x);
mti[i].pos.Y = LittleShort(mth[i].y);
mti[i].pos.Z = LittleShort(mth[i].z);
mti[i].angle = LittleShort(mth[i].angle);
mti[i].EdNum = LittleShort(mth[i].type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
mti[i].flags = LittleShort(mth[i].flags);
mti[i].special = mth[i].special;
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
mti[i].SkillFilter = MakeSkill(mti[i].flags);
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
if (level.flags2 & LEVEL2_HEXENHACK)
{
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
}
mti[i].Gravity = 1;
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].friendlyseeblocks = -1;
}
delete[] mtp;
}
//===========================================================================
//
//
//
//===========================================================================
void P_SpawnThings (int position)
{
int numthings = MapThingsConverted.Size();
for (int i=0; i < numthings; i++)
{
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
if (udi != NULL)
{
SetMapThingUserData(actor, *udi);
}
}
}
//=========================================================================== //===========================================================================
// //
// //
@ -891,138 +649,117 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
{ {
E_NewGame(EventHandlerType::PerMap); E_NewGame(EventHandlerType::PerMap);
} }
FBehavior::StaticUnloadModules ();
// [RH] Support loading Build maps (because I felt like it. :-) if (!map->HasBehavior || map->isText)
buildmap = false;
#if 0
// deactivated because broken.
if (map->Size(0) > 0)
{ {
uint8_t *mapdata = new uint8_t[map->Size(0)]; // Doom format and UDMF text maps get strict monster activation unless the mapinfo
map->Read(0, mapdata); // specifies differently.
times[0].Clock(); if (!(level.flags2 & LEVEL2_LAXACTIVATIONMAPINFO))
buildmap = P_LoadBuildMap (mapdata, map->Size(0), &buildthings, &numbuildthings); {
times[0].Unclock(); level.flags2 &= ~LEVEL2_LAXMONSTERACTIVATION;
delete[] mapdata; }
} }
#endif
if (!map->HasBehavior && !map->isText)
{
// set compatibility flags
if (gameinfo.gametype == GAME_Strife)
{
level.flags2 |= LEVEL2_RAILINGHACK;
}
level.flags2 |= LEVEL2_DUMMYSWITCHES;
}
// ----------------------------------------->
MapLoader maploader(level, &tagManager); MapLoader maploader(level, &tagManager);
maploader.maptype = level.maptype; maploader.maptype = level.maptype;
if (!buildmap) maploader.ForceNodeBuild = gennodes;
// note: most of this ordering is important
// [RH] Load in the BEHAVIOR lump
if (map->HasBehavior)
{ {
// note: most of this ordering is important P_LoadBehavior (map);
ForceNodeBuild = gennodes; level.maptype = MAPTYPE_HEXEN;
// [RH] Load in the BEHAVIOR lump
FBehavior::StaticUnloadModules ();
if (map->HasBehavior)
{
P_LoadBehavior (map);
level.maptype = MAPTYPE_HEXEN;
}
else
{
// We need translators only for Doom format maps.
const char *translator;
if (!level.info->Translator.IsEmpty())
{
// The map defines its own translator.
translator = level.info->Translator.GetChars();
}
else
{
// Has the user overridden the game's default translator with a commandline parameter?
translator = Args->CheckValue("-xlat");
if (translator == NULL)
{
// Use the game's default.
translator = gameinfo.translator.GetChars();
}
}
P_LoadTranslator(translator);
level.maptype = MAPTYPE_DOOM;
}
if (map->isText)
{
level.maptype = MAPTYPE_UDMF;
}
FName checksum = CheckCompatibility(map);
if (ib_compatflags & BCOMPATF_REBUILDNODES)
{
ForceNodeBuild = true;
}
T_LoadScripts(map);
if (!map->HasBehavior || map->isText)
{
// Doom format and UDMF text maps get strict monster activation unless the mapinfo
// specifies differently.
if (!(level.flags2 & LEVEL2_LAXACTIVATIONMAPINFO))
{
level.flags2 &= ~LEVEL2_LAXMONSTERACTIVATION;
}
}
if (!map->HasBehavior && !map->isText)
{
// set compatibility flags
if (gameinfo.gametype == GAME_Strife)
{
level.flags2 |= LEVEL2_RAILINGHACK;
}
level.flags2 |= LEVEL2_DUMMYSWITCHES;
}
FBehavior::StaticLoadDefaultModules ();
#ifndef NO_EDATA
LoadMapinfoACSLump();
#endif
P_LoadStrifeConversations (map, lumpname);
FMissingTextureTracker missingtex;
times[0].Clock();
if (!map->isText)
{
maploader.LoadVertexes (map);
maploader.LoadSectors (map, missingtex);
if (!map->HasBehavior)
maploader.LoadLineDefs (map);
else
maploader.LoadLineDefs2 (map); // [RH] Load Hexen-style linedefs
maploader.LoadSideDefs2 (map, missingtex);
maploader.FinishLoadingLineDefs ();
if (!map->HasBehavior)
P_LoadThings (map);
else
P_LoadThings2 (map); // [RH] Load Hexen-style things
}
else
{
maploader.ParseTextMap(map, missingtex);
}
times[0].Unclock();
SetCompatibilityParams(checksum);
times[6].Clock();
maploader.LoopSidedefs (true);
times[6].Unclock();
SummarizeMissingTextures(missingtex);
} }
else else
{ {
ForceNodeBuild = true; // We need translators only for Doom format maps.
level.maptype = MAPTYPE_BUILD; const char *translator;
if (!level.info->Translator.IsEmpty())
{
// The map defines its own translator.
translator = level.info->Translator.GetChars();
}
else
{
// Has the user overridden the game's default translator with a commandline parameter?
translator = Args->CheckValue("-xlat");
if (translator == NULL)
{
// Use the game's default.
translator = gameinfo.translator.GetChars();
}
}
P_LoadTranslator(translator);
level.maptype = MAPTYPE_DOOM;
} }
if (map->isText)
{
level.maptype = MAPTYPE_UDMF;
}
FName checksum = CheckCompatibility(map);
if (ib_compatflags & BCOMPATF_REBUILDNODES)
{
maploader.ForceNodeBuild = true;
}
T_LoadScripts(map);
FBehavior::StaticLoadDefaultModules ();
#ifndef NO_EDATA
LoadMapinfoACSLump();
#endif
P_LoadStrifeConversations (map, lumpname);
FMissingTextureTracker missingtex;
times[0].Clock();
if (!map->isText)
{
maploader.LoadVertexes (map);
maploader.LoadSectors (map, missingtex);
if (!map->HasBehavior)
maploader.LoadLineDefs (map);
else
maploader.LoadLineDefs2 (map); // [RH] Load Hexen-style linedefs
maploader.LoadSideDefs2 (map, missingtex);
maploader.FinishLoadingLineDefs ();
if (!map->HasBehavior)
P_LoadThings (map);
else
P_LoadThings2 (map); // [RH] Load Hexen-style things
}
else
{
maploader.ParseTextMap(map, missingtex);
}
times[0].Unclock();
SetCompatibilityParams(checksum);
maploader.LoopSidedefs (true);
SummarizeMissingTextures(missingtex);
bool reloop = false; bool reloop = false;
maploader.LoadBsp(map); maploader.LoadBsp(map);
@ -1032,66 +769,50 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
maploader.LoadBlockMap (map); maploader.LoadBlockMap (map);
times[11].Clock();
P_LoadReject (map, buildmap); P_LoadReject (map, buildmap);
times[11].Unclock();
times[12].Clock();
maploader.GroupLines (buildmap); maploader.GroupLines (buildmap);
maploader.SetSlopes(); maploader.SetSlopes();
times[12].Unclock();
times[13].Clock();
P_FloodZones (); P_FloodZones ();
times[13].Unclock();
P_SetRenderSector(); P_SetRenderSector();
FixMinisegReferences(); FixMinisegReferences();
FixHoles(); FixHoles();
bodyqueslot = 0;
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one.
// [RH] Spawn slope creating things first.
maploader.SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], maploader.oldvertextable);
maploader.CopySlopes();
//////////////////////// <-------------------------------------
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one.
bodyqueslot = 0;
for (i = 0; i < BODYQUESIZE; i++) for (i = 0; i < BODYQUESIZE; i++)
bodyque[i] = NULL; bodyque[i] = NULL;
CreateSections(level.sections); CreateSections(level.sections);
// Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials
P_Spawn3DFloors();
if (!buildmap) times[14].Clock();
P_SpawnThings(position);
for (i = 0; i < MAXPLAYERS; ++i)
{ {
// [RH] Spawn slope creating things first. if (playeringame[i] && players[i].mo != NULL)
maploader.SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], maploader.oldvertextable); players[i].health = players[i].mo->health;
maploader.CopySlopes();
// Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials
P_Spawn3DFloors();
times[14].Clock();
P_SpawnThings(position);
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo != NULL)
players[i].health = players[i].mo->health;
}
times[14].Unclock();
times[15].Clock();
if (!map->HasBehavior && !map->isText)
P_TranslateTeleportThings (); // [RH] Assign teleport destination TIDs
times[15].Unclock();
} }
#if 0 // There is no such thing as a build map. times[14].Unclock();
else
{ times[15].Clock();
for (i = 0; i < numbuildthings; ++i) if (!map->HasBehavior && !map->isText)
{ P_TranslateTeleportThings (); // [RH] Assign teleport destination TIDs
SpawnMapThing (i, &buildthings[i], 0); times[15].Unclock();
}
delete[] buildthings;
}
#endif
delete map; delete map;
// set up world state // set up world state