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"
|
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-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();
|
|
|
|
sect.ceilingz = fr.ReadInt32();
|
|
|
|
sect.floorz = fr.ReadInt32();
|
|
|
|
sect.ceilingstat = fr.ReadUInt16();
|
|
|
|
sect.floorstat = fr.ReadUInt16();
|
|
|
|
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);
|
2020-09-22 19:44:33 +00:00
|
|
|
sect.ceilingz = fr.ReadInt32();
|
|
|
|
sect.floorz = fr.ReadInt32();
|
|
|
|
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();
|
2020-09-22 19:44:33 +00:00
|
|
|
sect.ceilingstat = fr.ReadUInt8();
|
|
|
|
sect.floorstat = fr.ReadUInt8();
|
|
|
|
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);
|
2020-09-22 19:44:33 +00:00
|
|
|
sect.ceilingz = fr.ReadInt32();
|
|
|
|
sect.floorz = fr.ReadInt32();
|
|
|
|
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();
|
2020-09-22 19:44:33 +00:00
|
|
|
sect.ceilingstat = fr.ReadUInt8();
|
|
|
|
sect.floorstat = fr.ReadUInt8();
|
|
|
|
sect.ceilingpal = fr.ReadUInt8();
|
|
|
|
sect.floorpal = fr.ReadUInt8();
|
|
|
|
sect.visibility = fr.ReadUInt8();
|
|
|
|
sect.lotag = fr.ReadInt16();
|
|
|
|
sect.hitag = fr.ReadInt16();
|
|
|
|
sect.extra = fr.ReadInt16();
|
|
|
|
if ((sect.ceilingstat & 2) == 0) sect.ceilingheinum = 0;
|
|
|
|
if ((sect.floorstat & 2) == 0) sect.floorheinum = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadWallV7(FileReader& fr, walltype& wall)
|
|
|
|
{
|
|
|
|
wall.pos.x = fr.ReadInt32();
|
|
|
|
wall.pos.y = fr.ReadInt32();
|
|
|
|
wall.point2 = fr.ReadInt16();
|
|
|
|
wall.nextwall = fr.ReadInt16();
|
|
|
|
wall.nextsector = fr.ReadInt16();
|
|
|
|
wall.cstat = fr.ReadUInt16();
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
wall.pos.x = fr.ReadInt32();
|
|
|
|
wall.pos.y = fr.ReadInt32();
|
|
|
|
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();
|
|
|
|
wall.cstat = fr.ReadUInt16();
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
wall.pos.x = fr.ReadInt32();
|
|
|
|
wall.pos.y = fr.ReadInt32();
|
|
|
|
wall.point2 = fr.ReadInt16();
|
|
|
|
wall.picnum = fr.ReadInt16();
|
|
|
|
wall.overpicnum = fr.ReadInt16();
|
|
|
|
wall.shade = fr.ReadInt8();
|
|
|
|
wall.cstat = fr.ReadUInt16();
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
int startwall = sector[i].wallptr;
|
|
|
|
int endwall = startwall + sector[i].wallnum;
|
|
|
|
for (int w = startwall; w < endwall; w++)
|
|
|
|
{
|
|
|
|
wall[w].pal = sector[i].floorpal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-09 22:50:02 +00:00
|
|
|
void ValidateSprite(spritetype& spr)
|
2020-09-22 19:44:33 +00:00
|
|
|
{
|
2021-05-11 23:50:41 +00:00
|
|
|
int index = int(&spr - sprite);
|
2020-09-22 19:44:33 +00:00
|
|
|
bool bugged = false;
|
|
|
|
if ((unsigned)spr.statnum >= MAXSTATUS)
|
|
|
|
{
|
|
|
|
Printf("Sprite #%d (%d,%d) has invalid statnum %d.\n", index, spr.x, spr.y, spr.statnum);
|
|
|
|
bugged = true;
|
|
|
|
}
|
|
|
|
else if ((unsigned)spr.picnum >= MAXTILES)
|
|
|
|
{
|
|
|
|
Printf("Sprite #%d (%d,%d) has invalid picnum %d.\n", index, spr.x, spr.y, spr.picnum);
|
|
|
|
bugged = true;
|
|
|
|
}
|
2021-11-21 07:31:41 +00:00
|
|
|
else if (!validSectorIndex(spr.sectnum))
|
2020-09-22 19:44:33 +00:00
|
|
|
{
|
2021-11-07 18:11:16 +00:00
|
|
|
int sectnum = -1;
|
|
|
|
updatesector(spr.x, spr.y, §num);
|
2021-11-21 07:31:41 +00:00
|
|
|
bugged = sectnum < 0;
|
2020-09-22 19:44:33 +00:00
|
|
|
|
2021-11-09 22:50:02 +00:00
|
|
|
if (!DPrintf(DMSG_WARNING, "Sprite #%d (%d,%d) with invalid sector %d was corrected to sector %d\n", index, spr.x, spr.y, spr.sectnum, sectnum))
|
|
|
|
{
|
|
|
|
if (bugged) Printf("Sprite #%d (%d,%d) with invalid sector %d\n", index, spr.x, spr.y, spr.sectnum);
|
|
|
|
}
|
|
|
|
spr.sectnum = sectnum;
|
2020-09-22 19:44:33 +00:00
|
|
|
}
|
|
|
|
if (bugged)
|
|
|
|
{
|
2020-10-02 20:14:01 +00:00
|
|
|
spr.clear();
|
2020-09-22 19:44:33 +00:00
|
|
|
spr.statnum = MAXSTATUS;
|
|
|
|
spr.sectnum = MAXSECTORS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadSpriteV7(FileReader& fr, spritetype& spr)
|
|
|
|
{
|
|
|
|
spr.pos.x = fr.ReadInt32();
|
|
|
|
spr.pos.y = fr.ReadInt32();
|
|
|
|
spr.pos.z = fr.ReadInt32();
|
|
|
|
spr.cstat = fr.ReadUInt16();
|
|
|
|
spr.picnum = fr.ReadInt16();
|
|
|
|
spr.shade = fr.ReadInt8();
|
|
|
|
spr.pal = fr.ReadUInt8();
|
|
|
|
spr.clipdist = fr.ReadUInt8();
|
|
|
|
spr.blend = fr.ReadUInt8();
|
|
|
|
spr.xrepeat = fr.ReadUInt8();
|
|
|
|
spr.yrepeat = fr.ReadUInt8();
|
|
|
|
spr.xoffset = fr.ReadInt8();
|
|
|
|
spr.yoffset = fr.ReadInt8();
|
|
|
|
spr.sectnum = fr.ReadInt16();
|
|
|
|
spr.statnum = fr.ReadInt16();
|
|
|
|
spr.ang = fr.ReadInt16();
|
|
|
|
spr.owner = fr.ReadInt16();
|
|
|
|
spr.xvel = fr.ReadInt16();
|
|
|
|
spr.yvel = fr.ReadInt16();
|
|
|
|
spr.zvel = fr.ReadInt16();
|
|
|
|
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
|
|
|
ValidateSprite(spr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadSpriteV6(FileReader& fr, spritetype& spr)
|
|
|
|
{
|
|
|
|
spr.pos.x = fr.ReadInt32();
|
|
|
|
spr.pos.y = fr.ReadInt32();
|
|
|
|
spr.pos.z = fr.ReadInt32();
|
|
|
|
spr.cstat = fr.ReadUInt16();
|
|
|
|
spr.shade = fr.ReadInt8();
|
|
|
|
spr.pal = fr.ReadUInt8();
|
|
|
|
spr.clipdist = fr.ReadUInt8();
|
|
|
|
spr.xrepeat = fr.ReadUInt8();
|
|
|
|
spr.yrepeat = fr.ReadUInt8();
|
|
|
|
spr.xoffset = fr.ReadInt8();
|
|
|
|
spr.yoffset = fr.ReadInt8();
|
|
|
|
spr.picnum = fr.ReadInt16();
|
|
|
|
spr.ang = fr.ReadInt16();
|
|
|
|
spr.xvel = fr.ReadInt16();
|
|
|
|
spr.yvel = fr.ReadInt16();
|
|
|
|
spr.zvel = fr.ReadInt16();
|
|
|
|
spr.owner = fr.ReadInt16();
|
|
|
|
spr.sectnum = fr.ReadInt16();
|
|
|
|
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;
|
2020-09-22 19:44:33 +00:00
|
|
|
ValidateSprite(spr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadSpriteV5(FileReader& fr, spritetype& spr)
|
|
|
|
{
|
|
|
|
spr.pos.x = fr.ReadInt32();
|
|
|
|
spr.pos.y = fr.ReadInt32();
|
|
|
|
spr.pos.z = fr.ReadInt32();
|
|
|
|
spr.cstat = fr.ReadUInt16();
|
|
|
|
spr.shade = fr.ReadInt8();
|
|
|
|
spr.xrepeat = fr.ReadUInt8();
|
|
|
|
spr.yrepeat = fr.ReadUInt8();
|
|
|
|
spr.picnum = fr.ReadInt16();
|
|
|
|
spr.ang = fr.ReadInt16();
|
|
|
|
spr.xvel = fr.ReadInt16();
|
|
|
|
spr.yvel = fr.ReadInt16();
|
|
|
|
spr.zvel = fr.ReadInt16();
|
|
|
|
spr.owner = fr.ReadInt16();
|
|
|
|
spr.sectnum = fr.ReadInt16();
|
|
|
|
spr.statnum = fr.ReadInt16();
|
|
|
|
spr.lotag = fr.ReadInt16();
|
|
|
|
spr.hitag = fr.ReadInt16();
|
|
|
|
spr.extra = fr.ReadInt16();
|
|
|
|
|
|
|
|
int sec = spr.sectnum;
|
|
|
|
if ((sector[sec].ceilingstat & 1) > 0)
|
|
|
|
spr.pal = sector[sec].ceilingpal;
|
|
|
|
else
|
|
|
|
spr.pal = sector[sec].floorpal;
|
|
|
|
|
|
|
|
spr.blend = 0;
|
|
|
|
spr.clipdist = 32;
|
|
|
|
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
|
|
|
ValidateSprite(spr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-07 19:14:44 +00:00
|
|
|
static void insertAllSprites(const char* filename, const vec3_t* pos, int* cursectnum, int16_t numsprites)
|
2020-09-22 19:44:33 +00:00
|
|
|
{
|
|
|
|
// This function is stupid because it exploits side effects of insertsprite and should be redone by only inserting the valid sprites.
|
|
|
|
int i, realnumsprites = numsprites;
|
|
|
|
|
|
|
|
for (i = 0; i < numsprites; i++)
|
|
|
|
{
|
|
|
|
bool removeit = false;
|
|
|
|
auto& spr = sprite[i];
|
|
|
|
|
|
|
|
if (spr.statnum == MAXSTATUS)
|
|
|
|
{
|
|
|
|
spr.statnum = spr.sectnum = 0;
|
|
|
|
removeit = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
insertsprite(spr.sectnum, spr.statnum);
|
|
|
|
|
|
|
|
if (removeit)
|
|
|
|
{
|
|
|
|
sprite[i].statnum = MAXSTATUS;
|
|
|
|
realnumsprites--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numsprites != realnumsprites)
|
|
|
|
{
|
|
|
|
for (i = 0; i < numsprites; i++)
|
|
|
|
if (sprite[i].statnum == MAXSTATUS)
|
|
|
|
{
|
|
|
|
// Now remove it for real!
|
|
|
|
sprite[i].statnum = 0;
|
|
|
|
deletesprite(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(realnumsprites == Numsprites);
|
|
|
|
}
|
|
|
|
|
2021-04-24 18:00:54 +00:00
|
|
|
void addBlockingPairs();
|
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.
|
|
|
|
void allocateMapArrays(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-11-20 23:03:56 +00:00
|
|
|
show2dsector.Resize(numsectors);
|
|
|
|
show2dwall.Resize(numwalls);
|
|
|
|
|
2021-11-20 22:20:43 +00:00
|
|
|
mapDataArena.FreeAll();
|
|
|
|
sector.Resize(numsectors);
|
|
|
|
memset(sector.Data(), 0, sizeof(sectortype) * numsectors);
|
2021-11-20 22:42:01 +00:00
|
|
|
wall.Resize(numwalls);
|
|
|
|
memset(wall.Data(), 0, sizeof(walltype) * numwalls);
|
2021-11-20 16:35:41 +00:00
|
|
|
memset(sprite, 0, sizeof(*sprite) * MAXSPRITES);
|
2021-11-15 23:30:01 +00:00
|
|
|
memset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
|
|
|
|
memset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES + MAXUNIQHUDID));
|
|
|
|
|
|
|
|
ClearAutomap();
|
|
|
|
Polymost::Polymost_prepare_loadboard();
|
|
|
|
}
|
|
|
|
|
|
|
|
void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang, int* cursectnum)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
{
|
|
|
|
I_Error("%s: Invalid map format, expcted 5-9, got %d", filename, mapversion);
|
|
|
|
}
|
|
|
|
|
|
|
|
pos->x = fr.ReadInt32();
|
|
|
|
pos->y = fr.ReadInt32();
|
|
|
|
pos->z = fr.ReadInt32();
|
|
|
|
*ang = fr.ReadInt16() & 2047;
|
|
|
|
*cursectnum = fr.ReadUInt16();
|
|
|
|
|
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.
|
2020-09-22 19:44:33 +00:00
|
|
|
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);
|
|
|
|
numwalls = fr.ReadUInt16();
|
|
|
|
auto wallpos = fr.Tell();
|
|
|
|
fr.Seek((mapversion == 5 ? wallsize5 : mapversion == 6 ? wallsize6 : wallsize7)* numwalls, FileReader::SeekCur);
|
|
|
|
int numsprites = fr.ReadUInt16();
|
|
|
|
if ((unsigned)numsprites > MAXSPRITES) I_Error("%s: Invalid map, too many sprites", filename);
|
|
|
|
auto spritepos = fr.Tell();
|
|
|
|
|
|
|
|
// Now that we know the map's size, set up the globals.
|
|
|
|
allocateMapArrays(numsprites);
|
2021-12-11 11:51:07 +00:00
|
|
|
initspritelists(); // may not be used in Blood!
|
2021-11-15 23:30:01 +00:00
|
|
|
|
|
|
|
// Now load the actual data.
|
|
|
|
fr.Seek(sectorpos, FileReader::SeekSet);
|
2020-09-22 19:44:33 +00:00
|
|
|
for (int i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
switch (mapversion)
|
|
|
|
{
|
|
|
|
case 5: ReadSectorV5(fr, sector[i]); break;
|
|
|
|
case 6: ReadSectorV6(fr, sector[i]); break;
|
|
|
|
default: ReadSectorV7(fr, sector[i]); break;
|
|
|
|
}
|
2021-11-20 19:52:29 +00:00
|
|
|
// If we do not do this here, we need to do a lot more contortions to exclude these defaults from getting written out to savegames.
|
|
|
|
// This way they just get copied to the sector backup array. These 4 are the only values in all games needing such treatment.
|
|
|
|
if (isExhumed())
|
|
|
|
{
|
|
|
|
sector[i].Sound = -1;
|
|
|
|
sector[i].Above = -1;
|
|
|
|
sector[i].Below = -1;
|
|
|
|
}
|
2020-09-22 19:44:33 +00:00
|
|
|
}
|
|
|
|
|
2021-11-15 23:30:01 +00:00
|
|
|
fr.Seek(wallpos, FileReader::SeekSet);
|
2020-09-22 19:44:33 +00:00
|
|
|
for (int i = 0; i < numwalls; i++)
|
|
|
|
{
|
|
|
|
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++)
|
|
|
|
{
|
|
|
|
switch (mapversion)
|
|
|
|
{
|
|
|
|
case 5: ReadSpriteV5(fr, sprite[i]); break;
|
|
|
|
case 6: ReadSpriteV6(fr, sprite[i]); break;
|
|
|
|
default: ReadSpriteV7(fr, sprite[i]); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
artSetupMapArt(filename);
|
|
|
|
insertAllSprites(filename, pos, cursectnum, numsprites);
|
|
|
|
|
|
|
|
for (int i = 0; i < numsprites; i++)
|
|
|
|
{
|
|
|
|
if ((sprite[i].cstat & 48) == 48) sprite[i].cstat &= ~48;
|
|
|
|
}
|
|
|
|
//Must be last.
|
|
|
|
updatesector(pos->x, pos->y, cursectnum);
|
|
|
|
guniqhudid = 0;
|
2021-03-07 08:39:16 +00:00
|
|
|
fr.Seek(0, FileReader::SeekSet);
|
|
|
|
auto buffer = fr.Read();
|
|
|
|
unsigned char md4[16];
|
|
|
|
md4once(buffer.Data(), buffer.Size(), md4);
|
|
|
|
G_LoadMapHack(filename, md4);
|
2021-03-19 19:40:44 +00:00
|
|
|
setWallSectors();
|
2021-05-02 22:04:36 +00:00
|
|
|
hw_BuildSections();
|
2021-05-03 15:48:35 +00:00
|
|
|
sectorGeometry.SetSize(numsections);
|
|
|
|
|
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;
|
2020-09-22 19:44:33 +00:00
|
|
|
}
|
2020-10-11 18:57:20 +00:00
|
|
|
|
|
|
|
|
2021-11-16 17:20:24 +00:00
|
|
|
void qloadboard(const char* filename, char flags, vec3_t* dapos, int16_t* daang, int* dacursectnum);
|
2020-10-11 18:57:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
// loads a map into the backup buffer.
|
|
|
|
void loadMapBackup(const char* filename)
|
|
|
|
{
|
|
|
|
vec3_t pos;
|
|
|
|
int16_t scratch;
|
2021-11-07 19:14:44 +00:00
|
|
|
int scratch2;
|
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
|
|
|
{
|
2021-11-16 17:20:24 +00:00
|
|
|
qloadboard(filename, 0, &pos, &scratch, &scratch2);
|
2020-10-11 18:57:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-07 19:14:44 +00:00
|
|
|
engineLoadBoard(filename, 0, &pos, &scratch, &scratch2);
|
2020-10-11 18:57:20 +00:00
|
|
|
initspritelists();
|
|
|
|
}
|
2021-03-19 19:40:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the sector reference for each wall. We need this for the triangulation cache.
|
|
|
|
void setWallSectors()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
sector[i].dirty = 255;
|
2021-04-04 18:35:38 +00:00
|
|
|
sector[i].exflags = 0;
|
2021-03-19 19:40:44 +00:00
|
|
|
for (int w = 0; w < sector[i].wallnum; w++)
|
|
|
|
{
|
|
|
|
wall[sector[i].wallptr + w].sector = i;
|
|
|
|
}
|
|
|
|
}
|
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.
|
|
|
|
for (int i = 0; i < numwalls; i++)
|
|
|
|
{
|
|
|
|
if (wall[i].nextwall != -1)
|
|
|
|
{
|
|
|
|
if (wall[i].nextsector != wall[wall[i].nextwall].sector)
|
|
|
|
{
|
|
|
|
DPrintf(DMSG_ERROR, "Bad 'nextsector' reference %d on wall %d\n", wall[i].nextsector, i);
|
|
|
|
wall[i].nextsector = wall[wall[i].nextwall].sector;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-07 17:09:19 +00:00
|
|
|
}
|