2020-09-22 19:44:33 +00:00
/*
* * maploader . cpp
* *
* * Map loader for non - Blood maps
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2020 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# include <stdint.h>
# include "build.h"
# include "files.h"
# include "automap.h"
# include "printf.h"
# include "inputstate.h"
# include "md4.h"
2021-12-04 11:31:54 +00:00
# include "coreactor.h"
2020-10-11 18:57:20 +00:00
# include "gamecontrol.h"
2021-03-19 19:40:44 +00:00
# include "gamefuncs.h"
2021-03-19 22:11:23 +00:00
# include "sectorgeometry.h"
2021-03-20 22:01:16 +00:00
# include "render.h"
2021-05-02 22:04:36 +00:00
# include "hw_sections.h"
2021-11-27 12:47:53 +00:00
# include "interpolate.h"
2021-11-20 16:01:59 +00:00
# include "games/blood/src/mapstructs.h"
2020-09-22 19:44:33 +00:00
2021-12-06 19:08:32 +00:00
extern BitArray clipsectormap ;
2021-12-17 19:24:48 +00:00
TArray < sectortype > sector ;
TArray < walltype > wall ;
// for differential savegames.
TArray < sectortype > sectorbackup ;
TArray < walltype > wallbackup ;
2021-12-17 20:42:57 +00:00
void walltype : : calcLength ( )
{
lengthflags & = ~ 1 ;
point2Wall ( ) - > lengthflags & = ~ 2 ;
2022-09-17 19:18:04 +00:00
length = delta ( ) . Length ( ) ;
2021-12-17 20:42:57 +00:00
}
2021-11-15 23:30:01 +00:00
// needed for skipping over to get the map size first.
enum
{
sectorsize5 = 37 ,
sectorsize6 = 37 ,
sectorsize7 = 40 ,
wallsize5 = 35 ,
wallsize6 = 32 ,
wallsize7 = 32 ,
} ;
2021-11-20 16:01:59 +00:00
// This arena stores the larger allocated game-specific extension data. Since this can be freed in bulk a memory arena is better suited than malloc.
static FMemArena mapDataArena ;
void walltype : : allocX ( )
{
using XWALL = BLD_NS : : XWALL ;
_xw = ( XWALL * ) mapDataArena . Alloc ( sizeof ( XWALL ) ) ;
memset ( _xw , 0 , sizeof ( XWALL ) ) ;
}
2021-11-20 16:35:41 +00:00
void sectortype : : allocX ( )
{
using XSECTOR = BLD_NS : : XSECTOR ;
_xs = ( XSECTOR * ) mapDataArena . Alloc ( sizeof ( XSECTOR ) ) ;
memset ( _xs , 0 , sizeof ( XSECTOR ) ) ;
}
2021-11-15 23:30:01 +00:00
2020-09-22 19:44:33 +00:00
static void ReadSectorV7 ( FileReader & fr , sectortype & sect )
{
sect . wallptr = fr . ReadInt16 ( ) ;
sect . wallnum = fr . ReadInt16 ( ) ;
2022-02-03 18:15:46 +00:00
int c = fr . ReadInt32 ( ) ;
int f = fr . ReadInt32 ( ) ;
sect . setzfrommap ( c , f ) ;
2021-12-18 13:08:57 +00:00
sect . ceilingstat = ESectorFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
sect . floorstat = ESectorFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingpicnum = fr . ReadUInt16 ( ) ;
2020-12-06 19:17:27 +00:00
sect . ceilingheinum = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingshade = fr . ReadInt8 ( ) ;
sect . ceilingpal = fr . ReadUInt8 ( ) ;
2020-11-25 19:52:06 +00:00
sect . ceilingxpan_ = fr . ReadUInt8 ( ) ;
sect . ceilingypan_ = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
sect . floorpicnum = fr . ReadUInt16 ( ) ;
2020-12-06 19:17:27 +00:00
sect . floorheinum = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
sect . floorshade = fr . ReadInt8 ( ) ;
sect . floorpal = fr . ReadUInt8 ( ) ;
2020-11-25 19:52:06 +00:00
sect . floorxpan_ = fr . ReadUInt8 ( ) ;
sect . floorypan_ = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
sect . visibility = fr . ReadUInt8 ( ) ;
2020-10-11 18:57:20 +00:00
sect . fogpal = fr . ReadUInt8 ( ) ; // note: currently unused, except for Blood.
2020-09-22 19:44:33 +00:00
sect . lotag = fr . ReadInt16 ( ) ;
sect . hitag = fr . ReadInt16 ( ) ;
sect . extra = fr . ReadInt16 ( ) ;
}
static void ReadSectorV6 ( FileReader & fr , sectortype & sect )
{
sect . wallptr = fr . ReadUInt16 ( ) ;
sect . wallnum = fr . ReadUInt16 ( ) ;
sect . ceilingpicnum = fr . ReadUInt16 ( ) ;
sect . floorpicnum = fr . ReadUInt16 ( ) ;
2020-12-06 19:17:27 +00:00
sect . ceilingheinum = clamp ( fr . ReadInt16 ( ) < < 5 , - 32768 , 32767 ) ;
sect . floorheinum = clamp ( fr . ReadInt16 ( ) < < 5 , - 32768 , 32767 ) ;
2022-02-03 18:15:46 +00:00
int c = fr . ReadInt32 ( ) ;
int f = fr . ReadInt32 ( ) ;
sect . setzfrommap ( c , f ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingshade = fr . ReadInt8 ( ) ;
sect . floorshade = fr . ReadInt8 ( ) ;
2020-11-25 19:52:06 +00:00
sect . ceilingxpan_ = fr . ReadUInt8 ( ) ;
sect . floorxpan_ = fr . ReadUInt8 ( ) ;
sect . ceilingypan_ = fr . ReadUInt8 ( ) ;
sect . floorypan_ = fr . ReadUInt8 ( ) ;
2021-12-18 13:08:57 +00:00
sect . ceilingstat = ESectorFlags : : FromInt ( fr . ReadUInt8 ( ) ) ;
sect . floorstat = ESectorFlags : : FromInt ( fr . ReadUInt8 ( ) ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingpal = fr . ReadUInt8 ( ) ;
sect . floorpal = fr . ReadUInt8 ( ) ;
sect . visibility = fr . ReadUInt8 ( ) ;
sect . lotag = fr . ReadInt16 ( ) ;
sect . hitag = fr . ReadInt16 ( ) ;
sect . extra = fr . ReadInt16 ( ) ;
}
static void ReadSectorV5 ( FileReader & fr , sectortype & sect )
{
sect . wallptr = fr . ReadInt16 ( ) ;
sect . wallnum = fr . ReadInt16 ( ) ;
sect . ceilingpicnum = fr . ReadUInt16 ( ) ;
sect . floorpicnum = fr . ReadUInt16 ( ) ;
2020-12-06 19:17:27 +00:00
sect . ceilingheinum = clamp ( fr . ReadInt16 ( ) < < 5 , - 32768 , 32767 ) ;
sect . floorheinum = clamp ( fr . ReadInt16 ( ) < < 5 , - 32768 , 32767 ) ;
2022-02-03 18:15:46 +00:00
int c = fr . ReadInt32 ( ) ;
int f = fr . ReadInt32 ( ) ;
sect . setzfrommap ( c , f ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingshade = fr . ReadInt8 ( ) ;
sect . floorshade = fr . ReadInt8 ( ) ;
2020-11-25 19:52:06 +00:00
sect . ceilingxpan_ = fr . ReadUInt8 ( ) ;
sect . floorxpan_ = fr . ReadUInt8 ( ) ;
sect . ceilingypan_ = fr . ReadUInt8 ( ) ;
sect . floorypan_ = fr . ReadUInt8 ( ) ;
2021-12-18 13:08:57 +00:00
sect . ceilingstat = ESectorFlags : : FromInt ( fr . ReadUInt8 ( ) ) ;
sect . floorstat = ESectorFlags : : FromInt ( fr . ReadUInt8 ( ) ) ;
2020-09-22 19:44:33 +00:00
sect . ceilingpal = fr . ReadUInt8 ( ) ;
sect . floorpal = fr . ReadUInt8 ( ) ;
sect . visibility = fr . ReadUInt8 ( ) ;
sect . lotag = fr . ReadInt16 ( ) ;
sect . hitag = fr . ReadInt16 ( ) ;
sect . extra = fr . ReadInt16 ( ) ;
2021-12-18 13:08:57 +00:00
if ( ( sect . ceilingstat & CSTAT_SECTOR_SLOPE ) = = 0 ) sect . ceilingheinum = 0 ;
if ( ( sect . floorstat & CSTAT_SECTOR_SLOPE ) = = 0 ) sect . floorheinum = 0 ;
2020-09-22 19:44:33 +00:00
}
static void ReadWallV7 ( FileReader & fr , walltype & wall )
{
2022-01-27 17:03:59 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
2022-02-03 00:02:48 +00:00
wall . setPosFromMap ( x , y ) ;
2020-09-22 19:44:33 +00:00
wall . point2 = fr . ReadInt16 ( ) ;
wall . nextwall = fr . ReadInt16 ( ) ;
wall . nextsector = fr . ReadInt16 ( ) ;
2021-12-18 14:36:50 +00:00
wall . cstat = EWallFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
wall . picnum = fr . ReadInt16 ( ) ;
wall . overpicnum = fr . ReadInt16 ( ) ;
wall . shade = fr . ReadInt8 ( ) ;
wall . pal = fr . ReadUInt8 ( ) ;
wall . xrepeat = fr . ReadUInt8 ( ) ;
wall . yrepeat = fr . ReadUInt8 ( ) ;
2020-11-26 07:38:59 +00:00
wall . xpan_ = fr . ReadUInt8 ( ) ;
wall . ypan_ = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
wall . lotag = fr . ReadInt16 ( ) ;
wall . hitag = fr . ReadInt16 ( ) ;
wall . extra = fr . ReadInt16 ( ) ;
}
static void ReadWallV6 ( FileReader & fr , walltype & wall )
{
2022-01-27 17:03:59 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
2022-02-03 00:02:48 +00:00
wall . setPosFromMap ( x , y ) ;
2020-09-22 19:44:33 +00:00
wall . point2 = fr . ReadInt16 ( ) ;
wall . nextsector = fr . ReadInt16 ( ) ;
wall . nextwall = fr . ReadInt16 ( ) ;
wall . picnum = fr . ReadInt16 ( ) ;
wall . overpicnum = fr . ReadInt16 ( ) ;
wall . shade = fr . ReadInt8 ( ) ;
wall . pal = fr . ReadUInt8 ( ) ;
2021-12-18 14:36:50 +00:00
wall . cstat = EWallFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
wall . xrepeat = fr . ReadUInt8 ( ) ;
wall . yrepeat = fr . ReadUInt8 ( ) ;
2020-11-26 07:38:59 +00:00
wall . xpan_ = fr . ReadUInt8 ( ) ;
wall . ypan_ = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
wall . lotag = fr . ReadInt16 ( ) ;
wall . hitag = fr . ReadInt16 ( ) ;
wall . extra = fr . ReadInt16 ( ) ;
}
static void ReadWallV5 ( FileReader & fr , walltype & wall )
{
2022-01-27 17:03:59 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
2022-02-03 00:02:48 +00:00
wall . setPosFromMap ( x , y ) ;
2020-09-22 19:44:33 +00:00
wall . point2 = fr . ReadInt16 ( ) ;
wall . picnum = fr . ReadInt16 ( ) ;
wall . overpicnum = fr . ReadInt16 ( ) ;
wall . shade = fr . ReadInt8 ( ) ;
2021-12-18 14:36:50 +00:00
wall . cstat = EWallFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
wall . xrepeat = fr . ReadUInt8 ( ) ;
wall . yrepeat = fr . ReadUInt8 ( ) ;
2020-11-26 07:38:59 +00:00
wall . xpan_ = fr . ReadUInt8 ( ) ;
wall . ypan_ = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
wall . nextsector = fr . ReadInt16 ( ) ;
wall . nextwall = fr . ReadInt16 ( ) ;
fr . Seek ( 4 , FileReader : : SeekSet ) ; // skip over 2 unused 16 bit values
wall . lotag = fr . ReadInt16 ( ) ;
wall . hitag = fr . ReadInt16 ( ) ;
wall . extra = fr . ReadInt16 ( ) ;
}
static void SetWallPalV5 ( )
{
2021-12-21 09:51:41 +00:00
for ( unsigned i = 0 ; i < sector . Size ( ) ; i + + )
2020-09-22 19:44:33 +00:00
{
int startwall = sector [ i ] . wallptr ;
int endwall = startwall + sector [ i ] . wallnum ;
for ( int w = startwall ; w < endwall ; w + + )
{
wall [ w ] . pal = sector [ i ] . floorpal ;
}
}
}
2022-02-02 17:59:37 +00:00
void validateSprite ( spritetype & spri , int sectnum , int index )
2020-09-22 19:44:33 +00:00
{
2022-09-24 16:13:16 +00:00
sectortype * sectp = & sector [ sectnum ] ;
2022-09-24 11:55:50 +00:00
auto pos = spri . pos ;
2020-09-22 19:44:33 +00:00
bool bugged = false ;
2022-02-02 17:59:37 +00:00
if ( ( unsigned ) spri . statnum > = MAXSTATUS )
2020-09-22 19:44:33 +00:00
{
2022-09-24 11:55:50 +00:00
Printf ( " Sprite #%d (%.0f,%.0f) has invalid statnum %d. \n " , index , pos . X , pos . Y , spri . statnum ) ;
2020-09-22 19:44:33 +00:00
bugged = true ;
}
2022-02-02 17:59:37 +00:00
else if ( ( unsigned ) spri . picnum > = MAXTILES )
2020-09-22 19:44:33 +00:00
{
2022-09-24 11:55:50 +00:00
Printf ( " Sprite #%d (%.0f,%.0f) has invalid picnum %d. \n " , index , pos . X , pos . Y , spri . picnum ) ;
2020-09-22 19:44:33 +00:00
bugged = true ;
}
2021-12-06 16:18:11 +00:00
else if ( ! validSectorIndex ( sectnum ) )
2020-09-22 19:44:33 +00:00
{
2022-09-24 16:13:16 +00:00
sectp = nullptr ;
updatesector ( pos . XY ( ) , & sectp ) ;
bugged = sectp = = nullptr ;
2020-09-22 19:44:33 +00:00
2022-09-24 16:13:16 +00:00
if ( ! DPrintf ( DMSG_WARNING , " Sprite #%d (%.0f,%.0f) with invalid sector %d was corrected to sector %d \n " , index , pos . X , pos . Y , sectnum , : : sectnum ( sectp ) ) )
2021-11-09 22:50:02 +00:00
{
2022-09-24 11:55:50 +00:00
if ( bugged ) Printf ( " Sprite #%d (%.0f,%.0f) with invalid sector %d \n " , index , pos . X , pos . Y , sectnum ) ;
2021-11-09 22:50:02 +00:00
}
2020-09-22 19:44:33 +00:00
}
if ( bugged )
{
2022-02-02 17:59:37 +00:00
spri = { } ;
spri . statnum = MAXSTATUS ;
2022-09-24 16:13:16 +00:00
sectp = nullptr ;
2020-09-22 19:44:33 +00:00
}
2022-09-24 16:13:16 +00:00
spri . sectp = sectp ;
2020-09-22 19:44:33 +00:00
}
2021-12-06 16:18:11 +00:00
static void ReadSpriteV7 ( FileReader & fr , spritetype & spr , int & secno )
2020-09-22 19:44:33 +00:00
{
2022-02-01 18:36:58 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
int z = fr . ReadInt32 ( ) ;
spr . SetMapPos ( x , y , z ) ;
2021-12-18 11:28:48 +00:00
spr . cstat = ESpriteFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
spr . picnum = fr . ReadInt16 ( ) ;
spr . shade = fr . ReadInt8 ( ) ;
spr . pal = fr . ReadUInt8 ( ) ;
2022-09-09 16:32:42 +00:00
spr . clipdist = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
spr . blend = fr . ReadUInt8 ( ) ;
spr . xrepeat = fr . ReadUInt8 ( ) ;
spr . yrepeat = fr . ReadUInt8 ( ) ;
spr . xoffset = fr . ReadInt8 ( ) ;
spr . yoffset = fr . ReadInt8 ( ) ;
2021-12-06 16:18:11 +00:00
secno = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
spr . statnum = fr . ReadInt16 ( ) ;
2022-08-16 21:29:54 +00:00
spr . intangle = fr . ReadInt16 ( ) ;
2022-09-11 11:47:47 +00:00
spr . angle = mapangle ( spr . intangle ) ;
2022-05-23 22:30:41 +00:00
spr . intowner = fr . ReadInt16 ( ) ;
2022-09-01 14:33:54 +00:00
spr . xint = fr . ReadInt16 ( ) ;
spr . yint = fr . ReadInt16 ( ) ;
2022-08-31 22:00:36 +00:00
spr . inittype = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
spr . lotag = fr . ReadInt16 ( ) ;
spr . hitag = fr . ReadInt16 ( ) ;
spr . extra = fr . ReadInt16 ( ) ;
2020-10-02 20:42:54 +00:00
spr . detail = 0 ;
2020-09-22 19:44:33 +00:00
}
2021-12-06 16:18:11 +00:00
static void ReadSpriteV6 ( FileReader & fr , spritetype & spr , int & secno )
2020-09-22 19:44:33 +00:00
{
2022-02-01 18:36:58 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
int z = fr . ReadInt32 ( ) ;
spr . SetMapPos ( x , y , z ) ;
2021-12-18 11:28:48 +00:00
spr . cstat = ESpriteFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
spr . shade = fr . ReadInt8 ( ) ;
spr . pal = fr . ReadUInt8 ( ) ;
2022-09-09 16:32:42 +00:00
spr . clipdist = fr . ReadUInt8 ( ) ;
2020-09-22 19:44:33 +00:00
spr . xrepeat = fr . ReadUInt8 ( ) ;
spr . yrepeat = fr . ReadUInt8 ( ) ;
spr . xoffset = fr . ReadInt8 ( ) ;
spr . yoffset = fr . ReadInt8 ( ) ;
spr . picnum = fr . ReadInt16 ( ) ;
2022-08-16 21:29:54 +00:00
spr . intangle = fr . ReadInt16 ( ) ;
2022-09-11 11:47:47 +00:00
spr . angle = mapangle ( spr . intangle ) ;
2022-09-01 14:33:54 +00:00
spr . xint = fr . ReadInt16 ( ) ;
spr . yint = fr . ReadInt16 ( ) ;
2022-08-31 22:00:36 +00:00
spr . inittype = fr . ReadInt16 ( ) ;
2022-05-23 22:30:41 +00:00
spr . intowner = fr . ReadInt16 ( ) ;
2021-12-06 16:18:11 +00:00
secno = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
spr . statnum = fr . ReadInt16 ( ) ;
spr . lotag = fr . ReadInt16 ( ) ;
spr . hitag = fr . ReadInt16 ( ) ;
spr . extra = fr . ReadInt16 ( ) ;
spr . blend = 0 ;
2020-10-02 20:42:54 +00:00
spr . detail = 0 ;
2022-01-04 16:06:26 +00:00
if ( ( spr . cstat & CSTAT_SPRITE_ALIGNMENT_MASK ) = = CSTAT_SPRITE_ALIGNMENT_SLOPE )
spr . cstat & = ~ CSTAT_SPRITE_ALIGNMENT_MASK ;
2020-09-22 19:44:33 +00:00
}
2021-12-06 16:18:11 +00:00
static void ReadSpriteV5 ( FileReader & fr , spritetype & spr , int & secno )
2020-09-22 19:44:33 +00:00
{
2022-02-01 18:36:58 +00:00
int x = fr . ReadInt32 ( ) ;
int y = fr . ReadInt32 ( ) ;
int z = fr . ReadInt32 ( ) ;
spr . SetMapPos ( x , y , z ) ;
2021-12-18 11:28:48 +00:00
spr . cstat = ESpriteFlags : : FromInt ( fr . ReadUInt16 ( ) ) ;
2020-09-22 19:44:33 +00:00
spr . shade = fr . ReadInt8 ( ) ;
spr . xrepeat = fr . ReadUInt8 ( ) ;
spr . yrepeat = fr . ReadUInt8 ( ) ;
spr . picnum = fr . ReadInt16 ( ) ;
2022-08-16 21:29:54 +00:00
spr . intangle = fr . ReadInt16 ( ) ;
2022-09-11 11:47:47 +00:00
spr . angle = mapangle ( spr . intangle ) ;
2022-09-01 14:33:54 +00:00
spr . xint = fr . ReadInt16 ( ) ;
spr . yint = fr . ReadInt16 ( ) ;
2022-08-31 22:00:36 +00:00
spr . inittype = fr . ReadInt16 ( ) ;
2022-05-23 22:30:41 +00:00
spr . intowner = fr . ReadInt16 ( ) ;
2021-12-06 16:18:11 +00:00
secno = fr . ReadInt16 ( ) ;
2020-09-22 19:44:33 +00:00
spr . statnum = fr . ReadInt16 ( ) ;
spr . lotag = fr . ReadInt16 ( ) ;
spr . hitag = fr . ReadInt16 ( ) ;
spr . extra = fr . ReadInt16 ( ) ;
2022-01-04 16:06:26 +00:00
if ( ( spr . cstat & CSTAT_SPRITE_ALIGNMENT_MASK ) = = CSTAT_SPRITE_ALIGNMENT_SLOPE )
spr . cstat & = ~ CSTAT_SPRITE_ALIGNMENT_MASK ;
2020-09-22 19:44:33 +00:00
2021-12-30 15:51:56 +00:00
auto sec = spr . sectp ;
2021-12-18 12:14:56 +00:00
if ( ( sec - > ceilingstat & CSTAT_SECTOR_SKY ) > 0 )
2021-11-26 19:55:13 +00:00
spr . pal = sec - > ceilingpal ;
2020-09-22 19:44:33 +00:00
else
2021-11-26 19:55:13 +00:00
spr . pal = sec - > floorpal ;
2020-09-22 19:44:33 +00:00
spr . blend = 0 ;
2022-09-09 16:32:42 +00:00
spr . clipdist = 32 ;
2020-09-22 19:44:33 +00:00
spr . xoffset = 0 ;
spr . yoffset = 0 ;
2020-10-02 20:42:54 +00:00
spr . detail = 0 ;
2020-09-22 19:44:33 +00:00
}
2021-11-15 23:30:01 +00:00
// allocates global map storage. Blood will also call this.
2021-12-21 09:51:41 +00:00
void allocateMapArrays ( int numwall , int numsector , int numsprites )
2020-09-22 19:44:33 +00:00
{
2021-11-27 12:47:53 +00:00
ClearInterpolations ( ) ;
2021-11-15 23:30:01 +00:00
2021-11-20 22:20:43 +00:00
2021-12-21 09:51:41 +00:00
show2dsector . Resize ( numsector ) ;
show2dwall . Resize ( numwall ) ;
gotsector . Resize ( numsector ) ;
clipsectormap . Resize ( numsector ) ;
2021-11-20 23:03:56 +00:00
2021-11-20 22:20:43 +00:00
mapDataArena . FreeAll ( ) ;
2021-12-21 09:51:41 +00:00
sector . Resize ( numsector ) ;
memset ( sector . Data ( ) , 0 , sizeof ( sectortype ) * numsector ) ;
wall . Resize ( numwall ) ;
2021-12-21 08:54:21 +00:00
memset ( wall . Data ( ) , 0 , sizeof ( walltype ) * wall . Size ( ) ) ;
2021-11-15 23:30:01 +00:00
ClearAutomap ( ) ;
}
2021-12-06 19:54:21 +00:00
void fixSectors ( )
{
2021-12-21 08:23:39 +00:00
for ( auto & sect : sector )
2021-12-06 19:54:21 +00:00
{
// Fix maps which do not set their wallptr to the first wall of the sector. Lo Wang In Time's map 11 is such a case.
auto wp = sect . firstWall ( ) ;
// Note: we do not have the 'sector' index initialized here, it would not be helpful anyway for this fix.
while ( wp ! = wall . Data ( ) & & wp [ - 1 ] . twoSided ( ) & & wp [ - 1 ] . nextWall ( ) - > nextWall ( ) = = & wp [ - 1 ] & & wp [ - 1 ] . nextWall ( ) - > nextSector ( ) = = & sect )
{
sect . wallptr - - ;
sect . wallnum + + ;
wp - - ;
}
}
}
2022-09-24 16:27:02 +00:00
void validateStartSector ( const char * filename , const DVector3 & pos , sectortype * * cursect , unsigned numsectors , bool noabort )
2022-01-01 09:39:59 +00:00
{
2022-09-24 16:27:02 +00:00
if ( * cursect = = nullptr )
2022-01-01 09:39:59 +00:00
{
sectortype * sect = nullptr ;
2022-08-23 21:36:23 +00:00
updatesectorz ( pos , & sect ) ;
if ( ! sect ) updatesector ( pos , & sect ) ;
2022-01-12 23:21:15 +00:00
if ( sect | | noabort )
2022-01-01 09:39:59 +00:00
{
2022-09-24 16:27:02 +00:00
Printf ( PRINT_HIGH , " Error in map %s: Start sector %d out of range. Max. sector is %d \n " , filename , sectnum ( * cursect ) , numsectors ) ;
* cursect = sect ? sect : & sector [ 0 ] ;
2022-01-01 09:39:59 +00:00
}
else
{
2022-09-24 16:27:02 +00:00
I_Error ( " Unable to start map %s: Start sector %d out of range. Max. sector is %d. No valid location at start spot \n " , filename , sectnum ( * cursect ) , numsectors ) ;
2022-01-01 09:39:59 +00:00
}
}
}
2022-09-24 16:27:02 +00:00
void loadMap ( const char * filename , int flags , DVector3 * pos , int16_t * ang , sectortype * * cursect , SpawnSpriteDef & sprites )
2021-11-15 23:30:01 +00:00
{
inputState . ClearAllInput ( ) ;
2020-09-22 19:44:33 +00:00
FileReader fr = fileSystem . OpenFileReader ( filename ) ;
if ( ! fr . isOpen ( ) ) I_Error ( " Unable to open map %s " , filename ) ;
int mapversion = fr . ReadInt32 ( ) ;
if ( mapversion < 5 | | mapversion > 9 ) // 9 is most likely useless but let's try anyway.
{
2021-12-09 19:44:04 +00:00
I_Error ( " %s: Invalid map format, expected 5-9, got %d " , filename , mapversion ) ;
2020-09-22 19:44:33 +00:00
}
2022-08-23 21:36:23 +00:00
pos - > X = fr . ReadInt32 ( ) * maptoworld ;
pos - > Y = fr . ReadInt32 ( ) * maptoworld ;
pos - > Z = fr . ReadInt32 ( ) * zmaptoworld ;
2020-09-22 19:44:33 +00:00
* ang = fr . ReadInt16 ( ) & 2047 ;
2022-09-24 16:27:02 +00:00
int cursectnum = fr . ReadUInt16 ( ) ;
2020-09-22 19:44:33 +00:00
2021-11-15 23:30:01 +00:00
// Get the basics out before loading the data so that we can set up the global storage.
2021-12-21 10:58:20 +00:00
unsigned numsectors = fr . ReadUInt16 ( ) ;
2021-11-15 23:30:01 +00:00
auto sectorpos = fr . Tell ( ) ;
fr . Seek ( ( mapversion = = 5 ? sectorsize5 : mapversion = = 6 ? sectorsize6 : sectorsize7 ) * numsectors , FileReader : : SeekCur ) ;
2021-12-21 10:58:20 +00:00
unsigned numwalls = fr . ReadUInt16 ( ) ;
2021-11-15 23:30:01 +00:00
auto wallpos = fr . Tell ( ) ;
fr . Seek ( ( mapversion = = 5 ? wallsize5 : mapversion = = 6 ? wallsize6 : wallsize7 ) * numwalls , FileReader : : SeekCur ) ;
int numsprites = fr . ReadUInt16 ( ) ;
auto spritepos = fr . Tell ( ) ;
// Now that we know the map's size, set up the globals.
2021-12-21 09:51:41 +00:00
allocateMapArrays ( numwalls , numsectors , numsprites ) ;
2021-12-03 21:45:54 +00:00
sprites . sprites . Resize ( numsprites ) ;
2021-12-04 18:08:50 +00:00
memset ( sprites . sprites . Data ( ) , 0 , numsprites * sizeof ( spritetype ) ) ;
2021-11-15 23:30:01 +00:00
// Now load the actual data.
fr . Seek ( sectorpos , FileReader : : SeekSet ) ;
2021-12-21 09:51:41 +00:00
for ( unsigned i = 0 ; i < sector . Size ( ) ; i + + )
2020-09-22 19:44:33 +00:00
{
switch ( mapversion )
{
case 5 : ReadSectorV5 ( fr , sector [ i ] ) ; break ;
case 6 : ReadSectorV6 ( fr , sector [ i ] ) ; break ;
default : ReadSectorV7 ( fr , sector [ i ] ) ; break ;
}
2021-11-22 22:05:48 +00:00
// If we do not do this here, we need to do a lot more contortions to exclude this default from getting written out to savegames.
2021-11-20 19:52:29 +00:00
if ( isExhumed ( ) )
{
sector [ i ] . Sound = - 1 ;
}
2020-09-22 19:44:33 +00:00
}
2021-11-15 23:30:01 +00:00
fr . Seek ( wallpos , FileReader : : SeekSet ) ;
2021-12-21 08:54:21 +00:00
for ( unsigned i = 0 ; i < wall . Size ( ) ; i + + )
2020-09-22 19:44:33 +00:00
{
switch ( mapversion )
{
case 5 : ReadWallV5 ( fr , wall [ i ] ) ; break ;
case 6 : ReadWallV6 ( fr , wall [ i ] ) ; break ;
default : ReadWallV7 ( fr , wall [ i ] ) ; break ;
}
}
2021-11-15 23:30:01 +00:00
fr . Seek ( spritepos , FileReader : : SeekSet ) ;
2020-09-22 19:44:33 +00:00
for ( int i = 0 ; i < numsprites ; i + + )
{
2021-12-06 16:18:11 +00:00
int secno = - 1 ;
2020-09-22 19:44:33 +00:00
switch ( mapversion )
{
2021-12-06 16:18:11 +00:00
case 5 : ReadSpriteV5 ( fr , sprites . sprites [ i ] , secno ) ; break ;
case 6 : ReadSpriteV6 ( fr , sprites . sprites [ i ] , secno ) ; break ;
default : ReadSpriteV7 ( fr , sprites . sprites [ i ] , secno ) ; break ;
2020-09-22 19:44:33 +00:00
}
2021-12-17 19:24:48 +00:00
validateSprite ( sprites . sprites [ i ] , secno , i ) ;
2021-12-05 08:34:30 +00:00
2020-09-22 19:44:33 +00:00
}
artSetupMapArt ( filename ) ;
//Must be last.
2021-12-06 19:54:21 +00:00
fixSectors ( ) ;
2022-09-24 16:27:02 +00:00
* cursect = validSectorIndex ( cursectnum ) ? & sector [ cursectnum ] : nullptr ;
updatesector ( * pos , cursect ) ;
2021-03-07 08:39:16 +00:00
fr . Seek ( 0 , FileReader : : SeekSet ) ;
auto buffer = fr . Read ( ) ;
2022-08-04 17:13:55 +00:00
uint8_t md4 [ 16 ] ;
2021-03-07 08:39:16 +00:00
md4once ( buffer . Data ( ) , buffer . Size ( ) , md4 ) ;
2021-12-17 19:24:48 +00:00
loadMapHack ( filename , md4 , sprites ) ;
2021-03-19 19:40:44 +00:00
setWallSectors ( ) ;
2021-12-16 05:10:59 +00:00
hw_CreateSections ( ) ;
2021-12-15 12:08:09 +00:00
sectionGeometry . SetSize ( sections . Size ( ) ) ;
2021-05-03 15:48:35 +00:00
2020-09-22 19:44:33 +00:00
2021-11-20 22:42:01 +00:00
wallbackup = wall ;
2021-11-20 22:20:43 +00:00
sectorbackup = sector ;
2022-09-24 16:27:02 +00:00
validateStartSector ( filename , * pos , cursect , numsectors ) ;
2020-09-22 19:44:33 +00:00
}
2020-10-11 18:57:20 +00:00
2021-12-26 18:58:52 +00:00
//==========================================================================
//
// Decrypt
//
// Note that this is different from the general RFF encryption.
//
//==========================================================================
static void Decrypt ( void * to_ , const void * from_ , int len , int key )
{
uint8_t * to = ( uint8_t * ) to_ ;
const uint8_t * from = ( const uint8_t * ) from_ ;
for ( int i = 0 ; i < len ; + + i , + + key )
{
to [ i ] = from [ i ] ^ key ;
}
}
//==========================================================================
//
// P_LoadBloodMap
//
// This was adapted from ZDoom's old Build map loader.
//
//==========================================================================
static void P_LoadBloodMapWalls ( uint8_t * data , size_t len , TArray < walltype > & lwalls )
{
uint8_t infoBlock [ 37 ] ;
int mapver = data [ 5 ] ;
uint32_t matt ;
int i ;
int k ;
if ( mapver ! = 6 & & mapver ! = 7 )
{
return ;
}
matt = * ( uint32_t * ) ( data + 28 ) ;
if ( matt ! = 0 & &
matt ! = MAKE_ID ( ' M ' , ' a ' , ' t ' , ' t ' ) & &
matt ! = MAKE_ID ( ' t ' , ' t ' , ' a ' , ' M ' ) )
{
Decrypt ( infoBlock , data + 6 , 37 , 0x7474614d ) ;
}
else
{
memcpy ( infoBlock , data + 6 , 37 ) ;
}
int numRevisions = * ( uint32_t * ) ( infoBlock + 27 ) ;
int numSectors = * ( uint16_t * ) ( infoBlock + 31 ) ;
int numWalls = * ( uint16_t * ) ( infoBlock + 33 ) ;
int numSprites = * ( uint16_t * ) ( infoBlock + 35 ) ;
int skyLen = 2 < < * ( uint16_t * ) ( infoBlock + 16 ) ;
if ( mapver = = 7 )
{
// Version 7 has some extra stuff after the info block. This
// includes a copyright, and I have no idea what the rest of
// it is.
data + = 171 ;
}
else
{
data + = 43 ;
}
// Skip the sky info.
data + = skyLen ;
lwalls . Reserve ( numWalls ) ;
// Read sectors
k = numRevisions * sizeof ( sectortypedisk ) ;
for ( i = 0 ; i < numSectors ; + + i )
{
sectortypedisk bsec ;
if ( mapver = = 7 )
{
Decrypt ( & bsec , data , sizeof ( sectortypedisk ) , k ) ;
}
else
{
memcpy ( & bsec , data , sizeof ( sectortypedisk ) ) ;
}
data + = sizeof ( sectortypedisk ) ;
if ( bsec . extra > 0 ) // skip Xsector
{
data + = 60 ;
}
}
// Read walls
k | = 0x7474614d ;
for ( i = 0 ; i < numWalls ; + + i )
{
walltypedisk load ;
if ( mapver = = 7 )
{
Decrypt ( & load , data , sizeof ( walltypedisk ) , k ) ;
}
else
{
memcpy ( & load , data , sizeof ( walltypedisk ) ) ;
}
// only copy what we need to draw the map preview.
auto pWall = & lwalls [ i ] ;
2022-01-27 17:35:12 +00:00
int x = LittleLong ( load . x ) ;
int y = LittleLong ( load . y ) ;
2022-02-03 00:02:48 +00:00
pWall - > setPosFromMap ( x , y ) ;
2021-12-26 18:58:52 +00:00
pWall - > point2 = LittleShort ( load . point2 ) ;
pWall - > nextwall = LittleShort ( load . nextwall ) ;
pWall - > nextsector = LittleShort ( load . nextsector ) ;
data + = sizeof ( walltypedisk ) ;
if ( load . extra > 0 ) // skip Xwall
{
data + = 24 ;
}
}
}
TArray < walltype > loadMapWalls ( const char * filename )
{
TArray < walltype > lwalls ;
FileReader fr = fileSystem . OpenFileReader ( filename ) ;
if ( ! fr . isOpen ( ) ) return lwalls ;
if ( isBlood ( ) )
{
auto data = fr . Read ( ) ;
P_LoadBloodMapWalls ( data . Data ( ) , data . Size ( ) , lwalls ) ;
return lwalls ;
}
int mapversion = fr . ReadInt32 ( ) ;
if ( mapversion < 5 | | mapversion > 9 ) return lwalls ;
fr . Seek ( 16 , FileReader : : SeekCur ) ;
// Get the basics out before loading the data so that we can set up the global storage.
unsigned numsectors = fr . ReadUInt16 ( ) ;
fr . Seek ( ( mapversion = = 5 ? sectorsize5 : mapversion = = 6 ? sectorsize6 : sectorsize7 ) * numsectors , FileReader : : SeekCur ) ;
unsigned numwalls = fr . ReadUInt16 ( ) ;
lwalls . Resize ( numwalls ) ;
for ( unsigned i = 0 ; i < lwalls . Size ( ) ; i + + )
{
switch ( mapversion )
{
case 5 : ReadWallV5 ( fr , lwalls [ i ] ) ; break ;
case 6 : ReadWallV6 ( fr , lwalls [ i ] ) ; break ;
default : ReadWallV7 ( fr , lwalls [ i ] ) ; break ;
}
}
return lwalls ;
}
2022-08-23 21:36:23 +00:00
void qloadboard ( const char * filename , uint8_t flags , DVector3 * dapos , int16_t * daang ) ;
2020-10-11 18:57:20 +00:00
// loads a map into the backup buffer.
void loadMapBackup ( const char * filename )
{
2022-08-23 21:36:23 +00:00
DVector3 fpos ;
2020-10-11 18:57:20 +00:00
int16_t scratch ;
2022-09-24 16:27:02 +00:00
sectortype * scratch2 ;
2021-12-04 11:31:54 +00:00
SpawnSpriteDef scratch3 ;
2020-10-11 18:57:20 +00:00
2021-01-02 03:46:58 +00:00
if ( isBlood ( ) )
2020-10-11 18:57:20 +00:00
{
2022-08-23 21:36:23 +00:00
qloadboard ( filename , 0 , & fpos , & scratch ) ;
2020-10-11 18:57:20 +00:00
}
else
{
2022-08-23 21:36:23 +00:00
loadMap ( filename , 0 , & fpos , & scratch , & scratch2 , scratch3 ) ;
2020-10-11 18:57:20 +00:00
}
2021-03-19 19:40:44 +00:00
}
// Sets the sector reference for each wall. We need this for the triangulation cache.
void setWallSectors ( )
{
2021-12-21 08:23:39 +00:00
for ( auto & wal : wall )
2021-12-08 20:09:47 +00:00
{
wal . sector = - 1 ;
2021-12-17 17:22:42 +00:00
wal . lengthflags = 3 ;
2021-12-08 20:09:47 +00:00
}
2021-12-17 17:22:42 +00:00
2021-12-21 09:51:41 +00:00
for ( unsigned i = 0 ; i < sector . Size ( ) - 1 ; i + + )
2021-12-10 15:54:41 +00:00
{
auto sect = & sector [ i ] ;
auto nextsect = & sector [ i + 1 ] ;
if ( sect - > wallptr < nextsect - > wallptr & & sect - > wallptr + sect - > wallnum > nextsect - > wallptr )
{
// We have overlapping wall ranges for two sectors. Do some analysis to see where these walls belong
int checkstart = nextsect - > wallptr ;
int checkend = sect - > wallptr + sect - > wallnum ;
// for now assign the walls to the first sector. Final decisions are made below.
nextsect - > wallnum - = checkend - checkstart ;
nextsect - > wallptr = checkend ;
auto belongs = [ ] ( int wal , int first , int last , int firstwal )
{
bool point2ok = wall [ wal ] . point2 > = first & & wall [ wal ] . point2 < last ;
bool refok = false ;
for ( int i = first ; i < last ; i + + )
if ( wall [ i ] . point2 > = firstwal & & wall [ i ] . point2 < = wal )
{
refok = true ; break ;
}
return refok & & point2ok ;
} ;
while ( checkstart < checkend & & belongs ( checkstart , sect - > wallptr , checkstart , checkstart ) )
checkstart + + ;
sect - > wallnum = checkstart - sect - > wallptr ;
while ( checkstart < checkend & & belongs ( checkend - 1 , checkend , nextsect - > wallptr + nextsect - > wallnum , checkstart ) )
checkend - - ;
nextsect - > wallnum + = nextsect - > wallptr - checkend ;
nextsect - > wallptr = checkend ;
if ( nextsect - > wallptr > sect - > wallptr + sect - > wallnum )
{
// If there's a gap, assign to the first sector. In this case we may only guess.
Printf ( " Wall range %d - %d referenced by sectors %d and %d \n " , sect - > wallptr + sect - > wallnum , nextsect - > wallptr - 1 , i , i + 1 ) ;
sect - > wallnum = nextsect - > wallptr - sect - > wallptr ;
}
}
}
2021-12-24 08:19:43 +00:00
int i = 0 ;
2021-12-21 08:23:39 +00:00
for ( auto & sect : sector )
2021-03-19 19:40:44 +00:00
{
2021-12-11 22:00:38 +00:00
sect . dirty = EDirty : : AllDirty ;
2021-12-01 23:45:34 +00:00
for ( auto & wal : wallsofsector ( & sect ) )
2021-03-19 19:40:44 +00:00
{
2021-12-08 20:09:47 +00:00
if ( wal . sector = = - 1 )
wal . sector = i ;
2021-03-19 19:40:44 +00:00
}
2021-12-01 23:45:34 +00:00
i + + ;
2021-03-19 19:40:44 +00:00
}
2021-07-27 20:11:53 +00:00
2021-12-29 23:37:08 +00:00
//
for ( unsigned ii = 1 ; ii < wall . Size ( ) - 1 ; ii + + )
2021-12-14 12:37:08 +00:00
{
// two maps in RRRA have this error. Delete one of those 2 walls.
2021-12-29 23:37:08 +00:00
if ( wall [ ii ] . point2 = = wall [ ii + 1 ] . point2 )
2021-12-14 12:37:08 +00:00
{
2021-12-29 23:37:08 +00:00
auto w1 = wall [ ii ] . lastWall ( false ) ;
auto w2 = wall [ ii + 1 ] . lastWall ( false ) ;
// Neutralize the bad one of the two walls.
if ( w1 = = nullptr )
{
wall [ ii ] . nextwall = - 1 ;
wall [ ii ] . nextsector = - 1 ;
wall [ ii ] . point2 = ii ;
}
else if ( w2 = = nullptr )
{
wall [ ii + 1 ] . nextwall = - 1 ;
wall [ ii + 1 ] . nextsector = - 1 ;
wall [ ii + 1 ] . point2 = ii ;
}
2021-12-14 12:37:08 +00:00
}
}
2021-07-27 20:11:53 +00:00
// validate 'nextsector' fields. Some maps have these wrong which can cause render glitches and occasionally even crashes.
2021-12-21 08:23:39 +00:00
for ( auto & wal : wall )
2021-07-27 20:11:53 +00:00
{
2021-12-10 12:42:34 +00:00
if ( validWallIndex ( wal . nextwall ) )
2021-07-27 20:11:53 +00:00
{
2021-12-01 23:45:34 +00:00
if ( wal . nextsector ! = wal . nextWall ( ) - > sector )
2021-07-27 20:11:53 +00:00
{
2021-12-24 08:19:43 +00:00
DPrintf ( DMSG_ERROR , " Bad 'nextsector' reference %d on wall %d \n " , wal . nextsector , wall . IndexOf ( & wal ) ) ;
2021-12-01 23:45:34 +00:00
wal . nextsector = wal . nextWall ( ) - > sector ;
2021-07-27 20:11:53 +00:00
}
}
2022-02-15 21:47:53 +00:00
else
{
wal . nextwall = - 1 ;
wal . nextsector = - 1 ;
}
2021-07-27 20:11:53 +00:00
}
2021-11-07 17:09:19 +00:00
}
2021-12-05 23:50:33 +00:00
void MarkMap ( )
{
2021-12-06 16:00:15 +00:00
for ( auto & stat : statList )
{
GC : : Mark ( stat . firstEntry ) ;
GC : : Mark ( stat . lastEntry ) ;
}
2021-12-21 08:23:39 +00:00
for ( auto & sect : sector )
2021-12-05 23:50:33 +00:00
{
GC : : Mark ( sect . firstEntry ) ;
GC : : Mark ( sect . lastEntry ) ;
if ( isDukeLike ( ) ) GC : : Mark ( sect . hitagactor ) ;
else if ( isBlood ( ) )
{
GC : : Mark ( sect . upperLink ) ;
GC : : Mark ( sect . lowerLink ) ;
}
}
}