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.
2018-11-25 12:35:38 +00:00
// Copyright (C) 1999-2018 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"
# include "r_things.h"
# 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"
2018-08-28 20:08:47 +00:00
# include "filesrch.h" // refreshdirmenu
2014-03-15 16:59:03 +00:00
// wipes
# include "f_finale.h"
# include "md5.h" // map MD5
// for LUAh_MapLoad
# include "lua_script.h"
# include "lua_hook.h"
2019-02-07 03:03:05 +00:00
# if !defined (UNDER_CE)
# include <time.h>
# endif
2014-03-15 16:59:03 +00:00
# if defined (_WIN32) || defined (_WIN32_WCE)
# include <malloc.h>
# include <math.h>
# endif
# ifdef HWRENDER
# include "hardware/hw_main.h"
# include "hardware/hw_light.h"
# endif
2016-07-06 04:09:17 +00:00
# ifdef ESLOPE
# include "p_slopes.h"
# endif
2018-01-02 01:17:34 +00:00
// SRB2Kart
# include "k_kart.h"
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.
//
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 ;
INT32 numstarposts ;
boolean levelloading ;
2018-09-22 11:43:54 +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-12-11 01:57:46 +00:00
# define NUMLAPS_DEFAULT 3
2014-03-15 16:59:03 +00:00
/** 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 ) ;
DEH_WriteUndoline ( " LEVELNAME " , mapheaderinfo [ num ] - > lvlttl , UNDO_NONE ) ;
mapheaderinfo [ num ] - > lvlttl [ 0 ] = ' \0 ' ;
DEH_WriteUndoline ( " SUBTITLE " , mapheaderinfo [ num ] - > subttl , UNDO_NONE ) ;
mapheaderinfo [ num ] - > subttl [ 0 ] = ' \0 ' ;
2017-09-01 17:00:53 +00:00
DEH_WriteUndoline ( " ZONETITLE " , mapheaderinfo [ num ] - > zonttl , UNDO_NONE ) ; // SRB2kart
mapheaderinfo [ num ] - > zonttl [ 0 ] = ' \0 ' ;
2018-06-05 05:34:05 +00:00
DEH_WriteUndoline ( " ACT " , mapheaderinfo [ num ] - > actnum , UNDO_NONE ) ; // SRB2kart
mapheaderinfo [ num ] - > actnum [ 0 ] = ' \0 ' ;
2014-03-15 16:59:03 +00:00
DEH_WriteUndoline ( " TYPEOFLEVEL " , va ( " %d " , mapheaderinfo [ num ] - > typeoflevel ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > typeoflevel = 0 ;
DEH_WriteUndoline ( " NEXTLEVEL " , va ( " %d " , mapheaderinfo [ num ] - > nextlevel ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > nextlevel = ( INT16 ) ( i + 1 ) ;
2016-07-06 04:09:17 +00:00
DEH_WriteUndoline ( " MUSIC " , mapheaderinfo [ num ] - > musname , UNDO_NONE ) ;
snprintf ( mapheaderinfo [ num ] - > musname , 7 , " %sM " , G_BuildMapName ( i ) ) ;
mapheaderinfo [ num ] - > musname [ 6 ] = 0 ;
DEH_WriteUndoline ( " MUSICTRACK " , va ( " %d " , mapheaderinfo [ num ] - > mustrack ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > mustrack = 0 ;
2014-03-15 16:59:03 +00:00
DEH_WriteUndoline ( " FORCECHARACTER " , va ( " %d " , mapheaderinfo [ num ] - > forcecharacter ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > forcecharacter [ 0 ] = ' \0 ' ;
DEH_WriteUndoline ( " WEATHER " , va ( " %d " , mapheaderinfo [ num ] - > weather ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > weather = 0 ;
DEH_WriteUndoline ( " SKYNUM " , va ( " %d " , mapheaderinfo [ num ] - > skynum ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > skynum = 1 ;
DEH_WriteUndoline ( " SKYBOXSCALEX " , va ( " %d " , mapheaderinfo [ num ] - > skybox_scalex ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > skybox_scalex = 16 ;
DEH_WriteUndoline ( " SKYBOXSCALEY " , va ( " %d " , mapheaderinfo [ num ] - > skybox_scaley ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > skybox_scaley = 16 ;
DEH_WriteUndoline ( " SKYBOXSCALEZ " , va ( " %d " , mapheaderinfo [ num ] - > skybox_scalez ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > skybox_scalez = 16 ;
DEH_WriteUndoline ( " INTERSCREEN " , mapheaderinfo [ num ] - > interscreen , UNDO_NONE ) ;
mapheaderinfo [ num ] - > interscreen [ 0 ] = ' # ' ;
DEH_WriteUndoline ( " RUNSOC " , mapheaderinfo [ num ] - > runsoc , UNDO_NONE ) ;
mapheaderinfo [ num ] - > runsoc [ 0 ] = ' # ' ;
DEH_WriteUndoline ( " SCRIPTNAME " , mapheaderinfo [ num ] - > scriptname , UNDO_NONE ) ;
mapheaderinfo [ num ] - > scriptname [ 0 ] = ' # ' ;
DEH_WriteUndoline ( " PRECUTSCENENUM " , va ( " %d " , mapheaderinfo [ num ] - > precutscenenum ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > precutscenenum = 0 ;
DEH_WriteUndoline ( " CUTSCENENUM " , va ( " %d " , mapheaderinfo [ num ] - > cutscenenum ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > cutscenenum = 0 ;
DEH_WriteUndoline ( " COUNTDOWN " , va ( " %d " , mapheaderinfo [ num ] - > countdown ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > countdown = 0 ;
DEH_WriteUndoline ( " PALLETE " , va ( " %u " , mapheaderinfo [ num ] - > palette ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > palette = UINT16_MAX ;
2018-08-11 21:23:40 +00:00
DEH_WriteUndoline ( " ENCOREPAL " , va ( " %u " , mapheaderinfo [ num ] - > encorepal ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > encorepal = UINT16_MAX ;
2014-03-15 16:59:03 +00:00
DEH_WriteUndoline ( " NUMLAPS " , va ( " %u " , mapheaderinfo [ num ] - > numlaps ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > numlaps = NUMLAPS_DEFAULT ;
DEH_WriteUndoline ( " UNLOCKABLE " , va ( " %s " , mapheaderinfo [ num ] - > unlockrequired ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > unlockrequired = - 1 ;
DEH_WriteUndoline ( " LEVELSELECT " , va ( " %d " , mapheaderinfo [ num ] - > levelselect ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > levelselect = 0 ;
DEH_WriteUndoline ( " BONUSTYPE " , va ( " %d " , mapheaderinfo [ num ] - > bonustype ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > bonustype = 0 ;
2018-12-17 01:09:59 +00:00
DEH_WriteUndoline ( " SAVEOVERRIDE " , va ( " %d " , mapheaderinfo [ num ] - > saveoverride ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > saveoverride = SAVE_DEFAULT ;
2014-03-15 16:59:03 +00:00
DEH_WriteUndoline ( " LEVELFLAGS " , va ( " %d " , mapheaderinfo [ num ] - > levelflags ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > levelflags = 0 ;
DEH_WriteUndoline ( " MENUFLAGS " , va ( " %d " , mapheaderinfo [ num ] - > menuflags ) , UNDO_NONE ) ;
2019-01-15 19:01:55 +00:00
mapheaderinfo [ num ] - > menuflags = ( mainwads ? 0 : LF2_EXISTSHACK ) ; // see p_setup.c - prevents replacing maps in addons with easier versions
2014-03-15 16:59:03 +00:00
// TODO grades support for delfile (pfft yeah right)
P_DeleteGrades ( num ) ;
2017-12-16 20:38:15 +00:00
// SRB2Kart
//DEH_WriteUndoline("AUTOMAP", va("%d", mapheaderinfo[num]->automap), UNDO_NONE);
//mapheaderinfo[num]->automap = false;
DEH_WriteUndoline ( " MOBJSCALE " , va ( " %d " , mapheaderinfo [ num ] - > mobj_scale ) , UNDO_NONE ) ;
mapheaderinfo [ num ] - > mobj_scale = FRACUNIT ;
2014-08-04 03:49:33 +00:00
// an even further impossibility, delfile custom opts support
mapheaderinfo [ num ] - > customopts = NULL ;
mapheaderinfo [ num ] - > numCustomOptions = 0 ;
2014-03-15 16:59:03 +00:00
DEH_WriteUndoline ( va ( " # uload for map %d " , i ) , NULL , UNDO_DONE ) ;
}
/** 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 ) ;
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 ] ;
}
/** Loads the vertexes for a level.
*
* \ param lump VERTEXES lump number .
* \ sa ML_VERTEXES
*/
2018-11-23 15:58:16 +00:00
static inline void P_LoadRawVertexes ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
mapvertex_t * ml ;
vertex_t * li ;
2018-11-23 15:58:16 +00:00
numvertexes = i / sizeof ( mapvertex_t ) ;
2014-03-15 16:59:03 +00:00
if ( numvertexes < = 0 )
I_Error ( " Level has no vertices " ) ; // instead of crashing
// Allocate zone memory for buffer.
vertexes = Z_Calloc ( numvertexes * sizeof ( * vertexes ) , PU_LEVEL , NULL ) ;
ml = ( mapvertex_t * ) data ;
li = vertexes ;
// Copy and convert vertex coordinates, internal representation as fixed.
for ( i = 0 ; i < numvertexes ; i + + , li + + , ml + + )
{
li - > x = SHORT ( ml - > x ) < < FRACBITS ;
li - > y = SHORT ( ml - > y ) < < FRACBITS ;
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static inline void P_LoadVertexes ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawVertexes ( data , W_LumpLength ( lumpnum ) ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
/** 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 )
{
2018-12-20 21:52:51 +00:00
INT64 dx = ( seg - > v2 - > x - seg - > v1 - > x ) > > 1 ;
INT64 dy = ( seg - > v2 - > y - seg - > v1 - > y ) > > 1 ;
return FixedHypot ( dx , dy ) < < 1 ;
2014-03-15 16:59:03 +00:00
}
# ifdef HWRENDER
2018-12-09 22:34:17 +00:00
/** 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 )
2014-03-15 16:59:03 +00:00
{
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
/** Loads the SEGS resource from a level.
*
* \ param lump Lump number of the SEGS resource .
* \ sa : : ML_SEGS
*/
2018-11-23 15:58:16 +00:00
static void P_LoadRawSegs ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
INT32 linedef , side ;
mapseg_t * ml ;
seg_t * li ;
line_t * ldef ;
2018-11-23 15:58:16 +00:00
numsegs = i / sizeof ( mapseg_t ) ;
2014-03-15 16:59:03 +00:00
if ( numsegs < = 0 )
I_Error ( " Level has no segs " ) ; // instead of crashing
segs = Z_Calloc ( numsegs * sizeof ( * segs ) , PU_LEVEL , NULL ) ;
ml = ( mapseg_t * ) data ;
li = segs ;
for ( i = 0 ; i < numsegs ; i + + , li + + , ml + + )
{
li - > v1 = & vertexes [ SHORT ( ml - > v1 ) ] ;
li - > v2 = & vertexes [ SHORT ( ml - > v2 ) ] ;
2018-12-09 22:34:17 +00:00
li - > length = P_SegLength ( li ) ;
# ifdef HWRENDER
if ( rendermode = = render_opengl )
2014-03-15 16:59:03 +00:00
{
2018-12-09 22:34:17 +00:00
li - > flength = P_SegLengthFloat ( li ) ;
2014-03-15 16:59:03 +00:00
//Hurdler: 04/12/2000: for now, only used in hardware mode
li - > lightmaps = NULL ; // list of static lightmap for this seg
}
2016-11-07 21:55:56 +00:00
li - > pv1 = li - > pv2 = NULL ;
2014-03-15 16:59:03 +00:00
# endif
li - > angle = ( SHORT ( ml - > angle ) ) < < FRACBITS ;
li - > offset = ( SHORT ( ml - > offset ) ) < < FRACBITS ;
linedef = SHORT ( ml - > linedef ) ;
ldef = & lines [ linedef ] ;
li - > linedef = ldef ;
li - > side = side = SHORT ( ml - > side ) ;
li - > sidedef = & sides [ ldef - > sidenum [ side ] ] ;
li - > frontsector = sides [ ldef - > sidenum [ side ] ] . sector ;
if ( ldef - > flags & ML_TWOSIDED )
li - > backsector = sides [ ldef - > sidenum [ side ^ 1 ] ] . sector ;
else
li - > backsector = 0 ;
li - > numlights = 0 ;
li - > rlights = NULL ;
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_LoadSegs ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawSegs ( data , W_LumpLength ( lumpnum ) ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
2018-11-23 15:58:16 +00:00
2014-03-15 16:59:03 +00:00
/** Loads the SSECTORS resource from a level.
*
* \ param lump Lump number of the SSECTORS resource .
* \ sa : : ML_SSECTORS
*/
2018-11-23 15:58:16 +00:00
static inline void P_LoadRawSubsectors ( void * data , size_t i )
2014-03-15 16:59:03 +00:00
{
mapsubsector_t * ms ;
subsector_t * ss ;
2018-11-23 15:58:16 +00:00
numsubsectors = i / sizeof ( mapsubsector_t ) ;
2014-03-15 16:59:03 +00:00
if ( numsubsectors < = 0 )
I_Error ( " Level has no subsectors (did you forget to run it through a nodesbuilder?) " ) ;
ss = subsectors = Z_Calloc ( numsubsectors * sizeof ( * subsectors ) , PU_LEVEL , NULL ) ;
ms = ( mapsubsector_t * ) data ;
for ( i = 0 ; i < numsubsectors ; i + + , ss + + , ms + + )
{
ss - > sector = NULL ;
ss - > numlines = SHORT ( ms - > numsegs ) ;
ss - > firstline = SHORT ( ms - > firstseg ) ;
# ifdef FLOORSPLATS
ss - > splats = NULL ;
# endif
ss - > validcount = 0 ;
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_LoadSubsectors ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawSubsectors ( data , W_LumpLength ( lumpnum ) ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
//
// P_LoadSectors
//
//
// levelflats
//
# define MAXLEVELFLATS 256
size_t numlevelflats ;
levelflat_t * levelflats ;
//SoM: Other files want this info.
size_t P_PrecacheLevelFlats ( void )
{
lumpnum_t lump ;
size_t i , flatmemory = 0 ;
//SoM: 4/18/2000: New flat code to make use of levelflats.
for ( i = 0 ; i < numlevelflats ; i + + )
{
lump = levelflats [ i ] . lumpnum ;
if ( devparm )
flatmemory + = W_LumpLength ( lump ) ;
R_GetFlat ( lump ) ;
}
return flatmemory ;
}
// help function for P_LoadSectors, 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 )
{
size_t i ;
//
// first scan through the already found flats
//
for ( i = 0 ; i < numlevelflats ; i + + , levelflat + + )
if ( strnicmp ( levelflat - > name , flatname , 8 ) = = 0 )
break ;
// that flat was already found in the level, return the id
if ( i = = numlevelflats )
{
// store the name
strlcpy ( levelflat - > name , flatname , sizeof ( levelflat - > name ) ) ;
strupr ( levelflat - > name ) ;
// store the flat lump number
levelflat - > lumpnum = R_GetFlatNumForName ( flatname ) ;
# ifndef ZDEBUG
CONS_Debug ( DBG_SETUP , " flat #%03d: %s \n " , atoi ( sizeu1 ( numlevelflats ) ) , levelflat - > name ) ;
# endif
numlevelflats + + ;
if ( numlevelflats > = MAXLEVELFLATS )
I_Error ( " Too many flats in level \n " ) ;
}
// level flat id
return ( INT32 ) i ;
}
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 )
{
size_t i ;
levelflat_t * levelflat = levelflats ;
//
// first scan through the already found flats
//
for ( i = 0 ; i < numlevelflats ; i + + , levelflat + + )
if ( strnicmp ( levelflat - > name , flatname , 8 ) = = 0 )
break ;
// that flat was already found in the level, return the id
if ( i = = numlevelflats )
{
// allocate new flat memory
levelflats = Z_Realloc ( levelflats , ( numlevelflats + 1 ) * sizeof ( * levelflats ) , PU_LEVEL , NULL ) ;
levelflat = levelflats + i ;
// store the name
strlcpy ( levelflat - > name , flatname , sizeof ( levelflat - > name ) ) ;
strupr ( levelflat - > name ) ;
// store the flat lump number
levelflat - > lumpnum = R_GetFlatNumForName ( flatname ) ;
# ifndef ZDEBUG
CONS_Debug ( DBG_SETUP , " flat #%03d: %s \n " , atoi ( sizeu1 ( numlevelflats ) ) , levelflat - > name ) ;
# endif
numlevelflats + + ;
}
// level flat id
return ( INT32 ) i ;
}
// 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 ;
}
2018-11-23 15:58:16 +00:00
// Sets up the ingame sectors structures.
// Lumpnum is the lumpnum of a SECTORS lump.
static void P_LoadRawSectors ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
mapsector_t * ms ;
sector_t * ss ;
levelflat_t * foundflats ;
2018-11-23 15:58:16 +00:00
// We count how many sectors we got.
numsectors = i / sizeof ( mapsector_t ) ;
2014-03-15 16:59:03 +00:00
if ( numsectors < = 0 )
I_Error ( " Level has no sectors " ) ;
2018-11-23 15:58:16 +00:00
// Allocate as much memory as we need into the global sectors table.
sectors = Z_Calloc ( numsectors * sizeof ( * sectors ) , PU_LEVEL , NULL ) ;
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
// 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
2014-03-15 16:59:03 +00:00
foundflats = calloc ( MAXLEVELFLATS , sizeof ( * foundflats ) ) ;
if ( foundflats = = NULL )
I_Error ( " Ran out of memory while loading sectors \n " ) ;
numlevelflats = 0 ;
2018-11-23 15:58:16 +00:00
// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
2014-03-15 16:59:03 +00:00
ms = ( mapsector_t * ) data ;
ss = sectors ;
for ( i = 0 ; i < numsectors ; i + + , ss + + , ms + + )
{
ss - > floorheight = SHORT ( ms - > floorheight ) < < FRACBITS ;
ss - > ceilingheight = SHORT ( ms - > ceilingheight ) < < FRACBITS ;
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 ) ;
ss - > nexttag = ss - > firsttag = - 1 ;
2017-01-03 20:48:39 +00:00
ss - > spawn_nexttag = ss - > spawn_firsttag = - 1 ;
2014-03-15 16:59:03 +00:00
memset ( & ss - > soundorg , 0 , sizeof ( ss - > soundorg ) ) ;
ss - > validcount = 0 ;
ss - > thinglist = NULL ;
ss - > touching_thinglist = NULL ;
ss - > preciplist = NULL ;
ss - > touching_preciplist = NULL ;
ss - > floordata = NULL ;
ss - > ceilingdata = NULL ;
ss - > lightingdata = NULL ;
ss - > linecount = 0 ;
ss - > lines = NULL ;
ss - > heightsec = - 1 ;
2014-08-04 03:49:33 +00:00
ss - > camsec = - 1 ;
2014-03-15 16:59:03 +00:00
ss - > floorlightsec = - 1 ;
ss - > ceilinglightsec = - 1 ;
ss - > crumblestate = 0 ;
ss - > ffloors = NULL ;
ss - > lightlist = NULL ;
ss - > numlights = 0 ;
ss - > attached = NULL ;
ss - > attachedsolid = NULL ;
ss - > numattached = 0 ;
ss - > maxattached = 1 ;
ss - > moved = true ;
ss - > extra_colormap = NULL ;
ss - > floor_xoffs = ss - > ceiling_xoffs = ss - > floor_yoffs = ss - > ceiling_yoffs = 0 ;
2014-11-12 00:55:07 +00:00
ss - > spawn_flr_xoffs = ss - > spawn_ceil_xoffs = ss - > spawn_flr_yoffs = ss - > spawn_ceil_yoffs = 0 ;
2014-03-15 16:59:03 +00:00
ss - > floorpic_angle = ss - > ceilingpic_angle = 0 ;
2014-11-12 00:55:07 +00:00
ss - > spawn_flrpic_angle = ss - > spawn_ceilpic_angle = 0 ;
2014-03-15 16:59:03 +00:00
ss - > bottommap = ss - > midmap = ss - > topmap = - 1 ;
ss - > gravity = NULL ;
ss - > cullheight = NULL ;
ss - > verticalflip = false ;
ss - > flags = 0 ;
ss - > flags | = SF_FLIPSPECIAL_FLOOR ;
ss - > floorspeed = 0 ;
ss - > ceilspeed = 0 ;
# ifdef HWRENDER // ----- for special tricks with HW renderer -----
ss - > pseudoSector = false ;
ss - > virtualFloor = false ;
ss - > virtualCeiling = false ;
ss - > sectorLines = NULL ;
ss - > stackList = NULL ;
ss - > lineoutLength = - 1.0 l ;
# endif // ----- end special tricks -----
}
// 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 ( ) ;
}
2018-11-23 15:58:16 +00:00
static void P_LoadSectors ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawSectors ( data , W_LumpLength ( lumpnum ) ) ;
Z_Free ( data ) ;
}
2014-03-15 16:59:03 +00:00
//
// P_LoadNodes
//
2018-11-23 15:58:16 +00:00
static void P_LoadRawNodes ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
UINT8 j , k ;
mapnode_t * mn ;
node_t * no ;
2018-11-23 15:58:16 +00:00
numnodes = i / sizeof ( mapnode_t ) ;
2014-03-15 16:59:03 +00:00
if ( numnodes < = 0 )
I_Error ( " Level has no nodes " ) ;
nodes = Z_Calloc ( numnodes * sizeof ( * nodes ) , PU_LEVEL , NULL ) ;
mn = ( mapnode_t * ) data ;
no = nodes ;
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 ;
}
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_LoadNodes ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawNodes ( data , W_LumpLength ( lumpnum ) ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
//
// P_ReloadRings
// Used by NiGHTS, clears all ring/wing/etc items and respawns them
//
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 ;
// scan the thinkers to find rings/wings/hoops to unset
for ( th = thinkercap . next ; th ! = & thinkercap ; th = th - > next )
{
if ( th - > function . acp1 ! = ( actionf_p1 ) P_MobjThinker )
continue ;
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 ;
}
2017-03-04 22:13:19 +00:00
if ( ! ( mo - > type = = MT_RING | | mo - > type = = MT_NIGHTSWING | | mo - > type = = MT_COIN
2018-03-13 05:20:47 +00:00
| | mo - > type = = MT_BLUEBALL ) )
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.
if ( mt - > type = = 300 | | mt - > type = = 308 | | mt - > type = = 309
| | mt - > type = = 1706 | | ( mt - > type > = 600 & & mt - > type < = 609 )
| | mt - > type = = 1800 )
{
mt - > mobj = NULL ;
// Z for objects Tails 05-26-2002
mt - > z = ( INT16 ) ( R_PointInSubsector ( mt - > x < < FRACBITS , mt - > y < < FRACBITS )
- > sector - > floorheight > > FRACBITS ) ;
P_SpawnHoopsAndRings ( mt ) ;
}
}
for ( i = 0 ; i < numHoops ; i + + )
{
P_SpawnHoopsAndRings ( hoopsToRespawn [ i ] ) ;
}
}
# 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
//
// P_LoadThings
//
2018-11-23 15:58:16 +00:00
static void P_PrepareRawThings ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
mapthing_t * mt ;
2018-11-23 15:58:16 +00:00
nummapthings = i / ( 5 * sizeof ( INT16 ) ) ;
2014-03-15 16:59:03 +00:00
mapthings = Z_Calloc ( nummapthings * sizeof ( * mapthings ) , PU_LEVEL , NULL ) ;
// Spawn axis points first so they are
// at the front of the list for fast searching.
mt = mapthings ;
for ( i = 0 ; i < nummapthings ; i + + , mt + + )
{
mt - > x = READINT16 ( data ) ;
mt - > y = READINT16 ( data ) ;
mt - > angle = READINT16 ( data ) ;
mt - > type = READUINT16 ( data ) ;
mt - > options = READUINT16 ( data ) ;
mt - > extrainfo = ( UINT8 ) ( mt - > type > > 12 ) ;
mt - > type & = 4095 ;
switch ( mt - > type )
{
case 1700 : // MT_AXIS
case 1701 : // MT_AXISTRANSFER
case 1702 : // MT_AXISTRANSFERLINE
mt - > mobj = NULL ;
P_SpawnMapThing ( mt ) ;
break ;
default :
break ;
}
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_PrepareThings ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_PrepareRawThings ( data , W_LumpLength ( lumpnum ) ) ;
Z_Free ( data ) ;
2016-07-06 04:09:17 +00:00
}
static void P_LoadThings ( void )
{
size_t i ;
mapthing_t * mt ;
// Loading the things lump itself into memory is now handled in P_PrepareThings, above
2014-03-15 16:59:03 +00:00
mt = mapthings ;
numhuntemeralds = 0 ;
for ( i = 0 ; i < nummapthings ; i + + , mt + + )
{
2016-07-06 04:09:17 +00:00
sector_t * mtsector = R_PointInSubsector ( mt - > x < < FRACBITS , mt - > y < < FRACBITS ) - > sector ;
2014-03-15 16:59:03 +00:00
// Z for objects
2016-07-06 04:09:17 +00:00
mt - > z = ( INT16 ) (
# ifdef ESLOPE
mtsector - > f_slope ? P_GetZAt ( mtsector - > f_slope , mt - > x < < FRACBITS , mt - > y < < FRACBITS ) :
# endif
mtsector - > floorheight ) > > FRACBITS ;
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
2018-03-27 00:36:16 +00:00
if ( mt - > type = = mobjinfo [ MT_RANDOMITEM ] . doomednum )
2017-10-25 01:22:09 +00:00
nummapboxes + + ;
2014-03-15 16:59:03 +00:00
mt - > mobj = NULL ;
P_SpawnMapThing ( mt ) ;
}
// random emeralds for hunt
if ( numhuntemeralds )
{
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 - 1 ] - > x < < FRACBITS ,
huntemeralds [ emer1 - 1 ] - > y < < FRACBITS ,
huntemeralds [ emer1 - 1 ] - > z < < FRACBITS , MT_EMERHUNT ) ;
if ( emer2 )
P_SpawnMobj ( huntemeralds [ emer2 - 1 ] - > x < < FRACBITS ,
huntemeralds [ emer2 - 1 ] - > y < < FRACBITS ,
huntemeralds [ emer2 - 1 ] - > z < < FRACBITS , MT_EMERHUNT ) ;
if ( emer3 )
P_SpawnMobj ( huntemeralds [ emer3 - 1 ] - > x < < FRACBITS ,
huntemeralds [ emer3 - 1 ] - > y < < FRACBITS ,
huntemeralds [ emer3 - 1 ] - > z < < FRACBITS , MT_EMERHUNT ) ;
}
if ( metalrecording ) // Metal Sonic gets no rings to distract him.
return ;
// Run through the list of mapthings again to spawn hoops and rings
mt = mapthings ;
for ( i = 0 ; i < nummapthings ; i + + , mt + + )
{
if ( mt - > type = = 300 | | mt - > type = = 308 | | mt - > type = = 309
| | mt - > type = = 1706 | | ( mt - > type > = 600 & & mt - > type < = 609 )
| | mt - > type = = 1705 | | mt - > type = = 1713 | | mt - > type = = 1800 )
{
mt - > mobj = NULL ;
// Z for objects Tails 05-26-2002
mt - > z = ( INT16 ) ( R_PointInSubsector ( mt - > x < < FRACBITS , mt - > y < < FRACBITS )
- > sector - > floorheight > > FRACBITS ) ;
P_SpawnHoopsAndRings ( mt ) ;
}
}
}
static inline void P_SpawnEmblems ( void )
{
INT32 i , color ;
mobj_t * emblemmobj ;
for ( i = 0 ; i < numemblems ; i + + )
{
if ( emblemlocations [ i ] . level ! = gamemap | | emblemlocations [ i ] . type > ET_SKIN )
continue ;
emblemmobj = P_SpawnMobj ( emblemlocations [ i ] . x < < FRACBITS , emblemlocations [ i ] . y < < FRACBITS ,
emblemlocations [ i ] . z < < FRACBITS , MT_EMBLEM ) ;
I_Assert ( emblemlocations [ i ] . sprite > = ' A ' & & emblemlocations [ i ] . sprite < = ' Z ' ) ;
P_SetMobjStateNF ( emblemmobj , emblemmobj - > info - > spawnstate + ( emblemlocations [ i ] . sprite - ' A ' ) ) ;
emblemmobj - > health = i + 1 ;
color = M_GetEmblemColor ( & emblemlocations [ i ] ) ;
emblemmobj - > color = ( UINT8 ) color ;
if ( emblemlocations [ i ] . collected
| | ( emblemlocations [ i ] . type = = ET_SKIN & & emblemlocations [ i ] . var ! = players [ 0 ] . skin ) )
{
P_UnsetThingPosition ( emblemmobj ) ;
emblemmobj - > flags | = MF_NOCLIP ;
emblemmobj - > flags & = ~ MF_SPECIAL ;
emblemmobj - > flags | = MF_NOBLOCKMAP ;
emblemmobj - > frame | = ( tr_trans50 < < FF_TRANSSHIFT ) ;
P_SetThingPosition ( emblemmobj ) ;
}
else
emblemmobj - > frame & = ~ FF_TRANSMASK ;
}
}
static void P_SpawnSecretItems ( boolean loademblems )
{
// Now let's spawn those funky emblem things! Tails 12-08-2002
2019-01-26 16:58:45 +00:00
if ( netgame | | multiplayer | | majormods ) // No cheating!!
2014-03-15 16:59:03 +00:00
return ;
if ( loademblems )
P_SpawnEmblems ( ) ;
}
// 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 ) ;
}
2018-11-23 15:58:16 +00:00
static void P_LoadRawLineDefs ( UINT8 * data , size_t i )
2014-03-15 16:59:03 +00:00
{
maplinedef_t * mld ;
line_t * ld ;
vertex_t * v1 , * v2 ;
2018-11-23 15:58:16 +00:00
numlines = i / sizeof ( maplinedef_t ) ;
2014-03-15 16:59:03 +00:00
if ( numlines < = 0 )
I_Error ( " Level has no linedefs " ) ;
lines = Z_Calloc ( numlines * sizeof ( * lines ) , PU_LEVEL , NULL ) ;
mld = ( maplinedef_t * ) data ;
ld = lines ;
for ( i = 0 ; i < numlines ; i + + , mld + + , ld + + )
{
ld - > flags = SHORT ( mld - > flags ) ;
ld - > special = SHORT ( mld - > special ) ;
ld - > tag = SHORT ( mld - > tag ) ;
v1 = ld - > v1 = & vertexes [ SHORT ( mld - > v1 ) ] ;
v2 = ld - > v2 = & vertexes [ SHORT ( mld - > v2 ) ] ;
ld - > dx = v2 - > x - v1 - > x ;
ld - > dy = v2 - > y - v1 - > y ;
# ifdef WALLSPLATS
ld - > splats = NULL ;
# endif
if ( ! ld - > dx )
ld - > slopetype = ST_VERTICAL ;
else if ( ! ld - > dy )
ld - > slopetype = ST_HORIZONTAL ;
2018-11-23 15:58:16 +00:00
else if ( ( ld - > dy > 0 ) = = ( ld - > dx > 0 ) )
2014-03-15 16:59:03 +00:00
ld - > slopetype = ST_POSITIVE ;
else
ld - > slopetype = ST_NEGATIVE ;
if ( v1 - > x < v2 - > x )
{
ld - > bbox [ BOXLEFT ] = v1 - > x ;
ld - > bbox [ BOXRIGHT ] = v2 - > x ;
}
else
{
ld - > bbox [ BOXLEFT ] = v2 - > x ;
ld - > bbox [ BOXRIGHT ] = v1 - > x ;
}
if ( v1 - > y < v2 - > y )
{
ld - > bbox [ BOXBOTTOM ] = v1 - > y ;
ld - > bbox [ BOXTOP ] = v2 - > y ;
}
else
{
ld - > bbox [ BOXBOTTOM ] = v2 - > y ;
ld - > bbox [ BOXTOP ] = v1 - > y ;
}
ld - > sidenum [ 0 ] = SHORT ( mld - > sidenum [ 0 ] ) ;
ld - > sidenum [ 1 ] = SHORT ( mld - > sidenum [ 1 ] ) ;
{
// cph 2006/09/30 - fix sidedef errors right away.
// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
UINT8 j ;
for ( j = 0 ; j < 2 ; j + + )
{
if ( ld - > sidenum [ j ] ! = 0xffff & & ld - > sidenum [ j ] > = ( UINT16 ) numsides )
{
ld - > sidenum [ j ] = 0xffff ;
2018-11-23 15:58:16 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadRawLineDefs: linedef %s has out-of-range sidedef number \n " , sizeu1 ( numlines - i - 1 ) ) ;
2014-03-15 16:59:03 +00:00
}
}
}
ld - > frontsector = ld - > backsector = NULL ;
ld - > validcount = 0 ;
ld - > firsttag = ld - > nexttag = - 1 ;
2014-11-12 00:55:07 +00:00
ld - > callcount = 0 ;
2014-03-15 16:59:03 +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
2018-11-23 15:58:16 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadRawLineDefs: linedef %s missing first sidedef \n " , sizeu1 ( numlines - i - 1 ) ) ;
2014-03-15 16:59:03 +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
2018-11-23 15:58:16 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadRawLineDefs: linedef %s has two-sided flag set, but no second sidedef \n " , sizeu1 ( numlines - i - 1 ) ) ;
2014-03-15 16:59:03 +00:00
}
if ( ld - > sidenum [ 0 ] ! = 0xffff & & ld - > special )
sides [ ld - > sidenum [ 0 ] ] . special = ld - > special ;
if ( ld - > sidenum [ 1 ] ! = 0xffff & & ld - > special )
sides [ ld - > sidenum [ 1 ] ] . special = ld - > special ;
# ifdef POLYOBJECTS
ld - > polyobj = NULL ;
# endif
}
2018-11-23 15:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_LoadLineDefs ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawLineDefs ( data , W_LumpLength ( lumpnum ) ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
static void P_LoadLineDefs2 ( 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 ;
// Repeat count for midtexture
if ( ( ld - > flags & ML_EFFECT5 ) & & ( ld - > sidenum [ 1 ] ! = 0xffff ) )
{
sides [ ld - > sidenum [ 0 ] ] . repeatcnt = ( INT16 ) ( ( ( unsigned ) sides [ ld - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) > > 12 ) ;
sides [ ld - > sidenum [ 0 ] ] . textureoffset = ( ( ( unsigned ) sides [ ld - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) & 2047 ) < < FRACBITS ;
sides [ ld - > sidenum [ 1 ] ] . repeatcnt = ( INT16 ) ( ( ( unsigned ) sides [ ld - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) > > 12 ) ;
sides [ ld - > sidenum [ 1 ] ] . textureoffset = ( ( ( unsigned ) sides [ ld - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) & 2047 ) < < FRACBITS ;
}
// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
switch ( ld - > special )
{
case 443 : // Calls a named Lua function
if ( sides [ ld - > sidenum [ 0 ] ] . text )
{
2016-07-06 04:09:17 +00:00
size_t len = strlen ( sides [ ld - > sidenum [ 0 ] ] . text ) + 1 ;
2014-03-15 16:59:03 +00:00
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 ;
}
}
// Optimize sidedefs
if ( M_CheckParm ( " -compress " ) )
{
side_t * newsides ;
size_t numnewsides = 0 ;
size_t z ;
for ( i = 0 ; i < numsides ; i + + )
{
size_t j , k ;
if ( sides [ i ] . sector = = NULL )
continue ;
for ( k = numlines , ld = lines ; k - - ; ld + + )
{
if ( ld - > sidenum [ 0 ] = = i )
ld - > sidenum [ 0 ] = ( UINT16 ) numnewsides ;
if ( ld - > sidenum [ 1 ] = = i )
ld - > sidenum [ 1 ] = ( UINT16 ) numnewsides ;
}
for ( j = i + 1 ; j < numsides ; j + + )
{
if ( sides [ j ] . sector = = NULL )
continue ;
if ( ! memcmp ( & sides [ i ] , & sides [ j ] , sizeof ( side_t ) ) )
{
// Find the linedefs that belong to this one
for ( k = numlines , ld = lines ; k - - ; ld + + )
{
if ( ld - > sidenum [ 0 ] = = j )
ld - > sidenum [ 0 ] = ( UINT16 ) numnewsides ;
if ( ld - > sidenum [ 1 ] = = j )
ld - > sidenum [ 1 ] = ( UINT16 ) numnewsides ;
}
sides [ j ] . sector = NULL ; // Flag for deletion
}
}
numnewsides + + ;
}
// We're loading crap into this block anyhow, so no point in zeroing it out.
newsides = Z_Malloc ( numnewsides * sizeof ( * newsides ) , PU_LEVEL , NULL ) ;
// Copy the sides to their new block of memory.
for ( i = 0 , z = 0 ; i < numsides ; i + + )
{
if ( sides [ i ] . sector ! = NULL )
M_Memcpy ( & newsides [ z + + ] , & sides [ i ] , sizeof ( side_t ) ) ;
}
CONS_Debug ( DBG_SETUP , " Old sides is %s, new sides is %s \n " , sizeu1 ( numsides ) , sizeu1 ( numnewsides ) ) ;
Z_Free ( sides ) ;
sides = newsides ;
numsides = numnewsides ;
}
}
2018-11-23 15:58:16 +00:00
static inline void P_LoadRawSideDefs ( size_t i )
2014-03-15 16:59:03 +00:00
{
2018-11-23 15:58:16 +00:00
numsides = i / sizeof ( mapsidedef_t ) ;
2014-03-15 16:59:03 +00:00
if ( numsides < = 0 )
I_Error ( " Level has no sidedefs " ) ;
sides = Z_Calloc ( numsides * sizeof ( * sides ) , PU_LEVEL , NULL ) ;
}
2018-11-23 15:58:16 +00:00
static inline void P_LoadSideDefs ( lumpnum_t lumpnum )
{
P_LoadRawSideDefs ( W_LumpLength ( lumpnum ) ) ;
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
static void P_LoadRawSideDefs2 ( void * data )
2014-03-15 16:59:03 +00:00
{
UINT16 i ;
INT32 num ;
for ( i = 0 ; i < numsides ; i + + )
{
register mapsidedef_t * msd = ( mapsidedef_t * ) data + i ;
register side_t * sd = sides + i ;
register sector_t * sec ;
sd - > textureoffset = SHORT ( msd - > textureoffset ) < < FRACBITS ;
sd - > rowoffset = SHORT ( msd - > rowoffset ) < < FRACBITS ;
{ /* cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead */
UINT16 sector_num = SHORT ( msd - > sector ) ;
if ( sector_num > = numsectors )
{
2018-11-23 15:58:16 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadRawSideDefs2: sidedef %u has out-of-range sector num %u \n " , i , sector_num ) ;
2014-03-15 16:59:03 +00:00
sector_num = 0 ;
}
sd - > sector = sec = & sectors [ sector_num ] ;
}
// refined to allow colormaps to work as wall textures if invalid as colormaps
// but valid as textures.
sd - > sector = sec = & sectors [ SHORT ( msd - > sector ) ] ;
// Colormaps!
switch ( sd - > special )
{
case 63 : // variable colormap via 242 linedef
case 606 : //SoM: 4/4/2000: Just colormap transfer
// SoM: R_CreateColormap will only create a colormap in software mode...
// Perhaps we should just call it instead of doing the calculations here.
if ( rendermode = = render_soft | | rendermode = = render_none )
{
if ( msd - > toptexture [ 0 ] = = ' # ' | | msd - > bottomtexture [ 0 ] = = ' # ' )
{
sec - > midmap = R_CreateColormap ( msd - > toptexture , msd - > midtexture ,
msd - > bottomtexture ) ;
sd - > toptexture = sd - > bottomtexture = 0 ;
}
else
{
if ( ( num = R_CheckTextureNumForName ( msd - > toptexture ) ) = = - 1 )
sd - > toptexture = 0 ;
else
sd - > toptexture = num ;
if ( ( num = R_CheckTextureNumForName ( msd - > midtexture ) ) = = - 1 )
sd - > midtexture = 0 ;
else
sd - > midtexture = num ;
if ( ( num = R_CheckTextureNumForName ( msd - > bottomtexture ) ) = = - 1 )
sd - > bottomtexture = 0 ;
else
sd - > bottomtexture = num ;
}
break ;
}
# ifdef HWRENDER
else
{
// for now, full support of toptexture only
if ( ( msd - > toptexture [ 0 ] = = ' # ' & & msd - > toptexture [ 1 ] & & msd - > toptexture [ 2 ] & & msd - > toptexture [ 3 ] & & msd - > toptexture [ 4 ] & & msd - > toptexture [ 5 ] & & msd - > toptexture [ 6 ] )
| | ( msd - > bottomtexture [ 0 ] = = ' # ' & & msd - > bottomtexture [ 1 ] & & msd - > bottomtexture [ 2 ] & & msd - > bottomtexture [ 3 ] & & msd - > bottomtexture [ 4 ] & & msd - > bottomtexture [ 5 ] & & msd - > bottomtexture [ 6 ] ) )
{
char * col ;
sec - > midmap = R_CreateColormap ( msd - > toptexture , msd - > midtexture ,
msd - > bottomtexture ) ;
sd - > toptexture = sd - > bottomtexture = 0 ;
# define HEX2INT(x) (x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
# define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
sec - > extra_colormap = & extra_colormaps [ sec - > midmap ] ;
if ( msd - > toptexture [ 0 ] = = ' # ' & & msd - > toptexture [ 1 ] & & msd - > toptexture [ 2 ] & & msd - > toptexture [ 3 ] & & msd - > toptexture [ 4 ] & & msd - > toptexture [ 5 ] & & msd - > toptexture [ 6 ] )
{
col = msd - > toptexture ;
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > rgba =
( HEX2INT ( col [ 1 ] ) < < 4 ) + ( HEX2INT ( col [ 2 ] ) < < 0 ) +
( HEX2INT ( col [ 3 ] ) < < 12 ) + ( HEX2INT ( col [ 4 ] ) < < 8 ) +
( HEX2INT ( col [ 5 ] ) < < 20 ) + ( HEX2INT ( col [ 6 ] ) < < 16 ) ;
// alpha
2014-03-15 16:59:03 +00:00
if ( msd - > toptexture [ 7 ] )
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > rgba + = ( ALPHA2INT ( col [ 7 ] ) < < 24 ) ;
2014-03-15 16:59:03 +00:00
else
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > rgba + = ( 25 < < 24 ) ;
2014-03-15 16:59:03 +00:00
}
else
sec - > extra_colormap - > rgba = 0 ;
if ( msd - > bottomtexture [ 0 ] = = ' # ' & & msd - > bottomtexture [ 1 ] & & msd - > bottomtexture [ 2 ] & & msd - > bottomtexture [ 3 ] & & msd - > bottomtexture [ 4 ] & & msd - > bottomtexture [ 5 ] & & msd - > bottomtexture [ 6 ] )
{
col = msd - > bottomtexture ;
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > fadergba =
( HEX2INT ( col [ 1 ] ) < < 4 ) + ( HEX2INT ( col [ 2 ] ) < < 0 ) +
( HEX2INT ( col [ 3 ] ) < < 12 ) + ( HEX2INT ( col [ 4 ] ) < < 8 ) +
( HEX2INT ( col [ 5 ] ) < < 20 ) + ( HEX2INT ( col [ 6 ] ) < < 16 ) ;
// alpha
2014-03-15 16:59:03 +00:00
if ( msd - > bottomtexture [ 7 ] )
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > fadergba + = ( ALPHA2INT ( col [ 7 ] ) < < 24 ) ;
2014-03-15 16:59:03 +00:00
else
2014-08-27 03:56:30 +00:00
sec - > extra_colormap - > fadergba + = ( 25 < < 24 ) ;
2014-03-15 16:59:03 +00:00
}
else
2014-11-12 00:55:07 +00:00
sec - > extra_colormap - > fadergba = 0x19000000 ; // default alpha, (25 << 24)
2014-03-15 16:59:03 +00:00
# undef ALPHA2INT
# undef HEX2INT
}
else
{
if ( ( num = R_CheckTextureNumForName ( msd - > toptexture ) ) = = - 1 )
sd - > toptexture = 0 ;
else
sd - > toptexture = num ;
if ( ( num = R_CheckTextureNumForName ( msd - > midtexture ) ) = = - 1 )
sd - > midtexture = 0 ;
else
sd - > midtexture = num ;
if ( ( num = R_CheckTextureNumForName ( msd - > bottomtexture ) ) = = - 1 )
sd - > bottomtexture = 0 ;
else
sd - > bottomtexture = num ;
}
break ;
}
# endif
case 413 : // Change music
2016-07-06 04:09:17 +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 ' ;
sd - > bottomtexture = get_number ( process ) - 1 ;
}
M_Memcpy ( process , msd - > toptexture , 8 ) ;
process [ 8 ] = ' \0 ' ;
sd - > text = Z_Malloc ( 7 , PU_LEVEL , NULL ) ;
// 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 ;
break ;
}
2018-11-23 15:58:16 +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 ;
}
2018-11-23 15:58:16 +00:00
case 9 : // Mace parameters
case 14 : // Bustable block parameters
case 15 : // Fan particle spawner parameters
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
{
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 ;
}
case 443 : // Calls a named Lua function
{
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 ;
}
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 ;
}
}
2018-11-23 15:58:16 +00:00
R_ClearTextureNumCache ( true ) ;
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
// Delay loading texture names until after loaded linedefs.
static void P_LoadSideDefs2 ( lumpnum_t lumpnum )
{
UINT8 * data = W_CacheLumpNum ( lumpnum , PU_STATIC ) ;
P_LoadRawSideDefs2 ( data ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( data ) ;
}
2018-11-23 15:58:16 +00:00
2014-03-15 16:59:03 +00:00
static boolean LineInBlock ( fixed_t cx1 , fixed_t cy1 , fixed_t cx2 , fixed_t cy2 , fixed_t bx1 , fixed_t by1 )
{
2015-01-01 19:50:31 +00:00
fixed_t bbox [ 4 ] ;
line_t testline ;
vertex_t vtest ;
bbox [ BOXRIGHT ] = bx1 + MAPBLOCKUNITS ;
bbox [ BOXTOP ] = by1 + MAPBLOCKUNITS ;
bbox [ BOXLEFT ] = bx1 ;
bbox [ BOXBOTTOM ] = by1 ;
2014-03-15 16:59:03 +00:00
// Trivial rejection
2015-01-01 19:50:31 +00:00
if ( cx1 < bbox [ BOXLEFT ] & & cx2 < bbox [ BOXLEFT ] )
2014-03-15 16:59:03 +00:00
return false ;
2015-01-01 19:50:31 +00:00
if ( cx1 > bbox [ BOXRIGHT ] & & cx2 > bbox [ BOXRIGHT ] )
2014-03-15 16:59:03 +00:00
return false ;
2015-01-01 19:50:31 +00:00
if ( cy1 < bbox [ BOXBOTTOM ] & & cy2 < bbox [ BOXBOTTOM ] )
2014-03-15 16:59:03 +00:00
return false ;
2015-01-01 19:50:31 +00:00
if ( cy1 > bbox [ BOXTOP ] & & cy2 > bbox [ BOXTOP ] )
2014-03-15 16:59:03 +00:00
return false ;
// Rats, guess we gotta check
// if the line intersects
// any sides of the block.
cx1 < < = FRACBITS ;
cy1 < < = FRACBITS ;
cx2 < < = FRACBITS ;
cy2 < < = FRACBITS ;
2015-01-01 19:50:31 +00:00
bbox [ BOXTOP ] < < = FRACBITS ;
bbox [ BOXBOTTOM ] < < = FRACBITS ;
bbox [ BOXLEFT ] < < = FRACBITS ;
bbox [ BOXRIGHT ] < < = FRACBITS ;
2014-03-15 16:59:03 +00:00
testline . v1 = & vtest ;
testline . v1 - > x = cx1 ;
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
}
}
2018-11-23 15:58:16 +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 ;
}
}
2014-03-15 16:59:03 +00:00
//
// P_LoadBlockMap
//
// Levels might not have a blockmap, so if one does not exist
// this should return false.
static boolean P_LoadBlockMap ( lumpnum_t lumpnum )
{
#if 0
( void ) lumpnum ;
return false ;
# else
size_t count ;
const char * lumpname = W_CheckNameForNum ( lumpnum ) ;
// Check if the lump exists, and if it's named "BLOCKMAP"
if ( ! lumpname | | memcmp ( lumpname , " BLOCKMAP " , 8 ) ! = 0 )
{
return false ;
}
count = W_LumpLength ( lumpnum ) ;
if ( ! count | | count > = 0x20000 )
return false ;
{
INT16 * wadblockmaplump = malloc ( count ) ; //INT16 *wadblockmaplump = W_CacheLumpNum (lump, PU_LEVEL);
2018-11-23 15:58:16 +00:00
if ( ! wadblockmaplump )
return false ;
W_ReadLump ( lumpnum , wadblockmaplump ) ;
2014-03-15 16:59:03 +00:00
count / = 2 ;
2018-11-23 15:58:16 +00:00
P_ReadBlockMapLump ( wadblockmaplump , count ) ;
2014-03-15 16:59:03 +00:00
free ( wadblockmaplump ) ;
}
2018-11-23 15:58:16 +00:00
bmaporgx = blockmaplump [ 0 ] < < FRACBITS ;
bmaporgy = blockmaplump [ 1 ] < < FRACBITS ;
bmapwidth = blockmaplump [ 2 ] ;
bmapheight = blockmaplump [ 3 ] ;
2014-03-15 16:59:03 +00:00
// 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 ;
/* Original
blockmaplump = W_CacheLumpNum ( lump , PU_LEVEL ) ;
blockmap = blockmaplump + 4 ;
count = W_LumpLength ( lump ) / 2 ;
for ( i = 0 ; i < count ; i + + )
blockmaplump [ i ] = SHORT ( blockmaplump [ i ] ) ;
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 ) ;
return true ;
*/
# endif
}
2018-11-23 15:58:16 +00:00
// 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_LoadRawBlockMap ( UINT8 * data , size_t count , const char * lumpname )
{
#if 0
( void ) data ;
( void ) count ;
( void ) lumpname ;
return false ;
# else
// Check if the lump is named "BLOCKMAP"
if ( ! lumpname | | memcmp ( lumpname , " BLOCKMAP " , 8 ) ! = 0 )
{
CONS_Printf ( " No blockmap lump found for pk3! \n " ) ;
return false ;
}
if ( ! count | | count > = 0x20000 )
return false ;
2018-12-20 06:11:07 +00:00
//CONS_Printf("Reading blockmap lump for pk3...\n");
2018-11-23 15:58:16 +00:00
// 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 ;
# endif
}
2014-03-15 16:59:03 +00:00
//
// P_GroupLines
// Builds sector line lists and subsector sector numbers.
// Finds block bounding boxes for sectors.
//
static void P_GroupLines ( void )
{
2016-07-06 04:09:17 +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 )
CorruptMapError ( va ( " P_GroupLines: ss->firstline invalid "
" (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 )
CorruptMapError ( va ( " P_GroupLines: seg->sidedef is NULL "
" (subsector %s, firstline is %d) " , sizeu1 ( i ) , ss - > firstline ) ) ;
if ( seg - > sidedef - sides < 0 | | seg - > sidedef - sides > ( UINT16 ) numsides )
CorruptMapError ( va ( " P_GroupLines: seg->sidedef refers to sidedef %s of %s "
" (subsector %s, firstline is %d) " , sizeu1 ( sidei ) , sizeu2 ( numsides ) ,
sizeu3 ( i ) , ss - > firstline ) ) ;
if ( ! seg - > sidedef - > sector )
CorruptMapError ( va ( " P_GroupLines: seg->sidedef->sector is NULL "
" (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 ;
2016-06-19 16:48:35 +00:00
CONS_Debug ( DBG_SETUP , " P_GroupLines: 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
}
}
2016-06-11 17:45:56 +00:00
//
// P_LoadReject
//
// Detect if the REJECT lump is valid,
// if not, rejectmatrix will be NULL
static void P_LoadReject ( lumpnum_t lumpnum )
{
size_t count ;
const char * lumpname = W_CheckNameForNum ( lumpnum ) ;
// Check if the lump exists, and if it's named "REJECT"
2016-06-12 20:16:41 +00:00
if ( ! lumpname | | memcmp ( lumpname , " REJECT \0 \0 " , 8 ) ! = 0 )
2016-06-11 17:45:56 +00:00
{
rejectmatrix = NULL ;
2016-06-12 20:51:27 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadReject: No valid REJECT lump found \n " ) ;
2016-06-11 17:45:56 +00:00
return ;
}
count = W_LumpLength ( lumpnum ) ;
if ( ! count ) // zero length, someone probably used ZDBSP
2016-06-12 20:51:27 +00:00
{
2016-06-11 17:45:56 +00:00
rejectmatrix = NULL ;
2016-06-12 20:51:27 +00:00
CONS_Debug ( DBG_SETUP , " P_LoadReject: REJECT lump has size 0, will not be loaded \n " ) ;
}
2016-06-11 17:45:56 +00:00
else
rejectmatrix = W_CacheLumpNum ( lumpnum , PU_LEVEL ) ;
}
2018-11-23 15:58:16 +00:00
// PK3 version
// -- Monster Iestyn 09/01/18
static void P_LoadRawReject ( UINT8 * data , size_t count , const char * lumpname )
{
// Check if the lump is named "REJECT"
if ( ! lumpname | | memcmp ( lumpname , " REJECT \0 \0 " , 8 ) ! = 0 )
{
rejectmatrix = NULL ;
CONS_Debug ( DBG_SETUP , " P_LoadRawReject: No valid REJECT lump found \n " ) ;
return ;
}
if ( ! count ) // zero length, someone probably used ZDBSP
{
rejectmatrix = NULL ;
CONS_Debug ( DBG_SETUP , " P_LoadRawReject: 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
}
}
2014-03-15 16:59:03 +00:00
#if 0
static char * levellumps [ ] =
{
" label " , // ML_LABEL, A separator, name, MAPxx
" THINGS " , // ML_THINGS, Enemies, items..
" LINEDEFS " , // ML_LINEDEFS, Linedefs, from editing
" SIDEDEFS " , // ML_SIDEDEFS, Sidedefs, from editing
" VERTEXES " , // ML_VERTEXES, Vertices, edited and BSP splits generated
" SEGS " , // ML_SEGS, Linesegs, from linedefs split by BSP
" SSECTORS " , // ML_SSECTORS, Subsectors, list of linesegs
" NODES " , // ML_NODES, BSP nodes
" SECTORS " , // ML_SECTORS, Sectors, from editing
" REJECT " , // ML_REJECT, LUT, sector-sector visibility
} ;
/** Checks a lump and returns whether it is a valid start-of-level marker.
*
* \ param lumpnum Lump number to check .
* \ return True if the lump is a valid level marker , false if not .
*/
static inline boolean P_CheckLevel ( lumpnum_t lumpnum )
{
UINT16 file , lump ;
size_t i ;
for ( i = ML_THINGS ; i < = ML_REJECT ; i + + )
{
file = WADFILENUM ( lumpnum ) ;
lump = LUMPNUM ( lumpnum + 1 ) ;
if ( file > numwadfiles | | lump < LUMPNUM ( lumpnum ) | | lump > wadfiles [ file ] - > numlumps | |
memcmp ( wadfiles [ file ] - > lumpinfo [ lump ] . name , levellumps [ i ] , 8 ) ! = 0 )
return false ;
}
return true ; // all right
}
# endif
/** 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.
//
static void P_LevelInitStuff ( void )
{
INT32 i ;
leveltime = 0 ;
2019-01-09 01:14:58 +00:00
localaiming = localaiming2 = localaiming3 = localaiming4 = 0 ;
// map object scale
mapobjectscale = mapheaderinfo [ gamemap - 1 ] - > mobj_scale ;
2014-03-15 16:59:03 +00:00
// special stage tokens, emeralds, and ring total
tokenbits = 0 ;
runemeraldmanager = false ;
nummaprings = 0 ;
2017-10-24 21:04:22 +00:00
nummapboxes = 0 ;
numgotboxes = 0 ;
2014-03-15 16:59:03 +00:00
// emerald hunt
hunt1 = hunt2 = hunt3 = NULL ;
// map time limit
if ( mapheaderinfo [ gamemap - 1 ] - > countdown )
countdowntimer = mapheaderinfo [ gamemap - 1 ] - > countdown * TICRATE ;
else
countdowntimer = 0 ;
countdowntimeup = false ;
// clear ctf pointers
redflag = blueflag = NULL ;
rflagpoint = bflagpoint = NULL ;
// circuit, race and competition stuff
circuitmap = false ;
numstarposts = 0 ;
totalrings = timeinmap = 0 ;
// special stage
stagefailed = false ;
// Reset temporary record data
2018-01-22 00:15:26 +00:00
//memset(&ntemprecords, 0, sizeof(nightsdata_t));
2014-03-15 16:59:03 +00:00
// earthquake camera
memset ( & quake , 0 , sizeof ( struct quake ) ) ;
2019-01-04 21:47:03 +00:00
// song credit init
memset ( & cursongcredit , 0 , sizeof ( struct cursongcredit ) ) ;
cursongcredit . trans = NUMTRANSMAPS ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
Mammoth commit!
* Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla..
* Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves.
* Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three.
* Major tweaks to the First Person HUD.
* I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes.
* Dontdraw-ness, transparency, and colorization match the player's object!
* Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer!
* Flashes the colour of your drift sparks.
* Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step.
* Countdowns now play the drowning music again.
* Removed/disabled extraenous P_RestoreMusics.
* Made map-ending music called by its own function, P_EndingMusic(player_t *player).
* Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first.
* Make dead players spinout and clip through the floor, at least until we add the new death anims.
* Fix prior pogo spring usage making dead players fall faster.
* Make the time over countdown use the kart font when not splitscreen with 3 or 4 players.
* Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 19:19:30 +00:00
#if 0
2014-03-15 16:59:03 +00:00
if ( ( netgame | | multiplayer ) & & ( gametype = = GT_COMPETITION | | players [ i ] . lives < = 0 ) )
{
// In Co-Op, replenish a user's lives if they are depleted.
players [ i ] . lives = cv_startinglives . value ;
}
Mammoth commit!
* Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla..
* Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves.
* Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three.
* Major tweaks to the First Person HUD.
* I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes.
* Dontdraw-ness, transparency, and colorization match the player's object!
* Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer!
* Flashes the colour of your drift sparks.
* Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step.
* Countdowns now play the drowning music again.
* Removed/disabled extraenous P_RestoreMusics.
* Made map-ending music called by its own function, P_EndingMusic(player_t *player).
* Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first.
* Make dead players spinout and clip through the floor, at least until we add the new death anims.
* Fix prior pogo spring usage making dead players fall faster.
* Make the time over countdown use the kart font when not splitscreen with 3 or 4 players.
* Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 19:19:30 +00:00
# else
2018-10-17 12:40:17 +00:00
players [ i ] . lives = 1 ; // SRB2Kart
Mammoth commit!
* Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla..
* Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves.
* Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three.
* Major tweaks to the First Person HUD.
* I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes.
* Dontdraw-ness, transparency, and colorization match the player's object!
* Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer!
* Flashes the colour of your drift sparks.
* Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step.
* Countdowns now play the drowning music again.
* Removed/disabled extraenous P_RestoreMusics.
* Made map-ending music called by its own function, P_EndingMusic(player_t *player).
* Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first.
* Make dead players spinout and clip through the floor, at least until we add the new death anims.
* Fix prior pogo spring usage making dead players fall faster.
* Make the time over countdown use the kart font when not splitscreen with 3 or 4 players.
* Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 19:19:30 +00:00
# endif
2014-03-15 16:59:03 +00:00
players [ i ] . realtime = countdown = countdown2 = 0 ;
2018-01-22 02:03:44 +00:00
curlap = bestlap = 0 ; // SRB2Kart
2014-03-15 16:59:03 +00:00
players [ i ] . gotcontinue = false ;
players [ i ] . xtralife = players [ i ] . deadtimer = players [ i ] . numboxes = players [ i ] . totalring = players [ i ] . laps = 0 ;
players [ i ] . health = 1 ;
players [ i ] . aiming = 0 ;
players [ i ] . pflags & = ~ PF_TIMEOVER ;
players [ i ] . losstime = 0 ;
players [ i ] . timeshit = 0 ;
players [ i ] . marescore = players [ i ] . lastmarescore = players [ i ] . maxlink = 0 ;
players [ i ] . startedtime = players [ i ] . finishedtime = players [ i ] . finishedrings = 0 ;
players [ i ] . lastmare = players [ i ] . marebegunat = 0 ;
// Don't show anything
players [ i ] . textvar = players [ i ] . texttimer = 0 ;
players [ i ] . linkcount = players [ i ] . linktimer = 0 ;
players [ i ] . flyangle = players [ i ] . anotherflyangle = 0 ;
players [ i ] . nightstime = players [ i ] . mare = 0 ;
P_SetTarget ( & players [ i ] . capsule , NULL ) ;
players [ i ] . drillmeter = 40 * 20 ;
players [ i ] . exiting = 0 ;
P_ResetPlayer ( & players [ i ] ) ;
players [ i ] . mo = NULL ;
// we must unset axis details too
players [ i ] . axis1 = players [ i ] . axis2 = NULL ;
// and this stupid flag as a result
players [ i ] . pflags & = ~ PF_TRANSFERTOCLOSEST ;
}
2018-08-08 14:03:02 +00:00
// SRB2Kart: map load variables
if ( modeattacking ) // Just play it safe and set everything
{
gamespeed = 2 ;
franticitems = false ;
comeback = true ;
}
else
{
if ( G_BattleGametype ( ) )
gamespeed = 0 ;
else
gamespeed = ( UINT8 ) cv_kartspeed . value ;
franticitems = ( boolean ) cv_kartfrantic . value ;
comeback = ( boolean ) cv_kartcomeback . value ;
}
for ( i = 0 ; i < 4 ; i + + )
battlewanted [ i ] = - 1 ;
2014-03-15 16:59:03 +00:00
}
//
// P_LoadThingsOnly
//
// "Reloads" a level, but only reloads all of the mobjs.
//
void P_LoadThingsOnly ( void )
{
// Search through all the thinkers.
mobj_t * mo ;
thinker_t * think ;
for ( think = thinkercap . next ; think ! = & thinkercap ; think = think - > next )
{
if ( think - > function . acp1 ! = ( actionf_p1 ) P_MobjThinker )
continue ; // not a mobj thinker
mo = ( mobj_t * ) think ;
if ( mo )
P_RemoveMobj ( mo ) ;
}
P_LevelInitStuff ( ) ;
2018-11-23 15:58:16 +00:00
if ( W_IsLumpWad ( lastloadedmaplumpnum ) ) // welp it's a map wad in a pk3
{ // HACK: Open wad file rather quickly so we can use the things lump
UINT8 * wadData = W_CacheLumpNum ( lastloadedmaplumpnum , PU_STATIC ) ;
filelump_t * fileinfo = ( filelump_t * ) ( wadData + ( ( wadinfo_t * ) wadData ) - > infotableofs ) ;
fileinfo + = ML_THINGS ; // we only need the THINGS lump
P_PrepareRawThings ( wadData + fileinfo - > filepos , fileinfo - > size ) ;
Z_Free ( wadData ) ; // we're done with this now
}
else // phew it's just a WAD
P_PrepareThings ( lastloadedmaplumpnum + ML_THINGS ) ;
2016-07-06 04:09:17 +00:00
P_LoadThings ( ) ;
2014-03-15 16:59:03 +00:00
P_SpawnSecretItems ( true ) ;
}
/** 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
*/
static INT32 P_MakeBufferMD5 ( const char * buffer , size_t len , void * resblock )
{
# 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
}
static void P_MakeMapMD5 ( lumpnum_t maplumpnum , void * dest )
{
unsigned char linemd5 [ 16 ] ;
unsigned char sectormd5 [ 16 ] ;
unsigned char thingmd5 [ 16 ] ;
unsigned char sidedefmd5 [ 16 ] ;
unsigned char resmd5 [ 16 ] ;
UINT8 i ;
// Create a hash for the current map
// get the actual lumps!
UINT8 * datalines = W_CacheLumpNum ( maplumpnum + ML_LINEDEFS , PU_CACHE ) ;
UINT8 * datasectors = W_CacheLumpNum ( maplumpnum + ML_SECTORS , PU_CACHE ) ;
UINT8 * datathings = W_CacheLumpNum ( maplumpnum + ML_THINGS , PU_CACHE ) ;
UINT8 * datasides = W_CacheLumpNum ( maplumpnum + ML_SIDEDEFS , PU_CACHE ) ;
P_MakeBufferMD5 ( ( char * ) datalines , W_LumpLength ( maplumpnum + ML_LINEDEFS ) , linemd5 ) ;
P_MakeBufferMD5 ( ( char * ) datasectors , W_LumpLength ( maplumpnum + ML_SECTORS ) , sectormd5 ) ;
P_MakeBufferMD5 ( ( char * ) datathings , W_LumpLength ( maplumpnum + ML_THINGS ) , thingmd5 ) ;
P_MakeBufferMD5 ( ( char * ) datasides , W_LumpLength ( maplumpnum + ML_SIDEDEFS ) , sidedefmd5 ) ;
Z_Free ( datalines ) ;
Z_Free ( datasectors ) ;
Z_Free ( datathings ) ;
Z_Free ( datasides ) ;
for ( i = 0 ; i < 16 ; i + + )
resmd5 [ i ] = ( linemd5 [ i ] + sectormd5 [ i ] + thingmd5 [ i ] + sidedefmd5 [ i ] ) & 0xFF ;
M_Memcpy ( dest , & resmd5 , 16 ) ;
}
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 ] ;
2017-12-19 01:59:04 +00:00
if ( splitscreen )
2014-08-27 03:56:30 +00:00
{
sprintf ( skincmd , " skin2 %s \n " , forcecharskin ) ;
CV_Set ( & cv_skin2 , forcecharskin ) ;
2017-12-19 01:59:04 +00:00
if ( splitscreen > 1 )
2017-12-17 19:17:07 +00:00
{
sprintf ( skincmd , " skin3 %s \n " , forcecharskin ) ;
CV_Set ( & cv_skin3 , forcecharskin ) ;
2017-12-19 01:59:04 +00:00
if ( splitscreen > 2 )
2017-12-17 19:17:07 +00:00
{
sprintf ( skincmd , " skin4 %s \n " , forcecharskin ) ;
CV_Set ( & cv_skin4 , forcecharskin ) ;
}
}
2014-08-27 03:56:30 +00:00
}
sprintf ( skincmd , " skin %s \n " , forcecharskin ) ;
COM_BufAddText ( skincmd ) ;
}
else
{
2017-12-19 01:59:04 +00:00
if ( splitscreen )
2014-08-27 03:56:30 +00:00
{
SetPlayerSkin ( secondarydisplayplayer , forcecharskin ) ;
2017-10-21 02:01:07 +00:00
if ( ( unsigned ) cv_playercolor2 . value ! = skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor & & ! modeattacking )
2014-08-27 03:56:30 +00:00
{
CV_StealthSetValue ( & cv_playercolor2 , skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor ) ;
players [ secondarydisplayplayer ] . skincolor = skins [ players [ secondarydisplayplayer ] . skin ] . prefcolor ;
}
2017-12-19 01:59:04 +00:00
if ( splitscreen > 1 )
2017-12-17 06:21:24 +00:00
{
2017-12-19 01:59:04 +00:00
SetPlayerSkin ( thirddisplayplayer , forcecharskin ) ;
if ( ( unsigned ) cv_playercolor3 . value ! = skins [ players [ thirddisplayplayer ] . skin ] . prefcolor & & ! modeattacking )
{
CV_StealthSetValue ( & cv_playercolor3 , skins [ players [ thirddisplayplayer ] . skin ] . prefcolor ) ;
players [ thirddisplayplayer ] . skincolor = skins [ players [ thirddisplayplayer ] . skin ] . prefcolor ;
}
2017-12-17 06:21:24 +00:00
2017-12-19 01:59:04 +00:00
if ( splitscreen > 2 )
{
SetPlayerSkin ( fourthdisplayplayer , forcecharskin ) ;
if ( ( unsigned ) cv_playercolor4 . value ! = skins [ players [ fourthdisplayplayer ] . skin ] . prefcolor & & ! modeattacking )
{
CV_StealthSetValue ( & cv_playercolor4 , skins [ players [ fourthdisplayplayer ] . skin ] . prefcolor ) ;
players [ fourthdisplayplayer ] . skincolor = skins [ players [ fourthdisplayplayer ] . skin ] . prefcolor ;
}
}
2017-12-17 06:21:24 +00:00
}
}
2014-08-27 03:56:30 +00:00
SetPlayerSkin ( consoleplayer , forcecharskin ) ;
// normal player colors in single player
2017-10-21 02:01:07 +00:00
if ( ( unsigned ) cv_playercolor . value ! = skins [ players [ consoleplayer ] . skin ] . prefcolor & & ! modeattacking )
2014-08-27 03:56:30 +00:00
{
CV_StealthSetValue ( & cv_playercolor , skins [ players [ consoleplayer ] . skin ] . prefcolor ) ;
players [ consoleplayer ] . skincolor = skins [ players [ consoleplayer ] . skin ] . prefcolor ;
}
}
}
static void P_LoadRecordGhosts ( void )
{
2018-07-06 17:08:35 +00:00
// see also m_menu.c's Nextmap_OnChange
2014-08-27 03:56:30 +00:00
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 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 ) ) ;
}
}
2018-01-22 00:15:26 +00:00
// Best Lap ghost
if ( cv_ghost_bestlap . value )
2014-08-27 03:56:30 +00:00
{
for ( i = 0 ; i < numskins ; + + i )
{
2017-10-21 02:01:07 +00:00
if ( cv_ghost_bestlap . value = = 1 & & players [ consoleplayer ] . skin ! = i )
2014-08-27 03:56:30 +00:00
continue ;
2017-10-21 02:01:07 +00:00
if ( FIL_FileExists ( va ( " %s-%s-lap-best.lmp " , gpath , skins [ i ] . name ) ) )
G_AddGhost ( va ( " %s-%s-lap-best.lmp " , gpath , skins [ i ] . name ) ) ;
2014-08-27 03:56:30 +00:00
}
2018-01-22 00:15:26 +00:00
}
2014-08-27 03:56:30 +00:00
// 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 ) ) ;
2018-03-12 03:18:45 +00:00
2017-10-18 03:14:51 +00:00
// Staff Attack ghosts
if ( cv_ghost_staff . value )
{
lumpnum_t l ;
2017-11-21 07:23:06 +00:00
UINT8 j = 1 ;
while ( j < = 99 & & ( l = W_CheckNumForName ( va ( " %sS%02u " , G_BuildMapName ( gamemap ) , j ) ) ) ! = LUMPERROR )
2017-10-18 03:14:51 +00:00
{
2017-11-21 07:23:06 +00:00
G_AddGhost ( va ( " %sS%02u " , G_BuildMapName ( gamemap ) , j ) ) ;
j + + ;
2017-10-18 03:14:51 +00:00
}
}
2014-08-27 03:56:30 +00:00
free ( gpath ) ;
}
2018-01-22 00:15:26 +00:00
/*static void P_LoadNightsGhosts(void)
2014-11-12 00:55:07 +00:00
{
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 ) ) ;
2017-10-18 03:14:51 +00:00
// Staff Attack ghosts
if ( cv_ghost_staff . value )
{
lumpnum_t l ;
UINT8 i = 1 ;
2017-10-21 02:01:07 +00:00
while ( i < = 99 & & ( l = W_CheckNumForName ( va ( " %sS%02u " , G_BuildMapName ( gamemap ) , i ) ) ) ! = LUMPERROR )
2017-10-18 03:14:51 +00:00
{
2017-10-21 02:01:07 +00:00
G_AddGhost ( va ( " %sS%02u " , G_BuildMapName ( gamemap ) , i ) ) ;
2017-10-18 03:14:51 +00:00
i + + ;
}
}
2014-11-12 00:55:07 +00:00
free ( gpath ) ;
2018-01-22 00:15:26 +00:00
} */
2014-11-12 00:55:07 +00:00
2018-11-18 18:26:50 +00:00
static void P_SetupCamera ( UINT8 pnum , camera_t * cam )
2018-11-12 20:07:45 +00:00
{
2018-11-18 18:26:50 +00:00
if ( players [ pnum ] . mo & & ( server | | addedtogame ) )
2018-11-12 20:07:45 +00:00
{
2018-11-18 18:26:50 +00:00
cam - > x = players [ pnum ] . mo - > x ;
cam - > y = players [ pnum ] . mo - > y ;
cam - > z = players [ pnum ] . mo - > z ;
cam - > angle = players [ pnum ] . mo - > angle ;
cam - > subsector = R_PointInSubsector ( cam - > x , cam - > y ) ; // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
2018-11-12 20:07:45 +00:00
}
else
{
mapthing_t * thing ;
switch ( gametype )
{
case GT_MATCH :
case GT_TAG :
thing = deathmatchstarts [ 0 ] ;
break ;
default :
thing = playerstarts [ 0 ] ;
break ;
}
2018-11-18 18:26:50 +00:00
if ( ! thing )
return ; // we can't do jack shit
cam - > x = thing - > x ;
cam - > y = thing - > y ;
cam - > z = thing - > z ;
cam - > angle = FixedAngle ( ( fixed_t ) thing - > angle < < FRACBITS ) ;
cam - > subsector = R_PointInSubsector ( cam - > x , cam - > y ) ; // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
2018-11-12 20:07:45 +00:00
}
}
2018-12-17 01:09:59 +00:00
static boolean P_CanSave ( void )
{
2019-01-05 20:59:23 +00:00
#if 0
2018-12-17 01:09:59 +00:00
// Saving is completely ignored under these conditions:
if ( ( cursaveslot < 0 ) // Playing without saving
2018-12-26 20:47:12 +00:00
| | ( modifiedgame & & ! savemoddata ) // Game is modified
2018-12-17 01:09:59 +00:00
| | ( netgame | | multiplayer ) // Not in single-player
| | ( demoplayback | | demorecording | | metalrecording ) // Currently in demo
| | ( players [ consoleplayer ] . lives < = 0 ) // Completely dead
| | ( modeattacking | | ultimatemode | | G_IsSpecialStage ( gamemap ) ) ) // Specialized instances
return false ;
if ( mapheaderinfo [ gamemap - 1 ] - > saveoverride = = SAVE_ALWAYS )
return true ; // Saving should ALWAYS happen!
else if ( mapheaderinfo [ gamemap - 1 ] - > saveoverride = = SAVE_NEVER )
return false ; // Saving should NEVER happen!
2018-12-20 21:52:51 +00:00
2018-12-17 01:09:59 +00:00
// Default condition: In a non-hidden map, at the beginning of a zone or on a completed save-file, and not on save reload.
return ( ! ( mapheaderinfo [ gamemap - 1 ] - > menuflags & LF2_HIDEINMENU )
& & ( mapheaderinfo [ gamemap - 1 ] - > actnum < 2 | | gamecomplete )
& & ( gamemap ! = lastmapsaved ) ) ;
2019-01-05 20:59:23 +00:00
# else
return false ; // SRB2Kart: no SP, no saving.
# endif
2018-12-17 01:09:59 +00:00
}
2014-03-15 16:59:03 +00:00
/** Loads a level from a lump or external wad.
*
* \ param skipprecip If true , don ' t spawn precipitation .
* \ todo Clean up , refactor , split up ; get rid of the bloat .
*/
boolean P_SetupLevel ( boolean skipprecip )
{
// use gamemap to get map number.
// 99% of the things already did, so.
// Map header should always be in place at this point
2016-07-06 04:09:17 +00:00
INT32 i , loadprecip = 1 , ranspecialwipe = 0 ;
2014-03-15 16:59:03 +00:00
INT32 loademblems = 1 ;
INT32 fromnetsave = 0 ;
boolean loadedbm = false ;
sector_t * ss ;
boolean chase ;
levelloading = true ;
// This is needed. Don't touch.
maptol = mapheaderinfo [ gamemap - 1 ] - > typeoflevel ;
CON_Drawer ( ) ; // let the user know what we are going to do
I_FinishUpdate ( ) ; // page flip or blit buffer
// 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
P_LevelInitStuff ( ) ;
2017-12-17 21:33:21 +00:00
postimgtype = postimgtype2 = postimgtype3 = postimgtype4 = postimg_none ;
2014-03-15 16:59:03 +00:00
if ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter [ 0 ] ! = ' \0 '
& & atoi ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter ) ! = 255 )
2014-08-27 03:56:30 +00:00
P_ForceCharacter ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter ) ;
2014-03-15 16:59:03 +00:00
// chasecam on in chaos, race, coop
// chasecam off in match, tag, capture the flag
2017-10-22 07:06:35 +00:00
chase = true ; // srb2kart: always on
2014-03-15 16:59:03 +00:00
if ( ! dedicated )
{
2017-10-17 03:52:13 +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
if ( ! cv_chasecam . changed )
CV_SetValue ( & cv_chasecam , chase ) ;
// same for second player
if ( ! cv_chasecam2 . changed )
CV_SetValue ( & cv_chasecam2 , chase ) ;
2017-12-18 10:56:38 +00:00
if ( ! cv_chasecam3 . changed )
CV_SetValue ( & cv_chasecam3 , chase ) ;
if ( ! cv_chasecam4 . changed )
CV_SetValue ( & cv_chasecam4 , chase ) ;
2014-03-15 16:59:03 +00:00
}
// Initial height of PointOfView
// will be set by player think.
players [ consoleplayer ] . viewz = 1 ;
2018-08-08 14:03:02 +00:00
// Encore mode fade to pink to white
2016-07-06 04:09:17 +00:00
// This is handled BEFORE sounds are stopped.
2018-08-08 19:48:29 +00:00
if ( rendermode ! = render_none & & encoremode & & ! prevencoremode )
2016-07-06 04:09:17 +00:00
{
2018-11-14 21:53:57 +00:00
tic_t locstarttime , endtime , nowtime ;
2018-08-08 14:03:02 +00:00
S_StopMusic ( ) ; // er, about that...
2016-07-06 04:09:17 +00:00
2018-08-08 14:03:02 +00:00
S_StartSound ( NULL , sfx_ruby1 ) ;
2016-07-06 04:09:17 +00:00
F_WipeStartScreen ( ) ;
2018-08-08 14:03:02 +00:00
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , 122 ) ;
2016-07-06 04:09:17 +00:00
F_WipeEndScreen ( ) ;
F_RunWipe ( wipedefs [ wipe_speclevel_towhite ] , false ) ;
2018-08-08 14:03:02 +00:00
F_WipeStartScreen ( ) ;
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , 120 ) ;
F_WipeEndScreen ( ) ;
F_RunWipe ( wipedefs [ wipe_level_final ] , false ) ;
2018-11-14 21:53:57 +00:00
locstarttime = nowtime = lastwipetic ;
endtime = locstarttime + ( 3 * TICRATE ) / 2 ;
2018-08-08 14:03:02 +00:00
2016-07-06 04:09:17 +00:00
// Hold on white for extra effect.
2018-03-05 19:08:53 +00:00
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 ( ) ;
2019-02-04 21:54:10 +00:00
// Keep the network alive
NetKeepAlive ( ) ;
2018-03-05 19:08:53 +00:00
}
2016-07-06 04:09:17 +00:00
ranspecialwipe = 1 ;
2018-08-08 14:03:02 +00:00
}
2016-07-06 04:09:17 +00:00
2014-03-15 16:59:03 +00:00
// Make sure all sounds are stopped before Z_FreeTags.
S_StopSounds ( ) ;
S_ClearSfx ( ) ;
2017-03-11 00:09:10 +00:00
// As oddly named as this is, this handles music only.
// We should be fine starting it here.
S_Start ( ) ;
2014-03-15 16:59:03 +00:00
2018-09-22 11:43:54 +00:00
levelfadecol = ( encoremode & & ! ranspecialwipe ? 122 : 120 ) ;
2014-03-15 16:59:03 +00:00
2018-08-08 14:03:02 +00:00
// Let's fade to white here
2018-08-08 19:48:29 +00:00
// But only if we didn't do the encore startup wipe
2016-07-06 04:09:17 +00:00
if ( rendermode ! = render_none & & ! ranspecialwipe )
2014-03-15 16:59:03 +00:00
{
F_WipeStartScreen ( ) ;
2018-09-22 11:43:54 +00:00
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , levelfadecol ) ;
2014-03-31 22:47:12 +00:00
2014-03-15 16:59:03 +00:00
F_WipeEndScreen ( ) ;
2018-08-08 19:48:29 +00:00
F_RunWipe ( wipedefs [ ( encoremode ? wipe_level_final : wipe_level_toblack ) ] , false ) ;
2016-07-06 04:09:17 +00:00
}
2014-03-15 16:59:03 +00:00
2018-07-18 00:21:36 +00:00
// Reset the palette now all fades have been done
if ( rendermode ! = render_none )
V_SetPaletteLump ( GetPalette ( ) ) ; // Set the level palette
2016-07-06 04:09:17 +00:00
// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
2018-06-07 00:25:28 +00:00
/*if (rendermode != render_none)
2016-07-06 04:09:17 +00:00
{
2014-03-15 16:59:03 +00:00
// Don't include these in the fade!
2016-07-06 04:09:17 +00:00
char tx [ 64 ] ;
V_DrawSmallString ( 1 , 191 , V_ALLOWLOWERCASE , M_GetText ( " Speeding off to... " ) ) ;
snprintf ( tx , 63 , " %s%s%s " ,
mapheaderinfo [ gamemap - 1 ] - > lvlttl ,
2018-06-05 05:34:05 +00:00
( strlen ( mapheaderinfo [ gamemap - 1 ] - > zonttl ) > 0 ) ? va ( " %s " , mapheaderinfo [ gamemap - 1 ] - > zonttl ) : // SRB2kart
2017-09-01 17:00:53 +00:00
( ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_NOZONE ) ? " " : " ZONE " ) ,
2018-06-05 21:28:46 +00:00
( strlen ( mapheaderinfo [ gamemap - 1 ] - > actnum ) > 0 ) ? va ( " , Act %s " , mapheaderinfo [ gamemap - 1 ] - > actnum ) : " " ) ;
2016-07-06 04:09:17 +00:00
V_DrawSmallString ( 1 , 195 , V_ALLOWLOWERCASE , tx ) ;
I_UpdateNoVsync ( ) ;
2018-06-07 00:25:28 +00:00
} */
2014-03-15 16:59:03 +00:00
# ifdef HAVE_BLUA
LUA_InvalidateLevel ( ) ;
# endif
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 ( ) ;
/// \note for not spawning precipitation, etc. when loading netgame snapshots
if ( skipprecip )
{
fromnetsave = 1 ;
loadprecip = 0 ;
loademblems = 0 ;
}
// internal game map
2018-11-23 15:58:16 +00:00
maplumpname = G_BuildMapName ( gamemap ) ;
//lastloadedmaplumpnum = LUMPERROR;
lastloadedmaplumpnum = W_CheckNumForName ( maplumpname ) ;
if ( lastloadedmaplumpnum = = INT16_MAX )
I_Error ( " Map %s not found. \n " , maplumpname ) ;
2014-03-15 16:59:03 +00:00
2018-07-23 22:50:41 +00:00
R_ReInitColormaps ( mapheaderinfo [ gamemap - 1 ] - > palette ,
( encoremode ? W_CheckNumForName ( va ( " %sE " , maplumpname ) ) : LUMPERROR ) ) ;
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 ) ;
P_MakeMapMD5 ( lastloadedmaplumpnum , & mapmd5 ) ;
2018-11-23 15:58:16 +00:00
// HACK ALERT: Cache the WAD, get the map data into the tables, free memory.
// As it is implemented right now, we're assuming an uncompressed WAD.
// (As in, a normal PWAD, not ZWAD or anything. The lump itself can be compressed.)
// We're not accounting for extra lumps and scrambled lump positions. Any additional data will cause an error.
if ( W_IsLumpWad ( lastloadedmaplumpnum ) )
{
// Remember that we're assuming that the WAD will have a specific set of lumps in a specific order.
UINT8 * wadData = W_CacheLumpNum ( lastloadedmaplumpnum , PU_STATIC ) ;
//filelump_t *fileinfo = wadData + ((wadinfo_t *)wadData)->infotableofs;
filelump_t * fileinfo = ( filelump_t * ) ( wadData + ( ( wadinfo_t * ) wadData ) - > infotableofs ) ;
UINT32 numlumps = ( ( wadinfo_t * ) wadData ) - > numlumps ;
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
if ( numlumps < ML_REJECT ) // at least 9 lumps should be in the wad for a map to be loaded
{
I_Error ( " Bad WAD file for map %s! \n " , maplumpname ) ;
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
if ( numlumps > ML_BLOCKMAP ) // enough room for a BLOCKMAP lump at least
{
loadedbm = P_LoadRawBlockMap (
wadData + ( fileinfo + ML_BLOCKMAP ) - > filepos ,
( fileinfo + ML_BLOCKMAP ) - > size ,
( fileinfo + ML_BLOCKMAP ) - > name ) ;
}
P_LoadRawVertexes ( wadData + ( fileinfo + ML_VERTEXES ) - > filepos , ( fileinfo + ML_VERTEXES ) - > size ) ;
P_LoadRawSectors ( wadData + ( fileinfo + ML_SECTORS ) - > filepos , ( fileinfo + ML_SECTORS ) - > size ) ;
P_LoadRawSideDefs ( ( fileinfo + ML_SIDEDEFS ) - > size ) ;
P_LoadRawLineDefs ( wadData + ( fileinfo + ML_LINEDEFS ) - > filepos , ( fileinfo + ML_LINEDEFS ) - > size ) ;
P_LoadRawSideDefs2 ( wadData + ( fileinfo + ML_SIDEDEFS ) - > filepos ) ;
P_LoadRawSubsectors ( wadData + ( fileinfo + ML_SSECTORS ) - > filepos , ( fileinfo + ML_SSECTORS ) - > size ) ;
P_LoadRawNodes ( wadData + ( fileinfo + ML_NODES ) - > filepos , ( fileinfo + ML_NODES ) - > size ) ;
P_LoadRawSegs ( wadData + ( fileinfo + ML_SEGS ) - > filepos , ( fileinfo + ML_SEGS ) - > size ) ;
if ( numlumps > ML_REJECT ) // enough room for a REJECT lump at least
{
P_LoadRawReject (
wadData + ( fileinfo + ML_REJECT ) - > filepos ,
( fileinfo + ML_REJECT ) - > size ,
( fileinfo + ML_REJECT ) - > name ) ;
}
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
// Important: take care of the ordering of the next functions.
if ( ! loadedbm )
P_CreateBlockMap ( ) ; // Graue 02-29-2004
P_LoadLineDefs2 ( ) ;
P_GroupLines ( ) ;
numdmstarts = numredctfstarts = numbluectfstarts = 0 ;
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
// reset the player starts
for ( i = 0 ; i < MAXPLAYERS ; i + + )
playerstarts [ i ] = NULL ;
for ( i = 0 ; i < 2 ; i + + )
skyboxmo [ i ] = NULL ;
P_MapStart ( ) ;
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
P_PrepareRawThings ( wadData + ( fileinfo + ML_THINGS ) - > filepos , ( fileinfo + ML_THINGS ) - > size ) ;
Z_Free ( wadData ) ;
}
else
{
// Important: take care of the ordering of the next functions.
loadedbm = P_LoadBlockMap ( lastloadedmaplumpnum + ML_BLOCKMAP ) ;
P_LoadVertexes ( lastloadedmaplumpnum + ML_VERTEXES ) ;
P_LoadSectors ( lastloadedmaplumpnum + ML_SECTORS ) ;
P_LoadSideDefs ( lastloadedmaplumpnum + ML_SIDEDEFS ) ;
P_LoadLineDefs ( lastloadedmaplumpnum + ML_LINEDEFS ) ;
P_LoadSideDefs2 ( lastloadedmaplumpnum + ML_SIDEDEFS ) ;
P_LoadSubsectors ( lastloadedmaplumpnum + ML_SSECTORS ) ;
P_LoadNodes ( lastloadedmaplumpnum + ML_NODES ) ;
P_LoadSegs ( lastloadedmaplumpnum + ML_SEGS ) ;
P_LoadReject ( lastloadedmaplumpnum + ML_REJECT ) ;
// Important: take care of the ordering of the next functions.
if ( ! loadedbm )
P_CreateBlockMap ( ) ; // Graue 02-29-2004
P_LoadLineDefs2 ( ) ;
P_GroupLines ( ) ;
numdmstarts = numredctfstarts = numbluectfstarts = 0 ;
// reset the player starts
for ( i = 0 ; i < MAXPLAYERS ; i + + )
playerstarts [ i ] = NULL ;
for ( i = 0 ; i < 2 ; i + + )
skyboxmo [ i ] = NULL ;
P_MapStart ( ) ;
P_PrepareThings ( lastloadedmaplumpnum + ML_THINGS ) ;
}
2016-07-06 04:09:17 +00:00
# ifdef ESLOPE
P_ResetDynamicSlopes ( ) ;
# endif
P_LoadThings ( ) ;
2014-03-15 16:59:03 +00:00
P_SpawnSecretItems ( loademblems ) ;
for ( numcoopstarts = 0 ; numcoopstarts < MAXPLAYERS ; numcoopstarts + + )
if ( ! playerstarts [ numcoopstarts ] )
break ;
// set up world state
P_SpawnSpecials ( fromnetsave ) ;
if ( loadprecip ) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
P_SpawnPrecipitation ( ) ;
globalweather = mapheaderinfo [ gamemap - 1 ] - > weather ;
# ifdef HWRENDER // not win32 only 19990829 by Kin
if ( rendermode ! = render_soft & & rendermode ! = render_none )
{
# ifdef ALAM_LIGHTING
// BP: reset light between levels (we draw preview frame lights on current frame)
HWR_ResetLights ( ) ;
# endif
// Correct missing sidedefs & deep water trick
HWR_CorrectSWTricks ( ) ;
HWR_CreatePlanePolygons ( ( INT32 ) numnodes - 1 ) ;
}
# 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.)
if ( fromnetsave )
goto netgameskip ;
// ==========
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] )
{
players [ i ] . pflags & = ~ PF_NIGHTSMODE ;
// Start players with pity shields if possible
players [ i ] . pity = - 1 ;
2018-02-08 22:13:06 +00:00
if ( ! G_RaceGametype ( ) )
2014-03-15 16:59:03 +00:00
{
players [ i ] . mo = NULL ;
G_DoReborn ( i ) ;
}
else // gametype is GT_COOP or GT_RACE
{
players [ i ] . mo = NULL ;
2016-07-06 04:09:17 +00:00
if ( players [ i ] . starposttime )
2014-03-15 16:59:03 +00:00
{
G_SpawnPlayer ( i , true ) ;
}
else
G_SpawnPlayer ( i , false ) ;
}
}
if ( modeattacking = = ATTACKING_RECORD & & ! demoplayback )
2014-08-27 03:56:30 +00:00
P_LoadRecordGhosts ( ) ;
2018-01-22 00:15:26 +00:00
/*else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
P_LoadNightsGhosts ( ) ; */
2014-03-15 16:59:03 +00:00
if ( G_TagGametype ( ) )
{
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 + + )
{
if ( playeringame [ i ] & & ! players [ i ] . spectator )
{
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.
{
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 dissasociate the corpse
if ( players [ playersactive [ i ] ] . mo )
P_RemoveMobj ( players [ playersactive [ i ] ] . mo ) ;
G_SpawnPlayer ( playersactive [ i ] , false ) ; //respawn the lucky player in his dedicated spawn location.
}
else
CONS_Printf ( M_GetText ( " No player currently available to become IT. Awaiting available players. \n " ) ) ;
}
2018-06-29 16:49:02 +00:00
else if ( G_RaceGametype ( ) & & server )
CV_StealthSetValue ( & cv_numlaps ,
2018-10-01 11:12:48 +00:00
( ( netgame | | multiplayer ) & & cv_basenumlaps . value
& & ( ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_SECTIONRACE )
| | ( mapheaderinfo [ gamemap - 1 ] - > numlaps > cv_basenumlaps . value ) ) )
2018-06-29 16:49:02 +00:00
? cv_basenumlaps . value
: mapheaderinfo [ gamemap - 1 ] - > numlaps ) ;
2014-03-15 16:59:03 +00:00
2019-01-27 21:45:08 +00:00
//@TODO NET REPLAYS NEED BETTER FILE NAMING STUFF. ALSO OPTIONS. FUCK.
2019-03-20 04:45:45 +00:00
//@TODO I'd like to fix dedis crashing when recording replays for the future too...
2019-03-05 06:26:05 +00:00
if ( ! demoplayback & & multiplayer & & ! dedicated ) {
2019-02-01 07:01:14 +00:00
static char buf [ 256 ] ;
2019-02-07 03:03:05 +00:00
sprintf ( buf , " replay " PATHSEP " online " PATHSEP " %d-%s " , ( int ) ( time ( NULL ) ) , G_BuildMapName ( gamemap ) ) ;
2019-02-03 20:29:54 +00:00
I_mkdir ( va ( " %s " PATHSEP " replay " , srb2home ) , 0755 ) ;
I_mkdir ( va ( " %s " PATHSEP " replay " PATHSEP " online " , srb2home ) , 0755 ) ;
2019-02-01 07:01:14 +00:00
G_RecordDemo ( buf ) ;
2019-01-27 21:45:08 +00:00
}
2014-08-04 03:49:33 +00:00
// ===========
// landing point for netgames.
netgameskip :
2014-03-15 16:59:03 +00:00
if ( ! dedicated )
{
2018-11-18 18:26:50 +00:00
P_SetupCamera ( displayplayer , & camera ) ;
if ( splitscreen )
2014-03-15 16:59:03 +00:00
{
2018-11-18 18:26:50 +00:00
P_SetupCamera ( secondarydisplayplayer , & camera2 ) ;
if ( splitscreen > 1 )
2014-03-15 16:59:03 +00:00
{
2018-11-18 18:26:50 +00:00
P_SetupCamera ( thirddisplayplayer , & camera3 ) ;
if ( splitscreen > 2 )
{
P_SetupCamera ( fourthdisplayplayer , & camera4 ) ;
}
2014-03-15 16:59:03 +00:00
}
}
2017-10-17 03:52:13 +00:00
// Salt: CV_ClearChangedFlags() messes with your settings :(
/*if (!cv_cam_height.changed)
2014-03-15 16:59:03 +00:00
CV_Set ( & cv_cam_height , cv_cam_height . defaultvalue ) ;
if ( ! cv_cam_dist . changed )
CV_Set ( & cv_cam_dist , cv_cam_dist . defaultvalue ) ;
if ( ! cv_cam2_height . changed )
CV_Set ( & cv_cam2_height , cv_cam2_height . defaultvalue ) ;
2017-10-17 03:52:13 +00:00
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
2014-03-15 16:59:03 +00:00
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 ) ;
2017-12-17 19:17:07 +00:00
if ( ! cv_cam3_rotate . changed )
CV_Set ( & cv_cam3_rotate , cv_cam3_rotate . defaultvalue ) ;
if ( ! cv_cam4_rotate . changed )
CV_Set ( & cv_cam4_rotate , cv_cam4_rotate . defaultvalue ) ;
2018-03-12 03:18:45 +00:00
/*if (!cv_analog.changed)
2014-03-15 16:59:03 +00:00
CV_SetValue ( & cv_analog , 0 ) ;
if ( ! cv_analog2 . changed )
CV_SetValue ( & cv_analog2 , 0 ) ;
2017-12-17 19:17:07 +00:00
if ( ! cv_analog3 . changed )
CV_SetValue ( & cv_analog3 , 0 ) ;
if ( ! cv_analog4 . changed )
2018-03-12 03:18:45 +00:00
CV_SetValue ( & cv_analog4 , 0 ) ; */
2014-03-15 16:59:03 +00:00
2019-02-23 16:18:20 +00:00
// Shouldn't be necessary with render parity?
/*if (rendermode != render_none)
CV_Set ( & cv_fov , cv_fov . defaultvalue ) ; */
2014-03-15 16:59:03 +00:00
displayplayer = consoleplayer ; // Start with your OWN view, please!
}
2018-03-12 03:18:45 +00:00
/*if (cv_useranalog.value)
2014-03-15 16:59:03 +00:00
CV_SetValue ( & cv_analog , true ) ;
2017-12-19 01:59:04 +00:00
if ( ( splitscreen & & cv_useranalog2 . value ) | | botingame )
2014-03-15 16:59:03 +00:00
CV_SetValue ( & cv_analog2 , true ) ;
2017-12-19 01:59:04 +00:00
if ( splitscreen > 1 & & cv_useranalog3 . value )
2017-12-17 19:17:07 +00:00
CV_SetValue ( & cv_analog3 , true ) ;
2017-12-19 01:59:04 +00:00
if ( splitscreen > 2 & & cv_useranalog4 . value )
2017-12-17 19:17:07 +00:00
CV_SetValue ( & cv_analog4 , true ) ;
2014-03-15 16:59:03 +00:00
if ( twodlevel )
{
2017-12-17 19:17:07 +00:00
CV_SetValue ( & cv_analog4 , false ) ;
CV_SetValue ( & cv_analog3 , false ) ;
2014-03-15 16:59:03 +00:00
CV_SetValue ( & cv_analog2 , false ) ;
CV_SetValue ( & cv_analog , false ) ;
2018-03-12 03:18:45 +00:00
} */
2014-03-15 16:59:03 +00:00
2018-07-04 01:12:25 +00:00
wantedcalcdelay = wantedfrequency * 2 ;
2018-06-11 05:51:50 +00:00
indirectitemcooldown = 0 ;
2019-02-20 04:15:31 +00:00
hyubgone = 0 ;
2018-07-21 19:54:05 +00:00
mapreset = 0 ;
2018-10-15 22:41:34 +00:00
nospectategrief = 0 ;
2018-10-29 09:00:08 +00:00
thwompsactive = false ;
2018-11-25 01:41:17 +00:00
spbplace = - 1 ;
Lots of changes
- Menus now have all of the Kart cvars
- Removed any cvars that aren't useful for Kart from the menu (they
still exist in the console, though)
- Removed SP and NiGHTS Mode options from the main menu
- "kartcc" is renamed "kartspeed", uses values 0-2 instead of multiples
of 50, or the terms "Relaxed", "Standard", and "Turbo"
- Many gametype options (game speed, frantic, mirror, & karma comeback)
are now changed on map load instead of instantly
- New cvar, "kartminimap", for disabling the minimap
- The maxplayers cvar now actually matches up with our 16 player limit
- Game now keeps track of matches played. Has a condition type
associated with it, as well.
- Game checks for unlocks and saves gamedata when finishing a match,
even in MP
- Removed most of the normal emblems, added a single emblem for Green
Hills. Didn't know what to do with extra emblems and such so I just left
them (FOR NOW c:<)
2018-01-16 03:31:14 +00:00
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 ) ;
// preload graphics
# ifdef HWRENDER // not win32 only 19990829 by Kin
if ( rendermode ! = render_soft & & rendermode ! = render_none )
{
HWR_PrepLevelCache ( numtextures ) ;
}
# endif
P_MapEnd ( ) ;
// Remove the loading shit from the screen
if ( rendermode ! = render_none )
2018-09-22 11:43:54 +00:00
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , levelfadecol ) ;
2014-03-15 16:59:03 +00:00
if ( precache | | dedicated )
R_PrecacheLevel ( ) ;
nextmapoverride = 0 ;
skipstats = false ;
2019-01-26 16:58:45 +00:00
if ( ! ( netgame | | multiplayer ) & & ! majormods )
2014-03-15 16:59:03 +00:00
mapvisited [ gamemap - 1 ] | = MV_VISITED ;
levelloading = false ;
P_RunCachedActions ( ) ;
2018-12-17 01:09:59 +00:00
if ( P_CanSave ( ) )
2014-03-15 16:59:03 +00:00
G_SaveGame ( ( UINT32 ) cursaveslot ) ;
if ( savedata . lives > 0 )
{
players [ consoleplayer ] . continues = savedata . continues ;
players [ consoleplayer ] . lives = savedata . lives ;
players [ consoleplayer ] . score = savedata . score ;
botskin = savedata . botskin ;
botcolor = savedata . botcolor ;
botingame = ( savedata . botskin ! = 0 ) ;
emeralds = savedata . emeralds ;
savedata . lives = 0 ;
}
2017-12-17 19:17:07 +00:00
skyVisible = skyVisible1 = skyVisible2 = skyVisible3 = skyVisible4 = true ; // assume the skybox is visible on level load.
2014-03-15 16:59:03 +00:00
if ( loadprecip ) // uglier hack
{ // 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 ) ;
# ifdef HAVE_BLUA
LUAh_MapLoad ( ) ;
# endif
}
2018-11-22 17:10:36 +00:00
G_AddMapToBuffer ( gamemap - 1 ) ;
2014-03-15 16:59:03 +00:00
return true ;
}
//
// 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-11-23 15:58:16 +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 ;
}
//
// Add a wadfile to the active wad files,
// replace sounds, musics, patches, textures, sprites and maps
//
2018-11-23 15:58:16 +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 ;
boolean texturechange = false ;
2018-11-23 15:58:16 +00:00
boolean mapsadded = false ;
2014-03-15 16:59:03 +00:00
boolean replacedcurrentmap = false ;
2018-11-23 15:58:16 +00:00
if ( ( numlumps = W_InitFile ( wadfilename ) ) = = INT16_MAX )
2014-03-15 16:59:03 +00:00
{
2018-08-28 20:08:47 +00:00
refreshdirmenu | = REFRESHDIR_NOTLOADED ;
2018-05-17 17:57:19 +00:00
CONS_Printf ( M_GetText ( " Errors occurred while loading %s; not added. \n " ) , wadfilename ) ;
2014-03-15 16:59:03 +00:00
return false ;
}
else wadnum = ( UINT16 ) ( numwadfiles - 1 ) ;
//
// search for sound replacements
//
lumpinfo = wadfiles [ wadnum ] - > lumpinfo ;
for ( i = 0 ; i < numlumps ; i + + , lumpinfo + + )
{
name = lumpinfo - > name ;
if ( name [ 0 ] = = ' D ' )
{
if ( name [ 1 ] = = ' S ' ) for ( j = 1 ; j < NUMSFX ; j + + )
{
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 ) ;
I_FreeSfx ( & S_sfx [ j ] ) ;
sreplaces + + ;
}
}
else if ( name [ 1 ] = = ' _ ' )
{
2018-06-30 22:18:13 +00:00
CONS_Debug ( DBG_SETUP , " Music %.8s ignored \n " , name ) ;
2014-03-15 16:59:03 +00:00
mreplaces + + ;
}
}
else if ( name [ 0 ] = = ' O ' & & name [ 1 ] = = ' _ ' )
{
CONS_Debug ( DBG_SETUP , " Music %.8s replaced \n " , name ) ;
digmreplaces + + ;
}
#if 0
//
// search for texturechange replacements
//
else if ( ! memcmp ( name , " TEXTURE1 " , 8 ) | | ! memcmp ( name , " TEXTURE2 " , 8 )
| | ! memcmp ( name , " PNAMES " , 6 ) )
# endif
texturechange = true ;
}
if ( ! devparm & & sreplaces )
CONS_Printf ( M_GetText ( " %s sounds replaced \n " ) , sizeu1 ( sreplaces ) ) ;
if ( ! devparm & & mreplaces )
2018-06-30 22:18:13 +00:00
CONS_Printf ( M_GetText ( " %s midi musics ignored \n " ) , sizeu1 ( mreplaces ) ) ;
2014-03-15 16:59:03 +00:00
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
//
R_AddSpriteDefs ( wadnum ) ;
// Reload it all anyway, just in case they
// added some textures but didn't insert a
// TEXTURE1/PNAMES/etc. list.
if ( texturechange ) // initialized in the sound check
R_LoadTextures ( ) ; // numtexture changes
else
R_FlushTextureCache ( ) ; // just reload it from file
2016-10-19 07:22:11 +00:00
// Reload ANIMATED / ANIMDEFS
P_InitPicAnims ( ) ;
2014-03-15 16:59:03 +00:00
// Flush and reload HUD graphics
ST_UnloadGraphics ( ) ;
HU_LoadGraphics ( ) ;
ST_LoadGraphics ( ) ;
ST_ReloadSkinFaceGraphics ( ) ;
//
// look for skins
//
R_AddSkins ( wadnum ) ; // faB: wadfile index in wadfiles[]
2019-01-27 21:45:08 +00:00
//
2019-01-10 02:00:45 +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-11-23 15:58:16 +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 ] ) ;
2019-01-15 19:01:55 +00:00
// we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
if ( num < = NUMMAPS & & mapheaderinfo [ num - 1 ] )
{
if ( mapheaderinfo [ num - 1 ] - > menuflags & LF2_EXISTSHACK )
2019-01-17 22:01:28 +00:00
G_SetGameModified ( multiplayer , true ) ; // oops, double-defined - no record attack privileges for you
2019-01-15 19:01:55 +00:00
mapheaderinfo [ num - 1 ] - > menuflags | = LF2_EXISTSHACK ;
}
2014-03-15 16:59:03 +00:00
//If you replaced the map you're on, end the level when done.
if ( num = = gamemap )
replacedcurrentmap = true ;
CONS_Printf ( " %s \n " , name ) ;
2018-11-23 15:58:16 +00:00
mapsadded = true ;
2014-03-15 16:59:03 +00:00
}
}
2018-11-23 15:58:16 +00:00
if ( ! mapsadded )
2014-03-15 16:59:03 +00:00
CONS_Printf ( M_GetText ( " No maps added \n " ) ) ;
// reload status bar (warning should have valid player!)
if ( gamestate = = GS_LEVEL )
ST_Start ( ) ;
// Prevent savefile cheating
if ( cursaveslot > = 0 )
cursaveslot = - 1 ;
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 ) ;
}
2019-01-17 22:01:28 +00:00
refreshdirmenu & = ~ REFRESHDIR_GAMEDATA ; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
2014-03-15 16:59:03 +00:00
return true ;
}
# ifdef DELFILE
boolean P_DelWadFile ( void )
{
sfxenum_t i ;
const UINT16 wadnum = ( UINT16 ) ( numwadfiles - 1 ) ;
const lumpnum_t lumpnum = numwadfiles < < 16 ;
//lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo;
R_DelSkins ( wadnum ) ; // only used by DELFILE
R_DelSpriteDefs ( wadnum ) ; // only used by DELFILE
for ( i = 0 ; i < NUMSFX ; i + + )
{
if ( S_sfx [ i ] . lumpnum ! = LUMPERROR & & S_sfx [ i ] . lumpnum > = lumpnum )
{
S_StopSoundByNum ( i ) ;
S_RemoveSoundFx ( i ) ;
if ( S_sfx [ i ] . lumpnum ! = LUMPERROR )
{
I_FreeSfx ( & S_sfx [ i ] ) ;
S_sfx [ i ] . lumpnum = LUMPERROR ;
}
}
}
W_UnloadWadFile ( wadnum ) ; // only used by DELFILE
R_LoadTextures ( ) ;
return false ;
}
# endif