Skip to content

Instantly share code, notes, and snippets.

@noncom
Created April 13, 2018 11:45
Show Gist options
  • Save noncom/e0ec7fe490d796fabd185e1454971432 to your computer and use it in GitHub Desktop.
Save noncom/e0ec7fe490d796fabd185e1454971432 to your computer and use it in GitHub Desktop.
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Do all the WAD I/O, get map description,
// set up initial state and misc. LUTs.
//
//-----------------------------------------------------------------------------
#include <math.h>
#include "templates.h"
#include "m_alloc.h"
#include "m_argv.h"
#include "z_zone.h"
#include "m_swap.h"
#include "m_bbox.h"
#include "g_game.h"
#include "i_system.h"
#include "w_wad.h"
#include "doomdef.h"
#include "p_local.h"
#include "p_effect.h"
#include "p_terrain.h"
#include "s_sound.h"
#include "doomstat.h"
#include "p_lnspec.h"
#include "v_palette.h"
#include "c_console.h"
#include "p_acs.h"
#include "vectors.h"
#include "wi_stuff.h"
extern void P_SpawnMapThing (mapthing2_t *mthing, int position);
extern void P_TranslateLineDef (line_t *ld, maplinedef_t *mld);
extern void P_TranslateTeleportThings (void);
extern int P_TranslateSectorSpecial (int);
extern unsigned int R_OldBlend;
//
// MAP related Lookup tables.
// Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
//
int numvertexes;
vertex_t* vertexes;
int numsegs;
seg_t* segs;
int numsectors;
sector_t* sectors;
int numsubsectors;
subsector_t* subsectors;
int numnodes;
node_t* nodes;
int numlines;
line_t* lines;
int numsides;
side_t* sides;
int sidecount;
struct sidei_t // [RH] Only keep BOOM sidedef init stuff around for init
{
union
{
// Used when unpacking sidedefs and assigning
// properties based on linedefs.
struct
{
short tag, special, map;
} a;
// Used when grouping sidedefs into loops.
struct
{
short first, next;
char lineside;
} b;
};
} *sidetemp;
// [RH] Set true if the map contains a BEHAVIOR lump
BOOL HasBehavior;
// BLOCKMAP
// Created from axis aligned bounding box
// of the map, a rectangular array of
// blocks of size 256x256.
// Used to speed up collision detection
// by spatial subdivision in 2D.
//
// Blockmap size.
int bmapwidth;
int bmapheight; // size in mapblocks
int *blockmap; // int for larger maps ([RH] Made int because BOOM does)
int *blockmaplump; // offsets in blockmap are from here
fixed_t bmaporgx; // origin of block map
fixed_t bmaporgy;
AActor** blocklinks; // for thing chains
// 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.
//
byte* rejectmatrix;
BOOL rejectempty;
// Maintain single and multi player starting spots.
TArray<mapthing2_t> deathmatchstarts (16);
mapthing2_t playerstarts[MAXPLAYERS];
static void P_AllocateSideDefs (int count);
static void P_SetSideNum (short *sidenum_p, short sidenum);
// [RH] Figure out blends for deep water sectors
static void SetTexture (short *texture, DWORD *blend, char *name)
{
if ((*blend = R_ColormapNumForName (name)) == 0)
{
if ((*texture = R_CheckTextureNumForName (name)) == -1)
{
char name2[9];
char *stop;
strncpy (name2, name, 8);
name2[8] = 0;
*blend = strtoul (name2, &stop, 16);
*texture = 0;
}
else
{
*blend = 0;
}
}
else
{
*texture = 0;
}
}
static void SetTextureNoErr (short *texture, DWORD *color, char *name)
{
if ((*texture = R_CheckTextureNumForName (name)) == -1)
{
char name2[9];
char *stop;
strncpy (name2, name, 8);
name2[8] = 0;
*color = strtoul (name2, &stop, 16);
*texture = 0;
}
}
//
// P_LoadVertexes
//
void P_LoadVertexes (int lump)
{
byte *data;
int i;
// Determine number of vertices:
// total lump length / vertex record length.
numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t);
// Allocate zone memory for buffer.
vertexes = (vertex_t *)Z_Malloc (numvertexes*sizeof(vertex_t), PU_LEVEL, 0);
// Load data into cache.
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
// Copy and convert vertex coordinates,
// internal representation as fixed.
for (i = 0; i < numvertexes; i++)
{
vertexes[i].x = SHORT(((mapvertex_t *)data)[i].x)<<FRACBITS;
vertexes[i].y = SHORT(((mapvertex_t *)data)[i].y)<<FRACBITS;
}
// Free buffer memory.
Z_Free (data);
}
//
// P_LoadSegs
//
// killough 5/3/98: reformatted, cleaned up
void P_LoadSegs (int lump)
{
int i;
byte *data;
byte *vertchanged = (byte *)Z_Malloc (numvertexes,PU_LEVEL,0); // phares 10/4/98
line_t* line; // phares 10/4/98
int ptp_angle; // phares 10/4/98
int delta_angle; // phares 10/4/98
int dis; // phares 10/4/98
int dx,dy; // phares 10/4/98
int vnum1,vnum2; // phares 10/4/98
memset (vertchanged,0,numvertexes); // phares 10/4/98
numsegs = W_LumpLength (lump) / sizeof(mapseg_t);
segs = (seg_t *)Z_Malloc (numsegs*sizeof(seg_t), PU_LEVEL, 0);
memset (segs, 0, numsegs*sizeof(seg_t));
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
// phares: 10/4/98: Vertchanged is an array that represents the vertices.
// Mark those used by linedefs. A marked vertex is one that is not a
// candidate for movement further down.
line = lines;
for (i = 0; i < numlines ; i++, line++)
{
vertchanged[line->v1 - vertexes] = vertchanged[line->v2 - vertexes] = 1;
}
for (i = 0; i < numsegs; i++)
{
seg_t *li = segs+i;
mapseg_t *ml = (mapseg_t *) data + i;
int side, linedef;
line_t *ldef;
li->v1 = &vertexes[SHORT(ml->v1)];
li->v2 = &vertexes[SHORT(ml->v2)];
li->angle = (SHORT(ml->angle))<<16;
// phares 10/4/98: In the case of a lineseg that was created by splitting
// another line, it appears that the line angle is inherited from the
// father line. Due to roundoff, the new vertex may have been placed 'off
// the line'. When you get close to such a line, and it is very short,
// it's possible that the roundoff error causes 'firelines', the thin
// lines that can draw from screen top to screen bottom occasionally. This
// is due to all the angle calculations that are done based on the line
// angle, the angles from the viewer to the vertices, and the viewer's
// angle in the world. In the case of firelines, the rounded-off position
// of one of the vertices determines one of these angles, and introduces
// an error in the scaling factor for mapping textures and determining
// where on the screen the ceiling and floor spans should be shown. For a
// fireline, the engine thinks the ceiling bottom and floor top are at the
// midpoint of the screen. So you get ceilings drawn all the way down to the
// screen midpoint, and floors drawn all the way up. Thus 'firelines'. The
// name comes from the original sighting, which involved a fire texture.
//
// To correct this, reset the vertex that was added so that it sits ON the
// split line.
//
// To know which of the two vertices was added, its number is greater than
// that of the last of the author-created vertices. If both vertices of the
// line were added by splitting, pick the higher-numbered one. Once you've
// changed a vertex, don't change it again if it shows up in another seg.
//
// To determine if there's an error in the first place, find the
// angle of the line between the two seg vertices. If it's one degree or more
// off, then move one vertex. This may seem insignificant, but one degree
// errors _can_ cause firelines.
ptp_angle = R_PointToAngle2(li->v1->x,li->v1->y,li->v2->x,li->v2->y);
dis = 0;
delta_angle = (abs(ptp_angle-li->angle)>>ANGLETOFINESHIFT)*360/8192;
if (delta_angle != 0)
{
dx = (li->v1->x - li->v2->x)>>FRACBITS;
dy = (li->v1->y - li->v2->y)>>FRACBITS;
dis = ((int) sqrt(dx*dx + dy*dy))<<FRACBITS;
dx = finecosine[li->angle>>ANGLETOFINESHIFT];
dy = finesine[li->angle>>ANGLETOFINESHIFT];
vnum1 = li->v1 - vertexes;
vnum2 = li->v2 - vertexes;
if ((vnum2 > vnum1) && (vertchanged[vnum2] == 0))
{
li->v2->x = li->v1->x + FixedMul(dis,dx);
li->v2->y = li->v1->y + FixedMul(dis,dy);
vertchanged[vnum2] = 1; // this was changed
}
else if (vertchanged[vnum1] == 0)
{
li->v1->x = li->v2->x - FixedMul(dis,dx);
li->v1->y = li->v2->y - FixedMul(dis,dy);
vertchanged[vnum1] = 1; // this was changed
}
}
linedef = SHORT(ml->linedef);
ldef = &lines[linedef];
li->linedef = ldef;
side = SHORT(ml->side);
li->sidedef = &sides[ldef->sidenum[side]];
li->frontsector = sides[ldef->sidenum[side]].sector;
// killough 5/3/98: ignore 2s flag if second sidedef missing:
if (ldef->flags & ML_TWOSIDED && ldef->sidenum[side^1]!=-1)
{
li->backsector = sides[ldef->sidenum[side^1]].sector;
}
else
{
li->backsector = 0;
ldef->flags &= ~ML_TWOSIDED;
}
}
Z_Free (data);
Z_Free(vertchanged); // phares 10/4/98
}
//
// P_LoadSubsectors
//
void P_LoadSubsectors (int lump)
{
byte *data;
int i;
numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t);
subsectors = (subsector_t *)Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0);
data = (byte *)W_CacheLumpNum (lump,PU_STATIC);
memset (subsectors, 0, numsubsectors*sizeof(subsector_t));
for (i = 0; i < numsubsectors; i++)
{
subsectors[i].numlines = SHORT(((mapsubsector_t *)data)[i].numsegs);
subsectors[i].firstline = SHORT(((mapsubsector_t *)data)[i].firstseg);
}
Z_Free (data);
}
//
// P_LoadSectors
//
void P_LoadSectors (int lump)
{
byte* data;
int i;
mapsector_t* ms;
sector_t* ss;
int defSeqType;
FDynamicColormap *fogMap, *normMap;
numsectors = W_LumpLength (lump) / sizeof(mapsector_t);
sectors = (sector_t *)Z_Malloc (numsectors*sizeof(sector_t), PU_LEVEL, 0);
memset (sectors, 0, numsectors*sizeof(sector_t));
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
if (level.flags & LEVEL_SNDSEQTOTALCTRL)
defSeqType = 0;
else
defSeqType = -1;
fogMap = normMap = NULL;
ms = (mapsector_t *)data;
ss = sectors;
for (i = 0; i < numsectors; i++, ss++, ms++)
{
// [NightFang] - added
ss->netid = i;
ss->floortexz = SHORT(ms->floorheight)<<FRACBITS;
ss->floorplane.d = -ss->floortexz;
ss->floorplane.c = FRACUNIT;
ss->floorplane.ic = FRACUNIT;
ss->ceilingtexz = SHORT(ms->ceilingheight)<<FRACBITS;
ss->ceilingplane.d = ss->ceilingtexz;
ss->ceilingplane.c = -FRACUNIT;
ss->ceilingplane.ic = -FRACUNIT;
ss->floorpic = (short)R_FlatNumForName(ms->floorpic);
ss->ceilingpic = (short)R_FlatNumForName(ms->ceilingpic);
ss->lightlevel = clamp (SHORT(ms->lightlevel), (short)0, (short)255);
if (HasBehavior)
ss->special = SHORT(ms->special);
else // [RH] Translate to new sector special
ss->special = P_TranslateSectorSpecial (SHORT(ms->special));
ss->tag = SHORT(ms->tag);
ss->thinglist = NULL;
ss->touching_thinglist = NULL; // phares 3/14/98
ss->seqType = defSeqType;
ss->nextsec = -1; //jff 2/26/98 add fields to support locking out
ss->prevsec = -1; // stair retriggering until build completes
// killough 3/7/98:
ss->floor_xoffs = 0;
ss->floor_yoffs = 0; // floor and ceiling flats offsets
ss->ceiling_xoffs = 0;
ss->ceiling_yoffs = 0;
ss->floor_xscale = FRACUNIT; // [RH] floor and ceiling scaling
ss->floor_yscale = FRACUNIT;
ss->ceiling_xscale = FRACUNIT;
ss->ceiling_yscale = FRACUNIT;
ss->floor_angle = 0; // [RH] floor and ceiling rotation
ss->ceiling_angle = 0;
ss->base_ceiling_angle = ss->base_ceiling_yoffs =
ss->base_floor_angle = ss->base_floor_yoffs = 0;
ss->heightsec = NULL; // sector used to get floor and ceiling height
// killough 3/7/98: end changes
ss->FloorFlags = ss->CeilingFlags = 0;
ss->FloorLight = ss->CeilingLight = 0;
ss->gravity = 1.0f; // [RH] Default sector gravity of 1.0
// [RH] Sectors default to white light with the default fade.
// If they are outside (have a sky ceiling), they use the outside fog.
if (level.outsidefog != 0xff000000 && ss->ceilingpic == skyflatnum)
{
if (fogMap == NULL)
fogMap = GetSpecialLights (PalEntry (255,255,255), level.outsidefog);
ss->ceilingcolormap = ss->floorcolormap = fogMap;
}
else
{
if (normMap == NULL)
normMap = GetSpecialLights (PalEntry (255,255,255), level.fadeto);
ss->ceilingcolormap = ss->floorcolormap = normMap;
}
ss->sky = 0;
// killough 8/28/98: initialize all sectors to normal friction
ss->friction = ORIG_FRICTION;
ss->movefactor = ORIG_FRICTION_FACTOR;
}
Z_Free (data);
}
//
// P_LoadNodes
//
void P_LoadNodes (int lump)
{
byte* data;
int i;
int j;
int k;
mapnode_t* mn;
node_t* no;
numnodes = W_LumpLength (lump) / sizeof(mapnode_t);
nodes = (node_t *)Z_Malloc (numnodes*sizeof(node_t), PU_LEVEL, 0);
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
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;
}
}
Z_Free (data);
}
//
// P_LoadThings
//
void P_LoadThings (int lump)
{
mapthing2_t mt2; // [RH] for translation
byte *data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
mapthing_t *mt = (mapthing_t *)data;
mapthing_t *lastmt = (mapthing_t *)(data + W_LumpLength (lump));
// [RH] ZDoom now uses Hexen-style maps as its native format.
// Since this is the only place where Doom-style Things are ever
// referenced, we translate them into a Hexen-style thing.
memset (&mt2, 0, sizeof(mt2));
for ( ; mt < lastmt; mt++)
{
// [RH] At this point, monsters unique to Doom II were weeded out
// if the IWAD wasn't for Doom II. R_SpawnMapThing() can now
// handle these and more cases better, so we just pass it
// everything and let it decide what to do with them.
// [RH] Need to translate the spawn flags to Hexen format.
short flags = SHORT(mt->options);
mt2.flags = (short)((flags & 0xf) | 0x7e0);
if (flags & BTF_NOTSINGLE) mt2.flags &= ~MTF_SINGLE;
if (flags & BTF_NOTDEATHMATCH) mt2.flags &= ~MTF_DEATHMATCH;
if (flags & BTF_NOTCOOPERATIVE) mt2.flags &= ~MTF_COOPERATIVE;
mt2.x = SHORT(mt->x);
mt2.y = SHORT(mt->y);
mt2.angle = SHORT(mt->angle);
mt2.type = SHORT(mt->type);
P_SpawnMapThing (&mt2, 0);
}
Z_Free (data);
}
//
// P_SpawnSlopeMakers
//
static void P_SlopeLineToPoint (int lineid, fixed_t x, fixed_t y, fixed_t z, BOOL slopeCeil)
{
int linenum = -1;
while ((linenum = P_FindLineFromID (lineid, linenum)) != -1)
{
const line_t *line = &lines[linenum];
sector_t *sec;
secplane_t *plane;
if (P_PointOnLineSide (x, y, line) == 0)
{
sec = line->frontsector;
}
else
{
sec = line->backsector;
}
if (sec == NULL)
{
continue;
}
if (slopeCeil)
{
plane = &sec->ceilingplane;
}
else
{
plane = &sec->floorplane;
}
vec3_t p, v1, v2, cross;
p[0] = FIXED2FLOAT (line->v1->x);
p[1] = FIXED2FLOAT (line->v1->y);
p[2] = FIXED2FLOAT (plane->ZatPoint (line->v1->x, line->v1->y));
v1[0] = FIXED2FLOAT (line->dx);
v1[1] = FIXED2FLOAT (line->dy);
v1[2] = FIXED2FLOAT (plane->ZatPoint (line->v2->x, line->v2->y)) - p[2];
v2[0] = FIXED2FLOAT (x - line->v1->x);
v2[1] = FIXED2FLOAT (y - line->v1->y);
v2[2] = FIXED2FLOAT (z) - p[2];
CrossProduct (v1, v2, cross);
VectorNormalize (cross);
// Fix backward normals
if ((cross[2] < 0 && !slopeCeil) || (cross[2] > 0 && slopeCeil))
{
cross[0] = -cross[0];
cross[1] = -cross[1];
cross[2] = -cross[2];
}
plane->a = FLOAT2FIXED (cross[0]);
plane->b = FLOAT2FIXED (cross[1]);
plane->c = FLOAT2FIXED (cross[2]);
plane->ic = FLOAT2FIXED (1.f/cross[2]);
plane->d = -TMulScale16 (plane->a, x,
plane->b, y,
plane->c, z);
}
}
static void P_CopyPlane (int tag, fixed_t x, fixed_t y, BOOL copyCeil)
{
sector_t *dest = R_PointInSubsector (x, y)->sector;
sector_t *source;
int secnum;
size_t planeofs;
secnum = P_FindSectorFromTag (tag, -1);
if (secnum == -1)
{
return;
}
source = &sectors[secnum];
if (copyCeil)
{
planeofs = myoffsetof(sector_t, ceilingplane);
}
else
{
planeofs = myoffsetof(sector_t, floorplane);
}
*(secplane_t *)((BYTE *)dest + planeofs) = *(secplane_t *)((BYTE *)source + planeofs);
}
void P_SetSlope (secplane_t *plane, BOOL setCeil, int xyangi, int zangi,
fixed_t x, fixed_t y, fixed_t z)
{
angle_t xyang;
angle_t zang;
if (zangi >= 180)
{
zang = ANGLE_180-ANGLE_1;
}
else if (zangi <= 0)
{
zang = ANGLE_1;
}
else
{
zang = Scale (zangi, ANGLE_180, 180);
}
if (!setCeil)
{
zang += ANGLE_180;
}
zang >>= ANGLETOFINESHIFT;
xyang = (angle_t)Scale (xyangi, ANGLE_180, 180) >> ANGLETOFINESHIFT;
vec3_t norm;
norm[0] = (float)(finecosine[zang] * finecosine[xyang]);
norm[1] = (float)(finecosine[zang] * finesine[xyang]);
norm[2] = (float)(finesine[zang]) * 65536.f;
VectorNormalize (norm);
plane->a = (int)(norm[0] * 65536.f);
plane->b = (int)(norm[1] * 65536.f);
plane->c = (int)(norm[2] * 65536.f);
plane->ic = (int)(65536.f / norm[2]);
plane->d = -TMulScale16 (plane->a, x,
plane->b, y,
plane->c, z);
}
enum
{
THING_SlopeFloorPointLine = 9500,
THING_SlopeCeilingPointLine = 9501,
THING_SetFloorSlope = 9502,
THING_SetCeilingSlope = 9503,
THING_CopyFloorPlane = 9510,
THING_CopyCeilingPlane = 9511,
};
static void P_SpawnSlopeMakers (mapthing2_t *mt, mapthing2_t *lastmt)
{
mapthing2_t *firstmt = mt;
for (; mt < lastmt; ++mt)
{
if (mt->type >= THING_SlopeFloorPointLine &&
mt->type <= THING_SetCeilingSlope)
{
fixed_t x, y, z;
secplane_t *refplane;
sector_t *sec;
x = mt->x << FRACBITS;
y = mt->y << FRACBITS;
sec = R_PointInSubsector (x, y)->sector;
if (mt->type & 1)
{
refplane = &sec->ceilingplane;
}
else
{
refplane = &sec->floorplane;
}
z = refplane->ZatPoint (x, y) + (mt->z << FRACBITS);
if (mt->type <= THING_SlopeCeilingPointLine)
{
P_SlopeLineToPoint (mt->args[0], x, y, z, mt->type & 1);
}
else
{
P_SetSlope (refplane, mt->type & 1, mt->angle, mt->args[0], x, y, z);
}
mt->type = 0;
}
}
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->type == THING_CopyFloorPlane ||
mt->type == THING_CopyCeilingPlane)
{
P_CopyPlane (mt->args[0], mt->x << FRACBITS, mt->y << FRACBITS, mt->type & 1);
mt->type = 0;
}
}
}
// [RH]
// P_LoadThings2
//
// Same as P_LoadThings() except it assumes Things are
// saved Hexen-style. Position also controls which single-
// player start spots are spawned by filtering out those
// whose first parameter don't match position.
//
void P_LoadThings2 (int lump, int position)
{
byte *data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
mapthing2_t *mt = (mapthing2_t *)data;
mapthing2_t *lastmt = (mapthing2_t *)(data + W_LumpLength (lump));
#ifdef __BIG_ENDIAN__
for (; mt < lastmt; ++mt)
{
mt->thingid = SHORT(mt->thingid);
mt->x = SHORT(mt->x);
mt->y = SHORT(mt->y);
mt->z = SHORT(mt->z);
mt->angle = SHORT(mt->angle);
mt->type = SHORT(mt->type);
mt->flags = SHORT(mt->flags);
}
#endif
// [RH] Spawn slope creating things first.
P_SpawnSlopeMakers (mt, lastmt);
for (; mt < lastmt; mt++)
{
P_SpawnMapThing (mt, position);
}
Z_Free (data);
}
//
// P_LoadLineDefs
//
// killough 4/4/98: split into two functions, to allow sidedef overloading
//
// [RH] Actually split into four functions to allow for Hexen and Doom
// linedefs.
void P_AdjustLine (line_t *ld)
{
vertex_t *v1, *v2;
ld->alpha = 255; // [RH] Opaque by default
v1 = ld->v1;
v2 = ld->v2;
ld->dx = v2->x - v1->x;
ld->dy = v2->y - v1->y;
if (ld->dx == 0)
ld->slopetype = ST_VERTICAL;
else if (ld->dy == 0)
ld->slopetype = ST_HORIZONTAL;
else
ld->slopetype = ((ld->dy ^ ld->dx) >= 0) ? ST_POSITIVE : 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;
}
// [RH] Set line id (as appropriate) here
if (ld->special == Line_SetIdentification ||
ld->special == Teleport_Line ||
ld->special == TranslucentLine ||
ld->special == Scroll_Texture_Model)
{
ld->id = ld->args[0];
}
// killough 4/4/98: support special sidedef interpretation below
if ((ld->sidenum[0] != -1) &&
// [RH] Save Static_Init only if it's interested in the textures
(ld->special != Static_Init || ld->args[1] == Init_Color))
{
sidetemp[*ld->sidenum].a.special = ld->special;
sidetemp[*ld->sidenum].a.tag = ld->args[0];
}
else
{
sidetemp[*ld->sidenum].a.special = 0;
}
}
// killough 4/4/98: delay using sidedefs until they are loaded
void P_FinishLoadingLineDefs ()
{
WORD len;
int i, linenum;
register line_t *ld = lines;
for (i = numlines, linenum = 0; i--; ld++, linenum++)
{
ld->frontsector = ld->sidenum[0]!=-1 ? sides[ld->sidenum[0]].sector : 0;
ld->backsector = ld->sidenum[1]!=-1 ? sides[ld->sidenum[1]].sector : 0;
float dx = FIXED2FLOAT(ld->v2->x - ld->v1->x);
float dy = FIXED2FLOAT(ld->v2->y - ld->v1->y);
SBYTE light;
if (ld->frontsector == NULL)
{
Printf (PRINT_HIGH, "Line %d has no front sector\n", linenum);
}
// [RH] Set some new sidedef properties
len = (int)sqrtf (dx*dx + dy*dy);
light = dy == 0 ? level.WallHorizLight :
dx == 0 ? level.WallVertLight : 0;
if (len == 0)
{
Printf ("Line %d has 0 length\n", linenum);
len = 1;
}
if (ld->sidenum[0] != -1)
{
sides[ld->sidenum[0]].linenum = linenum;
sides[ld->sidenum[0]].TexelLength = len;
sides[ld->sidenum[0]].Light = light;
}
if (ld->sidenum[1] != -1)
{
sides[ld->sidenum[1]].linenum = linenum;
sides[ld->sidenum[1]].TexelLength = len;
sides[ld->sidenum[1]].Light = light;
}
switch (ld->special)
{ // killough 4/11/98: handle special types
int j;
case TranslucentLine: // killough 4/11/98: translucent 2s textures
// [RH] Second arg controls how opaque it is.
if (!ld->args[0])
ld->alpha = (byte)ld->args[1];
else
for (j = 0; j < numlines; j++)
if (lines[j].id == ld->args[0])
lines[j].alpha = (byte)ld->args[1];
break;
}
}
}
void P_LoadLineDefs (int lump)
{
byte *data;
int i;
line_t *ld;
numlines = W_LumpLength (lump) / sizeof(maplinedef_t);
//Raider debug stuff:
//Printf("*****>>Raider debug stuff: numlines = W_LumpLength--->%d\n",numlines);
lines = (line_t *)Z_Malloc (numlines*sizeof(line_t), PU_LEVEL, 0);
memset (lines, 0, numlines*sizeof(line_t));
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
// [RH] Count the number of sidedef references. This is the number of
// sidedefs we need. The actual number in the SIDEDEFS lump might be less.
for (i = sidecount = 0; i < numlines; i++)
{
maplinedef_t *mld = ((maplinedef_t *)data) + i;
if (SHORT(mld->sidenum[0]) != -1)
sidecount++;
if (SHORT(mld->sidenum[1]) != -1)
sidecount++;
}
P_AllocateSideDefs (sidecount);
ld = lines;
for (i = 0; i < numlines; i++, ld++)
{
maplinedef_t *mld = ((maplinedef_t *)data) + i;
// [RH] Translate old linedef special and flags to be
// compatible with the new format.
P_TranslateLineDef (ld, mld);
ld->v1 = &vertexes[SHORT(mld->v1)];
ld->v2 = &vertexes[SHORT(mld->v2)];
P_SetSideNum (&ld->sidenum[0], mld->sidenum[0]);
P_SetSideNum (&ld->sidenum[1], mld->sidenum[1]);
P_AdjustLine (ld);
}
Z_Free (data);
}
// [RH] Same as P_LoadLineDefs() except it uses Hexen-style LineDefs.
void P_LoadLineDefs2 (int lump)
{
byte* data;
int i;
maplinedef2_t* mld;
line_t* ld;
numlines = W_LumpLength (lump) / sizeof(maplinedef2_t);
lines = (line_t *)Z_Malloc (numlines*sizeof(line_t), PU_LEVEL,0 );
memset (lines, 0, numlines*sizeof(line_t));
data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
for (i = sidecount = 0; i < numlines; i++)
{
maplinedef2_t *mld = ((maplinedef2_t *)data) + i;
if (SHORT(mld->sidenum[0]) != -1)
sidecount++;
if (SHORT(mld->sidenum[1]) != -1)
sidecount++;
}
P_AllocateSideDefs (sidecount);
mld = (maplinedef2_t *)data;
ld = lines;
for (i = numlines; i > 0; i--, mld++, ld++)
{
int j;
for (j = 0; j < 5; j++)
ld->args[j] = mld->args[j];
ld->flags = SHORT(mld->flags);
ld->special = mld->special;
ld->v1 = &vertexes[SHORT(mld->v1)];
ld->v2 = &vertexes[SHORT(mld->v2)];
P_SetSideNum (&ld->sidenum[0], mld->sidenum[0]);
P_SetSideNum (&ld->sidenum[1], SHORT(mld->sidenum[1]));
P_AdjustLine (ld);
}
Z_Free (data);
}
//
// P_LoadSideDefs
//
// killough 4/4/98: split into two functions
void P_LoadSideDefs (int lump)
{
numsides = W_LumpLength (lump) / sizeof(mapsidedef_t);
}
static void P_AllocateSideDefs (int count)
{
int i;
sides = (side_t *)Z_Malloc (count*sizeof(side_t), PU_LEVEL, 0);
memset (sides, 0, count*sizeof(side_t));
sidetemp = (sidei_t *)Z_Malloc (MAX(count,numvertexes)
*sizeof(sidei_t), PU_LEVEL, 0);
for (i = 0; i < count; i++)
{
sidetemp[i].a.special = sidetemp[i].a.tag = 0;
sidetemp[i].a.map = -1;
}
if (count < numsides)
{
Printf ("Map has %d unused sidedefs\n", numsides - count);
}
numsides = count;
sidecount = 0;
}
static void P_SetSideNum (short *sidenum_p, short sidenum)
{
sidenum = SHORT(sidenum);
if (sidenum == -1)
{
*sidenum_p = sidenum;
}
else if (sidecount < numsides)
{
sidetemp[sidecount].a.map = sidenum;
*sidenum_p = sidecount++;
}
else
{
I_Error ("%d sidedefs is not enough\n", sidecount);
}
}
// [RH] Group sidedefs into loops so that we can easily determine
// what walls any particular wall neighbors.
static void P_LoopSidedefs ()
{
int i;
for (i = 0; i < numvertexes; ++i)
{
sidetemp[i].b.first = -1;
sidetemp[i].b.next = -1;
}
for (; i < numsides; ++i)
{
sidetemp[i].b.next = -1;
}
for (i = 0; i < numsides; ++i)
{
// For each vertex, build a list of sidedefs that use that vertex
// as their left edge.
line_t *line = &lines[sides[i].linenum];
int lineside = (line->sidenum[0] != i);
int vert = (lineside ? line->v2 : line->v1) - vertexes;
sidetemp[i].b.lineside = lineside;
sidetemp[i].b.next = sidetemp[vert].b.first;
sidetemp[vert].b.first = i;
// Set each side so that it is the only member of its loop
sides[i].LeftSide = -1;
sides[i].RightSide = -1;
}
// For each side, find the side that is to its right and set the
// loop pointers accordingly. If two sides share a left vertex, the
// one that forms the smallest angle is assumed to be the right one.
for (i = 0; i < numsides; ++i)
{
int right;
line_t *line = &lines[sides[i].linenum];
// If the side's line only exists in a single sector,
// then consider that line to be a self-contained loop
// instead of as part of another loop
if (line->frontsector == line->backsector)
{
right = line->sidenum[!sidetemp[i].b.lineside];
}
else
{
if (sidetemp[i].b.lineside)
{
right = line->v1 - vertexes;
}
else
{
right = line->v2 - vertexes;
}
right = sidetemp[right].b.first;
if (right == -1)
{ // There is no right side!
Printf ("Line %d's right edge is unconnected\n", line-lines);
continue;
}
if (sidetemp[right].b.next != -1)
{
int bestright = right; // Shut up, GCC
angle_t bestang = ANGLE_MAX;
line_t *leftline, *rightline;
angle_t ang1, ang2, ang;
leftline = &lines[sides[i].linenum];
ang1 = R_PointToAngle (leftline->dx, leftline->dy);
if (!sidetemp[i].b.lineside)
{
ang1 += ANGLE_180;
}
while (right != -1)
{
if (sides[right].LeftSide == -1)
{
rightline = &lines[sides[right].linenum];
if (rightline->frontsector != rightline->backsector)
{
ang2 = R_PointToAngle (rightline->dx, rightline->dy);
if (sidetemp[right].b.lineside)
{
ang2 += ANGLE_180;
}
ang = ang2 - ang1;
if (ang != 0 && ang <= bestang)
{
bestright = right;
bestang = ang;
}
}
}
right = sidetemp[right].b.next;
}
right = bestright;
}
}
sides[i].RightSide = right;
sides[right].LeftSide = i;
}
// Throw away sidedef init info now that we're done with it
Z_Free (sidetemp);
sidetemp = NULL;
}
// killough 4/4/98: delay using texture names until
// after linedefs are loaded, to allow overloading.
// killough 5/3/98: reformatted, cleaned up
void P_LoadSideDefs2 (int lump)
{
byte *data = (byte *)W_CacheLumpNum (lump, PU_STATIC);
int i;
for (i = 0; i < numsides; i++)
{
register mapsidedef_t *msd = (mapsidedef_t *)data + sidetemp[i].a.map;
register side_t *sd = sides + i;
register sector_t *sec;
sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;
sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
sd->linenum = -1;
// killough 4/4/98: allow sidedef texture names to be overloaded
// killough 4/11/98: refined to allow colormaps to work as wall
// textures if invalid as colormaps but valid as textures.
if ((unsigned)SHORT(msd->sector)>=(unsigned)numsectors)
{
Printf (PRINT_HIGH, "Sidedef %d has a bad sector\n", i);
sd->sector = sec = NULL;
}
else
{
sd->sector = sec = &sectors[SHORT(msd->sector)];
}
switch (sidetemp[i].a.special)
{
case Transfer_Heights: // variable colormap via 242 linedef
// [RH] The colormap num we get here isn't really a colormap,
// but a packed ARGB word for blending, so we also allow
// the blend to be specified directly by the texture names
// instead of figuring something out from the colormap.
if (sec != NULL)
{
SetTexture (&sd->bottomtexture, &sec->bottommap, msd->bottomtexture);
SetTexture (&sd->midtexture, &sec->midmap, msd->midtexture);
SetTexture (&sd->toptexture, &sec->topmap, msd->toptexture);
}
break;
case Static_Init:
// [RH] Set sector color and fog
// upper "texture" is light color
// lower "texture" is fog color
{
DWORD color = 0xffffff, fog = 0x000000;
SetTextureNoErr (&sd->bottomtexture, &fog, msd->bottomtexture);
SetTextureNoErr (&sd->toptexture, &color, msd->toptexture);
sd->midtexture = R_TextureNumForName (msd->midtexture);
if (fog != 0x000000 || color != 0xffffff)
{
int s;
FDynamicColormap *colormap = GetSpecialLights (color, fog);
for (s = 0; s < numsectors; s++)
{
if (sectors[s].tag == sidetemp[i].a.tag)
{
sectors[s].ceilingcolormap =
sectors[s].floorcolormap = colormap;
}
}
}
}
break;
/*
case TranslucentLine: // killough 4/11/98: apply translucency to 2s normal texture
sd->midtexture = strncasecmp("TRANMAP", msd->midtexture, 8) ?
(sd->special = W_CheckNumForName(msd->midtexture)) < 0 ||
W_LumpLength(sd->special) != 65536 ?
sd->special=0, R_TextureNumForName(msd->midtexture) :
(sd->special++, 0) : (sd->special=0);
sd->toptexture = R_TextureNumForName(msd->toptexture);
sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
break;
*/
default: // normal cases
sd->midtexture = R_TextureNumForName(msd->midtexture);
sd->toptexture = R_TextureNumForName(msd->toptexture);
sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
break;
}
}
Z_Free (data);
}
// [RH] Set slopes for sectors, based on line specials
//
// P_AlignPlane
//
// Aligns the floor or ceiling of a sector to the corresponding plane
// on the other side of the reference line. (By definition, line must be
// two-sided.)
//
// If (which & 1), sets floor.
// If (which & 2), sets ceiling.
//
static void P_AlignPlane (sector_t *sec, line_t *line, int which)
{
sector_t *refsec;
int bestdist;
vertex_t *refvert = (*sec->lines)->v1; // Shut up, GCC
int i;
line_t **probe;
if (line->backsector == NULL)
return;
// Find furthest vertex from the reference line. It, along with the two ends
// of the line will define the plane.
bestdist = 0;
for (i = sec->linecount*2, probe = sec->lines; i > 0; i--)
{
int dist;
vertex_t *vert;
// Do calculations with only the upper bits, because the lower ones
// are all zero, and we would overflow for a lot of distances if we
// kept them around.
if (i & 1)
vert = (*probe++)->v2;
else
vert = (*probe)->v1;
dist = abs (((line->v1->y - vert->y) >> FRACBITS) * (line->dx >> FRACBITS) -
((line->v1->x - vert->x) >> FRACBITS) * (line->dy >> FRACBITS));
if (dist > bestdist)
{
bestdist = dist;
refvert = vert;
}
}
refsec = line->frontsector == sec ? line->backsector : line->frontsector;
vec3_t p, v1, v2, cross;
p[0] = FIXED2FLOAT (line->v1->x);
p[1] = FIXED2FLOAT (line->v1->y);
v1[0] = FIXED2FLOAT (line->dx);
v1[1] = FIXED2FLOAT (line->dy);
v2[0] = FIXED2FLOAT (refvert->x - line->v1->x);
v2[1] = FIXED2FLOAT (refvert->y - line->v1->y);
const secplane_t *refplane;
secplane_t *srcplane;
fixed_t srcheight, destheight;
refplane = (which == 0) ? &refsec->floorplane : &refsec->ceilingplane;
srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
srcheight = (which == 0) ? sec->floortexz : sec->ceilingtexz;
destheight = (which == 0) ? refsec->floortexz : refsec->ceilingtexz;
p[2] = FIXED2FLOAT (destheight);
v1[2] = 0;
v2[2] = FIXED2FLOAT (srcheight - destheight);
CrossProduct (v1, v2, cross);
VectorNormalize (cross);
// Fix backward normals
if ((cross[2] < 0 && which == 0) || (cross[2] > 0 && which == 1))
{
cross[0] = -cross[0];
cross[1] = -cross[1];
cross[2] = -cross[2];
}
srcplane->a = FLOAT2FIXED (cross[0]);
srcplane->b = FLOAT2FIXED (cross[1]);
srcplane->c = FLOAT2FIXED (cross[2]);
srcplane->ic = FLOAT2FIXED (1.f/cross[2]);
srcplane->d = -TMulScale16 (srcplane->a, line->v1->x,
srcplane->b, line->v1->y,
srcplane->c, destheight);
}
void P_SetSlopes ()
{
int i, s;
for (i = 0; i < numlines; i++)
{
if (lines[i].special == Plane_Align)
{
lines[i].special = 0;
lines[i].id = lines[i].args[2];
if (lines[i].backsector != NULL)
{
// args[0] is for floor, args[1] is for ceiling
//
// As a special case, if args[1] is 0,
// then args[0], bits 2-3 are for ceiling.
for (s = 0; s < 2; s++)
{
int bits = lines[i].args[s] & 3;
if (s == 1 && bits == 0)
bits = (lines[i].args[0] >> 2) & 3;
if (bits == 1) // align front side to back
P_AlignPlane (lines[i].frontsector, lines + i, s);
else if (bits == 2) // align back side to front
P_AlignPlane (lines[i].backsector, lines + i, s);
}
}
}
}
}
//
// killough 10/98:
//
// Rewritten to use faster algorithm.
//
// New procedure uses Bresenham-like algorithm on the linedefs, adding the
// linedef to each block visited from the beginning to the end of the linedef.
//
// The algorithm's complexity is on the order of nlines*total_linedef_length.
//
// Please note: This section of code is not interchangable with TeamTNT's
// code which attempts to fix the same problem.
static void P_CreateBlockMap ()
{
register int i;
fixed_t minx = FIXED_MAX, miny = FIXED_MAX,
maxx = FIXED_MIN, maxy = FIXED_MIN;
// First find limits of map
for (i = 0; i < numvertexes; i++)
{
if (vertexes[i].x < minx)
minx = vertexes[i].x;
else if (vertexes[i].x > maxx)
maxx = vertexes[i].x;
if (vertexes[i].y < miny)
miny = vertexes[i].y;
else if (vertexes[i].y > maxy)
maxy = vertexes[i].y;
}
minx >>= FRACBITS;
miny >>= FRACBITS;
maxx >>= FRACBITS;
maxy >>= 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.
struct bmap_t { int n, nalloc, *list; }; // blocklist structure
unsigned tot = bmapwidth * bmapheight; // size of blockmap
bmap_t *bmap = (bmap_t *)calloc(sizeof *bmap, tot); // array of blocklists
for (i = 0; i < numlines; i++)
{
// starting coordinates
int x = (lines[i].v1->x >> FRACBITS) - minx;
int y = (lines[i].v1->y >> FRACBITS) - miny;
// x-y deltas
int adx = lines[i].dx >> FRACBITS, dx = adx < 0 ? -1 : 1;
int ady = lines[i].dy >> FRACBITS, dy = ady < 0 ? -1 : 1;
// difference in preferring to move across y (>0) instead of x (<0)
int diff = !adx ? 1 : !ady ? -1 :
(((x >> MAPBTOFRAC) << MAPBTOFRAC) +
(dx > 0 ? MAPBLOCKUNITS-1 : 0) - x) * (ady = abs(ady)) * dx -
(((y >> MAPBTOFRAC) << MAPBTOFRAC) +
(dy > 0 ? MAPBLOCKUNITS-1 : 0) - y) * (adx = abs(adx)) * dy;
// starting block, and pointer to its blocklist structure
int b = (y >> MAPBTOFRAC)*bmapwidth + (x >> MAPBTOFRAC);
// ending block
int bend = (((lines[i].v2->y >> FRACBITS) - miny) >> MAPBTOFRAC) *
bmapwidth + (((lines[i].v2->x >> FRACBITS) - minx) >> MAPBTOFRAC);
// delta for pointer when moving across y
dy *= bmapwidth;
// deltas for diff inside the loop
adx <<= MAPBTOFRAC;
ady <<= MAPBTOFRAC;
// Now we simply iterate block-by-block until we reach the end block.
while ((unsigned) b < tot) // failsafe -- should ALWAYS be true
{
// Increase size of allocated list if necessary
if (bmap[b].n >= bmap[b].nalloc)
bmap[b].list = (int *)realloc(bmap[b].list,
(bmap[b].nalloc = bmap[b].nalloc ?
bmap[b].nalloc*2 : 8)*sizeof*bmap->list);
// Add linedef to end of list
bmap[b].list[bmap[b].n++] = i;
// If we have reached the last block, exit
if (b == bend)
break;
// Move in either the x or y direction to the next block
if (diff < 0)
diff += ady, b += dx;
else
diff -= adx, b += dy;
}
}
// 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.
{
int count = tot+6; // we need at least 1 word per block, plus reserved's
for (i = 0; (unsigned)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 = (int *)Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, 0);
}
// Now compress the blockmap.
{
int 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; (unsigned)i < tot; i++, bp++)
if (bp->n) // Non-empty blocklist
{
blockmaplump[blockmaplump[i] = ndx++] = 0; // Store index & header
do
blockmaplump[ndx++] = bp->list[--bp->n]; // Copy linedef list
while (bp->n);
blockmaplump[ndx++] = -1; // Store trailer
free(bp->list); // Free linedef list
}
else // Empty blocklist: point to reserved empty blocklist
blockmaplump[i] = tot;
free (bmap); // Free uncompressed blockmap
}
}
//
// P_LoadBlockMap
//
// killough 3/1/98: substantially modified to work
// towards removing blockmap limit (a wad limitation)
//
// killough 3/30/98: Rewritten to remove blockmap limit
//
CVAR (Bool, genblockmap, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
void P_LoadBlockMap (int lump)
{
int count;
if (genblockmap ||
(count = W_LumpLength(lump)/2) >= 0x10000 ||
Args.CheckParm("-blockmap") ||
W_LumpLength (lump) == 0)
{
DPrintf ("Generating BLOCKMAP lump\n");
P_CreateBlockMap ();
}
else
{
short *wadblockmaplump = (short *)W_CacheLumpNum (lump, PU_LEVEL);
int i;
blockmaplump = (int *)Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, 0);
// 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] = (DWORD)(SHORT(wadblockmaplump[2])) & 0xffff;
blockmaplump[3] = (DWORD)(SHORT(wadblockmaplump[3])) & 0xffff;
for (i = 4; i < count; i++)
{
short t = SHORT(wadblockmaplump[i]); // killough 3/1/98
blockmaplump[i] = t == -1 ? (DWORD)0xffffffff : (DWORD) t & 0xffff;
}
Z_Free (wadblockmaplump);
bmaporgx = blockmaplump[0]<<FRACBITS;
bmaporgy = blockmaplump[1]<<FRACBITS;
bmapwidth = blockmaplump[2];
bmapheight = blockmaplump[3];
}
// clear out mobj chains
count = sizeof(*blocklinks) * bmapwidth*bmapheight;
blocklinks = (AActor **)Z_Malloc (count, PU_LEVEL, 0);
memset (blocklinks, 0, count);
blockmap = blockmaplump+4;
}
//
// P_GroupLines
// Builds sector line lists and subsector sector numbers.
// Finds block bounding boxes for sectors.
//
void P_GroupLines ()
{
line_t** linebuffer;
int i;
int j;
int total;
line_t* li;
sector_t* sector;
DBoundingBox bbox;
bool flaggedNoFronts = false;
// look up sector number for each subsector
for (i = 0; i < numsubsectors; i++)
subsectors[i].sector = segs[subsectors[i].firstline].sidedef->sector;
// count number of lines in each sector
li = lines;
total = 0;
for (i = 0; i < numlines; i++, li++)
{
if (li->frontsector == NULL)
{
if (!flaggedNoFronts)
{
flaggedNoFronts = true;
Printf ("The following lines do not have a frontsector:\n");
}
Printf (" %d\n", i);
}
else
{
li->frontsector->linecount++;
total++;
}
if (li->backsector && li->backsector != li->frontsector)
{
li->backsector->linecount++;
total++;
}
}
if (flaggedNoFronts)
{
I_Error ("This map contains errors that must be fixed.\n");
}
// build line tables for each sector
linebuffer = (line_t **)Z_Malloc (total*sizeof(line_t *), PU_LEVEL, 0);
sector = sectors;
for (i = 0; i < numsectors; i++, sector++)
{
bbox.ClearBox ();
if (sector->linecount == 0)
{
Printf ("Sector %i (tag %i) has no lines\n", i, sector->tag);
// 0 the sector's tag so that no specials can use it
sector->tag = 0;
}
else
{
sector->lines = linebuffer;
li = lines;
for (j = 0; j < numlines; j++, li++)
{
if (li->frontsector == sector || li->backsector == sector)
{
*linebuffer++ = li;
bbox.AddToBox (li->v1->x, li->v1->y);
bbox.AddToBox (li->v2->x, li->v2->y);
}
}
if (linebuffer - sector->lines != sector->linecount)
{
I_Error ("P_GroupLines: miscounted");
}
}
// set the soundorg to the middle of the bounding box
sector->soundorg[0] = (bbox.Right()+bbox.Left())/2;
sector->soundorg[1] = (bbox.Top()+bbox.Bottom())/2;
#if 0
int block;
// adjust bounding box to map blocks
block = (bbox.Top()-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT;
block = block >= bmapheight ? bmapheight-1 : block;
//sector->blockbox.Top()=block;
block = (bbox.Bottom()-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT;
block = block < 0 ? 0 : block;
//sector->blockbox.Bottom()=block;
block = (bbox.Right()-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT;
block = block >= bmapwidth ? bmapwidth-1 : block;
//sector->blockbox.Right()=block;
block = (bbox.Left()-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT;
block = block < 0 ? 0 : block;
//sector->blockbox.Left()=block;
#endif
}
}
//
// [RH] P_LoadBehavior
//
void P_LoadBehavior (int lumpnum)
{
byte *behavior = (byte *)W_CacheLumpNum (lumpnum, PU_LEVEL);
level.behavior = new FBehavior (behavior, lumpinfo[lumpnum].size);
if (!level.behavior->IsGood ())
{
delete level.behavior;
level.behavior = NULL;
}
}
// Hash the sector tags across the sectors and linedefs.
static void P_InitTagLists ()
{
int i;
for (i=numsectors; --i>=0; ) // Initially make all slots empty.
sectors[i].firsttag = -1;
for (i=numsectors; --i>=0; ) // Proceed from last to first sector
{ // so that lower sectors appear first
int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func
sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain
sectors[j].firsttag = i;
}
// killough 4/17/98: same thing, only for linedefs
for (i=numlines; --i>=0; ) // Initially make all slots empty.
lines[i].firstid = -1;
for (i=numlines; --i>=0; ) // Proceed from last to first linedef
{ // so that lower linedefs appear first
int j = (unsigned) lines[i].id % (unsigned) numlines; // Hash func
lines[i].nextid = lines[j].firstid; // Prepend linedef to chain
lines[j].firstid = i;
}
}
//
// P_SetupLevel
//
extern AActor *bodyquesize[];
extern polyblock_t **PolyBlockMap;
// [NightFang] - added
void CT_Stop(void);
// [RH] position indicates the start spot to spawn at
void P_SetupLevel (char *lumpname, int position)
{
int i, lumpnum;
level.total_monsters = level.total_items = level.total_secrets =
level.killed_monsters = level.found_items = level.found_secrets =
wminfo.maxfrags = 0;
wminfo.partime = 180;
// [NightFang] - stop all chat
CT_Stop();
if (!savegamerestore)
{
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].killcount = players[i].secretcount
= players[i].itemcount = players[i].deathcount = 0;
}
}
for (i = 0; i < MAXPLAYERS; ++i)
{
// [NightFang] - reset time
players[i].minutesingame = 0;
players[i].mo = NULL;
}
// [RH] Set default scripted translation colors
for (i = 0; i < 256; ++i)
{
translationtables[TRANSLATION_LevelScripted][i] = i;
}
for (i = 1; i < MAX_ACS_TRANSLATIONS; ++i)
{
memcpy (&translationtables[TRANSLATION_LevelScripted][i*256],
translationtables[TRANSLATION_LevelScripted], 256);
}
// Initial height of PointOfView will be set by player think.
players[consoleplayer].viewz = 1;
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ();
// [RH] Clear all ThingID hash chains.
AActor::ClearTIDHashes ();
// [RH] clear out the mid-screen message
C_MidPrint (NULL);
PolyBlockMap = NULL;
DThinker::DestroyAllThinkers ();
Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
NormalLight.Next = NULL; // [RH] Z_FreeTags frees all the custom colormaps
// find map num
level.lumpnum = lumpnum = W_GetNumForName (lumpname);
// [RH] Check if this map is Hexen-style.
// LINEDEFS and THINGS need to be handled accordingly.
HasBehavior = W_CheckLumpName (lumpnum+ML_BEHAVIOR, "BEHAVIOR");
// note: most of this ordering is important
// [RH] Load in the BEHAVIOR lump
if (level.behavior != NULL)
{
delete level.behavior;
level.behavior = NULL;
}
if (HasBehavior)
{
P_LoadBehavior (lumpnum+ML_BEHAVIOR);
}
P_LoadVertexes (lumpnum+ML_VERTEXES);
P_LoadSectors (lumpnum+ML_SECTORS);
P_LoadSideDefs (lumpnum+ML_SIDEDEFS);
if (!HasBehavior)
P_LoadLineDefs (lumpnum+ML_LINEDEFS);
else
P_LoadLineDefs2 (lumpnum+ML_LINEDEFS); // [RH] Load Hexen-style linedefs
P_LoadSideDefs2 (lumpnum+ML_SIDEDEFS);
P_FinishLoadingLineDefs ();
P_LoopSidedefs ();
P_LoadBlockMap (lumpnum+ML_BLOCKMAP);
P_LoadSubsectors (lumpnum+ML_SSECTORS);
P_LoadNodes (lumpnum+ML_NODES);
P_LoadSegs (lumpnum+ML_SEGS);
rejectmatrix = (byte *)W_CacheLumpNum (lumpnum+ML_REJECT, PU_LEVEL);
{
// [RH] Scan the rejectmatrix and see if it actually contains anything
int i, end = (W_LumpLength (lumpnum+ML_REJECT)-(sizeof(int)-1)) / sizeof(int);
for (i = 0; i < end; i++)
{
if (((int *)rejectmatrix)[i])
{
rejectempty = false;
break;
}
}
if (i >= end)
{
DPrintf ("Reject matrix is empty\n");
rejectempty = true;
}
}
P_GroupLines ();
bodyqueslot = 0;
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one. The actors belonging to these corpses
// are cleared in the normal freeing of zoned memory between maps, so all
// we have to do here is clear the pointers to them.
for (i = 0; i < BODYQUESIZE; i++)
bodyque[i] = NULL;
po_NumPolyobjs = 0;
deathmatchstarts.Clear ();
P_SetSlopes ();
P_InitTagLists(); // killough 1/30/98: Create xref tables for tags
if (!HasBehavior)
P_LoadThings (lumpnum+ML_THINGS);
else
P_LoadThings2 (lumpnum+ML_THINGS, position); // [RH] Load Hexen-style things
if (!HasBehavior)
P_TranslateTeleportThings (); // [RH] Assign teleport destination TIDs
PO_Init (); // Initialize the polyobjs
// if deathmatch, randomly spawn the active players
if (deathmatch)
{
for (i=0 ; i<MAXPLAYERS ; i++)
{
if (playeringame[i])
{
players[i].mo = NULL;
G_DeathMatchSpawnPlayer (i);
}
}
}
// set up world state
P_SpawnSpecials ();
// build subsector connect matrix
// UNUSED P_ConnectSubsectors ();
R_OldBlend = ~0;
// preload graphics
if (precache)
R_PrecacheLevel ();
P_ResetSightCounters (true);
//Printf ("free memory: 0x%x\n", Z_FreeMemory());
}
//
// P_Init
//
void P_Init ()
{
P_InitEffects (); // [RH]
P_InitPicAnims ();
P_InitSwitchList ();
P_InitTerrainTypes ();
R_InitSprites ();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment