2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2020-02-19 22:08:45 +00:00
// Copyright (C) 1999-2020 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file p_setup.c
/// \brief Do all the WAD I/O, get map description, set up initial state and misc. LUTs
# include "doomdef.h"
# include "d_main.h"
# include "byteptr.h"
# include "g_game.h"
# include "p_local.h"
# include "p_setup.h"
# include "p_spec.h"
# include "p_saveg.h"
# include "i_sound.h" // for I_PlayCD()..
# include "i_video.h" // for I_FinishUpdate()..
# include "r_sky.h"
# include "i_system.h"
# include "r_data.h"
2020-03-09 13:54:56 +00:00
# include "r_things.h" // for R_AddSpriteDefs
2019-10-28 18:28:42 +00:00
# include "r_patch.h"
2014-03-15 16:59:03 +00:00
# include "r_sky.h"
# include "r_draw.h"
# include "s_sound.h"
# include "st_stuff.h"
# include "w_wad.h"
# include "z_zone.h"
# include "r_splats.h"
# include "hu_stuff.h"
# include "console.h"
# include "m_misc.h"
# include "m_fixed.h"
# include "m_random.h"
# include "dehacked.h" // for map headers
# include "r_main.h"
# include "m_cond.h" // for emblems
# include "m_argv.h"
# include "p_polyobj.h"
# include "v_video.h"
2017-05-08 16:28:48 +00:00
# include "filesrch.h" // refreshdirmenu
2018-06-14 19:17:31 +00:00
# include "lua_hud.h" // level title
# include "f_finale.h" // wipes
2014-03-15 16:59:03 +00:00
# include "md5.h" // map MD5
// for LUAh_MapLoad
# include "lua_script.h"
# include "lua_hook.h"
2017-09-29 22:25:34 +00:00
# ifdef _WIN32
2014-03-15 16:59:03 +00:00
# include <malloc.h>
# include <math.h>
# endif
# ifdef HWRENDER
# include "hardware/hw_main.h"
# include "hardware/hw_light.h"
2019-09-03 21:27:22 +00:00
# include "hardware/hw_model.h"
2014-03-15 16:59:03 +00:00
# endif
2015-04-19 21:54:20 +00:00
# include "p_slopes.h"
2019-12-30 10:33:22 +00:00
# include "fastcmp.h" // textmap parsing
2014-03-15 16:59:03 +00:00
//
// Map MD5, calculated on level load.
// Sent to clients in PT_SERVERINFO.
//
unsigned char mapmd5 [ 16 ] ;
//
// MAP related Lookup tables.
// Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
//
2020-03-15 15:23:15 +00:00
boolean udmf ;
2014-03-15 16:59:03 +00:00
size_t numvertexes , numsegs , numsectors , numsubsectors , numnodes , numlines , numsides , nummapthings ;
vertex_t * vertexes ;
seg_t * segs ;
sector_t * sectors ;
subsector_t * subsectors ;
node_t * nodes ;
line_t * lines ;
side_t * sides ;
mapthing_t * mapthings ;
2019-12-23 11:30:01 +00:00
sector_t * spawnsectors ;
line_t * spawnlines ;
side_t * spawnsides ;
2014-03-15 16:59:03 +00:00
INT32 numstarposts ;
2019-07-31 22:17:17 +00:00
UINT16 bossdisabled ;
2019-11-08 15:47:12 +00:00
boolean stoppedclock ;
2014-03-15 16:59:03 +00:00
boolean levelloading ;
2018-06-14 21:23:20 +00:00
UINT8 levelfadecol ;
2014-03-15 16:59:03 +00:00
// BLOCKMAP
// Created from axis aligned bounding box
// of the map, a rectangular array of
// blocks of size ...
// Used to speed up collision detection
// by spatial subdivision in 2D.
//
// Blockmap size.
INT32 bmapwidth , bmapheight ; // size in mapblocks
INT32 * blockmap ; // INT32 for large maps
// offsets in blockmap are from here
INT32 * blockmaplump ; // Big blockmap
// origin of block map
fixed_t bmaporgx , bmaporgy ;
// for thing chains
mobj_t * * blocklinks ;
// REJECT
// For fast sight rejection.
// Speeds up enemy AI by skipping detailed LineOf Sight calculation.
// Without special effect, this could be used as a PVS lookup as well.
//
UINT8 * rejectmatrix ;
// Maintain single and multi player starting spots.
INT32 numdmstarts , numcoopstarts , numredctfstarts , numbluectfstarts ;
mapthing_t * deathmatchstarts [ MAX_DM_STARTS ] ;
mapthing_t * playerstarts [ MAXPLAYERS ] ;
mapthing_t * bluectfstarts [ MAXPLAYERS ] ;
mapthing_t * redctfstarts [ MAXPLAYERS ] ;
/** Logs an error about a map being corrupt, then terminate.
* This allows reporting highly technical errors for usefulness , without
* confusing a novice map designer who simply needs to run ZenNode .
*
* If logging is disabled in this compile , or the log file is not opened , the
* full technical details are printed in the I_Error ( ) message .
*
* \ param msg The message to log . This message can safely result from a call
* to va ( ) , since that function is not used here .
* \ todo Fix the I_Error ( ) message . On some implementations the logfile may
* not be called log . txt .
* \ sa CON_LogMessage , I_Error
*/
FUNCNORETURN static ATTRNORETURN void CorruptMapError ( const char * msg )
{
// don't use va() because the calling function probably uses it
char mapnum [ 10 ] ;
sprintf ( mapnum , " %hd " , gamemap ) ;
CON_LogMessage ( " Map " ) ;
CON_LogMessage ( mapnum ) ;
CON_LogMessage ( " is corrupt: " ) ;
CON_LogMessage ( msg ) ;
CON_LogMessage ( " \n " ) ;
I_Error ( " Invalid or corrupt map. \n Look in log file or text console for technical details. " ) ;
}
2017-01-01 16:24:47 +00:00
/** Sets a header's flickies to be equivalent to the original Freed Animals
*
* \ param i The header to set flickies for
*/
void P_SetDemoFlickies ( INT16 i )
{
mapheaderinfo [ i ] - > numFlickies = 5 ;
mapheaderinfo [ i ] - > flickies = Z_Realloc ( mapheaderinfo [ i ] - > flickies , 5 * sizeof ( mobjtype_t ) , PU_STATIC , NULL ) ;
mapheaderinfo [ i ] - > flickies [ 0 ] = MT_FLICKY_02 /*MT_BUNNY*/ ;
mapheaderinfo [ i ] - > flickies [ 1 ] = MT_FLICKY_01 /*MT_BIRD*/ ;
mapheaderinfo [ i ] - > flickies [ 2 ] = MT_FLICKY_12 /*MT_MOUSE*/ ;
mapheaderinfo [ i ] - > flickies [ 3 ] = MT_FLICKY_11 /*MT_COW*/ ;
mapheaderinfo [ i ] - > flickies [ 4 ] = MT_FLICKY_03 /*MT_CHICKEN*/ ;
}
/** Clears a header's flickies
*
* \ param i The header to clear flickies for
*/
void P_DeleteFlickies ( INT16 i )
{
if ( mapheaderinfo [ i ] - > flickies )
Z_Free ( mapheaderinfo [ i ] - > flickies ) ;
mapheaderinfo [ i ] - > flickies = NULL ;
mapheaderinfo [ i ] - > numFlickies = 0 ;
}
2014-03-15 16:59:03 +00:00
# define NUMLAPS_DEFAULT 4
/** Clears the data from a single map header.
*
* \ param i Map number to clear header for .
2014-08-27 03:56:30 +00:00
* \ sa P_ClearMapHeaderInfo
2014-03-15 16:59:03 +00:00
*/
static void P_ClearSingleMapHeaderInfo ( INT16 i )
{
const INT16 num = ( INT16 ) ( i - 1 ) ;
mapheaderinfo [ num ] - > lvlttl [ 0 ] = ' \0 ' ;
2017-01-26 19:14:52 +00:00
mapheaderinfo [ num ] - > selectheading [ 0 ] = ' \0 ' ;
2014-03-15 16:59:03 +00:00
mapheaderinfo [ num ] - > subttl [ 0 ] = ' \0 ' ;
2019-12-18 03:28:58 +00:00
mapheaderinfo [ num ] - > ltzzpatch [ 0 ] = ' \0 ' ;
mapheaderinfo [ num ] - > ltzztext [ 0 ] = ' \0 ' ;
mapheaderinfo [ num ] - > ltactdiamond [ 0 ] = ' \0 ' ;
2014-03-15 16:59:03 +00:00
mapheaderinfo [ num ] - > actnum = 0 ;
mapheaderinfo [ num ] - > typeoflevel = 0 ;
mapheaderinfo [ num ] - > nextlevel = ( INT16 ) ( i + 1 ) ;
2019-06-23 22:51:42 +00:00
mapheaderinfo [ num ] - > startrings = 0 ;
2020-01-08 22:41:38 +00:00
mapheaderinfo [ num ] - > keywords [ 0 ] = ' \0 ' ;
2016-02-26 07:31:48 +00:00
snprintf ( mapheaderinfo [ num ] - > musname , 7 , " %sM " , G_BuildMapName ( i ) ) ;
2016-01-08 03:48:20 +00:00
mapheaderinfo [ num ] - > musname [ 6 ] = 0 ;
mapheaderinfo [ num ] - > mustrack = 0 ;
2018-09-18 14:22:17 +00:00
mapheaderinfo [ num ] - > muspos = 0 ;
2019-03-15 05:00:50 +00:00
mapheaderinfo [ num ] - > musinterfadeout = 0 ;
2018-10-21 18:51:49 +00:00
mapheaderinfo [ num ] - > musintername [ 0 ] = ' \0 ' ;
2019-08-04 11:03:57 +00:00
mapheaderinfo [ num ] - > muspostbossname [ 6 ] = 0 ;
mapheaderinfo [ num ] - > muspostbosstrack = 0 ;
mapheaderinfo [ num ] - > muspostbosspos = 0 ;
mapheaderinfo [ num ] - > muspostbossfadein = 0 ;
2019-08-05 00:02:38 +00:00
mapheaderinfo [ num ] - > musforcereset = - 1 ;
2014-03-15 16:59:03 +00:00
mapheaderinfo [ num ] - > forcecharacter [ 0 ] = ' \0 ' ;
mapheaderinfo [ num ] - > weather = 0 ;
mapheaderinfo [ num ] - > skynum = 1 ;
mapheaderinfo [ num ] - > skybox_scalex = 16 ;
mapheaderinfo [ num ] - > skybox_scaley = 16 ;
mapheaderinfo [ num ] - > skybox_scalez = 16 ;
mapheaderinfo [ num ] - > interscreen [ 0 ] = ' # ' ;
mapheaderinfo [ num ] - > runsoc [ 0 ] = ' # ' ;
mapheaderinfo [ num ] - > scriptname [ 0 ] = ' # ' ;
mapheaderinfo [ num ] - > precutscenenum = 0 ;
mapheaderinfo [ num ] - > cutscenenum = 0 ;
mapheaderinfo [ num ] - > countdown = 0 ;
mapheaderinfo [ num ] - > palette = UINT16_MAX ;
mapheaderinfo [ num ] - > numlaps = NUMLAPS_DEFAULT ;
mapheaderinfo [ num ] - > unlockrequired = - 1 ;
mapheaderinfo [ num ] - > levelselect = 0 ;
mapheaderinfo [ num ] - > bonustype = 0 ;
2018-08-13 18:16:33 +00:00
mapheaderinfo [ num ] - > maxbonuslives = - 1 ;
2014-03-15 16:59:03 +00:00
mapheaderinfo [ num ] - > levelflags = 0 ;
mapheaderinfo [ num ] - > menuflags = 0 ;
2017-01-01 16:24:47 +00:00
# if 1 // equivalent to "FlickyList = DEMO"
P_SetDemoFlickies ( num ) ;
# else // equivalent to "FlickyList = NONE"
P_DeleteFlickies ( num ) ;
# endif
2014-03-15 16:59:03 +00:00
P_DeleteGrades ( num ) ;
2014-08-04 03:49:33 +00:00
mapheaderinfo [ num ] - > customopts = NULL ;
mapheaderinfo [ num ] - > numCustomOptions = 0 ;
2014-03-15 16:59:03 +00:00
}
/** Allocates a new map-header structure.
*
* \ param i Index of header to allocate .
*/
void P_AllocMapHeader ( INT16 i )
{
if ( ! mapheaderinfo [ i ] )
{
mapheaderinfo [ i ] = Z_Malloc ( sizeof ( mapheader_t ) , PU_STATIC , NULL ) ;
2016-12-29 17:02:05 +00:00
mapheaderinfo [ i ] - > flickies = NULL ;
2014-03-15 16:59:03 +00:00
mapheaderinfo [ i ] - > grades = NULL ;
}
P_ClearSingleMapHeaderInfo ( i + 1 ) ;
}
/** NiGHTS Grades are a special structure,
* we initialize them here .
*
* \ param i Index of header to allocate grades for
* \ param mare The mare we ' re adding grades for
* \ param grades the string from DeHackEd , we work with it ourselves
*/
void P_AddGradesForMare ( INT16 i , UINT8 mare , char * gtext )
{
INT32 g ;
char * spos = gtext ;
CONS_Debug ( DBG_SETUP , " Map %d Mare %d: " , i + 1 , ( UINT16 ) mare + 1 ) ;
if ( mapheaderinfo [ i ] - > numGradedMares < mare + 1 )
{
mapheaderinfo [ i ] - > numGradedMares = mare + 1 ;
mapheaderinfo [ i ] - > grades = Z_Realloc ( mapheaderinfo [ i ] - > grades , sizeof ( nightsgrades_t ) * mapheaderinfo [ i ] - > numGradedMares , PU_STATIC , NULL ) ;
}
for ( g = 0 ; g < 6 ; + + g )
{
// Allow "partial" grading systems
if ( spos ! = NULL )
{
mapheaderinfo [ i ] - > grades [ mare ] . grade [ g ] = atoi ( spos ) ;
CONS_Debug ( DBG_SETUP , " %u " , atoi ( spos ) ) ;
// Grab next comma
spos = strchr ( spos , ' , ' ) ;
if ( spos )
+ + spos ;
}
else
{
// Grade not reachable
mapheaderinfo [ i ] - > grades [ mare ] . grade [ g ] = UINT32_MAX ;
}
}
CONS_Debug ( DBG_SETUP , " \n " ) ;
}
/** And this removes the grades safely.
*
* \ param i The header to remove grades from
*/
void P_DeleteGrades ( INT16 i )
{
if ( mapheaderinfo [ i ] - > grades )
Z_Free ( mapheaderinfo [ i ] - > grades ) ;
mapheaderinfo [ i ] - > grades = NULL ;
mapheaderinfo [ i ] - > numGradedMares = 0 ;
}
/** And this fetches the grades
*
* \ param pscore The player ' s score .
* \ param map The game map .
* \ param mare The mare to test .
*/
UINT8 P_GetGrade ( UINT32 pscore , INT16 map , UINT8 mare )
{
INT32 i ;
// Determining the grade
if ( mapheaderinfo [ map - 1 ] & & mapheaderinfo [ map - 1 ] - > grades & & mapheaderinfo [ map - 1 ] - > numGradedMares > = mare + 1 )
{
INT32 pgrade = 0 ;
for ( i = 0 ; i < 6 ; + + i )
{
if ( pscore > = mapheaderinfo [ map - 1 ] - > grades [ mare ] . grade [ i ] )
+ + pgrade ;
}
return ( UINT8 ) pgrade ;
}
return 0 ;
}
UINT8 P_HasGrades ( INT16 map , UINT8 mare )
{
// Determining the grade
// Mare 0 is treated as overall and is true if ANY grades exist
if ( mapheaderinfo [ map - 1 ] & & mapheaderinfo [ map - 1 ] - > grades
& & ( mare = = 0 | | mapheaderinfo [ map - 1 ] - > numGradedMares > = mare ) )
return true ;
return false ;
}
UINT32 P_GetScoreForGrade ( INT16 map , UINT8 mare , UINT8 grade )
{
// Get the score for the grade... if it exists
if ( grade = = GRADE_F | | grade > GRADE_S | | ! P_HasGrades ( map , mare ) ) return 0 ;
return mapheaderinfo [ map - 1 ] - > grades [ mare ] . grade [ grade - 1 ] ;
}
//
// levelflats
//
# define MAXLEVELFLATS 256
size_t numlevelflats ;
levelflat_t * levelflats ;
2019-12-23 15:36:26 +00:00
levelflat_t * foundflats ;
2014-03-15 16:59:03 +00:00
//SoM: Other files want this info.
size_t P_PrecacheLevelFlats ( void )
{
lumpnum_t lump ;
2019-08-18 17:16:48 +00:00
size_t i ;
2014-03-15 16:59:03 +00:00
//SoM: 4/18/2000: New flat code to make use of levelflats.
2019-08-18 17:16:48 +00:00
flatmemory = 0 ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < numlevelflats ; i + + )
{
2019-10-21 03:21:22 +00:00
if ( levelflats [ i ] . type = = LEVELFLAT_FLAT )
{
lump = levelflats [ i ] . u . flat . lumpnum ;
if ( devparm )
flatmemory + = W_LumpLength ( lump ) ;
R_GetFlat ( lump ) ;
}
2014-03-15 16:59:03 +00:00
}
return flatmemory ;
}
2019-10-21 03:21:22 +00:00
/*
levelflat refers to an array of level flats ,
or NULL if we want to allocate it now .
*/
static INT32
2020-01-22 00:47:47 +00:00
Ploadflat ( levelflat_t * levelflat , const char * flatname , boolean resize )
2014-03-15 16:59:03 +00:00
{
2019-12-04 18:41:29 +00:00
# ifndef NO_PNG_LUMPS
2019-10-21 03:21:22 +00:00
UINT8 buffer [ 8 ] ;
2019-12-04 18:41:29 +00:00
# endif
2014-03-15 16:59:03 +00:00
2019-10-21 03:21:22 +00:00
lumpnum_t flatnum ;
int texturenum ;
2014-03-15 16:59:03 +00:00
2019-10-21 03:21:22 +00:00
size_t i ;
2019-05-21 18:24:26 +00:00
2020-01-22 00:47:47 +00:00
// Scan through the already found flats, return if it matches.
for ( i = 0 ; i < numlevelflats ; i + + )
2019-10-21 03:21:22 +00:00
{
2020-01-22 00:47:47 +00:00
if ( strnicmp ( levelflat [ i ] . name , flatname , 8 ) = = 0 )
return i ;
2019-10-21 03:21:22 +00:00
}
2014-03-15 16:59:03 +00:00
2020-01-22 00:47:47 +00:00
if ( resize )
2019-10-21 03:21:22 +00:00
{
// allocate new flat memory
levelflats = Z_Realloc ( levelflats , ( numlevelflats + 1 ) * sizeof ( * levelflats ) , PU_LEVEL , NULL ) ;
levelflat = levelflats + numlevelflats ;
2014-03-15 16:59:03 +00:00
}
2020-01-22 00:47:47 +00:00
else
{
if ( numlevelflats > = MAXLEVELFLATS )
I_Error ( " Too many flats in level \n " ) ;
levelflat + = numlevelflats ;
}
2014-03-15 16:59:03 +00:00
2019-10-21 03:21:22 +00:00
// Store the name.
strlcpy ( levelflat - > name , flatname , sizeof ( levelflat - > name ) ) ;
strupr ( levelflat - > name ) ;
/* If we can't find a flat, try looking for a texture! */
if ( ( flatnum = R_GetFlatNumForName ( flatname ) ) = = LUMPERROR )
{
if ( ( texturenum = R_CheckTextureNumForName ( flatname ) ) = = - 1 )
{
2019-11-09 03:08:22 +00:00
// check for REDWALL
if ( ( texturenum = R_CheckTextureNumForName ( " REDWALL " ) ) ! = - 1 )
goto texturefound ;
// check for REDFLR
else if ( ( flatnum = R_GetFlatNumForName ( " REDFLR " ) ) ! = LUMPERROR )
goto flatfound ;
// nevermind
2019-10-21 03:21:22 +00:00
levelflat - > type = LEVELFLAT_NONE ;
}
else
{
2019-11-09 03:08:22 +00:00
texturefound :
2019-10-21 03:21:22 +00:00
levelflat - > type = LEVELFLAT_TEXTURE ;
levelflat - > u . texture . num = texturenum ;
levelflat - > u . texture . lastnum = texturenum ;
/* start out unanimated */
levelflat - > u . texture . basenum = - 1 ;
}
}
else
{
2019-11-09 03:08:22 +00:00
flatfound :
2019-10-21 03:21:22 +00:00
/* This could be a flat, patch, or PNG. */
if ( R_CheckIfPatch ( flatnum ) )
levelflat - > type = LEVELFLAT_PATCH ;
else
{
# ifndef NO_PNG_LUMPS
/*
Only need eight bytes for PNG headers .
FIXME : Put this elsewhere .
*/
W_ReadLumpHeader ( flatnum , buffer , 8 , 0 ) ;
if ( R_IsLumpPNG ( buffer , W_LumpLength ( flatnum ) ) )
levelflat - > type = LEVELFLAT_PNG ;
else
# endif /*NO_PNG_LUMPS*/
levelflat - > type = LEVELFLAT_FLAT ; /* phew */
}
levelflat - > u . flat . lumpnum = flatnum ;
levelflat - > u . flat . baselumpnum = LUMPERROR ;
}
2020-01-21 23:11:16 +00:00
# ifndef ZDEBUG
CONS_Debug ( DBG_SETUP , " flat #%03d: %s \n " , atoi ( sizeu1 ( numlevelflats ) ) , levelflat - > name ) ;
# endif
2019-10-21 03:21:22 +00:00
return ( numlevelflats + + ) ;
}
// Auxiliary function. Find a flat in the active wad files,
// allocate an id for it, and set the levelflat (to speedup search)
INT32 P_AddLevelFlat ( const char * flatname , levelflat_t * levelflat )
{
2020-01-22 00:47:47 +00:00
return Ploadflat ( levelflat , flatname , false ) ;
2014-03-15 16:59:03 +00:00
}
2016-12-08 21:45:25 +00:00
// help function for Lua and $$$.sav reading
// same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
// no longer a static func in lua_maplib.c because p_saveg.c also needs it
//
INT32 P_AddLevelFlatRuntime ( const char * flatname )
{
2020-01-22 00:47:47 +00:00
return Ploadflat ( levelflats , flatname , true ) ;
2016-12-08 21:45:25 +00:00
}
// help function for $$$.sav checking
// this simply returns the flat # for the name given
//
INT32 P_CheckLevelFlat ( const char * flatname )
{
size_t i ;
levelflat_t * levelflat = levelflats ;
//
// scan through the already found flats
//
for ( i = 0 ; i < numlevelflats ; i + + , levelflat + + )
if ( strnicmp ( levelflat - > name , flatname , 8 ) = = 0 )
break ;
if ( i = = numlevelflats )
return 0 ; // ??? flat was not found, this should not happen!
// level flat id
return ( INT32 ) i ;
}
2014-03-15 16:59:03 +00:00
//
// P_ReloadRings
2019-12-24 09:50:49 +00:00
// Used by NiGHTS, clears all ring/sphere/hoop/etc items and respawns them
2014-03-15 16:59:03 +00:00
//
void P_ReloadRings ( void )
{
mobj_t * mo ;
thinker_t * th ;
size_t i , numHoops = 0 ;
// Okay, if you have more than 4000 hoops in your map,
// you're insane.
mapthing_t * hoopsToRespawn [ 4096 ] ;
mapthing_t * mt = mapthings ;
2018-06-03 21:41:54 +00:00
// scan the thinkers to find rings/spheres/hoops to unset
2019-04-20 21:29:20 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2014-03-15 16:59:03 +00:00
mo = ( mobj_t * ) th ;
if ( mo - > type = = MT_HOOPCENTER )
{
// Hoops give me a headache
if ( mo - > threshold = = 4242 ) // Dead hoop
{
hoopsToRespawn [ numHoops + + ] = mo - > spawnpoint ;
P_RemoveMobj ( mo ) ;
}
continue ;
}
2018-06-05 16:22:28 +00:00
if ( ! ( mo - > type = = MT_RING | | mo - > type = = MT_COIN
| | mo - > type = = MT_BLUESPHERE | | mo - > type = = MT_BOMBSPHERE
2018-06-03 21:41:54 +00:00
| | mo - > type = = MT_NIGHTSCHIP | | mo - > type = = MT_NIGHTSSTAR ) )
2014-03-15 16:59:03 +00:00
continue ;
// Don't auto-disintegrate things being pulled to us
if ( mo - > flags2 & MF2_NIGHTSPULL )
continue ;
P_RemoveMobj ( mo ) ;
}
// Reiterate through mapthings
for ( i = 0 ; i < nummapthings ; i + + , mt + + )
{
// Notice an omission? We handle hoops differently.
2018-06-08 16:16:20 +00:00
if ( mt - > type = = mobjinfo [ MT_RING ] . doomednum | | mt - > type = = mobjinfo [ MT_COIN ] . doomednum
2019-12-24 09:50:49 +00:00
| | mt - > type = = mobjinfo [ MT_REDTEAMRING ] . doomednum | | mt - > type = = mobjinfo [ MT_BLUETEAMRING ] . doomednum
| | mt - > type = = mobjinfo [ MT_BLUESPHERE ] . doomednum | | mt - > type = = mobjinfo [ MT_BOMBSPHERE ] . doomednum )
2014-03-15 16:59:03 +00:00
{
mt - > mobj = NULL ;
2019-12-24 11:52:43 +00:00
P_SetBonusTime ( P_SpawnMapThing ( mt ) ) ;
2019-12-24 09:50:49 +00:00
}
else if ( mt - > type > = 600 & & mt - > type < = 609 ) // Item patterns
{
mt - > mobj = NULL ;
P_SpawnItemPattern ( mt , true ) ;
2014-03-15 16:59:03 +00:00
}
}
for ( i = 0 ; i < numHoops ; i + + )
{
2019-12-24 09:50:49 +00:00
P_SpawnHoop ( hoopsToRespawn [ i ] ) ;
2018-06-03 21:41:54 +00:00
}
}
void P_SwitchSpheresBonusMode ( boolean bonustime )
{
mobj_t * mo ;
thinker_t * th ;
// scan the thinkers to find spheres to switch
2019-04-20 21:29:20 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2018-06-03 21:41:54 +00:00
{
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2018-06-03 21:41:54 +00:00
mo = ( mobj_t * ) th ;
2018-08-10 04:20:30 +00:00
if ( mo - > type ! = MT_BLUESPHERE & & mo - > type ! = MT_NIGHTSCHIP
& & mo - > type ! = MT_FLINGBLUESPHERE & & mo - > type ! = MT_FLINGNIGHTSCHIP )
2018-06-03 21:41:54 +00:00
continue ;
if ( ! mo - > health )
continue ;
P_SetMobjState ( mo , ( ( bonustime ) ? mo - > info - > raisestate : mo - > info - > spawnstate ) ) ;
2014-03-15 16:59:03 +00:00
}
}
# ifdef SCANTHINGS
void P_ScanThings ( INT16 mapnum , INT16 wadnum , INT16 lumpnum )
{
size_t i , n ;
UINT8 * data , * datastart ;
UINT16 type , maprings ;
INT16 tol ;
UINT32 flags ;
tol = mapheaderinfo [ mapnum - 1 ] - > typeoflevel ;
if ( ! ( tol & TOL_SP ) )
return ;
flags = mapheaderinfo [ mapnum - 1 ] - > levelflags ;
n = W_LumpLengthPwad ( wadnum , lumpnum ) / ( 5 * sizeof ( INT16 ) ) ;
//CONS_Printf("%u map things found!\n", n);
maprings = 0 ;
data = datastart = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_STATIC ) ;
for ( i = 0 ; i < n ; i + + )
{
data + = 3 * sizeof ( INT16 ) ; // skip x y position, angle
type = READUINT16 ( data ) & 4095 ;
data + = sizeof ( INT16 ) ; // skip options
switch ( type )
{
case 300 : // MT_RING
case 1800 : // MT_COIN
case 308 : // red team ring
case 309 : // blue team ring
maprings + + ;
break ;
case 400 : // MT_SUPERRINGBOX
case 414 : // red ring box
case 415 : // blue ring box
case 603 : // 10 diagonal rings
maprings + = 10 ;
break ;
case 600 : // 5 vertical rings
case 601 : // 5 vertical rings
case 602 : // 5 diagonal rings
maprings + = 5 ;
break ;
case 604 : // 8 circle rings
case 609 : // 16 circle rings & wings
maprings + = 8 ;
break ;
case 605 : // 16 circle rings
maprings + = 16 ;
break ;
case 608 : // 8 circle rings & wings
maprings + = 4 ;
break ;
}
}
Z_Free ( datastart ) ;
if ( maprings )
CONS_Printf ( " %s has %u rings \n " , G_BuildMapName ( mapnum ) , maprings ) ;
}
# endif
2019-12-15 08:49:54 +00:00
static void P_SpawnEmeraldHunt ( void )
2019-12-10 13:21:08 +00:00
{
INT32 emer1 , emer2 , emer3 ;
INT32 timeout = 0 ; // keeps from getting stuck
emer1 = emer2 = emer3 = 0 ;
//increment spawn numbers because zero is valid.
emer1 = ( P_RandomKey ( numhuntemeralds ) ) + 1 ;
while ( timeout + + < 100 )
{
emer2 = ( P_RandomKey ( numhuntemeralds ) ) + 1 ;
if ( emer2 ! = emer1 )
break ;
}
timeout = 0 ;
while ( timeout + + < 100 )
{
emer3 = ( P_RandomKey ( numhuntemeralds ) ) + 1 ;
if ( emer3 ! = emer2 & & emer3 ! = emer1 )
break ;
}
//decrement spawn values to the actual number because zero is valid.
if ( emer1 - - )
P_SpawnMobj ( huntemeralds [ emer1 ] - > x < < FRACBITS ,
huntemeralds [ emer1 ] - > y < < FRACBITS ,
huntemeralds [ emer1 ] - > z < < FRACBITS , MT_EMERHUNT ) ;
if ( emer2 - - )
P_SetMobjStateNF ( P_SpawnMobj ( huntemeralds [ emer2 ] - > x < < FRACBITS ,
huntemeralds [ emer2 ] - > y < < FRACBITS ,
huntemeralds [ emer2 ] - > z < < FRACBITS , MT_EMERHUNT ) ,
mobjinfo [ MT_EMERHUNT ] . spawnstate + 1 ) ;
if ( emer3 - - )
P_SetMobjStateNF ( P_SpawnMobj ( huntemeralds [ emer3 ] - > x < < FRACBITS ,
huntemeralds [ emer3 ] - > y < < FRACBITS ,
huntemeralds [ emer3 ] - > z < < FRACBITS , MT_EMERHUNT ) ,
mobjinfo [ MT_EMERHUNT ] . spawnstate + 2 ) ;
}
2019-12-28 10:30:39 +00:00
static void P_SpawnMapThings ( boolean spawnemblems )
2017-05-16 19:10:02 +00:00
{
2017-05-18 19:13:18 +00:00
size_t i ;
2017-05-16 19:10:02 +00:00
mapthing_t * mt ;
2019-12-22 22:02:47 +00:00
// Spawn axis points first so they are at the front of the list for fast searching.
2019-12-09 12:26:31 +00:00
for ( i = 0 , mt = mapthings ; i < nummapthings ; i + + , mt + + )
2014-03-15 16:59:03 +00:00
{
2019-12-09 12:26:31 +00:00
switch ( mt - > type )
{
case 1700 : // MT_AXIS
case 1701 : // MT_AXISTRANSFER
case 1702 : // MT_AXISTRANSFERLINE
mt - > mobj = NULL ;
P_SpawnMapThing ( mt ) ;
break ;
default :
break ;
}
}
2015-05-16 05:02:01 +00:00
2019-12-09 12:26:31 +00:00
numhuntemeralds = 0 ;
2014-03-15 16:59:03 +00:00
2019-12-09 12:26:31 +00:00
for ( i = 0 , mt = mapthings ; i < nummapthings ; i + + , mt + + )
{
2014-03-15 16:59:03 +00:00
if ( mt - > type = = 1700 // MT_AXIS
| | mt - > type = = 1701 // MT_AXISTRANSFER
| | mt - > type = = 1702 ) // MT_AXISTRANSFERLINE
continue ; // These were already spawned
2019-12-28 10:30:39 +00:00
if ( ! spawnemblems & & mt - > type = = mobjinfo [ MT_EMBLEM ] . doomednum )
2019-02-03 13:32:07 +00:00
continue ;
2014-03-15 16:59:03 +00:00
mt - > mobj = NULL ;
2019-12-24 09:50:49 +00:00
if ( mt - > type > = 600 & & mt - > type < = 609 ) // item patterns
P_SpawnItemPattern ( mt , false ) ;
else if ( mt - > type = = 1705 | | mt - > type = = 1713 ) // hoops
P_SpawnHoop ( mt ) ;
else // Everything else
P_SpawnMapThing ( mt ) ;
2014-03-15 16:59:03 +00:00
}
// random emeralds for hunt
if ( numhuntemeralds )
2019-12-15 08:49:54 +00:00
P_SpawnEmeraldHunt ( ) ;
2014-03-15 16:59:03 +00:00
}
// Experimental groovy write function!
void P_WriteThings ( lumpnum_t lumpnum )
{
size_t i , length ;
mapthing_t * mt ;
UINT8 * data ;
UINT8 * savebuffer , * savebuf_p ;
INT16 temp ;
data = W_CacheLumpNum ( lumpnum , PU_LEVEL ) ;
savebuf_p = savebuffer = ( UINT8 * ) malloc ( nummapthings * sizeof ( mapthing_t ) ) ;
if ( ! savebuf_p )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No more free memory for thing writing! \n " ) ) ;
return ;
}
mt = mapthings ;
for ( i = 0 ; i < nummapthings ; i + + , mt + + )
{
WRITEINT16 ( savebuf_p , mt - > x ) ;
WRITEINT16 ( savebuf_p , mt - > y ) ;
WRITEINT16 ( savebuf_p , mt - > angle ) ;
temp = ( INT16 ) ( mt - > type + ( ( INT16 ) mt - > extrainfo < < 12 ) ) ;
WRITEINT16 ( savebuf_p , temp ) ;
WRITEUINT16 ( savebuf_p , mt - > options ) ;
}
Z_Free ( data ) ;
length = savebuf_p - savebuffer ;
FIL_WriteFile ( va ( " newthings%d.lmp " , gamemap ) , savebuffer , length ) ;
free ( savebuffer ) ;
savebuf_p = NULL ;
CONS_Printf ( M_GetText ( " newthings%d.lmp saved. \n " ) , gamemap ) ;
}
2019-12-28 15:40:35 +00:00
//
// MAP LOADING FUNCTIONS
//
static void P_LoadVertices ( UINT8 * data )
2017-05-16 19:10:02 +00:00
{
2019-12-28 15:58:48 +00:00
mapvertex_t * mv = ( mapvertex_t * ) data ;
vertex_t * v = vertexes ;
2019-12-12 09:35:38 +00:00
size_t i ;
2017-05-16 19:10:02 +00:00
2019-12-28 15:40:35 +00:00
// Copy and convert vertex coordinates, internal representation as fixed.
2019-12-28 15:58:48 +00:00
for ( i = 0 ; i < numvertexes ; i + + , v + + , mv + + )
2017-05-16 19:10:02 +00:00
{
2019-12-28 15:58:48 +00:00
v - > x = SHORT ( mv - > x ) < < FRACBITS ;
v - > y = SHORT ( mv - > y ) < < FRACBITS ;
2020-01-04 23:34:16 +00:00
v - > floorzset = v - > ceilingzset = false ;
v - > floorz = v - > ceilingz = 0 ;
2019-12-12 09:35:38 +00:00
}
}
2019-12-28 16:29:58 +00:00
static void P_InitializeSector ( sector_t * ss )
{
ss - > nexttag = ss - > firsttag = - 1 ;
memset ( & ss - > soundorg , 0 , sizeof ( ss - > soundorg ) ) ;
ss - > validcount = 0 ;
ss - > thinglist = NULL ;
ss - > floordata = NULL ;
ss - > ceilingdata = NULL ;
ss - > lightingdata = NULL ;
ss - > fadecolormapdata = NULL ;
ss - > heightsec = - 1 ;
ss - > camsec = - 1 ;
ss - > floorlightsec = ss - > ceilinglightsec = - 1 ;
ss - > crumblestate = 0 ;
ss - > touching_thinglist = NULL ;
ss - > linecount = 0 ;
ss - > lines = NULL ;
ss - > ffloors = NULL ;
ss - > attached = NULL ;
ss - > attachedsolid = NULL ;
ss - > numattached = 0 ;
ss - > maxattached = 1 ;
ss - > lightlist = NULL ;
ss - > numlights = 0 ;
ss - > moved = true ;
ss - > extra_colormap = NULL ;
# ifdef HWRENDER // ----- for special tricks with HW renderer -----
ss - > pseudoSector = false ;
ss - > virtualFloor = false ;
ss - > virtualFloorheight = 0 ;
ss - > virtualCeiling = false ;
ss - > virtualCeilingheight = 0 ;
ss - > sectorLines = NULL ;
ss - > stackList = NULL ;
ss - > lineoutLength = - 1.0 l ;
# endif // ----- end special tricks -----
ss - > gravity = NULL ;
ss - > verticalflip = false ;
ss - > flags = SF_FLIPSPECIAL_FLOOR ;
ss - > cullheight = NULL ;
ss - > floorspeed = ss - > ceilspeed = 0 ;
ss - > preciplist = NULL ;
ss - > touching_preciplist = NULL ;
ss - > f_slope = NULL ;
ss - > c_slope = NULL ;
ss - > hasslope = false ;
ss - > spawn_lightlevel = ss - > lightlevel ;
ss - > spawn_extra_colormap = NULL ;
}
2019-12-28 15:40:35 +00:00
static void P_LoadSectors ( UINT8 * data )
2019-12-12 09:35:38 +00:00
{
2019-12-28 15:40:35 +00:00
mapsector_t * ms = ( mapsector_t * ) data ;
sector_t * ss = sectors ;
2019-12-12 09:35:38 +00:00
size_t i ;
2019-12-28 15:40:35 +00:00
// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
for ( i = 0 ; i < numsectors ; i + + , ss + + , ms + + )
2019-12-12 09:35:38 +00:00
{
2019-12-28 15:40:35 +00:00
ss - > floorheight = SHORT ( ms - > floorheight ) < < FRACBITS ;
ss - > ceilingheight = SHORT ( ms - > ceilingheight ) < < FRACBITS ;
2017-05-16 19:10:02 +00:00
2019-12-28 15:40:35 +00:00
ss - > floorpic = P_AddLevelFlat ( ms - > floorpic , foundflats ) ;
ss - > ceilingpic = P_AddLevelFlat ( ms - > ceilingpic , foundflats ) ;
ss - > lightlevel = SHORT ( ms - > lightlevel ) ;
ss - > special = SHORT ( ms - > special ) ;
ss - > tag = SHORT ( ms - > tag ) ;
2019-12-30 20:23:00 +00:00
ss - > floor_xoffs = ss - > floor_yoffs = 0 ;
ss - > ceiling_xoffs = ss - > ceiling_yoffs = 0 ;
ss - > floorpic_angle = ss - > ceilingpic_angle = 0 ;
2020-03-20 11:19:02 +00:00
ss - > colormap_protected = false ;
2019-12-28 16:29:58 +00:00
P_InitializeSector ( ss ) ;
2019-12-28 15:40:35 +00:00
}
}
2019-12-28 16:21:31 +00:00
static void P_InitializeLinedef ( line_t * ld )
2019-12-28 15:40:35 +00:00
{
2019-12-28 16:21:31 +00:00
vertex_t * v1 = ld - > v1 ;
vertex_t * v2 = ld - > v2 ;
UINT8 j ;
ld - > dx = v2 - > x - v1 - > x ;
ld - > dy = v2 - > y - v1 - > y ;
ld - > bbox [ BOXLEFT ] = min ( v1 - > x , v2 - > x ) ;
ld - > bbox [ BOXRIGHT ] = max ( v1 - > x , v2 - > x ) ;
ld - > bbox [ BOXBOTTOM ] = min ( v1 - > y , v2 - > y ) ;
ld - > bbox [ BOXTOP ] = max ( v1 - > y , v2 - > y ) ;
if ( ! ld - > dx )
ld - > slopetype = ST_VERTICAL ;
else if ( ! ld - > dy )
ld - > slopetype = ST_HORIZONTAL ;
else if ( ( ld - > dy > 0 ) = = ( ld - > dx > 0 ) )
ld - > slopetype = ST_POSITIVE ;
else
ld - > slopetype = ST_NEGATIVE ;
2019-12-28 15:40:35 +00:00
2019-12-28 16:21:31 +00:00
ld - > frontsector = ld - > backsector = NULL ;
2019-12-28 15:40:35 +00:00
2019-12-28 16:21:31 +00:00
ld - > validcount = 0 ;
2019-12-28 15:40:35 +00:00
# ifdef WALLSPLATS
2019-12-28 16:21:31 +00:00
ld - > splats = NULL ;
2019-12-28 15:40:35 +00:00
# endif
2019-12-28 16:21:31 +00:00
ld - > firsttag = ld - > nexttag = - 1 ;
2019-12-12 09:35:38 +00:00
# ifdef POLYOBJECTS
2019-12-28 16:21:31 +00:00
ld - > polyobj = NULL ;
2019-12-12 09:35:38 +00:00
# endif
2019-12-28 16:21:31 +00:00
ld - > text = NULL ;
ld - > callcount = 0 ;
2019-12-12 09:35:38 +00:00
2019-12-28 16:21:31 +00:00
// cph 2006/09/30 - fix sidedef errors right away.
// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
for ( j = 0 ; j < 2 ; j + + )
if ( ld - > sidenum [ j ] ! = 0xffff & & ld - > sidenum [ j ] > = ( UINT16 ) numsides )
2017-05-16 19:10:02 +00:00
{
2019-12-28 16:21:31 +00:00
ld - > sidenum [ j ] = 0xffff ;
2019-12-28 16:37:32 +00:00
CONS_Debug ( DBG_SETUP , " P_InitializeLinedef: Linedef %s has out-of-range sidedef number \n " , sizeu1 ( ( size_t ) ( ld - lines ) ) ) ;
2017-05-16 19:10:02 +00:00
}
2019-12-28 16:21:31 +00:00
// killough 11/98: fix common wad errors (missing sidedefs):
if ( ld - > sidenum [ 0 ] = = 0xffff )
{
ld - > sidenum [ 0 ] = 0 ; // Substitute dummy sidedef for missing right side
// cph - print a warning about the bug
2019-12-28 16:37:32 +00:00
CONS_Debug ( DBG_SETUP , " P_InitializeLinedef: Linedef %s missing first sidedef \n " , sizeu1 ( ( size_t ) ( ld - lines ) ) ) ;
2019-12-28 16:21:31 +00:00
}
2017-05-16 19:10:02 +00:00
2019-12-28 16:21:31 +00:00
if ( ( ld - > sidenum [ 1 ] = = 0xffff ) & & ( ld - > flags & ML_TWOSIDED ) )
{
ld - > flags & = ~ ML_TWOSIDED ; // Clear 2s flag for missing left side
// cph - print a warning about the bug
2019-12-28 16:37:32 +00:00
CONS_Debug ( DBG_SETUP , " P_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef \n " , sizeu1 ( ( size_t ) ( ld - lines ) ) ) ;
2019-12-28 16:21:31 +00:00
}
2017-05-16 19:10:02 +00:00
2019-12-29 08:39:50 +00:00
if ( ld - > sidenum [ 0 ] ! = 0xffff )
{
2019-12-28 16:21:31 +00:00
sides [ ld - > sidenum [ 0 ] ] . special = ld - > special ;
2019-12-29 08:39:50 +00:00
sides [ ld - > sidenum [ 0 ] ] . line = ld ;
}
if ( ld - > sidenum [ 1 ] ! = 0xffff )
{
2019-12-28 16:21:31 +00:00
sides [ ld - > sidenum [ 1 ] ] . special = ld - > special ;
2019-12-29 08:39:50 +00:00
sides [ ld - > sidenum [ 1 ] ] . line = ld ;
}
2019-12-28 16:21:31 +00:00
}
2017-05-16 19:10:02 +00:00
2020-01-01 13:27:01 +00:00
static void P_SetLinedefV1 ( size_t i , UINT16 vertex_num )
{
if ( vertex_num > = numvertexes )
{
CONS_Debug ( DBG_SETUP , " P_SetLinedefV1: linedef %s has out-of-range v1 num %u \n " , sizeu1 ( i ) , vertex_num ) ;
vertex_num = 0 ;
}
lines [ i ] . v1 = & vertexes [ vertex_num ] ;
}
static void P_SetLinedefV2 ( size_t i , UINT16 vertex_num )
{
if ( vertex_num > = numvertexes )
{
CONS_Debug ( DBG_SETUP , " P_SetLinedefV2: linedef %s has out-of-range v2 num %u \n " , sizeu1 ( i ) , vertex_num ) ;
vertex_num = 0 ;
}
lines [ i ] . v2 = & vertexes [ vertex_num ] ;
}
2019-12-28 16:21:31 +00:00
static void P_LoadLinedefs ( UINT8 * data )
{
maplinedef_t * mld = ( maplinedef_t * ) data ;
line_t * ld = lines ;
size_t i ;
2017-05-16 19:10:02 +00:00
2019-12-28 16:21:31 +00:00
for ( i = 0 ; i < numlines ; i + + , mld + + , ld + + )
{
ld - > flags = SHORT ( mld - > flags ) ;
ld - > special = SHORT ( mld - > special ) ;
ld - > tag = SHORT ( mld - > tag ) ;
2020-01-02 11:23:14 +00:00
memset ( ld - > args , 0 , NUMLINEARGS * sizeof ( * ld - > args ) ) ;
2020-01-12 23:59:35 +00:00
memset ( ld - > stringargs , 0x00 , NUMLINESTRINGARGS * sizeof ( * ld - > stringargs ) ) ;
2020-02-10 19:26:29 +00:00
ld - > alpha = FRACUNIT ;
2020-01-01 13:27:01 +00:00
P_SetLinedefV1 ( i , SHORT ( mld - > v1 ) ) ;
P_SetLinedefV2 ( i , SHORT ( mld - > v2 ) ) ;
2017-05-16 19:10:02 +00:00
2019-12-28 16:21:31 +00:00
ld - > sidenum [ 0 ] = SHORT ( mld - > sidenum [ 0 ] ) ;
ld - > sidenum [ 1 ] = SHORT ( mld - > sidenum [ 1 ] ) ;
2014-03-15 16:59:03 +00:00
2019-12-28 16:21:31 +00:00
P_InitializeLinedef ( ld ) ;
2017-05-18 19:13:18 +00:00
}
}
2014-03-15 16:59:03 +00:00
2020-01-01 13:27:01 +00:00
static void P_SetSidedefSector ( size_t i , UINT16 sector_num )
{
// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
if ( sector_num > = numsectors )
{
CONS_Debug ( DBG_SETUP , " P_SetSidedefSector: sidedef %s has out-of-range sector num %u \n " , sizeu1 ( i ) , sector_num ) ;
sector_num = 0 ;
}
sides [ i ] . sector = & sectors [ sector_num ] ;
}
2020-01-01 14:10:41 +00:00
static void P_InitializeSidedef ( side_t * sd )
{
if ( ! sd - > line )
{
2020-01-01 14:11:39 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadSidedefs: Sidedef %s is not used by any linedef \n " , sizeu1 ( ( size_t ) ( sd - sides ) ) ) ;
2020-01-01 14:10:41 +00:00
sd - > line = & lines [ 0 ] ;
sd - > special = sd - > line - > special ;
}
sd - > text = NULL ;
sd - > colormap_data = NULL ;
}
2019-12-28 15:40:35 +00:00
static void P_LoadSidedefs ( UINT8 * data )
2014-03-15 16:59:03 +00:00
{
2019-12-28 15:58:48 +00:00
mapsidedef_t * msd = ( mapsidedef_t * ) data ;
side_t * sd = sides ;
size_t i ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:58:48 +00:00
for ( i = 0 ; i < numsides ; i + + , sd + + , msd + + )
2014-03-15 16:59:03 +00:00
{
2020-01-01 13:13:24 +00:00
INT16 textureoffset = SHORT ( msd - > textureoffset ) ;
boolean isfrontside ;
2020-01-01 14:10:41 +00:00
P_InitializeSidedef ( sd ) ;
2020-01-01 13:13:24 +00:00
isfrontside = sd - > line - > sidenum [ 0 ] = = i ;
2014-03-15 16:59:03 +00:00
2020-01-01 13:13:24 +00:00
// Repeat count for midtexture
if ( ( ( sd - > line - > flags & ( ML_TWOSIDED | ML_EFFECT5 ) ) = = ( ML_TWOSIDED | ML_EFFECT5 ) )
& & ! ( sd - > special > = 300 & & sd - > special < 500 ) ) // exempt linedef exec specials
{
2020-01-10 22:32:31 +00:00
sd - > repeatcnt = ( INT16 ) ( ( ( UINT16 ) textureoffset ) > > 12 ) ;
sd - > textureoffset = ( ( ( UINT16 ) textureoffset ) & 2047 ) < < FRACBITS ;
2020-01-01 13:13:24 +00:00
}
else
{
sd - > repeatcnt = 0 ;
sd - > textureoffset = textureoffset < < FRACBITS ;
}
2014-03-15 16:59:03 +00:00
sd - > rowoffset = SHORT ( msd - > rowoffset ) < < FRACBITS ;
2020-01-01 13:27:01 +00:00
P_SetSidedefSector ( i , SHORT ( msd - > sector ) ) ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:58:48 +00:00
// Special info stored in texture fields!
2014-03-15 16:59:03 +00:00
switch ( sd - > special )
{
case 606 : //SoM: 4/4/2000: Just colormap transfer
2018-06-14 20:51:21 +00:00
case 447 : // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
2018-09-12 13:06:38 +00:00
case 455 : // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
2014-03-15 16:59:03 +00:00
// SoM: R_CreateColormap will only create a colormap in software mode...
// Perhaps we should just call it instead of doing the calculations here.
2020-03-20 10:19:30 +00:00
if ( ! udmf )
{
sd - > colormap_data = R_CreateColormapFromLinedef ( msd - > toptexture , msd - > midtexture , msd - > bottomtexture ) ;
sd - > toptexture = sd - > midtexture = sd - > bottomtexture = 0 ;
}
2018-09-10 20:32:54 +00:00
break ;
2014-03-15 16:59:03 +00:00
case 413 : // Change music
2016-01-08 03:48:20 +00:00
{
char process [ 8 + 1 ] ;
sd - > toptexture = sd - > midtexture = sd - > bottomtexture = 0 ;
if ( msd - > bottomtexture [ 0 ] ! = ' - ' | | msd - > bottomtexture [ 1 ] ! = ' \0 ' )
{
M_Memcpy ( process , msd - > bottomtexture , 8 ) ;
process [ 8 ] = ' \0 ' ;
2018-09-18 19:28:57 +00:00
sd - > bottomtexture = get_number ( process ) ;
2016-01-08 03:48:20 +00:00
}
2018-09-18 19:28:57 +00:00
if ( ! ( msd - > midtexture [ 0 ] = = ' - ' & & msd - > midtexture [ 1 ] = = ' \0 ' ) | | msd - > midtexture [ 1 ] ! = ' \0 ' )
{
M_Memcpy ( process , msd - > midtexture , 8 ) ;
process [ 8 ] = ' \0 ' ;
sd - > midtexture = get_number ( process ) ;
}
sd - > text = Z_Malloc ( 7 , PU_LEVEL , NULL ) ;
2019-12-29 08:39:50 +00:00
if ( isfrontside & & ! ( msd - > toptexture [ 0 ] = = ' - ' & & msd - > toptexture [ 1 ] = = ' \0 ' ) )
2018-09-18 19:28:57 +00:00
{
M_Memcpy ( process , msd - > toptexture , 8 ) ;
process [ 8 ] = ' \0 ' ;
// If they type in O_ or D_ and their music name, just shrug,
// then copy the rest instead.
if ( ( process [ 0 ] = = ' O ' | | process [ 0 ] = = ' D ' ) & & process [ 7 ] )
M_Memcpy ( sd - > text , process + 2 , 6 ) ;
else // Assume it's a proper music name.
M_Memcpy ( sd - > text , process , 6 ) ;
sd - > text [ 6 ] = 0 ;
}
else
sd - > text [ 0 ] = 0 ;
2016-01-08 03:48:20 +00:00
break ;
}
2016-06-04 23:23:20 +00:00
case 4 : // Speed pad parameters
2014-03-15 16:59:03 +00:00
case 414 : // Play SFX
{
sd - > toptexture = sd - > midtexture = sd - > bottomtexture = 0 ;
if ( msd - > toptexture [ 0 ] ! = ' - ' | | msd - > toptexture [ 1 ] ! = ' \0 ' )
{
char process [ 8 + 1 ] ;
M_Memcpy ( process , msd - > toptexture , 8 ) ;
process [ 8 ] = ' \0 ' ;
sd - > toptexture = get_number ( process ) ;
}
break ;
}
Hooh boy.
****
* MF2_MACEROTATE. Apply to any object. Replaces the indiscriminate spamming of A_MaceRotate each tic.
****
* Mace point mapthings have been slightly modified.
- MTF_AMBUSH: bigger luke theory (has no effect on custom mace, different effect on spring mace)
- MTF_OBJECTFLIP: flips the objects, but nothing else - just so it doesn't look out of place in gravflip sections
- MTF_OBJECTSPECIAL: keeps it from attempting to play swinging sounds
- angle: tag of controlling linedef
- parameter: number of "spokes" minus one - for example, a parameter of 2 results in 3 equidistant maces rotating around the same point.
****
* Mace linedefs have been significantly revamped.
- line dx: number of chain links
- line dy: speed (in FU)
- frontside floor height: Pitch (in degrees; how much it "tilts" over - Yaw influences the axis it's tilting on)
- frontside ceiling height: Yaw (in degrees; rotation of entire thing on xy plane)
- frontside x offset: Phase (in degrees; how far it is through the rotation cycle)
- frontside y offset: Max speed (in FU; if less than speed, set to speed*2)
- backside floor height: Pinch (in degrees; 0 if no backside; essentially makes rotation conical instead of wheel-like)
- backside ceiling height: Roll (in degrees; 0 if no backside; rotates on the axis of the spinning - identical to Phase for spinning maces, but useful for rotating swinging maces as opposed to just offsetting them)
- backside x offset: Number of "antispokes" (0 if no backside; makes that many spokes not exist so you can put another mace/chain type in there instead; for combo mace/chain instead turns them into chains directly)
- backside y offset: Width (in number of extra chains per side; 0 if no backside; creates a "skiprope" arrangement)
----
- ML_NOCLIMB: for chains and chain-mace combos, allow for player control of yaw through strafe keys
- ML_EFFECT1: replacing the seperate mapthings, this makes a mace type swing instead of spin.
- ML_EFFECT2: for all spokes of the mace wheel ending in maces, make the chains out of the mace type (inverted for firebars)
- ML_EFFECT3: spawn a bonus mace type at the center(s) of rotation
- ML_EFFECT4: don't clip inside ground
****
* Mapthing 1104 represents both spinning and swinging maces from prior versions of SRB2.
* Mapthing 1105 has gone from being a swinging mace variant to a combination of chains and maces in a single unit, provided the number of "spokes" is greater than one.
* Mapthing 1105 has gone from being a swinging chain variant to a vertical spring-on-a-ball-on-a-chain. Yellow by default, apply MTF_AMBUSH to turn into a red spring.
* Mapthing 1107 represents both spinning and swinging chains from prior versions of SRB2.
* Mapthing 1108 is completely untouched except to port over 2.1's functionality to the new backend.
* Mapthing 1109 is a Mario castle-level style firebar. This inverts the functionality of ML_EFFECT2 on the tagged linedef.
* Mapthing 1110 is a free slot should we want to implement another type of base-game mace.
* Mapthing 1111 is a custom mace. Use the linedef's frontside texture slots to identify a macetype mobjtype, then use the backside texture slots to identify a linktype mobjtype (defaults to MT_NULL if no backside).
****
Whooh. Requires new patch.dta for sprites.
2017-07-02 14:11:09 +00:00
case 9 : // Mace parameters
2016-06-04 23:23:20 +00:00
case 14 : // Bustable block parameters
2016-06-05 23:00:31 +00:00
case 15 : // Fan particle spawner parameters
2020-01-11 23:40:57 +00:00
case 334 : // Trigger linedef executor: Object dye - Continuous
case 335 : // Trigger linedef executor: Object dye - Each time
case 336 : // Trigger linedef executor: Object dye - Once
2014-03-15 16:59:03 +00:00
case 425 : // Calls P_SetMobjState on calling mobj
case 434 : // Custom Power
case 442 : // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
2019-08-29 05:57:58 +00:00
case 461 : // Spawns an object on the map based on texture offsets
2020-01-11 23:40:57 +00:00
case 463 : // Colorizes an object
2014-03-15 16:59:03 +00:00
{
char process [ 8 * 3 + 1 ] ;
memset ( process , 0 , 8 * 3 + 1 ) ;
sd - > toptexture = sd - > midtexture = sd - > bottomtexture = 0 ;
if ( msd - > toptexture [ 0 ] = = ' - ' & & msd - > toptexture [ 1 ] = = ' \0 ' )
break ;
else
M_Memcpy ( process , msd - > toptexture , 8 ) ;
if ( msd - > midtexture [ 0 ] ! = ' - ' | | msd - > midtexture [ 1 ] ! = ' \0 ' )
M_Memcpy ( process + strlen ( process ) , msd - > midtexture , 8 ) ;
if ( msd - > bottomtexture [ 0 ] ! = ' - ' | | msd - > bottomtexture [ 1 ] ! = ' \0 ' )
M_Memcpy ( process + strlen ( process ) , msd - > bottomtexture , 8 ) ;
sd - > toptexture = get_number ( process ) ;
break ;
}
2019-07-03 07:19:29 +00:00
case 331 : // Trigger linedef executor: Skin - Continuous
case 332 : // Trigger linedef executor: Skin - Each time
case 333 : // Trigger linedef executor: Skin - Once
2014-03-15 16:59:03 +00:00
case 443 : // Calls a named Lua function
2018-11-10 14:32:53 +00:00
case 459 : // Control text prompt (named tag)
2014-03-15 16:59:03 +00:00
{
char process [ 8 * 3 + 1 ] ;
memset ( process , 0 , 8 * 3 + 1 ) ;
sd - > toptexture = sd - > midtexture = sd - > bottomtexture = 0 ;
if ( msd - > toptexture [ 0 ] = = ' - ' & & msd - > toptexture [ 1 ] = = ' \0 ' )
break ;
else
M_Memcpy ( process , msd - > toptexture , 8 ) ;
if ( msd - > midtexture [ 0 ] ! = ' - ' | | msd - > midtexture [ 1 ] ! = ' \0 ' )
M_Memcpy ( process + strlen ( process ) , msd - > midtexture , 8 ) ;
if ( msd - > bottomtexture [ 0 ] ! = ' - ' | | msd - > bottomtexture [ 1 ] ! = ' \0 ' )
M_Memcpy ( process + strlen ( process ) , msd - > bottomtexture , 8 ) ;
sd - > text = Z_Malloc ( strlen ( process ) + 1 , PU_LEVEL , NULL ) ;
M_Memcpy ( sd - > text , process , strlen ( process ) + 1 ) ;
break ;
}
2019-12-29 11:01:41 +00:00
case 259 : // Custom FOF
if ( ! isfrontside )
{
if ( ( msd - > toptexture [ 0 ] > = ' 0 ' & & msd - > toptexture [ 0 ] < = ' 9 ' )
| | ( msd - > toptexture [ 0 ] > = ' A ' & & msd - > toptexture [ 0 ] < = ' F ' ) )
sd - > toptexture = axtoi ( msd - > toptexture ) ;
else
I_Error ( " Custom FOF (tag %d) needs a value in the linedef's back side upper texture field. " , sd - > line - > tag ) ;
sd - > midtexture = R_TextureNumForName ( msd - > midtexture ) ;
sd - > bottomtexture = R_TextureNumForName ( msd - > bottomtexture ) ;
break ;
}
// FALLTHRU
2014-03-15 16:59:03 +00:00
default : // normal cases
if ( msd - > toptexture [ 0 ] = = ' # ' )
{
char * col = msd - > toptexture ;
sd - > toptexture = sd - > bottomtexture =
( ( col [ 1 ] - ' 0 ' ) * 100 + ( col [ 2 ] - ' 0 ' ) * 10 + col [ 3 ] - ' 0 ' ) + 1 ;
sd - > midtexture = R_TextureNumForName ( msd - > midtexture ) ;
}
else
{
sd - > midtexture = R_TextureNumForName ( msd - > midtexture ) ;
sd - > toptexture = R_TextureNumForName ( msd - > toptexture ) ;
sd - > bottomtexture = R_TextureNumForName ( msd - > bottomtexture ) ;
}
break ;
}
}
2017-05-18 19:13:18 +00:00
}
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
static void P_LoadThings ( UINT8 * data )
2014-03-15 16:59:03 +00:00
{
2019-12-28 15:40:35 +00:00
mapthing_t * mt ;
size_t i ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
for ( i = 0 , mt = mapthings ; i < nummapthings ; i + + , mt + + )
{
mt - > x = READINT16 ( data ) ;
mt - > y = READINT16 ( data ) ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
mt - > angle = READINT16 ( data ) ;
mt - > type = READUINT16 ( data ) ;
mt - > options = READUINT16 ( data ) ;
mt - > extrainfo = ( UINT8 ) ( mt - > type > > 12 ) ;
2020-01-25 08:52:13 +00:00
mt - > tag = 0 ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
mt - > type & = 4095 ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
if ( mt - > type = = 1705 | | ( mt - > type = = 750 & & mt - > extrainfo ) )
mt - > z = mt - > options ; // NiGHTS Hoops use the full flags bits to set the height.
else
mt - > z = mt - > options > > ZSHIFT ;
2019-12-30 20:23:00 +00:00
mt - > mobj = NULL ;
2019-12-28 15:40:35 +00:00
}
}
2014-03-15 16:59:03 +00:00
2019-12-30 10:33:22 +00:00
// Stores positions for relevant map data spread through a TEXTMAP.
UINT32 mapthingsPos [ UINT16_MAX ] ;
UINT32 linesPos [ UINT16_MAX ] ;
UINT32 sidesPos [ UINT16_MAX ] ;
UINT32 vertexesPos [ UINT16_MAX ] ;
UINT32 sectorsPos [ UINT16_MAX ] ;
2019-12-30 12:27:05 +00:00
// Determine total amount of map data in TEXTMAP.
static boolean TextmapCount ( UINT8 * data , size_t size )
2019-12-30 10:33:22 +00:00
{
2019-12-30 12:27:05 +00:00
char * tkn = M_GetToken ( ( char * ) data ) ;
2019-12-30 12:38:52 +00:00
UINT8 brackets = 0 ;
2019-12-30 12:27:05 +00:00
nummapthings = 0 ;
numlines = 0 ;
numsides = 0 ;
numvertexes = 0 ;
numsectors = 0 ;
2019-12-30 10:33:22 +00:00
// Look for namespace at the beginning.
2019-12-30 12:27:05 +00:00
if ( ! fastcmp ( tkn , " namespace " ) )
2019-12-30 10:33:22 +00:00
{
2019-12-30 12:27:05 +00:00
Z_Free ( tkn ) ;
2019-12-30 13:47:48 +00:00
CONS_Alert ( CONS_ERROR , " No namespace at beginning of lump! \n " ) ;
2019-12-30 12:27:05 +00:00
return false ;
}
Z_Free ( tkn ) ;
2019-12-30 10:33:22 +00:00
2019-12-30 12:27:05 +00:00
// Check if namespace is valid.
tkn = M_GetToken ( NULL ) ;
if ( ! fastcmp ( tkn , " srb2 " ) )
CONS_Alert ( CONS_WARNING , " Invalid namespace '%s', only 'srb2' is supported. \n " , tkn ) ;
Z_Free ( tkn ) ;
2019-12-30 10:33:22 +00:00
2019-12-30 12:27:05 +00:00
tkn = M_GetToken ( NULL ) ;
while ( tkn & & M_GetTokenPos ( ) < size )
{
// Avoid anything inside bracketed stuff, only look for external keywords.
2019-12-30 12:38:52 +00:00
if ( brackets )
2019-12-30 10:33:22 +00:00
{
2019-12-30 12:38:52 +00:00
if ( fastcmp ( tkn , " } " ) )
brackets - - ;
2019-12-30 10:33:22 +00:00
}
2019-12-30 12:38:52 +00:00
else if ( fastcmp ( tkn , " { " ) )
brackets + + ;
2019-12-30 12:27:05 +00:00
// Check for valid fields.
else if ( fastcmp ( tkn , " thing " ) )
mapthingsPos [ nummapthings + + ] = M_GetTokenPos ( ) ;
else if ( fastcmp ( tkn , " linedef " ) )
linesPos [ numlines + + ] = M_GetTokenPos ( ) ;
else if ( fastcmp ( tkn , " sidedef " ) )
sidesPos [ numsides + + ] = M_GetTokenPos ( ) ;
else if ( fastcmp ( tkn , " vertex " ) )
vertexesPos [ numvertexes + + ] = M_GetTokenPos ( ) ;
else if ( fastcmp ( tkn , " sector " ) )
sectorsPos [ numsectors + + ] = M_GetTokenPos ( ) ;
else
CONS_Alert ( CONS_NOTICE , " Unknown field '%s'. \n " , tkn ) ;
Z_Free ( tkn ) ;
tkn = M_GetToken ( NULL ) ;
2019-12-30 10:33:22 +00:00
}
2019-12-30 12:27:05 +00:00
Z_Free ( tkn ) ;
2019-12-30 13:47:48 +00:00
if ( brackets )
{
CONS_Alert ( CONS_ERROR , " Unclosed brackets detected in textmap lump. \n " ) ;
return false ;
}
2019-12-30 12:27:05 +00:00
return true ;
2019-12-30 10:33:22 +00:00
}
2020-01-01 14:52:59 +00:00
static void ParseTextmapVertexParameter ( UINT32 i , char * param , char * val )
2019-12-30 10:33:22 +00:00
{
if ( fastcmp ( param , " x " ) )
2020-01-01 14:52:59 +00:00
vertexes [ i ] . x = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " y " ) )
2020-01-01 14:52:59 +00:00
vertexes [ i ] . y = FLOAT_TO_FIXED ( atof ( val ) ) ;
2020-01-04 09:24:42 +00:00
else if ( fastcmp ( param , " zfloor " ) )
{
vertexes [ i ] . floorz = FLOAT_TO_FIXED ( atof ( val ) ) ;
vertexes [ i ] . floorzset = true ;
}
else if ( fastcmp ( param , " zceiling " ) )
{
vertexes [ i ] . ceilingz = FLOAT_TO_FIXED ( atof ( val ) ) ;
vertexes [ i ] . ceilingzset = true ;
}
2019-12-30 10:33:22 +00:00
}
2020-03-15 08:55:57 +00:00
typedef struct textmap_colormap_s {
boolean used ;
INT32 lightcolor ;
UINT8 lightalpha ;
INT32 fadecolor ;
UINT8 fadealpha ;
UINT8 fadestart ;
UINT8 fadeend ;
UINT8 flags ;
} textmap_colormap_t ;
textmap_colormap_t textmap_colormap = { false , 0 , 25 , 0 , 25 , 0 , 31 , 0 } ;
2020-01-01 14:52:59 +00:00
static void ParseTextmapSectorParameter ( UINT32 i , char * param , char * val )
2019-12-30 10:33:22 +00:00
{
if ( fastcmp ( param , " heightfloor " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . floorheight = atol ( val ) < < FRACBITS ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " heightceiling " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . ceilingheight = atol ( val ) < < FRACBITS ;
2019-12-30 10:33:22 +00:00
if ( fastcmp ( param , " texturefloor " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . floorpic = P_AddLevelFlat ( val , foundflats ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " textureceiling " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . ceilingpic = P_AddLevelFlat ( val , foundflats ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " lightlevel " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . lightlevel = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " special " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . special = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " id " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . tag = atol ( val ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " xpanningfloor " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . floor_xoffs = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " ypanningfloor " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . floor_yoffs = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " xpanningceiling " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . ceiling_xoffs = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " ypanningceiling " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . ceiling_yoffs = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " rotationfloor " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . floorpic_angle = FixedAngle ( FLOAT_TO_FIXED ( atof ( val ) ) ) ;
2019-12-30 16:28:10 +00:00
else if ( fastcmp ( param , " rotationceiling " ) )
2020-01-01 14:52:59 +00:00
sectors [ i ] . ceilingpic_angle = FixedAngle ( FLOAT_TO_FIXED ( atof ( val ) ) ) ;
2020-03-15 08:55:57 +00:00
else if ( fastcmp ( param , " lightcolor " ) )
{
textmap_colormap . used = true ;
textmap_colormap . lightcolor = atol ( val ) ;
}
else if ( fastcmp ( param , " lightalpha " ) )
{
textmap_colormap . used = true ;
textmap_colormap . lightalpha = atol ( val ) ;
}
else if ( fastcmp ( param , " fadecolor " ) )
{
textmap_colormap . used = true ;
textmap_colormap . fadecolor = atol ( val ) ;
}
else if ( fastcmp ( param , " fadealpha " ) )
{
textmap_colormap . used = true ;
textmap_colormap . fadealpha = atol ( val ) ;
}
else if ( fastcmp ( param , " fadestart " ) )
{
textmap_colormap . used = true ;
textmap_colormap . fadestart = atol ( val ) ;
}
else if ( fastcmp ( param , " fadeend " ) )
{
textmap_colormap . used = true ;
textmap_colormap . fadeend = atol ( val ) ;
}
else if ( fastcmp ( param , " colormapfog " ) & & fastcmp ( " true " , val ) )
{
textmap_colormap . used = true ;
textmap_colormap . flags | = CMF_FOG ;
}
else if ( fastcmp ( param , " colormapfadesprites " ) & & fastcmp ( " true " , val ) )
{
textmap_colormap . used = true ;
textmap_colormap . flags | = CMF_FADEFULLBRIGHTSPRITES ;
}
2020-03-20 11:19:02 +00:00
else if ( fastcmp ( param , " colormapprotected " ) & & fastcmp ( " true " , val ) )
sectors [ i ] . colormap_protected = true ;
2019-12-30 10:33:22 +00:00
}
2020-01-01 14:52:59 +00:00
static void ParseTextmapSidedefParameter ( UINT32 i , char * param , char * val )
2019-12-30 10:33:22 +00:00
{
if ( fastcmp ( param , " offsetx " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . textureoffset = atol ( val ) < < FRACBITS ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " offsety " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . rowoffset = atol ( val ) < < FRACBITS ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " texturetop " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . toptexture = R_TextureNumForName ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " texturebottom " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . bottomtexture = R_TextureNumForName ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " texturemiddle " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . midtexture = R_TextureNumForName ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " sector " ) )
2020-01-01 14:52:59 +00:00
P_SetSidedefSector ( i , atol ( val ) ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " repeatcnt " ) )
2020-01-01 14:52:59 +00:00
sides [ i ] . repeatcnt = atol ( val ) ;
2019-12-30 10:33:22 +00:00
}
2020-01-01 14:52:59 +00:00
static void ParseTextmapLinedefParameter ( UINT32 i , char * param , char * val )
2019-12-30 10:33:22 +00:00
{
if ( fastcmp ( param , " id " ) )
2020-01-01 14:52:59 +00:00
lines [ i ] . tag = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " special " ) )
2020-01-01 14:52:59 +00:00
lines [ i ] . special = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " v1 " ) )
2020-01-01 14:52:59 +00:00
P_SetLinedefV1 ( i , atol ( val ) ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " v2 " ) )
2020-01-01 14:52:59 +00:00
P_SetLinedefV2 ( i , atol ( val ) ) ;
2020-01-02 11:23:14 +00:00
else if ( fastncmp ( param , " arg " , 3 ) & & strlen ( param ) > 3 )
{
2020-01-24 22:02:42 +00:00
size_t argnum = atol ( param + 3 ) ;
if ( argnum > = NUMLINEARGS )
return ;
lines [ i ] . args [ argnum ] = atol ( val ) ;
}
else if ( fastncmp ( param , " stringarg " , 9 ) & & strlen ( param ) > 9 )
{
size_t argnum = param [ 9 ] - ' 0 ' ;
if ( argnum > = NUMLINESTRINGARGS )
return ;
lines [ i ] . stringargs [ argnum ] = Z_Malloc ( strlen ( val ) + 1 , PU_LEVEL , NULL ) ;
M_Memcpy ( lines [ i ] . stringargs [ argnum ] , val , strlen ( val ) + 1 ) ;
2020-01-02 11:23:14 +00:00
}
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " sidefront " ) )
2020-01-01 14:52:59 +00:00
lines [ i ] . sidenum [ 0 ] = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " sideback " ) )
2020-01-01 14:52:59 +00:00
lines [ i ] . sidenum [ 1 ] = atol ( val ) ;
2020-01-26 11:24:52 +00:00
else if ( fastcmp ( param , " alpha " ) )
lines [ i ] . alpha = FLOAT_TO_FIXED ( atof ( val ) ) ;
2019-12-30 10:33:22 +00:00
// Flags
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " blocking " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_IMPASSIBLE ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " blockmonsters " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_BLOCKMONSTERS ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " twosided " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_TWOSIDED ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " dontpegtop " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_DONTPEGTOP ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " dontpegbottom " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_DONTPEGBOTTOM ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " skewtd " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT1 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " noclimb " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_NOCLIMB ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " noskew " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT2 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " midpeg " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT3 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " midsolid " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT4 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " wrapmidtex " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT5 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " effect6 " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_EFFECT6 ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " nonet " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_NONET ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " netonly " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_NETONLY ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " bouncy " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_BOUNCY ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " transfer " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
lines [ i ] . flags | = ML_TFERLINE ;
}
2020-01-01 14:52:59 +00:00
static void ParseTextmapThingParameter ( UINT32 i , char * param , char * val )
2019-12-30 10:33:22 +00:00
{
2020-01-25 08:52:13 +00:00
if ( fastcmp ( param , " id " ) )
mapthings [ i ] . tag = atol ( val ) ;
2019-12-30 10:33:22 +00:00
if ( fastcmp ( param , " x " ) )
2020-01-01 14:52:59 +00:00
mapthings [ i ] . x = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " y " ) )
2020-01-01 14:52:59 +00:00
mapthings [ i ] . y = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " height " ) )
2020-01-01 14:52:59 +00:00
mapthings [ i ] . z = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " angle " ) )
2020-01-01 14:52:59 +00:00
mapthings [ i ] . angle = atol ( val ) ;
2019-12-30 10:33:22 +00:00
else if ( fastcmp ( param , " type " ) )
2020-01-01 14:52:59 +00:00
mapthings [ i ] . type = atol ( val ) ;
2019-12-30 10:33:22 +00:00
// Flags
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " extra " ) & & fastcmp ( " true " , val ) )
2019-12-30 15:28:22 +00:00
mapthings [ i ] . options | = MTF_EXTRA ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " flip " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
mapthings [ i ] . options | = MTF_OBJECTFLIP ;
2020-01-24 22:02:42 +00:00
else if ( fastcmp ( param , " objectspecial " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
mapthings [ i ] . options | = MTF_OBJECTSPECIAL ;
2020-01-01 14:52:59 +00:00
else if ( fastcmp ( param , " ambush " ) & & fastcmp ( " true " , val ) )
2019-12-30 10:33:22 +00:00
mapthings [ i ] . options | = MTF_AMBUSH ;
}
/** From a given position table, run a specified parser function through a {}-encapsuled text.
*
* \ param Position of the data to parse , in the textmap .
* \ param Structure number ( mapthings , sectors , . . . ) .
* \ param Parser function pointer .
*/
2020-01-01 14:52:59 +00:00
static void TextmapParse ( UINT32 dataPos , size_t num , void ( * parser ) ( UINT32 , char * , char * ) )
2019-12-30 10:33:22 +00:00
{
2020-01-01 14:52:59 +00:00
char * param , * val ;
2019-12-30 10:33:22 +00:00
M_SetTokenPos ( dataPos ) ;
2020-01-01 14:52:59 +00:00
param = M_GetToken ( NULL ) ;
if ( ! fastcmp ( param , " { " ) )
2019-12-30 10:33:22 +00:00
{
2020-01-01 14:52:59 +00:00
Z_Free ( param ) ;
2019-12-30 13:33:41 +00:00
CONS_Alert ( CONS_WARNING , " Invalid UDMF data capsule! \n " ) ;
return ;
}
2020-01-01 14:52:59 +00:00
Z_Free ( param ) ;
2019-12-30 13:33:41 +00:00
2020-01-01 14:52:59 +00:00
while ( true )
2019-12-30 13:33:41 +00:00
{
2020-01-01 14:52:59 +00:00
param = M_GetToken ( NULL ) ;
if ( fastcmp ( param , " } " ) )
{
Z_Free ( param ) ;
break ;
}
val = M_GetToken ( NULL ) ;
parser ( num , param , val ) ;
Z_Free ( param ) ;
Z_Free ( val ) ;
2019-12-30 10:33:22 +00:00
}
}
2019-12-30 11:33:24 +00:00
/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
*/
2019-12-30 13:33:41 +00:00
static void TextmapFixFlatOffsets ( sector_t * sec )
2019-12-30 11:33:24 +00:00
{
if ( sec - > floorpic_angle )
{
fixed_t pc = FINECOSINE ( sec - > floorpic_angle > > ANGLETOFINESHIFT ) ;
fixed_t ps = FINESINE ( sec - > floorpic_angle > > ANGLETOFINESHIFT ) ;
fixed_t xoffs = sec - > floor_xoffs ;
fixed_t yoffs = sec - > floor_yoffs ;
sec - > floor_xoffs = ( FixedMul ( xoffs , pc ) % MAXFLATSIZE ) - ( FixedMul ( yoffs , ps ) % MAXFLATSIZE ) ;
sec - > floor_yoffs = ( FixedMul ( xoffs , ps ) % MAXFLATSIZE ) + ( FixedMul ( yoffs , pc ) % MAXFLATSIZE ) ;
}
if ( sec - > ceilingpic_angle )
{
fixed_t pc = FINECOSINE ( sec - > ceilingpic_angle > > ANGLETOFINESHIFT ) ;
fixed_t ps = FINESINE ( sec - > ceilingpic_angle > > ANGLETOFINESHIFT ) ;
fixed_t xoffs = sec - > ceiling_xoffs ;
fixed_t yoffs = sec - > ceiling_yoffs ;
sec - > ceiling_xoffs = ( FixedMul ( xoffs , pc ) % MAXFLATSIZE ) - ( FixedMul ( yoffs , ps ) % MAXFLATSIZE ) ;
sec - > ceiling_yoffs = ( FixedMul ( xoffs , ps ) % MAXFLATSIZE ) + ( FixedMul ( yoffs , pc ) % MAXFLATSIZE ) ;
}
}
2020-03-15 08:55:57 +00:00
static INT32 P_ColorToRGBA ( INT32 color , UINT8 alpha )
{
UINT8 r = ( color > > 16 ) & 0xFF ;
UINT8 g = ( color > > 8 ) & 0xFF ;
UINT8 b = color & 0xFF ;
return R_PutRgbaRGBA ( r , g , b , alpha ) ;
}
2019-12-30 10:33:22 +00:00
/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
*/
2019-12-30 13:33:41 +00:00
static void P_LoadTextmap ( void )
2019-12-30 10:33:22 +00:00
{
UINT32 i ;
vertex_t * vt ;
sector_t * sc ;
line_t * ld ;
side_t * sd ;
mapthing_t * mt ;
2019-12-30 13:42:41 +00:00
CONS_Alert ( CONS_NOTICE , " UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented. \n " ) ;
2019-12-30 10:33:22 +00:00
/// Given the UDMF specs, some fields are given a default value.
2019-12-30 15:28:22 +00:00
/// If an element's field has a default value set, it is omitted
2019-12-30 10:33:22 +00:00
/// from the textmap, and therefore we have to account for it by
/// preemptively setting that value beforehand.
for ( i = 0 , vt = vertexes ; i < numvertexes ; i + + , vt + + )
{
// Defaults.
2020-01-01 22:52:30 +00:00
vt - > x = vt - > y = INT32_MAX ;
2020-01-04 09:24:42 +00:00
vt - > floorzset = vt - > ceilingzset = false ;
vt - > floorz = vt - > ceilingz = 0 ;
2019-12-30 10:33:22 +00:00
2019-12-30 15:28:22 +00:00
TextmapParse ( vertexesPos [ i ] , i , ParseTextmapVertexParameter ) ;
2020-01-01 22:52:30 +00:00
if ( vt - > x = = INT32_MAX )
I_Error ( " P_LoadTextmap: vertex %s has no x value set! \n " , sizeu1 ( i ) ) ;
if ( vt - > y = = INT32_MAX )
I_Error ( " P_LoadTextmap: vertex %s has no y value set! \n " , sizeu1 ( i ) ) ;
2019-12-30 10:33:22 +00:00
}
for ( i = 0 , sc = sectors ; i < numsectors ; i + + , sc + + )
{
// Defaults.
sc - > floorheight = 0 ;
sc - > ceilingheight = 0 ;
sc - > floorpic = 0 ;
sc - > ceilingpic = 0 ;
sc - > lightlevel = 255 ;
sc - > special = 0 ;
sc - > tag = 0 ;
2019-12-30 20:23:00 +00:00
sc - > floor_xoffs = sc - > floor_yoffs = 0 ;
sc - > ceiling_xoffs = sc - > ceiling_yoffs = 0 ;
sc - > floorpic_angle = sc - > ceilingpic_angle = 0 ;
2020-03-20 11:19:02 +00:00
sc - > colormap_protected = false ;
2020-03-15 08:55:57 +00:00
textmap_colormap . used = false ;
textmap_colormap . lightcolor = 0 ;
textmap_colormap . lightalpha = 25 ;
textmap_colormap . fadecolor = 0 ;
textmap_colormap . fadealpha = 25 ;
textmap_colormap . fadestart = 0 ;
textmap_colormap . fadeend = 31 ;
textmap_colormap . flags = 0 ;
2019-12-30 16:28:10 +00:00
TextmapParse ( sectorsPos [ i ] , i , ParseTextmapSectorParameter ) ;
2019-12-30 20:23:00 +00:00
P_InitializeSector ( sc ) ;
2020-03-15 08:55:57 +00:00
if ( textmap_colormap . used )
{
INT32 rgba = P_ColorToRGBA ( textmap_colormap . lightcolor , textmap_colormap . lightalpha ) ;
INT32 fadergba = P_ColorToRGBA ( textmap_colormap . fadecolor , textmap_colormap . fadealpha ) ;
2020-03-20 10:19:30 +00:00
sc - > extra_colormap = sc - > spawn_extra_colormap = R_CreateColormap ( rgba , fadergba , textmap_colormap . fadestart , textmap_colormap . fadeend , textmap_colormap . flags ) ;
2020-03-15 08:55:57 +00:00
}
2019-12-30 11:31:55 +00:00
TextmapFixFlatOffsets ( sc ) ;
2019-12-30 10:33:22 +00:00
}
for ( i = 0 , ld = lines ; i < numlines ; i + + , ld + + )
{
// Defaults.
2020-01-01 12:40:17 +00:00
ld - > v1 = ld - > v2 = NULL ;
ld - > flags = 0 ;
2019-12-30 10:33:22 +00:00
ld - > special = 0 ;
2020-01-01 12:40:17 +00:00
ld - > tag = 0 ;
2020-01-02 11:23:14 +00:00
memset ( ld - > args , 0 , NUMLINEARGS * sizeof ( * ld - > args ) ) ;
2020-01-12 23:59:35 +00:00
memset ( ld - > stringargs , 0x00 , NUMLINESTRINGARGS * sizeof ( * ld - > stringargs ) ) ;
2020-02-10 19:26:29 +00:00
ld - > alpha = FRACUNIT ;
2020-01-01 12:40:17 +00:00
ld - > sidenum [ 0 ] = 0xffff ;
2019-12-30 10:33:22 +00:00
ld - > sidenum [ 1 ] = 0xffff ;
2019-12-30 15:28:22 +00:00
TextmapParse ( linesPos [ i ] , i , ParseTextmapLinedefParameter ) ;
2020-01-01 22:52:30 +00:00
2020-01-01 13:27:01 +00:00
if ( ! ld - > v1 )
2020-01-01 22:52:30 +00:00
I_Error ( " P_LoadTextmap: linedef %s has no v1 value set! \n " , sizeu1 ( i ) ) ;
2020-01-01 13:27:01 +00:00
if ( ! ld - > v2 )
2020-01-01 22:52:30 +00:00
I_Error ( " P_LoadTextmap: linedef %s has no v2 value set! \n " , sizeu1 ( i ) ) ;
if ( ld - > sidenum [ 0 ] = = 0xffff )
I_Error ( " P_LoadTextmap: linedef %s has no sidefront value set! \n " , sizeu1 ( i ) ) ;
2019-12-30 10:33:22 +00:00
P_InitializeLinedef ( ld ) ;
}
for ( i = 0 , sd = sides ; i < numsides ; i + + , sd + + )
{
// Defaults.
sd - > textureoffset = 0 ;
2020-01-01 12:40:17 +00:00
sd - > rowoffset = 0 ;
2019-12-30 10:33:22 +00:00
sd - > toptexture = R_TextureNumForName ( " - " ) ;
sd - > midtexture = R_TextureNumForName ( " - " ) ;
sd - > bottomtexture = R_TextureNumForName ( " - " ) ;
2020-01-01 12:40:17 +00:00
sd - > sector = NULL ;
2019-12-30 10:33:22 +00:00
sd - > repeatcnt = 0 ;
2019-12-30 15:28:22 +00:00
TextmapParse ( sidesPos [ i ] , i , ParseTextmapSidedefParameter ) ;
2020-01-01 13:27:01 +00:00
if ( ! sd - > sector )
2020-01-01 22:52:30 +00:00
I_Error ( " P_LoadTextmap: sidedef %s has no sector value set! \n " , sizeu1 ( i ) ) ;
2020-01-01 14:10:41 +00:00
P_InitializeSidedef ( sd ) ;
2019-12-30 10:33:22 +00:00
}
for ( i = 0 , mt = mapthings ; i < nummapthings ; i + + , mt + + )
{
// Defaults.
2019-12-30 20:23:00 +00:00
mt - > x = mt - > y = 0 ;
2019-12-30 10:33:22 +00:00
mt - > angle = 0 ;
2019-12-30 20:23:00 +00:00
mt - > type = 0 ;
mt - > options = 0 ;
mt - > z = 0 ;
mt - > extrainfo = 0 ;
2020-01-25 08:52:13 +00:00
mt - > tag = 0 ;
2019-12-30 20:23:00 +00:00
mt - > mobj = NULL ;
2019-12-30 10:33:22 +00:00
2019-12-30 15:28:22 +00:00
TextmapParse ( mapthingsPos [ i ] , i , ParseTextmapThingParameter ) ;
2019-12-30 10:33:22 +00:00
}
}
2020-01-04 10:08:05 +00:00
static void P_ProcessLinedefsAfterSidedefs ( void )
{
size_t i = numlines ;
register line_t * ld = lines ;
for ( ; i - - ; ld + + )
{
ld - > frontsector = sides [ ld - > sidenum [ 0 ] ] . sector ; //e6y: Can't be -1 here
ld - > backsector = ld - > sidenum [ 1 ] ! = 0xffff ? sides [ ld - > sidenum [ 1 ] ] . sector : 0 ;
switch ( ld - > special )
{
2020-03-20 10:19:30 +00:00
// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
2020-01-04 10:08:05 +00:00
case 331 : // Trigger linedef executor: Skin - Continuous
case 332 : // Trigger linedef executor: Skin - Each time
case 333 : // Trigger linedef executor: Skin - Once
case 443 : // Calls a named Lua function
if ( sides [ ld - > sidenum [ 0 ] ] . text )
{
size_t len = strlen ( sides [ ld - > sidenum [ 0 ] ] . text ) + 1 ;
if ( ld - > sidenum [ 1 ] ! = 0xffff & & sides [ ld - > sidenum [ 1 ] ] . text )
len + = strlen ( sides [ ld - > sidenum [ 1 ] ] . text ) ;
ld - > text = Z_Malloc ( len , PU_LEVEL , NULL ) ;
M_Memcpy ( ld - > text , sides [ ld - > sidenum [ 0 ] ] . text , strlen ( sides [ ld - > sidenum [ 0 ] ] . text ) + 1 ) ;
if ( ld - > sidenum [ 1 ] ! = 0xffff & & sides [ ld - > sidenum [ 1 ] ] . text )
M_Memcpy ( ld - > text + strlen ( ld - > text ) + 1 , sides [ ld - > sidenum [ 1 ] ] . text , strlen ( sides [ ld - > sidenum [ 1 ] ] . text ) + 1 ) ;
}
break ;
2020-03-20 10:19:30 +00:00
case 447 : // Change colormap
case 455 : // Fade colormap
if ( udmf )
break ;
if ( ld - > flags & ML_DONTPEGBOTTOM ) // alternate alpha (by texture offsets)
{
extracolormap_t * exc = R_CopyColormap ( sides [ ld - > sidenum [ 0 ] ] . colormap_data , false ) ;
INT16 alpha = max ( min ( sides [ ld - > sidenum [ 0 ] ] . textureoffset > > FRACBITS , 25 ) , - 25 ) ;
INT16 fadealpha = max ( min ( sides [ ld - > sidenum [ 0 ] ] . rowoffset > > FRACBITS , 25 ) , - 25 ) ;
// If alpha is negative, set "subtract alpha" flag and store absolute value
if ( alpha < 0 )
{
alpha * = - 1 ;
ld - > args [ 2 ] | = 16 ;
}
if ( fadealpha < 0 )
{
fadealpha * = - 1 ;
ld - > args [ 2 ] | = 256 ;
}
exc - > rgba = R_GetRgbaRGB ( exc - > rgba ) + R_PutRgbaA ( alpha ) ;
exc - > fadergba = R_GetRgbaRGB ( exc - > fadergba ) + R_PutRgbaA ( fadealpha ) ;
if ( ! ( sides [ ld - > sidenum [ 0 ] ] . colormap_data = R_GetColormapFromList ( exc ) ) )
{
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
sides [ ld - > sidenum [ 0 ] ] . colormap_data = exc ;
}
else
Z_Free ( exc ) ;
}
break ;
2020-01-04 10:08:05 +00:00
}
}
}
2019-12-30 13:47:48 +00:00
static boolean P_LoadMapData ( const virtres_t * virt )
2019-12-28 15:40:35 +00:00
{
2019-12-30 12:27:05 +00:00
virtlump_t * virtvertexes = NULL , * virtsectors = NULL , * virtsidedefs = NULL , * virtlinedefs = NULL , * virtthings = NULL ;
2014-03-15 16:59:03 +00:00
2019-12-28 15:40:35 +00:00
// Count map data.
2020-03-15 15:23:15 +00:00
if ( udmf ) // Count how many entries for each type we got in textmap.
2019-12-30 13:47:48 +00:00
{
2020-03-15 15:23:15 +00:00
virtlump_t * textmap = vres_Find ( virt , " TEXTMAP " ) ;
2019-12-30 13:47:48 +00:00
if ( ! TextmapCount ( textmap - > data , textmap - > size ) )
return false ;
}
2019-12-28 15:40:35 +00:00
else
{
virtthings = vres_Find ( virt , " THINGS " ) ;
virtvertexes = vres_Find ( virt , " VERTEXES " ) ;
virtsectors = vres_Find ( virt , " SECTORS " ) ;
virtsidedefs = vres_Find ( virt , " SIDEDEFS " ) ;
virtlinedefs = vres_Find ( virt , " LINEDEFS " ) ;
if ( ! virtthings )
I_Error ( " THINGS lump not found " ) ;
if ( ! virtvertexes )
I_Error ( " VERTEXES lump not found " ) ;
if ( ! virtsectors )
I_Error ( " SECTORS lump not found " ) ;
if ( ! virtsidedefs )
I_Error ( " SIDEDEFS lump not found " ) ;
if ( ! virtlinedefs )
I_Error ( " LINEDEFS lump not found " ) ;
// Traditional doom map format just assumes the number of elements from the lump sizes.
numvertexes = virtvertexes - > size / sizeof ( mapvertex_t ) ;
numsectors = virtsectors - > size / sizeof ( mapsector_t ) ;
numsides = virtsidedefs - > size / sizeof ( mapsidedef_t ) ;
numlines = virtlinedefs - > size / sizeof ( maplinedef_t ) ;
nummapthings = virtthings - > size / ( 5 * sizeof ( INT16 ) ) ;
}
if ( numvertexes < = 0 )
I_Error ( " Level has no vertices " ) ;
if ( numsectors < = 0 )
I_Error ( " Level has no sectors " ) ;
if ( numsides < = 0 )
I_Error ( " Level has no sidedefs " ) ;
if ( numlines < = 0 )
I_Error ( " Level has no linedefs " ) ;
vertexes = Z_Calloc ( numvertexes * sizeof ( * vertexes ) , PU_LEVEL , NULL ) ;
sectors = Z_Calloc ( numsectors * sizeof ( * sectors ) , PU_LEVEL , NULL ) ;
sides = Z_Calloc ( numsides * sizeof ( * sides ) , PU_LEVEL , NULL ) ;
lines = Z_Calloc ( numlines * sizeof ( * lines ) , PU_LEVEL , NULL ) ;
mapthings = Z_Calloc ( nummapthings * sizeof ( * mapthings ) , PU_LEVEL , NULL ) ;
// Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
//Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
foundflats = calloc ( MAXLEVELFLATS , sizeof ( * foundflats ) ) ;
if ( foundflats = = NULL )
I_Error ( " Ran out of memory while loading sectors \n " ) ;
numlevelflats = 0 ;
2019-12-30 10:33:22 +00:00
// Load map data.
2020-03-15 15:23:15 +00:00
if ( udmf )
2019-12-30 10:33:22 +00:00
P_LoadTextmap ( ) ;
2019-12-28 15:40:35 +00:00
else
{
P_LoadVertices ( virtvertexes - > data ) ;
P_LoadSectors ( virtsectors - > data ) ;
P_LoadLinedefs ( virtlinedefs - > data ) ;
P_LoadSidedefs ( virtsidedefs - > data ) ;
P_LoadThings ( virtthings - > data ) ;
}
2020-01-04 10:08:05 +00:00
P_ProcessLinedefsAfterSidedefs ( ) ;
2019-12-28 15:40:35 +00:00
R_ClearTextureNumCache ( true ) ;
// set the sky flat num
skyflatnum = P_AddLevelFlat ( SKYFLATNAME , foundflats ) ;
// copy table for global usage
levelflats = M_Memcpy ( Z_Calloc ( numlevelflats * sizeof ( * levelflats ) , PU_LEVEL , NULL ) , foundflats , numlevelflats * sizeof ( levelflat_t ) ) ;
free ( foundflats ) ;
// search for animated flats and set up
P_SetupLevelFlatAnims ( ) ;
2019-12-30 13:47:48 +00:00
return true ;
2019-12-28 15:40:35 +00:00
}
2019-12-29 18:54:04 +00:00
static void P_InitializeSubsector ( subsector_t * ss )
{
ss - > sector = NULL ;
# ifdef FLOORSPLATS
ss - > splats = NULL ;
# endif
ss - > validcount = 0 ;
}
2019-12-28 15:40:35 +00:00
static inline void P_LoadSubsectors ( UINT8 * data )
{
mapsubsector_t * ms = ( mapsubsector_t * ) data ;
subsector_t * ss = subsectors ;
size_t i ;
for ( i = 0 ; i < numsubsectors ; i + + , ss + + , ms + + )
{
ss - > numlines = SHORT ( ms - > numsegs ) ;
ss - > firstline = SHORT ( ms - > firstseg ) ;
2019-12-29 18:54:04 +00:00
P_InitializeSubsector ( ss ) ;
2019-12-28 15:40:35 +00:00
}
}
static void P_LoadNodes ( UINT8 * data )
{
UINT8 j , k ;
mapnode_t * mn = ( mapnode_t * ) data ;
node_t * no = nodes ;
size_t i ;
for ( i = 0 ; i < numnodes ; i + + , no + + , mn + + )
{
no - > x = SHORT ( mn - > x ) < < FRACBITS ;
no - > y = SHORT ( mn - > y ) < < FRACBITS ;
no - > dx = SHORT ( mn - > dx ) < < FRACBITS ;
no - > dy = SHORT ( mn - > dy ) < < FRACBITS ;
for ( j = 0 ; j < 2 ; j + + )
{
no - > children [ j ] = SHORT ( mn - > children [ j ] ) ;
for ( k = 0 ; k < 4 ; k + + )
no - > bbox [ j ] [ k ] = SHORT ( mn - > bbox [ j ] [ k ] ) < < FRACBITS ;
}
}
}
/** Computes the length of a seg in fracunits.
*
* \ param seg Seg to compute length for .
* \ return Length in fracunits .
*/
fixed_t P_SegLength ( seg_t * seg )
{
INT64 dx = ( seg - > v2 - > x - seg - > v1 - > x ) > > 1 ;
INT64 dy = ( seg - > v2 - > y - seg - > v1 - > y ) > > 1 ;
return FixedHypot ( dx , dy ) < < 1 ;
}
# ifdef HWRENDER
/** Computes the length of a seg as a float.
* This is needed for OpenGL .
*
* \ param seg Seg to compute length for .
* \ return Length as a float .
*/
static inline float P_SegLengthFloat ( seg_t * seg )
{
float dx , dy ;
// make a vector (start at origin)
dx = FIXED_TO_FLOAT ( seg - > v2 - > x - seg - > v1 - > x ) ;
dy = FIXED_TO_FLOAT ( seg - > v2 - > y - seg - > v1 - > y ) ;
return ( float ) hypot ( dx , dy ) ;
}
# endif
2019-12-29 19:24:14 +00:00
static void P_InitializeSeg ( seg_t * seg )
{
2020-01-02 08:51:07 +00:00
if ( seg - > linedef )
{
seg - > sidedef = & sides [ seg - > linedef - > sidenum [ seg - > side ] ] ;
2019-12-29 19:24:14 +00:00
2020-01-02 08:51:07 +00:00
seg - > frontsector = seg - > sidedef - > sector ;
seg - > backsector = ( seg - > linedef - > flags & ML_TWOSIDED ) ? sides [ seg - > linedef - > sidenum [ seg - > side ^ 1 ] ] . sector : NULL ;
}
2019-12-29 19:24:14 +00:00
# ifdef HWRENDER
seg - > pv1 = seg - > pv2 = NULL ;
//Hurdler: 04/12/2000: for now, only used in hardware mode
seg - > lightmaps = NULL ; // list of static lightmap for this seg
# endif
seg - > numlights = 0 ;
seg - > rlights = NULL ;
# ifdef POLYOBJECTS
seg - > polyseg = NULL ;
seg - > dontrenderme = false ;
# endif
}
2019-12-28 15:40:35 +00:00
static void P_LoadSegs ( UINT8 * data )
{
2019-12-29 19:24:14 +00:00
mapseg_t * ms = ( mapseg_t * ) data ;
seg_t * seg = segs ;
2019-12-28 15:40:35 +00:00
size_t i ;
2019-12-29 19:24:14 +00:00
for ( i = 0 ; i < numsegs ; i + + , seg + + , ms + + )
2019-12-28 15:40:35 +00:00
{
2019-12-29 19:24:14 +00:00
seg - > v1 = & vertexes [ SHORT ( ms - > v1 ) ] ;
seg - > v2 = & vertexes [ SHORT ( ms - > v2 ) ] ;
seg - > side = SHORT ( ms - > side ) ;
seg - > offset = ( SHORT ( ms - > offset ) ) < < FRACBITS ;
2019-12-28 15:40:35 +00:00
2019-12-29 19:24:14 +00:00
seg - > angle = ( SHORT ( ms - > angle ) ) < < FRACBITS ;
seg - > linedef = & lines [ SHORT ( ms - > linedef ) ] ;
seg - > length = P_SegLength ( seg ) ;
2019-12-28 15:40:35 +00:00
# ifdef HWRENDER
2019-12-29 19:24:14 +00:00
seg - > flength = ( rendermode = = render_opengl ) ? P_SegLengthFloat ( seg ) : 0 ;
2019-12-28 15:40:35 +00:00
# endif
2019-12-29 19:24:14 +00:00
seg - > glseg = false ;
P_InitializeSeg ( seg ) ;
2019-12-28 15:40:35 +00:00
}
}
2019-12-29 13:17:51 +00:00
typedef enum {
NT_DOOM ,
NT_XNOD ,
NT_ZNOD ,
NT_XGLN ,
NT_ZGLN ,
NT_XGL2 ,
NT_ZGL2 ,
NT_XGL3 ,
NT_ZGL3 ,
2019-12-29 14:50:53 +00:00
NT_UNSUPPORTED ,
NUMNODETYPES
2019-12-29 13:17:51 +00:00
} nodetype_t ;
2019-12-29 14:50:53 +00:00
// Find out the BSP format.
2019-12-29 21:11:15 +00:00
static nodetype_t P_GetNodetype ( const virtres_t * virt , UINT8 * * nodedata )
2019-12-29 14:50:53 +00:00
{
2019-12-30 00:52:39 +00:00
boolean supported [ NUMNODETYPES ] = { 0 } ;
2019-12-29 14:50:53 +00:00
nodetype_t nodetype = NT_UNSUPPORTED ;
char signature [ 4 + 1 ] ;
2020-03-15 15:23:15 +00:00
if ( udmf )
2019-12-29 14:50:53 +00:00
{
2019-12-29 21:11:15 +00:00
* nodedata = vres_Find ( virt , " ZNODES " ) - > data ;
2019-12-29 14:50:53 +00:00
supported [ NT_XGLN ] = supported [ NT_XGL3 ] = true ;
}
else
{
virtlump_t * virtsegs = vres_Find ( virt , " SEGS " ) ;
virtlump_t * virtssectors ;
if ( virtsegs & & virtsegs - > size )
2019-12-29 19:43:41 +00:00
{
2019-12-29 21:11:15 +00:00
* nodedata = vres_Find ( virt , " NODES " ) - > data ;
2019-12-29 14:50:53 +00:00
return NT_DOOM ; // Traditional map format BSP tree.
2019-12-29 19:43:41 +00:00
}
2019-12-29 14:50:53 +00:00
virtssectors = vres_Find ( virt , " SSECTORS " ) ;
if ( virtssectors & & virtssectors - > size )
{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
2019-12-29 21:11:15 +00:00
* nodedata = virtssectors - > data ;
2019-12-29 14:50:53 +00:00
supported [ NT_XGLN ] = supported [ NT_ZGLN ] = supported [ NT_XGL3 ] = true ;
}
else
{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
2019-12-29 21:11:15 +00:00
* nodedata = vres_Find ( virt , " NODES " ) - > data ;
2019-12-29 14:50:53 +00:00
supported [ NT_XNOD ] = supported [ NT_ZNOD ] = true ;
}
}
2019-12-29 21:11:15 +00:00
M_Memcpy ( signature , * nodedata , 4 ) ;
2019-12-29 14:59:15 +00:00
signature [ 4 ] = ' \0 ' ;
2019-12-29 21:11:15 +00:00
( * nodedata ) + = 4 ;
2019-12-29 14:59:15 +00:00
2019-12-29 14:50:53 +00:00
if ( ! strcmp ( signature , " XNOD " ) )
nodetype = NT_XNOD ;
else if ( ! strcmp ( signature , " ZNOD " ) )
nodetype = NT_ZNOD ;
else if ( ! strcmp ( signature , " XGLN " ) )
nodetype = NT_XGLN ;
else if ( ! strcmp ( signature , " ZGLN " ) )
nodetype = NT_ZGLN ;
else if ( ! strcmp ( signature , " XGL3 " ) )
nodetype = NT_XGL3 ;
return supported [ nodetype ] ? nodetype : NT_UNSUPPORTED ;
}
2019-12-29 17:07:44 +00:00
// Extended node formats feature additional vertices; useful for OpenGL, but totally useless in gamelogic.
2019-12-29 21:11:15 +00:00
static boolean P_LoadExtraVertices ( UINT8 * * data )
2019-12-29 17:07:44 +00:00
{
2019-12-29 21:11:15 +00:00
UINT32 origvrtx = READUINT32 ( ( * data ) ) ;
UINT32 xtrvrtx = READUINT32 ( ( * data ) ) ;
2019-12-29 17:07:44 +00:00
line_t * ld = lines ;
vertex_t * oldpos = vertexes ;
ssize_t offset ;
size_t i ;
if ( numvertexes ! = origvrtx ) // If native vertex count doesn't match node original vertex count, bail out (broken data?).
{
CONS_Alert ( CONS_WARNING , " Vertex count in map data and nodes differ! \n " ) ;
return false ;
}
if ( ! xtrvrtx )
return true ;
// If extra vertexes were generated, reallocate the vertex array and fix the pointers.
numvertexes + = xtrvrtx ;
vertexes = Z_Realloc ( vertexes , numvertexes * sizeof ( * vertexes ) , PU_LEVEL , NULL ) ;
offset = ( size_t ) ( vertexes - oldpos ) ;
for ( i = 0 , ld = lines ; i < numlines ; i + + , ld + + )
{
ld - > v1 + = offset ;
ld - > v2 + = offset ;
}
// Read extra vertex data.
for ( i = origvrtx ; i < numvertexes ; i + + )
{
2019-12-29 21:11:15 +00:00
vertexes [ i ] . x = READFIXED ( ( * data ) ) ;
vertexes [ i ] . y = READFIXED ( ( * data ) ) ;
2019-12-29 17:07:44 +00:00
}
return true ;
}
2019-12-29 21:11:15 +00:00
static boolean P_LoadExtendedSubsectorsAndSegs ( UINT8 * * data , nodetype_t nodetype )
2019-12-29 14:15:32 +00:00
{
2019-12-29 17:07:44 +00:00
size_t i , k ;
2019-12-29 14:15:32 +00:00
INT16 m ;
2019-12-29 18:54:04 +00:00
seg_t * seg ;
2019-12-29 14:15:32 +00:00
// Subsectors
2019-12-29 21:11:15 +00:00
numsubsectors = READUINT32 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
subsectors = Z_Calloc ( numsubsectors * sizeof ( * subsectors ) , PU_LEVEL , NULL ) ;
for ( i = 0 ; i < numsubsectors ; i + + )
2019-12-29 21:11:15 +00:00
subsectors [ i ] . numlines = READUINT32 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
// Segs
2019-12-29 21:11:15 +00:00
numsegs = READUINT32 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
segs = Z_Calloc ( numsegs * sizeof ( * segs ) , PU_LEVEL , NULL ) ;
for ( i = 0 , k = 0 ; i < numsubsectors ; i + + )
{
subsectors [ i ] . firstline = k ;
2019-12-29 18:54:04 +00:00
P_InitializeSubsector ( & subsectors [ i ] ) ;
2019-12-29 14:15:32 +00:00
switch ( nodetype )
{
case NT_XGLN :
2019-12-29 18:04:50 +00:00
case NT_XGL3 :
2019-12-29 14:15:32 +00:00
for ( m = 0 ; m < subsectors [ i ] . numlines ; m + + , k + + )
{
2020-01-02 08:51:07 +00:00
UINT32 vertexnum = READUINT32 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
UINT16 linenum ;
2019-12-29 19:24:14 +00:00
2020-01-02 08:51:07 +00:00
if ( vertexnum > = numvertexes )
2020-01-02 21:28:32 +00:00
I_Error ( " P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d! \n " , sizeu1 ( k ) , m , vertexnum ) ;
2020-01-02 08:51:07 +00:00
segs [ k - 1 + ( ( m = = 0 ) ? subsectors [ i ] . numlines : 0 ) ] . v2 = segs [ k ] . v1 = & vertexes [ vertexnum ] ;
2019-12-29 19:24:14 +00:00
2020-01-02 08:51:07 +00:00
READUINT32 ( ( * data ) ) ; // partner, can be ignored by software renderer
2019-12-29 18:04:50 +00:00
if ( nodetype = = NT_XGL3 )
2020-01-02 08:51:07 +00:00
READUINT16 ( ( * data ) ) ; // Line number is 32-bit in XGL3, but we're limited to 16 bits.
2019-12-29 19:24:14 +00:00
2019-12-29 21:11:15 +00:00
linenum = READUINT16 ( ( * data ) ) ;
2020-01-02 08:51:07 +00:00
if ( linenum ! = 0xFFFF & & linenum > = numlines )
2020-01-02 21:28:32 +00:00
I_Error ( " P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d! \n " , sizeu1 ( k ) , m , linenum ) ;
2019-12-29 18:04:50 +00:00
segs [ k ] . glseg = ( linenum = = 0xFFFF ) ;
segs [ k ] . linedef = ( linenum = = 0xFFFF ) ? NULL : & lines [ linenum ] ;
2019-12-29 21:11:15 +00:00
segs [ k ] . side = READUINT8 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
}
break ;
case NT_XNOD :
for ( m = 0 ; m < subsectors [ i ] . numlines ; m + + , k + + )
{
2020-01-02 08:51:07 +00:00
UINT32 v1num = READUINT32 ( ( * data ) ) ;
UINT32 v2num = READUINT32 ( ( * data ) ) ;
UINT16 linenum = READUINT16 ( ( * data ) ) ;
if ( v1num > = numvertexes )
2020-01-02 21:28:32 +00:00
I_Error ( " P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d! \n " , sizeu1 ( k ) , m , v1num ) ;
2020-01-02 08:51:07 +00:00
if ( v2num > = numvertexes )
2020-01-02 21:28:32 +00:00
I_Error ( " P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d! \n " , sizeu1 ( k ) , m , v2num ) ;
2020-01-02 08:51:07 +00:00
if ( linenum > = numlines )
2020-01-02 21:28:32 +00:00
I_Error ( " P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d! \n " , sizeu1 ( k ) , m , linenum ) ;
2020-01-02 08:51:07 +00:00
segs [ k ] . v1 = & vertexes [ v1num ] ;
segs [ k ] . v2 = & vertexes [ v2num ] ;
segs [ k ] . linedef = & lines [ linenum ] ;
2019-12-29 21:11:15 +00:00
segs [ k ] . side = READUINT8 ( ( * data ) ) ;
2019-12-29 19:24:14 +00:00
segs [ k ] . glseg = false ;
2019-12-29 14:15:32 +00:00
}
break ;
default :
2019-12-29 19:24:14 +00:00
return false ;
2019-12-29 14:15:32 +00:00
}
}
2019-12-29 18:54:04 +00:00
for ( i = 0 , seg = segs ; i < numsegs ; i + + , seg + + )
2019-12-29 14:15:32 +00:00
{
2019-12-29 18:54:04 +00:00
vertex_t * v1 = seg - > v1 ;
vertex_t * v2 = seg - > v2 ;
2019-12-29 19:24:14 +00:00
P_InitializeSeg ( seg ) ;
2019-12-29 18:54:04 +00:00
seg - > angle = R_PointToAngle2 ( v1 - > x , v1 - > y , v2 - > x , v2 - > y ) ;
2020-01-02 08:51:07 +00:00
if ( seg - > linedef )
segs [ i ] . offset = FixedHypot ( v1 - > x - seg - > linedef - > v1 - > x , v1 - > y - seg - > linedef - > v1 - > y ) ;
2019-12-29 14:15:32 +00:00
}
2019-12-29 19:24:14 +00:00
return true ;
2019-12-29 18:54:04 +00:00
}
// Auxiliary function: Shrink node ID from 32-bit to 16-bit.
static UINT16 ShrinkNodeID ( UINT32 x ) {
UINT16 mask = ( x > > 16 ) & 0xC000 ;
UINT16 result = x ;
return result | mask ;
}
2019-12-29 21:11:15 +00:00
static void P_LoadExtendedNodes ( UINT8 * * data , nodetype_t nodetype )
2019-12-29 18:54:04 +00:00
{
node_t * mn ;
size_t i , j , k ;
boolean xgl3 = ( nodetype = = NT_XGL3 ) ;
2019-12-29 14:15:32 +00:00
2019-12-29 21:11:15 +00:00
numnodes = READINT32 ( ( * data ) ) ;
2019-12-29 14:15:32 +00:00
nodes = Z_Calloc ( numnodes * sizeof ( * nodes ) , PU_LEVEL , NULL ) ;
2019-12-29 18:54:04 +00:00
for ( i = 0 , mn = nodes ; i < numnodes ; i + + , mn + + )
{
// Splitter
2019-12-29 21:11:15 +00:00
mn - > x = xgl3 ? READINT32 ( ( * data ) ) : ( READINT16 ( ( * data ) ) < < FRACBITS ) ;
mn - > y = xgl3 ? READINT32 ( ( * data ) ) : ( READINT16 ( ( * data ) ) < < FRACBITS ) ;
mn - > dx = xgl3 ? READINT32 ( ( * data ) ) : ( READINT16 ( ( * data ) ) < < FRACBITS ) ;
mn - > dy = xgl3 ? READINT32 ( ( * data ) ) : ( READINT16 ( ( * data ) ) < < FRACBITS ) ;
2019-12-29 18:54:04 +00:00
// Bounding boxes
for ( j = 0 ; j < 2 ; j + + )
for ( k = 0 ; k < 4 ; k + + )
2019-12-29 21:11:15 +00:00
mn - > bbox [ j ] [ k ] = READINT16 ( ( * data ) ) < < FRACBITS ;
2019-12-29 18:54:04 +00:00
//Children
2019-12-29 21:11:15 +00:00
mn - > children [ 0 ] = ShrinkNodeID ( READUINT32 ( ( * data ) ) ) ; /// \todo Use UINT32 for node children in a future, instead?
mn - > children [ 1 ] = ShrinkNodeID ( READUINT32 ( ( * data ) ) ) ;
2019-12-29 18:54:04 +00:00
}
2019-12-29 14:15:32 +00:00
}
2019-12-28 15:40:35 +00:00
static void P_LoadMapBSP ( const virtres_t * virt )
{
2019-12-29 21:11:15 +00:00
UINT8 * nodedata = NULL ;
nodetype_t nodetype = P_GetNodetype ( virt , & nodedata ) ;
2019-12-29 13:17:51 +00:00
switch ( nodetype )
{
case NT_DOOM :
2019-12-29 14:50:53 +00:00
{
virtlump_t * virtssectors = vres_Find ( virt , " SSECTORS " ) ;
2019-12-29 21:11:15 +00:00
virtlump_t * virtnodes = vres_Find ( virt , " NODES " ) ;
2019-12-29 14:50:53 +00:00
virtlump_t * virtsegs = vres_Find ( virt , " SEGS " ) ;
2019-12-29 13:17:51 +00:00
numsubsectors = virtssectors - > size / sizeof ( mapsubsector_t ) ;
numnodes = virtnodes - > size / sizeof ( mapnode_t ) ;
numsegs = virtsegs - > size / sizeof ( mapseg_t ) ;
if ( numsubsectors < = 0 )
I_Error ( " Level has no subsectors (did you forget to run it through a nodesbuilder?) " ) ;
if ( numnodes < = 0 )
I_Error ( " Level has no nodes " ) ;
if ( numsegs < = 0 )
I_Error ( " Level has no segs " ) ;
subsectors = Z_Calloc ( numsubsectors * sizeof ( * subsectors ) , PU_LEVEL , NULL ) ;
nodes = Z_Calloc ( numnodes * sizeof ( * nodes ) , PU_LEVEL , NULL ) ;
segs = Z_Calloc ( numsegs * sizeof ( * segs ) , PU_LEVEL , NULL ) ;
P_LoadSubsectors ( virtssectors - > data ) ;
P_LoadNodes ( virtnodes - > data ) ;
P_LoadSegs ( virtsegs - > data ) ;
break ;
2019-12-29 14:50:53 +00:00
}
2019-12-29 13:17:51 +00:00
case NT_XNOD :
case NT_XGLN :
case NT_XGL3 :
2019-12-29 21:11:15 +00:00
if ( ! P_LoadExtraVertices ( & nodedata ) )
2019-12-29 18:54:04 +00:00
return ;
2019-12-29 21:11:15 +00:00
if ( ! P_LoadExtendedSubsectorsAndSegs ( & nodedata , nodetype ) )
2019-12-29 18:54:04 +00:00
return ;
2019-12-29 21:11:15 +00:00
P_LoadExtendedNodes ( & nodedata , nodetype ) ;
2019-12-29 13:17:51 +00:00
break ;
default :
CONS_Alert ( CONS_WARNING , " Unsupported BSP format detected. \n " ) ;
return ;
}
return ;
2019-12-28 15:40:35 +00:00
}
// Split from P_LoadBlockMap for convenience
// -- Monster Iestyn 08/01/18
static void P_ReadBlockMapLump ( INT16 * wadblockmaplump , size_t count )
{
size_t i ;
blockmaplump = Z_Calloc ( sizeof ( * blockmaplump ) * count , PU_LEVEL , NULL ) ;
// 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.
blockmaplump [ 0 ] = SHORT ( wadblockmaplump [ 0 ] ) ;
blockmaplump [ 1 ] = SHORT ( wadblockmaplump [ 1 ] ) ;
blockmaplump [ 2 ] = ( INT32 ) ( SHORT ( wadblockmaplump [ 2 ] ) ) & 0xffff ;
blockmaplump [ 3 ] = ( INT32 ) ( SHORT ( wadblockmaplump [ 3 ] ) ) & 0xffff ;
for ( i = 4 ; i < count ; i + + )
{
INT16 t = SHORT ( wadblockmaplump [ i ] ) ; // killough 3/1/98
blockmaplump [ i ] = t = = - 1 ? ( INT32 ) - 1 : ( INT32 ) t & 0xffff ;
}
}
// This needs to be a separate function
// because making both the WAD and PK3 loading code use
// the same functions is trickier than it looks for blockmap
// -- Monster Iestyn 09/01/18
static boolean P_LoadBlockMap ( UINT8 * data , size_t count )
{
if ( ! count | | count > = 0x20000 )
return false ;
//CONS_Printf("Reading blockmap lump for pk3...\n");
// no need to malloc anything, assume the data is uncompressed for now
count / = 2 ;
P_ReadBlockMapLump ( ( INT16 * ) data , count ) ;
bmaporgx = blockmaplump [ 0 ] < < FRACBITS ;
bmaporgy = blockmaplump [ 1 ] < < FRACBITS ;
bmapwidth = blockmaplump [ 2 ] ;
bmapheight = blockmaplump [ 3 ] ;
// clear out mobj chains
count = sizeof ( * blocklinks ) * bmapwidth * bmapheight ;
blocklinks = Z_Calloc ( count , PU_LEVEL , NULL ) ;
blockmap = blockmaplump + 4 ;
# ifdef POLYOBJECTS
// haleyjd 2/22/06: setup polyobject blockmap
count = sizeof ( * polyblocklinks ) * bmapwidth * bmapheight ;
polyblocklinks = Z_Calloc ( count , PU_LEVEL , NULL ) ;
# endif
return true ;
}
static boolean LineInBlock ( fixed_t cx1 , fixed_t cy1 , fixed_t cx2 , fixed_t cy2 , fixed_t bx1 , fixed_t by1 )
{
fixed_t bbox [ 4 ] ;
line_t testline ;
vertex_t vtest ;
bbox [ BOXRIGHT ] = bx1 + MAPBLOCKUNITS ;
bbox [ BOXTOP ] = by1 + MAPBLOCKUNITS ;
bbox [ BOXLEFT ] = bx1 ;
bbox [ BOXBOTTOM ] = by1 ;
// Trivial rejection
if ( cx1 < bbox [ BOXLEFT ] & & cx2 < bbox [ BOXLEFT ] )
return false ;
if ( cx1 > bbox [ BOXRIGHT ] & & cx2 > bbox [ BOXRIGHT ] )
return false ;
if ( cy1 < bbox [ BOXBOTTOM ] & & cy2 < bbox [ BOXBOTTOM ] )
return false ;
if ( cy1 > bbox [ BOXTOP ] & & cy2 > bbox [ BOXTOP ] )
return false ;
// Rats, guess we gotta check
// if the line intersects
// any sides of the block.
cx1 < < = FRACBITS ;
cy1 < < = FRACBITS ;
cx2 < < = FRACBITS ;
cy2 < < = FRACBITS ;
bbox [ BOXTOP ] < < = FRACBITS ;
bbox [ BOXBOTTOM ] < < = FRACBITS ;
bbox [ BOXLEFT ] < < = FRACBITS ;
bbox [ BOXRIGHT ] < < = FRACBITS ;
testline . v1 = & vtest ;
testline . v1 - > x = cx1 ;
2014-03-15 16:59:03 +00:00
testline . v1 - > y = cy1 ;
testline . dx = cx2 - cx1 ;
testline . dy = cy2 - cy1 ;
2015-01-01 19:50:31 +00:00
if ( ( testline . dx > 0 ) ^ ( testline . dy > 0 ) )
testline . slopetype = ST_NEGATIVE ;
else
testline . slopetype = ST_POSITIVE ;
2014-03-15 16:59:03 +00:00
2015-01-01 19:50:31 +00:00
return P_BoxOnLineSide ( bbox , & testline ) = = - 1 ;
2014-03-15 16:59:03 +00:00
}
//
// killough 10/98:
//
// Rewritten to use faster algorithm.
//
// SSN Edit: Killough's wasn't accurate enough, sometimes excluding
// blocks that the line did in fact exist in, so now we use
// a fail-safe approach that puts a 'box' around each line.
//
// Please note: This section of code is not interchangable with TeamTNT's
// code which attempts to fix the same problem.
static void P_CreateBlockMap ( void )
{
register size_t i ;
fixed_t minx = INT32_MAX , miny = INT32_MAX , maxx = INT32_MIN , maxy = INT32_MIN ;
// First find limits of map
for ( i = 0 ; i < numvertexes ; i + + )
{
if ( vertexes [ i ] . x > > FRACBITS < minx )
minx = vertexes [ i ] . x > > FRACBITS ;
else if ( vertexes [ i ] . x > > FRACBITS > maxx )
maxx = vertexes [ i ] . x > > FRACBITS ;
if ( vertexes [ i ] . y > > FRACBITS < miny )
miny = vertexes [ i ] . y > > FRACBITS ;
else if ( vertexes [ i ] . y > > FRACBITS > maxy )
maxy = vertexes [ i ] . y > > FRACBITS ;
}
// Save blockmap parameters
bmaporgx = minx < < FRACBITS ;
bmaporgy = miny < < FRACBITS ;
bmapwidth = ( ( maxx - minx ) > > MAPBTOFRAC ) + 1 ;
bmapheight = ( ( maxy - miny ) > > MAPBTOFRAC ) + 1 ;
// Compute blockmap, which is stored as a 2d array of variable-sized lists.
//
// Pseudocode:
//
// For each linedef:
//
// Map the starting and ending vertices to blocks.
//
// Starting in the starting vertex's block, do:
//
// Add linedef to current block's list, dynamically resizing it.
//
// If current block is the same as the ending vertex's block, exit loop.
//
// Move to an adjacent block by moving towards the ending block in
// either the x or y direction, to the block which contains the linedef.
{
typedef struct
{
INT32 n , nalloc ;
INT32 * list ;
} bmap_t ; // blocklist structure
size_t tot = bmapwidth * bmapheight ; // size of blockmap
bmap_t * bmap = calloc ( tot , sizeof ( * bmap ) ) ; // array of blocklists
boolean straight ;
if ( bmap = = NULL ) I_Error ( " %s: Out of memory making blockmap " , " P_CreateBlockMap " ) ;
for ( i = 0 ; i < numlines ; i + + )
{
// starting coordinates
INT32 x = ( lines [ i ] . v1 - > x > > FRACBITS ) - minx ;
INT32 y = ( lines [ i ] . v1 - > y > > FRACBITS ) - miny ;
INT32 bxstart , bxend , bystart , byend , v2x , v2y , curblockx , curblocky ;
v2x = lines [ i ] . v2 - > x > > FRACBITS ;
v2y = lines [ i ] . v2 - > y > > FRACBITS ;
// Draw a "box" around the line.
bxstart = ( x > > MAPBTOFRAC ) ;
bystart = ( y > > MAPBTOFRAC ) ;
v2x - = minx ;
v2y - = miny ;
bxend = ( ( v2x ) > > MAPBTOFRAC ) ;
byend = ( ( v2y ) > > MAPBTOFRAC ) ;
if ( bxend < bxstart )
{
INT32 temp = bxstart ;
bxstart = bxend ;
bxend = temp ;
}
if ( byend < bystart )
{
INT32 temp = bystart ;
bystart = byend ;
byend = temp ;
}
// Catch straight lines
// This fixes the error where straight lines
// directly on a blockmap boundary would not
// be included in the proper blocks.
if ( lines [ i ] . v1 - > y = = lines [ i ] . v2 - > y )
{
straight = true ;
bystart - - ;
byend + + ;
}
else if ( lines [ i ] . v1 - > x = = lines [ i ] . v2 - > x )
{
straight = true ;
bxstart - - ;
bxend + + ;
}
else
straight = false ;
// Now we simply iterate block-by-block until we reach the end block.
for ( curblockx = bxstart ; curblockx < = bxend ; curblockx + + )
for ( curblocky = bystart ; curblocky < = byend ; curblocky + + )
{
size_t b = curblocky * bmapwidth + curblockx ;
if ( b > = tot )
continue ;
if ( ! straight & & ! ( LineInBlock ( ( fixed_t ) x , ( fixed_t ) y , ( fixed_t ) v2x , ( fixed_t ) v2y , ( fixed_t ) ( curblockx < < MAPBTOFRAC ) , ( fixed_t ) ( curblocky < < MAPBTOFRAC ) ) ) )
continue ;
// Increase size of allocated list if necessary
if ( bmap [ b ] . n > = bmap [ b ] . nalloc )
{
// Graue 02-29-2004: make code more readable, don't realloc a null pointer
// (because it crashes for me, and because the comp.lang.c FAQ says so)
if ( bmap [ b ] . nalloc = = 0 )
bmap [ b ] . nalloc = 8 ;
else
bmap [ b ] . nalloc * = 2 ;
bmap [ b ] . list = Z_Realloc ( bmap [ b ] . list , bmap [ b ] . nalloc * sizeof ( * bmap - > list ) , PU_CACHE , & bmap [ b ] . list ) ;
if ( ! bmap [ b ] . list )
I_Error ( " Out of Memory in P_CreateBlockMap " ) ;
}
// Add linedef to end of list
bmap [ b ] . list [ bmap [ b ] . n + + ] = ( INT32 ) i ;
}
}
// Compute the total size of the blockmap.
//
// Compression of empty blocks is performed by reserving two offset words
// at tot and tot+1.
//
// 4 words, unused if this routine is called, are reserved at the start.
{
size_t count = tot + 6 ; // we need at least 1 word per block, plus reserved's
for ( i = 0 ; i < tot ; i + + )
if ( bmap [ i ] . n )
count + = bmap [ i ] . n + 2 ; // 1 header word + 1 trailer word + blocklist
// Allocate blockmap lump with computed count
blockmaplump = Z_Calloc ( sizeof ( * blockmaplump ) * count , PU_LEVEL , NULL ) ;
}
// Now compress the blockmap.
{
size_t ndx = tot + = 4 ; // Advance index to start of linedef lists
bmap_t * bp = bmap ; // Start of uncompressed blockmap
blockmaplump [ ndx + + ] = 0 ; // Store an empty blockmap list at start
blockmaplump [ ndx + + ] = - 1 ; // (Used for compression)
for ( i = 4 ; i < tot ; i + + , bp + + )
if ( bp - > n ) // Non-empty blocklist
{
blockmaplump [ blockmaplump [ i ] = ( INT32 ) ( ndx + + ) ] = 0 ; // Store index & header
do
blockmaplump [ ndx + + ] = bp - > list [ - - bp - > n ] ; // Copy linedef list
while ( bp - > n ) ;
blockmaplump [ ndx + + ] = - 1 ; // Store trailer
Z_Free ( bp - > list ) ; // Free linedef list
}
else // Empty blocklist: point to reserved empty blocklist
blockmaplump [ i ] = ( INT32 ) tot ;
free ( bmap ) ; // Free uncompressed blockmap
}
}
{
size_t count = sizeof ( * blocklinks ) * bmapwidth * bmapheight ;
// clear out mobj chains (copied from from P_LoadBlockMap)
blocklinks = Z_Calloc ( count , PU_LEVEL , NULL ) ;
blockmap = blockmaplump + 4 ;
# ifdef POLYOBJECTS
// haleyjd 2/22/06: setup polyobject blockmap
count = sizeof ( * polyblocklinks ) * bmapwidth * bmapheight ;
polyblocklinks = Z_Calloc ( count , PU_LEVEL , NULL ) ;
# endif
}
2019-12-28 15:40:35 +00:00
}
// PK3 version
// -- Monster Iestyn 09/01/18
static void P_LoadReject ( UINT8 * data , size_t count )
{
if ( ! count ) // zero length, someone probably used ZDBSP
{
rejectmatrix = NULL ;
CONS_Debug ( DBG_SETUP , " P_LoadReject: REJECT lump has size 0, will not be loaded \n " ) ;
}
else
{
rejectmatrix = Z_Malloc ( count , PU_LEVEL , NULL ) ; // allocate memory for the reject matrix
M_Memcpy ( rejectmatrix , data , count ) ; // copy the data into it
}
}
static void P_LoadMapLUT ( const virtres_t * virt )
{
virtlump_t * virtblockmap = vres_Find ( virt , " BLOCKMAP " ) ;
virtlump_t * virtreject = vres_Find ( virt , " REJECT " ) ;
// Lookup tables
if ( virtreject )
P_LoadReject ( virtreject - > data , virtreject - > size ) ;
else
rejectmatrix = NULL ;
if ( ! ( virtblockmap & & P_LoadBlockMap ( virtblockmap - > data , virtblockmap - > size ) ) )
P_CreateBlockMap ( ) ;
}
2014-03-15 16:59:03 +00:00
//
2019-12-28 16:40:08 +00:00
// P_LinkMapData
2014-03-15 16:59:03 +00:00
// Builds sector line lists and subsector sector numbers.
// Finds block bounding boxes for sectors.
//
2019-12-28 16:40:08 +00:00
static void P_LinkMapData ( void )
2014-03-15 16:59:03 +00:00
{
2016-04-02 16:01:58 +00:00
size_t i , j ;
2014-03-15 16:59:03 +00:00
line_t * li ;
sector_t * sector ;
subsector_t * ss = subsectors ;
size_t sidei ;
seg_t * seg ;
fixed_t bbox [ 4 ] ;
// look up sector number for each subsector
for ( i = 0 ; i < numsubsectors ; i + + , ss + + )
{
if ( ss - > firstline > = numsegs )
2019-12-28 16:40:08 +00:00
CorruptMapError ( va ( " P_LinkMapData: ss->firstline invalid "
2014-03-15 16:59:03 +00:00
" (subsector %s, firstline refers to %d of %s) " , sizeu1 ( i ) , ss - > firstline ,
sizeu2 ( numsegs ) ) ) ;
seg = & segs [ ss - > firstline ] ;
sidei = ( size_t ) ( seg - > sidedef - sides ) ;
if ( ! seg - > sidedef )
2019-12-28 16:40:08 +00:00
CorruptMapError ( va ( " P_LinkMapData: seg->sidedef is NULL "
2014-03-15 16:59:03 +00:00
" (subsector %s, firstline is %d) " , sizeu1 ( i ) , ss - > firstline ) ) ;
if ( seg - > sidedef - sides < 0 | | seg - > sidedef - sides > ( UINT16 ) numsides )
2019-12-28 16:40:08 +00:00
CorruptMapError ( va ( " P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
2014-03-15 16:59:03 +00:00
" (subsector %s, firstline is %d) " , sizeu1 ( sidei ) , sizeu2 ( numsides ) ,
sizeu3 ( i ) , ss - > firstline ) ) ;
if ( ! seg - > sidedef - > sector )
2019-12-28 16:40:08 +00:00
CorruptMapError ( va ( " P_LinkMapData: seg->sidedef->sector is NULL "
2014-03-15 16:59:03 +00:00
" (subsector %s, firstline is %d, sidedef is %s) " , sizeu1 ( i ) , ss - > firstline ,
sizeu1 ( sidei ) ) ) ;
ss - > sector = seg - > sidedef - > sector ;
}
// count number of lines in each sector
for ( i = 0 , li = lines ; i < numlines ; i + + , li + + )
{
li - > frontsector - > linecount + + ;
if ( li - > backsector & & li - > backsector ! = li - > frontsector )
li - > backsector - > linecount + + ;
}
// allocate linebuffers for each sector
for ( i = 0 , sector = sectors ; i < numsectors ; i + + , sector + + )
{
2016-06-11 15:14:08 +00:00
if ( sector - > linecount = = 0 ) // no lines found?
{
sector - > lines = NULL ;
2019-12-28 16:40:08 +00:00
CONS_Debug ( DBG_SETUP , " P_LinkMapData: sector %s has no lines \n " , sizeu1 ( i ) ) ;
2016-06-11 15:14:08 +00:00
}
else
{
sector - > lines = Z_Calloc ( sector - > linecount * sizeof ( line_t * ) , PU_LEVEL , NULL ) ;
2014-03-15 16:59:03 +00:00
2016-06-11 15:14:08 +00:00
// zero the count, since we'll later use this to track how many we've recorded
sector - > linecount = 0 ;
}
2014-03-15 16:59:03 +00:00
}
// iterate through lines, assigning them to sectors' linebuffers,
// and recalculate the counts in the process
for ( i = 0 , li = lines ; i < numlines ; i + + , li + + )
{
li - > frontsector - > lines [ li - > frontsector - > linecount + + ] = li ;
if ( li - > backsector & & li - > backsector ! = li - > frontsector )
li - > backsector - > lines [ li - > backsector - > linecount + + ] = li ;
}
// set soundorg's position for each sector
for ( i = 0 , sector = sectors ; i < numsectors ; i + + , sector + + )
{
M_ClearBox ( bbox ) ;
2016-06-11 15:14:08 +00:00
if ( sector - > linecount ! = 0 )
2014-03-15 16:59:03 +00:00
{
2016-06-11 15:14:08 +00:00
for ( j = 0 ; j < sector - > linecount ; j + + )
{
li = sector - > lines [ j ] ;
M_AddToBox ( bbox , li - > v1 - > x , li - > v1 - > y ) ;
M_AddToBox ( bbox , li - > v2 - > x , li - > v2 - > y ) ;
}
2014-03-15 16:59:03 +00:00
}
// set the degenmobj_t to the middle of the bounding box
sector - > soundorg . x = ( ( ( bbox [ BOXRIGHT ] > > FRACBITS ) + ( bbox [ BOXLEFT ] > > FRACBITS ) ) / 2 ) < < FRACBITS ;
sector - > soundorg . y = ( ( ( bbox [ BOXTOP ] > > FRACBITS ) + ( bbox [ BOXBOTTOM ] > > FRACBITS ) ) / 2 ) < < FRACBITS ;
sector - > soundorg . z = sector - > floorheight ; // default to sector's floor height
}
}
2020-01-05 13:31:56 +00:00
//For maps in binary format, converts setup of specials to UDMF format.
static void P_ConvertBinaryMap ( void )
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
switch ( lines [ i ] . special )
{
2020-01-11 14:38:50 +00:00
case 443 : //Call Lua function
if ( lines [ i ] . text )
{
lines [ i ] . stringargs [ 0 ] = Z_Malloc ( strlen ( lines [ i ] . text ) + 1 , PU_LEVEL , NULL ) ;
M_Memcpy ( lines [ i ] . stringargs [ 0 ] , lines [ i ] . text , strlen ( lines [ i ] . text ) + 1 ) ;
}
else
CONS_Alert ( CONS_WARNING , " Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields) \n " , sizeu1 ( i ) ) ;
break ;
2020-03-20 10:19:30 +00:00
case 447 : //Change colormap
lines [ i ] . args [ 0 ] = lines [ i ] . tag ;
if ( lines [ i ] . flags & ML_EFFECT3 )
lines [ i ] . args [ 2 ] | = 1 ;
if ( lines [ i ] . flags & ML_EFFECT1 )
lines [ i ] . args [ 2 ] | = 34 ;
if ( lines [ i ] . flags & ML_NOCLIMB )
lines [ i ] . args [ 2 ] | = 68 ;
if ( lines [ i ] . flags & ML_EFFECT2 )
lines [ i ] . args [ 2 ] | = 136 ;
break ;
case 455 : //Fade colormap
{
INT32 speed = ( INT32 ) ( ( ( ( lines [ i ] . flags & ML_DONTPEGBOTTOM ) | | ! sides [ lines [ i ] . sidenum [ 0 ] ] . rowoffset ) & & lines [ i ] . sidenum [ 1 ] ! = 0xFFFF ) ?
abs ( sides [ lines [ i ] . sidenum [ 1 ] ] . rowoffset > > FRACBITS )
: abs ( sides [ lines [ i ] . sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ) ;
lines [ i ] . args [ 0 ] = lines [ i ] . tag ;
if ( lines [ i ] . flags & ML_EFFECT4 )
lines [ i ] . args [ 2 ] = speed ;
else
lines [ i ] . args [ 2 ] = ( 256 + speed - 1 ) / speed ;
if ( lines [ i ] . flags & ML_EFFECT3 )
lines [ i ] . args [ 3 ] | = 1 ;
if ( lines [ i ] . flags & ML_EFFECT1 )
lines [ i ] . args [ 3 ] | = 34 ;
if ( lines [ i ] . flags & ML_NOCLIMB )
lines [ i ] . args [ 3 ] | = 68 ;
if ( lines [ i ] . flags & ML_EFFECT2 )
lines [ i ] . args [ 3 ] | = 136 ;
if ( lines [ i ] . flags & ML_BOUNCY )
lines [ i ] . args [ 3 ] | = 4096 ;
if ( lines [ i ] . flags & ML_EFFECT5 )
lines [ i ] . args [ 3 ] | = 8192 ;
break ;
}
case 456 : //Stop fading colormap
lines [ i ] . args [ 0 ] = lines [ i ] . tag ;
break ;
case 606 : //Colormap
lines [ i ] . args [ 0 ] = lines [ i ] . tag ;
break ;
2020-01-05 13:31:56 +00:00
case 700 : //Slope front sector floor
case 701 : //Slope front sector ceiling
case 702 : //Slope front sector floor and ceiling
case 703 : //Slope front sector floor and back sector ceiling
case 710 : //Slope back sector floor
case 711 : //Slope back sector ceiling
case 712 : //Slope back sector floor and ceiling
case 713 : //Slope back sector floor and front sector ceiling
{
boolean frontfloor = ( lines [ i ] . special = = 700 | | lines [ i ] . special = = 702 | | lines [ i ] . special = = 703 ) ;
boolean backfloor = ( lines [ i ] . special = = 710 | | lines [ i ] . special = = 712 | | lines [ i ] . special = = 713 ) ;
boolean frontceil = ( lines [ i ] . special = = 701 | | lines [ i ] . special = = 702 | | lines [ i ] . special = = 713 ) ;
boolean backceil = ( lines [ i ] . special = = 711 | | lines [ i ] . special = = 712 | | lines [ i ] . special = = 703 ) ;
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 0 ] = backfloor ? TMS_BACK : ( frontfloor ? TMS_FRONT : TMS_NONE ) ;
lines [ i ] . args [ 1 ] = backceil ? TMS_BACK : ( frontceil ? TMS_FRONT : TMS_NONE ) ;
2020-01-05 13:31:56 +00:00
if ( lines [ i ] . flags & ML_NETONLY )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 2 ] | = TMSL_NOPHYSICS ;
2020-01-05 13:31:56 +00:00
if ( lines [ i ] . flags & ML_NONET )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 2 ] | = TMSL_DYNAMIC ;
2020-01-05 13:31:56 +00:00
lines [ i ] . special = 700 ;
break ;
}
2020-01-06 14:34:28 +00:00
case 704 : //Slope front sector floor by 3 tagged vertices
case 705 : //Slope front sector ceiling by 3 tagged vertices
case 714 : //Slope back sector floor by 3 tagged vertices
case 715 : //Slope back sector ceiling by 3 tagged vertices
{
if ( lines [ i ] . special = = 704 )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 0 ] = TMSP_FRONTFLOOR ;
2020-01-06 14:34:28 +00:00
else if ( lines [ i ] . special = = 705 )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 0 ] = TMSP_FRONTCEILING ;
2020-01-06 14:34:28 +00:00
else if ( lines [ i ] . special = = 714 )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 0 ] = TMSP_BACKFLOOR ;
2020-01-06 14:34:28 +00:00
else if ( lines [ i ] . special = = 715 )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 0 ] = TMSP_BACKCEILING ;
2020-01-06 14:34:28 +00:00
lines [ i ] . args [ 1 ] = lines [ i ] . tag ;
if ( lines [ i ] . flags & ML_EFFECT6 )
{
UINT8 side = lines [ i ] . special > = 714 ;
if ( side = = 1 & & lines [ i ] . sidenum [ 1 ] = = 0xffff )
CONS_Debug ( DBG_GAMELOGIC , " P_ConvertBinaryMap: Line special %d (line #%s) missing 2nd side! \n " , lines [ i ] . special , sizeu1 ( i ) ) ;
else
{
lines [ i ] . args [ 2 ] = sides [ lines [ i ] . sidenum [ side ] ] . textureoffset > > FRACBITS ;
lines [ i ] . args [ 3 ] = sides [ lines [ i ] . sidenum [ side ] ] . rowoffset > > FRACBITS ;
}
}
else
{
lines [ i ] . args [ 2 ] = lines [ i ] . args [ 1 ] ;
lines [ i ] . args [ 3 ] = lines [ i ] . args [ 1 ] ;
}
if ( lines [ i ] . flags & ML_NETONLY )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 4 ] | = TMSL_NOPHYSICS ;
2020-01-06 14:34:28 +00:00
if ( lines [ i ] . flags & ML_NONET )
2020-04-18 14:55:56 +00:00
lines [ i ] . args [ 4 ] | = TMSL_DYNAMIC ;
2020-01-06 14:34:28 +00:00
lines [ i ] . special = 704 ;
break ;
}
2020-01-06 16:39:39 +00:00
case 720 : //Copy front side floor slope
case 721 : //Copy front side ceiling slope
case 722 : //Copy front side floor and ceiling slope
if ( lines [ i ] . special ! = 721 )
lines [ i ] . args [ 0 ] = lines [ i ] . tag ;
if ( lines [ i ] . special ! = 720 )
lines [ i ] . args [ 1 ] = lines [ i ] . tag ;
lines [ i ] . special = 720 ;
break ;
2020-01-26 11:24:52 +00:00
case 900 : //Translucent wall (10%)
case 901 : //Translucent wall (20%)
case 902 : //Translucent wall (30%)
case 903 : //Translucent wall (40%)
case 904 : //Translucent wall (50%)
case 905 : //Translucent wall (60%)
case 906 : //Translucent wall (70%)
case 907 : //Translucent wall (80%)
case 908 : //Translucent wall (90%)
lines [ i ] . alpha = ( ( 909 - lines [ i ] . special ) < < FRACBITS ) / 10 ;
break ;
2020-01-05 13:31:56 +00:00
default :
break ;
}
}
2020-01-25 09:01:01 +00:00
for ( i = 0 ; i < nummapthings ; i + + )
{
switch ( mapthings [ i ] . type )
{
case 750 :
2020-01-25 09:15:20 +00:00
case 760 :
case 761 :
case 762 :
2020-01-25 09:01:01 +00:00
mapthings [ i ] . tag = mapthings [ i ] . angle ;
break ;
2020-01-25 09:26:38 +00:00
case 780 :
mapthings [ i ] . tag = mapthings [ i ] . extrainfo ;
break ;
2020-01-25 09:01:01 +00:00
default :
break ;
}
}
2020-01-05 13:31:56 +00:00
}
2019-12-28 10:30:39 +00:00
/** Compute MD5 message digest for bytes read from memory source
*
* The resulting message digest number will be written into the 16 bytes
* beginning at RESBLOCK .
*
* \ param filename path of file
* \ param resblock resulting MD5 checksum
* \ return 0 if MD5 checksum was made , and is at resblock , 1 if error was found
*/
2019-12-28 15:40:35 +00:00
static INT32 P_MakeBufferMD5 ( const char * buffer , size_t len , void * resblock )
2019-12-28 10:30:39 +00:00
{
# ifdef NOMD5
( void ) buffer ;
( void ) len ;
memset ( resblock , 0x00 , 16 ) ;
return 1 ;
# else
tic_t t = I_GetTime ( ) ;
CONS_Debug ( DBG_SETUP , " Making MD5 \n " ) ;
if ( md5_buffer ( buffer , len , resblock ) = = NULL )
return 1 ;
CONS_Debug ( DBG_SETUP , " MD5 calc took %f seconds \n " , ( float ) ( I_GetTime ( ) - t ) / NEWTICRATE ) ;
return 0 ;
# endif
}
2019-12-28 15:40:35 +00:00
static void P_MakeMapMD5 ( virtres_t * virt , void * dest )
2019-12-28 10:30:39 +00:00
{
unsigned char resmd5 [ 16 ] ;
2019-12-30 11:07:54 +00:00
2020-03-15 15:23:15 +00:00
if ( udmf )
{
virtlump_t * textmap = vres_Find ( virt , " TEXTMAP " ) ;
2019-12-30 11:07:54 +00:00
P_MakeBufferMD5 ( ( char * ) textmap - > data , textmap - > size , resmd5 ) ;
2020-03-15 15:23:15 +00:00
}
2019-12-30 11:07:54 +00:00
else
{
unsigned char linemd5 [ 16 ] ;
unsigned char sectormd5 [ 16 ] ;
unsigned char thingmd5 [ 16 ] ;
unsigned char sidedefmd5 [ 16 ] ;
UINT8 i ;
// Create a hash for the current map
// get the actual lumps!
virtlump_t * virtlines = vres_Find ( virt , " LINEDEFS " ) ;
virtlump_t * virtsectors = vres_Find ( virt , " SECTORS " ) ;
virtlump_t * virtmthings = vres_Find ( virt , " THINGS " ) ;
virtlump_t * virtsides = vres_Find ( virt , " SIDEDEFS " ) ;
P_MakeBufferMD5 ( ( char * ) virtlines - > data , virtlines - > size , linemd5 ) ;
P_MakeBufferMD5 ( ( char * ) virtsectors - > data , virtsectors - > size , sectormd5 ) ;
P_MakeBufferMD5 ( ( char * ) virtmthings - > data , virtmthings - > size , thingmd5 ) ;
P_MakeBufferMD5 ( ( char * ) virtsides - > data , virtsides - > size , sidedefmd5 ) ;
for ( i = 0 ; i < 16 ; i + + )
resmd5 [ i ] = ( linemd5 [ i ] + sectormd5 [ i ] + thingmd5 [ i ] + sidedefmd5 [ i ] ) & 0xFF ;
}
2019-12-28 10:30:39 +00:00
M_Memcpy ( dest , & resmd5 , 16 ) ;
}
2019-12-30 13:47:48 +00:00
static boolean P_LoadMapFromFile ( void )
2019-12-28 10:30:39 +00:00
{
virtres_t * virt = vres_GetMap ( lastloadedmaplumpnum ) ;
2020-01-05 13:31:56 +00:00
virtlump_t * textmap = vres_Find ( virt , " TEXTMAP " ) ;
2020-03-15 15:23:15 +00:00
udmf = textmap ! = NULL ;
2019-12-28 10:30:39 +00:00
2019-12-30 13:47:48 +00:00
if ( ! P_LoadMapData ( virt ) )
return false ;
2019-12-28 10:30:39 +00:00
P_LoadMapBSP ( virt ) ;
P_LoadMapLUT ( virt ) ;
2019-12-28 16:40:08 +00:00
P_LinkMapData ( ) ;
2019-12-28 10:30:39 +00:00
2020-03-15 15:23:15 +00:00
if ( ! udmf )
2020-01-05 13:31:56 +00:00
P_ConvertBinaryMap ( ) ;
2020-01-04 10:08:05 +00:00
// Copy relevant map data for NetArchive purposes.
spawnsectors = Z_Calloc ( numsectors * sizeof ( * sectors ) , PU_LEVEL , NULL ) ;
spawnlines = Z_Calloc ( numlines * sizeof ( * lines ) , PU_LEVEL , NULL ) ;
spawnsides = Z_Calloc ( numsides * sizeof ( * sides ) , PU_LEVEL , NULL ) ;
memcpy ( spawnsectors , sectors , numsectors * sizeof ( * sectors ) ) ;
memcpy ( spawnlines , lines , numlines * sizeof ( * lines ) ) ;
memcpy ( spawnsides , sides , numsides * sizeof ( * sides ) ) ;
2019-12-28 10:30:39 +00:00
P_MakeMapMD5 ( virt , & mapmd5 ) ;
vres_Free ( virt ) ;
2019-12-30 13:47:48 +00:00
return true ;
2019-12-28 10:30:39 +00:00
}
2019-12-28 15:40:35 +00:00
//
// LEVEL INITIALIZATION FUNCTIONS
//
2014-03-15 16:59:03 +00:00
/** Sets up a sky texture to use for the level.
* The sky texture is used instead of F_SKY1 .
*/
void P_SetupLevelSky ( INT32 skynum , boolean global )
{
char skytexname [ 12 ] ;
sprintf ( skytexname , " SKY%d " , skynum ) ;
skytexture = R_TextureNumForName ( skytexname ) ;
levelskynum = skynum ;
// Global change
if ( global )
globallevelskynum = levelskynum ;
// Don't go beyond for dedicated servers
if ( dedicated )
return ;
// scale up the old skies, if needed
R_SetupSkyDraw ( ) ;
}
static const char * maplumpname ;
lumpnum_t lastloadedmaplumpnum ; // for comparative savegame
//
// P_LevelInitStuff
//
// Some player initialization for map start.
//
2019-12-28 11:48:32 +00:00
static void P_InitLevelSettings ( void )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
2017-05-30 19:31:51 +00:00
boolean canresetlives = true ;
2014-03-15 16:59:03 +00:00
leveltime = 0 ;
localaiming = 0 ;
localaiming2 = 0 ;
2019-08-04 15:34:55 +00:00
modulothing = 0 ;
2014-03-15 16:59:03 +00:00
// special stage tokens, emeralds, and ring total
tokenbits = 0 ;
runemeraldmanager = false ;
2015-05-27 08:01:58 +00:00
emeraldspawndelay = 60 * TICRATE ;
2019-11-14 18:25:19 +00:00
if ( ( netgame | | multiplayer ) & & ! G_IsSpecialStage ( gamemap ) )
nummaprings = - 1 ;
else
nummaprings = mapheaderinfo [ gamemap - 1 ] - > startrings ;
2014-03-15 16:59:03 +00:00
// emerald hunt
hunt1 = hunt2 = hunt3 = NULL ;
// map time limit
if ( mapheaderinfo [ gamemap - 1 ] - > countdown )
2017-07-04 13:58:58 +00:00
{
tic_t maxtime = 0 ;
2014-03-15 16:59:03 +00:00
countdowntimer = mapheaderinfo [ gamemap - 1 ] - > countdown * TICRATE ;
2017-07-04 13:58:58 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
if ( players [ i ] . starposttime > maxtime )
maxtime = players [ i ] . starposttime ;
}
countdowntimer - = maxtime ;
}
2014-03-15 16:59:03 +00:00
else
countdowntimer = 0 ;
countdowntimeup = false ;
// clear ctf pointers
redflag = blueflag = NULL ;
rflagpoint = bflagpoint = NULL ;
// circuit, race and competition stuff
circuitmap = false ;
numstarposts = 0 ;
2018-06-03 21:41:54 +00:00
ssspheres = timeinmap = 0 ;
2014-03-15 16:59:03 +00:00
// special stage
2019-07-30 15:44:40 +00:00
stagefailed = true ; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial
2014-03-15 16:59:03 +00:00
// Reset temporary record data
memset ( & ntemprecords , 0 , sizeof ( nightsdata_t ) ) ;
// earthquake camera
memset ( & quake , 0 , sizeof ( struct quake ) ) ;
2019-12-27 20:14:56 +00:00
if ( ( netgame | | multiplayer ) & & G_GametypeUsesCoopStarposts ( ) & & cv_coopstarposts . value = = 2 )
2017-05-30 19:31:51 +00:00
{
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] & & players [ i ] . lives > 0 )
{
canresetlives = false ;
break ;
}
}
}
2019-06-29 19:55:58 +00:00
countdown = countdown2 = exitfadestarted = 0 ;
2018-06-09 17:06:14 +00:00
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2019-09-18 15:06:13 +00:00
G_PlayerReborn ( i , true ) ;
2019-12-27 15:28:00 +00:00
if ( canresetlives & & ( netgame | | multiplayer ) & & playeringame [ i ] & & ( G_CompetitionGametype ( ) | | players [ i ] . lives < = 0 ) )
2014-03-15 16:59:03 +00:00
{
// In Co-Op, replenish a user's lives if they are depleted.
players [ i ] . lives = cv_startinglives . value ;
}
2018-06-09 17:06:14 +00:00
// obliteration station...
2019-09-18 15:06:13 +00:00
players [ i ] . numboxes = players [ i ] . totalring = \
players [ i ] . laps = players [ i ] . marescore = players [ i ] . lastmarescore = \
players [ i ] . mare = players [ i ] . exiting = 0 ;
2014-03-15 16:59:03 +00:00
players [ i ] . drillmeter = 40 * 20 ;
2018-06-09 17:06:14 +00:00
// hit these too
2019-09-18 15:06:13 +00:00
players [ i ] . pflags & = ~ ( PF_GAMETYPEOVER ) ;
2014-03-15 16:59:03 +00:00
}
2019-09-18 15:06:13 +00:00
if ( botingame )
2019-12-30 20:01:14 +00:00
CV_SetValue ( & cv_analog [ 1 ] , true ) ;
2014-03-15 16:59:03 +00:00
}
2019-12-28 13:13:26 +00:00
// Respawns all the mapthings and mobjs in the map from the already loaded map data.
void P_RespawnThings ( void )
2014-03-15 16:59:03 +00:00
{
// Search through all the thinkers.
thinker_t * think ;
2017-06-25 16:30:53 +00:00
INT32 i , viewid = - 1 , centerid = - 1 ; // for skyboxes
// check if these are any of the normal viewpoint/centerpoint mobjs in the level or not
if ( skyboxmo [ 0 ] | | skyboxmo [ 1 ] )
for ( i = 0 ; i < 16 ; i + + )
{
if ( skyboxmo [ 0 ] & & skyboxmo [ 0 ] = = skyboxviewpnts [ i ] )
viewid = i ; // save id just in case
if ( skyboxmo [ 1 ] & & skyboxmo [ 1 ] = = skyboxcenterpnts [ i ] )
centerid = i ; // save id just in case
}
2019-04-20 21:29:20 +00:00
for ( think = thlist [ THINK_MOBJ ] . next ; think ! = & thlist [ THINK_MOBJ ] ; think = think - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( think - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
P_RemoveMobj ( ( mobj_t * ) think ) ;
2014-03-15 16:59:03 +00:00
}
2019-12-28 11:48:32 +00:00
P_InitLevelSettings ( ) ;
2014-03-15 16:59:03 +00:00
2019-12-28 10:30:39 +00:00
P_SpawnMapThings ( true ) ;
2017-06-25 16:30:53 +00:00
// restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
skyboxmo [ 0 ] = skyboxviewpnts [ ( viewid > = 0 ) ? viewid : 0 ] ;
skyboxmo [ 1 ] = skyboxcenterpnts [ ( centerid > = 0 ) ? centerid : 0 ] ;
2014-03-15 16:59:03 +00:00
}
2014-08-27 03:56:30 +00:00
static void P_RunLevelScript ( const char * scriptname )
{
if ( ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_SCRIPTISFILE ) )
{
lumpnum_t lumpnum ;
char newname [ 9 ] ;
strncpy ( newname , scriptname , 8 ) ;
newname [ 8 ] = ' \0 ' ;
lumpnum = W_CheckNumForName ( newname ) ;
if ( lumpnum = = LUMPERROR | | W_LumpLength ( lumpnum ) = = 0 )
{
CONS_Debug ( DBG_SETUP , " SOC Error: script lump %s not found/not valid. \n " , newname ) ;
return ;
}
COM_BufInsertText ( W_CacheLumpNum ( lumpnum , PU_CACHE ) ) ;
}
else
{
COM_BufAddText ( va ( " exec %s \n " , scriptname ) ) ;
}
COM_BufExecute ( ) ; // Run it!
}
static void P_ForceCharacter ( const char * forcecharskin )
{
if ( netgame )
{
char skincmd [ 33 ] ;
if ( splitscreen )
{
sprintf ( skincmd , " skin2 %s \n " , forcecharskin ) ;
CV_Set ( & cv_skin2 , forcecharskin ) ;
}
sprintf ( skincmd , " skin %s \n " , forcecharskin ) ;
COM_BufAddText ( skincmd ) ;
}
else
{
if ( splitscreen )
{
SetPlayerSkin ( secondarydisplayplayer , forcecharskin ) ;
if ( ( unsigned ) cv_playercolor2 . value ! = skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor )
{
CV_StealthSetValue ( & cv_playercolor2 , skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor ) ;
players [ secondarydisplayplayer ] . skincolor = skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor ;
}
}
SetPlayerSkin ( consoleplayer , forcecharskin ) ;
// normal player colors in single player
if ( ( unsigned ) cv_playercolor . value ! = skins [ players [ consoleplayer ] . skin ] . prefcolor )
{
CV_StealthSetValue ( & cv_playercolor , skins [ players [ consoleplayer ] . skin ] . prefcolor ) ;
players [ consoleplayer ] . skincolor = skins [ players [ consoleplayer ] . skin ] . prefcolor ;
}
}
}
2019-12-28 10:37:56 +00:00
static void P_ResetSpawnpoints ( void )
{
UINT8 i ;
numdmstarts = numredctfstarts = numbluectfstarts = 0 ;
// reset the player starts
for ( i = 0 ; i < MAXPLAYERS ; i + + )
playerstarts [ i ] = bluectfstarts [ i ] = redctfstarts [ i ] = NULL ;
for ( i = 0 ; i < MAX_DM_STARTS ; i + + )
deathmatchstarts [ i ] = NULL ;
for ( i = 0 ; i < 2 ; i + + )
skyboxmo [ i ] = NULL ;
for ( i = 0 ; i < 16 ; i + + )
skyboxviewpnts [ i ] = skyboxcenterpnts [ i ] = NULL ;
}
2014-08-27 03:56:30 +00:00
static void P_LoadRecordGhosts ( void )
{
const size_t glen = strlen ( srb2home ) + 1 + strlen ( " replay " ) + 1 + strlen ( timeattackfolder ) + 1 + strlen ( " MAPXX " ) + 1 ;
char * gpath = malloc ( glen ) ;
INT32 i ;
if ( ! gpath )
return ;
sprintf ( gpath , " %s " PATHSEP " replay " PATHSEP " %s " PATHSEP " %s " , srb2home , timeattackfolder , G_BuildMapName ( gamemap ) ) ;
// Best Score ghost
if ( cv_ghost_bestscore . value )
{
for ( i = 0 ; i < numskins ; + + i )
{
if ( cv_ghost_bestscore . value = = 1 & & players [ consoleplayer ] . skin ! = i )
continue ;
if ( FIL_FileExists ( va ( " %s-%s-score-best.lmp " , gpath , skins [ i ] . name ) ) )
G_AddGhost ( va ( " %s-%s-score-best.lmp " , gpath , skins [ i ] . name ) ) ;
}
}
// Best Time ghost
if ( cv_ghost_besttime . value )
{
for ( i = 0 ; i < numskins ; + + i )
{
if ( cv_ghost_besttime . value = = 1 & & players [ consoleplayer ] . skin ! = i )
continue ;
if ( FIL_FileExists ( va ( " %s-%s-time-best.lmp " , gpath , skins [ i ] . name ) ) )
G_AddGhost ( va ( " %s-%s-time-best.lmp " , gpath , skins [ i ] . name ) ) ;
}
}
// Best Rings ghost
if ( cv_ghost_bestrings . value )
{
for ( i = 0 ; i < numskins ; + + i )
{
if ( cv_ghost_bestrings . value = = 1 & & players [ consoleplayer ] . skin ! = i )
continue ;
if ( FIL_FileExists ( va ( " %s-%s-rings-best.lmp " , gpath , skins [ i ] . name ) ) )
G_AddGhost ( va ( " %s-%s-rings-best.lmp " , gpath , skins [ i ] . name ) ) ;
}
}
// Last ghost
if ( cv_ghost_last . value )
{
for ( i = 0 ; i < numskins ; + + i )
{
if ( cv_ghost_last . value = = 1 & & players [ consoleplayer ] . skin ! = i )
continue ;
if ( FIL_FileExists ( va ( " %s-%s-last.lmp " , gpath , skins [ i ] . name ) ) )
G_AddGhost ( va ( " %s-%s-last.lmp " , gpath , skins [ i ] . name ) ) ;
}
}
// Guest ghost
if ( cv_ghost_guest . value & & FIL_FileExists ( va ( " %s-guest.lmp " , gpath ) ) )
G_AddGhost ( va ( " %s-guest.lmp " , gpath ) ) ;
free ( gpath ) ;
}
2014-11-12 00:55:07 +00:00
static void P_LoadNightsGhosts ( void )
{
const size_t glen = strlen ( srb2home ) + 1 + strlen ( " replay " ) + 1 + strlen ( timeattackfolder ) + 1 + strlen ( " MAPXX " ) + 1 ;
char * gpath = malloc ( glen ) ;
if ( ! gpath )
return ;
sprintf ( gpath , " %s " PATHSEP " replay " PATHSEP " %s " PATHSEP " %s " , srb2home , timeattackfolder , G_BuildMapName ( gamemap ) ) ;
// Best Score ghost
if ( cv_ghost_bestscore . value & & FIL_FileExists ( va ( " %s-score-best.lmp " , gpath ) ) )
G_AddGhost ( va ( " %s-score-best.lmp " , gpath ) ) ;
// Best Time ghost
if ( cv_ghost_besttime . value & & FIL_FileExists ( va ( " %s-time-best.lmp " , gpath ) ) )
G_AddGhost ( va ( " %s-time-best.lmp " , gpath ) ) ;
// Last ghost
if ( cv_ghost_last . value & & FIL_FileExists ( va ( " %s-last.lmp " , gpath ) ) )
G_AddGhost ( va ( " %s-last.lmp " , gpath ) ) ;
// Guest ghost
if ( cv_ghost_guest . value & & FIL_FileExists ( va ( " %s-guest.lmp " , gpath ) ) )
G_AddGhost ( va ( " %s-guest.lmp " , gpath ) ) ;
free ( gpath ) ;
}
2019-12-28 10:30:39 +00:00
static void P_InitTagGametype ( void )
{
UINT8 i ;
INT32 realnumplayers = 0 ;
INT32 playersactive [ MAXPLAYERS ] ;
//I just realized how problematic this code can be.
//D_NumPlayers() will not always cover the scope of the netgame.
//What if one player is node 0 and the other node 31?
//The solution? Make a temp array of all players that are currently playing and pick from them.
//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
//Also, you'd never have to loop through all 32 players slots to find anything ever again.
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2020-02-23 22:28:16 +00:00
if ( playeringame [ i ] & & ! ( players [ i ] . spectator | | players [ i ] . quittime ) )
2019-12-28 10:30:39 +00:00
{
playersactive [ realnumplayers ] = i ; //stores the player's node in the array.
realnumplayers + + ;
}
}
if ( ! realnumplayers ) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
{
CONS_Printf ( M_GetText ( " No player currently available to become IT. Awaiting available players. \n " ) ) ;
return ;
}
i = P_RandomKey ( realnumplayers ) ;
players [ playersactive [ i ] ] . pflags | = PF_TAGIT ; //choose our initial tagger before map starts.
// Taken and modified from G_DoReborn()
// Remove the player so he can respawn elsewhere.
// first disassociate the corpse
if ( players [ playersactive [ i ] ] . mo )
P_RemoveMobj ( players [ playersactive [ i ] ] . mo ) ;
2020-01-22 02:57:22 +00:00
G_SpawnPlayer ( playersactive [ i ] ) ; //respawn the lucky player in his dedicated spawn location.
2019-12-28 10:30:39 +00:00
}
2018-11-12 20:07:45 +00:00
static void P_SetupCamera ( void )
{
if ( players [ displayplayer ] . mo & & ( server | | addedtogame ) )
{
camera . x = players [ displayplayer ] . mo - > x ;
camera . y = players [ displayplayer ] . mo - > y ;
camera . z = players [ displayplayer ] . mo - > z ;
camera . angle = players [ displayplayer ] . mo - > angle ;
camera . subsector = R_PointInSubsector ( camera . x , camera . y ) ; // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
}
else
{
mapthing_t * thing ;
2019-12-27 15:14:33 +00:00
if ( gametyperules & GTR_DEATHMATCHSTARTS )
2018-11-12 20:07:45 +00:00
thing = deathmatchstarts [ 0 ] ;
2019-12-27 05:07:13 +00:00
else
2018-11-12 20:07:45 +00:00
thing = playerstarts [ 0 ] ;
if ( thing )
{
camera . x = thing - > x ;
camera . y = thing - > y ;
camera . z = thing - > z ;
camera . angle = FixedAngle ( ( fixed_t ) thing - > angle < < FRACBITS ) ;
camera . subsector = R_PointInSubsector ( camera . x , camera . y ) ; // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
}
}
}
2019-12-28 10:30:39 +00:00
static void P_InitCamera ( void )
{
if ( ! dedicated )
{
P_SetupCamera ( ) ;
// Salt: CV_ClearChangedFlags() messes with your settings :(
/*if (!cv_cam_height.changed)
CV_Set ( & cv_cam_height , cv_cam_height . defaultvalue ) ;
if ( ! cv_cam2_height . changed )
CV_Set ( & cv_cam2_height , cv_cam2_height . defaultvalue ) ;
if ( ! cv_cam_dist . changed )
CV_Set ( & cv_cam_dist , cv_cam_dist . defaultvalue ) ;
if ( ! cv_cam2_dist . changed )
CV_Set ( & cv_cam2_dist , cv_cam2_dist . defaultvalue ) ; */
// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
if ( ! cv_cam_rotate . changed )
CV_Set ( & cv_cam_rotate , cv_cam_rotate . defaultvalue ) ;
if ( ! cv_cam2_rotate . changed )
CV_Set ( & cv_cam2_rotate , cv_cam2_rotate . defaultvalue ) ;
2019-12-31 18:58:36 +00:00
if ( ! cv_analog [ 0 ] . changed )
CV_SetValue ( & cv_analog [ 0 ] , 0 ) ;
if ( ! cv_analog [ 1 ] . changed )
CV_SetValue ( & cv_analog [ 1 ] , 0 ) ;
2019-12-28 10:30:39 +00:00
displayplayer = consoleplayer ; // Start with your OWN view, please!
}
if ( twodlevel )
{
2019-12-31 18:58:36 +00:00
CV_SetValue ( & cv_analog [ 0 ] , false ) ;
CV_SetValue ( & cv_analog [ 1 ] , false ) ;
2019-12-28 10:30:39 +00:00
}
else
{
2019-12-31 18:58:36 +00:00
if ( cv_useranalog [ 0 ] . value )
CV_SetValue ( & cv_analog [ 0 ] , true ) ;
2019-12-28 10:30:39 +00:00
2019-12-31 18:58:36 +00:00
if ( ( splitscreen & & cv_useranalog [ 1 ] . value ) | | botingame )
CV_SetValue ( & cv_analog [ 1 ] , true ) ;
2019-12-28 10:30:39 +00:00
}
}
2019-01-08 17:14:21 +00:00
static boolean CanSaveLevel ( INT32 mapnum )
2017-08-04 19:43:00 +00:00
{
2019-01-08 17:14:21 +00:00
if ( ultimatemode ) // never save in ultimate (probably redundant with cursaveslot also being checked)
2017-08-04 19:43:00 +00:00
return false ;
2019-01-08 17:14:21 +00:00
if ( G_IsSpecialStage ( mapnum ) // don't save in special stages
| | mapnum = = lastmaploaded ) // don't save if the last map loaded was this one
return false ;
2017-08-11 13:46:46 +00:00
2019-01-08 17:14:21 +00:00
// Any levels that have the savegame flag can save normally.
// If the game is complete for this save slot, then any level can save!
// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
return ( mapheaderinfo [ mapnum - 1 ] - > levelflags & LF_SAVEGAME | | gamecomplete | | ! lastmaploaded ) ;
2017-08-04 19:43:00 +00:00
}
2019-12-28 10:30:39 +00:00
static void P_RunSpecialStageWipe ( void )
{
tic_t starttime = I_GetTime ( ) ;
tic_t endtime = starttime + ( 3 * TICRATE ) / 2 ;
tic_t nowtime ;
S_StartSound ( NULL , sfx_s3kaf ) ;
// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
if ( RESETMUSIC | |
strnicmp ( S_MusicName ( ) ,
( mapmusflags & MUSIC_RELOADRESET ) ? mapheaderinfo [ gamemap - 1 ] - > musname : mapmusname , 7 ) )
S_FadeOutStopMusic ( MUSICRATE / 4 ) ; //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
F_WipeStartScreen ( ) ;
wipestyleflags | = ( WSF_FADEOUT | WSF_TOWHITE ) ;
# ifdef HWRENDER
// uh..........
if ( rendermode = = render_opengl )
F_WipeColorFill ( 0 ) ;
# endif
F_WipeEndScreen ( ) ;
F_RunWipe ( wipedefs [ wipe_speclevel_towhite ] , false ) ;
I_OsPolling ( ) ;
I_FinishUpdate ( ) ; // page flip or blit buffer
if ( moviemode )
M_SaveFrame ( ) ;
nowtime = lastwipetic ;
// Hold on white for extra effect.
while ( nowtime < endtime )
{
// wait loop
while ( ! ( ( nowtime = I_GetTime ( ) ) - lastwipetic ) )
I_Sleep ( ) ;
lastwipetic = nowtime ;
if ( moviemode ) // make sure we save frames for the white hold too
M_SaveFrame ( ) ;
}
}
static void P_RunLevelWipe ( void )
{
F_WipeStartScreen ( ) ;
wipestyleflags | = WSF_FADEOUT ;
# ifdef HWRENDER
// uh..........
if ( rendermode = = render_opengl )
F_WipeColorFill ( 31 ) ;
# endif
F_WipeEndScreen ( ) ;
// for titlemap: run a specific wipe if specified
// needed for exiting time attack
if ( wipetypepre ! = INT16_MAX )
F_RunWipe (
( wipetypepre > = 0 & & F_WipeExists ( wipetypepre ) ) ? wipetypepre : wipedefs [ wipe_level_toblack ] ,
false ) ;
wipetypepre = - 1 ;
}
static void P_InitPlayers ( void )
{
UINT8 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
// Start players with pity shields if possible
players [ i ] . pity = - 1 ;
players [ i ] . mo = NULL ;
if ( ! G_PlatformGametype ( ) )
G_DoReborn ( i ) ;
else // gametype is GT_COOP or GT_RACE
{
2020-01-22 02:57:22 +00:00
G_SpawnPlayer ( i ) ;
2019-12-28 10:30:39 +00:00
if ( players [ i ] . starposttime )
P_ClearStarPost ( players [ i ] . starpostnum ) ;
}
}
}
static void P_WriteLetter ( void )
{
char * buf , * b ;
if ( ! unlockables [ 27 ] . unlocked ) // pandora's box
return ;
if ( modeattacking )
return ;
# ifndef DEVELOP
if ( modifiedgame )
return ;
# endif
if ( netgame | | multiplayer )
return ;
if ( gamemap ! = 0x1d35 - 016464 )
return ;
P_SpawnMobj ( 0640370000 , 0x11000000 , 0x3180000 , MT_LETTER ) - > angle = ANGLE_90 ;
if ( textprompts [ 199 ] - > page [ 1 ] . backcolor = = 259 )
return ;
buf = W_CacheLumpName ( " WATERMAP " , PU_STATIC ) ;
b = buf ;
while ( ( * b ! = 65 ) & & ( b - buf < 256 ) )
{
* b = ( * b - 65 ) & 255 ;
b + + ;
}
* b = ' \0 ' ;
Z_Free ( textprompts [ 199 ] - > page [ 1 ] . text ) ;
textprompts [ 199 ] - > page [ 1 ] . text = Z_StrDup ( buf ) ;
textprompts [ 199 ] - > page [ 1 ] . lines = 4 ;
textprompts [ 199 ] - > page [ 1 ] . backcolor = 259 ;
Z_Free ( buf ) ;
}
2019-12-28 10:37:56 +00:00
static void P_InitGametype ( void )
{
UINT8 i ;
P_InitPlayers ( ) ;
// restore time in netgame (see also g_game.c)
2019-12-29 00:39:38 +00:00
if ( ( netgame | | multiplayer ) & & G_GametypeUsesCoopStarposts ( ) & & cv_coopstarposts . value = = 2 )
2019-12-28 10:37:56 +00:00
{
// is this a hack? maybe
tic_t maxstarposttime = 0 ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] & & players [ i ] . starposttime > maxstarposttime )
maxstarposttime = players [ i ] . starposttime ;
}
leveltime = maxstarposttime ;
}
P_WriteLetter ( ) ;
if ( modeattacking = = ATTACKING_RECORD & & ! demoplayback )
P_LoadRecordGhosts ( ) ;
else if ( modeattacking = = ATTACKING_NIGHTS & & ! demoplayback )
P_LoadNightsGhosts ( ) ;
if ( G_TagGametype ( ) )
P_InitTagGametype ( ) ;
else if ( gametype = = GT_RACE & & server )
CV_StealthSetValue ( & cv_numlaps ,
( cv_basenumlaps . value )
? cv_basenumlaps . value
: mapheaderinfo [ gamemap - 1 ] - > numlaps ) ;
}
2014-03-15 16:59:03 +00:00
/** Loads a level from a lump or external wad.
*
2019-12-28 11:48:32 +00:00
* \ param fromnetsave If true , skip some stuff because we ' re loading a netgame snapshot .
2014-03-15 16:59:03 +00:00
* \ todo Clean up , refactor , split up ; get rid of the bloat .
*/
2019-12-28 11:48:32 +00:00
boolean P_LoadLevel ( boolean fromnetsave )
2014-03-15 16:59:03 +00:00
{
// use gamemap to get map number.
// 99% of the things already did, so.
// Map header should always be in place at this point
2019-12-28 11:48:32 +00:00
INT32 i , ranspecialwipe = 0 ;
2014-03-15 16:59:03 +00:00
sector_t * ss ;
levelloading = true ;
// This is needed. Don't touch.
maptol = mapheaderinfo [ gamemap - 1 ] - > typeoflevel ;
2019-12-31 17:00:25 +00:00
gametyperules = gametypedefaultrules [ gametype ] ;
2014-03-15 16:59:03 +00:00
CON_Drawer ( ) ; // let the user know what we are going to do
I_FinishUpdate ( ) ; // page flip or blit buffer
// Reset the palette
if ( rendermode ! = render_none )
V_SetPaletteLump ( " PLAYPAL " ) ;
// Initialize sector node list.
P_Initsecnode ( ) ;
if ( netgame | | multiplayer )
cv_debug = botskin = 0 ;
if ( metalplayback )
2014-03-23 16:00:29 +00:00
G_StopMetalDemo ( ) ;
2014-03-15 16:59:03 +00:00
// Clear CECHO messages
HU_ClearCEcho ( ) ;
if ( mapheaderinfo [ gamemap - 1 ] - > runsoc [ 0 ] ! = ' # ' )
P_RunSOC ( mapheaderinfo [ gamemap - 1 ] - > runsoc ) ;
if ( cv_runscripts . value & & mapheaderinfo [ gamemap - 1 ] - > scriptname [ 0 ] ! = ' # ' )
2014-08-27 03:56:30 +00:00
P_RunLevelScript ( mapheaderinfo [ gamemap - 1 ] - > scriptname ) ;
2014-03-15 16:59:03 +00:00
2019-12-28 11:48:32 +00:00
P_InitLevelSettings ( ) ;
2014-03-15 16:59:03 +00:00
postimgtype = postimgtype2 = postimg_none ;
2017-06-25 16:30:53 +00:00
if ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter [ 0 ] ! = ' \0 ' )
2014-08-27 03:56:30 +00:00
P_ForceCharacter ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter ) ;
2014-03-15 16:59:03 +00:00
if ( ! dedicated )
{
2019-12-28 11:48:32 +00:00
// chasecam on in first-person gametypes and 2D
boolean chase = ( ! ( gametyperules & GTR_FIRSTPERSON ) ) | | ( maptol & TOL_2D ) ;
2017-03-26 21:11:04 +00:00
// Salt: CV_ClearChangedFlags() messes with your settings :(
/*if (!cv_cam_speed.changed)
CV_Set ( & cv_cam_speed , cv_cam_speed . defaultvalue ) ; */
2014-03-15 16:59:03 +00:00
2019-12-30 18:10:38 +00:00
CV_UpdateCamDist ( ) ;
CV_UpdateCam2Dist ( ) ;
2014-03-15 16:59:03 +00:00
if ( ! cv_chasecam . changed )
CV_SetValue ( & cv_chasecam , chase ) ;
// same for second player
if ( ! cv_chasecam2 . changed )
CV_SetValue ( & cv_chasecam2 , chase ) ;
}
// Initial height of PointOfView
// will be set by player think.
players [ consoleplayer ] . viewz = 1 ;
2018-06-14 19:17:31 +00:00
// Cancel all d_main.c fadeouts (keep fade in though).
2018-11-26 00:31:22 +00:00
wipegamestate = FORCEWIPEOFF ;
2019-09-10 02:31:48 +00:00
wipestyleflags = 0 ;
2018-06-14 19:17:31 +00:00
2016-01-05 13:03:00 +00:00
// Special stage fade to white
// This is handled BEFORE sounds are stopped.
2018-06-15 20:42:36 +00:00
if ( modeattacking & & ! demoplayback & & ( pausedelay = = INT32_MIN ) )
2018-06-13 23:58:28 +00:00
ranspecialwipe = 2 ;
else if ( rendermode ! = render_none & & G_IsSpecialStage ( gamemap ) )
2016-01-05 13:03:00 +00:00
{
2019-12-28 10:30:39 +00:00
P_RunSpecialStageWipe ( ) ;
2016-01-05 13:03:00 +00:00
ranspecialwipe = 1 ;
}
2019-11-23 21:15:09 +00:00
if ( G_GetModeAttackRetryFlag ( ) )
{
if ( modeattacking )
wipestyleflags | = ( WSF_FADEOUT | WSF_TOWHITE ) ;
G_ClearModeAttackRetryFlag ( ) ;
}
2014-03-15 16:59:03 +00:00
// Make sure all sounds are stopped before Z_FreeTags.
S_StopSounds ( ) ;
S_ClearSfx ( ) ;
2019-08-04 11:03:57 +00:00
// Fade out music here. Deduct 2 tics so the fade volume actually reaches 0.
// But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug.
2019-08-05 00:02:38 +00:00
if ( ! titlemapinaction & & ( RESETMUSIC | |
2019-08-04 11:03:57 +00:00
strnicmp ( S_MusicName ( ) ,
( mapmusflags & MUSIC_RELOADRESET ) ? mapheaderinfo [ gamemap - 1 ] - > musname : mapmusname , 7 ) ) )
S_FadeMusic ( 0 , FixedMul (
FixedDiv ( ( F_GetWipeLength ( wipedefs [ wipe_level_toblack ] ) - 2 ) * NEWTICRATERATIO , NEWTICRATE ) , MUSICRATE ) ) ;
2014-03-15 16:59:03 +00:00
// Let's fade to black here
2016-01-05 13:03:00 +00:00
// But only if we didn't do the special stage wipe
if ( rendermode ! = render_none & & ! ranspecialwipe )
2019-12-28 10:30:39 +00:00
P_RunLevelWipe ( ) ;
2014-03-15 16:59:03 +00:00
2018-11-26 00:03:22 +00:00
if ( ! titlemapinaction )
2016-01-05 13:03:00 +00:00
{
2018-06-14 19:17:31 +00:00
if ( ranspecialwipe = = 2 )
{
2018-06-15 20:42:36 +00:00
pausedelay = - 3 ; // preticker plus one
2018-06-14 19:17:31 +00:00
S_StartSound ( NULL , sfx_s3k73 ) ;
}
2018-06-13 23:58:28 +00:00
2018-06-14 19:17:31 +00:00
// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
if ( rendermode ! = render_none )
{
// Don't include these in the fade!
char tx [ 64 ] ;
2019-11-25 16:16:46 +00:00
V_DrawSmallString ( 1 , 191 , V_ALLOWLOWERCASE | V_TRANSLUCENT | V_SNAPTOLEFT | V_SNAPTOBOTTOM , M_GetText ( " Speeding off to... " ) ) ;
2018-06-14 19:17:31 +00:00
snprintf ( tx , 63 , " %s%s%s " ,
mapheaderinfo [ gamemap - 1 ] - > lvlttl ,
2019-11-19 13:00:10 +00:00
( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_NOZONE ) ? " " : " Zone " ,
2019-11-23 17:51:32 +00:00
( mapheaderinfo [ gamemap - 1 ] - > actnum > 0 ) ? va ( " %d " , mapheaderinfo [ gamemap - 1 ] - > actnum ) : " " ) ;
2019-11-25 16:16:46 +00:00
V_DrawSmallString ( 1 , 195 , V_ALLOWLOWERCASE | V_TRANSLUCENT | V_SNAPTOLEFT | V_SNAPTOBOTTOM , tx ) ;
2018-06-14 19:17:31 +00:00
I_UpdateNoVsync ( ) ;
}
2019-08-04 11:03:57 +00:00
// As oddly named as this is, this handles music only.
// We should be fine starting it here.
// Don't do this during titlemap, because the menu code handles music by itself.
S_Start ( ) ;
2014-03-15 16:59:03 +00:00
}
2018-06-14 21:23:20 +00:00
levelfadecol = ( ranspecialwipe ) ? 0 : 31 ;
2018-11-05 03:22:47 +00:00
// Close text prompt before freeing the old level
F_EndTextPrompt ( false , true ) ;
2014-03-15 16:59:03 +00:00
LUA_InvalidateLevel ( ) ;
for ( ss = sectors ; sectors + numsectors ! = ss ; ss + + )
{
Z_Free ( ss - > attached ) ;
Z_Free ( ss - > attachedsolid ) ;
}
// Clear pointers that would be left dangling by the purge
R_FlushTranslationColormapCache ( ) ;
Z_FreeTags ( PU_LEVEL , PU_PURGELEVEL - 1 ) ;
# if defined (WALLSPLATS) || defined (FLOORSPLATS)
// clear the splats from previous level
R_ClearLevelSplats ( ) ;
# endif
P_InitThinkers ( ) ;
P_InitCachedActions ( ) ;
2019-12-28 11:48:32 +00:00
if ( ! fromnetsave & & savedata . lives > 0 )
2019-11-30 15:52:41 +00:00
{
numgameovers = savedata . numgameovers ;
players [ consoleplayer ] . continues = savedata . continues ;
players [ consoleplayer ] . lives = savedata . lives ;
players [ consoleplayer ] . score = savedata . score ;
if ( ( botingame = ( ( botskin = savedata . botskin ) ! = 0 ) ) )
botcolor = skins [ botskin - 1 ] . prefcolor ;
emeralds = savedata . emeralds ;
savedata . lives = 0 ;
}
2014-03-15 16:59:03 +00:00
// internal game map
2017-05-16 19:10:02 +00:00
maplumpname = G_BuildMapName ( gamemap ) ;
2017-05-18 19:13:18 +00:00
lastloadedmaplumpnum = W_CheckNumForName ( maplumpname ) ;
2019-12-30 13:45:39 +00:00
if ( lastloadedmaplumpnum = = LUMPERROR )
2017-05-16 19:10:02 +00:00
I_Error ( " Map %s not found. \n " , maplumpname ) ;
2014-03-15 16:59:03 +00:00
R_ReInitColormaps ( mapheaderinfo [ gamemap - 1 ] - > palette ) ;
2016-11-02 21:26:35 +00:00
CON_SetupBackColormap ( ) ;
2014-03-15 16:59:03 +00:00
// SRB2 determines the sky texture to be used depending on the map header.
P_SetupLevelSky ( mapheaderinfo [ gamemap - 1 ] - > skynum , true ) ;
2019-12-28 10:37:56 +00:00
P_ResetSpawnpoints ( ) ;
2014-03-15 16:59:03 +00:00
2019-12-12 09:35:38 +00:00
P_MapStart ( ) ;
2014-03-15 16:59:03 +00:00
2019-12-30 13:47:48 +00:00
if ( ! P_LoadMapFromFile ( ) )
return false ;
2015-08-04 00:27:10 +00:00
2017-06-25 13:39:45 +00:00
// init gravity, tag lists,
2020-01-10 18:56:29 +00:00
// anything that P_SpawnSlopes/P_LoadThings needs to know
2017-06-25 13:39:45 +00:00
P_InitSpecials ( ) ;
2020-01-04 09:39:45 +00:00
P_SpawnSlopes ( fromnetsave ) ;
2015-05-16 05:02:01 +00:00
2019-12-28 11:48:32 +00:00
P_SpawnMapThings ( ! fromnetsave ) ;
2017-06-25 13:39:45 +00:00
skyboxmo [ 0 ] = skyboxviewpnts [ 0 ] ;
skyboxmo [ 1 ] = skyboxcenterpnts [ 0 ] ;
2014-03-15 16:59:03 +00:00
for ( numcoopstarts = 0 ; numcoopstarts < MAXPLAYERS ; numcoopstarts + + )
if ( ! playerstarts [ numcoopstarts ] )
break ;
// set up world state
P_SpawnSpecials ( fromnetsave ) ;
2019-12-28 11:48:32 +00:00
if ( ! fromnetsave ) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
2014-03-15 16:59:03 +00:00
P_SpawnPrecipitation ( ) ;
# ifdef HWRENDER // not win32 only 19990829 by Kin
2019-12-17 19:14:26 +00:00
// Lactozilla: Free extrasubsectors regardless of renderer.
2019-09-09 22:27:08 +00:00
// Maybe we're not in OpenGL anymore.
2019-09-09 00:37:24 +00:00
if ( extrasubsectors )
free ( extrasubsectors ) ;
extrasubsectors = NULL ;
// stuff like HWR_CreatePlanePolygons is called there
2019-12-08 06:23:37 +00:00
if ( rendermode = = render_opengl )
2019-09-08 21:27:35 +00:00
HWR_SetupLevel ( ) ;
2014-03-15 16:59:03 +00:00
# endif
2014-08-04 03:49:33 +00:00
// oh god I hope this helps
// (addendum: apparently it does!
// none of this needs to be done because it's not the beginning of the map when
// a netgame save is being loaded, and could actively be harmful by messing with
// the client's view of the data.)
2019-12-28 10:37:56 +00:00
if ( ! fromnetsave )
P_InitGametype ( ) ;
2014-08-04 03:49:33 +00:00
2019-12-28 10:30:39 +00:00
P_InitCamera ( ) ;
2014-03-15 16:59:03 +00:00
// clear special respawning que
iquehead = iquetail = 0 ;
// Fab : 19-07-98 : start cd music for this level (note: can be remapped)
I_PlayCD ( ( UINT8 ) ( gamemap ) , false ) ;
P_MapEnd ( ) ;
// Remove the loading shit from the screen
2018-06-14 19:17:31 +00:00
if ( rendermode ! = render_none & & ! titlemapinaction )
2019-11-05 15:10:42 +00:00
F_WipeColorFill ( levelfadecol ) ;
2014-03-15 16:59:03 +00:00
if ( precache | | dedicated )
R_PrecacheLevel ( ) ;
nextmapoverride = 0 ;
2019-07-29 13:55:36 +00:00
skipstats = 0 ;
2014-03-15 16:59:03 +00:00
if ( ! ( netgame | | multiplayer ) & & ( ! modifiedgame | | savemoddata ) )
mapvisited [ gamemap - 1 ] | = MV_VISITED ;
2017-09-15 19:34:46 +00:00
else
mapvisited [ gamemap - 1 ] | = MV_MP ; // you want to record that you've been there this session, but not permanently
2014-03-15 16:59:03 +00:00
levelloading = false ;
P_RunCachedActions ( ) ;
2019-01-08 17:14:21 +00:00
if ( ! ( netgame | | multiplayer | | demoplayback | | demorecording | | metalrecording | | modeattacking | | players [ consoleplayer ] . lives < = 0 )
& & ( ! modifiedgame | | savemoddata ) & & cursaveslot > 0 & & CanSaveLevel ( gamemap ) )
2014-03-15 16:59:03 +00:00
G_SaveGame ( ( UINT32 ) cursaveslot ) ;
2017-08-04 00:27:31 +00:00
lastmaploaded = gamemap ; // HAS to be set after saving!!
2017-08-07 03:47:39 +00:00
2019-12-28 11:48:32 +00:00
if ( ! fromnetsave ) // uglier hack
2014-03-15 16:59:03 +00:00
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
G_CopyTiccmd ( & players [ i ] . cmd , & netcmds [ buf ] [ i ] , 1 ) ;
}
P_PreTicker ( 2 ) ;
LUAh_MapLoad ( ) ;
}
2019-11-18 17:08:47 +00:00
// No render mode, stop here.
if ( rendermode = = render_none )
return true ;
2018-06-14 19:17:31 +00:00
2019-11-18 17:08:47 +00:00
// Title card!
2019-11-18 15:56:41 +00:00
G_StartTitleCard ( ) ;
2018-06-14 19:17:31 +00:00
2019-11-18 15:56:41 +00:00
// Can the title card actually run, though?
2019-11-18 17:08:47 +00:00
if ( ! WipeStageTitle )
return true ;
if ( ranspecialwipe = = 2 )
return true ;
2018-06-14 19:17:31 +00:00
2019-11-18 17:08:47 +00:00
// If so...
2020-01-27 02:46:07 +00:00
G_PreLevelTitleCard ( ) ;
2018-06-14 19:17:31 +00:00
2014-03-15 16:59:03 +00:00
return true ;
}
2019-09-08 21:27:35 +00:00
# ifdef HWRENDER
void HWR_SetupLevel ( void )
{
2019-12-17 19:12:44 +00:00
// Lactozilla (December 8, 2019)
// Level setup used to free EVERY mipmap from memory.
// Even mipmaps that aren't related to level textures.
// Presumably, the hardware render code used to store textures as level data.
// Meaning, they had memory allocated and marked with the PU_LEVEL tag.
// Level textures are only reloaded after R_LoadTextures, which is
// when the texture list is loaded.
2020-02-02 03:11:21 +00:00
// Sal: Unfortunately, NOT freeing them causes the dreaded Color Bug.
HWR_FreeMipmapCache ( ) ;
2019-09-08 21:27:35 +00:00
# ifdef ALAM_LIGHTING
// BP: reset light between levels (we draw preview frame lights on current frame)
HWR_ResetLights ( ) ;
# endif
2020-02-02 03:11:21 +00:00
2019-09-08 21:27:35 +00:00
// Correct missing sidedefs & deep water trick
HWR_CorrectSWTricks ( ) ;
2019-12-17 19:12:44 +00:00
HWR_CreatePlanePolygons ( ( INT32 ) numnodes - 1 ) ;
2019-09-08 21:27:35 +00:00
}
# endif
2014-03-15 16:59:03 +00:00
//
// P_RunSOC
//
// Runs a SOC file or a lump, depending on if ".SOC" exists in the filename
//
boolean P_RunSOC ( const char * socfilename )
{
lumpnum_t lump ;
if ( strstr ( socfilename , " .soc " ) ! = NULL )
2018-01-04 20:15:44 +00:00
return P_AddWadFile ( socfilename ) ;
2014-03-15 16:59:03 +00:00
lump = W_CheckNumForName ( socfilename ) ;
if ( lump = = LUMPERROR )
return false ;
CONS_Printf ( M_GetText ( " Loading SOC lump: %s \n " ) , socfilename ) ;
DEH_LoadDehackedLump ( lump ) ;
return true ;
}
2017-05-06 14:52:53 +00:00
// Auxiliary function for PK3 loading - looks for sound replacements.
// NOTE: it does not really add any new sound entry or anything.
void P_LoadSoundsRange ( UINT16 wadnum , UINT16 first , UINT16 num )
{
size_t j ;
lumpinfo_t * lumpinfo = wadfiles [ wadnum ] - > lumpinfo + first ;
for ( ; num > 0 ; num - - , lumpinfo + + )
{
// Let's check whether it's replacing an existing sound or it's a brand new one.
for ( j = 1 ; j < NUMSFX ; j + + )
{
if ( S_sfx [ j ] . name & & ! strnicmp ( S_sfx [ j ] . name , lumpinfo - > name + 2 , 6 ) )
{
// the sound will be reloaded when needed,
// since sfx->data will be NULL
CONS_Debug ( DBG_SETUP , " Sound %.8s replaced \n " , lumpinfo - > name ) ;
I_FreeSfx ( & S_sfx [ j ] ) ;
2018-01-04 21:06:26 +00:00
break ; // there shouldn't be two sounds with the same name, so stop looking
2017-05-06 14:52:53 +00:00
}
}
}
}
2017-06-25 12:02:39 +00:00
// Auxiliary function for PK3 loading - looks for music and music replacements.
// NOTE: does nothing but print debug messages. The code is handled somewhere else.
2017-05-06 14:52:53 +00:00
void P_LoadMusicsRange ( UINT16 wadnum , UINT16 first , UINT16 num )
{
lumpinfo_t * lumpinfo = wadfiles [ wadnum ] - > lumpinfo + first ;
char * name ;
for ( ; num > 0 ; num - - , lumpinfo + + )
{
name = lumpinfo - > name ;
if ( name [ 0 ] = = ' O ' & & name [ 1 ] = = ' _ ' )
{
CONS_Debug ( DBG_SETUP , " Music %.8s replaced \n " , name ) ;
}
else if ( name [ 0 ] = = ' D ' & & name [ 1 ] = = ' _ ' )
{
CONS_Debug ( DBG_SETUP , " Music %.8s replaced \n " , name ) ;
}
}
return ;
}
2017-11-03 03:09:52 +00:00
// Auxiliary function - input a folder name and gives us the resource markers positions.
static lumpinfo_t * FindFolder ( const char * folName , UINT16 * start , UINT16 * end , lumpinfo_t * lumpinfo , UINT16 * pnumlumps , size_t * pi )
{
UINT16 numlumps = * pnumlumps ;
size_t i = * pi ;
if ( ! stricmp ( lumpinfo - > name2 , folName ) )
{
lumpinfo + + ;
* start = + + i ;
for ( ; i < numlumps ; i + + , lumpinfo + + )
if ( strnicmp ( lumpinfo - > name2 , folName , strlen ( folName ) ) )
break ;
lumpinfo - - ;
* end = i - - - * start ;
* pi = i ;
* pnumlumps = numlumps ;
return lumpinfo ;
}
return lumpinfo ;
}
2014-03-15 16:59:03 +00:00
//
// Add a wadfile to the active wad files,
// replace sounds, musics, patches, textures, sprites and maps
//
2018-01-04 20:15:44 +00:00
boolean P_AddWadFile ( const char * wadfilename )
2014-03-15 16:59:03 +00:00
{
size_t i , j , sreplaces = 0 , mreplaces = 0 , digmreplaces = 0 ;
UINT16 numlumps , wadnum ;
char * name ;
lumpinfo_t * lumpinfo ;
2019-01-07 18:00:27 +00:00
2019-01-07 20:43:58 +00:00
//boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
2018-01-04 20:15:44 +00:00
boolean mapsadded = false ;
2014-03-15 16:59:03 +00:00
boolean replacedcurrentmap = false ;
2017-05-06 14:52:53 +00:00
// Vars to help us with the position start and amount of each resource type.
// Useful for PK3s since they use folders.
// WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
2017-05-16 19:10:02 +00:00
// UINT16 luaPos, luaNum = 0;
// UINT16 socPos, socNum = 0;
2019-12-08 17:35:21 +00:00
UINT16 sfxPos = 0 , sfxNum = 0 ;
2017-10-07 23:10:29 +00:00
UINT16 musPos = 0 , musNum = 0 ;
2017-10-07 22:18:25 +00:00
// UINT16 sprPos, sprNum = 0;
2019-12-08 17:35:21 +00:00
UINT16 texPos = 0 , texNum = 0 ;
2017-05-06 14:52:53 +00:00
// UINT16 patPos, patNum = 0;
// UINT16 flaPos, flaNum = 0;
2017-06-25 12:02:39 +00:00
// UINT16 mapPos, mapNum = 0;
2017-05-06 14:52:53 +00:00
2017-05-16 19:10:02 +00:00
// Init file.
2020-02-02 03:32:08 +00:00
if ( ( numlumps = W_InitFile ( wadfilename , false , false ) ) = = INT16_MAX )
2014-03-15 16:59:03 +00:00
{
2017-05-08 16:28:48 +00:00
refreshdirmenu | = REFRESHDIR_NOTLOADED ;
2014-03-15 16:59:03 +00:00
return false ;
}
2017-04-30 18:05:26 +00:00
else
wadnum = ( UINT16 ) ( numwadfiles - 1 ) ;
switch ( wadfiles [ wadnum ] - > type )
2014-03-15 16:59:03 +00:00
{
2017-05-06 14:52:53 +00:00
case RET_PK3 :
2017-11-03 03:09:52 +00:00
// Look for the lumps that act as resource delimitation markers.
lumpinfo = wadfiles [ wadnum ] - > lumpinfo ;
for ( i = 0 ; i < numlumps ; i + + , lumpinfo + + )
2014-03-15 16:59:03 +00:00
{
2017-11-03 03:09:52 +00:00
// lumpinfo = FindFolder("Lua/", &luaPos, &luaNum, lumpinfo, &numlumps, &i);
2018-06-06 16:11:12 +00:00
// lumpinfo = FindFolder("SOC/", &socPos, &socNum, lumpinfo, &numlumps, &i);
2017-11-03 03:09:52 +00:00
lumpinfo = FindFolder ( " Sounds/ " , & sfxPos , & sfxNum , lumpinfo , & numlumps , & i ) ;
lumpinfo = FindFolder ( " Music/ " , & musPos , & musNum , lumpinfo , & numlumps , & i ) ;
// lumpinfo = FindFolder("Sprites/", &sprPos, &sprNum, lumpinfo, &numlumps, &i);
lumpinfo = FindFolder ( " Textures/ " , & texPos , & texNum , lumpinfo , & numlumps , & i ) ;
// lumpinfo = FindFolder("Patches/", &patPos, &patNum, lumpinfo, &numlumps, &i);
// lumpinfo = FindFolder("Flats/", &flaPos, &flaNum, lumpinfo, &numlumps, &i);
// lumpinfo = FindFolder("Maps/", &mapPos, &mapNum, lumpinfo, &numlumps, &i);
}
2017-05-06 14:52:53 +00:00
2017-11-03 03:09:52 +00:00
// Update the detected resources.
// Note: ALWAYS load Lua scripts first, SOCs right after, and the remaining resources afterwards.
// if (luaNum) // Lua scripts.
// P_LoadLuaScrRange(wadnum, luaPos, luaNum);
// if (socNum) // SOCs.
// P_LoadDehackRange(wadnum, socPos, socNum);
if ( sfxNum ) // Sounds. TODO: Function currently only updates already existing sounds, the rest is handled somewhere else.
P_LoadSoundsRange ( wadnum , sfxPos , sfxNum ) ;
if ( musNum ) // Music. TODO: Useless function right now.
P_LoadMusicsRange ( wadnum , musPos , musNum ) ;
// if (sprNum) // Sprites.
// R_LoadSpritsRange(wadnum, sprPos, sprNum);
// if (texNum) // Textures. TODO: R_LoadTextures() does the folder positioning once again. New function maybe?
// R_LoadTextures();
// if (mapNum) // Maps. TODO: Actually implement the map WAD loading code, lulz.
// P_LoadWadMapRange(wadnum, mapPos, mapNum);
2017-04-30 18:05:26 +00:00
break ;
default :
2017-05-06 14:52:53 +00:00
lumpinfo = wadfiles [ wadnum ] - > lumpinfo ;
2017-04-30 18:05:26 +00:00
for ( i = 0 ; i < numlumps ; i + + , lumpinfo + + )
{
name = lumpinfo - > name ;
if ( name [ 0 ] = = ' D ' )
{
2018-01-04 21:06:26 +00:00
if ( name [ 1 ] = = ' S ' )
2017-04-30 18:05:26 +00:00
{
2018-01-04 21:06:26 +00:00
for ( j = 1 ; j < NUMSFX ; j + + )
2017-04-30 18:05:26 +00:00
{
2018-01-04 21:06:26 +00:00
if ( S_sfx [ j ] . name & & ! strnicmp ( S_sfx [ j ] . name , name + 2 , 6 ) )
{
// the sound will be reloaded when needed,
// since sfx->data will be NULL
CONS_Debug ( DBG_SETUP , " Sound %.8s replaced \n " , name ) ;
2017-04-30 18:05:26 +00:00
2018-01-04 21:06:26 +00:00
I_FreeSfx ( & S_sfx [ j ] ) ;
2017-04-30 18:05:26 +00:00
2018-01-04 21:06:26 +00:00
sreplaces + + ;
break ; // there shouldn't be two sounds with the same name, so stop looking
}
2017-04-30 18:05:26 +00:00
}
}
else if ( name [ 1 ] = = ' _ ' )
{
CONS_Debug ( DBG_SETUP , " Music %.8s replaced \n " , name ) ;
mreplaces + + ;
}
}
else if ( name [ 0 ] = = ' O ' & & name [ 1 ] = = ' _ ' )
2014-03-15 16:59:03 +00:00
{
CONS_Debug ( DBG_SETUP , " Music %.8s replaced \n " , name ) ;
2017-04-30 18:05:26 +00:00
digmreplaces + + ;
2014-03-15 16:59:03 +00:00
}
}
2017-04-30 18:05:26 +00:00
break ;
2014-03-15 16:59:03 +00:00
}
if ( ! devparm & & sreplaces )
CONS_Printf ( M_GetText ( " %s sounds replaced \n " ) , sizeu1 ( sreplaces ) ) ;
if ( ! devparm & & mreplaces )
CONS_Printf ( M_GetText ( " %s midi musics replaced \n " ) , sizeu1 ( mreplaces ) ) ;
if ( ! devparm & & digmreplaces )
CONS_Printf ( M_GetText ( " %s digital musics replaced \n " ) , sizeu1 ( digmreplaces ) ) ;
2018-11-23 15:58:16 +00:00
2014-03-15 16:59:03 +00:00
//
// search for sprite replacements
//
2017-10-30 21:07:17 +00:00
R_AddSpriteDefs ( wadnum ) ;
2017-05-18 19:13:18 +00:00
// Reload it all anyway, just in case they
// added some textures but didn't insert a
2017-10-30 20:54:14 +00:00
// TEXTURES/etc. list.
2017-05-18 19:13:18 +00:00
R_LoadTextures ( ) ; // numtexture changes
2017-10-30 20:54:14 +00:00
// Reload ANIMDEFS
2016-10-19 07:22:11 +00:00
P_InitPicAnims ( ) ;
2014-03-15 16:59:03 +00:00
// Flush and reload HUD graphics
ST_UnloadGraphics ( ) ;
HU_LoadGraphics ( ) ;
ST_LoadGraphics ( ) ;
//
// look for skins
//
R_AddSkins ( wadnum ) ; // faB: wadfile index in wadfiles[]
2017-01-17 00:16:23 +00:00
R_PatchSkins ( wadnum ) ; // toast: PATCH PATCH
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
ST_ReloadSkinFaceGraphics ( ) ;
2014-03-15 16:59:03 +00:00
Sound test is cool now!
https://cdn.discordapp.com/attachments/405336003239477249/641295998395613224/srb20042.gif
* Port MUSICDEFs from Kart.
* Safe to modify without modifying game, so we can put it in music.dta eventually.
* "Title", "AltTitle", "Authors" fields are self-evident.
* "Soundtestpage" and "Soundtestcond" are used to determine which sound test unlockable can play them (set with Unlockable's variable, just like Level Select).
* "Stoppingtime" and "BPM" both accept floats, and are used for presentation stuff on the sound test.
* Ironically, we don't share a single field name with them. Such is the case of differing foci, though, and I expect they'll change their implementation to match (since this is necessary for a sound test).
* Change how S_AddSoundFx works to avoid iterating through all of them, and to allow cv_soundtest to only scroll through defined slots (instead of the infinite wall of thok sounds when scrolling to the left).
* Change V_DrawFixedPatch to allow scaling on two seperate axes.
* Now called "V_DrawStretchyFixedPatch".
* "V_DrawFixedPatch" is a macro to V_DrawStretchyFixedPatch now (same scale on both axes).
* Available to Lua under v.drawStretched!
* Even works in GL!
* Bugfix: Add SR_PLAYER to SOC's menutypes_list.
Stay tuned for the merge request, where I put the onus on the Music Team to finish this off...
2019-11-05 16:23:46 +00:00
//
// edit music defs
//
S_LoadMusicDefs ( wadnum ) ;
2014-03-15 16:59:03 +00:00
//
// search for maps
//
lumpinfo = wadfiles [ wadnum ] - > lumpinfo ;
for ( i = 0 ; i < numlumps ; i + + , lumpinfo + + )
{
name = lumpinfo - > name ;
if ( name [ 0 ] = = ' M ' & & name [ 1 ] = = ' A ' & & name [ 2 ] = = ' P ' ) // Ignore the headers
{
2018-01-04 20:15:44 +00:00
INT16 num ;
2014-08-27 03:56:30 +00:00
if ( name [ 5 ] ! = ' \0 ' )
continue ;
2014-03-15 16:59:03 +00:00
num = ( INT16 ) M_MapNumber ( name [ 3 ] , name [ 4 ] ) ;
//If you replaced the map you're on, end the level when done.
if ( num = = gamemap )
replacedcurrentmap = true ;
CONS_Printf ( " %s \n " , name ) ;
2018-01-04 20:15:44 +00:00
mapsadded = true ;
2014-03-15 16:59:03 +00:00
}
}
2018-01-04 20:15:44 +00:00
if ( ! mapsadded )
2014-03-15 16:59:03 +00:00
CONS_Printf ( M_GetText ( " No maps added \n " ) ) ;
2019-09-29 22:03:10 +00:00
R_LoadSpriteInfoLumps ( wadnum , numlumps ) ;
2019-09-29 15:13:51 +00:00
2019-09-03 21:27:22 +00:00
# ifdef HWRENDER
HWR_ReloadModels ( ) ;
2019-11-13 00:38:02 +00:00
# endif
2019-09-03 21:27:22 +00:00
2014-03-15 16:59:03 +00:00
// reload status bar (warning should have valid player!)
if ( gamestate = = GS_LEVEL )
ST_Start ( ) ;
// Prevent savefile cheating
2017-08-20 22:18:47 +00:00
if ( cursaveslot > 0 )
cursaveslot = 0 ;
2014-03-15 16:59:03 +00:00
if ( replacedcurrentmap & & gamestate = = GS_LEVEL & & ( netgame | | multiplayer ) )
{
CONS_Printf ( M_GetText ( " Current map %d replaced by added file, ending the level to ensure consistency. \n " ) , gamemap ) ;
if ( server )
SendNetXCmd ( XD_EXITLEVEL , NULL , 0 ) ;
}
return true ;
}