Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
117 results
Select Git revision
Show changes
Commits on Source (1)
...@@ -640,10 +640,37 @@ linedeftypes ...@@ -640,10 +640,37 @@ linedeftypes
prefix = "(63)"; prefix = "(63)";
} }
540
{
title = "Floor Friction";
prefix = "(540)";
}
}
tagging
{
title = "Tagging";
94
{
title = "Flood Sectors With Tag";
prefix = "(94)";
flags8192text = "[13] Use front side offsets";
flags32768text = "[15] Use back side offsets";
}
95
{
title = "Flood Sectors Blocker";
prefix = "(95)";
}
96 96
{ {
title = "Apply Tag to Tagged Sectors"; title = "Apply Tag to Other Sectors";
prefix = "(96)"; prefix = "(96)";
flags256text = "[8] Targets are sector indices";
flags512text = "[9] Don't apply tag from sector";
flags1024text = "[10] Offsets are target tags"; flags1024text = "[10] Offsets are target tags";
flags8192text = "[13] Use front side offsets"; flags8192text = "[13] Use front side offsets";
flags32768text = "[15] Use back side offsets"; flags32768text = "[15] Use back side offsets";
...@@ -672,11 +699,142 @@ linedeftypes ...@@ -672,11 +699,142 @@ linedeftypes
flags8192text = "[13] Use front side offsets"; flags8192text = "[13] Use front side offsets";
flags32768text = "[15] Use back side offsets"; flags32768text = "[15] Use back side offsets";
} }
}
540 macros
{
title = "Macros";
70
{ {
title = "Floor Friction"; title = "Expand Macro - First Line";
prefix = "(540)"; prefix = "(70)";
}
71
{
title = "Expand Macro - Continued";
prefix = "(71)";
}
72
{
title = "Define Macro";
prefix = "(72)";
}
73
{
title = "Macro Parameters - Linedef";
prefix = "(73)";
}
74
{
title = "Macro Parameters - Sidedef";
prefix = "(74)";
flags8192text = "[13] Back sidedef";
}
75
{
title = "Macro Parameters - Sector";
prefix = "(75)";
flags8192text = "[13] Back sector";
flags16384text = "[14] Unify specials";
}
76
{
title = "Macro Parameters - Vertex";
prefix = "(76)";
}
77
{
title = "Macro Parameters - Thing";
prefix = "(77)";
}
78
{
title = "Macro Variables - New Tags";
prefix = "(78)";
}
79
{
title = "Macro Variables - Calculation";
prefix = "(79)";
flags8192text = "[13] Single calculation";
}
80
{
title = "Macro Proxy - Linedef";
prefix = "(80)";
flags8text = "[3] Set special";
flags16text = "[4] Set tag";
flags32text = "[5] Change flags";
flags256text = "[8] Set first sidedef";
flags512text = "[9] Set second sidedef";
flags1024text = "[10] Set first vertex";
flags2048text = "[11] Set second vertex";
flags32768text = "[15] Target linedef by index";
}
81
{
title = "Macro Proxy - Sidedef";
prefix = "(81)";
flags8text = "[3] Set X offset";
flags16text = "[4] Set Y offset";
flags32text = "[5] Set upper texture";
flags64text = "[6] Set middle texture";
flags128text = "[7] Set lower texture";
flags256text = "[8] Set sector index";
flags8192text = "[13] Back sidedef";
flags32768text = "[15] Target sidedef by index";
}
82
{
title = "Macro Proxy - Sector";
prefix = "(82)";
flags8text = "[3] Set floor height";
flags16text = "[4] Set ceiling height";
flags32text = "[5] Set floor flat";
flags64text = "[6] Set ceiling flat";
flags128text = "[7] Set special (group 1)";
flags256text = "[8] Set light level";
flags512text = "[9] Set tag";
flags1024text = "[10] Set special group 2";
flags2048text = "[11] Set special group 3";
flags4096text = "[12] Set special group 4";
flags8192text = "[13] Back sector";
flags16384text = "[14] Unify specials";
flags32768text = "[15] Target sector by index";
}
83
{
title = "Macro Proxy - Vertex";
prefix = "(83)";
flags8text = "[3] Change X position";
flags16text = "[4] Change Y position";
flags2048text = "[11] First vertex";
flags4096text = "[12] Second vertex";
flags8192text = "[13] Absolute X position";
flags16384text = "[14] Absolute Y position";
flags32768text = "[15] Target vertex by index";
}
84
{
title = "Macro Proxy - Thing";
prefix = "(84)";
flags8text = "[3] Change X position";
flags16text = "[4] Change Y position";
flags32text = "[5] Change flags";
flags64text = "[6] Copy thing";
flags128text = "[7] New thing";
flags256text = "[8] Set angle";
flags512text = "[9] Set Z position";
flags1024text = "[10] Set parameter";
flags2048text = "[11] Set type";
flags8192text = "[13] Absolute X position";
flags16384text = "[14] Absolute Y position";
flags32768text = "[15] Target thing by index";
} }
} }
...@@ -4822,6 +4980,14 @@ thingtypes ...@@ -4822,6 +4980,14 @@ thingtypes
title = "Object Angle Anchor"; title = "Object Angle Anchor";
sprite = "internal:view"; sprite = "internal:view";
} }
759
{
title = "Macro Thing Helper";
angletext = "Tag";
fixedrotation = 1;
}
760 760
{ {
title = "PolyObject Anchor"; title = "PolyObject Anchor";
......
...@@ -43,6 +43,7 @@ p_map.c ...@@ -43,6 +43,7 @@ p_map.c
p_maputl.c p_maputl.c
p_mobj.c p_mobj.c
p_polyobj.c p_polyobj.c
p_preprocess.c
p_saveg.c p_saveg.c
p_setup.c p_setup.c
p_sight.c p_sight.c
......
...@@ -63,15 +63,11 @@ enum ...@@ -63,15 +63,11 @@ enum
// Do not use bit five or after, as they are used for object z-offsets. // Do not use bit five or after, as they are used for object z-offsets.
#if defined(_MSC_VER)
#pragma pack(1)
#endif
// A single Vertex. // A single Vertex.
typedef struct typedef struct
{ {
INT16 x, y; INT16 x, y;
}ATTRPACK mapvertex_t; } ATTRPACK mapvertex_t;
// A SideDef, defining the visual appearance of a wall, // A SideDef, defining the visual appearance of a wall,
// by setting textures and offsets. // by setting textures and offsets.
...@@ -80,15 +76,15 @@ typedef struct ...@@ -80,15 +76,15 @@ typedef struct
INT16 textureoffset, rowoffset; INT16 textureoffset, rowoffset;
char toptexture[8], bottomtexture[8], midtexture[8]; char toptexture[8], bottomtexture[8], midtexture[8];
// Front sector, towards viewer. // Front sector, towards viewer.
INT16 sector; UINT16 sector;
} ATTRPACK mapsidedef_t; } ATTRPACK mapsidedef_t;
// A LineDef, as used for editing, and as input // A LineDef, as used for editing, and as input
// to the BSP builder. // to the BSP builder.
typedef struct typedef struct
{ {
INT16 v1, v2; UINT16 v1, v2;
INT16 flags; UINT16 flags;
INT16 special; INT16 special;
INT16 tag; INT16 tag;
// sidenum[1] will be 0xffff if one sided // sidenum[1] will be 0xffff if one sided
...@@ -154,6 +150,16 @@ typedef struct ...@@ -154,6 +150,16 @@ typedef struct
INT16 tag; INT16 tag;
} ATTRPACK mapsector_t; } ATTRPACK mapsector_t;
// Actual thing structure found in binary maps; `mapthing_t` has been
// contaminated with other stuff.
typedef struct
{
INT16 x, y;
INT16 angle;
UINT16 type;
UINT16 options;
} ATTRPACK raw_mapthing_t;
// SubSector, as generated by BSP. // SubSector, as generated by BSP.
typedef struct typedef struct
{ {
...@@ -181,7 +187,7 @@ typedef struct ...@@ -181,7 +187,7 @@ typedef struct
typedef struct typedef struct
{ {
// Partition line from (x,y) to x+dx,y+dy) // Partition line from (x,y) to (x+dx,y+dy)
INT16 x, y; INT16 x, y;
INT16 dx, dy; INT16 dx, dy;
...@@ -192,10 +198,6 @@ typedef struct ...@@ -192,10 +198,6 @@ typedef struct
UINT16 children[2]; UINT16 children[2];
} ATTRPACK mapnode_t; } ATTRPACK mapnode_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
#define NUMMAPTHINGARGS 6 #define NUMMAPTHINGARGS 6
#define NUMMAPTHINGSTRINGARGS 2 #define NUMMAPTHINGSTRINGARGS 2
...@@ -217,6 +219,8 @@ typedef struct ...@@ -217,6 +219,8 @@ typedef struct
} mapthing_t; } mapthing_t;
#define ZSHIFT 4 #define ZSHIFT 4
#define PARAMSHIFT 12
#define TYPEMASK 0xFFF
#define NUMMAPS 1035 #define NUMMAPS 1035
......
...@@ -692,11 +692,20 @@ void LUA_DumpFile(const char *filename) ...@@ -692,11 +692,20 @@ void LUA_DumpFile(const char *filename)
} }
#endif #endif
fixed_t LUA_EvalMath(const char *word) fixed_t LUA_EvalMathEx(const char *math, boolean soc, boolean is_expr,
lua_eval_math_callback_t setter, lua_eval_math_callback_t getter,
void *callback_data)
{ {
lua_State *L = NULL; lua_State *L = NULL;
char buf[1024], *b;
const char *p; size_t math_idx = 0;
size_t math_len = strlen(math);
// Make sure there's space for "return " and the null terminator, so add 8.
size_t buf_size = math_len + 8;
char *buf = Z_Malloc(buf_size, PU_LUA, NULL);
size_t buf_idx = 0;
fixed_t res = 0; fixed_t res = 0;
// make a new state so SOC can't interefere with scripts // make a new state so SOC can't interefere with scripts
...@@ -709,34 +718,55 @@ fixed_t LUA_EvalMath(const char *word) ...@@ -709,34 +718,55 @@ fixed_t LUA_EvalMath(const char *word)
lua_pushboolean(L, true); lua_pushboolean(L, true);
lua_call(L, 1, 0); lua_call(L, 1, 0);
// change ^ into ^^ for Lua. // If it exists, call the callback so it can add variables.
strcpy(buf, "return "); if (setter != NULL)
b = buf+strlen(buf); setter(L, callback_data);
for (p = word; *p && b < &buf[1022]; p++)
{ if (is_expr) {
*b++ = *p; strcpy(buf, "return ");
if (*p == '^') buf_idx += 7;
*b++ = '^'; buf_size += 7;
} }
*b = '\0';
// Copy characters over, adjusting for SOC if necessary.
for (; math_idx < math_len; math_idx++, buf_idx++) {
char c = math[math_idx];
buf[buf_idx] = c;
if (soc && c == '^') {
buf = Z_Realloc(buf, ++buf_size, PU_LUA, NULL);
buf[++buf_idx] = '^';
}
}
buf[buf_idx] = '\0';
// eval string. // eval string.
lua_settop(L, 0); lua_settop(L, 0);
if (luaL_dostring(L, buf)) if (luaL_dostring(L, buf)) {
{ const char *p = lua_tostring(L, -1);
p = lua_tostring(L, -1);
while (*p++ != ':' && *p) ; while (*p++ != ':' && *p) ;
p += 3; // "1: " p += 3; // "1: "
CONS_Alert(CONS_WARNING, "%s\n", p); CONS_Alert(CONS_WARNING, "%s\n", p);
} else {
if (is_expr)
res = lua_tointeger(L, -1);
// If it exists, call the other callback so it can examine variables.
if (getter != NULL)
getter(L, callback_data);
} }
else
res = lua_tointeger(L, -1);
// clean up and return. // clean up and return.
lua_close(L); lua_close(L);
Z_Free(buf);
return res; return res;
} }
fixed_t LUA_EvalMath(const char *math)
{
return LUA_EvalMathEx(math, true, true, NULL, NULL, NULL);
}
// Takes a pointer, any pointer, and a metatable name // Takes a pointer, any pointer, and a metatable name
// Creates a userdata for that pointer with the given metatable // Creates a userdata for that pointer with the given metatable
// Pushes it to the stack and stores it in the registry. // Pushes it to the stack and stores it in the registry.
......
...@@ -49,7 +49,11 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); ...@@ -49,7 +49,11 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults);
#ifdef LUA_ALLOW_BYTECODE #ifdef LUA_ALLOW_BYTECODE
void LUA_DumpFile(const char *filename); void LUA_DumpFile(const char *filename);
#endif #endif
fixed_t LUA_EvalMath(const char *word); typedef void (*lua_eval_math_callback_t)(lua_State *L, void *data);
fixed_t LUA_EvalMathEx(const char *math, boolean soc, boolean is_expr,
lua_eval_math_callback_t setter, lua_eval_math_callback_t getter,
void *callback_data);
fixed_t LUA_EvalMath(const char *math);
void LUA_Step(void); void LUA_Step(void);
void LUA_Archive(void); void LUA_Archive(void);
void LUA_UnArchive(void); void LUA_UnArchive(void);
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2021 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include "p_preprocess.h"
#include "lua_script.h"
#include "p_spec.h"
#include "r_state.h"
#include "r_textures.h"
#include "z_zone.h"
// TODO: Lua hooks (PreprocessMap, MacroCalculation)
// TODO: Lua SetSecSpecial, R_IsBlankTexture, p_preprocess.h stuff
// TODO: Linedef 94-96
/** Initialize a map pack to its empty state.
*
* \param pack The pack to initialize to empty.
*/
void P_NewEmptyMapPack(mappack_t *pack)
{
memset(pack, 0, sizeof(*pack));
// See `P_GetNewTag` for the importance of this value.
pack->last_tag = 32000;
}
/** Allocates the values inside a mappack_t. Each value in the pack is copied from the
* lump because you can't realloc and edit the lump data in place since that will
* clobber it for the next time the map is loaded.
*
* \param pack A pointer to the mappack_t to fill
* \param virtvertices The VERTEXES lump
* \param virtsidedefs The SIDEDEFS lump
* \param virtlinedefs The LINEDEFS lump
* \param virtsectors The SECTORS lump
* \param virtthings The THINGS lump
*/
void P_NewMapPackFromLumps(mappack_t *pack,
virtlump_t *virtvertices,
virtlump_t *virtsidedefs,
virtlump_t *virtlinedefs,
virtlump_t *virtsectors,
virtlump_t *virtthings)
{
P_NewEmptyMapPack(pack);
P_AppendVertices(pack, virtvertices->size / sizeof(mapvertex_t),
(mapvertex_t *)virtvertices->data);
P_AppendSidedefs(pack, virtsidedefs->size / sizeof(mapsidedef_t),
(mapsidedef_t *)virtsidedefs->data);
P_AppendLinedefs(pack, virtlinedefs->size / sizeof(maplinedef_t),
(maplinedef_t *)virtlinedefs->data);
P_AppendSectors(pack, virtsectors->size / sizeof(mapsector_t),
(mapsector_t *)virtsectors->data);
P_AppendThings(pack, virtthings->size / sizeof(raw_mapthing_t),
(raw_mapthing_t *)virtthings->data);
}
/** Frees the values inside the mappack_t.
*
* \param pack The pack to free
*/
void P_FreeMapPack(mappack_t *pack)
{
Z_Free(pack->vertices);
Z_Free(pack->sidedefs);
Z_Free(pack->linedefs);
Z_Free(pack->sectors);
Z_Free(pack->things);
memset(pack, 0, sizeof(*pack));
}
/** Returns a new tag to be used that, for all intents and purposes, is likely unique.
*
* \param pack The map pack the tag will be gotten from and used for.
*/
INT16 P_GetNewTag(mappack_t *pack)
{
/* It's almost impossible to generate totally unique tags what with multitagging, things
* storing tags in angles, tags stored in texture offsets, etc., so an approximation
* must be used instead. This method is to start at a high tag just at the start of the
* turret and PolyObject trigger linedef reserved tags (i.e. 32000) and subtract one for
* each consecutive tag that is needed. It is unlikely that maps will need tags so high up
* as this, so these are probably going to be safe to use.
*/
return --pack->last_tag;
}
/** Adds vertices to the array of map vertices and optionally copies new vertices to the space.
*
* \param pack The map containing the list of vertices.
* \param new Number of vertices to add.
* \param fill If not NULL, array of vertices to copy to the new space. Must have
* `new` vertices and must not be part of `pack`.
* \return The first of the new vertices.
*/
mapvertex_t *P_AppendVertices(mappack_t *pack, size_t new, const mapvertex_t *fill)
{
size_t old_num_vertices = pack->num_vertices;
pack->num_vertices += new;
if (pack->num_vertices > UINT16_MAX)
I_Error("P_AppendVertices: Too many vertices (%u)", (unsigned)pack->num_vertices);
pack->vertices = Z_Realloc(pack->vertices, pack->num_vertices * sizeof(*pack->vertices),
PU_LEVEL, NULL);
if (fill != NULL) {
size_t i;
for (i = 0; i < new; i++)
pack->vertices[i + old_num_vertices] = fill[i];
}
return &pack->vertices[old_num_vertices];
}
/** Adds sidedefs to the array of map sidedefs and optionally copies new sidedefs to the space.
*
* \param pack The map containing the list of sidedefs.
* \param new Number of sidedefs to add.
* \param fill If not NULL, array of sidedefs to copy to the new space. Must have
* `new` sidedefs and must not be part of `pack`.
* \return The first of the new sidedefs.
*/
mapsidedef_t *P_AppendSidedefs(mappack_t *pack, size_t new, const mapsidedef_t *fill)
{
size_t old_num_sidedefs = pack->num_sidedefs;
pack->num_sidedefs += new;
if (pack->num_sidedefs >= UINT16_MAX) // >= because 0xFFFF is not a valid sidedef
I_Error("P_AppendSidedefs: Too many sidedefs (%u)", (unsigned)pack->num_sidedefs);
pack->sidedefs = Z_Realloc(pack->sidedefs, pack->num_sidedefs * sizeof(*pack->sidedefs),
PU_LEVEL, NULL);
if (fill != NULL) {
size_t i;
for (i = 0; i < new; i++)
pack->sidedefs[i + old_num_sidedefs] = fill[i];
}
return &pack->sidedefs[old_num_sidedefs];
}
/** Adds linedefs to the array of map linedefs and optionally copies new linedefs to the space.
*
* \param pack The map containing the list of linedefs.
* \param new Number of linedefs to add.
* \param fill If not NULL, array of linedefs to copy to the new space. Must have
* `new` linedefs and must not be part of `pack`.
* \return The first of the new linedefs.
*/
maplinedef_t *P_AppendLinedefs(mappack_t *pack, size_t new, const maplinedef_t *fill)
{
size_t old_num_linedefs = pack->num_linedefs;
pack->num_linedefs += new;
if (pack->num_linedefs > UINT16_MAX)
I_Error("P_AppendLinedefs: Too many linedefs (%u)", (unsigned)pack->num_linedefs);
pack->linedefs = Z_Realloc(pack->linedefs, pack->num_linedefs * sizeof(*pack->linedefs),
PU_LEVEL, NULL);
if (fill != NULL) {
size_t i;
for (i = 0; i < new; i++)
pack->linedefs[i + old_num_linedefs] = fill[i];
}
return &pack->linedefs[old_num_linedefs];
}
/** Adds sectors to the array of map sectors and optionally copies new sectors to the space.
*
* \param pack The map containing the list of sectors.
* \param new Number of sectors to add.
* \param fill If not NULL, array of sectors to copy to the new space. Must have
* `new` sectors and must not be part of `pack`.
* \return The first of the new sectors.
*/
mapsector_t *P_AppendSectors(mappack_t *pack, size_t new, const mapsector_t *fill)
{
size_t old_num_sectors = pack->num_sectors;
pack->num_sectors += new;
if (pack->num_sectors > UINT16_MAX)
I_Error("P_AppendSectors: Too many sectors (%u)", (unsigned)pack->num_sectors);
pack->sectors = Z_Realloc(pack->sectors, pack->num_sectors * sizeof(*pack->sectors),
PU_LEVEL, NULL);
if (fill != NULL) {
size_t i;
for (i = 0; i < new; i++)
pack->sectors[i + old_num_sectors] = fill[i];
}
return &pack->sectors[old_num_sectors];
}
/** Adds things to the array of map things and optionally copies new things to the space.
*
* \param pack The map containing the list of things.
* \param new Number of things to add.
* \param fill If not NULL, array of things to copy to the new space. Must have
* `new` things and must not be part of `pack`.
* \return The first of the new things.
*/
raw_mapthing_t *P_AppendThings(mappack_t *pack, size_t new, const raw_mapthing_t *fill)
{
size_t old_num_things = pack->num_things;
pack->num_things += new;
if (pack->num_things > UINT16_MAX)
I_Error("P_AppendThings: Too many things (%u)", (unsigned)pack->num_things);
pack->things = Z_Realloc(pack->things, pack->num_things * sizeof(*pack->things),
PU_LEVEL, NULL);
if (fill != NULL) {
size_t i;
for (i = 0; i < new; i++)
pack->things[i + old_num_things] = fill[i];
}
return &pack->things[old_num_things];
}
/** Tests whether two linedefs share a vertex and returns which vertex is shared,
* or zero if they are the same linedef or do not share a vertex.
*
* \param first The first linedef to check shared vertices with.
* \param second The second linedef to check shared vertices with.
* \return Zero if the same linedef or not shared, or the vertex index of `first`
* (i.e. 1 for `first->v1` and 2 for `first->v2`).
*/
int P_HasSharedVertex(maplinedef_t *first, maplinedef_t *second)
{
if (first == second)
return 0;
if (first->v1 == second->v1 || first->v1 == second->v2)
return 1;
if (first->v2 == second->v1 || first->v2 == second->v2)
return 2;
return 0;
}
/** Gets the angle of a linedef.
*
* \param linedef The linedef to get the angle of.
* \param pack The map containing the linedef and vertices.
* \return The angle.
*/
angle_t P_GetLinedefAngle(maplinedef_t *linedef, mappack_t *pack)
{
mapvertex_t *v1 = &pack->vertices[linedef->v1];
mapvertex_t *v2 = &pack->vertices[linedef->v2];
return R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
}
/** Gets the direction-independent angle of a linedef, i.e. returns the angle
* ANGLE_90 if the linedef's angle is ANGLE_270 because the direction is
* irrelevant.
*
* \param linedef The linedef to get the angle of.
* \param pack The map containing the linedef and vertices.
* \return The direction-independent angle.
*/
angle_t P_GetBidiLinedefAngle(maplinedef_t *linedef, mappack_t *pack)
{
angle_t angle = P_GetLinedefAngle(linedef, pack);
if (angle >= ANGLE_180)
angle -= ANGLE_180;
return angle;
}
/** Searches for a neighboring linedef that is connected to this one by a vertex. The
* linedef must be in a specified range of linedef special types.
*
* If there are multiple potential linedefs connected to this one (excluding the
* previous one), it is an error. This means that branching and looping are not allowed.
*
* It can easily be used in a loop, like so:
*
* maplinedef_t *linedef = &pack->linedefs[i];
* maplinedef_t *previous = NULL;
* while (P_FindNextConnectedLinedef(&linedef, &previous, 73, 79, pack)) {
* // Do thing
* }
*
* To get the shared vertex on the new linedef instead of on the original linedef, use
* P_HasSharedVertex. For instance:
*
* int previous_vertex = P_FindNextConnectedLinedef(&linedef, &previous, 73, 79, pack);
* if (previous_vertex) {
* int linedef_vertex = P_HasSharedVertex(linedef, previous);
* // Do thing
* }
*
* \param[in,out] linedef The linedef to find a neighbor for. It is set to the new
* linedef if one is found.
* \param[in,out] previous When searching for a linedef, do not include this one as
* it was a previous neighbor. It may point to NULL. It is
* set to the value of `linedef` if a neighbor is found.
* \param special_low The inclusive lower bound of the range of specials the
* linedef must be.
* \param special_high The inclusive upper bound of the range of specials the
* linedef must be.
* \param pack The map containing the linedefs.
* \return Zero if no suitable neighbor was found. If found, returns one or two, which
* is the vertex on the original linedef that is shared with the new one.
*/
int P_FindNextConnectedLinedef(maplinedef_t **linedef, maplinedef_t **previous,
INT16 special_low, INT16 special_high, mappack_t *pack)
{
// Back these up because they will be changed during the lifespan of the loop.
maplinedef_t *use_linedef = *linedef;
maplinedef_t *use_previous = *previous;
int final_vertex = 0;
boolean found = false;
size_t i;
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *testing = &pack->linedefs[i];
int current_vertex;
if (testing == use_linedef || testing == use_previous ||
testing->special < special_low || testing->special > special_high)
continue;
current_vertex = P_HasSharedVertex(use_linedef, testing);
if (current_vertex) {
// Prevent ambiguities with branching linedefs as well as potential infinite
// loops with circularly-looped linedefs.
if (found) {
I_Error("P_FindNextConnectedLinedef: Multiple possible candidates for "
"linedef connected to %u", (unsigned)(use_linedef - pack->linedefs));
}
*previous = use_linedef;
*linedef = testing;
final_vertex = current_vertex;
found = true;
}
}
return final_vertex;
}
/** Searches for a thing in the map that has the specified type and angle. If
* none are found or there are multiple possibilities, it is an error.
*
* \param type The type of the thing.
* \param angle The angle (tag) the thing must have.
* \param pack The map containing the things.
* \return The thing with the type and angle.
*/
raw_mapthing_t *P_FindThingByTypeAndAngle(INT16 type, INT16 angle, mappack_t *pack)
{
size_t i;
raw_mapthing_t *found_thing = NULL;
for (i = 0; i < pack->num_things; i++) {
raw_mapthing_t *thing = &pack->things[i];
if ((thing->type & TYPEMASK) == type && thing->angle == angle) {
if (found_thing != NULL) {
I_Error("P_FindThingByTypeAndAngle: Multiple possible candidates for "
"thing with type %d and angle %d found", type, angle);
}
found_thing = thing;
}
}
if (found_thing == NULL) {
I_Error("P_FindThingByTypeAndAngle: No thing with type %d and angle %d found",
type, angle);
}
return found_thing;
}
/** Searches for the linedef in the map that shares a vertex with and has the
* closest direction-independent angle to the linedef provided. If multiple have
* the same angle or no linedefs are are connected, it is an error.
*
* \param linedef The linedef to search for a close-angle neighbor for.
* \param pack The map containing the linedef and vertices.
* \return The linedef with the closest angle.
*/
maplinedef_t *P_GetClosestAngleLinedef(maplinedef_t *linedef, mappack_t *pack)
{
size_t i;
// The linedef and angle of the closest linedef found so far.
INT32 smallest_angle = ANGLE_180;
maplinedef_t *closest_linedef = NULL;
INT32 main_angle = P_GetBidiLinedefAngle(linedef, pack);
boolean multiple_found = false;
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *testing = &pack->linedefs[i];
INT32 diff_angle;
if (!P_HasSharedVertex(linedef, testing))
continue;
diff_angle = abs((INT32)P_GetBidiLinedefAngle(testing, pack) - main_angle);
if (diff_angle == smallest_angle) {
multiple_found = true;
} else if (diff_angle < smallest_angle) {
smallest_angle = diff_angle;
closest_linedef = testing;
multiple_found = false;
}
}
if (closest_linedef == NULL) {
I_Error("P_GetClosestAngleLinedef: No connected linedef found for %u",
(unsigned)(linedef - pack->linedefs));
}
if (multiple_found) {
I_Error("P_GetClosestAngleLinedef: Multiple possible candidates for linedef "
"with closest angle to %u", (unsigned)(linedef - pack->linedefs));
}
return closest_linedef;
}
/** A single variable and values in a single macro expansion.
*
* Although variables come from either an integer or texture, they are also stored
* in the other field as well so it can be used as either. For instance, some variable
* may come from a sidedef texture, but be used as an integer in the macro body.
*/
typedef struct {
//! The number identifier of this variable.
INT16 id;
//! Whether this variable was set from an integer or a texture. Exists for Lua
//! to decide whether to make the variable a number or a string.
boolean is_integer;
//! The integer representation of this variable. It is an INT32 to avoid conflict
//! between INT16 and UINT16 in different `map<geometry>_t` fields.
INT32 integer;
//! The texture representation of this variable.
char texture[8];
} macro_var_t;
/** Contains an array of variables.
*/
typedef struct {
macro_var_t *vars;
size_t num_vars;
} macro_varlist_t;
struct macro_state_s;
/** Structure containing a macro definition, complete with tag, definition linedefs,
* and geometry that will be copied when the macro is expanded (the macro body).
*
* All geometry is stored as indices instead of pointers so that they stay valid
* after reallocations.
*/
typedef struct {
//! The tag of the macro.
INT16 tag;
//! Pointer to the global macro state.
struct macro_state_s *state;
//! Linedefs that contain the "Macro Variables" linedefs. "Define Macro" is not
//! included as it is only computed once.
size_t *def_linedefs;
size_t num_def_linedefs;
//! Linedef geometry exclusively touching macro body sectors.
size_t *linedefs;
size_t num_linedefs;
//! Number of sidedefs in the macro body. Although sidedefs are copied directly
//! from the linedefs, the number of sidedefs is stored for immediate reallocation.
size_t num_sidedefs;
//! Vertices connected to linedefs in the macro body.
size_t *vertices;
size_t num_vertices;
//! Sectors comprising the main part of the macro body.
size_t *sectors;
size_t num_sectors;
//! List of static variables shared by all expansions of this macro.
macro_varlist_t statics;
} macro_t;
/** Contains the entire state of macros, including the macro definition array
* and global variables.
*/
typedef struct macro_state_s {
//! Array of macro definitions.
macro_t *macros;
size_t num_macros;
//! List of global variables shared by all macros.
macro_varlist_t globals;
} macro_state_t;
/** Data specific to a single expansion of a macro.
*
* All geometry is stored as indices instead of pointers so that they stay valid
* after reallocations.
*/
typedef struct {
//! The definition of the macro being expanded.
macro_t *macro;
//! The "Expand Macro" parameter linedefs used to expand the macro and provide
//! variables to it.
size_t *linedefs;
size_t num_linedefs;
//! The vertices of the "Expand Macro" linedefs are also stored so they can be
//! retrieved in consecutive order from "Macro Parameters - Vertex" linedefs.
size_t *vertices;
size_t num_vertices;
//! For each type of geometry, this is the index of the first of the newly copied
//! pieces of geometry from `P_CopyMacroGeometry`.
size_t start_vertices;
size_t start_sidedefs;
size_t start_linedefs;
size_t start_sectors;
//! Array of variables retrieved with the "Macro Variables" linedefs.
macro_varlist_t locals;
} macro_expander_t;
/** Tries to convert an integer representation of a variable ID (i.e. texture offset
* variables in the range 30000 to 31000) to an actual variable ID and variable list
* containing that ID.
*
* \param expander The expander containing the local variables.
* \param raw_id The integer representation of the variable ID.
* \param[out] id The converted ID will be returned here on success if non-NULL.
* \return The variable list containing the variable or NULL if not a variable.
*/
static macro_varlist_t *P_IntegerToVar(macro_expander_t *expander, INT32 raw_id,
INT16 *id)
{
if (raw_id >= 30000) {
if (raw_id < 30500) {
if (id != NULL)
*id = raw_id - 30000;
return &expander->locals;
}
if (raw_id < 31000) {
if (id != NULL)
*id = raw_id - 30500;
return &expander->macro->statics;
}
if (raw_id < 31500) {
if (id != NULL)
*id = raw_id - 31000;
return &expander->macro->state->globals;
}
}
return NULL;
}
/** Tries to convert a texture representation of a variable ID (i.e. texture variables
* of the format `V[#]`) to an actual variable ID and variable list containing that ID.
*
* \param expander The expander containing the local variables.
* \param raw_id The texture representation of the variable ID.
* \param[out] id The converted ID will be returned here on success if non-NULL.
* \return The variable list containing the variable or NULL if not a variable.
*/
static macro_varlist_t *P_TextureToVar(macro_expander_t *expander,
const char raw_id[8], INT16 *id)
{
char *nulled = R_NullTermTexture(raw_id);
int value;
// Spaces are intentional in the format string since whitespace is allowed there.
if (sscanf(nulled, " V [%d]", &value)) {
if (id != NULL)
*id = value;
return &expander->locals;
}
if (sscanf(nulled, " S [%d]", &value)) {
if (id != NULL)
*id = value;
return &expander->macro->statics;
}
if (sscanf(nulled, " G [%d]", &value)) {
if (id != NULL)
*id = value;
return &expander->macro->state->globals;
}
return NULL;
}
/** Searches through a list of variables for a variable with the specified ID.
*
* \param list The variable list containing the variables.
* \param id The ID of the variable to look for.
* \return A pointer to the variable or NULL if it doesn't exist.
*/
static macro_var_t *P_FindVar(macro_varlist_t *list, INT16 id)
{
size_t i;
for (i = 0; i < list->num_vars; i++) {
macro_var_t *var = &list->vars[i];
if (var->id == id)
return var;
}
return NULL;
}
/** Searches through a list of variables for a variable with the specified ID.
* If it does not exist, it creates a new empty one with that ID.
*
* \param list The variable list containing the variables.
* \param id The ID of the variable to look for or add.
* \return A pointer to the (possibly new) variable.
*/
static macro_var_t *P_FindOrCreateVar(macro_varlist_t *list, INT16 id)
{
macro_var_t *var = P_FindVar(list, id);
if (var != NULL)
return var;
list->vars = Z_Realloc(list->vars,
++list->num_vars * sizeof(*list->vars), PU_LEVEL, NULL);
return &list->vars[list->num_vars - 1];
}
/** Sets a macro variable to an integer value, creating it if it doesn't exist.
*
* \param list The variable list containing the variables.
* \param id The ID of the variable to change.
* \param value The integer value to set the variable to.
*/
static void P_SetIntegerVar(macro_varlist_t *list, INT16 id, INT32 value)
{
macro_var_t *var = P_FindOrCreateVar(list, id);
char buffer[9];
var->id = id;
var->is_integer = true;
var->integer = value;
snprintf(buffer, 9, "%d", value);
memcpy(var->texture, &buffer, 8);
}
/** Sets a macro variable to a texture value, creating it if it doesn't exist.
*
* \param list The variable list containing the variables.
* \param id The ID of the variable to change.
* \param value The texture value to set the variable to.
*/
static void P_SetTextureVar(macro_varlist_t *list, INT16 id, const char value[8])
{
macro_var_t *var = P_FindOrCreateVar(list, id);
var->id = id;
var->is_integer = false;
memcpy(var->texture, value, 8);
sscanf(R_NullTermTexture(value), "%d", &var->integer);
}
/** Sets a variable to an integer value using a raw integer representation of a
* variable ID, creating the variable if it doesn't exist.
*
* \param expander The expander containing the local variables.
* \param raw_id The integer representation of the ID of the variable to change.
* \param value The integer value to set the variable to.
*/
static boolean P_SetIntVarToInt(macro_expander_t *expander, INT32 raw_id, INT32 value)
{
INT16 id;
macro_varlist_t *list = P_IntegerToVar(expander, raw_id, &id);
if (list == NULL)
return false;
P_SetIntegerVar(list, id, value);
return true;
}
/** Sets a variable to an integer value using a raw texture representation of a
* variable ID, creating the variable if it doesn't exist.
*
* \param expander The expander containing the local variables.
* \param raw_id The texture representation of the ID of the variable to change.
* \param value The integer value to set the variable to.
*/
static boolean P_SetTexVarToInt(macro_expander_t *expander, const char raw_id[8],
INT32 value)
{
INT16 id;
macro_varlist_t *list = P_TextureToVar(expander, raw_id, &id);
if (list == NULL)
return false;
P_SetIntegerVar(list, id, value);
return true;
}
/** Sets a variable to a texture value using a raw texture representation of a
* variable ID, creating the variable if it doesn't exist.
*
* \param expander The expander containing the local variables.
* \param raw_id The texture representation of the ID of the variable to change.
* \param value The texture value to set the variable to.
*/
static boolean P_SetTexVarToTex(macro_expander_t *expander, const char raw_id[8],
const char value[8])
{
INT16 id;
macro_varlist_t *list = P_TextureToVar(expander, raw_id, &id);
if (list == NULL)
return false;
P_SetTextureVar(list, id, value);
return true;
}
/** Returns a raw integer representation of a variable ID converted to that variable's
* value, returning the original integer if it isn't actually a variable.
*
* For instance, calling this on a sidedef texture offset with the value 30010 will
* return the value of variable 10. Calling it on an offset with the value 24 will
* just return 24 since that is not a variable.
*
* \param expander The expander containing the variables.
* \param value The integer that is possibly a variable.
* \return The value of the variable or the original number.
*/
static INT32 P_ReplaceIntegerVar(macro_expander_t *expander, INT32 value)
{
INT16 id;
macro_varlist_t *list = P_IntegerToVar(expander, value, &id);
macro_var_t *var;
if (list == NULL)
return value;
var = P_FindVar(list, id);
if (var == NULL)
return value;
return var->integer;
}
/** Directly changes a texture variable in some piece of geometry in the macro body
* to the variable value, doing nothing if it isn't actually a variable.
*
* For instance, calling this on a sidedef texture with the value `V[10]` will replace
* the offset with the value of variable 10. Calling it on a texture with the value
* `GFZROCK` will do nothing since that is not a variable.
*
* \param expander The expander containing the variables.
* \param[in,out] value The texture that is possibly a variable that may be changed.
* \return True if it was a variable and replaced and false if just a normal texture.
*/
static boolean P_ReplaceTextureVar(macro_expander_t *expander, char value[8])
{
INT16 id;
macro_varlist_t *list = P_TextureToVar(expander, value, &id);
macro_var_t *var;
if (list == NULL)
return false;
var = P_FindVar(list, id);
if (var == NULL)
return false;
memcpy(value, var->texture, 8);
return true;
}
/** Pushes all the variables in a variable list to a Lua table and leaves it at
* the top of the stack.
*
* \param L The Lua state to push the variables to.
* \param list The list of variables to push.
*/
static void P_PushLuaVarList(lua_State *L, macro_varlist_t *list)
{
size_t i;
lua_createtable(L, 0, list->num_vars);
for (i = 0; i < list->num_vars; i++) {
macro_var_t *var = &list->vars[i];
if (var->is_integer)
lua_pushinteger(L, var->integer);
else
lua_pushstring(L, var->texture);
lua_rawseti(L, -2, var->id);
}
}
/** Pops a table from the stack and reads all the variables from it and sets them
* into the variable list.
*
* \param L The Lua state to read the variables from.
* \param index The index of the variable table on the Lua stack.
* \param list The list of variables to read into.
*/
static void P_ReadLuaVarList(lua_State *L, macro_varlist_t *list)
{
// Index of the table on the stack
int index = lua_gettop(L);
if (!lua_istable(L, index)) {
I_Error("P_ReadLuaVarList: Macro calculation expected table, got %s",
lua_typename(L, lua_type(L, index)));
}
// First key
lua_pushnil(L);
while (lua_next(L, index)) {
INT16 id = lua_tointeger(L, -2);
int type = lua_type(L, -1);
if (type == LUA_TNUMBER) {
P_SetIntegerVar(list, id, lua_tointeger(L, -1));
} else if (type == LUA_TSTRING) {
size_t len;
char buffer[8];
const char *raw = lua_tolstring(L, -1, &len);
if (raw) {
memset(buffer, 0, 8);
memcpy(buffer, raw, (len > 8) ? 8 : len);
P_SetTextureVar(list, id, buffer);
}
} else {
I_Error("P_ReadLuaVarList: Macro calculation variable %d has a %s, "
"not a string or number", id, lua_typename(L, type));
}
// Pop the value but keep the key
lua_pop(L, 1);
}
// Pop the table
lua_pop(L, 1);
}
/** Frees all the allocated values in a variable list and zeroes it.
*
* \param list The variable list to free.
*/
static void P_FreeVarList(macro_varlist_t *list)
{
Z_Free(list->vars);
memset(list, 0, sizeof(*list));
}
/** Searches for a macro with the specified tag.
*
* \param state The state containing the macros.
* \param tag The tag of the macro to find.
* \return The macro or NULL if not found.
*/
static macro_t *P_FindMacro(macro_state_t *state, INT16 tag)
{
size_t i;
for (i = 0; i < state->num_macros; i++) {
macro_t *macro = &state->macros[i];
if (macro->tag == tag)
return macro;
}
return NULL;
}
/** Adds a new empty macro (i.e. no definition lines or geometry) with a specified tag
* to the array of macros. If a macro with the same tag already exists, it is an error.
*
* \param state The state to create the new macro in.
* \param tag The tag that the new macro should have.
* \return The pointer to the new macro.
*/
static macro_t *P_NewMacro(macro_state_t *state, INT16 tag)
{
macro_t *macro;
if (P_FindMacro(state, tag))
I_Error("P_NewMacro: Attempt to define multiple macros with the same tag: %d", tag);
state->macros = Z_Realloc(state->macros,
++state->num_macros * sizeof(*state->macros), PU_LEVEL, NULL);
macro = &state->macros[state->num_macros - 1];
memset(macro, 0, sizeof(*macro));
macro->tag = tag;
macro->state = state;
return macro;
}
/** Adds a definition linedef to the specified macro's list of definition linedefs.
*
* \param macro The macro to add the definition linedef to.
* \param linedef The index of the linedef to add.
*/
static void P_AddDefLinedefToMacro(macro_t *macro, size_t linedef)
{
macro->def_linedefs = Z_Realloc(macro->def_linedefs,
++macro->num_def_linedefs * sizeof(*macro->def_linedefs), PU_LEVEL, NULL);
macro->def_linedefs[macro->num_def_linedefs - 1] = linedef;
}
/** Searches through a macro body's list of linedefs to see if a specified linedef
* exists.
*
* \param macro The macro to search through.
* \param linedef The linedef to search for.
* \return The index of the linedef in the body array, or SIZE_MAX if not found.
*/
static size_t P_FindLinedefInMacro(macro_t *macro, size_t linedef)
{
size_t i;
for (i = 0; i < macro->num_linedefs; i++) {
if (macro->linedefs[i] == linedef)
return i;
}
return SIZE_MAX;
}
/** Adds a new linedef to the macro body's list of linedefs and also updates the
* number of sidedefs in the macro body. It will not add duplicates.
*
* \param macro The macro to add the linedef to.
* \param linedef The index of the linedef to add.
* \param pack The map pack the linedefs and sidedefs are in.
* \return True if added successfully, false if the linedef has already been added.
*/
static boolean P_AddLinedefToMacro(macro_t *macro, size_t linedef,
const mappack_t *pack)
{
if (P_FindLinedefInMacro(macro, linedef) != SIZE_MAX)
return false;
macro->linedefs = Z_Realloc(macro->linedefs,
++macro->num_linedefs * sizeof(*macro->linedefs), PU_LEVEL, NULL);
macro->linedefs[macro->num_linedefs - 1] = linedef;
macro->num_sidedefs += pack->linedefs[linedef].sidenum[1] == 0xFFFF ? 1 : 2;
return true;
}
/** Searches through a macro body's list of vertices to see if a specified vertex
* exists.
*
* \param macro The macro to search through.
* \param vertex The vertex to search for.
* \return The index of the vertex in the body array, or SIZE_MAX if not found.
*/
static size_t P_FindVertexInMacro(macro_t *macro, size_t vertex)
{
size_t i;
for (i = 0; i < macro->num_vertices; i++) {
if (macro->vertices[i] == vertex)
return i;
}
return SIZE_MAX;
}
/** Adds a new vertex to the macro body's list of vertices. It will not add duplicates.
*
* \param macro The macro to add the vertex to.
* \param vertex The index of the vertex to add.
* \return True if added successfully, false if the vertex has already been added.
*/
static boolean P_AddVertexToMacro(macro_t *macro, size_t vertex)
{
if (P_FindVertexInMacro(macro, vertex) != SIZE_MAX)
return false;
macro->vertices = Z_Realloc(macro->vertices,
++macro->num_vertices * sizeof(*macro->vertices), PU_LEVEL, NULL);
macro->vertices[macro->num_vertices - 1] = vertex;
return true;
}
/** Searches through a macro body's list of sectors to see if a specified sector
* exists.
*
* \param macro The macro to search through.
* \param sector The sector to search for.
* \return The index of the sector in the body array, or SIZE_MAX if not found.
*/
static size_t P_FindSectorInMacro(macro_t *macro, size_t sector)
{
size_t i;
for (i = 0; i < macro->num_sectors; i++) {
if (macro->sectors[i] == sector)
return i;
}
return SIZE_MAX;
}
/** Adds a new sector to the macro body's list of sectors. It will not add duplicates.
*
* \param macro The macro to add the sector to.
* \param sector The index of the sector to add.
* \return True if added successfully, false if the sector has already been added.
*/
static boolean P_AddSectorToMacro(macro_t *macro, size_t sector)
{
if (P_FindSectorInMacro(macro, sector) != SIZE_MAX)
return false;
macro->sectors = Z_Realloc(macro->sectors,
++macro->num_sectors * sizeof(*macro->sectors), PU_LEVEL, NULL);
macro->sectors[macro->num_sectors - 1] = sector;
return true;
}
/** Searches for all geometry that is part of the macro body and adds it all to the
* macro. In specific, it searches for sectors that have the same tag as the macro,
* adds all linedefs that exclusively touch those sectors, and adds the vertices of
* those linedefs.
*
* \param macro The macro to build the geometry for.
* \param pack The map pack that contains all the geometry.
*/
static void P_BuildMacroBody(macro_t *macro, mappack_t *pack)
{
size_t i;
// Add the sectors with the macro's tag.
for (i = 0; i < pack->num_sectors; i++) {
mapsector_t *sector = &pack->sectors[i];
if (sector->tag == macro->tag)
P_AddSectorToMacro(macro, i);
}
// Add linedefs and their vertices that only touch macro sectors.
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[i];
boolean has_back = linedef->sidenum[1] != 0xFFFF;
// Macro linedefs can't be split between a macro sector and non-macro sector.
// They must either be single sided or reference two macro sectors.
boolean front_valid = false;
boolean back_valid = false;
size_t j;
for (j = 0; j < macro->num_sectors; j++) {
size_t sector_idx = macro->sectors[j];
if (pack->sidedefs[linedef->sidenum[0]].sector == sector_idx)
front_valid = true;
if (has_back && pack->sidedefs[linedef->sidenum[1]].sector == sector_idx)
back_valid = true;
if (front_valid && (!has_back || back_valid)) {
// This linedef is valid, no need to keep searching.
P_AddLinedefToMacro(macro, i, pack);
// Duplicate vertices are not added automatically, so just add both vertices.
P_AddVertexToMacro(macro, linedef->v1);
P_AddVertexToMacro(macro, linedef->v2);
break;
}
}
if (has_back && front_valid != back_valid) {
I_Error("P_BuildMacroBody: Linedef %d is split between a macro sector "
"and a non-macro sector", i);
}
}
}
// DEBUGGING
#define DebugPrintSize(name, val) printf("\t%s = %u\n", (name), (unsigned)(val));
#define DebugPrintInt(name, val) printf("\t%s = %d\n", (name), (int)(val));
#define DebugPrintTex(name, val) printf("\t%s = \"%s\"\n", (name), R_NullTermTexture((val)));
#define DebugPrintBoolean(name, val) printf("\t%s = %s\n", (name), (val) ? "true" : "false");
static void DebugPrintArr(const char *num_name, size_t num, const char *arr_name, size_t *arr)
{
size_t i;
printf("\t%s = %u\n\t%s = {", num_name, (unsigned)num, arr_name);
for (i = 0; i < num; i++) {
if (i == num - 1)
printf("%u", (unsigned)(arr[i]));
else
printf("%u, ", (unsigned)(arr[i]));
}
printf("}\n");
}
static void DebugPrintVarList(const char *field_name, macro_varlist_t *varlist)
{
size_t i;
printf("\t%s.num_vars = %u\n\t%s.vars = {", field_name, (unsigned)varlist->num_vars,
field_name);
for (i = 0; i < varlist->num_vars; i++) {
macro_var_t *var = &varlist->vars[i];
printf("{\n\t");
DebugPrintSize("id", var->id);
putchar('\t');
DebugPrintBoolean("is_integer", var->is_integer);
putchar('\t');
DebugPrintInt("integer", var->integer);
putchar('\t');
DebugPrintTex("texture", var->texture);
if (i == varlist->num_vars - 1)
printf("\t}");
else
printf("\t}, ");
}
printf("}\n");
}
static void DebugPrintMapPack(mappack_t *pack)
{
size_t i;
puts("Vertices:\n"
" | X | Y |\n"
" |--------|--------|");
for (i = 0; i < pack->num_vertices; i++) {
mapvertex_t *vertex = &pack->vertices[i];
printf("%5u | %6d | %6d |\n", (unsigned)i, vertex->x, vertex->y);
}
puts("\nSidedefs:\n"
" | X Off | Y Off | Top Tex | Mid Tex | Bot Tex | Sector |\n"
" |--------|--------|----------|----------|----------|--------|");
for (i = 0; i < pack->num_sidedefs; i++) {
mapsidedef_t *sidedef = &pack->sidedefs[i];
printf("%5u | %6d | %6d | %8s | ",
(unsigned)i,
sidedef->rowoffset,
sidedef->textureoffset,
R_NullTermTexture(sidedef->toptexture));
printf("%8s | ",
R_NullTermTexture(sidedef->midtexture));
printf("%8s | %6u |\n",
R_NullTermTexture(sidedef->bottomtexture),
sidedef->sector);
}
puts("\nLinedefs:\n"
" | Vert 1 | Vert 2 | Flags | Spec | Tag | Front | Back |\n"
" |--------|--------|--------|--------|--------|--------|--------|");
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[i];
printf("%5u | %6u | %6u | %6d | %6d | %6d | %6u | %6u |\n",
(unsigned)i,
linedef->v1,
linedef->v2,
linedef->flags,
linedef->special,
linedef->tag,
linedef->sidenum[0],
linedef->sidenum[1]);
}
puts("\nSectors:\n"
" | Floor | Ceilng | Flr Pic | Ceil Pic | Light | Spec | Tag |\n"
" |--------|--------|----------|----------|--------|--------|--------|");
for (i = 0; i < pack->num_sectors; i++) {
mapsector_t *sector = &pack->sectors[i];
printf("%5u | %6d | %6d | %8s | ",
(unsigned)i,
sector->floorheight,
sector->ceilingheight,
R_NullTermTexture(sector->floorpic));
printf("%8s | %6d | %6d | %6d |\n",
R_NullTermTexture(sector->ceilingpic),
sector->lightlevel,
sector->special,
sector->tag);
}
puts("\nThings:\n"
" | X | Y | Angle | Type | Param | Flags | Z |\n"
" |--------|--------|--------|--------|--------|--------|--------|");
for (i = 0; i < pack->num_things; i++) {
raw_mapthing_t *thing = &pack->things[i];
printf("%5u | %6d | %6d | %6d | %6u | %6u | %6u | %6u |\n",
(unsigned)i,
thing->x,
thing->y,
thing->angle,
thing->type & TYPEMASK,
thing->type >> PARAMSHIFT,
thing->options,
thing->options >> ZSHIFT);
}
}
/** Searches through the entire map and builds the list of macro definitions and
* fills out the geometry in the macro body.
*
* \param state The state to build the macros in.
* \param pack The map pack that contains the level geometry.
*/
static void P_BuildMacros(macro_state_t *state, mappack_t *pack)
{
size_t i;
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[i];
maplinedef_t *previous = NULL;
macro_t *macro;
// Search only for "Define Macro" in this first pass.
if (linedef->special != 72)
continue;
macro = P_NewMacro(state, linedef->tag);
// Find the rest of the linedefs in the macro and add them.
while (P_FindNextConnectedLinedef(&linedef, &previous, 73, 79, pack))
P_AddDefLinedefToMacro(macro, linedef - pack->linedefs);
// Find and build the geometry that this macro will expand each time.
P_BuildMacroBody(macro, pack);
}
// DEBUGGING
for (i = 0; i < state->num_macros; i++) {
macro_t *macro = &state->macros[i];
printf("macros[%u] = {\n", (unsigned)i);
DebugPrintInt("tag", macro->tag);
DebugPrintArr("num_def_linedefs", macro->num_def_linedefs, "def_linedefs", macro->def_linedefs);
DebugPrintArr("num_linedefs", macro->num_linedefs, "linedefs", macro->linedefs);
DebugPrintSize("num_sidedefs", macro->num_sidedefs);
DebugPrintArr("num_vertices", macro->num_vertices, "vertices", macro->vertices);
DebugPrintArr("num_sectors", macro->num_sectors, "sectors", macro->sectors);
printf("}\n");
}
putchar('\n');
}
/** Frees all allocated values in the macro and zeros it.
*
* \param macro The state to free everything from.
*/
static void P_FreeMacro(macro_t *macro)
{
Z_Free(macro->def_linedefs);
Z_Free(macro->linedefs);
Z_Free(macro->vertices);
Z_Free(macro->sectors);
P_FreeVarList(&macro->statics);
memset(macro, 0, sizeof(*macro));
}
/** Adds an linedef to a macro expander's list of parameter linedefs.
*
* \param expander The expander to add the linedef to.
* \param linedef The index of the linedef to add.
*/
static void P_AddLinedefToExpander(macro_expander_t *expander, size_t linedef)
{
expander->linedefs = Z_Realloc(expander->linedefs,
++expander->num_linedefs * sizeof(*expander->linedefs), PU_LEVEL, NULL);
expander->linedefs[expander->num_linedefs - 1] = linedef;
}
/** Adds an vertex to a macro expander's list of parameter vertices.
*
* \param expander The expander to add the vertex to.
* \param vertex The index of the vertex to add.
*/
static void P_AddVertexToExpander(macro_expander_t *expander, size_t vertex)
{
expander->vertices = Z_Realloc(expander->vertices,
++expander->num_vertices * sizeof(*expander->vertices), PU_LEVEL, NULL);
expander->vertices[expander->num_vertices - 1] = vertex;
}
/** Builds the list of "Expand Macro - Continued" parameter linedefs and their
* vertices based on the provided "Expand Macro - First Line" linedef.
*
* \param expander The expander to build the parameter linedefs for.
* \param first_linedef The first linedef of the macro parameters.
* \param pack The map pack containing the linedefs and vertices.
*/
static void P_BuildMacroParamLines(macro_expander_t *expander,
maplinedef_t *first_linedef, mappack_t *pack)
{
maplinedef_t *linedef = first_linedef;
maplinedef_t *previous = NULL;
int vertex;
// Simply add the first linedef;
P_AddLinedefToExpander(expander, first_linedef - pack->linedefs);
// The first linedef needs special treatment to add both vertices in the
// proper order.
vertex = P_FindNextConnectedLinedef(&linedef, &previous, 71, 71, pack);
if (vertex) {
// Add the non-connected vertex and then the connected one.
P_AddVertexToExpander(expander, vertex == 1 ? previous->v2 : previous->v1);
P_AddVertexToExpander(expander, vertex == 1 ? previous->v1 : previous->v2);
do {
// Add the rest of the shared linedefs with their other vertex that
// was not shared with the previously one. Use a do-while since we
// already found the first shared one.
P_AddLinedefToExpander(expander, linedef - pack->linedefs);
P_AddVertexToExpander(expander, P_HasSharedVertex(linedef, previous) == 1 ?
linedef->v2 : linedef->v1);
} while ((vertex =
P_FindNextConnectedLinedef(&linedef, &previous, 71, 71, pack)));
} else {
// There are no connected linedefs; just add the vertices in the
// order v1, v2.
P_AddVertexToExpander(expander, first_linedef->v1);
P_AddVertexToExpander(expander, first_linedef->v2);
}
}
/** The helper callback function for LUA_EvalMathEx that adds all the variables,
* local and global, to the macro expander.
*
* \param L The Lua state to add the variables to.
* \param data Of type `macro_expander_t`, contains the variables to add.
*/
static void P_CalcSetter(lua_State *L, void *data)
{
macro_expander_t *expander = data;
P_PushLuaVarList(L, &expander->locals);
lua_setglobal(L, "V");
P_PushLuaVarList(L, &expander->macro->statics);
lua_setglobal(L, "S");
P_PushLuaVarList(L, &expander->macro->state->globals);
lua_setglobal(L, "G");
}
/** The helper callback function for LUA_EvalMathEx that searches through all the
* entries in the variable tables and adds them back to the variable arrays.
*
* \param L The Lua state to extract the variables from.
* \param data Of type `macro_expander_t`, where the variables will be added back to.
*/
static void P_CalcGetter(lua_State *L, void *data)
{
macro_expander_t *expander = data;
lua_getglobal(L, "V");
P_ReadLuaVarList(L, &expander->locals);
lua_getglobal(L, "S");
P_ReadLuaVarList(L, &expander->macro->statics);
lua_getglobal(L, "G");
P_ReadLuaVarList(L, &expander->macro->state->globals);
}
/** Builds the full list of variables for a macro expander using the "Macro
* Parameters" and "Macro Variables" linedefs, extracting them from the
* "Expand Macro" parameter linedefs.
*
* \param expander The expander to build the variables for.
* \param pack The map pack containing all the geometry.
*/
static void P_BuildMacroVariables(macro_expander_t *expander, mappack_t *pack)
{
macro_t *macro = expander->macro;
size_t i;
for (i = 0; i < macro->num_def_linedefs; i++) {
// Control geometry has the `c_` prefix, and target geometry has the `t_` prefix.
maplinedef_t *c_linedef = &pack->linedefs[macro->def_linedefs[i]];
mapsidedef_t *c_front = &pack->sidedefs[c_linedef->sidenum[0]];
mapsidedef_t *c_back = &pack->sidedefs[c_linedef->sidenum[1]];
maplinedef_t *t_linedef;
mapsidedef_t *t_sidedef;
// It is valid to check this afterwards since we haven't dereferenced the back
// sidedef yet.
if (c_linedef->sidenum[1] == 0xFFFF) {
I_Error("P_BuildMacroVariables: Macro variables linedefs must "
"have a back sidedef");
}
// Multiple cases in the switch require checking that the linedef is in bounds
// and that there is a back sidedef, so do that here instead of duplicated code
// inside the switch.
if (c_linedef->special <= 75) {
boolean back;
if ((size_t)c_linedef->tag >= expander->num_linedefs) {
I_Error("P_BuildMacroVariables: Macro tag %d expected a linedef %d",
macro->tag, c_linedef->tag);
}
t_linedef = &pack->linedefs[expander->linedefs[c_linedef->tag]];
back = !!(c_linedef->flags & ML_EFFECT6);
if (c_linedef->special >= 74 && back && t_linedef->sidenum[1] == 0xFFFF) {
I_Error("P_BuildMacroVariables: Macro tag %d expected a back sidedef on "
"linedef %d", macro->tag, c_linedef->tag);
}
t_sidedef = &pack->sidedefs[t_linedef->sidenum[back]];
}
switch (c_linedef->special) {
case 73: // Macro Parameters - Linedef
P_SetIntVarToInt(expander, c_front->textureoffset, t_linedef->special);
P_SetIntVarToInt(expander, c_front->rowoffset, t_linedef->tag);
P_SetTexVarToInt(expander, c_front->toptexture, t_linedef->flags);
P_SetTexVarToInt(expander, c_front->bottomtexture, t_linedef - pack->linedefs);
P_SetIntVarToInt(expander, c_back->textureoffset, t_linedef->sidenum[0]);
P_SetIntVarToInt(expander, c_back->rowoffset, t_linedef->sidenum[1]);
P_SetTexVarToInt(expander, c_back->toptexture, t_linedef->v1);
P_SetTexVarToInt(expander, c_back->midtexture, t_linedef->v2);
break;
case 74: // Macro Parameters - Sidedef
P_SetIntVarToInt(expander, c_front->textureoffset, t_sidedef->textureoffset);
P_SetIntVarToInt(expander, c_front->rowoffset, t_sidedef->rowoffset);
P_SetTexVarToTex(expander, c_front->toptexture, t_sidedef->toptexture);
P_SetTexVarToTex(expander, c_front->midtexture, t_sidedef->midtexture);
P_SetTexVarToTex(expander, c_front->bottomtexture, t_sidedef->bottomtexture);
P_SetIntVarToInt(expander, c_back->textureoffset, t_sidedef->sector);
break;
case 75: { // Macro Parameters - Sector
mapsector_t *t_sector = &pack->sectors[t_sidedef->sector];
P_SetIntVarToInt(expander, c_front->textureoffset, t_sector->floorheight);
P_SetIntVarToInt(expander, c_front->rowoffset, t_sector->ceilingheight);
P_SetTexVarToTex(expander, c_front->toptexture, t_sector->floorpic);
P_SetTexVarToTex(expander, c_front->midtexture, t_sector->ceilingpic);
P_SetIntVarToInt(expander, c_back->textureoffset, t_sector->lightlevel);
P_SetIntVarToInt(expander, c_back->rowoffset, t_sector->tag);
if (c_linedef->flags & ML_BOUNCY) {
P_SetTexVarToInt(expander, c_front->bottomtexture, t_sector->special);
} else {
P_SetTexVarToInt(expander, c_front->bottomtexture,
GetSecSpecial(t_sector->special, 1));
P_SetTexVarToInt(expander, c_back->toptexture,
GetSecSpecial(t_sector->special, 2));
P_SetTexVarToInt(expander, c_back->midtexture,
GetSecSpecial(t_sector->special, 3));
P_SetTexVarToInt(expander, c_back->bottomtexture,
GetSecSpecial(t_sector->special, 4));
}
break;
}
case 76: { // Macro Parameters - Vertex
mapvertex_t *t_vertex;
if ((size_t)c_linedef->tag >= expander->num_vertices) {
I_Error("P_BuildMacroVariables: Macro tag %d expected a vertex %d",
macro->tag, c_linedef->tag);
}
t_vertex = &pack->vertices[expander->vertices[c_linedef->tag]];
P_SetIntVarToInt(expander, c_front->textureoffset, t_vertex->x);
P_SetIntVarToInt(expander, c_front->rowoffset, t_vertex->y);
P_SetTexVarToInt(expander, c_front->bottomtexture, t_vertex - pack->vertices);
break;
}
case 77: { // Macro Parameters - Thing
raw_mapthing_t *t_thing;
INT16 type = 759; // Macro Thing Helper
if (!R_IsBlankTexture(c_front->midtexture))
type = atoi(R_NullTermTexture(c_front->midtexture));
t_thing = P_FindThingByTypeAndAngle(type, c_linedef->tag, pack);
P_SetIntVarToInt(expander, c_front->textureoffset, t_thing->x);
P_SetIntVarToInt(expander, c_front->rowoffset, t_thing->y);
P_SetTexVarToInt(expander, c_front->toptexture, t_thing->options);
P_SetTexVarToInt(expander, c_front->bottomtexture, t_thing - pack->things);
P_SetIntVarToInt(expander, c_back->textureoffset, t_thing->angle);
P_SetIntVarToInt(expander, c_back->rowoffset, t_thing->options >> ZSHIFT);
P_SetTexVarToInt(expander, c_back->toptexture, t_thing->type >> PARAMSHIFT);
P_SetTexVarToInt(expander, c_back->midtexture, t_thing->type & TYPEMASK);
break;
}
case 78: { // Macro Variables - New Tags
// We should only call P_GetNewTag for a valid variable. No need to be
// wasteful with tags.
if (P_IntegerToVar(expander, c_linedef->tag, NULL) != NULL)
P_SetIntVarToInt(expander, c_linedef->tag, P_GetNewTag(pack));
if (P_IntegerToVar(expander, c_front->textureoffset, NULL) != NULL)
P_SetIntVarToInt(expander, c_front->textureoffset, P_GetNewTag(pack));
if (P_IntegerToVar(expander, c_front->rowoffset, NULL) != NULL)
P_SetIntVarToInt(expander, c_front->rowoffset, P_GetNewTag(pack));
if (P_IntegerToVar(expander, c_back->textureoffset, NULL) != NULL)
P_SetIntVarToInt(expander, c_back->textureoffset, P_GetNewTag(pack));
if (P_IntegerToVar(expander, c_back->rowoffset, NULL) != NULL)
P_SetIntVarToInt(expander, c_back->rowoffset, P_GetNewTag(pack));
break;
}
case 79: { // Macro Variables - Calculation
char buffer[49];
char *combined;
combined = R_CombineTextures(
c_front->toptexture, c_front->midtexture, c_front->bottomtexture);
strcpy(buffer, combined);
combined = R_CombineTextures(
c_back->toptexture, c_back->midtexture, c_back->bottomtexture);
if (c_linedef->flags & ML_EFFECT6) {
// Combine both calculations into one larger one.
strcpy(buffer + strlen(buffer), combined);
if (strlen(buffer) != 0) {
LUA_EvalMathEx(buffer, false, false,
&P_CalcSetter, &P_CalcGetter, expander);
}
} else {
if (strlen(buffer) != 0) {
LUA_EvalMathEx(buffer, false, false,
&P_CalcSetter, &P_CalcGetter, expander);
}
if (strlen(combined) != 0) {
LUA_EvalMathEx(combined, false, false,
&P_CalcSetter, &P_CalcGetter, expander);
}
}
break;
}
default:
I_Error("P_BuildMacroVariables: Unreachable code reached; this is a bug");
break;
}
}
}
/** Helper for `P_CopyMacroGeometry`; copies a sidedef and makes it refer to the copied
* sector instead of the original sector the original sidedef pointed to. Returns the
* new index of the sidedef for updating its parent linedef.
*
* \param expander The expander to copy the sidedef for.
* \param sidedef_idx The index of the sidedef to be copied.
* \param copy_idx The index of where the sidedef should be copied to.
* \param pack The map pack containing the geometry.
* \return The value of copy_idx
*/
static size_t P_CopyMacroSidedef(macro_expander_t *expander, size_t sidedef_idx,
size_t copy_idx, mappack_t *pack)
{
mapsidedef_t *sidedef = &pack->sidedefs[copy_idx];
size_t sector_idx;
*sidedef = pack->sidedefs[sidedef_idx];
sector_idx = P_FindSectorInMacro(expander->macro, sidedef->sector);
if (sector_idx == SIZE_MAX) {
I_Error("P_CopyMacroSidedef: Sidedef %d references sector %d, which was "
"not copied; this is a bug", copy_idx, sidedef->sector);
}
sidedef->sector = expander->start_sectors + sector_idx;
return copy_idx;
}
/** Helper for `P_CopyMacroGeometry`; finds the new index of a copied vertex and
* returns it for updating its parent linedef.
*
* \param expander The expander to set the vertex for.
* \param vertex The old index of the vertex
* \param linedef_idx The index of the linedef being updated.
* \param linedef_v 1 if changing v1, 2 if changing v2.
* \return The new index of the vertex.
*/
static size_t P_GetCopiedMacroVertex(macro_expander_t *expander, size_t vertex,
int linedef_idx, int linedef_v)
{
size_t vertex_idx = P_FindVertexInMacro(expander->macro, vertex);
if (vertex_idx == SIZE_MAX) {
I_Error("P_GetCopiedMacroVertex: Vertex %d of linedef %d references vertex %u"
", which was not copied; this is a bug",
linedef_v, linedef_idx, (unsigned)vertex);
}
return expander->start_vertices + vertex_idx;
}
/** Goes through all the geometry in the macro body and copies it, making sure all
* references within the copied geometry point to the copied geometry only. This
* also fills out the `start_<geometry>` field in `macro_expander_t`.
*
* \param expander The expander to copy the geometry for.
* \param pack Contains all the map geometry referred to by the macro.
*/
static void P_CopyMacroGeometry(macro_expander_t *expander, mappack_t *pack)
{
macro_t *macro = expander->macro;
size_t i;
size_t sidedef_idx = expander->start_sidedefs;
// Set the start of the copied geometry.
expander->start_vertices = pack->num_vertices;
expander->start_sidedefs = pack->num_sidedefs;
expander->start_linedefs = pack->num_linedefs;
expander->start_sectors = pack->num_sectors;
// Copy sectors and vertices first since they don't depend on anything.
P_AppendSectors(pack, macro->num_sectors, NULL);
for (i = 0; i < macro->num_sectors; i++)
pack->sectors[expander->start_sectors + i] = pack->sectors[macro->sectors[i]];
P_AppendVertices(pack, macro->num_vertices, NULL);
for (i = 0; i < macro->num_vertices; i++)
pack->vertices[expander->start_vertices + i] = pack->vertices[macro->vertices[i]];
// Then copy sidedefs, since they depend on sectors, and linedefs, which depend
// on vertices and those newly copied sidedefs.
P_AppendSidedefs(pack, macro->num_sidedefs, NULL);
P_AppendLinedefs(pack, macro->num_linedefs, NULL);
for (i = 0; i < macro->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[expander->start_linedefs + i];
size_t linedef_idx = macro->linedefs[i];
// Raw linedef copy
*linedef = pack->linedefs[linedef_idx];
// Copy sidedefs and set linedef sidenums
linedef->sidenum[0] = P_CopyMacroSidedef(expander, linedef->sidenum[0],
sidedef_idx, pack);
sidedef_idx++;
if (linedef->sidenum[1] != 0xFFFF) {
linedef->sidenum[1] = P_CopyMacroSidedef(expander,
linedef->sidenum[1], sidedef_idx, pack);
sidedef_idx++;
}
// Change linedef vertices
linedef->v1 = P_GetCopiedMacroVertex(expander, linedef->v1, linedef_idx, 1);
linedef->v2 = P_GetCopiedMacroVertex(expander, linedef->v2, linedef_idx, 2);
}
}
/** Goes through all the expanded macro geometry and replaces embedded integer and
* texture variables with their integer or texture value.
*
* \param expander The expander with the copied geometry.
* \param pack Contains all the map geometry referred to by the macro.
*/
static void P_ApplyMacroVariables(macro_expander_t *expander, mappack_t *pack)
{
macro_t *macro = expander->macro;
size_t i;
for (i = 0; i < macro->num_sectors; i++) {
mapsector_t *sector = &pack->sectors[expander->start_sectors + i];
sector->floorheight = P_ReplaceIntegerVar(expander, sector->floorheight);
sector->floorheight = P_ReplaceIntegerVar(expander, sector->ceilingheight);
P_ReplaceTextureVar(expander, sector->floorpic);
P_ReplaceTextureVar(expander, sector->ceilingpic);
sector->lightlevel = P_ReplaceIntegerVar(expander, sector->lightlevel);
sector->tag = P_ReplaceIntegerVar(expander, sector->tag);
}
for (i = 0; i < macro->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[expander->start_linedefs + i];
linedef->special = P_ReplaceIntegerVar(expander, linedef->special);
linedef->tag = P_ReplaceIntegerVar(expander, linedef->tag);
}
for (i = 0; i < macro->num_sidedefs; i++) {
mapsidedef_t *sidedef = &pack->sidedefs[expander->start_sidedefs + i];
sidedef->textureoffset = P_ReplaceIntegerVar(expander, sidedef->textureoffset);
sidedef->rowoffset = P_ReplaceIntegerVar(expander, sidedef->rowoffset);
P_ReplaceTextureVar(expander, sidedef->toptexture);
P_ReplaceTextureVar(expander, sidedef->midtexture);
P_ReplaceTextureVar(expander, sidedef->bottomtexture);
}
}
/** Goes through all the "Macro Proxy" linedefs in the expanded macro geometry
* and changes all the things referred to by those proxies.
*
* \param expander The expander with the copied geometry.
* \param pack Contains all the map geometry referred to by the macro.
*/
static void P_ApplyMacroProxies(macro_expander_t *expander, mappack_t *pack)
{
macro_t *macro = expander->macro;
size_t i;
for (i = 0; i < macro->num_linedefs; i++) {
// Control geometry has the `c_` prefix, and target geometry has the `t_` prefix.
maplinedef_t *c_linedef = &pack->linedefs[expander->start_linedefs + i];
size_t c_tag = c_linedef->tag;
INT16 c_flags = c_linedef->flags;
mapsidedef_t *c_front = &pack->sidedefs[c_linedef->sidenum[0]];
mapsidedef_t *c_back = &pack->sidedefs[c_linedef->sidenum[1]];
// It is valid to check this afterwards since we haven't dereferenced the back
// sidedef yet.
if (c_linedef->sidenum[1] == 0xFFFF) {
I_Error("P_ApplyMacroProxies: Macro proxy linedefs must "
"have a back sidedef");
}
// Macros that automatically set some integer or texture value from an integer
// or texture field on the proxy linedef if a specified flag is set.
// It isn't much prettier, but more concise.
// Sets an integer field from a texture offset.
#define PROXY_INT(flag, set, from) \
if (c_flags & (flag)) \
(set) = (from);
// Sets an integer field from a texture.
#define PROXY_TEXINT(flag, set, from) \
if (c_flags & (flag)) \
(set) = atoi(R_NullTermTexture((from)));
// Gets a texture as an integer in the variable `value` and then runs
// `assignment`, which can set fields using `value`.
#define PROXY_TEXINT_EX(flag, from, assignment) \
if (c_flags & (flag)) { \
INT16 value = atoi(R_NullTermTexture((from))); \
assignment; \
}
// Sets a texture field from a texture.
#define PROXY_TEX(flag, set, from) \
if (c_flags & (flag)) \
memcpy((set), (from), 8);
// Sets a flags field from all the textures on a sidedef.
#define PROXY_FLAGS(flag, set, side) \
if (c_flags & (flag)) { \
if (!R_IsBlankTexture((side)->toptexture)) \
(set) = atoi(R_NullTermTexture((side)->toptexture)); \
if (!R_IsBlankTexture((side)->midtexture)) \
(set) |= atoi(R_NullTermTexture((side)->midtexture)); \
if (!R_IsBlankTexture((side)->bottomtexture)) \
(set) &= atoi(R_NullTermTexture((side)->bottomtexture)); \
}
switch (c_linedef->special) {
case 80: { // Macro Proxy - Linedef
maplinedef_t *t_linedef;
if (c_flags & ML_TFERLINE) {
if (c_tag >= pack->num_linedefs)
I_Error("P_ApplyMacroProxies: Linedef %d does not exist", c_linedef->tag);
t_linedef = &pack->linedefs[c_tag];
} else {
t_linedef = P_GetClosestAngleLinedef(c_linedef, pack);
if ((size_t)(t_linedef - pack->linedefs) < expander->start_linedefs) {
I_Error("P_ApplyMacroProxies: Linedef with closest angle to linedef "
"%d is not a macro linedef", t_linedef - pack->linedefs);
}
}
PROXY_INT(ML_DONTPEGTOP, t_linedef->special, c_front->textureoffset)
PROXY_INT(ML_DONTPEGBOTTOM, t_linedef->tag, c_front->rowoffset)
PROXY_FLAGS(ML_EFFECT1, t_linedef->flags, c_front)
PROXY_INT(ML_EFFECT3, t_linedef->sidenum[0], c_back->textureoffset)
PROXY_INT(ML_EFFECT4, t_linedef->sidenum[1], c_back->rowoffset)
PROXY_TEXINT(ML_EFFECT5, t_linedef->v1, c_back->toptexture)
PROXY_TEXINT(ML_NETONLY, t_linedef->v2, c_back->midtexture)
break;
}
case 81: { // Macro Proxy - Sidedef
mapsidedef_t *t_sidedef;
if (c_flags & ML_TFERLINE) {
if (c_tag >= pack->num_sidedefs)
I_Error("P_ApplyMacroProxies: Sidedef %d does not exist", c_linedef->tag);
t_sidedef = &pack->sidedefs[c_tag];
} else {
maplinedef_t *t_linedef = P_GetClosestAngleLinedef(c_linedef, pack);
if ((size_t)(t_linedef - pack->linedefs) < expander->start_linedefs) {
I_Error("P_ApplyMacroProxies: Linedef with closest angle to linedef "
"%d is not a macro linedef", t_linedef - pack->linedefs);
}
if (c_flags & ML_EFFECT6) {
if (c_linedef->sidenum[1] == 0xFFFF) {
I_Error("P_ApplyMacroProxies: Linedef %u has no back sidedef",
(unsigned)(t_linedef - pack->linedefs));
}
t_sidedef = &pack->sidedefs[c_linedef->sidenum[1]];
} else {
t_sidedef = &pack->sidedefs[c_linedef->sidenum[0]];
}
}
PROXY_INT(ML_DONTPEGTOP, t_sidedef->textureoffset, c_front->textureoffset)
PROXY_INT(ML_DONTPEGBOTTOM, t_sidedef->rowoffset, c_front->rowoffset)
PROXY_TEX(ML_EFFECT1, t_sidedef->toptexture, c_front->toptexture)
PROXY_TEX(ML_NOCLIMB, t_sidedef->midtexture, c_front->midtexture)
PROXY_TEX(ML_EFFECT2, t_sidedef->bottomtexture, c_front->bottomtexture)
PROXY_INT(ML_EFFECT3, t_sidedef->sector, c_back->textureoffset)
break;
}
case 82: { // Macro Proxy - Sector
mapsector_t *t_sector;
size_t sidedef_idx;
if (c_flags & ML_TFERLINE) {
if (c_tag >= pack->num_sectors)
I_Error("P_ApplyMacroProxies: Sector %d does not exist", c_linedef->tag);
sidedef_idx = c_tag;
} else {
if (c_flags & ML_EFFECT6) {
if (c_linedef->sidenum[1] == 0xFFFF) {
I_Error("P_ApplyMacroProxies: Linedef %u has no back sector",
(unsigned)(c_linedef - pack->linedefs));
}
sidedef_idx = c_linedef->sidenum[1];
} else {
sidedef_idx = c_linedef->sidenum[0];
}
}
t_sector = &pack->sectors[pack->sidedefs[sidedef_idx].sector];
PROXY_INT(ML_DONTPEGTOP, t_sector->floorheight, c_front->textureoffset)
PROXY_INT(ML_DONTPEGBOTTOM, t_sector->ceilingheight, c_front->rowoffset)
PROXY_TEX(ML_EFFECT1, t_sector->floorpic, c_front->toptexture)
PROXY_TEX(ML_NOCLIMB, t_sector->ceilingpic, c_front->midtexture)
PROXY_INT(ML_EFFECT3, t_sector->lightlevel, c_back->textureoffset)
PROXY_INT(ML_EFFECT4, t_sector->tag, c_back->rowoffset)
if (c_flags & ML_BOUNCY) {
PROXY_TEXINT(ML_EFFECT2, t_sector->special, c_front->bottomtexture)
} else {
PROXY_TEXINT_EX(ML_EFFECT2, c_front->bottomtexture,
t_sector->special = SetSecSpecial(t_sector->special, 1, value))
PROXY_TEXINT_EX(ML_EFFECT5, c_back->toptexture,
t_sector->special = SetSecSpecial(t_sector->special, 2, value))
PROXY_TEXINT_EX(ML_NETONLY, c_back->midtexture,
t_sector->special = SetSecSpecial(t_sector->special, 3, value))
PROXY_TEXINT_EX(ML_NONET, c_back->bottomtexture,
t_sector->special = SetSecSpecial(t_sector->special, 4, value))
}
break;
}
case 83: { // Macro Proxy - Vertex
mapvertex_t *t_vertex;
size_t vertex_idx = SIZE_MAX;
if (c_flags & ML_TFERLINE) {
if (c_tag >= pack->num_vertices)
I_Error("P_ApplyMacroProxies: Vertex %d does not exist", c_linedef->tag);
vertex_idx = c_tag;
} else if (c_flags & ML_NETONLY) {
vertex_idx = c_linedef->v1;
} else if (c_flags & ML_NONET) {
vertex_idx = c_linedef->v2;
} else {
size_t j;
for (j = 0; j < macro->num_linedefs; j++) {
maplinedef_t *testing = &pack->linedefs[expander->start_linedefs + i];
int index = P_HasSharedVertex(c_linedef, testing);
if (index) {
vertex_idx = index == 1 ? c_linedef->v1 : c_linedef->v2;
break;
}
}
if (vertex_idx == SIZE_MAX)
I_Error("P_ApplyMacroProxies: No shared vertex found");
}
t_vertex = &pack->vertices[vertex_idx];
PROXY_INT(ML_DONTPEGTOP, t_vertex->x, (c_flags & ML_EFFECT6) ?
c_front->textureoffset : t_vertex->x + c_front->textureoffset)
PROXY_INT(ML_DONTPEGBOTTOM, t_vertex->y, (c_flags & ML_BOUNCY) ?
c_front->rowoffset : t_vertex->y + c_front->rowoffset)
break;
}
case 84: { // Macro Proxy - Thing
raw_mapthing_t *t_thing;
if ((c_flags & ML_NOCLIMB) || (c_flags & ML_EFFECT2))
t_thing = P_AppendThings(pack, 1, NULL);
if (c_flags & ML_EFFECT2) {
memset(t_thing, 0, sizeof(*t_thing));
} else {
raw_mapthing_t *find;
if (c_flags & ML_TFERLINE) {
if (c_tag >= pack->num_things) {
I_Error("P_ApplyMacroProxies: Thing %d does not exist",
c_linedef->tag);
}
find = &pack->things[c_tag];
} else {
INT16 type = 759; // Macro Thing Helper
if (!R_IsBlankTexture(c_back->bottomtexture))
type = atoi(R_NullTermTexture(c_back->bottomtexture));
find = P_FindThingByTypeAndAngle(type, c_linedef->tag, pack);
}
if (c_flags & ML_NOCLIMB)
*t_thing = *find;
else
t_thing = find;
}
PROXY_INT(ML_DONTPEGTOP, t_thing->x, (c_flags & ML_EFFECT6) ?
c_front->textureoffset : t_thing->x + c_front->textureoffset)
PROXY_INT(ML_DONTPEGBOTTOM, t_thing->y, (c_flags & ML_BOUNCY) ?
c_front->rowoffset : t_thing->y + c_front->rowoffset)
PROXY_FLAGS(ML_EFFECT1, t_thing->options, c_front)
PROXY_INT(ML_EFFECT3, t_thing->angle, c_back->textureoffset)
PROXY_INT(ML_EFFECT4, t_thing->options,
(t_thing->options & 0xF) | (c_back->rowoffset << ZSHIFT))
// These just need to be done manually since they're too complicated
// for the proxy setter macros.
PROXY_TEXINT_EX(ML_EFFECT5, c_back->toptexture,
t_thing->type = (t_thing->type & TYPEMASK) | (value << PARAMSHIFT))
PROXY_TEXINT_EX(ML_NETONLY, c_back->midtexture,
t_thing->type = (t_thing->type & ~TYPEMASK) | (value & TYPEMASK))
break;
}
}
}
// Clean up after ourselves.
#undef PROXY_INT
#undef PROXY_TEXINT
#undef PROXY_TEX
#undef PROXY_FLAGS
}
/** Frees all the allocated values in a macro expander and zeroes it.
*
* \param expander The expander to free.
*/
static void P_FreeExpander(macro_expander_t *expander)
{
Z_Free(expander->linedefs);
Z_Free(expander->vertices);
P_FreeVarList(&expander->locals);
memset(expander, 0, sizeof(*expander));
}
/** Expands every macro on the map using the already-built macro definitions in the
* macro state.
*
* \param state The state containing macro definitions.
* \param pack The entire pack of map geometry.
*/
static void P_ExpandMacros(macro_state_t *state, mappack_t *pack)
{
size_t i;
for (i = 0; i < pack->num_linedefs; i++) {
maplinedef_t *linedef = &pack->linedefs[i];
macro_expander_t expander;
// Search only for "Expand Macro - First Line" in this first pass.
if (linedef->special != 70)
continue;
memset(&expander, 0, sizeof(expander));
expander.macro = P_FindMacro(state, linedef->tag);
if (expander.macro == NULL) {
I_Error("P_ExpandMacros: Attempt to expand non-defined macro: tag %d",
linedef->tag);
}
P_BuildMacroParamLines(&expander, linedef, pack);
P_BuildMacroVariables(&expander, pack);
P_CopyMacroGeometry(&expander, pack);
// P_ApplyMacroVariables(&expander, pack); OHNO
// P_ApplyMacroProxies(&expander, pack); OHNO
// DEBUGGING
printf("expander = { // linedefs[%d]\n", i);
DebugPrintInt("macro->tag", expander.macro->tag);
DebugPrintArr("num_linedefs", expander.num_linedefs, "linedefs", expander.linedefs);
DebugPrintArr("num_vertices", expander.num_vertices, "vertices", expander.vertices);
DebugPrintSize("start_vertices", expander.start_vertices);
DebugPrintSize("start_sidedefs", expander.start_sidedefs);
DebugPrintSize("start_linedefs", expander.start_linedefs);
DebugPrintSize("start_sectors", expander.start_sectors);
DebugPrintVarList("locals", &expander.locals);
printf("}\n");
P_FreeExpander(&expander);
}
}
/** Frees all allocated values in the macro state including each individual macro
* definition and zeroes it.
*
* \param state The state to free everything from.
*/
static void P_FreeState(macro_state_t *state)
{
size_t i;
for (i = 0; i < state->num_macros; i++)
P_FreeMacro(&state->macros[i]);
Z_Free(state->macros);
P_FreeVarList(&state->globals);
memset(state, 0, sizeof(*state));
}
/** Performs everything pertaining to macros, building macro definitions and expanding
* them entirely.
*
* \param pack The entire pack of map geometry.
*/
static void P_ProcessMacros(mappack_t *pack)
{
macro_state_t state;
memset(&state, 0, sizeof(state));
P_BuildMacros(&state, pack);
P_ExpandMacros(&state, pack);
P_FreeState(&state);
}
/** Runs special map preprocessing on raw `map<geometry>_t` types before they get
* converted to normal `<geometry>_t` types. Since this happens after nodebuilding,
* visible geometry cannot be changed, but control sectors can be added and modified.
*
* \param pack The entire pack of map geometry.
*/
void P_PreprocessBinaryMapPack(mappack_t *pack)
{
// DEBUGGING
puts("\t\t\t\tMap Before");
DebugPrintMapPack(pack);
// Currently, only macros are done in the preprocessing stage, but more can be added later.
P_ProcessMacros(pack);
// DEBUGGING
puts("\n\t\t\t\tMap After");
DebugPrintMapPack(pack);
exit(0);
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2021 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/** \file p_preprocess.h
* \brief Runs preprocessing on raw WAD binary maps before they are converted
* to internal data structures.
*/
#ifndef __P_PREPROCESS__
#define __P_PREPROCESS__
#include "doomdata.h"
#include "doomdef.h"
#include "doomtype.h"
#include "r_main.h"
#include "w_wad.h"
/** Pack of all map geometry into one struct that can be passed around easily through
* preprocessing functions. Also contains preprocessing state.
*/
typedef struct
{
size_t num_vertices;
size_t num_sidedefs;
size_t num_linedefs;
size_t num_sectors;
size_t num_things;
mapvertex_t *vertices;
mapsidedef_t *sidedefs;
maplinedef_t *linedefs;
mapsector_t *sectors;
raw_mapthing_t *things;
//! Holds the tag returned by the last `P_GetNewTag` call.
INT16 last_tag;
} mappack_t;
// Creation and deletion functions for map packs.
void P_NewEmptyMapPack(mappack_t *pack);
void P_NewMapPackFromLumps(mappack_t *pack,
virtlump_t *virtvertices,
virtlump_t *virtsidedefs,
virtlump_t *virtlinedefs,
virtlump_t *virtsectors,
virtlump_t *virtthings);
void P_FreeMapPack(mappack_t *pack);
// General functions to add new geometry.
mapvertex_t *P_AppendVertices(mappack_t *pack, size_t new, const mapvertex_t *fill);
mapsidedef_t *P_AppendSidedefs(mappack_t *pack, size_t new, const mapsidedef_t *fill);
maplinedef_t *P_AppendLinedefs(mappack_t *pack, size_t new, const maplinedef_t *fill);
mapsector_t *P_AppendSectors (mappack_t *pack, size_t new, const mapsector_t *fill);
raw_mapthing_t *P_AppendThings (mappack_t *pack, size_t new, const raw_mapthing_t *fill);
// Special map pack functions.
INT16 P_GetNewTag(mappack_t *pack);
// Other utility functions.
int P_HasSharedVertex(maplinedef_t *first, maplinedef_t *second);
angle_t P_GetLinedefAngle(maplinedef_t *linedef, mappack_t *pack);
angle_t P_GetBidiLinedefAngle(maplinedef_t *linedef, mappack_t *pack);
int P_FindNextConnectedLinedef(maplinedef_t **linedef, maplinedef_t **previous,
INT16 special_low, INT16 special_high, mappack_t *pack);
raw_mapthing_t *P_FindThingByTypeAndAngle(INT16 type, INT16 angle, mappack_t *pack);
maplinedef_t *P_GetClosestAngleLinedef(maplinedef_t *linedef, mappack_t *pack);
// Actual preprocessor function that does all the magic.
void P_PreprocessBinaryMapPack(mappack_t *pack);
#endif
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "g_game.h" #include "g_game.h"
#include "p_local.h" #include "p_local.h"
#include "p_preprocess.h"
#include "p_setup.h" #include "p_setup.h"
#include "p_spec.h" #include "p_spec.h"
#include "p_saveg.h" #include "p_saveg.h"
...@@ -792,7 +793,7 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) ...@@ -792,7 +793,7 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
data += 3 * sizeof (INT16); // skip x y position, angle data += 3 * sizeof (INT16); // skip x y position, angle
type = READUINT16(data) & 4095; type = READUINT16(data) & TYPEMASK;
data += sizeof (INT16); // skip options data += sizeof (INT16); // skip options
switch (type) switch (type)
...@@ -930,7 +931,7 @@ void P_WriteThings(void) ...@@ -930,7 +931,7 @@ void P_WriteThings(void)
WRITEINT16(savebuf_p, mt->angle); WRITEINT16(savebuf_p, mt->angle);
temp = (INT16)(mt->type + ((INT16)mt->extrainfo << 12)); temp = (INT16)(mt->type + ((INT16)mt->extrainfo << PARAMSHIFT));
WRITEINT16(savebuf_p, temp); WRITEINT16(savebuf_p, temp);
WRITEUINT16(savebuf_p, mt->options); WRITEUINT16(savebuf_p, mt->options);
} }
...@@ -948,9 +949,8 @@ void P_WriteThings(void) ...@@ -948,9 +949,8 @@ void P_WriteThings(void)
// MAP LOADING FUNCTIONS // MAP LOADING FUNCTIONS
// //
static void P_LoadVertices(UINT8 *data) static void P_LoadVertices(mapvertex_t *mv)
{ {
mapvertex_t *mv = (mapvertex_t *)data;
vertex_t *v = vertexes; vertex_t *v = vertexes;
size_t i; size_t i;
...@@ -1019,9 +1019,8 @@ static void P_InitializeSector(sector_t *ss) ...@@ -1019,9 +1019,8 @@ static void P_InitializeSector(sector_t *ss)
ss->spawn_extra_colormap = NULL; ss->spawn_extra_colormap = NULL;
} }
static void P_LoadSectors(UINT8 *data) static void P_LoadSectors(mapsector_t *ms)
{ {
mapsector_t *ms = (mapsector_t *)data;
sector_t *ss = sectors; sector_t *ss = sectors;
size_t i; size_t i;
...@@ -1136,9 +1135,8 @@ static void P_SetLinedefV2(size_t i, UINT16 vertex_num) ...@@ -1136,9 +1135,8 @@ static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
lines[i].v2 = &vertexes[vertex_num]; lines[i].v2 = &vertexes[vertex_num];
} }
static void P_LoadLinedefs(UINT8 *data) static void P_LoadLinedefs(maplinedef_t *mld)
{ {
maplinedef_t *mld = (maplinedef_t *)data;
line_t *ld = lines; line_t *ld = lines;
size_t i; size_t i;
...@@ -1185,9 +1183,8 @@ static void P_InitializeSidedef(side_t *sd) ...@@ -1185,9 +1183,8 @@ static void P_InitializeSidedef(side_t *sd)
sd->colormap_data = NULL; sd->colormap_data = NULL;
} }
static void P_LoadSidedefs(UINT8 *data) static void P_LoadSidedefs(mapsidedef_t *msd)
{ {
mapsidedef_t *msd = (mapsidedef_t*)data;
side_t *sd = sides; side_t *sd = sides;
size_t i; size_t i;
...@@ -1233,28 +1230,17 @@ static void P_LoadSidedefs(UINT8 *data) ...@@ -1233,28 +1230,17 @@ static void P_LoadSidedefs(UINT8 *data)
case 413: // Change music case 413: // Change music
{ {
char process[8+1];
sd->toptexture = sd->midtexture = sd->bottomtexture = 0; sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') if (!R_IsBlankTexture(msd->bottomtexture))
{ sd->bottomtexture = get_number(R_NullTermTexture(msd->bottomtexture));
M_Memcpy(process,msd->bottomtexture,8);
process[8] = '\0';
sd->bottomtexture = get_number(process);
}
if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0') if (!R_IsBlankTexture(msd->midtexture))
{ sd->midtexture = get_number(R_NullTermTexture(msd->midtexture));
M_Memcpy(process,msd->midtexture,8);
process[8] = '\0';
sd->midtexture = get_number(process);
}
sd->text = Z_Malloc(7, PU_LEVEL, NULL); sd->text = Z_Malloc(7, PU_LEVEL, NULL);
if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')) if (isfrontside && !R_IsBlankTexture(msd->toptexture))
{ {
M_Memcpy(process,msd->toptexture,8); char *process = R_NullTermTexture(msd->toptexture);
process[8] = '\0';
// If they type in O_ or D_ and their music name, just shrug, // If they type in O_ or D_ and their music name, just shrug,
// then copy the rest instead. // then copy the rest instead.
...@@ -1273,13 +1259,8 @@ static void P_LoadSidedefs(UINT8 *data) ...@@ -1273,13 +1259,8 @@ static void P_LoadSidedefs(UINT8 *data)
case 414: // Play SFX case 414: // Play SFX
{ {
sd->toptexture = sd->midtexture = sd->bottomtexture = 0; sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0') if (!R_IsBlankTexture(msd->toptexture))
{ sd->toptexture = get_number(R_NullTermTexture(msd->toptexture));
char process[8+1];
M_Memcpy(process,msd->toptexture,8);
process[8] = '\0';
sd->toptexture = get_number(process);
}
break; break;
} }
...@@ -1295,18 +1276,10 @@ static void P_LoadSidedefs(UINT8 *data) ...@@ -1295,18 +1276,10 @@ static void P_LoadSidedefs(UINT8 *data)
case 461: // Spawns an object on the map based on texture offsets case 461: // Spawns an object on the map based on texture offsets
case 463: // Colorizes an object case 463: // Colorizes an object
{ {
char process[8*3+1]; char *process = R_CombineTextures(
memset(process,0,8*3+1); msd->toptexture, msd->midtexture, msd->bottomtexture);
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
break;
else
M_Memcpy(process,msd->toptexture,8);
if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
M_Memcpy(process+strlen(process), msd->midtexture, 8);
if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
sd->toptexture = get_number(process); sd->toptexture = get_number(process);
sd->midtexture = sd->bottomtexture = 0;
break; break;
} }
...@@ -1316,19 +1289,40 @@ static void P_LoadSidedefs(UINT8 *data) ...@@ -1316,19 +1289,40 @@ static void P_LoadSidedefs(UINT8 *data)
case 443: // Calls a named Lua function case 443: // Calls a named Lua function
case 459: // Control text prompt (named tag) case 459: // Control text prompt (named tag)
{ {
char process[8*3+1]; char *process = R_CombineTextures(
memset(process,0,8*3+1); msd->toptexture, msd->midtexture, msd->bottomtexture);
M_Memcpy(sd->text, process, strlen(process) + 1);
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
break;
}
case 72: // Define Macro
case 73: // Macro Parameters - Linedef
case 74: // Macro Parameters - Sidedef
case 75: // Macro Parameters - Sector
case 76: // Macro Parameters - Vertex
case 77: // Macro Parameters - Thing
case 78: // Macro Variables - New Tags
case 79: // Macro Variables - Calculation
case 80: // Macro Proxy - Linedef
case 81: // Macro Proxy - Sidedef
case 82: // Macro Proxy - Sector
case 83: // Macro Proxy - Vertex
case 84: // Macro Proxy - Thing
// Do nothing; these aren't real textures, and they're already handled in
// the preprocessing stage.
sd->toptexture = sd->midtexture = sd->bottomtexture = 0; sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0') break;
break;
else case 70: // Expand Macro - First Line
M_Memcpy(process,msd->toptexture,8); case 71: // Expand Macro - Continued
if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0') {
M_Memcpy(process+strlen(process), msd->midtexture, 8); // For these, we can't assume that these are textures (they could be numbers
if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') // or state names or anything, really), so we'll try and load them and do
M_Memcpy(process+strlen(process), msd->bottomtexture, 8); // nothing if they actually aren't.
sd->text = Z_Malloc(strlen(process)+1, PU_LEVEL, NULL); sd->midtexture = R_TextureNumForNameEx(msd->midtexture, true);
M_Memcpy(sd->text, process, strlen(process)+1); sd->toptexture = R_TextureNumForNameEx(msd->toptexture, true);
sd->bottomtexture = R_TextureNumForNameEx(msd->bottomtexture, true);
break; break;
} }
...@@ -1365,27 +1359,27 @@ static void P_LoadSidedefs(UINT8 *data) ...@@ -1365,27 +1359,27 @@ static void P_LoadSidedefs(UINT8 *data)
} }
} }
static void P_LoadThings(UINT8 *data) static void P_LoadThings(raw_mapthing_t *bmt)
{ {
mapthing_t *mt; mapthing_t *mt;
size_t i; size_t i;
for (i = 0, mt = mapthings; i < nummapthings; i++, mt++) for (i = 0, mt = mapthings; i < nummapthings; i++, mt++, bmt++)
{ {
mt->x = READINT16(data); mt->x = SHORT(bmt->x);
mt->y = READINT16(data); mt->y = SHORT(bmt->y);
mt->angle = READINT16(data); mt->angle = SHORT(bmt->angle);
mt->type = READUINT16(data); mt->type = SHORT(bmt->type);
mt->options = READUINT16(data); mt->options = SHORT(bmt->options);
mt->extrainfo = (UINT8)(mt->type >> 12); mt->extrainfo = (UINT8)(mt->type >> PARAMSHIFT);
Tag_FSet(&mt->tags, 0); Tag_FSet(&mt->tags, 0);
mt->scale = FRACUNIT; mt->scale = FRACUNIT;
memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args)); memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs)); memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
mt->pitch = mt->roll = 0; mt->pitch = mt->roll = 0;
mt->type &= 4095; mt->type &= TYPEMASK;
if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo)) if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height. mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
...@@ -2086,7 +2080,7 @@ static void P_ProcessLinedefsAfterSidedefs(void) ...@@ -2086,7 +2080,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
static boolean P_LoadMapData(const virtres_t *virt) static boolean P_LoadMapData(const virtres_t *virt)
{ {
virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL; mappack_t pack;
// Count map data. // Count map data.
if (udmf) // Count how many entries for each type we got in textmap. if (udmf) // Count how many entries for each type we got in textmap.
...@@ -2097,15 +2091,15 @@ static boolean P_LoadMapData(const virtres_t *virt) ...@@ -2097,15 +2091,15 @@ static boolean P_LoadMapData(const virtres_t *virt)
} }
else else
{ {
virtthings = vres_Find(virt, "THINGS"); virtlump_t *virtthings = vres_Find(virt, "THINGS");
virtvertexes = vres_Find(virt, "VERTEXES"); virtlump_t *virtvertices = vres_Find(virt, "VERTEXES");
virtsectors = vres_Find(virt, "SECTORS"); virtlump_t *virtsectors = vres_Find(virt, "SECTORS");
virtsidedefs = vres_Find(virt, "SIDEDEFS"); virtlump_t *virtsidedefs = vres_Find(virt, "SIDEDEFS");
virtlinedefs = vres_Find(virt, "LINEDEFS"); virtlump_t *virtlinedefs = vres_Find(virt, "LINEDEFS");
if (!virtthings) if (!virtthings)
I_Error("THINGS lump not found"); I_Error("THINGS lump not found");
if (!virtvertexes) if (!virtvertices)
I_Error("VERTEXES lump not found"); I_Error("VERTEXES lump not found");
if (!virtsectors) if (!virtsectors)
I_Error("SECTORS lump not found"); I_Error("SECTORS lump not found");
...@@ -2114,12 +2108,18 @@ static boolean P_LoadMapData(const virtres_t *virt) ...@@ -2114,12 +2108,18 @@ static boolean P_LoadMapData(const virtres_t *virt)
if (!virtlinedefs) if (!virtlinedefs)
I_Error("LINEDEFS lump not found"); I_Error("LINEDEFS lump not found");
// Traditional doom map format just assumes the number of elements from the lump sizes. P_NewMapPackFromLumps(&pack,
numvertexes = virtvertexes->size / sizeof (mapvertex_t); virtvertices, virtsidedefs, virtlinedefs, virtsectors, virtthings);
numsectors = virtsectors->size / sizeof (mapsector_t);
numsides = virtsidedefs->size / sizeof (mapsidedef_t); // Sneak in extra map geometry in the preprocessing stage.
numlines = virtlinedefs->size / sizeof (maplinedef_t); P_PreprocessBinaryMapPack(&pack);
nummapthings = virtthings->size / (5 * sizeof (INT16));
// Now we know our final amount of geometry.
numvertexes = pack.num_vertices;
numsides = pack.num_sidedefs;
numlines = pack.num_linedefs;
numsectors = pack.num_sectors;
nummapthings = pack.num_things;
} }
if (numvertexes <= 0) if (numvertexes <= 0)
...@@ -2150,11 +2150,13 @@ static boolean P_LoadMapData(const virtres_t *virt) ...@@ -2150,11 +2150,13 @@ static boolean P_LoadMapData(const virtres_t *virt)
P_LoadTextmap(); P_LoadTextmap();
else else
{ {
P_LoadVertices(virtvertexes->data); P_LoadVertices(pack.vertices);
P_LoadSectors(virtsectors->data); P_LoadSectors (pack.sectors);
P_LoadLinedefs(virtlinedefs->data); P_LoadLinedefs(pack.linedefs);
P_LoadSidedefs(virtsidedefs->data); P_LoadSidedefs(pack.sidedefs);
P_LoadThings(virtthings->data); P_LoadThings (pack.things);
P_FreeMapPack(&pack);
} }
P_ProcessLinedefsAfterSidedefs(); P_ProcessLinedefsAfterSidedefs();
...@@ -3023,8 +3025,11 @@ static void P_LinkMapData(void) ...@@ -3023,8 +3025,11 @@ static void P_LinkMapData(void)
} }
} }
// For maps in binary format, add multi-tags from linedef specials. This must be done /** Add tags to either the front or back sector for linedefs 97-99.
// before any linedef specials have been processed. *
* \param sector The sector to add the tags to.
* \param line The linedef to add the tags from.
*/
static void P_AddBinaryMapTagsFromLine(sector_t *sector, line_t *line) static void P_AddBinaryMapTagsFromLine(sector_t *sector, line_t *line)
{ {
Tag_Add(&sector->tags, Tag_FGet(&line->tags)); Tag_Add(&sector->tags, Tag_FGet(&line->tags));
...@@ -3042,6 +3047,9 @@ static void P_AddBinaryMapTagsFromLine(sector_t *sector, line_t *line) ...@@ -3042,6 +3047,9 @@ static void P_AddBinaryMapTagsFromLine(sector_t *sector, line_t *line)
} }
} }
/** Processes binary map tag specials before any other linedef specials or tag lists
* have been processed, which allows adding tags at will.
*/
static void P_AddBinaryMapTags(void) static void P_AddBinaryMapTags(void)
{ {
size_t i; size_t i;
......
...@@ -17,15 +17,40 @@ ...@@ -17,15 +17,40 @@
#ifndef __P_SPEC__ #ifndef __P_SPEC__
#define __P_SPEC__ #define __P_SPEC__
#include "r_defs.h"
extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint
extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs
extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
// GETSECSPECIAL (specialval, section) /** Gets the sector special value from a particular section.
// *
// Pulls out the special # from a particular section. * \param special The full sector special value to get the value from.
// * \param section The sector special section where the value is located.
#define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15) * \return The value in the specified sector special section.
*/
static inline INT16 GetSecSpecial(INT16 special, INT16 section)
{
int shift = (section - 1) * 4;
return (special >> shift) & 0xF;
}
// Alias to GetSecSpecial for compatibility.
#define GETSECSPECIAL GetSecSpecial
/** Sets a sector special value in a particular section and returns the modified value.
*
* \param special The full sector special value to modify.
* \param section The sector special section to change.
* \param value The value to put in the sector special section.
* \return The modified special.
*/
static inline INT16 SetSecSpecial(INT16 special, INT16 section, INT16 value)
{
int shift = (section - 1) * 4;
INT16 mask = 0xF << shift;
return (special & ~mask) | ((value << shift) & mask);
}
// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize. // This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
#define MAXFLATSIZE (2048<<FRACBITS) #define MAXFLATSIZE (2048<<FRACBITS)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "d_player.h" #include "d_player.h"
#include "r_data.h" #include "r_data.h"
#include "r_textures.h" #include "r_textures.h"
#include "tables.h"
// //
// POV related. // POV related.
......
...@@ -1623,18 +1623,19 @@ INT32 R_CheckTextureNumForName(const char *name) ...@@ -1623,18 +1623,19 @@ INT32 R_CheckTextureNumForName(const char *name)
} }
// //
// R_TextureNumForName // R_TextureNumForNameEx
// //
// Calls R_CheckTextureNumForName, aborts with error message. // Calls R_CheckTextureNumForName, can issue a warning on invalid texture.
// //
INT32 R_TextureNumForName(const char *name) INT32 R_TextureNumForNameEx(const char *name, boolean no_warning)
{ {
const INT32 i = R_CheckTextureNumForName(name); const INT32 i = R_CheckTextureNumForName(name);
if (i == -1) if (i == -1)
{ {
static INT32 redwall = -2; static INT32 redwall = -2;
CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name); if (!no_warning)
CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name);
if (redwall == -2) if (redwall == -2)
redwall = R_CheckTextureNumForName("REDWALL"); redwall = R_CheckTextureNumForName("REDWALL");
if (redwall != -1) if (redwall != -1)
...@@ -1643,3 +1644,71 @@ INT32 R_TextureNumForName(const char *name) ...@@ -1643,3 +1644,71 @@ INT32 R_TextureNumForName(const char *name)
} }
return i; return i;
} }
//
// R_TextureNumForName
//
// Calls R_TextureNumForNameEx and always issues the warning.
//
INT32 R_TextureNumForName(const char *name)
{
return R_TextureNumForNameEx(name, false);
}
/** Returns true if this texture is blank, i.e. it is "-".
*/
boolean R_IsBlankTexture(const char texture[8])
{
return texture[0] == '-' && texture[1] == '\0';
}
/** Returns a temporary buffer that is the same as the passed in texture, but null-
* terminated. For instance, this is necessary for `atoi` to not cause buffer overflows.
* The buffer is static, so only one texture may be used at a time.
*
* \param texture The texture to convert
* \return The null-terminated buffer with the texture.
*/
char *R_NullTermTexture(const char texture[8])
{
static char nulled[9];
memcpy(nulled, texture, 8);
nulled[8] = '\0';
return nulled;
}
/** Returns a temporary null-terminated buffer that is the combination of the passed in
* textures. Null characters from any of the textures are not included, and blank
* textures are ignored. The buffer is static, so only one combined may be used at a time.
*
* \param top The top texture of the sidedef
* \param mid The middle texture of the sidedef
* \param bot The bottom texture of the sidedef.
* \return The buffer with the combined textures.
*/
char *R_CombineTextures(const char top[8], const char mid[8], const char bot[8])
{
static char combined[25];
char *pos = combined;
size_t i;
memset(combined, 0, 25);
if (!R_IsBlankTexture(top)) {
for (i = 0; i < 8 && top[i] != '\0'; i++)
pos[i] = top[i];
pos += i;
}
if (!R_IsBlankTexture(mid)) {
for (i = 0; i < 8 && mid[i] != '\0'; i++)
pos[i] = mid[i];
pos += i;
}
if (!R_IsBlankTexture(bot)) {
for (i = 0; i < 8 && bot[i] != '\0'; i++)
pos[i] = bot[i];
pos += i;
}
return combined;
}
...@@ -95,9 +95,15 @@ void R_CheckFlatLength(size_t size); ...@@ -95,9 +95,15 @@ void R_CheckFlatLength(size_t size);
// Returns the texture number for the texture name. // Returns the texture number for the texture name.
INT32 R_TextureNumForName(const char *name); INT32 R_TextureNumForName(const char *name);
INT32 R_TextureNumForNameEx(const char *name, boolean no_warning);
INT32 R_CheckTextureNumForName(const char *name); INT32 R_CheckTextureNumForName(const char *name);
lumpnum_t R_GetFlatNumForName(const char *name); lumpnum_t R_GetFlatNumForName(const char *name);
// Helper functions for texture strings
boolean R_IsBlankTexture(const char texture[8]);
char *R_NullTermTexture(const char texture[8]);
char *R_CombineTextures(const char top[8], const char mid[8], const char bot[8]);
extern INT32 numtextures; extern INT32 numtextures;
#endif #endif