2016-03-01 15:47:10 +00:00
//-----------------------------------------------------------------------------
//
2017-04-17 11:33:19 +00:00
// Copyright 1993-1996 id Software
// Copyright 1994-1996 Raven Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2016 Christoph Oelckers
// Copyright 2010 James Haley
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is distributed in the hope that it will be useful,
2016-03-01 15:47:10 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2017-04-17 11:33:19 +00:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
//-----------------------------------------------------------------------------
2016-03-01 15:47:10 +00:00
//
// DESCRIPTION:
// Do all the WAD I/O, get map description,
// set up initial state and misc. LUTs.
//
//-----------------------------------------------------------------------------
2017-04-17 11:33:19 +00:00
/* For code that originates from ZDoom the following applies:
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-03-01 15:47:10 +00:00
# include <math.h>
# ifdef _MSC_VER
# include <malloc.h> // for alloca()
# endif
# include "templates.h"
# include "d_player.h"
# include "m_argv.h"
# include "g_game.h"
# include "w_wad.h"
# include "p_local.h"
# include "p_effect.h"
# include "p_terrain.h"
# include "nodebuild.h"
# include "p_lnspec.h"
# include "c_console.h"
# include "p_acs.h"
# include "announcer.h"
# include "wi_stuff.h"
# include "doomerrors.h"
# include "gi.h"
# include "p_conversation.h"
# include "a_keys.h"
# include "s_sndseq.h"
# include "sbar.h"
# include "p_setup.h"
# include "r_data/r_interpolate.h"
# include "r_sky.h"
# include "cmdlib.h"
# include "md5.h"
# include "compatibility.h"
# include "po_man.h"
# include "r_renderer.h"
# include "p_blockmap.h"
# include "r_utility.h"
# include "p_spec.h"
2017-01-08 17:45:30 +00:00
# include "g_levellocals.h"
2018-04-01 16:45:27 +00:00
# include "c_dispatch.h"
2018-04-01 20:26:57 +00:00
# include "a_dynlight.h"
2016-03-01 15:47:10 +00:00
# ifndef NO_EDATA
# include "edata.h"
# endif
2017-02-03 07:04:01 +00:00
# include "events.h"
2018-10-28 22:08:38 +00:00
# include "p_destructible.h"
2017-04-12 23:12:04 +00:00
# include "types.h"
2017-11-12 08:06:40 +00:00
# include "i_time.h"
2018-01-07 00:59:49 +00:00
# include "scripting/vm/vm.h"
2018-10-28 13:25:29 +00:00
# include "hwrenderer/data/flatvertices.h"
2016-03-01 15:47:10 +00:00
# include "fragglescript/t_fs.h"
# define MISSING_TEXTURE_WARN_LIMIT 20
void P_SpawnSlopeMakers ( FMapThing * firstmt , FMapThing * lastmt , const int * oldvertextable ) ;
void P_SetSlopes ( ) ;
void P_CopySlopes ( ) ;
void BloodCrypt ( void * data , int key , int len ) ;
void P_ClearUDMFKeys ( ) ;
2018-04-02 07:27:40 +00:00
void InitRenderInfo ( ) ;
2016-03-01 15:47:10 +00:00
extern AActor * P_SpawnMapThing ( FMapThing * mthing , int position ) ;
extern void P_TranslateTeleportThings ( void ) ;
void P_ParseTextMap ( MapData * map , FMissingTextureTracker & ) ;
extern int numinterpolations ;
extern unsigned int R_OldBlend ;
EXTERN_CVAR ( Bool , am_textured )
CVAR ( Bool , genblockmap , false , CVAR_SERVERINFO | CVAR_GLOBALCONFIG ) ;
CVAR ( Bool , gennodes , false , CVAR_SERVERINFO | CVAR_GLOBALCONFIG ) ;
CVAR ( Bool , genglnodes , false , CVAR_SERVERINFO ) ;
CVAR ( Bool , showloadtimes , false , 0 ) ;
static void P_Shutdown ( ) ;
2017-01-08 13:39:16 +00:00
inline bool P_IsBuildMap ( MapData * map )
{
return false ;
}
2017-03-08 14:20:00 +00:00
inline bool P_LoadBuildMap ( uint8_t * mapdata , size_t len , FMapThing * * things , int * numthings )
2017-01-08 13:39:16 +00:00
{
return false ;
}
2016-03-01 15:47:10 +00:00
//
// MAP related Lookup tables.
// Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
//
2017-01-09 00:40:14 +00:00
TArray < vertexdata_t > vertexdatas ;
2016-03-01 15:47:10 +00:00
bool hasglnodes ;
TArray < FMapThing > MapThingsConverted ;
TMap < unsigned , unsigned > MapThingsUserDataIndex ; // from mapthing idx -> user data idx
2018-03-19 14:39:50 +00:00
TArray < FUDMFKey > MapThingsUserData ;
2016-03-01 15:47:10 +00:00
int sidecount ;
sidei_t * sidetemp ;
TArray < int > linemap ;
bool ForceNodeBuild ;
2017-01-08 17:45:30 +00:00
static void P_AllocateSideDefs ( MapData * map , int count ) ;
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// GetMapIndex
//
// Gets the type of map lump or -1 if invalid or -2 if required and not found.
//
//===========================================================================
struct checkstruct
{
const char lumpname [ 9 ] ;
bool required ;
} ;
static int GetMapIndex ( const char * mapname , int lastindex , const char * lumpname , bool needrequired )
{
static const checkstruct check [ ] =
{
{ " " , true } ,
{ " THINGS " , true } ,
{ " LINEDEFS " , true } ,
{ " SIDEDEFS " , true } ,
{ " VERTEXES " , true } ,
{ " SEGS " , false } ,
{ " SSECTORS " , false } ,
{ " NODES " , false } ,
{ " SECTORS " , true } ,
{ " REJECT " , false } ,
{ " BLOCKMAP " , false } ,
{ " BEHAVIOR " , false } ,
//{"SCRIPTS", false},
} ;
if ( lumpname = = NULL ) lumpname = " " ;
for ( size_t i = lastindex + 1 ; i < countof ( check ) ; i + + )
{
if ( ! strnicmp ( lumpname , check [ i ] . lumpname , 8 ) )
return ( int ) i ;
if ( check [ i ] . required )
{
if ( needrequired )
{
I_Error ( " '%s' not found in %s \n " , check [ i ] . lumpname , mapname ) ;
}
return - 2 ;
}
}
return - 1 ; // End of map reached
}
//===========================================================================
//
// Opens a map for reading
//
//===========================================================================
MapData * P_OpenMapData ( const char * mapname , bool justcheck )
{
MapData * map = new MapData ;
2018-03-11 17:32:00 +00:00
FileReader * wadReader = nullptr ;
2016-03-01 15:47:10 +00:00
bool externalfile = ! strnicmp ( mapname , " file: " , 5 ) ;
if ( externalfile )
{
mapname + = 5 ;
if ( ! FileExists ( mapname ) )
{
delete map ;
return NULL ;
}
2018-03-11 11:33:46 +00:00
map - > resource = FResourceFile : : OpenResourceFile ( mapname , true ) ;
2016-03-01 15:47:10 +00:00
wadReader = map - > resource - > GetReader ( ) ;
}
else
{
FString fmt ;
int lump_wad ;
int lump_map ;
int lump_name = - 1 ;
// Check for both *.wad and *.map in order to load Build maps
// as well. The higher one will take precedence.
// Names with more than 8 characters will only be checked as .wad and .map.
if ( strlen ( mapname ) < = 8 ) lump_name = Wads . CheckNumForName ( mapname ) ;
fmt . Format ( " maps/%s.wad " , mapname ) ;
lump_wad = Wads . CheckNumForFullName ( fmt ) ;
fmt . Format ( " maps/%s.map " , mapname ) ;
lump_map = Wads . CheckNumForFullName ( fmt ) ;
if ( lump_name > lump_wad & & lump_name > lump_map & & lump_name ! = - 1 )
{
int lumpfile = Wads . GetLumpFile ( lump_name ) ;
int nextfile = Wads . GetLumpFile ( lump_name + 1 ) ;
map - > lumpnum = lump_name ;
if ( lumpfile ! = nextfile )
{
// The following lump is from a different file so whatever this is,
// it is not a multi-lump Doom level so let's assume it is a Build map.
2018-03-10 20:48:33 +00:00
map - > MapLumps [ 0 ] . Reader = Wads . ReopenLumpReader ( lump_name ) ;
2016-03-01 15:47:10 +00:00
if ( ! P_IsBuildMap ( map ) )
{
delete map ;
return NULL ;
}
return map ;
}
// This case can only happen if the lump is inside a real WAD file.
// As such any special handling for other types of lumps is skipped.
2018-03-10 20:48:33 +00:00
map - > MapLumps [ 0 ] . Reader = Wads . ReopenLumpReader ( lump_name ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ 0 ] . Name , Wads . GetLumpFullName ( lump_name ) , 8 ) ;
map - > Encrypted = Wads . IsEncryptedFile ( lump_name ) ;
map - > InWad = true ;
if ( map - > Encrypted )
{ // If it's encrypted, then it's a Blood file, presumably a map.
if ( ! P_IsBuildMap ( map ) )
{
delete map ;
return NULL ;
}
return map ;
}
int index = 0 ;
if ( stricmp ( Wads . GetLumpFullName ( lump_name + 1 ) , " TEXTMAP " ) ! = 0 )
{
for ( int i = 1 ; ; i + + )
{
// Since levels must be stored in WADs they can't really have full
// names and for any valid level lump this always returns the short name.
const char * lumpname = Wads . GetLumpFullName ( lump_name + i ) ;
try
{
index = GetMapIndex ( mapname , index , lumpname , ! justcheck ) ;
}
catch ( . . . )
{
delete map ;
throw ;
}
if ( index = = - 2 )
{
delete map ;
return NULL ;
}
if ( index = = ML_BEHAVIOR ) map - > HasBehavior = true ;
// The next lump is not part of this map anymore
if ( index < 0 ) break ;
2018-03-10 20:48:33 +00:00
map - > MapLumps [ index ] . Reader = Wads . ReopenLumpReader ( lump_name + i ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ index ] . Name , lumpname , 8 ) ;
}
}
else
{
map - > isText = true ;
2018-03-10 20:48:33 +00:00
map - > MapLumps [ 1 ] . Reader = Wads . ReopenLumpReader ( lump_name + 1 ) ;
2016-03-01 15:47:10 +00:00
for ( int i = 2 ; ; i + + )
{
const char * lumpname = Wads . GetLumpFullName ( lump_name + i ) ;
if ( lumpname = = NULL )
{
I_Error ( " Invalid map definition for %s " , mapname ) ;
}
else if ( ! stricmp ( lumpname , " ZNODES " ) )
{
index = ML_GLZNODES ;
}
else if ( ! stricmp ( lumpname , " BLOCKMAP " ) )
{
// there is no real point in creating a blockmap but let's use it anyway
index = ML_BLOCKMAP ;
}
else if ( ! stricmp ( lumpname , " REJECT " ) )
{
index = ML_REJECT ;
}
else if ( ! stricmp ( lumpname , " DIALOGUE " ) )
{
index = ML_CONVERSATION ;
}
else if ( ! stricmp ( lumpname , " BEHAVIOR " ) )
{
index = ML_BEHAVIOR ;
map - > HasBehavior = true ;
}
else if ( ! stricmp ( lumpname , " ENDMAP " ) )
{
break ;
}
else continue ;
2018-03-10 20:48:33 +00:00
map - > MapLumps [ index ] . Reader = Wads . ReopenLumpReader ( lump_name + i ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ index ] . Name , lumpname , 8 ) ;
}
}
return map ;
}
else
{
if ( lump_map > lump_wad )
{
lump_wad = lump_map ;
}
if ( lump_wad = = - 1 )
{
delete map ;
return NULL ;
}
map - > lumpnum = lump_wad ;
2018-03-11 21:44:05 +00:00
auto reader = Wads . ReopenLumpReader ( lump_wad ) ;
map - > resource = FResourceFile : : OpenResourceFile ( Wads . GetLumpFullName ( lump_wad ) , reader , true ) ;
2016-03-01 15:47:10 +00:00
wadReader = map - > resource - > GetReader ( ) ;
}
}
2017-03-08 14:20:00 +00:00
uint32_t id ;
2016-03-01 15:47:10 +00:00
// Although we're using the resource system, we still want to be sure we're
// reading from a wad file.
2018-03-11 17:32:00 +00:00
wadReader - > Seek ( 0 , FileReader : : SeekSet ) ;
2016-03-01 15:47:10 +00:00
wadReader - > Read ( & id , sizeof ( id ) ) ;
if ( id = = IWAD_ID | | id = = PWAD_ID )
{
char maplabel [ 9 ] = " " ;
int index = 0 ;
2018-03-11 11:33:46 +00:00
map - > MapLumps [ 0 ] . Reader = map - > resource - > GetLump ( 0 ) - > NewReader ( ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ 0 ] . Name , map - > resource - > GetLump ( 0 ) - > Name , 8 ) ;
2017-03-08 14:20:00 +00:00
for ( uint32_t i = 1 ; i < map - > resource - > LumpCount ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
const char * lumpname = map - > resource - > GetLump ( i ) - > Name ;
if ( i = = 1 & & ! strnicmp ( lumpname , " TEXTMAP " , 8 ) )
{
map - > isText = true ;
2018-03-11 11:33:46 +00:00
map - > MapLumps [ ML_TEXTMAP ] . Reader = map - > resource - > GetLump ( i ) - > NewReader ( ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ ML_TEXTMAP ] . Name , lumpname , 8 ) ;
for ( int i = 2 ; ; i + + )
{
lumpname = map - > resource - > GetLump ( i ) - > Name ;
if ( ! strnicmp ( lumpname , " ZNODES " , 8 ) )
{
index = ML_GLZNODES ;
}
else if ( ! strnicmp ( lumpname , " BLOCKMAP " , 8 ) )
{
// there is no real point in creating a blockmap but let's use it anyway
index = ML_BLOCKMAP ;
}
else if ( ! strnicmp ( lumpname , " REJECT " , 8 ) )
{
index = ML_REJECT ;
}
else if ( ! strnicmp ( lumpname , " DIALOGUE " , 8 ) )
{
index = ML_CONVERSATION ;
}
else if ( ! strnicmp ( lumpname , " BEHAVIOR " , 8 ) )
{
index = ML_BEHAVIOR ;
map - > HasBehavior = true ;
}
else if ( ! strnicmp ( lumpname , " ENDMAP " , 8 ) )
{
return map ;
}
else continue ;
2018-03-11 11:33:46 +00:00
map - > MapLumps [ index ] . Reader = map - > resource - > GetLump ( i ) - > NewReader ( ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ index ] . Name , lumpname , 8 ) ;
}
}
if ( i > 0 )
{
try
{
index = GetMapIndex ( maplabel , index , lumpname , ! justcheck ) ;
}
catch ( . . . )
{
delete map ;
throw ;
}
if ( index = = - 2 )
{
delete map ;
return NULL ;
}
if ( index = = ML_BEHAVIOR ) map - > HasBehavior = true ;
// The next lump is not part of this map anymore
if ( index < 0 ) break ;
}
else
{
strncpy ( maplabel , lumpname , 8 ) ;
maplabel [ 8 ] = 0 ;
}
2018-03-11 11:33:46 +00:00
map - > MapLumps [ index ] . Reader = map - > resource - > GetLump ( i ) - > NewReader ( ) ;
2016-03-01 15:47:10 +00:00
strncpy ( map - > MapLumps [ index ] . Name , lumpname , 8 ) ;
}
}
else
{
// This is a Build map and not subject to WAD consistency checks.
//map->MapLumps[0].Size = wadReader->GetLength();
if ( ! P_IsBuildMap ( map ) )
{
delete map ;
return NULL ;
}
}
return map ;
}
bool P_CheckMapData ( const char * mapname )
{
MapData * mapd = P_OpenMapData ( mapname , true ) ;
if ( mapd = = NULL ) return false ;
delete mapd ;
return true ;
}
//===========================================================================
//
// MapData :: GetChecksum
//
// Hashes a map based on its header, THINGS, LINEDEFS, SIDEDEFS, SECTORS,
// and BEHAVIOR lumps. Node-builder generated lumps are not included.
//
//===========================================================================
2017-03-08 14:20:00 +00:00
void MapData : : GetChecksum ( uint8_t cksum [ 16 ] )
2016-03-01 15:47:10 +00:00
{
MD5Context md5 ;
if ( isText )
{
2018-03-10 20:48:33 +00:00
md5 . Update ( Reader ( ML_TEXTMAP ) , Size ( ML_TEXTMAP ) ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2018-03-10 20:48:33 +00:00
md5 . Update ( Reader ( ML_LABEL ) , Size ( ML_LABEL ) ) ;
md5 . Update ( Reader ( ML_THINGS ) , Size ( ML_THINGS ) ) ;
md5 . Update ( Reader ( ML_LINEDEFS ) , Size ( ML_LINEDEFS ) ) ;
md5 . Update ( Reader ( ML_SIDEDEFS ) , Size ( ML_SIDEDEFS ) ) ;
md5 . Update ( Reader ( ML_SECTORS ) , Size ( ML_SECTORS ) ) ;
2016-03-01 15:47:10 +00:00
}
if ( HasBehavior )
{
2018-03-10 20:48:33 +00:00
md5 . Update ( Reader ( ML_BEHAVIOR ) , Size ( ML_BEHAVIOR ) ) ;
2016-03-01 15:47:10 +00:00
}
md5 . Final ( cksum ) ;
}
2018-01-07 00:59:49 +00:00
DEFINE_ACTION_FUNCTION ( FLevelLocals , GetChecksum )
{
PARAM_SELF_STRUCT_PROLOGUE ( FLevelLocals ) ;
char md5string [ 33 ] ;
for ( int j = 0 ; j < 16 ; + + j )
{
sprintf ( md5string + j * 2 , " %02x " , level . md5 [ j ] ) ;
}
ACTION_RETURN_STRING ( ( const char * ) md5string ) ;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// Sets a sidedef's texture and prints a message if it's not present.
//
//===========================================================================
static void SetTexture ( side_t * side , int position , const char * name , FMissingTextureTracker & track )
{
static const char * positionnames [ ] = { " top " , " middle " , " bottom " } ;
static const char * sidenames [ ] = { " first " , " second " } ;
2018-03-25 18:26:16 +00:00
FTextureID texture = TexMan . CheckForTexture ( name , ETextureType : : Wall ,
2016-03-01 15:47:10 +00:00
FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny ) ;
if ( ! texture . Exists ( ) )
{
if ( + + track [ name ] . Count < = MISSING_TEXTURE_WARN_LIMIT )
{
// Print an error that lists all references to this sidedef.
// We must scan the linedefs manually for all references to this sidedef.
2017-01-08 13:39:16 +00:00
for ( unsigned i = 0 ; i < level . lines . Size ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
for ( int j = 0 ; j < 2 ; j + + )
{
2017-01-08 13:39:16 +00:00
if ( level . lines [ i ] . sidedef [ j ] = = side )
2016-03-01 15:47:10 +00:00
{
Printf ( TEXTCOLOR_RED " Unknown %s texture ' "
TEXTCOLOR_ORANGE " %s " TEXTCOLOR_RED
" ' on %s side of linedef %d \n " ,
positionnames [ position ] , name , sidenames [ j ] , i ) ;
}
}
}
}
texture = TexMan . GetDefaultTexture ( ) ;
}
side - > SetTexture ( position , texture ) ;
}
//===========================================================================
//
// Sets a sidedef's texture and prints a message if it's not present.
// (Passing index separately is for UDMF which does not have sectors allocated yet)
//
//===========================================================================
void SetTexture ( sector_t * sector , int index , int position , const char * name , FMissingTextureTracker & track , bool truncate )
{
static const char * positionnames [ ] = { " floor " , " ceiling " } ;
char name8 [ 9 ] ;
if ( truncate )
{
strncpy ( name8 , name , 8 ) ;
name8 [ 8 ] = 0 ;
name = name8 ;
}
2018-03-25 18:26:16 +00:00
FTextureID texture = TexMan . CheckForTexture ( name , ETextureType : : Flat ,
2016-03-01 15:47:10 +00:00
FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny ) ;
if ( ! texture . Exists ( ) )
{
if ( + + track [ name ] . Count < = MISSING_TEXTURE_WARN_LIMIT )
{
Printf ( TEXTCOLOR_RED " Unknown %s texture ' "
TEXTCOLOR_ORANGE " %s " TEXTCOLOR_RED
" ' in sector %d \n " ,
positionnames [ position ] , name , index ) ;
}
texture = TexMan . GetDefaultTexture ( ) ;
}
sector - > SetTexture ( position , texture ) ;
}
//===========================================================================
//
// SummarizeMissingTextures
//
// Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
// times.
//
//===========================================================================
static void SummarizeMissingTextures ( const FMissingTextureTracker & missing )
{
FMissingTextureTracker : : ConstIterator it ( missing ) ;
FMissingTextureTracker : : ConstPair * pair ;
while ( it . NextPair ( pair ) )
{
if ( pair - > Value . Count > MISSING_TEXTURE_WARN_LIMIT )
{
Printf ( TEXTCOLOR_RED " Missing texture ' "
TEXTCOLOR_ORANGE " %s " TEXTCOLOR_RED
" ' is used %d more times \n " ,
pair - > Key . GetChars ( ) , pair - > Value . Count - MISSING_TEXTURE_WARN_LIMIT ) ;
}
}
}
//===========================================================================
//
// [RH] Figure out blends for deep water sectors
//
//===========================================================================
2017-03-08 14:20:00 +00:00
static void SetTexture ( side_t * side , int position , uint32_t * blend , const char * name )
2016-03-01 15:47:10 +00:00
{
FTextureID texture ;
if ( ( * blend = R_ColormapNumForName ( name ) ) = = 0 )
{
2018-03-25 18:26:16 +00:00
texture = TexMan . CheckForTexture ( name , ETextureType : : Wall ,
2016-03-01 15:47:10 +00:00
FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny ) ;
if ( ! texture . Exists ( ) )
{
char name2 [ 9 ] ;
char * stop ;
strncpy ( name2 , name , 8 ) ;
name2 [ 8 ] = 0 ;
* blend = strtoul ( name2 , & stop , 16 ) ;
texture = FNullTextureID ( ) ;
}
else
{
* blend = 0 ;
}
}
else
{
texture = FNullTextureID ( ) ;
}
side - > SetTexture ( position , texture ) ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2017-03-08 14:20:00 +00:00
static void SetTextureNoErr ( side_t * side , int position , uint32_t * color , const char * name , bool * validcolor , bool isFog )
2016-03-01 15:47:10 +00:00
{
FTextureID texture ;
* validcolor = false ;
2018-03-25 18:26:16 +00:00
texture = TexMan . CheckForTexture ( name , ETextureType : : Wall ,
2016-03-01 15:47:10 +00:00
FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny ) ;
if ( ! texture . Exists ( ) )
{
char name2 [ 9 ] ;
char * stop ;
strncpy ( name2 , name + 1 , 7 ) ;
name2 [ 7 ] = 0 ;
if ( * name ! = ' # ' )
{
* color = strtoul ( name , & stop , 16 ) ;
texture = FNullTextureID ( ) ;
* validcolor = ( * stop = = 0 ) & & ( stop > = name + 2 ) & & ( stop < = name + 6 ) ;
return ;
}
else // Support for Legacy's color format!
{
int l = ( int ) strlen ( name ) ;
texture = FNullTextureID ( ) ;
* validcolor = false ;
if ( l > = 7 )
{
for ( stop = name2 ; stop < name2 + 6 ; stop + + ) if ( ! isxdigit ( * stop ) ) * stop = ' 0 ' ;
int factor = l = = 7 ? 0 : clamp < int > ( ( name2 [ 6 ] & 223 ) - ' A ' , 0 , 25 ) ;
name2 [ 6 ] = 0 ; int blue = strtol ( name2 + 4 , NULL , 16 ) ;
name2 [ 4 ] = 0 ; int green = strtol ( name2 + 2 , NULL , 16 ) ;
name2 [ 2 ] = 0 ; int red = strtol ( name2 , NULL , 16 ) ;
if ( ! isFog )
{
if ( factor = = 0 )
{
* validcolor = false ;
return ;
}
factor = factor * 255 / 25 ;
}
else
{
factor = 0 ;
}
* color = MAKEARGB ( factor , red , green , blue ) ;
texture = FNullTextureID ( ) ;
* validcolor = true ;
return ;
}
}
texture = FNullTextureID ( ) ;
}
side - > SetTexture ( position , texture ) ;
}
//===========================================================================
//
// Sound enviroment handling
//
//===========================================================================
void P_FloodZone ( sector_t * sec , int zonenum )
{
if ( sec - > ZoneNumber = = zonenum )
return ;
sec - > ZoneNumber = zonenum ;
2017-01-02 20:40:52 +00:00
for ( auto check : sec - > Lines )
2016-03-01 15:47:10 +00:00
{
sector_t * other ;
if ( check - > sidedef [ 1 ] = = NULL | | ( check - > flags & ML_ZONEBOUNDARY ) )
continue ;
if ( check - > frontsector = = sec )
{
assert ( check - > backsector ! = NULL ) ;
other = check - > backsector ;
}
else
{
assert ( check - > frontsector ! = NULL ) ;
other = check - > frontsector ;
}
if ( other - > ZoneNumber ! = zonenum )
P_FloodZone ( other , zonenum ) ;
}
}
void P_FloodZones ( )
{
int z = 0 , i ;
ReverbContainer * reverb ;
2017-01-07 18:32:24 +00:00
for ( auto & sec : level . sectors )
2016-03-01 15:47:10 +00:00
{
2017-01-07 18:32:24 +00:00
if ( sec . ZoneNumber = = 0xFFFF )
2016-03-01 15:47:10 +00:00
{
2017-01-07 18:32:24 +00:00
P_FloodZone ( & sec , z + + ) ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 11:09:38 +00:00
level . Zones . Resize ( z ) ;
2016-03-01 15:47:10 +00:00
reverb = S_FindEnvironment ( level . DefaultEnvironment ) ;
if ( reverb = = NULL )
{
Printf ( " Sound environment %d, %d not found \n " , level . DefaultEnvironment > > 8 , level . DefaultEnvironment & 255 ) ;
reverb = DefaultEnvironments [ 0 ] ;
}
for ( i = 0 ; i < z ; + + i )
{
2017-03-17 11:09:38 +00:00
level . Zones [ i ] . Environment = reverb ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_LoadVertexes
//
//===========================================================================
void P_LoadVertexes ( MapData * map )
{
// Determine number of vertices:
// total lump length / vertex record length.
2017-01-08 23:46:16 +00:00
unsigned numvertexes = map - > Size ( ML_VERTEXES ) / sizeof ( mapvertex_t ) ;
2016-03-01 15:47:10 +00:00
if ( numvertexes = = 0 )
{
I_Error ( " Map has no vertices. \n " ) ;
}
// Allocate memory for buffer.
2017-01-08 23:46:16 +00:00
level . vertexes . Alloc ( numvertexes ) ;
2017-01-09 00:40:14 +00:00
vertexdatas . Clear ( ) ;
2016-03-01 15:47:10 +00:00
2018-03-10 20:48:33 +00:00
auto & fr = map - > Reader ( ML_VERTEXES ) ;
2016-03-01 15:47:10 +00:00
// Copy and convert vertex coordinates, internal representation as fixed.
2017-01-08 23:46:16 +00:00
for ( auto & v : level . vertexes )
2016-03-01 15:47:10 +00:00
{
2018-03-11 17:20:49 +00:00
int16_t x = fr . ReadInt16 ( ) ;
int16_t y = fr . ReadInt16 ( ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 23:46:16 +00:00
v . set ( double ( x ) , double ( y ) ) ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_LoadZSegs
//
//===========================================================================
2018-03-11 17:32:00 +00:00
void P_LoadZSegs ( FileReader & data )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
for ( auto & seg : level . segs )
2016-03-01 15:47:10 +00:00
{
line_t * ldef ;
2018-03-11 17:20:49 +00:00
uint32_t v1 = data . ReadUInt32 ( ) ;
uint32_t v2 = data . ReadUInt32 ( ) ;
uint16_t line = data . ReadUInt16 ( ) ;
uint8_t side = data . ReadUInt8 ( ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
seg . v1 = & level . vertexes [ v1 ] ;
seg . v2 = & level . vertexes [ v2 ] ;
seg . linedef = ldef = & level . lines [ line ] ;
seg . sidedef = ldef - > sidedef [ side ] ;
seg . frontsector = ldef - > sidedef [ side ] - > sector ;
2016-03-01 15:47:10 +00:00
if ( ldef - > flags & ML_TWOSIDED & & ldef - > sidedef [ side ^ 1 ] ! = NULL )
{
2017-03-16 23:22:52 +00:00
seg . backsector = ldef - > sidedef [ side ^ 1 ] - > sector ;
2016-03-01 15:47:10 +00:00
}
else
{
2017-03-16 23:22:52 +00:00
seg . backsector = 0 ;
2016-03-01 15:47:10 +00:00
ldef - > flags & = ~ ML_TWOSIDED ;
}
}
}
//===========================================================================
//
// P_LoadGLZSegs
//
// This is the GL nodes version of the above function.
//
//===========================================================================
2018-03-11 17:32:00 +00:00
void P_LoadGLZSegs ( FileReader & data , int type )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
for ( unsigned i = 0 ; i < level . subsectors . Size ( ) ; + + i )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
for ( size_t j = 0 ; j < level . subsectors [ i ] . numlines ; + + j )
2016-03-01 15:47:10 +00:00
{
seg_t * seg ;
2018-03-11 17:20:49 +00:00
uint32_t v1 = data . ReadUInt32 ( ) ;
uint32_t partner = data . ReadUInt32 ( ) ;
2017-03-08 14:20:00 +00:00
uint32_t line ;
2016-03-01 15:47:10 +00:00
if ( type > = 2 )
{
2018-03-11 17:20:49 +00:00
line = data . ReadUInt32 ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2018-03-11 17:20:49 +00:00
line = data . ReadUInt16 ( ) ;
if ( line = = 0xffff ) line = 0xffffffff ;
2016-03-01 15:47:10 +00:00
}
2018-03-11 17:20:49 +00:00
uint8_t side = data . ReadUInt8 ( ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
seg = level . subsectors [ i ] . firstline + j ;
2017-01-08 23:46:16 +00:00
seg - > v1 = & level . vertexes [ v1 ] ;
2016-03-01 15:47:10 +00:00
if ( j = = 0 )
{
2017-03-16 23:22:52 +00:00
seg [ level . subsectors [ i ] . numlines - 1 ] . v2 = seg - > v1 ;
2016-03-01 15:47:10 +00:00
}
else
{
seg [ - 1 ] . v2 = seg - > v1 ;
}
2017-01-09 19:53:37 +00:00
2017-03-16 23:22:52 +00:00
seg - > PartnerSeg = partner = = 0xffffffffu ? nullptr : & level . segs [ partner ] ;
2016-03-01 15:47:10 +00:00
if ( line ! = 0xFFFFFFFF )
{
line_t * ldef ;
2017-01-08 13:39:16 +00:00
seg - > linedef = ldef = & level . lines [ line ] ;
2016-03-01 15:47:10 +00:00
seg - > sidedef = ldef - > sidedef [ side ] ;
seg - > frontsector = ldef - > sidedef [ side ] - > sector ;
if ( ldef - > flags & ML_TWOSIDED & & ldef - > sidedef [ side ^ 1 ] ! = NULL )
{
seg - > backsector = ldef - > sidedef [ side ^ 1 ] - > sector ;
}
else
{
seg - > backsector = 0 ;
ldef - > flags & = ~ ML_TWOSIDED ;
}
}
else
{
seg - > linedef = NULL ;
seg - > sidedef = NULL ;
2017-03-16 23:22:52 +00:00
seg - > frontsector = seg - > backsector = level . subsectors [ i ] . firstline - > frontsector ;
2016-03-01 15:47:10 +00:00
}
}
}
}
//===========================================================================
//
// P_LoadZNodes
//
//===========================================================================
2018-03-11 17:32:00 +00:00
void LoadZNodes ( FileReader & data , int glnodes )
2016-03-01 15:47:10 +00:00
{
// Read extra vertices added during node building
unsigned int i ;
2018-03-11 17:20:49 +00:00
uint32_t orgVerts = data . ReadUInt32 ( ) ;
uint32_t newVerts = data . ReadUInt32 ( ) ;
2017-01-08 23:46:16 +00:00
if ( orgVerts > level . vertexes . Size ( ) )
2016-03-01 15:47:10 +00:00
{ // These nodes are based on a map with more vertex data than we have.
// We can't use them.
throw CRecoverableError ( " Incorrect number of vertexes in nodes. \n " ) ;
}
2017-03-17 11:09:38 +00:00
auto oldvertexes = & level . vertexes [ 0 ] ;
if ( orgVerts + newVerts ! = level . vertexes . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2017-03-17 11:09:38 +00:00
level . vertexes . Reserve ( newVerts ) ;
2016-03-01 15:47:10 +00:00
}
for ( i = 0 ; i < newVerts ; + + i )
{
2018-03-11 17:20:49 +00:00
fixed_t x = data . ReadInt32 ( ) ;
fixed_t y = data . ReadInt32 ( ) ;
2017-03-17 11:09:38 +00:00
level . vertexes [ i + orgVerts ] . set ( x , y ) ;
2016-03-01 15:47:10 +00:00
}
2017-03-17 11:09:38 +00:00
if ( oldvertexes ! = & level . vertexes [ 0 ] )
2016-03-01 15:47:10 +00:00
{
2017-01-08 13:39:16 +00:00
for ( auto & line : level . lines )
2016-03-01 15:47:10 +00:00
{
2017-03-17 11:09:38 +00:00
line . v1 = line . v1 - oldvertexes + & level . vertexes [ 0 ] ;
line . v2 = line . v2 - oldvertexes + & level . vertexes [ 0 ] ;
2016-03-01 15:47:10 +00:00
}
}
// Read the subsectors
2018-03-11 17:20:49 +00:00
uint32_t currSeg ;
uint32_t numSubs = data . ReadUInt32 ( ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Alloc ( numSubs ) ;
memset ( & level . subsectors [ 0 ] , 0 , level . subsectors . Size ( ) * sizeof ( subsector_t ) ) ;
2016-03-01 15:47:10 +00:00
for ( i = currSeg = 0 ; i < numSubs ; + + i )
{
2018-03-11 17:20:49 +00:00
uint32_t numsegs = data . ReadUInt32 ( ) ;
2017-03-16 23:22:52 +00:00
level . subsectors [ i ] . firstline = ( seg_t * ) ( size_t ) currSeg ; // Oh damn. I should have stored the seg count sooner.
level . subsectors [ i ] . numlines = numsegs ;
2016-03-01 15:47:10 +00:00
currSeg + = numsegs ;
}
// Read the segs
2018-03-11 17:20:49 +00:00
uint32_t numSegs = data . ReadUInt32 ( ) ;
2016-03-01 15:47:10 +00:00
// The number of segs stored should match the number of
// segs used by subsectors.
if ( numSegs ! = currSeg )
{
throw CRecoverableError ( " Incorrect number of segs in nodes. \n " ) ;
}
2017-03-16 23:22:52 +00:00
level . segs . Alloc ( numSegs ) ;
memset ( & level . segs [ 0 ] , 0 , numSegs * sizeof ( seg_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
for ( auto & sub : level . subsectors )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
sub . firstline = & level . segs [ ( size_t ) sub . firstline ] ;
2016-03-01 15:47:10 +00:00
}
if ( glnodes = = 0 )
{
P_LoadZSegs ( data ) ;
}
else
{
P_LoadGLZSegs ( data , glnodes ) ;
}
// Read nodes
2018-03-11 17:20:49 +00:00
uint32_t numNodes = data . ReadUInt32 ( ) ;
2016-03-01 15:47:10 +00:00
2017-03-17 00:42:37 +00:00
auto & nodes = level . nodes ;
nodes . Alloc ( numNodes ) ;
memset ( & nodes [ 0 ] , 0 , sizeof ( node_t ) * numNodes ) ;
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < numNodes ; + + i )
{
if ( glnodes < 3 )
{
2018-03-11 17:20:49 +00:00
nodes [ i ] . x = data . ReadInt16 ( ) * FRACUNIT ;
nodes [ i ] . y = data . ReadInt16 ( ) * FRACUNIT ;
nodes [ i ] . dx = data . ReadInt16 ( ) * FRACUNIT ;
nodes [ i ] . dy = data . ReadInt16 ( ) * FRACUNIT ;
2016-03-01 15:47:10 +00:00
}
else
{
2018-03-11 17:20:49 +00:00
nodes [ i ] . x = data . ReadInt32 ( ) ;
nodes [ i ] . y = data . ReadInt32 ( ) ;
nodes [ i ] . dx = data . ReadInt32 ( ) ;
nodes [ i ] . dy = data . ReadInt32 ( ) ;
2016-03-01 15:47:10 +00:00
}
for ( int j = 0 ; j < 2 ; + + j )
{
for ( int k = 0 ; k < 4 ; + + k )
{
2018-03-11 17:20:49 +00:00
nodes [ i ] . bbox [ j ] [ k ] = data . ReadInt16 ( ) ;
2016-03-01 15:47:10 +00:00
}
}
for ( int m = 0 ; m < 2 ; + + m )
{
2018-03-11 17:20:49 +00:00
uint32_t child = data . ReadUInt32 ( ) ;
2016-03-01 15:47:10 +00:00
if ( child & 0x80000000 )
{
2017-03-16 23:22:52 +00:00
nodes [ i ] . children [ m ] = ( uint8_t * ) & level . subsectors [ child & 0x7FFFFFFF ] + 1 ;
2016-03-01 15:47:10 +00:00
}
else
{
nodes [ i ] . children [ m ] = & nodes [ child ] ;
}
}
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
2018-03-11 17:32:00 +00:00
void P_LoadZNodes ( FileReader & dalump , uint32_t id )
2016-03-01 15:47:10 +00:00
{
int type ;
bool compressed ;
switch ( id )
{
case MAKE_ID ( ' Z ' , ' N ' , ' O ' , ' D ' ) :
type = 0 ;
compressed = true ;
break ;
case MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' N ' ) :
type = 1 ;
compressed = true ;
break ;
case MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' 2 ' ) :
type = 2 ;
compressed = true ;
break ;
case MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' 3 ' ) :
type = 3 ;
compressed = true ;
break ;
case MAKE_ID ( ' X ' , ' N ' , ' O ' , ' D ' ) :
type = 0 ;
compressed = false ;
break ;
case MAKE_ID ( ' X ' , ' G ' , ' L ' , ' N ' ) :
type = 1 ;
compressed = false ;
break ;
case MAKE_ID ( ' X ' , ' G ' , ' L ' , ' 2 ' ) :
type = 2 ;
compressed = false ;
break ;
case MAKE_ID ( ' X ' , ' G ' , ' L ' , ' 3 ' ) :
type = 3 ;
compressed = false ;
break ;
default :
return ;
}
if ( compressed )
{
2018-03-11 17:32:00 +00:00
FileReader zip ;
2018-03-11 11:33:46 +00:00
if ( zip . OpenDecompressor ( dalump , - 1 , METHOD_ZLIB , false ) )
{
LoadZNodes ( zip , type ) ;
}
2016-03-01 15:47:10 +00:00
}
else
{
2018-03-11 11:33:46 +00:00
LoadZNodes ( dalump , type ) ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_CheckV4Nodes
// http://www.sbsoftware.com/files/DeePBSPV4specs.txt
//
//===========================================================================
static bool P_CheckV4Nodes ( MapData * map )
{
char header [ 8 ] ;
map - > Read ( ML_NODES , header , 8 ) ;
return ! memcmp ( header , " xNd4 \0 \0 \0 \0 " , 8 ) ;
}
//===========================================================================
//
// P_LoadSegs
//
// killough 5/3/98: reformatted, cleaned up
//
//===========================================================================
struct badseg
{
badseg ( int t , int s , int d ) : badtype ( t ) , badsegnum ( s ) , baddata ( d ) { }
int badtype ;
int badsegnum ;
int baddata ;
} ;
template < class segtype >
void P_LoadSegs ( MapData * map )
{
2017-03-08 14:20:00 +00:00
uint8_t * data ;
2017-01-08 23:46:16 +00:00
int numvertexes = level . vertexes . Size ( ) ;
2017-03-08 14:20:00 +00:00
uint8_t * vertchanged = new uint8_t [ numvertexes ] ; // phares 10/4/98
uint32_t segangle ;
2016-04-01 15:57:16 +00:00
//int ptp_angle; // phares 10/4/98
//int delta_angle; // phares 10/4/98
2016-03-01 15:47:10 +00:00
int vnum1 , vnum2 ; // phares 10/4/98
int lumplen = map - > Size ( ML_SEGS ) ;
memset ( vertchanged , 0 , numvertexes ) ; // phares 10/4/98
2017-03-16 23:22:52 +00:00
unsigned numsegs = lumplen / sizeof ( segtype ) ;
2016-03-01 15:47:10 +00:00
if ( numsegs = = 0 )
{
Printf ( " This map has no segs. \n " ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
delete [ ] vertchanged ;
ForceNodeBuild = true ;
return ;
}
2017-03-16 23:22:52 +00:00
level . segs . Alloc ( numsegs ) ;
auto & segs = level . segs ;
memset ( & segs [ 0 ] , 0 , numsegs * sizeof ( seg_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-03-08 14:20:00 +00:00
data = new uint8_t [ lumplen ] ;
2016-03-01 15:47:10 +00:00
map - > Read ( ML_SEGS , data ) ;
2017-03-16 23:22:52 +00:00
for ( auto & sub : level . subsectors )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
sub . firstline = & segs [ ( size_t ) sub . firstline ] ;
2016-03-01 15:47:10 +00:00
}
// phares: 10/4/98: Vertchanged is an array that represents the vertices.
// Mark those used by linedefs. A marked vertex is one that is not a
// candidate for movement further down.
2017-01-08 13:39:16 +00:00
for ( auto & line : level . lines )
2016-03-01 15:47:10 +00:00
{
2017-01-08 23:46:16 +00:00
vertchanged [ line . v1 - > Index ( ) ] = vertchanged [ line . v2 - > Index ( ) ] = 1 ;
2016-03-01 15:47:10 +00:00
}
try
{
2017-03-16 23:22:52 +00:00
for ( unsigned i = 0 ; i < numsegs ; i + + )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
seg_t * li = & segs [ i ] ;
2016-03-01 15:47:10 +00:00
segtype * ml = ( ( segtype * ) data ) + i ;
int side , linedef ;
line_t * ldef ;
vnum1 = ml - > V1 ( ) ;
vnum2 = ml - > V2 ( ) ;
if ( vnum1 > = numvertexes | | vnum2 > = numvertexes )
{
throw badseg ( 0 , i , MAX ( vnum1 , vnum2 ) ) ;
}
2017-01-08 23:46:16 +00:00
li - > v1 = & level . vertexes [ vnum1 ] ;
li - > v2 = & level . vertexes [ vnum2 ] ;
2016-03-01 15:47:10 +00:00
2017-03-08 14:20:00 +00:00
segangle = ( uint16_t ) LittleShort ( ml - > angle ) ;
2016-03-01 15:47:10 +00:00
// phares 10/4/98: In the case of a lineseg that was created by splitting
// another line, it appears that the line angle is inherited from the
// father line. Due to roundoff, the new vertex may have been placed 'off
// the line'. When you get close to such a line, and it is very short,
// it's possible that the roundoff error causes 'firelines', the thin
// lines that can draw from screen top to screen bottom occasionally. This
// is due to all the angle calculations that are done based on the line
// angle, the angles from the viewer to the vertices, and the viewer's
// angle in the world. In the case of firelines, the rounded-off position
// of one of the vertices determines one of these angles, and introduces
// an error in the scaling factor for mapping textures and determining
// where on the screen the ceiling and floor spans should be shown. For a
// fireline, the engine thinks the ceiling bottom and floor top are at the
// midpoint of the screen. So you get ceilings drawn all the way down to the
// screen midpoint, and floors drawn all the way up. Thus 'firelines'. The
// name comes from the original sighting, which involved a fire texture.
//
// To correct this, reset the vertex that was added so that it sits ON the
// split line.
//
// To know which of the two vertices was added, its number is greater than
// that of the last of the author-created vertices. If both vertices of the
// line were added by splitting, pick the higher-numbered one. Once you've
// changed a vertex, don't change it again if it shows up in another seg.
//
// To determine if there's an error in the first place, find the
// angle of the line between the two seg vertices. If it's one degree or more
// off, then move one vertex. This may seem insignificant, but one degree
// errors _can_ cause firelines.
2016-04-01 15:57:16 +00:00
DAngle ptp_angle = ( li - > v2 - > fPos ( ) - li - > v1 - > fPos ( ) ) . Angle ( ) ;
2016-04-06 13:44:46 +00:00
DAngle seg_angle = AngleToFloat ( segangle < < 16 ) ;
2016-04-01 15:57:16 +00:00
DAngle delta_angle = absangle ( ptp_angle , seg_angle ) ;
2016-03-01 15:47:10 +00:00
2016-04-01 15:57:16 +00:00
if ( delta_angle > = 1. )
2016-03-01 15:47:10 +00:00
{
2016-04-01 15:57:16 +00:00
double dis = ( li - > v2 - > fPos ( ) - li - > v1 - > fPos ( ) ) . Length ( ) ;
2016-04-07 01:09:12 +00:00
DVector2 delta = seg_angle . ToVector ( dis ) ;
2016-03-01 15:47:10 +00:00
if ( ( vnum2 > vnum1 ) & & ( vertchanged [ vnum2 ] = = 0 ) )
{
2016-04-07 01:09:12 +00:00
li - > v2 - > set ( li - > v1 - > fPos ( ) + delta ) ;
2016-03-01 15:47:10 +00:00
vertchanged [ vnum2 ] = 1 ; // this was changed
}
else if ( vertchanged [ vnum1 ] = = 0 )
{
2016-04-07 01:09:12 +00:00
li - > v1 - > set ( li - > v2 - > fPos ( ) - delta ) ;
2016-03-01 15:47:10 +00:00
vertchanged [ vnum1 ] = 1 ; // this was changed
}
}
linedef = LittleShort ( ml - > linedef ) ;
2017-01-08 13:39:16 +00:00
if ( ( unsigned ) linedef > = level . lines . Size ( ) )
2016-03-01 15:47:10 +00:00
{
throw badseg ( 1 , i , linedef ) ;
}
2017-01-08 13:39:16 +00:00
ldef = & level . lines [ linedef ] ;
2016-03-01 15:47:10 +00:00
li - > linedef = ldef ;
side = LittleShort ( ml - > side ) ;
2017-01-08 17:45:30 +00:00
if ( ( unsigned ) ( ldef - > sidedef [ side ] - > Index ( ) ) > = level . sides . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2017-01-08 17:45:30 +00:00
throw badseg ( 2 , i , ldef - > sidedef [ side ] - > Index ( ) ) ;
2016-03-01 15:47:10 +00:00
}
li - > sidedef = ldef - > sidedef [ side ] ;
li - > frontsector = ldef - > sidedef [ side ] - > sector ;
// killough 5/3/98: ignore 2s flag if second sidedef missing:
if ( ldef - > flags & ML_TWOSIDED & & ldef - > sidedef [ side ^ 1 ] ! = NULL )
{
li - > backsector = ldef - > sidedef [ side ^ 1 ] - > sector ;
}
else
{
li - > backsector = 0 ;
ldef - > flags & = ~ ML_TWOSIDED ;
}
}
}
catch ( const badseg & bad ) // the preferred way is to catch by (const) reference.
{
switch ( bad . badtype )
{
case 0 :
Printf ( " Seg %d references a nonexistant vertex %d (max %d). \n " , bad . badsegnum , bad . baddata , numvertexes ) ;
break ;
case 1 :
2017-01-08 13:39:16 +00:00
Printf ( " Seg %d references a nonexistant linedef %d (max %u). \n " , bad . badsegnum , bad . baddata , level . lines . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
break ;
case 2 :
2017-01-08 17:45:30 +00:00
Printf ( " The linedef for seg %d references a nonexistant sidedef %d (max %d). \n " , bad . badsegnum , bad . baddata , level . sides . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
break ;
}
Printf ( " The BSP will be rebuilt. \n " ) ;
2017-03-16 23:22:52 +00:00
level . segs . Clear ( ) ;
level . subsectors . Clear ( ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
ForceNodeBuild = true ;
}
delete [ ] vertchanged ; // phares 10/4/98
delete [ ] data ;
}
//===========================================================================
//
// P_LoadSubsectors
//
//===========================================================================
template < class subsectortype , class segtype >
void P_LoadSubsectors ( MapData * map )
{
2017-03-08 14:20:00 +00:00
uint32_t maxseg = map - > Size ( ML_SEGS ) / sizeof ( segtype ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
unsigned numsubsectors = map - > Size ( ML_SSECTORS ) / sizeof ( subsectortype ) ;
2016-03-01 15:47:10 +00:00
if ( numsubsectors = = 0 | | maxseg = = 0 )
{
Printf ( " This map has an incomplete BSP tree. \n " ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
ForceNodeBuild = true ;
return ;
}
2017-03-16 23:22:52 +00:00
auto & subsectors = level . subsectors ;
subsectors . Alloc ( numsubsectors ) ;
2018-03-10 20:48:33 +00:00
auto & fr = map - > Reader ( ML_SSECTORS ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
memset ( & subsectors [ 0 ] , 0 , numsubsectors * sizeof ( subsector_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-03-16 23:22:52 +00:00
for ( unsigned i = 0 ; i < numsubsectors ; i + + )
2016-03-01 15:47:10 +00:00
{
subsectortype subd ;
2018-03-11 17:20:49 +00:00
subd . numsegs = sizeof ( subd . numsegs ) = = 2 ? fr . ReadUInt16 ( ) : fr . ReadUInt32 ( ) ;
subd . firstseg = sizeof ( subd . firstseg ) = = 2 ? fr . ReadUInt16 ( ) : fr . ReadUInt32 ( ) ;
2016-03-01 15:47:10 +00:00
if ( subd . numsegs = = 0 )
{
Printf ( " Subsector %i is empty. \n " , i ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
ForceNodeBuild = true ;
return ;
}
subsectors [ i ] . numlines = subd . numsegs ;
subsectors [ i ] . firstline = ( seg_t * ) ( size_t ) subd . firstseg ;
if ( ( size_t ) subsectors [ i ] . firstline > = maxseg )
{
Printf ( " Subsector %d contains invalid segs %u-%u \n "
" The BSP will be rebuilt. \n " , i , ( unsigned ) ( ( size_t ) subsectors [ i ] . firstline ) ,
( unsigned ) ( ( size_t ) subsectors [ i ] . firstline ) + subsectors [ i ] . numlines - 1 ) ;
ForceNodeBuild = true ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
2016-03-01 15:47:10 +00:00
break ;
}
else if ( ( size_t ) subsectors [ i ] . firstline + subsectors [ i ] . numlines > maxseg )
{
Printf ( " Subsector %d contains invalid segs %u-%u \n "
" The BSP will be rebuilt. \n " , i , maxseg ,
( unsigned ) ( ( size_t ) subsectors [ i ] . firstline ) + subsectors [ i ] . numlines - 1 ) ;
ForceNodeBuild = true ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
2016-03-01 15:47:10 +00:00
break ;
}
}
}
//===========================================================================
//
// P_LoadSectors
//
//===========================================================================
void P_LoadSectors ( MapData * map , FMissingTextureTracker & missingtex )
{
char * msp ;
mapsector_t * ms ;
sector_t * ss ;
int defSeqType ;
int lumplen = map - > Size ( ML_SECTORS ) ;
2017-01-07 18:32:24 +00:00
unsigned numsectors = lumplen / sizeof ( mapsector_t ) ;
level . sectors . Alloc ( numsectors ) ;
auto sectors = & level . sectors [ 0 ] ;
2016-03-01 15:47:10 +00:00
memset ( sectors , 0 , numsectors * sizeof ( sector_t ) ) ;
if ( level . flags & LEVEL_SNDSEQTOTALCTRL )
defSeqType = 0 ;
else
defSeqType = - 1 ;
msp = new char [ lumplen ] ;
map - > Read ( ML_SECTORS , msp ) ;
ms = ( mapsector_t * ) msp ;
ss = sectors ;
// Extended properties
sectors [ 0 ] . e = new extsector_t [ numsectors ] ;
2017-01-07 18:32:24 +00:00
for ( unsigned i = 0 ; i < numsectors ; i + + , ss + + , ms + + )
2016-03-01 15:47:10 +00:00
{
ss - > e = & sectors [ 0 ] . e [ i ] ;
if ( ! map - > HasBehavior ) ss - > Flags | = SECF_FLOORDROP ;
2016-04-02 20:05:23 +00:00
ss - > SetPlaneTexZ ( sector_t : : floor , ( double ) LittleShort ( ms - > floorheight ) ) ;
2016-04-24 10:15:09 +00:00
ss - > floorplane . set ( 0 , 0 , 1. , - ss - > GetPlaneTexZ ( sector_t : : floor ) ) ;
2016-04-02 20:05:23 +00:00
ss - > SetPlaneTexZ ( sector_t : : ceiling , ( double ) LittleShort ( ms - > ceilingheight ) ) ;
2016-04-24 10:15:09 +00:00
ss - > ceilingplane . set ( 0 , 0 , - 1. , ss - > GetPlaneTexZ ( sector_t : : ceiling ) ) ;
2016-03-01 15:47:10 +00:00
SetTexture ( ss , i , sector_t : : floor , ms - > floorpic , missingtex , true ) ;
SetTexture ( ss , i , sector_t : : ceiling , ms - > ceilingpic , missingtex , true ) ;
ss - > lightlevel = LittleShort ( ms - > lightlevel ) ;
if ( map - > HasBehavior )
ss - > special = LittleShort ( ms - > special ) ;
else // [RH] Translate to new sector special
ss - > special = P_TranslateSectorSpecial ( LittleShort ( ms - > special ) ) ;
tagManager . AddSectorTag ( i , LittleShort ( ms - > tag ) ) ;
2016-12-26 10:58:08 +00:00
ss - > thinglist = nullptr ;
ss - > touching_thinglist = nullptr ; // phares 3/14/98
2017-01-06 10:56:17 +00:00
ss - > sectorportal_thinglist = nullptr ;
2016-12-26 10:58:08 +00:00
ss - > touching_renderthings = nullptr ;
2016-03-01 15:47:10 +00:00
ss - > seqType = defSeqType ;
ss - > SeqName = NAME_None ;
ss - > nextsec = - 1 ; //jff 2/26/98 add fields to support locking out
ss - > prevsec = - 1 ; // stair retriggering until build completes
2017-01-28 18:05:39 +00:00
memset ( ss - > SpecialColors , - 1 , sizeof ( ss - > SpecialColors ) ) ;
2016-03-01 15:47:10 +00:00
2016-04-02 20:05:23 +00:00
ss - > SetAlpha ( sector_t : : floor , 1. ) ;
ss - > SetAlpha ( sector_t : : ceiling , 1. ) ;
ss - > SetXScale ( sector_t : : floor , 1. ) ; // [RH] floor and ceiling scaling
ss - > SetYScale ( sector_t : : floor , 1. ) ;
ss - > SetXScale ( sector_t : : ceiling , 1. ) ;
ss - > SetYScale ( sector_t : : ceiling , 1. ) ;
2016-03-01 15:47:10 +00:00
ss - > heightsec = NULL ; // sector used to get floor and ceiling height
// killough 3/7/98: end changes
ss - > gravity = 1.f ; // [RH] Default sector gravity of 1.0
ss - > ZoneNumber = 0xFFFF ;
ss - > terrainnum [ sector_t : : ceiling ] = ss - > terrainnum [ sector_t : : floor ] = - 1 ;
2018-04-03 18:55:36 +00:00
2016-03-01 15:47:10 +00:00
// [RH] Sectors default to white light with the default fade.
// If they are outside (have a sky ceiling), they use the outside fog.
2017-03-15 15:47:42 +00:00
ss - > Colormap . LightColor = PalEntry ( 255 , 255 , 255 ) ;
2016-03-01 15:47:10 +00:00
if ( level . outsidefog ! = 0xff000000 & & ( ss - > GetTexture ( sector_t : : ceiling ) = = skyflatnum | | ( ss - > special & 0xff ) = = Sector_Outside ) )
{
2017-03-15 15:47:42 +00:00
ss - > Colormap . FadeColor . SetRGB ( level . outsidefog ) ;
2016-03-01 15:47:10 +00:00
}
2018-04-03 18:55:36 +00:00
else if ( level . flags & LEVEL_HASFADETABLE )
{
ss - > Colormap . FadeColor = 0x939393 ; // The true color software renderer needs this. (The hardware renderer will ignore this value if LEVEL_HASFADETABLE is set.)
}
2016-03-01 15:47:10 +00:00
else
{
2017-03-15 15:47:42 +00:00
ss - > Colormap . FadeColor . SetRGB ( level . fadeto ) ;
2016-03-01 15:47:10 +00:00
}
2018-04-03 18:55:36 +00:00
2016-03-01 15:47:10 +00:00
// killough 8/28/98: initialize all sectors to normal friction
ss - > friction = ORIG_FRICTION ;
ss - > movefactor = ORIG_FRICTION_FACTOR ;
ss - > sectornum = i ;
2018-05-19 11:33:28 +00:00
ss - > ibocount = - 1 ;
2016-03-01 15:47:10 +00:00
}
delete [ ] msp ;
}
//===========================================================================
//
// P_LoadNodes
//
//===========================================================================
template < class nodetype , class subsectortype >
void P_LoadNodes ( MapData * map )
{
FMemLump data ;
int j ;
int k ;
char * mnp ;
nodetype * mn ;
node_t * no ;
2017-03-08 14:20:00 +00:00
uint16_t * used ;
2016-03-01 15:47:10 +00:00
int lumplen = map - > Size ( ML_NODES ) ;
int maxss = map - > Size ( ML_SSECTORS ) / sizeof ( subsectortype ) ;
2017-03-17 00:42:37 +00:00
unsigned numnodes = ( lumplen - nodetype : : NF_LUMPOFFSET ) / sizeof ( nodetype ) ;
2016-03-01 15:47:10 +00:00
if ( ( numnodes = = 0 & & maxss ! = 1 ) | | maxss = = 0 )
{
ForceNodeBuild = true ;
return ;
}
2017-03-17 00:42:37 +00:00
auto & nodes = level . nodes ;
nodes . Alloc ( numnodes ) ;
2017-03-08 14:20:00 +00:00
used = ( uint16_t * ) alloca ( sizeof ( uint16_t ) * numnodes ) ;
memset ( used , 0 , sizeof ( uint16_t ) * numnodes ) ;
2016-03-01 15:47:10 +00:00
mnp = new char [ lumplen ] ;
mn = ( nodetype * ) ( mnp + nodetype : : NF_LUMPOFFSET ) ;
map - > Read ( ML_NODES , mnp ) ;
2017-03-17 00:42:37 +00:00
no = & nodes [ 0 ] ;
2016-03-01 15:47:10 +00:00
2017-03-17 00:42:37 +00:00
for ( unsigned i = 0 ; i < numnodes ; i + + , no + + , mn + + )
2016-03-01 15:47:10 +00:00
{
no - > x = LittleShort ( mn - > x ) < < FRACBITS ;
no - > y = LittleShort ( mn - > y ) < < FRACBITS ;
no - > dx = LittleShort ( mn - > dx ) < < FRACBITS ;
no - > dy = LittleShort ( mn - > dy ) < < FRACBITS ;
for ( j = 0 ; j < 2 ; j + + )
{
int child = mn - > Child ( j ) ;
if ( child & nodetype : : NF_SUBSECTOR )
{
child & = ~ nodetype : : NF_SUBSECTOR ;
if ( child > = maxss )
{
Printf ( " BSP node %d references invalid subsector %d. \n "
" The BSP will be rebuilt. \n " , i , child ) ;
ForceNodeBuild = true ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
delete [ ] mnp ;
return ;
}
2017-03-16 23:22:52 +00:00
no - > children [ j ] = ( uint8_t * ) & level . subsectors [ child ] + 1 ;
2016-03-01 15:47:10 +00:00
}
2017-03-17 00:42:37 +00:00
else if ( ( unsigned ) child > = numnodes )
2016-03-01 15:47:10 +00:00
{
2017-03-18 12:59:23 +00:00
Printf ( " BSP node %d references invalid node %d. \n "
2017-03-17 00:42:37 +00:00
" The BSP will be rebuilt. \n " , i , ( ( node_t * ) no - > children [ j ] ) - > Index ( ) ) ;
2016-03-01 15:47:10 +00:00
ForceNodeBuild = true ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
delete [ ] mnp ;
return ;
}
else if ( used [ child ] )
{
Printf ( " BSP node %d references node %d, \n "
" which is already used by node %d. \n "
" The BSP will be rebuilt. \n " , i , child , used [ child ] - 1 ) ;
ForceNodeBuild = true ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
delete [ ] mnp ;
return ;
}
else
{
no - > children [ j ] = & nodes [ child ] ;
used [ child ] = j + 1 ;
}
for ( k = 0 ; k < 4 ; k + + )
{
2016-05-01 03:37:02 +00:00
no - > bbox [ j ] [ k ] = ( float ) LittleShort ( mn - > bbox [ j ] [ k ] ) ;
2016-03-01 15:47:10 +00:00
}
}
}
delete [ ] mnp ;
}
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR ( Bool , dumpspawnedthings , false , 0 )
AActor * SpawnMapThing ( int index , FMapThing * mt , int position )
{
AActor * spawned = P_SpawnMapThing ( mt , position ) ;
if ( dumpspawnedthings )
{
2016-03-23 11:21:52 +00:00
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 ,
2016-03-01 15:47:10 +00:00
spawned ? spawned - > GetClass ( ) - > TypeName . GetChars ( ) : " (none) " ) ;
}
T_AddSpawnedThing ( spawned ) ;
return spawned ;
}
//===========================================================================
//
// SetMapThingUserData
//
//===========================================================================
static void SetMapThingUserData ( AActor * actor , unsigned udi )
{
if ( actor = = NULL )
{
return ;
}
2018-03-19 14:39:50 +00:00
while ( MapThingsUserData [ udi ] . Key ! = NAME_None )
2016-03-01 15:47:10 +00:00
{
2018-03-19 14:39:50 +00:00
FName varname = MapThingsUserData [ udi ] . Key ;
2017-04-12 11:08:41 +00:00
PField * var = dyn_cast < PField > ( actor - > GetClass ( ) - > FindSymbol ( varname , true ) ) ;
2016-03-01 15:47:10 +00:00
2017-04-13 10:47:41 +00:00
if ( var = = NULL | | ( var - > Flags & ( VARF_Native | VARF_Private | VARF_Protected | VARF_Static ) ) | | ! var - > Type - > isScalar ( ) )
2016-03-01 15:47:10 +00:00
{
2018-03-19 14:39:50 +00:00
DPrintf ( DMSG_WARNING , " %s is not a writable user variable in class %s \n " , varname . GetChars ( ) ,
2016-03-01 15:47:10 +00:00
actor - > GetClass ( ) - > TypeName . GetChars ( ) ) ;
}
else
{ // Set the value of the specified user variable.
2018-03-19 14:39:50 +00:00
void * addr = reinterpret_cast < uint8_t * > ( actor ) + var - > Offset ;
2018-03-19 16:43:26 +00:00
if ( var - > Type = = TypeString )
2018-03-19 14:39:50 +00:00
{
var - > Type - > InitializeValue ( addr , & MapThingsUserData [ udi ] . StringVal ) ;
}
else if ( var - > Type - > isFloat ( ) )
{
var - > Type - > SetValue ( addr , MapThingsUserData [ udi ] . FloatVal ) ;
}
2018-03-19 16:43:26 +00:00
else if ( var - > Type - > isInt ( ) | | var - > Type = = TypeBool )
2018-03-19 14:39:50 +00:00
{
var - > Type - > SetValue ( addr , MapThingsUserData [ udi ] . IntVal ) ;
}
2016-03-01 15:47:10 +00:00
}
2018-03-19 14:39:50 +00:00
udi + + ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_LoadThings
//
//===========================================================================
2017-03-08 14:20:00 +00:00
uint16_t MakeSkill ( int flags )
2016-03-01 15:47:10 +00:00
{
2017-03-08 14:20:00 +00:00
uint16_t res = 0 ;
2016-03-01 15:47:10 +00:00
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 ] ) ) ;
2016-03-20 23:51:19 +00:00
mti [ i ] . Gravity = 1 ;
2016-03-01 15:47:10 +00:00
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 ;
2016-03-21 11:18:46 +00:00
mti [ i ] . Alpha = - 1 ;
2017-02-27 23:59:09 +00:00
mti [ i ] . Health = 1 ;
2016-03-01 15:47:10 +00:00
mti [ i ] . FloatbobPhase = - 1 ;
2016-03-23 11:21:52 +00:00
mti [ i ] . pos . X = LittleShort ( mt - > x ) ;
mti [ i ] . pos . Y = LittleShort ( mt - > y ) ;
2016-03-01 15:47:10 +00:00
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 ) ;
2016-03-23 11:21:52 +00:00
mti [ i ] . pos . X = LittleShort ( mth [ i ] . x ) ;
mti [ i ] . pos . Y = LittleShort ( mth [ i ] . y ) ;
mti [ i ] . pos . Z = LittleShort ( mth [ i ] . z ) ;
2016-03-01 15:47:10 +00:00
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.
}
2016-03-20 23:51:19 +00:00
mti [ i ] . Gravity = 1 ;
2016-03-01 15:47:10 +00:00
mti [ i ] . RenderStyle = STYLE_Count ;
2016-03-21 11:18:46 +00:00
mti [ i ] . Alpha = - 1 ;
2017-02-27 23:59:09 +00:00
mti [ i ] . Health = 1 ;
2016-03-01 15:47:10 +00:00
mti [ i ] . FloatbobPhase = - 1 ;
2018-01-09 20:48:19 +00:00
mti [ i ] . friendlyseeblocks = - 1 ;
2016-03-01 15:47:10 +00:00
}
delete [ ] mtp ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
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 ) ;
}
}
}
//===========================================================================
//
// P_LoadLineDefs
//
// killough 4/4/98: split into two functions, to allow sidedef overloading
//
// [RH] Actually split into four functions to allow for Hexen and Doom
// linedefs.
//
//===========================================================================
void P_AdjustLine ( line_t * ld )
{
vertex_t * v1 , * v2 ;
v1 = ld - > v1 ;
v2 = ld - > v2 ;
2016-03-29 14:13:16 +00:00
ld - > setDelta ( v2 - > fX ( ) - v1 - > fX ( ) , v2 - > fY ( ) - v1 - > fY ( ) ) ;
2016-03-01 15:47:10 +00:00
2016-03-30 23:22:49 +00:00
if ( v1 - > fX ( ) < v2 - > fX ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-30 23:22:49 +00:00
ld - > bbox [ BOXLEFT ] = v1 - > fX ( ) ;
ld - > bbox [ BOXRIGHT ] = v2 - > fX ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-30 23:22:49 +00:00
ld - > bbox [ BOXLEFT ] = v2 - > fX ( ) ;
ld - > bbox [ BOXRIGHT ] = v1 - > fX ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-30 23:22:49 +00:00
if ( v1 - > fY ( ) < v2 - > fY ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-30 23:22:49 +00:00
ld - > bbox [ BOXBOTTOM ] = v1 - > fY ( ) ;
ld - > bbox [ BOXTOP ] = v2 - > fY ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-30 23:22:49 +00:00
ld - > bbox [ BOXBOTTOM ] = v2 - > fY ( ) ;
ld - > bbox [ BOXTOP ] = v1 - > fY ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
// [RH] Set line id (as appropriate) here
// for Doom format maps this must be done in P_TranslateLineDef because
// the tag doesn't always go into the first arg.
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_SetLineID ( int i , line_t * ld )
{
if ( level . maptype = = MAPTYPE_HEXEN )
{
int setid = - 1 ;
switch ( ld - > special )
{
case Line_SetIdentification :
if ( ! ( level . flags2 & LEVEL2_HEXENHACK ) )
{
setid = ld - > args [ 0 ] + 256 * ld - > args [ 4 ] ;
ld - > flags | = ld - > args [ 1 ] < < 16 ;
}
else
{
setid = ld - > args [ 0 ] ;
}
ld - > special = 0 ;
break ;
case TranslucentLine :
setid = ld - > args [ 0 ] ;
ld - > flags | = ld - > args [ 3 ] < < 16 ;
break ;
case Teleport_Line :
case Scroll_Texture_Model :
setid = ld - > args [ 0 ] ;
break ;
case Polyobj_StartLine :
setid = ld - > args [ 3 ] ;
break ;
case Polyobj_ExplicitLine :
setid = ld - > args [ 4 ] ;
break ;
case Plane_Align :
2016-08-09 08:07:06 +00:00
if ( ! ( ib_compatflags & BCOMPATF_NOSLOPEID ) ) setid = ld - > args [ 2 ] ;
2016-03-01 15:47:10 +00:00
break ;
case Static_Init :
if ( ld - > args [ 1 ] = = Init_SectorLink ) setid = ld - > args [ 0 ] ;
break ;
case Line_SetPortal :
setid = ld - > args [ 1 ] ; // 0 = target id, 1 = this id, 2 = plane anchor
break ;
}
if ( setid ! = - 1 )
{
tagManager . AddLineID ( i , setid ) ;
}
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_SaveLineSpecial ( line_t * ld )
{
if ( ld - > sidedef [ 0 ] = = NULL )
return ;
2017-03-08 14:20:00 +00:00
uint32_t sidenum = ld - > sidedef [ 0 ] - > Index ( ) ;
2016-03-01 15:47:10 +00:00
// killough 4/4/98: support special sidedef interpretation below
// [RH] Save Static_Init only if it's interested in the textures
if ( ld - > special ! = Static_Init | | ld - > args [ 1 ] = = Init_Color )
{
sidetemp [ sidenum ] . a . special = ld - > special ;
sidetemp [ sidenum ] . a . tag = ld - > args [ 0 ] ;
}
else
{
sidetemp [ sidenum ] . a . special = 0 ;
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_FinishLoadingLineDef ( line_t * ld , int alpha )
{
bool additive = false ;
ld - > frontsector = ld - > sidedef [ 0 ] ! = NULL ? ld - > sidedef [ 0 ] - > sector : NULL ;
ld - > backsector = ld - > sidedef [ 1 ] ! = NULL ? ld - > sidedef [ 1 ] - > sector : NULL ;
2016-04-01 15:57:16 +00:00
double dx = ( ld - > v2 - > fX ( ) - ld - > v1 - > fX ( ) ) ;
double dy = ( ld - > v2 - > fY ( ) - ld - > v1 - > fY ( ) ) ;
2017-01-08 13:39:16 +00:00
int linenum = ld - > Index ( ) ;
2016-03-01 15:47:10 +00:00
if ( ld - > frontsector = = NULL )
{
Printf ( " Line %d has no front sector \n " , linemap [ linenum ] ) ;
}
// [RH] Set some new sidedef properties
2016-03-11 14:45:47 +00:00
int len = ( int ) ( g_sqrt ( dx * dx + dy * dy ) + 0.5f ) ;
2016-03-01 15:47:10 +00:00
if ( ld - > sidedef [ 0 ] ! = NULL )
{
ld - > sidedef [ 0 ] - > linedef = ld ;
ld - > sidedef [ 0 ] - > TexelLength = len ;
}
if ( ld - > sidedef [ 1 ] ! = NULL )
{
ld - > sidedef [ 1 ] - > linedef = ld ;
ld - > sidedef [ 1 ] - > TexelLength = len ;
}
switch ( ld - > special )
{ // killough 4/11/98: handle special types
case TranslucentLine : // killough 4/11/98: translucent 2s textures
// [RH] Second arg controls how opaque it is.
if ( alpha = = SHRT_MIN )
{
alpha = ld - > args [ 1 ] ;
additive = ! ! ld - > args [ 2 ] ;
}
else if ( alpha < 0 )
{
alpha = - alpha ;
additive = true ;
}
2016-04-24 10:15:09 +00:00
double dalpha = alpha / 255. ;
2016-03-01 15:47:10 +00:00
if ( ! ld - > args [ 0 ] )
{
2016-04-24 10:15:09 +00:00
ld - > alpha = dalpha ;
2016-03-01 15:47:10 +00:00
if ( additive )
{
ld - > flags | = ML_ADDTRANS ;
}
}
else
{
2017-01-08 13:39:16 +00:00
for ( unsigned j = 0 ; j < level . lines . Size ( ) ; j + + )
2016-03-01 15:47:10 +00:00
{
if ( tagManager . LineHasID ( j , ld - > args [ 0 ] ) )
{
2017-01-08 13:39:16 +00:00
level . lines [ j ] . alpha = dalpha ;
2016-03-01 15:47:10 +00:00
if ( additive )
{
2017-01-08 13:39:16 +00:00
level . lines [ j ] . flags | = ML_ADDTRANS ;
2016-03-01 15:47:10 +00:00
}
}
}
}
ld - > special = 0 ;
break ;
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
2016-03-01 15:47:10 +00:00
// killough 4/4/98: delay using sidedefs until they are loaded
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_FinishLoadingLineDefs ( )
{
2017-01-08 13:39:16 +00:00
for ( auto & line : level . lines )
2016-03-01 15:47:10 +00:00
{
2017-01-08 17:45:30 +00:00
P_FinishLoadingLineDef ( & line , sidetemp [ line . sidedef [ 0 ] - > Index ( ) ] . a . alpha ) ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2017-03-08 14:20:00 +00:00
static void P_SetSideNum ( side_t * * sidenum_p , uint16_t sidenum )
2016-03-01 15:47:10 +00:00
{
if ( sidenum = = NO_INDEX )
{
* sidenum_p = NULL ;
}
2017-01-08 17:45:30 +00:00
else if ( sidecount < ( int ) level . sides . Size ( ) )
2016-03-01 15:47:10 +00:00
{
sidetemp [ sidecount ] . a . map = sidenum ;
2017-01-08 17:45:30 +00:00
* sidenum_p = & level . sides [ sidecount + + ] ;
2016-03-01 15:47:10 +00:00
}
else
{
I_Error ( " %d sidedefs is not enough \n " , sidecount ) ;
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_LoadLineDefs ( MapData * map )
{
int i , skipped ;
line_t * ld ;
int lumplen = map - > Size ( ML_LINEDEFS ) ;
char * mldf ;
maplinedef_t * mld ;
2017-01-08 13:39:16 +00:00
int numlines = lumplen / sizeof ( maplinedef_t ) ;
2016-03-01 15:47:10 +00:00
linemap . Resize ( numlines ) ;
mldf = new char [ lumplen ] ;
map - > Read ( ML_LINEDEFS , mldf ) ;
// [RH] Count the number of sidedef references. This is the number of
// sidedefs we need. The actual number in the SIDEDEFS lump might be less.
// Lines with 0 length are also removed.
for ( skipped = sidecount = i = 0 ; i < numlines ; )
{
mld = ( ( maplinedef_t * ) mldf ) + i ;
2017-01-08 23:46:16 +00:00
unsigned v1 = LittleShort ( mld - > v1 ) ;
unsigned v2 = LittleShort ( mld - > v2 ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 23:46:16 +00:00
if ( v1 > = level . vertexes . Size ( ) | | v2 > = level . vertexes . Size ( ) )
2016-03-01 15:47:10 +00:00
{
delete [ ] mldf ;
2017-01-08 23:46:16 +00:00
I_Error ( " Line %d has invalid vertices: %d and/or %d. \n The map only contains %u vertices. " , i + skipped , v1 , v2 , level . vertexes . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
else if ( v1 = = v2 | |
2017-01-08 23:46:16 +00:00
( level . vertexes [ v1 ] . fX ( ) = = level . vertexes [ v2 ] . fX ( ) & & level . vertexes [ v1 ] . fY ( ) = = level . vertexes [ v2 ] . fY ( ) ) )
2016-03-01 15:47:10 +00:00
{
Printf ( " Removing 0-length line %d \n " , i + skipped ) ;
memmove ( mld , mld + 1 , sizeof ( * mld ) * ( numlines - i - 1 ) ) ;
ForceNodeBuild = true ;
skipped + + ;
numlines - - ;
}
else
{
// patch missing first sides instead of crashing out.
// Visual glitches are better than not being able to play.
if ( LittleShort ( mld - > sidenum [ 0 ] ) = = NO_INDEX )
{
Printf ( " Line %d has no first side. \n " , i ) ;
mld - > sidenum [ 0 ] = 0 ;
}
sidecount + + ;
if ( LittleShort ( mld - > sidenum [ 1 ] ) ! = NO_INDEX )
sidecount + + ;
linemap [ i ] = i + skipped ;
i + + ;
}
}
2017-01-29 23:25:40 +00:00
level . lines . Alloc ( numlines ) ;
memset ( & level . lines [ 0 ] , 0 , numlines * sizeof ( line_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 17:45:30 +00:00
P_AllocateSideDefs ( map , sidecount ) ;
2016-03-01 15:47:10 +00:00
mld = ( maplinedef_t * ) mldf ;
2017-01-08 13:39:16 +00:00
ld = & level . lines [ 0 ] ;
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < numlines ; i + + , mld + + , ld + + )
{
2016-04-24 10:15:09 +00:00
ld - > alpha = 1. ; // [RH] Opaque by default
2016-03-01 15:47:10 +00:00
ld - > portalindex = UINT_MAX ;
2017-01-08 13:59:31 +00:00
ld - > portaltransferred = UINT_MAX ;
2016-03-01 15:47:10 +00:00
// [RH] Translate old linedef special and flags to be
// compatible with the new format.
2017-05-04 21:52:16 +00:00
mld - > special = LittleShort ( mld - > special ) ;
mld - > tag = LittleShort ( mld - > tag ) ;
mld - > flags = LittleShort ( mld - > flags ) ;
2016-03-01 15:47:10 +00:00
P_TranslateLineDef ( ld , mld , - 1 ) ;
// do not assign the tag for Extradata lines.
if ( ld - > special ! = Static_Init | | ( ld - > args [ 1 ] ! = Init_EDLine & & ld - > args [ 1 ] ! = Init_EDSector ) )
{
tagManager . AddLineID ( i , mld - > tag ) ;
}
# ifndef NO_EDATA
if ( ld - > special = = Static_Init & & ld - > args [ 1 ] = = Init_EDLine )
{
ProcessEDLinedef ( ld , mld - > tag ) ;
}
# endif
2017-01-08 23:46:16 +00:00
ld - > v1 = & level . vertexes [ LittleShort ( mld - > v1 ) ] ;
ld - > v2 = & level . vertexes [ LittleShort ( mld - > v2 ) ] ;
2016-03-01 15:47:10 +00:00
P_SetSideNum ( & ld - > sidedef [ 0 ] , LittleShort ( mld - > sidenum [ 0 ] ) ) ;
P_SetSideNum ( & ld - > sidedef [ 1 ] , LittleShort ( mld - > sidenum [ 1 ] ) ) ;
P_AdjustLine ( ld ) ;
P_SaveLineSpecial ( ld ) ;
if ( level . flags2 & LEVEL2_CLIPMIDTEX ) ld - > flags | = ML_CLIP_MIDTEX ;
if ( level . flags2 & LEVEL2_WRAPMIDTEX ) ld - > flags | = ML_WRAP_MIDTEX ;
if ( level . flags2 & LEVEL2_CHECKSWITCHRANGE ) ld - > flags | = ML_CHECKSWITCHRANGE ;
}
delete [ ] mldf ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
2016-03-01 15:47:10 +00:00
// [RH] Same as P_LoadLineDefs() except it uses Hexen-style LineDefs.
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_LoadLineDefs2 ( MapData * map )
{
int i , skipped ;
line_t * ld ;
int lumplen = map - > Size ( ML_LINEDEFS ) ;
char * mldf ;
maplinedef2_t * mld ;
2017-01-08 13:39:16 +00:00
int numlines = lumplen / sizeof ( maplinedef2_t ) ;
2016-03-01 15:47:10 +00:00
linemap . Resize ( numlines ) ;
mldf = new char [ lumplen ] ;
map - > Read ( ML_LINEDEFS , mldf ) ;
// [RH] Remove any lines that have 0 length and count sidedefs used
for ( skipped = sidecount = i = 0 ; i < numlines ; )
{
mld = ( ( maplinedef2_t * ) mldf ) + i ;
if ( mld - > v1 = = mld - > v2 | |
2017-01-08 23:46:16 +00:00
( level . vertexes [ LittleShort ( mld - > v1 ) ] . fX ( ) = = level . vertexes [ LittleShort ( mld - > v2 ) ] . fX ( ) & &
level . vertexes [ LittleShort ( mld - > v1 ) ] . fY ( ) = = level . vertexes [ LittleShort ( mld - > v2 ) ] . fY ( ) ) )
2016-03-01 15:47:10 +00:00
{
Printf ( " Removing 0-length line %d \n " , i + skipped ) ;
memmove ( mld , mld + 1 , sizeof ( * mld ) * ( numlines - i - 1 ) ) ;
skipped + + ;
numlines - - ;
}
else
{
// patch missing first sides instead of crashing out.
// Visual glitches are better than not being able to play.
if ( LittleShort ( mld - > sidenum [ 0 ] ) = = NO_INDEX )
{
Printf ( " Line %d has no first side. \n " , i ) ;
mld - > sidenum [ 0 ] = 0 ;
}
sidecount + + ;
if ( LittleShort ( mld - > sidenum [ 1 ] ) ! = NO_INDEX )
sidecount + + ;
linemap [ i ] = i + skipped ;
i + + ;
}
}
if ( skipped > 0 )
{
ForceNodeBuild = true ;
}
2017-01-29 23:25:40 +00:00
level . lines . Alloc ( numlines ) ;
memset ( & level . lines [ 0 ] , 0 , numlines * sizeof ( line_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 17:45:30 +00:00
P_AllocateSideDefs ( map , sidecount ) ;
2016-03-01 15:47:10 +00:00
mld = ( maplinedef2_t * ) mldf ;
2017-01-08 13:39:16 +00:00
ld = & level . lines [ 0 ] ;
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < numlines ; i + + , mld + + , ld + + )
{
int j ;
ld - > portalindex = UINT_MAX ;
2017-01-08 13:59:31 +00:00
ld - > portaltransferred = UINT_MAX ;
2016-03-01 15:47:10 +00:00
for ( j = 0 ; j < 5 ; j + + )
ld - > args [ j ] = mld - > args [ j ] ;
ld - > flags = LittleShort ( mld - > flags ) ;
ld - > special = mld - > special ;
2017-01-08 23:46:16 +00:00
ld - > v1 = & level . vertexes [ LittleShort ( mld - > v1 ) ] ;
ld - > v2 = & level . vertexes [ LittleShort ( mld - > v2 ) ] ;
2016-04-24 10:15:09 +00:00
ld - > alpha = 1. ; // [RH] Opaque by default
2016-03-01 15:47:10 +00:00
P_SetSideNum ( & ld - > sidedef [ 0 ] , LittleShort ( mld - > sidenum [ 0 ] ) ) ;
P_SetSideNum ( & ld - > sidedef [ 1 ] , LittleShort ( mld - > sidenum [ 1 ] ) ) ;
P_AdjustLine ( ld ) ;
P_SetLineID ( i , ld ) ;
P_SaveLineSpecial ( ld ) ;
if ( level . flags2 & LEVEL2_CLIPMIDTEX ) ld - > flags | = ML_CLIP_MIDTEX ;
if ( level . flags2 & LEVEL2_WRAPMIDTEX ) ld - > flags | = ML_WRAP_MIDTEX ;
if ( level . flags2 & LEVEL2_CHECKSWITCHRANGE ) ld - > flags | = ML_CHECKSWITCHRANGE ;
// convert the activation type
ld - > activation = 1 < < GET_SPAC ( ld - > flags ) ;
if ( ld - > activation = = SPAC_AnyCross )
{ // this is really PTouch
ld - > activation = SPAC_Impact | SPAC_PCross ;
}
else if ( ld - > activation = = SPAC_Impact )
{ // In non-UMDF maps, Impact implies PCross
ld - > activation = SPAC_Impact | SPAC_PCross ;
}
ld - > flags & = ~ ML_SPAC_MASK ;
}
delete [ ] mldf ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
//
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
2017-01-08 17:45:30 +00:00
static void P_AllocateSideDefs ( MapData * map , int count )
2016-03-01 15:47:10 +00:00
{
int i ;
2017-01-08 17:45:30 +00:00
level . sides . Alloc ( count ) ;
memset ( & level . sides [ 0 ] , 0 , count * sizeof ( side_t ) ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 23:46:16 +00:00
sidetemp = new sidei_t [ MAX < int > ( count , level . vertexes . Size ( ) ) ] ;
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < count ; i + + )
{
sidetemp [ i ] . a . special = sidetemp [ i ] . a . tag = 0 ;
sidetemp [ i ] . a . alpha = SHRT_MIN ;
sidetemp [ i ] . a . map = NO_SIDE ;
}
2017-01-14 11:35:23 +00:00
int numsides = map - > Size ( ML_SIDEDEFS ) / sizeof ( mapsidedef_t ) ;
2016-03-01 15:47:10 +00:00
if ( count < numsides )
{
Printf ( " Map has %d unused sidedefs \n " , numsides - count ) ;
}
numsides = count ;
sidecount = 0 ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
2016-03-01 15:47:10 +00:00
// [RH] Group sidedefs into loops so that we can easily determine
// what walls any particular wall neighbors.
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
static void P_LoopSidedefs ( bool firstloop )
{
int i ;
if ( sidetemp ! = NULL )
{
delete [ ] sidetemp ;
}
2017-01-08 17:45:30 +00:00
int numsides = level . sides . Size ( ) ;
2017-01-08 23:46:16 +00:00
sidetemp = new sidei_t [ MAX < int > ( level . vertexes . Size ( ) , numsides ) ] ;
2016-03-01 15:47:10 +00:00
2017-01-08 23:46:16 +00:00
for ( i = 0 ; i < ( int ) level . vertexes . Size ( ) ; + + i )
2016-03-01 15:47:10 +00:00
{
sidetemp [ i ] . b . first = NO_SIDE ;
sidetemp [ i ] . b . next = NO_SIDE ;
}
for ( ; i < numsides ; + + i )
{
sidetemp [ i ] . b . next = NO_SIDE ;
}
for ( i = 0 ; i < numsides ; + + i )
{
// For each vertex, build a list of sidedefs that use that vertex
// as their left edge.
2017-01-08 17:45:30 +00:00
line_t * line = level . sides [ i ] . linedef ;
int lineside = ( line - > sidedef [ 0 ] ! = & level . sides [ i ] ) ;
2017-01-08 23:46:16 +00:00
int vert = lineside ? line - > v2 - > Index ( ) : line - > v1 - > Index ( ) ;
2016-03-01 15:47:10 +00:00
sidetemp [ i ] . b . lineside = lineside ;
sidetemp [ i ] . b . next = sidetemp [ vert ] . b . first ;
sidetemp [ vert ] . b . first = i ;
// Set each side so that it is the only member of its loop
2017-01-08 17:45:30 +00:00
level . sides [ i ] . LeftSide = NO_SIDE ;
level . sides [ i ] . RightSide = NO_SIDE ;
2016-03-01 15:47:10 +00:00
}
// For each side, find the side that is to its right and set the
// loop pointers accordingly. If two sides share a left vertex, the
// one that forms the smallest angle is assumed to be the right one.
for ( i = 0 ; i < numsides ; + + i )
{
2017-03-08 14:20:00 +00:00
uint32_t right ;
2017-01-08 17:45:30 +00:00
line_t * line = level . sides [ i ] . linedef ;
2016-03-01 15:47:10 +00:00
// If the side's line only exists in a single sector,
// then consider that line to be a self-contained loop
// instead of as part of another loop
if ( line - > frontsector = = line - > backsector )
{
const side_t * const rightside = line - > sidedef [ ! sidetemp [ i ] . b . lineside ] ;
if ( NULL = = rightside )
{
// There is no right side!
2017-01-08 13:39:16 +00:00
if ( firstloop ) Printf ( " Line %d's right edge is unconnected \n " , linemap [ line - > Index ( ) ] ) ;
2016-03-01 15:47:10 +00:00
continue ;
}
2017-01-08 17:45:30 +00:00
right = rightside - > Index ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
if ( sidetemp [ i ] . b . lineside )
{
2017-01-08 23:46:16 +00:00
right = line - > v1 - > Index ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2017-01-08 23:46:16 +00:00
right = line - > v2 - > Index ( ) ;
2016-03-01 15:47:10 +00:00
}
right = sidetemp [ right ] . b . first ;
if ( right = = NO_SIDE )
{
// There is no right side!
2017-01-08 13:39:16 +00:00
if ( firstloop ) Printf ( " Line %d's right edge is unconnected \n " , linemap [ line - > Index ( ) ] ) ;
2016-03-01 15:47:10 +00:00
continue ;
}
if ( sidetemp [ right ] . b . next ! = NO_SIDE )
{
int bestright = right ; // Shut up, GCC
2016-03-29 14:13:16 +00:00
DAngle bestang = 360. ;
2016-03-01 15:47:10 +00:00
line_t * leftline , * rightline ;
2016-03-29 14:13:16 +00:00
DAngle ang1 , ang2 , ang ;
2016-03-01 15:47:10 +00:00
2017-01-08 17:45:30 +00:00
leftline = level . sides [ i ] . linedef ;
2016-03-29 14:13:16 +00:00
ang1 = leftline - > Delta ( ) . Angle ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! sidetemp [ i ] . b . lineside )
{
2016-03-29 14:13:16 +00:00
ang1 + = 180 ;
2016-03-01 15:47:10 +00:00
}
while ( right ! = NO_SIDE )
{
2017-01-08 17:45:30 +00:00
if ( level . sides [ right ] . LeftSide = = NO_SIDE )
2016-03-01 15:47:10 +00:00
{
2017-01-08 17:45:30 +00:00
rightline = level . sides [ right ] . linedef ;
2016-03-01 15:47:10 +00:00
if ( rightline - > frontsector ! = rightline - > backsector )
{
2016-03-29 14:13:16 +00:00
ang2 = rightline - > Delta ( ) . Angle ( ) ;
2016-03-01 15:47:10 +00:00
if ( sidetemp [ right ] . b . lineside )
{
2016-03-29 14:13:16 +00:00
ang2 + = 180 ;
2016-03-01 15:47:10 +00:00
}
2016-03-29 14:13:16 +00:00
ang = ( ang2 - ang1 ) . Normalized360 ( ) ;
2016-03-01 15:47:10 +00:00
if ( ang ! = 0 & & ang < = bestang )
{
bestright = right ;
bestang = ang ;
}
}
}
right = sidetemp [ right ] . b . next ;
}
right = bestright ;
}
}
assert ( ( unsigned ) i < ( unsigned ) numsides ) ;
assert ( right < ( unsigned ) numsides ) ;
2017-01-08 17:45:30 +00:00
level . sides [ i ] . RightSide = right ;
level . sides [ right ] . LeftSide = i ;
2016-03-01 15:47:10 +00:00
}
// We keep the sidedef init info around until after polyobjects are initialized,
// so don't delete just yet.
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
int P_DetermineTranslucency ( int lumpnum )
{
2018-03-10 17:45:11 +00:00
auto tranmap = Wads . OpenLumpReader ( lumpnum ) ;
2017-03-08 14:20:00 +00:00
uint8_t index ;
2016-03-01 15:47:10 +00:00
PalEntry newcolor ;
PalEntry newcolor2 ;
2018-03-11 17:32:00 +00:00
tranmap . Seek ( GPalette . BlackIndex * 256 + GPalette . WhiteIndex , FileReader : : SeekSet ) ;
2016-03-01 15:47:10 +00:00
tranmap . Read ( & index , 1 ) ;
newcolor = GPalette . BaseColors [ GPalette . Remap [ index ] ] ;
2018-03-11 17:32:00 +00:00
tranmap . Seek ( GPalette . WhiteIndex * 256 + GPalette . BlackIndex , FileReader : : SeekSet ) ;
2016-03-01 15:47:10 +00:00
tranmap . Read ( & index , 1 ) ;
newcolor2 = GPalette . BaseColors [ GPalette . Remap [ index ] ] ;
if ( newcolor2 . r = = 255 ) // if black on white results in white it's either
// fully transparent or additive
{
2016-08-28 07:55:04 +00:00
if ( developer > = DMSG_NOTIFY )
2016-03-01 15:47:10 +00:00
{
char lumpname [ 9 ] ;
lumpname [ 8 ] = 0 ;
Wads . GetLumpName ( lumpname , lumpnum ) ;
Printf ( " %s appears to be additive translucency %d (%d%%) \n " , lumpname , newcolor . r ,
newcolor . r * 100 / 255 ) ;
}
return - newcolor . r ;
}
2016-08-28 07:55:04 +00:00
if ( developer > = DMSG_NOTIFY )
2016-03-01 15:47:10 +00:00
{
char lumpname [ 9 ] ;
lumpname [ 8 ] = 0 ;
Wads . GetLumpName ( lumpname , lumpnum ) ;
Printf ( " %s appears to be translucency %d (%d%%) \n " , lumpname , newcolor . r ,
newcolor . r * 100 / 255 ) ;
}
return newcolor . r ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_ProcessSideTextures ( bool checktranmap , side_t * sd , sector_t * sec , intmapsidedef_t * msd , int special , int tag , short * alpha , FMissingTextureTracker & missingtex )
{
switch ( special )
{
case Transfer_Heights : // variable colormap via 242 linedef
// [RH] The colormap num we get here isn't really a colormap,
// but a packed ARGB word for blending, so we also allow
// the blend to be specified directly by the texture names
// instead of figuring something out from the colormap.
if ( sec ! = NULL )
{
SetTexture ( sd , side_t : : bottom , & sec - > bottommap , msd - > bottomtexture ) ;
SetTexture ( sd , side_t : : mid , & sec - > midmap , msd - > midtexture ) ;
SetTexture ( sd , side_t : : top , & sec - > topmap , msd - > toptexture ) ;
}
break ;
case Static_Init :
// [RH] Set sector color and fog
// upper "texture" is light color
// lower "texture" is fog color
{
2017-03-08 14:20:00 +00:00
uint32_t color = MAKERGB ( 255 , 255 , 255 ) , fog = 0 ;
2016-03-01 15:47:10 +00:00
bool colorgood , foggood ;
SetTextureNoErr ( sd , side_t : : bottom , & fog , msd - > bottomtexture , & foggood , true ) ;
SetTextureNoErr ( sd , side_t : : top , & color , msd - > toptexture , & colorgood , false ) ;
SetTexture ( sd , side_t : : mid , msd - > midtexture , missingtex ) ;
if ( colorgood | foggood )
{
2017-01-07 18:32:24 +00:00
for ( unsigned s = 0 ; s < level . sectors . Size ( ) ; s + + )
2016-03-01 15:47:10 +00:00
{
if ( tagManager . SectorHasTag ( s , tag ) )
{
2017-03-15 15:47:42 +00:00
if ( colorgood )
2016-03-01 15:47:10 +00:00
{
2017-03-15 15:47:42 +00:00
level . sectors [ s ] . Colormap . LightColor . SetRGB ( color ) ;
level . sectors [ s ] . Colormap . BlendFactor = APART ( color ) ;
2016-03-01 15:47:10 +00:00
}
2017-03-15 15:47:42 +00:00
if ( foggood ) level . sectors [ s ] . Colormap . FadeColor . SetRGB ( fog ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
break ;
case Sector_Set3DFloor :
if ( msd - > toptexture [ 0 ] = = ' # ' )
{
2017-02-01 10:19:55 +00:00
sd - > SetTexture ( side_t : : top , FNullTextureID ( ) + ( int ) ( - strtoll ( & msd - > toptexture [ 1 ] , NULL , 10 ) ) ) ; // store the alpha as a negative texture index
2016-03-01 15:47:10 +00:00
// This will be sorted out by the 3D-floor code later.
}
else
{
SetTexture ( sd , side_t : : top , msd - > toptexture , missingtex ) ;
}
SetTexture ( sd , side_t : : mid , msd - > midtexture , missingtex ) ;
SetTexture ( sd , side_t : : bottom , msd - > bottomtexture , missingtex ) ;
break ;
case TranslucentLine : // killough 4/11/98: apply translucency to 2s normal texture
if ( checktranmap )
{
int lumpnum ;
if ( strnicmp ( " TRANMAP " , msd - > midtexture , 8 ) = = 0 )
{
// The translator set the alpha argument already; no reason to do it again.
sd - > SetTexture ( side_t : : mid , FNullTextureID ( ) ) ;
}
else if ( ( lumpnum = Wads . CheckNumForName ( msd - > midtexture ) ) > 0 & &
Wads . LumpLength ( lumpnum ) = = 65536 )
{
* alpha = ( short ) P_DetermineTranslucency ( lumpnum ) ;
sd - > SetTexture ( side_t : : mid , FNullTextureID ( ) ) ;
}
else
{
SetTexture ( sd , side_t : : mid , msd - > midtexture , missingtex ) ;
}
SetTexture ( sd , side_t : : top , msd - > toptexture , missingtex ) ;
SetTexture ( sd , side_t : : bottom , msd - > bottomtexture , missingtex ) ;
break ;
}
// Fallthrough for Hexen maps is intentional
default : // normal cases
SetTexture ( sd , side_t : : mid , msd - > midtexture , missingtex ) ;
SetTexture ( sd , side_t : : top , msd - > toptexture , missingtex ) ;
SetTexture ( sd , side_t : : bottom , msd - > bottomtexture , missingtex ) ;
break ;
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
2016-03-01 15:47:10 +00:00
// killough 4/4/98: delay using texture names until
// after linedefs are loaded, to allow overloading.
// killough 5/3/98: reformatted, cleaned up
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_LoadSideDefs2 ( MapData * map , FMissingTextureTracker & missingtex )
{
char * msdf = new char [ map - > Size ( ML_SIDEDEFS ) ] ;
map - > Read ( ML_SIDEDEFS , msdf ) ;
2017-01-08 17:45:30 +00:00
for ( unsigned i = 0 ; i < level . sides . Size ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
mapsidedef_t * msd = ( ( mapsidedef_t * ) msdf ) + sidetemp [ i ] . a . map ;
2017-01-08 17:45:30 +00:00
side_t * sd = & level . sides [ i ] ;
2016-03-01 15:47:10 +00:00
sector_t * sec ;
// [RH] The Doom renderer ignored the patch y locations when
// drawing mid textures. ZDoom does not, so fix the laser beams in Strife.
if ( gameinfo . gametype = = GAME_Strife & &
strncmp ( msd - > midtexture , " LASERB01 " , 8 ) = = 0 )
{
msd - > rowoffset + = 102 ;
}
2016-04-23 07:41:59 +00:00
sd - > SetTextureXOffset ( LittleShort ( msd - > textureoffset ) ) ;
sd - > SetTextureYOffset ( LittleShort ( msd - > rowoffset ) ) ;
sd - > SetTextureXScale ( 1. ) ;
sd - > SetTextureYScale ( 1. ) ;
2016-03-01 15:47:10 +00:00
sd - > linedef = NULL ;
sd - > Flags = 0 ;
2017-01-08 17:45:30 +00:00
sd - > UDMFIndex = i ;
2016-03-01 15:47:10 +00:00
// killough 4/4/98: allow sidedef texture names to be overloaded
// killough 4/11/98: refined to allow colormaps to work as wall
// textures if invalid as colormaps but valid as textures.
2017-01-07 18:32:24 +00:00
if ( ( unsigned ) LittleShort ( msd - > sector ) > = level . sectors . Size ( ) )
2016-03-01 15:47:10 +00:00
{
Printf ( PRINT_HIGH , " Sidedef %d has a bad sector \n " , i ) ;
sd - > sector = sec = NULL ;
}
else
{
2017-01-07 18:32:24 +00:00
sd - > sector = sec = & level . sectors [ LittleShort ( msd - > sector ) ] ;
2016-03-01 15:47:10 +00:00
}
intmapsidedef_t imsd ;
imsd . toptexture . CopyCStrPart ( msd - > toptexture , 8 ) ;
imsd . midtexture . CopyCStrPart ( msd - > midtexture , 8 ) ;
imsd . bottomtexture . CopyCStrPart ( msd - > bottomtexture , 8 ) ;
P_ProcessSideTextures ( ! map - > HasBehavior , sd , sec , & imsd ,
sidetemp [ i ] . a . special , sidetemp [ i ] . a . tag , & sidetemp [ i ] . a . alpha , missingtex ) ;
}
delete [ ] msdf ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
// [RH] My own blockmap builder, not Killough's or TeamTNT's.
//
// Killough's turned out not to be correct enough, and I had
// written this for ZDBSP before I discovered that, so
// replacing the one he wrote for MBF seemed like the easiest
// thing to do. (Doom E3M6, near vertex 0--the one furthest east
// on the map--had problems.)
//
// Using a hash table to get the minimum possible blockmap size
// seems like overkill, but I wanted to change the code as little
// as possible from its ZDBSP incarnation.
//
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
static unsigned int BlockHash ( TArray < int > * block )
{
int hash = 0 ;
int * ar = & ( * block ) [ 0 ] ;
for ( size_t i = 0 ; i < block - > Size ( ) ; + + i )
{
hash = hash * 12235 + ar [ i ] ;
}
return hash & 0x7fffffff ;
}
static bool BlockCompare ( TArray < int > * block1 , TArray < int > * block2 )
{
size_t size = block1 - > Size ( ) ;
if ( size ! = block2 - > Size ( ) )
{
return false ;
}
if ( size = = 0 )
{
return true ;
}
int * ar1 = & ( * block1 ) [ 0 ] ;
int * ar2 = & ( * block2 ) [ 0 ] ;
for ( size_t i = 0 ; i < size ; + + i )
{
if ( ar1 [ i ] ! = ar2 [ i ] )
{
return false ;
}
}
return true ;
}
static void CreatePackedBlockmap ( TArray < int > & BlockMap , TArray < int > * blocks , int bmapwidth , int bmapheight )
{
int buckets [ 4096 ] ;
int * hashes , hashblock ;
TArray < int > * block ;
int zero = 0 ;
int terminator = - 1 ;
int * array ;
int i , hash ;
int hashed = 0 , nothashed = 0 ;
hashes = new int [ bmapwidth * bmapheight ] ;
memset ( hashes , 0xff , sizeof ( int ) * bmapwidth * bmapheight ) ;
memset ( buckets , 0xff , sizeof ( buckets ) ) ;
for ( i = 0 ; i < bmapwidth * bmapheight ; + + i )
{
block = & blocks [ i ] ;
hash = BlockHash ( block ) % 4096 ;
hashblock = buckets [ hash ] ;
while ( hashblock ! = - 1 )
{
if ( BlockCompare ( block , & blocks [ hashblock ] ) )
{
break ;
}
hashblock = hashes [ hashblock ] ;
}
if ( hashblock ! = - 1 )
{
BlockMap [ 4 + i ] = BlockMap [ 4 + hashblock ] ;
hashed + + ;
}
else
{
hashes [ i ] = buckets [ hash ] ;
buckets [ hash ] = i ;
BlockMap [ 4 + i ] = BlockMap . Size ( ) ;
BlockMap . Push ( zero ) ;
array = & ( * block ) [ 0 ] ;
for ( size_t j = 0 ; j < block - > Size ( ) ; + + j )
{
BlockMap . Push ( array [ j ] ) ;
}
BlockMap . Push ( terminator ) ;
nothashed + + ;
}
}
delete [ ] hashes ;
// printf ("%d blocks written, %d blocks saved\n", nothashed, hashed);
}
# define BLOCKBITS 7
# define BLOCKSIZE 128
static void P_CreateBlockMap ( )
{
TArray < int > * BlockLists , * block , * endblock ;
int adder ;
int bmapwidth , bmapheight ;
2016-04-01 15:57:16 +00:00
double dminx , dmaxx , dminy , dmaxy ;
2016-03-01 15:47:10 +00:00
int minx , maxx , miny , maxy ;
int line ;
2017-01-08 23:46:16 +00:00
if ( level . vertexes . Size ( ) = = 0 )
2016-03-01 15:47:10 +00:00
return ;
// Find map extents for the blockmap
2017-01-08 23:46:16 +00:00
dminx = dmaxx = level . vertexes [ 0 ] . fX ( ) ;
dminy = dmaxy = level . vertexes [ 0 ] . fY ( ) ;
2016-03-01 15:47:10 +00:00
2017-01-08 23:46:16 +00:00
for ( auto & vert : level . vertexes )
2016-03-01 15:47:10 +00:00
{
2017-01-08 23:46:16 +00:00
if ( vert . fX ( ) < dminx ) dminx = vert . fX ( ) ;
else if ( vert . fX ( ) > dmaxx ) dmaxx = vert . fX ( ) ;
if ( vert . fY ( ) < dminy ) dminy = vert . fY ( ) ;
else if ( vert . fY ( ) > dmaxy ) dmaxy = vert . fY ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-04-01 15:57:16 +00:00
minx = int ( dminx ) ;
miny = int ( dminy ) ;
maxx = int ( dmaxx ) ;
maxy = int ( dmaxy ) ;
2016-03-01 15:47:10 +00:00
bmapwidth = ( ( maxx - minx ) > > BLOCKBITS ) + 1 ;
bmapheight = ( ( maxy - miny ) > > BLOCKBITS ) + 1 ;
TArray < int > BlockMap ( bmapwidth * bmapheight * 3 + 4 ) ;
adder = minx ; BlockMap . Push ( adder ) ;
adder = miny ; BlockMap . Push ( adder ) ;
adder = bmapwidth ; BlockMap . Push ( adder ) ;
adder = bmapheight ; BlockMap . Push ( adder ) ;
BlockLists = new TArray < int > [ bmapwidth * bmapheight ] ;
2017-01-08 13:39:16 +00:00
for ( line = 0 ; line < ( int ) level . lines . Size ( ) ; + + line )
2016-03-01 15:47:10 +00:00
{
2017-01-08 13:39:16 +00:00
int x1 = int ( level . lines [ line ] . v1 - > fX ( ) ) ;
int y1 = int ( level . lines [ line ] . v1 - > fY ( ) ) ;
int x2 = int ( level . lines [ line ] . v2 - > fX ( ) ) ;
int y2 = int ( level . lines [ line ] . v2 - > fY ( ) ) ;
2016-03-01 15:47:10 +00:00
int dx = x2 - x1 ;
int dy = y2 - y1 ;
int bx = ( x1 - minx ) > > BLOCKBITS ;
int by = ( y1 - miny ) > > BLOCKBITS ;
int bx2 = ( x2 - minx ) > > BLOCKBITS ;
int by2 = ( y2 - miny ) > > BLOCKBITS ;
block = & BlockLists [ bx + by * bmapwidth ] ;
endblock = & BlockLists [ bx2 + by2 * bmapwidth ] ;
if ( block = = endblock ) // Single block
{
block - > Push ( line ) ;
}
else if ( by = = by2 ) // Horizontal line
{
if ( bx > bx2 )
{
swapvalues ( block , endblock ) ;
}
do
{
block - > Push ( line ) ;
block + = 1 ;
} while ( block < = endblock ) ;
}
else if ( bx = = bx2 ) // Vertical line
{
if ( by > by2 )
{
swapvalues ( block , endblock ) ;
}
do
{
block - > Push ( line ) ;
block + = bmapwidth ;
} while ( block < = endblock ) ;
}
else // Diagonal line
{
int xchange = ( dx < 0 ) ? - 1 : 1 ;
int ychange = ( dy < 0 ) ? - 1 : 1 ;
int ymove = ychange * bmapwidth ;
int adx = abs ( dx ) ;
int ady = abs ( dy ) ;
if ( adx = = ady ) // 45 degrees
{
int xb = ( x1 - minx ) & ( BLOCKSIZE - 1 ) ;
int yb = ( y1 - miny ) & ( BLOCKSIZE - 1 ) ;
if ( dx < 0 )
{
xb = BLOCKSIZE - xb ;
}
if ( dy < 0 )
{
yb = BLOCKSIZE - yb ;
}
if ( xb < yb )
adx - - ;
}
if ( adx > = ady ) // X-major
{
int yadd = dy < 0 ? - 1 : BLOCKSIZE ;
do
{
int stop = ( Scale ( ( by < < BLOCKBITS ) + yadd - ( y1 - miny ) , dx , dy ) + ( x1 - minx ) ) > > BLOCKBITS ;
while ( bx ! = stop )
{
block - > Push ( line ) ;
block + = xchange ;
bx + = xchange ;
}
block - > Push ( line ) ;
block + = ymove ;
by + = ychange ;
} while ( by ! = by2 ) ;
while ( block ! = endblock )
{
block - > Push ( line ) ;
block + = xchange ;
}
block - > Push ( line ) ;
}
else // Y-major
{
int xadd = dx < 0 ? - 1 : BLOCKSIZE ;
do
{
int stop = ( Scale ( ( bx < < BLOCKBITS ) + xadd - ( x1 - minx ) , dy , dx ) + ( y1 - miny ) ) > > BLOCKBITS ;
while ( by ! = stop )
{
block - > Push ( line ) ;
block + = ymove ;
by + = ychange ;
}
block - > Push ( line ) ;
block + = xchange ;
bx + = xchange ;
} while ( bx ! = bx2 ) ;
while ( block ! = endblock )
{
block - > Push ( line ) ;
block + = ymove ;
}
block - > Push ( line ) ;
}
}
}
BlockMap . Reserve ( bmapwidth * bmapheight ) ;
CreatePackedBlockmap ( BlockMap , BlockLists , bmapwidth , bmapheight ) ;
delete [ ] BlockLists ;
2017-03-17 13:24:21 +00:00
level . blockmap . blockmaplump = new int [ BlockMap . Size ( ) ] ;
2016-03-01 15:47:10 +00:00
for ( unsigned int ii = 0 ; ii < BlockMap . Size ( ) ; + + ii )
{
2017-03-17 13:24:21 +00:00
level . blockmap . blockmaplump [ ii ] = BlockMap [ ii ] ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
// P_VerifyBlockMap
//
// haleyjd 03/04/10: do verification on validity of blockmap.
//
2017-03-17 20:54:03 +00:00
//===========================================================================
2017-03-17 13:24:21 +00:00
bool FBlockmap : : VerifyBlockMap ( int count )
2016-03-01 15:47:10 +00:00
{
int x , y ;
int * maxoffs = blockmaplump + count ;
int bmapwidth = blockmaplump [ 2 ] ;
int bmapheight = blockmaplump [ 3 ] ;
for ( y = 0 ; y < bmapheight ; y + + )
{
for ( x = 0 ; x < bmapwidth ; x + + )
{
int offset ;
int * list , * tmplist ;
int * blockoffset ;
offset = y * bmapwidth + x ;
blockoffset = blockmaplump + offset + 4 ;
// check that block offset is in bounds
if ( blockoffset > = maxoffs )
{
2017-03-17 13:24:21 +00:00
Printf ( PRINT_HIGH , " VerifyBlockMap: block offset overflow \n " ) ;
2016-03-01 15:47:10 +00:00
return false ;
}
offset = * blockoffset ;
// check that list offset is in bounds
if ( offset < 4 | | offset > = count )
{
2017-03-17 13:24:21 +00:00
Printf ( PRINT_HIGH , " VerifyBlockMap: list offset overflow \n " ) ;
2016-03-01 15:47:10 +00:00
return false ;
}
list = blockmaplump + offset ;
// scan forward for a -1 terminator before maxoffs
for ( tmplist = list ; ; tmplist + + )
{
// we have overflowed the lump?
if ( tmplist > = maxoffs )
{
2017-03-17 13:24:21 +00:00
Printf ( PRINT_HIGH , " VerifyBlockMap: open blocklist \n " ) ;
2016-03-01 15:47:10 +00:00
return false ;
}
if ( * tmplist = = - 1 ) // found -1
break ;
}
2017-02-01 20:40:47 +00:00
// there's some node builder which carelessly removed the initial 0-entry.
// Rather than second-guessing the intent, let's just discard such blockmaps entirely
// to be on the safe side.
if ( * list ! = 0 )
{
2017-03-17 13:24:21 +00:00
Printf ( PRINT_HIGH , " VerifyBlockMap: first entry is not 0. \n " ) ;
2017-02-01 20:40:47 +00:00
return false ;
}
2016-03-01 15:47:10 +00:00
// scan the list for out-of-range linedef indicies in list
for ( tmplist = list ; * tmplist ! = - 1 ; tmplist + + )
{
2017-01-08 13:39:16 +00:00
if ( ( unsigned ) * tmplist > = level . lines . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2017-03-17 13:24:21 +00:00
Printf ( PRINT_HIGH , " VerifyBlockMap: index >= numlines \n " ) ;
2016-03-01 15:47:10 +00:00
return false ;
}
}
}
}
return true ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
// P_LoadBlockMap
//
// killough 3/1/98: substantially modified to work
// towards removing blockmap limit (a wad limitation)
//
// killough 3/30/98: Rewritten to remove blockmap limit
//
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_LoadBlockMap ( MapData * map )
{
int count = map - > Size ( ML_BLOCKMAP ) ;
if ( ForceNodeBuild | | genblockmap | |
count / 2 > = 0x10000 | | count = = 0 | |
Args - > CheckParm ( " -blockmap " )
)
{
2016-08-28 07:55:04 +00:00
DPrintf ( DMSG_SPAMMY , " Generating BLOCKMAP \n " ) ;
2016-03-01 15:47:10 +00:00
P_CreateBlockMap ( ) ;
}
else
{
2017-03-08 14:20:00 +00:00
uint8_t * data = new uint8_t [ count ] ;
2016-03-01 15:47:10 +00:00
map - > Read ( ML_BLOCKMAP , data ) ;
const short * wadblockmaplump = ( short * ) data ;
int i ;
count / = 2 ;
2017-03-17 13:24:21 +00:00
level . blockmap . blockmaplump = new int [ count ] ;
2016-03-01 15:47:10 +00:00
// killough 3/1/98: Expand wad blockmap into larger internal one,
// by treating all offsets except -1 as unsigned and zero-extending
// them. This potentially doubles the size of blockmaps allowed,
// because Doom originally considered the offsets as always signed.
2017-03-17 13:24:21 +00:00
level . blockmap . blockmaplump [ 0 ] = LittleShort ( wadblockmaplump [ 0 ] ) ;
level . blockmap . blockmaplump [ 1 ] = LittleShort ( wadblockmaplump [ 1 ] ) ;
level . blockmap . blockmaplump [ 2 ] = ( uint32_t ) ( LittleShort ( wadblockmaplump [ 2 ] ) ) & 0xffff ;
level . blockmap . blockmaplump [ 3 ] = ( uint32_t ) ( LittleShort ( wadblockmaplump [ 3 ] ) ) & 0xffff ;
2016-03-01 15:47:10 +00:00
for ( i = 4 ; i < count ; i + + )
{
short t = LittleShort ( wadblockmaplump [ i ] ) ; // killough 3/1/98
2017-03-17 13:24:21 +00:00
level . blockmap . blockmaplump [ i ] = t = = - 1 ? ( uint32_t ) 0xffffffff : ( uint32_t ) t & 0xffff ;
2016-03-01 15:47:10 +00:00
}
delete [ ] data ;
2017-03-17 13:24:21 +00:00
if ( ! level . blockmap . VerifyBlockMap ( count ) )
2016-03-01 15:47:10 +00:00
{
2016-08-28 07:55:04 +00:00
DPrintf ( DMSG_SPAMMY , " Generating BLOCKMAP \n " ) ;
2016-03-01 15:47:10 +00:00
P_CreateBlockMap ( ) ;
}
}
2017-03-17 13:24:21 +00:00
level . blockmap . bmaporgx = level . blockmap . blockmaplump [ 0 ] ;
level . blockmap . bmaporgy = level . blockmap . blockmaplump [ 1 ] ;
level . blockmap . bmapwidth = level . blockmap . blockmaplump [ 2 ] ;
level . blockmap . bmapheight = level . blockmap . blockmaplump [ 3 ] ;
2016-03-01 15:47:10 +00:00
// clear out mobj chains
2017-03-17 13:24:21 +00:00
count = level . blockmap . bmapwidth * level . blockmap . bmapheight ;
level . blockmap . blocklinks = new FBlockNode * [ count ] ;
memset ( level . blockmap . blocklinks , 0 , count * sizeof ( * level . blockmap . blocklinks ) ) ;
level . blockmap . blockmap = level . blockmap . blockmaplump + 4 ;
2016-03-01 15:47:10 +00:00
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
// P_GroupLines
// Builds sector line lists and subsector sector numbers.
// Finds block bounding boxes for sectors.
//
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
static void P_GroupLines ( bool buildmap )
{
cycle_t times [ 16 ] ;
2017-01-05 13:45:15 +00:00
unsigned int * linesDoneInEachSector ;
2016-03-01 15:47:10 +00:00
int total ;
sector_t * sector ;
FBoundingBox bbox ;
bool flaggedNoFronts = false ;
unsigned int jj ;
2017-01-07 18:32:24 +00:00
for ( unsigned i = 0 ; i < countof ( times ) ; + + i )
2016-03-01 15:47:10 +00:00
{
times [ i ] . Reset ( ) ;
}
// look up sector number for each subsector
times [ 0 ] . Clock ( ) ;
2017-03-16 23:22:52 +00:00
for ( auto & sub : level . subsectors )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
sub . sector = sub . firstline - > sidedef - > sector ;
2016-03-01 15:47:10 +00:00
}
2017-03-16 23:22:52 +00:00
for ( auto & sub : level . subsectors )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
for ( jj = 0 ; jj < sub . numlines ; + + jj )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
sub . firstline [ jj ] . Subsector = & sub ;
2016-03-01 15:47:10 +00:00
}
}
times [ 0 ] . Unclock ( ) ;
// count number of lines in each sector
times [ 1 ] . Clock ( ) ;
total = 0 ;
2017-01-08 13:39:16 +00:00
for ( unsigned i = 0 ; i < level . lines . Size ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
2017-01-08 13:39:16 +00:00
auto li = & level . lines [ i ] ;
2016-03-01 15:47:10 +00:00
if ( li - > frontsector = = NULL )
{
if ( ! flaggedNoFronts )
{
flaggedNoFronts = true ;
Printf ( " The following lines do not have a front sidedef: \n " ) ;
}
Printf ( " %d \n " , i ) ;
}
else
{
2017-01-02 20:40:52 +00:00
li - > frontsector - > Lines . Count + + ;
2016-03-01 15:47:10 +00:00
total + + ;
}
if ( li - > backsector & & li - > backsector ! = li - > frontsector )
{
2017-01-02 20:40:52 +00:00
li - > backsector - > Lines . Count + + ;
2016-03-01 15:47:10 +00:00
total + + ;
}
}
if ( flaggedNoFronts )
{
I_Error ( " You need to fix these lines to play this map. \n " ) ;
}
times [ 1 ] . Unclock ( ) ;
// build line tables for each sector
times [ 3 ] . Clock ( ) ;
2017-03-17 11:49:43 +00:00
level . linebuffer . Alloc ( total ) ;
line_t * * lineb_p = & level . linebuffer [ 0 ] ;
2017-01-07 18:32:24 +00:00
auto numsectors = level . sectors . Size ( ) ;
2017-01-05 13:45:15 +00:00
linesDoneInEachSector = new unsigned int [ numsectors ] ;
2016-03-01 15:47:10 +00:00
memset ( linesDoneInEachSector , 0 , sizeof ( int ) * numsectors ) ;
2017-01-07 18:32:24 +00:00
sector = & level . sectors [ 0 ] ;
for ( unsigned i = 0 ; i < numsectors ; i + + , sector + + )
2016-03-01 15:47:10 +00:00
{
2017-01-02 20:40:52 +00:00
if ( sector - > Lines . Count = = 0 )
2016-03-01 15:47:10 +00:00
{
Printf ( " Sector %i (tag %i) has no lines \n " , i , tagManager . GetFirstSectorTag ( sector ) ) ;
// 0 the sector's tag so that no specials can use it
tagManager . RemoveSectorTags ( i ) ;
}
else
{
2017-01-02 20:40:52 +00:00
sector - > Lines . Array = lineb_p ;
lineb_p + = sector - > Lines . Count ;
2016-03-01 15:47:10 +00:00
}
}
2017-01-08 13:39:16 +00:00
for ( unsigned i = 0 ; i < level . lines . Size ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
2017-01-08 13:39:16 +00:00
auto li = & level . lines [ i ] ;
2016-03-01 15:47:10 +00:00
if ( li - > frontsector ! = NULL )
{
2017-01-07 19:02:25 +00:00
li - > frontsector - > Lines [ linesDoneInEachSector [ li - > frontsector - > Index ( ) ] + + ] = li ;
2016-03-01 15:47:10 +00:00
}
if ( li - > backsector ! = NULL & & li - > backsector ! = li - > frontsector )
{
2017-01-07 19:02:25 +00:00
li - > backsector - > Lines [ linesDoneInEachSector [ li - > backsector - > Index ( ) ] + + ] = li ;
2016-03-01 15:47:10 +00:00
}
}
2017-01-07 18:32:24 +00:00
sector = & level . sectors [ 0 ] ;
for ( unsigned i = 0 ; i < numsectors ; + + i , + + sector )
2016-03-01 15:47:10 +00:00
{
2017-01-02 20:40:52 +00:00
if ( linesDoneInEachSector [ i ] ! = sector - > Lines . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2016-06-19 09:48:20 +00:00
I_Error ( " P_GroupLines: miscounted " ) ;
2016-03-01 15:47:10 +00:00
}
2017-01-02 20:40:52 +00:00
if ( sector - > Lines . Size ( ) > 3 )
2016-03-01 15:47:10 +00:00
{
2016-06-19 09:48:20 +00:00
bbox . ClearBox ( ) ;
2017-01-02 20:40:52 +00:00
for ( auto li : sector - > Lines )
2016-03-01 15:47:10 +00:00
{
2016-06-19 09:48:20 +00:00
bbox . AddToBox ( li - > v1 - > fPos ( ) ) ;
bbox . AddToBox ( li - > v2 - > fPos ( ) ) ;
2016-03-01 15:47:10 +00:00
}
2016-06-19 09:48:20 +00:00
// set the center to the middle of the bounding box
sector - > centerspot . X = ( bbox . Right ( ) + bbox . Left ( ) ) / 2 ;
sector - > centerspot . Y = ( bbox . Top ( ) + bbox . Bottom ( ) ) / 2 ;
}
2017-01-02 20:40:52 +00:00
else if ( sector - > Lines . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
2016-06-19 09:48:20 +00:00
// For triangular sectors the above does not calculate good points unless the longest of the triangle's lines is perfectly horizontal and vertical
DVector2 pos = { 0 , 0 } ;
2017-01-02 20:40:52 +00:00
for ( auto ln : sector - > Lines )
2016-03-01 15:47:10 +00:00
{
2017-01-02 20:40:52 +00:00
pos + = ln - > v1 - > fPos ( ) + ln - > v2 - > fPos ( ) ;
2016-03-01 15:47:10 +00:00
}
2017-01-02 20:40:52 +00:00
sector - > centerspot = pos / ( 2 * sector - > Lines . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
}
delete [ ] linesDoneInEachSector ;
times [ 3 ] . Unclock ( ) ;
// [RH] Moved this here
times [ 4 ] . Clock ( ) ;
// killough 1/30/98: Create xref tables for tags
tagManager . HashTags ( ) ;
times [ 4 ] . Unclock ( ) ;
times [ 5 ] . Clock ( ) ;
if ( ! buildmap )
{
P_SetSlopes ( ) ;
}
times [ 5 ] . Unclock ( ) ;
if ( showloadtimes )
{
Printf ( " ---Group Lines Times--- \n " ) ;
2017-01-07 18:32:24 +00:00
for ( int i = 0 ; i < 7 ; + + i )
2016-03-01 15:47:10 +00:00
{
Printf ( " time %d:%9.4f ms \n " , i , times [ i ] . TimeMS ( ) ) ;
}
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
//
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_LoadReject ( MapData * map , bool junk )
{
2017-01-07 18:32:24 +00:00
const int neededsize = ( level . sectors . Size ( ) * level . sectors . Size ( ) + 7 ) > > 3 ;
2016-03-01 15:47:10 +00:00
int rejectsize ;
2018-03-10 20:48:33 +00:00
if ( ! map - > CheckName ( ML_REJECT , " REJECT " ) )
2016-03-01 15:47:10 +00:00
{
rejectsize = 0 ;
}
else
{
rejectsize = junk ? 0 : map - > Size ( ML_REJECT ) ;
}
if ( rejectsize < neededsize )
{
if ( rejectsize > 0 )
{
Printf ( " REJECT is %d byte%s too small. \n " , neededsize - rejectsize ,
neededsize - rejectsize = = 1 ? " " : " s " ) ;
}
2017-03-17 11:49:43 +00:00
level . rejectmatrix . Reset ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
// Check if the reject has some actual content. If not, free it.
rejectsize = MIN ( rejectsize , neededsize ) ;
2017-03-17 11:49:43 +00:00
level . rejectmatrix . Alloc ( rejectsize ) ;
2016-03-01 15:47:10 +00:00
2018-03-10 20:48:33 +00:00
map - > Read ( ML_REJECT , & level . rejectmatrix [ 0 ] , rejectsize ) ;
2016-03-01 15:47:10 +00:00
int qwords = rejectsize / 8 ;
int i ;
if ( qwords > 0 )
{
2017-03-17 11:49:43 +00:00
const uint64_t * qreject = ( const uint64_t * ) & level . rejectmatrix [ 0 ] ;
2016-03-01 15:47:10 +00:00
i = 0 ;
do
{
if ( qreject [ i ] ! = 0 )
return ;
} while ( + + i < qwords ) ;
}
rejectsize & = 7 ;
qwords * = 8 ;
for ( i = 0 ; i < rejectsize ; + + i )
{
2017-03-17 11:49:43 +00:00
if ( level . rejectmatrix [ qwords + i ] ! = 0 )
2016-03-01 15:47:10 +00:00
return ;
}
// Reject has no data, so pretend it isn't there.
2017-03-17 11:49:43 +00:00
level . rejectmatrix . Reset ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
//
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2016-06-12 09:43:40 +00:00
void P_LoadBehavior ( MapData * map )
2016-03-01 15:47:10 +00:00
{
2016-06-12 09:43:40 +00:00
if ( map - > Size ( ML_BEHAVIOR ) > 0 )
2016-03-01 15:47:10 +00:00
{
2018-03-10 20:48:33 +00:00
FBehavior : : StaticLoadModule ( - 1 , & map - > Reader ( ML_BEHAVIOR ) , map - > Size ( ML_BEHAVIOR ) ) ;
2016-06-12 09:43:40 +00:00
}
if ( ! FBehavior : : StaticCheckAllGood ( ) )
{
Printf ( " ACS scripts unloaded. \n " ) ;
FBehavior : : StaticUnloadModules ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void P_GetPolySpots ( MapData * map , TArray < FNodeBuilder : : FPolyStart > & spots , TArray < FNodeBuilder : : FPolyStart > & anchors )
{
//if (map->HasBehavior)
{
for ( unsigned int i = 0 ; i < MapThingsConverted . Size ( ) ; + + i )
{
FDoomEdEntry * mentry = MapThingsConverted [ i ] . info ;
if ( mentry ! = NULL & & mentry - > Type = = NULL & & mentry - > Special > = SMT_PolyAnchor & & mentry - > Special < = SMT_PolySpawnHurt )
{
FNodeBuilder : : FPolyStart newvert ;
2016-03-23 11:21:52 +00:00
newvert . x = FLOAT2FIXED ( MapThingsConverted [ i ] . pos . X ) ;
newvert . y = FLOAT2FIXED ( MapThingsConverted [ i ] . pos . Y ) ;
2016-03-01 15:47:10 +00:00
newvert . polynum = MapThingsConverted [ i ] . angle ;
if ( mentry - > Special = = SMT_PolyAnchor )
{
anchors . Push ( newvert ) ;
}
else
{
spots . Push ( newvert ) ;
}
}
}
}
}
2016-05-01 20:47:36 +00:00
//===========================================================================
//
// P_PrecacheLevel
//
// Preloads all relevant graphics for the level.
//
//===========================================================================
2018-04-24 22:07:46 +00:00
void hw_PrecacheTexture ( uint8_t * texhitlist , TMap < PClassActor * , bool > & actorhitlist ) ;
2016-05-01 20:47:36 +00:00
static void P_PrecacheLevel ( )
{
int i ;
2017-03-08 14:20:00 +00:00
uint8_t * hitlist ;
2016-05-01 20:47:36 +00:00
TMap < PClassActor * , bool > actorhitlist ;
int cnt = TexMan . NumTextures ( ) ;
if ( demoplayback )
return ;
2017-03-08 14:20:00 +00:00
hitlist = new uint8_t [ cnt ] ;
2016-05-01 20:47:36 +00:00
memset ( hitlist , 0 , cnt ) ;
AActor * actor ;
TThinkerIterator < AActor > iterator ;
while ( ( actor = iterator . Next ( ) ) )
{
actorhitlist [ actor - > GetClass ( ) ] = true ;
}
2017-01-10 00:00:06 +00:00
for ( auto n : gameinfo . PrecachedClasses )
{
PClassActor * cls = PClass : : FindActor ( n ) ;
if ( cls ! = NULL ) actorhitlist [ cls ] = true ;
}
2016-05-01 20:47:36 +00:00
for ( unsigned i = 0 ; i < level . info - > PrecacheClasses . Size ( ) ; i + + )
{
// level.info can only store names, no class pointers.
PClassActor * cls = PClass : : FindActor ( level . info - > PrecacheClasses [ i ] ) ;
if ( cls ! = NULL ) actorhitlist [ cls ] = true ;
}
2017-01-07 18:32:24 +00:00
for ( i = level . sectors . Size ( ) - 1 ; i > = 0 ; i - - )
2016-05-01 20:47:36 +00:00
{
2017-01-07 18:32:24 +00:00
hitlist [ level . sectors [ i ] . GetTexture ( sector_t : : floor ) . GetIndex ( ) ] | = FTextureManager : : HIT_Flat ;
hitlist [ level . sectors [ i ] . GetTexture ( sector_t : : ceiling ) . GetIndex ( ) ] | = FTextureManager : : HIT_Flat ;
2016-05-01 20:47:36 +00:00
}
2017-01-08 17:45:30 +00:00
for ( i = level . sides . Size ( ) - 1 ; i > = 0 ; i - - )
2016-05-01 20:47:36 +00:00
{
2017-01-08 17:45:30 +00:00
hitlist [ level . sides [ i ] . GetTexture ( side_t : : top ) . GetIndex ( ) ] | = FTextureManager : : HIT_Wall ;
hitlist [ level . sides [ i ] . GetTexture ( side_t : : mid ) . GetIndex ( ) ] | = FTextureManager : : HIT_Wall ;
hitlist [ level . sides [ i ] . GetTexture ( side_t : : bottom ) . GetIndex ( ) ] | = FTextureManager : : HIT_Wall ;
2016-05-01 20:47:36 +00:00
}
// Sky texture is always present.
// Note that F_SKY1 is the name used to
// indicate a sky floor/ceiling as a flat,
// while the sky texture is stored like
// a wall texture, with an episode dependant
// name.
if ( sky1texture . isValid ( ) )
{
hitlist [ sky1texture . GetIndex ( ) ] | = FTextureManager : : HIT_Sky ;
}
if ( sky2texture . isValid ( ) )
{
hitlist [ sky2texture . GetIndex ( ) ] | = FTextureManager : : HIT_Sky ;
}
2017-01-10 00:00:06 +00:00
for ( auto n : gameinfo . PrecachedTextures )
{
2018-03-25 18:26:16 +00:00
FTextureID tex = TexMan . CheckForTexture ( n , ETextureType : : Wall , FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ReturnFirst ) ;
2017-01-10 00:00:06 +00:00
if ( tex . Exists ( ) ) hitlist [ tex . GetIndex ( ) ] | = FTextureManager : : HIT_Wall ;
}
2016-05-01 20:47:36 +00:00
for ( unsigned i = 0 ; i < level . info - > PrecacheTextures . Size ( ) ; i + + )
{
2018-03-25 18:26:16 +00:00
FTextureID tex = TexMan . CheckForTexture ( level . info - > PrecacheTextures [ i ] , ETextureType : : Wall , FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ReturnFirst ) ;
2016-05-01 20:47:36 +00:00
if ( tex . Exists ( ) ) hitlist [ tex . GetIndex ( ) ] | = FTextureManager : : HIT_Wall ;
}
2018-04-03 22:21:25 +00:00
// This is just a temporary solution, until the hardware renderer's texture manager is in a better state.
2018-04-07 21:30:28 +00:00
if ( ! V_IsHardwareRenderer ( ) )
2018-04-03 22:21:25 +00:00
SWRenderer - > Precache ( hitlist , actorhitlist ) ;
else
2018-04-24 22:07:46 +00:00
hw_PrecacheTexture ( hitlist , actorhitlist ) ;
2016-05-01 20:47:36 +00:00
delete [ ] hitlist ;
}
2016-03-01 15:47:10 +00:00
extern polyblock_t * * PolyBlockMap ;
2018-04-01 20:26:57 +00:00
//==========================================================================
2017-03-17 20:54:03 +00:00
//
//
//
2018-04-01 20:26:57 +00:00
//==========================================================================
2017-03-17 20:54:03 +00:00
2016-03-01 15:47:10 +00:00
void P_FreeLevelData ( )
{
2018-04-01 20:26:57 +00:00
TThinkerIterator < ADynamicLight > it ( STAT_DLIGHT ) ;
auto mo = it . Next ( ) ;
while ( mo )
{
auto next = it . Next ( ) ;
mo - > Destroy ( ) ;
mo = next ;
}
2017-02-03 11:01:15 +00:00
// [ZZ] delete per-map event handlers
E_Shutdown ( true ) ;
2017-01-31 12:46:35 +00:00
MapThingsConverted . Clear ( ) ;
MapThingsUserDataIndex . Clear ( ) ;
MapThingsUserData . Clear ( ) ;
linemap . Clear ( ) ;
FCanvasTextureInfo : : EmptyList ( ) ;
R_FreePastViewers ( ) ;
P_ClearUDMFKeys ( ) ;
// [RH] Clear all ThingID hash chains.
AActor : : ClearTIDHashes ( ) ;
2016-03-01 15:47:10 +00:00
interpolator . ClearInterpolations ( ) ; // [RH] Nothing to interpolate on a fresh level.
FPolyObj : : ClearAllSubsectorLinks ( ) ; // can't be done as part of the polyobj deletion process.
SN_StopAllSequences ( ) ;
DThinker : : DestroyAllThinkers ( ) ;
P_ClearPortals ( ) ;
tagManager . Clear ( ) ;
level . total_monsters = level . total_items = level . total_secrets =
level . killed_monsters = level . found_items = level . found_secrets =
wminfo . maxfrags = 0 ;
2018-04-01 20:26:57 +00:00
// delete allocated data in the level arrays.
2017-01-07 18:32:24 +00:00
if ( level . sectors . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
2017-01-07 18:32:24 +00:00
delete [ ] level . sectors [ 0 ] . e ;
2018-04-01 20:26:57 +00:00
if ( level . sectors [ 0 ] . subsectors )
{
delete [ ] level . sectors [ 0 ] . subsectors ;
level . sectors [ 0 ] . subsectors = nullptr ;
}
2016-03-01 15:47:10 +00:00
}
2017-03-16 23:31:15 +00:00
for ( auto & sub : level . subsectors )
{
if ( sub . BSP ! = nullptr ) delete sub . BSP ;
}
2018-04-01 20:26:57 +00:00
if ( level . sides . Size ( ) > 0 & & level . sides [ 0 ] . segs )
{
delete [ ] level . sides [ 0 ] . segs ;
level . sides [ 0 ] . segs = nullptr ;
}
2017-03-16 23:31:15 +00:00
FBehavior : : StaticUnloadModules ( ) ;
level . segs . Clear ( ) ;
2017-01-07 18:32:24 +00:00
level . sectors . Clear ( ) ;
2017-01-08 13:39:16 +00:00
level . lines . Clear ( ) ;
2017-01-08 17:45:30 +00:00
level . sides . Clear ( ) ;
2017-03-17 11:09:38 +00:00
level . loadsectors . Clear ( ) ;
level . loadlines . Clear ( ) ;
level . loadsides . Clear ( ) ;
2017-01-08 23:46:16 +00:00
level . vertexes . Clear ( ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2017-03-17 11:09:38 +00:00
level . gamenodes . Reset ( ) ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
2017-03-17 11:09:38 +00:00
level . gamesubsectors . Reset ( ) ;
2017-03-17 11:49:43 +00:00
level . rejectmatrix . Clear ( ) ;
level . Zones . Clear ( ) ;
2017-03-17 13:24:21 +00:00
level . blockmap . Clear ( ) ;
2016-03-01 15:47:10 +00:00
if ( PolyBlockMap ! = NULL )
{
2017-03-17 13:24:21 +00:00
for ( int i = level . blockmap . bmapwidth * level . blockmap . bmapheight - 1 ; i > = 0 ; - - i )
2016-03-01 15:47:10 +00:00
{
polyblock_t * link = PolyBlockMap [ i ] ;
while ( link ! = NULL )
{
polyblock_t * next = link - > next ;
delete link ;
link = next ;
}
}
delete [ ] PolyBlockMap ;
PolyBlockMap = NULL ;
}
if ( polyobjs ! = NULL )
{
delete [ ] polyobjs ;
polyobjs = NULL ;
}
po_NumPolyobjs = 0 ;
2017-03-17 11:49:43 +00:00
level . deathmatchstarts . Clear ( ) ;
level . AllPlayerStarts . Clear ( ) ;
memset ( level . playerstarts , 0 , sizeof ( level . playerstarts ) ) ;
2016-03-01 15:47:10 +00:00
P_FreeStrifeConversations ( ) ;
2016-09-20 07:11:13 +00:00
level . Scrolls . Clear ( ) ;
2016-03-01 15:47:10 +00:00
P_ClearUDMFKeys ( ) ;
}
2017-03-17 20:54:03 +00:00
//===========================================================================
//
//
//
//===========================================================================
2017-01-06 12:09:58 +00:00
extern FMemArena secnodearena ;
2016-03-01 15:47:10 +00:00
extern msecnode_t * headsecnode ;
void P_FreeExtraLevelData ( )
{
// Free all blocknodes and msecnodes.
// *NEVER* call this function without calling
// P_FreeLevelData() first, or they might not all be freed.
{
FBlockNode * node = FBlockNode : : FreeBlocks ;
while ( node ! = NULL )
{
FBlockNode * next = node - > NextBlock ;
delete node ;
node = next ;
}
FBlockNode : : FreeBlocks = NULL ;
}
2017-01-06 12:09:58 +00:00
secnodearena . FreeAllBlocks ( ) ;
headsecnode = nullptr ;
2016-03-01 15:47:10 +00:00
}
2017-01-06 12:09:58 +00:00
2017-03-17 20:54:03 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
// P_SetupLevel
//
// [RH] position indicates the start spot to spawn at
2017-03-17 20:54:03 +00:00
//
//===========================================================================
2018-10-31 07:15:56 +00:00
void P_SetupLevel ( const char * lumpname , int position , bool newGame )
2016-03-01 15:47:10 +00:00
{
cycle_t times [ 20 ] ;
2018-03-10 20:48:33 +00:00
#if 0
2016-03-01 15:47:10 +00:00
FMapThing * buildthings ;
int numbuildthings ;
2018-03-10 20:48:33 +00:00
# endif
2016-03-01 15:47:10 +00:00
int i ;
bool buildmap ;
const int * oldvertextable = NULL ;
2017-12-07 21:31:27 +00:00
level . ShaderStartTime = I_msTimeFS ( ) ; // indicate to the shader system that the level just started
2017-11-15 16:06:21 +00:00
2016-03-01 15:47:10 +00:00
// This is motivated as follows:
2018-03-30 22:33:09 +00:00
bool RequireGLNodes = true ; // Even the software renderer needs GL nodes now.
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < ( int ) countof ( times ) ; + + i )
{
times [ i ] . Reset ( ) ;
}
level . maptype = MAPTYPE_UNKNOWN ;
wminfo . partime = 180 ;
if ( ! savegamerestore )
{
2018-03-24 12:06:37 +00:00
level . SetMusicVolume ( level . MusicVolume ) ;
2016-03-01 15:47:10 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
players [ i ] . killcount = players [ i ] . secretcount
= players [ i ] . itemcount = 0 ;
}
}
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
players [ i ] . mo = NULL ;
}
// [RH] Clear any scripted translation colors the previous level may have set.
for ( i = 0 ; i < int ( translationtables [ TRANSLATION_LevelScripted ] . Size ( ) ) ; + + i )
{
FRemapTable * table = translationtables [ TRANSLATION_LevelScripted ] [ i ] ;
if ( table ! = NULL )
{
delete table ;
translationtables [ TRANSLATION_LevelScripted ] [ i ] = NULL ;
}
}
translationtables [ TRANSLATION_LevelScripted ] . Clear ( ) ;
// Initial height of PointOfView will be set by player think.
2016-03-23 09:42:41 +00:00
players [ consoleplayer ] . viewz = NO_VALUE ;
2016-03-01 15:47:10 +00:00
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ( ) ;
// [RH] clear out the mid-screen message
C_MidPrint ( NULL , NULL ) ;
// Free all level data from the previous map
P_FreeLevelData ( ) ;
MapData * map = P_OpenMapData ( lumpname , true ) ;
if ( map = = NULL )
{
I_Error ( " Unable to open map '%s' \n " , lumpname ) ;
}
2017-02-03 07:04:01 +00:00
// [ZZ] init per-map static handlers. we need to call this before everything is set up because otherwise scripts don't receive PlayerEntered event
// (which happens at god-knows-what stage in this function, but definitely not the last part, because otherwise it'd work to put E_InitStaticHandlers before the player spawning)
E_InitStaticHandlers ( true ) ;
2016-09-20 07:11:13 +00:00
// generate a checksum for the level, to be included and checked with savegames.
map - > GetChecksum ( level . md5 ) ;
2016-03-01 15:47:10 +00:00
// find map num
level . lumpnum = map - > lumpnum ;
hasglnodes = false ;
2018-10-31 07:15:56 +00:00
if ( newGame )
{
2018-10-31 14:52:39 +00:00
E_NewGame ( EventHandlerType : : PerMap ) ;
2018-10-31 07:15:56 +00:00
}
2016-03-01 15:47:10 +00:00
// [RH] Support loading Build maps (because I felt like it. :-)
buildmap = false ;
2018-03-10 20:48:33 +00:00
#if 0
// deactivated because broken.
2016-03-01 15:47:10 +00:00
if ( map - > Size ( 0 ) > 0 )
{
2017-03-08 14:20:00 +00:00
uint8_t * mapdata = new uint8_t [ map - > Size ( 0 ) ] ;
2018-03-10 20:48:33 +00:00
map - > Read ( 0 , mapdata ) ;
2016-03-01 15:47:10 +00:00
times [ 0 ] . Clock ( ) ;
buildmap = P_LoadBuildMap ( mapdata , map - > Size ( 0 ) , & buildthings , & numbuildthings ) ;
times [ 0 ] . Unclock ( ) ;
delete [ ] mapdata ;
}
2018-03-10 20:48:33 +00:00
# endif
2016-03-01 15:47:10 +00:00
if ( ! buildmap )
{
// note: most of this ordering is important
ForceNodeBuild = gennodes ;
// [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 ;
}
2018-04-09 20:09:28 +00:00
FName checksum = CheckCompatibility ( map ) ;
2016-03-01 15:47:10 +00:00
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 ;
if ( ! map - > isText )
{
times [ 0 ] . Clock ( ) ;
P_LoadVertexes ( map ) ;
times [ 0 ] . Unclock ( ) ;
// Check for maps without any BSP data at all (e.g. SLIGE)
times [ 1 ] . Clock ( ) ;
P_LoadSectors ( map , missingtex ) ;
times [ 1 ] . Unclock ( ) ;
times [ 2 ] . Clock ( ) ;
times [ 2 ] . Unclock ( ) ;
times [ 3 ] . Clock ( ) ;
if ( ! map - > HasBehavior )
P_LoadLineDefs ( map ) ;
else
P_LoadLineDefs2 ( map ) ; // [RH] Load Hexen-style linedefs
times [ 3 ] . Unclock ( ) ;
times [ 4 ] . Clock ( ) ;
P_LoadSideDefs2 ( map , missingtex ) ;
times [ 4 ] . Unclock ( ) ;
times [ 5 ] . Clock ( ) ;
P_FinishLoadingLineDefs ( ) ;
times [ 5 ] . Unclock ( ) ;
if ( ! map - > HasBehavior )
P_LoadThings ( map ) ;
else
P_LoadThings2 ( map ) ; // [RH] Load Hexen-style things
}
else
{
times [ 0 ] . Clock ( ) ;
P_ParseTextMap ( map , missingtex ) ;
times [ 0 ] . Unclock ( ) ;
}
2018-04-09 20:09:28 +00:00
SetCompatibilityParams ( checksum ) ;
2017-12-24 08:33:29 +00:00
2016-03-01 15:47:10 +00:00
times [ 6 ] . Clock ( ) ;
P_LoopSidedefs ( true ) ;
times [ 6 ] . Unclock ( ) ;
linemap . Clear ( ) ;
linemap . ShrinkToFit ( ) ;
SummarizeMissingTextures ( missingtex ) ;
}
else
{
ForceNodeBuild = true ;
level . maptype = MAPTYPE_BUILD ;
}
bool reloop = false ;
if ( ! ForceNodeBuild )
{
// Check for compressed nodes first, then uncompressed nodes
2018-03-11 17:32:00 +00:00
FileReader * fr = nullptr ;
2017-03-08 14:20:00 +00:00
uint32_t id = MAKE_ID ( ' X ' , ' x ' , ' X ' , ' x ' ) , idcheck = 0 , idcheck2 = 0 , idcheck3 = 0 , idcheck4 = 0 , idcheck5 = 0 , idcheck6 = 0 ;
2016-03-01 15:47:10 +00:00
if ( map - > Size ( ML_ZNODES ) ! = 0 )
{
// Test normal nodes first
2018-03-10 20:48:33 +00:00
fr = & map - > Reader ( ML_ZNODES ) ;
2016-03-01 15:47:10 +00:00
idcheck = MAKE_ID ( ' Z ' , ' N ' , ' O ' , ' D ' ) ;
idcheck2 = MAKE_ID ( ' X ' , ' N ' , ' O ' , ' D ' ) ;
}
else if ( map - > Size ( ML_GLZNODES ) ! = 0 )
{
2018-03-10 20:48:33 +00:00
fr = & map - > Reader ( ML_GLZNODES ) ;
2016-03-01 15:47:10 +00:00
idcheck = MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' N ' ) ;
idcheck2 = MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' 2 ' ) ;
idcheck3 = MAKE_ID ( ' Z ' , ' G ' , ' L ' , ' 3 ' ) ;
idcheck4 = MAKE_ID ( ' X ' , ' G ' , ' L ' , ' N ' ) ;
idcheck5 = MAKE_ID ( ' X ' , ' G ' , ' L ' , ' 2 ' ) ;
idcheck6 = MAKE_ID ( ' X ' , ' G ' , ' L ' , ' 3 ' ) ;
}
2018-03-11 12:26:30 +00:00
if ( fr ! = nullptr & & fr - > isOpen ( ) ) fr - > Read ( & id , 4 ) ;
2016-03-01 15:47:10 +00:00
if ( id ! = 0 & & ( id = = idcheck | | id = = idcheck2 | | id = = idcheck3 | | id = = idcheck4 | | id = = idcheck5 | | id = = idcheck6 ) )
{
try
{
2018-03-10 20:48:33 +00:00
P_LoadZNodes ( * fr , id ) ;
2016-03-01 15:47:10 +00:00
}
catch ( CRecoverableError & error )
{
Printf ( " Error loading nodes: %s \n " , error . GetMessage ( ) ) ;
ForceNodeBuild = true ;
2017-03-16 23:22:52 +00:00
level . subsectors . Clear ( ) ;
level . segs . Clear ( ) ;
2017-03-17 00:42:37 +00:00
level . nodes . Clear ( ) ;
2016-03-01 15:47:10 +00:00
}
}
else if ( ! map - > isText ) // regular nodes are not supported for text maps
{
// If all 3 node related lumps are empty there's no need to output a message.
// This just means that the map has no nodes and the engine is supposed to build them.
if ( map - > Size ( ML_SEGS ) ! = 0 | | map - > Size ( ML_SSECTORS ) ! = 0 | | map - > Size ( ML_NODES ) ! = 0 )
{
if ( ! P_CheckV4Nodes ( map ) )
{
times [ 7 ] . Clock ( ) ;
P_LoadSubsectors < mapsubsector_t , mapseg_t > ( map ) ;
times [ 7 ] . Unclock ( ) ;
times [ 8 ] . Clock ( ) ;
if ( ! ForceNodeBuild ) P_LoadNodes < mapnode_t , mapsubsector_t > ( map ) ;
times [ 8 ] . Unclock ( ) ;
times [ 9 ] . Clock ( ) ;
if ( ! ForceNodeBuild ) P_LoadSegs < mapseg_t > ( map ) ;
times [ 9 ] . Unclock ( ) ;
}
else
{
times [ 7 ] . Clock ( ) ;
P_LoadSubsectors < mapsubsector4_t , mapseg4_t > ( map ) ;
times [ 7 ] . Unclock ( ) ;
times [ 8 ] . Clock ( ) ;
if ( ! ForceNodeBuild ) P_LoadNodes < mapnode4_t , mapsubsector4_t > ( map ) ;
times [ 8 ] . Unclock ( ) ;
times [ 9 ] . Clock ( ) ;
if ( ! ForceNodeBuild ) P_LoadSegs < mapseg4_t > ( map ) ;
times [ 9 ] . Unclock ( ) ;
}
}
else ForceNodeBuild = true ;
}
else ForceNodeBuild = true ;
// If loading the regular nodes failed try GL nodes before considering a rebuild
if ( ForceNodeBuild )
{
if ( P_LoadGLNodes ( map ) )
{
ForceNodeBuild = false ;
reloop = true ;
}
}
}
else reloop = true ;
2017-11-16 01:33:08 +00:00
uint64_t startTime = 0 , endTime = 0 ;
2016-03-01 15:47:10 +00:00
bool BuildGLNodes ;
if ( ForceNodeBuild )
{
BuildGLNodes = RequireGLNodes | | multiplayer | | demoplayback | | demorecording | | genglnodes ;
2017-11-12 23:54:32 +00:00
startTime = I_msTime ( ) ;
2016-03-01 15:47:10 +00:00
TArray < FNodeBuilder : : FPolyStart > polyspots , anchors ;
P_GetPolySpots ( map , polyspots , anchors ) ;
FNodeBuilder : : FLevel leveldata =
{
2017-01-08 23:46:16 +00:00
& level . vertexes [ 0 ] , ( int ) level . vertexes . Size ( ) ,
2017-01-08 17:45:30 +00:00
& level . sides [ 0 ] , ( int ) level . sides . Size ( ) ,
2017-01-08 13:39:16 +00:00
& level . lines [ 0 ] , ( int ) level . lines . Size ( ) ,
2016-03-01 15:47:10 +00:00
0 , 0 , 0 , 0
} ;
leveldata . FindMapBounds ( ) ;
// We need GL nodes if am_textured is on.
// In case a sync critical game mode is started, also build GL nodes to avoid problems
// if the different machines' am_textured setting differs.
FNodeBuilder builder ( leveldata , polyspots , anchors , BuildGLNodes ) ;
2017-03-17 00:42:37 +00:00
builder . Extract ( level ) ;
2017-11-12 23:54:32 +00:00
endTime = I_msTime ( ) ;
2017-03-16 23:22:52 +00:00
DPrintf ( DMSG_NOTIFY , " BSP generation took %.3f sec (%d segs) \n " , ( endTime - startTime ) * 0.001 , level . segs . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
oldvertextable = builder . GetOldVertexTable ( ) ;
reloop = true ;
}
else
{
BuildGLNodes = false ;
// Older ZDBSPs had problems with compressed sidedefs and assigned wrong sides to the segs if both sides were the same sidedef.
2017-03-16 23:22:52 +00:00
for ( auto & seg : level . segs )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
if ( seg . backsector = = seg . frontsector & & seg . linedef )
2016-03-01 15:47:10 +00:00
{
2017-03-16 23:22:52 +00:00
double d1 = ( seg . v1 - > fPos ( ) - seg . linedef - > v1 - > fPos ( ) ) . LengthSquared ( ) ;
double d2 = ( seg . v2 - > fPos ( ) - seg . linedef - > v1 - > fPos ( ) ) . LengthSquared ( ) ;
2016-03-01 15:47:10 +00:00
if ( d2 < d1 ) // backside
{
2017-03-16 23:22:52 +00:00
seg . sidedef = seg . linedef - > sidedef [ 1 ] ;
2016-03-01 15:47:10 +00:00
}
else // front side
{
2017-03-16 23:22:52 +00:00
seg . sidedef = seg . linedef - > sidedef [ 0 ] ;
2016-03-01 15:47:10 +00:00
}
}
}
}
if ( RequireGLNodes )
{
// Build GL nodes if we want a textured automap or GL nodes are forced to be built.
// If the original nodes being loaded are not GL nodes they will be kept around for
// use in P_PointInSubsector to avoid problems with maps that depend on the specific
// nodes they were built with (P:AR E1M3 is a good example for a map where this is the case.)
2017-11-16 01:33:08 +00:00
reloop | = P_CheckNodes ( map , BuildGLNodes , ( uint32_t ) ( endTime - startTime ) ) ;
2016-03-01 15:47:10 +00:00
hasglnodes = true ;
}
else
{
hasglnodes = P_CheckForGLNodes ( ) ;
}
2017-03-17 11:09:38 +00:00
// set the head node for gameplay purposes. If the separate gamenodes array is not empty, use that, otherwise use the render nodes.
2017-03-18 21:44:03 +00:00
level . headgamenode = level . gamenodes . Size ( ) > 0 ? & level . gamenodes [ level . gamenodes . Size ( ) - 1 ] : level . nodes . Size ( ) ? & level . nodes [ level . nodes . Size ( ) - 1 ] : nullptr ;
2017-03-17 11:09:38 +00:00
2016-03-01 15:47:10 +00:00
times [ 10 ] . Clock ( ) ;
P_LoadBlockMap ( map ) ;
times [ 10 ] . Unclock ( ) ;
times [ 11 ] . Clock ( ) ;
P_LoadReject ( map , buildmap ) ;
times [ 11 ] . Unclock ( ) ;
times [ 12 ] . Clock ( ) ;
P_GroupLines ( buildmap ) ;
times [ 12 ] . Unclock ( ) ;
times [ 13 ] . Clock ( ) ;
P_FloodZones ( ) ;
times [ 13 ] . Unclock ( ) ;
if ( hasglnodes )
{
P_SetRenderSector ( ) ;
}
bodyqueslot = 0 ;
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one.
for ( i = 0 ; i < BODYQUESIZE ; i + + )
bodyque [ i ] = NULL ;
if ( ! buildmap )
{
// [RH] Spawn slope creating things first.
P_SpawnSlopeMakers ( & MapThingsConverted [ 0 ] , & MapThingsConverted [ MapThingsConverted . Size ( ) ] , oldvertextable ) ;
P_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 ( ) ;
}
2018-03-10 20:48:33 +00:00
#if 0 // There is no such thing as a build map.
2016-03-01 15:47:10 +00:00
else
{
for ( i = 0 ; i < numbuildthings ; + + i )
{
SpawnMapThing ( i , & buildthings [ i ] , 0 ) ;
}
delete [ ] buildthings ;
}
2018-03-10 20:48:33 +00:00
# endif
2016-03-01 15:47:10 +00:00
delete map ;
if ( oldvertextable ! = NULL )
{
delete [ ] oldvertextable ;
}
// set up world state
P_SpawnSpecials ( ) ;
2016-12-28 00:09:42 +00:00
// disable reflective planes on sloped sectors.
2017-01-07 18:32:24 +00:00
for ( auto & sec : level . sectors )
2016-12-28 00:09:42 +00:00
{
2017-01-07 18:32:24 +00:00
if ( sec . floorplane . isSlope ( ) ) sec . reflect [ sector_t : : floor ] = 0 ;
if ( sec . ceilingplane . isSlope ( ) ) sec . reflect [ sector_t : : ceiling ] = 0 ;
2016-12-28 00:09:42 +00:00
}
2017-03-17 00:42:37 +00:00
for ( auto & node : level . nodes )
{
double fdx = FIXED2DBL ( node . dx ) ;
double fdy = FIXED2DBL ( node . dy ) ;
node . len = ( float ) g_sqrt ( fdx * fdx + fdy * fdy ) ;
}
2016-12-28 00:09:42 +00:00
2016-03-01 15:47:10 +00:00
// This must be done BEFORE the PolyObj Spawn!!!
2018-04-03 20:50:47 +00:00
InitRenderInfo ( ) ; // create hardware independent renderer resources for the level.
2018-10-28 13:25:29 +00:00
screen - > mVertexData - > CreateVBO ( ) ;
2018-04-03 20:50:47 +00:00
SWRenderer - > SetColormap ( ) ; //The SW renderer needs to do some special setup for the level's default colormap.
2018-04-01 22:14:53 +00:00
InitPortalGroups ( ) ;
2018-10-28 22:08:38 +00:00
P_InitHealthGroups ( ) ;
2016-03-01 15:47:10 +00:00
times [ 16 ] . Clock ( ) ;
if ( reloop ) P_LoopSidedefs ( false ) ;
PO_Init ( ) ; // Initialize the polyobjs
2018-06-11 08:55:49 +00:00
if ( ! level . IsReentering ( ) )
2018-04-15 07:52:23 +00:00
P_FinalizePortals ( ) ; // finalize line portals after polyobjects have been initialized. This info is needed for properly flagging them.
2016-03-01 15:47:10 +00:00
times [ 16 ] . Unclock ( ) ;
assert ( sidetemp ! = NULL ) ;
delete [ ] sidetemp ;
sidetemp = NULL ;
// if deathmatch, randomly spawn the active players
if ( deathmatch )
{
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
{
players [ i ] . mo = NULL ;
G_DeathMatchSpawnPlayer ( i ) ;
}
}
}
// the same, but for random single/coop player starts
else if ( level . flags2 & LEVEL2_RANDOMPLAYERSTARTS )
{
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
if ( playeringame [ i ] )
{
players [ i ] . mo = NULL ;
FPlayerStart * mthing = G_PickPlayerStart ( i ) ;
P_SpawnPlayer ( mthing , i , ( level . flags2 & LEVEL2_PRERAISEWEAPON ) ? SPF_WEAPONFULLYUP : 0 ) ;
}
}
}
2017-03-14 08:02:40 +00:00
// [SP] move unfriendly players around
// horribly hacky - yes, this needs rewritten.
2018-02-20 10:35:18 +00:00
if ( level . deathmatchstarts . Size ( ) > 0 )
2017-03-14 08:02:40 +00:00
{
2018-02-20 10:35:18 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
2017-03-14 08:02:40 +00:00
{
2018-02-20 10:35:18 +00:00
if ( playeringame [ i ] & & players [ i ] . mo ! = NULL )
2017-03-14 08:02:40 +00:00
{
2018-02-20 10:35:18 +00:00
if ( ! ( players [ i ] . mo - > flags & MF_FRIENDLY ) )
{
AActor * oldSpawn = players [ i ] . mo ;
G_DeathMatchSpawnPlayer ( i ) ;
oldSpawn - > Destroy ( ) ;
}
2017-03-14 08:02:40 +00:00
}
}
}
2016-03-01 15:47:10 +00:00
// Don't count monsters in end-of-level sectors if option is on
if ( dmflags2 & DF2_NOCOUNTENDMONST )
{
TThinkerIterator < AActor > it ;
AActor * mo ;
while ( ( mo = it . Next ( ) ) )
{
if ( mo - > flags & MF_COUNTKILL )
{
if ( mo - > Sector - > damageamount > 0 & & ( mo - > Sector - > Flags & ( SECF_ENDGODMODE | SECF_ENDLEVEL ) ) = = ( SECF_ENDGODMODE | SECF_ENDLEVEL ) )
{
mo - > ClearCounters ( ) ;
}
}
}
}
T_PreprocessScripts ( ) ; // preprocess FraggleScript scripts
// build subsector connect matrix
// UNUSED P_ConnectSubsectors ();
R_OldBlend = 0xffffffff ;
// [RH] Remove all particles
P_ClearParticles ( ) ;
times [ 17 ] . Clock ( ) ;
// preload graphics and sounds
if ( precache )
{
2016-05-01 20:47:36 +00:00
P_PrecacheLevel ( ) ;
2016-03-01 15:47:10 +00:00
S_PrecacheLevel ( ) ;
}
times [ 17 ] . Unclock ( ) ;
if ( deathmatch )
{
AnnounceGameStart ( ) ;
}
2018-05-01 07:47:09 +00:00
// This check was previously done at run time each time the heightsec was checked.
// However, since 3D floors are static data, we can easily precalculate this and store it in the sector's flags for quick access.
for ( auto & s : level . sectors )
{
if ( s . heightsec ! = nullptr )
{
// If any of these 3D floors render their planes, ignore heightsec.
for ( auto & ff : s . e - > XFloor . ffloors )
{
if ( ( ff - > flags & ( FF_EXISTS | FF_RENDERPLANES ) ) = = ( FF_EXISTS | FF_RENDERPLANES ) )
{
2018-05-01 09:29:29 +00:00
s . MoreFlags | = SECMF_IGNOREHEIGHTSEC ; // mark the heightsec inactive.
2018-05-01 07:47:09 +00:00
}
}
}
}
2016-03-01 15:47:10 +00:00
P_ResetSightCounters ( true ) ;
//Printf ("free memory: 0x%x\n", Z_FreeMemory());
if ( showloadtimes )
{
Printf ( " ---Total load times--- \n " ) ;
for ( i = 0 ; i < 18 ; + + i )
{
static const char * timenames [ ] =
{
" load vertexes " ,
" load sectors " ,
" load sides " ,
" load lines " ,
" load sides 2 " ,
" load lines 2 " ,
" loop sides " ,
" load subsectors " ,
" load nodes " ,
" load segs " ,
" load blockmap " ,
" load reject " ,
" group lines " ,
" flood zones " ,
" load things " ,
" translate teleports " ,
" init polys " ,
" precache "
} ;
Printf ( " Time%3d:%9.4f ms (%s) \n " , i , times [ i ] . TimeMS ( ) , timenames [ i ] ) ;
}
}
MapThingsConverted . Clear ( ) ;
MapThingsUserDataIndex . Clear ( ) ;
MapThingsUserData . Clear ( ) ;
2017-03-17 11:09:38 +00:00
// Create a backup of the map data so the savegame code can toss out all fields that haven't changed in order to reduce processing time and file size.
2017-03-18 12:25:22 +00:00
// Note that we want binary identity here, so assignment is not sufficient because it won't initialize any padding bytes.
// Note that none of these structures may contain non POD fields anyway.
level . loadsectors . Resize ( level . sectors . Size ( ) ) ;
memcpy ( & level . loadsectors [ 0 ] , & level . sectors [ 0 ] , level . sectors . Size ( ) * sizeof ( level . sectors [ 0 ] ) ) ;
level . loadlines . Resize ( level . lines . Size ( ) ) ;
memcpy ( & level . loadlines [ 0 ] , & level . lines [ 0 ] , level . lines . Size ( ) * sizeof ( level . lines [ 0 ] ) ) ;
level . loadsides . Resize ( level . sides . Size ( ) ) ;
memcpy ( & level . loadsides [ 0 ] , & level . sides [ 0 ] , level . sides . Size ( ) * sizeof ( level . sides [ 0 ] ) ) ;
2016-03-01 15:47:10 +00:00
}
//
// P_Init
//
void P_Init ( )
{
atterm ( P_Shutdown ) ;
P_InitEffects ( ) ; // [RH]
P_InitTerrainTypes ( ) ;
P_InitKeyMessages ( ) ;
R_InitSprites ( ) ;
}
static void P_Shutdown ( )
2018-10-31 07:15:56 +00:00
{
DThinker : : DestroyThinkersInList ( STAT_STATIC ) ;
2016-03-01 15:47:10 +00:00
P_DeinitKeyMessages ( ) ;
P_FreeLevelData ( ) ;
P_FreeExtraLevelData ( ) ;
2018-10-31 07:15:56 +00:00
// [ZZ] delete global event handlers
E_Shutdown ( false ) ;
2016-03-01 15:47:10 +00:00
ST_Clear ( ) ;
2017-01-12 21:49:18 +00:00
FS_Close ( ) ;
for ( auto & p : players )
{
if ( p . psprites ! = nullptr ) p . psprites - > Destroy ( ) ;
}
2016-03-01 15:47:10 +00:00
}
#if 0
# include "c_dispatch.h"
CCMD ( lineloc )
{
if ( argv . argc ( ) ! = 2 )
{
return ;
}
int linenum = atoi ( argv [ 1 ] ) ;
if ( linenum < 0 | | linenum > = numlines )
{
Printf ( " No such line \n " ) ;
}
2016-04-01 15:57:16 +00:00
Printf ( " (%f,%f) -> (%f,%f) \n " , lines [ linenum ] . v1 - > fX ( ) ,
lines [ linenum ] . v1 - > fY ( ) ,
lines [ linenum ] . v2 - > fX ( ) ,
lines [ linenum ] . v2 - > fY ( ) ) ;
2016-03-01 15:47:10 +00:00
}
# endif
2018-03-11 17:32:00 +00:00
2018-04-01 16:45:27 +00:00
//==========================================================================
//
// dumpgeometry
//
//==========================================================================
CCMD ( dumpgeometry )
{
for ( auto & sector : level . sectors )
{
Printf ( PRINT_LOG , " Sector %d \n " , sector . sectornum ) ;
for ( int j = 0 ; j < sector . subsectorcount ; j + + )
{
subsector_t * sub = sector . subsectors [ j ] ;
Printf ( PRINT_LOG , " Subsector %d - real sector = %d - %s \n " , int ( sub - > Index ( ) ) , sub - > sector - > sectornum , sub - > hacked & 1 ? " hacked " : " " ) ;
for ( uint32_t k = 0 ; k < sub - > numlines ; k + + )
{
seg_t * seg = sub - > firstline + k ;
if ( seg - > linedef )
{
Printf ( PRINT_LOG , " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d " ,
seg - > v1 - > fX ( ) , seg - > v1 - > fY ( ) , seg - > v2 - > fX ( ) , seg - > v2 - > fY ( ) ,
seg - > Index ( ) , seg - > linedef - > Index ( ) , seg - > sidedef ! = seg - > linedef - > sidedef [ 0 ] ) ;
}
else
{
Printf ( PRINT_LOG , " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg " ,
seg - > v1 - > fX ( ) , seg - > v1 - > fY ( ) , seg - > v2 - > fX ( ) , seg - > v2 - > fY ( ) , seg - > Index ( ) ) ;
}
if ( seg - > PartnerSeg )
{
subsector_t * sub2 = seg - > PartnerSeg - > Subsector ;
Printf ( PRINT_LOG , " , back sector = %d, real back sector = %d " , sub2 - > render_sector - > sectornum , seg - > PartnerSeg - > frontsector - > sectornum ) ;
}
else if ( seg - > backsector )
{
Printf ( PRINT_LOG , " , back sector = %d (no partnerseg) " , seg - > backsector - > sectornum ) ;
}
Printf ( PRINT_LOG , " \n " ) ;
}
}
}
2018-04-15 07:52:23 +00:00
}