mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-28 12:30:46 +00:00
- rewrote the map hack parser using sc_man.
This unexpectedly turned out a complete rewrite so now it is under my own license. Also moved the remaining parts of map hack loading into the engine. Overall I have to say that the feature is not what I expected, it's merely used to fudge the positioning of model sprites and for adding Polymer lights.
This commit is contained in:
parent
8ca0e9b97a
commit
a4754d7f34
9 changed files with 244 additions and 486 deletions
|
@ -771,7 +771,6 @@ set (PCH_SOURCES
|
||||||
build/src/defs.cpp
|
build/src/defs.cpp
|
||||||
build/src/engine.cpp
|
build/src/engine.cpp
|
||||||
build/src/mdsprite.cpp
|
build/src/mdsprite.cpp
|
||||||
build/src/mhk.cpp
|
|
||||||
build/src/polymost.cpp
|
build/src/polymost.cpp
|
||||||
build/src/scriptfile.cpp
|
build/src/scriptfile.cpp
|
||||||
build/src/voxmodel.cpp
|
build/src/voxmodel.cpp
|
||||||
|
@ -790,6 +789,7 @@ set (PCH_SOURCES
|
||||||
core/gamecvars.cpp
|
core/gamecvars.cpp
|
||||||
core/gamecontrol.cpp
|
core/gamecontrol.cpp
|
||||||
core/inputstate.cpp
|
core/inputstate.cpp
|
||||||
|
core/maphack.cpp
|
||||||
core/mapinfo.cpp
|
core/mapinfo.cpp
|
||||||
core/searchpaths.cpp
|
core/searchpaths.cpp
|
||||||
core/screenjob.cpp
|
core/screenjob.cpp
|
||||||
|
|
|
@ -135,7 +135,6 @@ void StartLevel(MapRecord* level)
|
||||||
}
|
}
|
||||||
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
|
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
|
||||||
STAT_NewLevel(currentLevel->fileName);
|
STAT_NewLevel(currentLevel->fileName);
|
||||||
G_LoadMapHack(currentLevel->fileName);
|
|
||||||
wsrand(dbReadMapCRC(currentLevel->LabelName()));
|
wsrand(dbReadMapCRC(currentLevel->LabelName()));
|
||||||
gKillMgr.Clear();
|
gKillMgr.Clear();
|
||||||
gSecretMgr.Clear();
|
gSecretMgr.Clear();
|
||||||
|
|
|
@ -1049,6 +1049,8 @@ int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short
|
||||||
fr.Seek(0, FileReader::SeekSet);
|
fr.Seek(0, FileReader::SeekSet);
|
||||||
auto buffer = fr.Read();
|
auto buffer = fr.Read();
|
||||||
md4once(buffer.Data(), buffer.Size(), g_loadedMapHack.md4);
|
md4once(buffer.Data(), buffer.Size(), g_loadedMapHack.md4);
|
||||||
|
G_LoadMapHack(mapname);
|
||||||
|
|
||||||
if (CalcCRC32(buffer.Data(), buffer.Size() -4) != nCRC)
|
if (CalcCRC32(buffer.Data(), buffer.Size() -4) != nCRC)
|
||||||
{
|
{
|
||||||
Printf("%s: Map File does not match CRC", mapname.GetChars());
|
Printf("%s: Map File does not match CRC", mapname.GetChars());
|
||||||
|
|
|
@ -267,16 +267,14 @@ EXTERN int32_t guniqhudid;
|
||||||
EXTERN int32_t spritesortcnt;
|
EXTERN int32_t spritesortcnt;
|
||||||
extern int32_t g_loadedMapVersion;
|
extern int32_t g_loadedMapVersion;
|
||||||
|
|
||||||
typedef struct {
|
struct usermaphack_t
|
||||||
char *mhkfile;
|
{
|
||||||
char *title;
|
FString mhkfile;
|
||||||
uint8_t md4[16];
|
FString title;
|
||||||
} usermaphack_t;
|
uint8_t md4[16]{};
|
||||||
|
};
|
||||||
|
|
||||||
extern usermaphack_t g_loadedMapHack;
|
extern usermaphack_t g_loadedMapHack;
|
||||||
extern int compare_usermaphacks(const void *, const void *);
|
|
||||||
extern usermaphack_t *usermaphacks;
|
|
||||||
extern int32_t num_usermaphacks;
|
|
||||||
|
|
||||||
#if !defined DEBUG_MAIN_ARRAYS
|
#if !defined DEBUG_MAIN_ARRAYS
|
||||||
EXTERN spriteext_t *spriteext;
|
EXTERN spriteext_t *spriteext;
|
||||||
|
@ -597,7 +595,6 @@ void initspritelists(void);
|
||||||
int32_t engineLoadBoard(const char *filename, char flags, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
|
int32_t engineLoadBoard(const char *filename, char flags, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
|
||||||
int32_t engineLoadMHK(const char *filename);
|
int32_t engineLoadMHK(const char *filename);
|
||||||
void G_LoadMapHack(const char* filename);
|
void G_LoadMapHack(const char* filename);
|
||||||
void engineClearLightsFromMHK();
|
|
||||||
int32_t saveboard(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
|
int32_t saveboard(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
|
||||||
|
|
||||||
int32_t qloadkvx(int32_t voxindex, const char *filename);
|
int32_t qloadkvx(int32_t voxindex, const char *filename);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "palettecontainer.h"
|
#include "palettecontainer.h"
|
||||||
#include "mapinfo.h"
|
#include "mapinfo.h"
|
||||||
|
|
||||||
|
void AddUserMapHack(usermaphack_t&);
|
||||||
#if 0
|
#if 0
|
||||||
// For later
|
// For later
|
||||||
{
|
{
|
||||||
|
@ -2395,8 +2396,9 @@ static int32_t defsparser(scriptfile *script)
|
||||||
|
|
||||||
case T_MAPINFO:
|
case T_MAPINFO:
|
||||||
{
|
{
|
||||||
FString mapmd4string, title, mhkfile, dummy;
|
FString mapmd4string;
|
||||||
FScanner::SavedPos mapinfoend;
|
FScanner::SavedPos mapinfoend;
|
||||||
|
usermaphack_t mhk;
|
||||||
static const tokenlist mapinfotokens[] =
|
static const tokenlist mapinfotokens[] =
|
||||||
{
|
{
|
||||||
{ "mapfile", T_MAPFILE },
|
{ "mapfile", T_MAPFILE },
|
||||||
|
@ -2404,7 +2406,6 @@ static int32_t defsparser(scriptfile *script)
|
||||||
{ "mapmd4", T_MAPMD4 },
|
{ "mapmd4", T_MAPMD4 },
|
||||||
{ "mhkfile", T_MHKFILE },
|
{ "mhkfile", T_MHKFILE },
|
||||||
};
|
};
|
||||||
int32_t previous_usermaphacks = num_usermaphacks;
|
|
||||||
|
|
||||||
if (scriptfile_getbraces(script,&mapinfoend)) break;
|
if (scriptfile_getbraces(script,&mapinfoend)) break;
|
||||||
while (script->textptr < mapinfoend.SavedScriptPtr)
|
while (script->textptr < mapinfoend.SavedScriptPtr)
|
||||||
|
@ -2412,38 +2413,29 @@ static int32_t defsparser(scriptfile *script)
|
||||||
switch (getatoken(script,mapinfotokens,countof(mapinfotokens)))
|
switch (getatoken(script,mapinfotokens,countof(mapinfotokens)))
|
||||||
{
|
{
|
||||||
case T_MAPFILE:
|
case T_MAPFILE:
|
||||||
scriptfile_getstring(script,&dummy);
|
scriptfile_getstring(script,nullptr);
|
||||||
break;
|
break;
|
||||||
case T_MAPTITLE:
|
case T_MAPTITLE:
|
||||||
scriptfile_getstring(script,&title);
|
scriptfile_getstring(script,&mhk.title);
|
||||||
break;
|
break;
|
||||||
case T_MAPMD4:
|
case T_MAPMD4:
|
||||||
{
|
{
|
||||||
scriptfile_getstring(script,&mapmd4string);
|
scriptfile_getstring(script,&mapmd4string);
|
||||||
|
|
||||||
num_usermaphacks++;
|
|
||||||
usermaphacks = (usermaphack_t *)Xrealloc(usermaphacks, num_usermaphacks*sizeof(usermaphack_t));
|
|
||||||
usermaphack_t *newusermaphack = &usermaphacks[num_usermaphacks - 1];
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
char smallbuf[3] = { mapmd4string[2 * i], mapmd4string[2 * i + 1], 0 };
|
char smallbuf[3] = { mapmd4string[2 * i], mapmd4string[2 * i + 1], 0 };
|
||||||
newusermaphack->md4[i] = strtol(smallbuf, NULL, 16);
|
mhk.md4[i] = strtol(smallbuf, NULL, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_MHKFILE:
|
case T_MHKFILE:
|
||||||
scriptfile_getstring(script,&mhkfile);
|
scriptfile_getstring(script,&mhk.mhkfile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AddUserMapHack(mhk);
|
||||||
for (; previous_usermaphacks < num_usermaphacks; previous_usermaphacks++)
|
|
||||||
{
|
|
||||||
usermaphacks[previous_usermaphacks].mhkfile = mhkfile.IsNotEmpty() ? Xstrdup(mhkfile) : NULL;
|
|
||||||
usermaphacks[previous_usermaphacks].title = title.IsNotEmpty() ? Xstrdup(title) : NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3452,9 +3444,6 @@ int32_t loaddefinitionsfile(const char *fn)
|
||||||
DO_FREE_AND_NULL(faketilebuffer);
|
DO_FREE_AND_NULL(faketilebuffer);
|
||||||
faketilebuffersiz = 0;
|
faketilebuffersiz = 0;
|
||||||
|
|
||||||
if (usermaphacks != NULL)
|
|
||||||
qsort(usermaphacks, num_usermaphacks, sizeof(usermaphack_t), compare_usermaphacks);
|
|
||||||
|
|
||||||
if (!script) return -1;
|
if (!script) return -1;
|
||||||
|
|
||||||
Printf(PRINT_NONOTIFY, "\n");
|
Printf(PRINT_NONOTIFY, "\n");
|
||||||
|
|
|
@ -1025,14 +1025,6 @@ void engineUnInit(void)
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
TileFiles.CloseAll();
|
TileFiles.CloseAll();
|
||||||
|
|
||||||
for (bssize_t i = 0; i < num_usermaphacks; i++)
|
|
||||||
{
|
|
||||||
Xfree(usermaphacks[i].mhkfile);
|
|
||||||
Xfree(usermaphacks[i].title);
|
|
||||||
}
|
|
||||||
DO_FREE_AND_NULL(usermaphacks);
|
|
||||||
num_usermaphacks = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1987,6 +1979,9 @@ static FORCE_INLINE int32_t have_maptext(void)
|
||||||
|
|
||||||
static void enginePrepareLoadBoard(FileReader & fr, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum)
|
static void enginePrepareLoadBoard(FileReader & fr, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum)
|
||||||
{
|
{
|
||||||
|
memset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
|
||||||
|
memset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES + MAXUNIQHUDID));
|
||||||
|
|
||||||
initspritelists();
|
initspritelists();
|
||||||
ClearAutomap();
|
ClearAutomap();
|
||||||
|
|
||||||
|
@ -2004,7 +1999,7 @@ static void enginePrepareLoadBoard(FileReader & fr, vec3_t *dapos, int16_t *daan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t engineFinishLoadBoard(const vec3_t *dapos, int16_t *dacursectnum, int16_t numsprites, char myflags)
|
static int32_t engineFinishLoadBoard(const char *filename, const vec3_t *dapos, int16_t *dacursectnum, int16_t numsprites, char myflags)
|
||||||
{
|
{
|
||||||
int32_t i, realnumsprites=numsprites, numremoved;
|
int32_t i, realnumsprites=numsprites, numremoved;
|
||||||
|
|
||||||
|
@ -2077,6 +2072,8 @@ static int32_t engineFinishLoadBoard(const vec3_t *dapos, int16_t *dacursectnum,
|
||||||
|
|
||||||
guniqhudid = 0;
|
guniqhudid = 0;
|
||||||
|
|
||||||
|
G_LoadMapHack(filename);
|
||||||
|
|
||||||
return numremoved;
|
return numremoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2299,7 +2296,7 @@ int32_t engineLoadBoard(const char *filename, char flags, vec3_t *dapos, int16_t
|
||||||
artSetupMapArt(filename);
|
artSetupMapArt(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return engineFinishLoadBoard(dapos, dacursectnum, numsprites, myflags);
|
return engineFinishLoadBoard(filename, dapos, dacursectnum, numsprites, myflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2487,7 +2484,7 @@ int32_t engineLoadBoardV5V6(const char *filename, char fromwhere, vec3_t *dapos,
|
||||||
|
|
||||||
g_loadedMapVersion = mapversion;
|
g_loadedMapVersion = mapversion;
|
||||||
|
|
||||||
return engineFinishLoadBoard(dapos, dacursectnum, numsprites, 0);
|
return engineFinishLoadBoard(filename, dapos, dacursectnum, numsprites, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,444 +0,0 @@
|
||||||
|
|
||||||
#include "compat.h"
|
|
||||||
#include "build.h"
|
|
||||||
#include "scriptfile.h"
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
usermaphack_t g_loadedMapHack; // used only for the MD4 part
|
|
||||||
|
|
||||||
int compare_usermaphacks(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return memcmp(((usermaphack_t const *) a)->md4, ((usermaphack_t const *) b)->md4, 16);
|
|
||||||
}
|
|
||||||
usermaphack_t *usermaphacks;
|
|
||||||
int32_t num_usermaphacks;
|
|
||||||
|
|
||||||
#ifdef POLYMER
|
|
||||||
static int16_t maphacklightcnt=0;
|
|
||||||
static int16_t maphacklight[PR_MAXLIGHTS];
|
|
||||||
|
|
||||||
void engineClearLightsFromMHK()
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
for (i=0; i<maphacklightcnt; i++)
|
|
||||||
{
|
|
||||||
if (maphacklight[i] >= 0)
|
|
||||||
polymer_deletelight(maphacklight[i]);
|
|
||||||
maphacklight[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
maphacklightcnt = 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void engineClearLightsFromMHK() {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// loadmaphack
|
|
||||||
//
|
|
||||||
int32_t engineLoadMHK(const char *filename)
|
|
||||||
{
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
T_SPRITE = 0,
|
|
||||||
T_ANGOFF,
|
|
||||||
T_NOMODEL,
|
|
||||||
T_NOANIM,
|
|
||||||
T_PITCH,
|
|
||||||
T_ROLL,
|
|
||||||
T_MDPIVOTXOFF,
|
|
||||||
T_MDPIVOTYOFF,
|
|
||||||
T_MDPIVOTZOFF,
|
|
||||||
T_MDPOSITIONXOFF,
|
|
||||||
T_MDPOSITIONYOFF,
|
|
||||||
T_MDPOSITIONZOFF,
|
|
||||||
T_AWAY1,
|
|
||||||
T_AWAY2,
|
|
||||||
T_MHKRESET,
|
|
||||||
T_LIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct { const char *text; int32_t tokenid; } legaltokens [] =
|
|
||||||
{
|
|
||||||
{ "sprite", T_SPRITE },
|
|
||||||
{ "angleoff", T_ANGOFF },
|
|
||||||
{ "angoff", T_ANGOFF },
|
|
||||||
{ "notmd2", T_NOMODEL },
|
|
||||||
{ "notmd3", T_NOMODEL },
|
|
||||||
{ "notmd", T_NOMODEL },
|
|
||||||
{ "nomd2anim", T_NOANIM },
|
|
||||||
{ "nomd3anim", T_NOANIM },
|
|
||||||
{ "nomdanim", T_NOANIM },
|
|
||||||
{ "pitch", T_PITCH },
|
|
||||||
{ "roll", T_ROLL },
|
|
||||||
{ "mdxoff", T_MDPIVOTXOFF },
|
|
||||||
{ "mdyoff", T_MDPIVOTYOFF },
|
|
||||||
{ "mdzoff", T_MDPIVOTZOFF },
|
|
||||||
{ "mdpivxoff", T_MDPIVOTXOFF },
|
|
||||||
{ "mdpivyoff", T_MDPIVOTYOFF },
|
|
||||||
{ "mdpivzoff", T_MDPIVOTZOFF },
|
|
||||||
{ "mdpivotxoff", T_MDPIVOTXOFF },
|
|
||||||
{ "mdpivotyoff", T_MDPIVOTYOFF },
|
|
||||||
{ "mdpivotzoff", T_MDPIVOTZOFF },
|
|
||||||
{ "mdposxoff", T_MDPOSITIONXOFF },
|
|
||||||
{ "mdposyoff", T_MDPOSITIONYOFF },
|
|
||||||
{ "mdposzoff", T_MDPOSITIONZOFF },
|
|
||||||
{ "mdpositionxoff", T_MDPOSITIONXOFF },
|
|
||||||
{ "mdpositionyoff", T_MDPOSITIONYOFF },
|
|
||||||
{ "mdpositionzoff", T_MDPOSITIONZOFF },
|
|
||||||
{ "away1", T_AWAY1 },
|
|
||||||
{ "away2", T_AWAY2 },
|
|
||||||
{ "mhkreset", T_MHKRESET },
|
|
||||||
{ "light", T_LIGHT },
|
|
||||||
{ NULL, -1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
scriptfile *script = NULL;
|
|
||||||
char *tok, *cmdtokptr;
|
|
||||||
int32_t i;
|
|
||||||
int32_t whichsprite = -1;
|
|
||||||
static char fn[BMAX_PATH];
|
|
||||||
|
|
||||||
#ifdef POLYMER
|
|
||||||
int32_t toomanylights = 0;
|
|
||||||
|
|
||||||
engineClearLightsFromMHK();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (filename)
|
|
||||||
{
|
|
||||||
memset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
|
|
||||||
memset(spritesmooth, 0, sizeof(spritesmooth_t) *(MAXSPRITES+MAXUNIQHUDID));
|
|
||||||
strcpy(fn, filename);
|
|
||||||
script = scriptfile_fromfile(filename);
|
|
||||||
}
|
|
||||||
else if (fn[0])
|
|
||||||
{
|
|
||||||
// re-load
|
|
||||||
// XXX: what if we changed between levels? Could a wrong maphack be loaded?
|
|
||||||
script = scriptfile_fromfile(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!script)
|
|
||||||
{
|
|
||||||
fn[0] = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
tok = scriptfile_gettoken(script);
|
|
||||||
if (!tok) break;
|
|
||||||
for (i=0; legaltokens[i].text; i++) if (!stricmp(tok, legaltokens[i].text)) break;
|
|
||||||
cmdtokptr = script->ltextptr;
|
|
||||||
|
|
||||||
if (!filename && legaltokens[i].tokenid != T_LIGHT) continue;
|
|
||||||
|
|
||||||
switch (legaltokens[i].tokenid)
|
|
||||||
{
|
|
||||||
case T_SPRITE: // sprite <xx>
|
|
||||||
if (scriptfile_getnumber(script, &whichsprite)) break;
|
|
||||||
|
|
||||||
if ((unsigned) whichsprite >= (unsigned) MAXSPRITES)
|
|
||||||
{
|
|
||||||
// sprite number out of range
|
|
||||||
Printf("Sprite number out of range 0-%d on line %s:%d\n",
|
|
||||||
MAXSPRITES-1, script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
whichsprite = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case T_ANGOFF: // angoff <xx>
|
|
||||||
{
|
|
||||||
int32_t ang;
|
|
||||||
if (scriptfile_getnumber(script, &ang)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring angle offset directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].angoff = (int16_t) ang;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_NOMODEL: // notmd
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring not-MD2/MD3 directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].flags |= SPREXT_NOTMD;
|
|
||||||
break;
|
|
||||||
case T_NOANIM: // nomdanim
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring no-MD2/MD3-anim directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].flags |= SPREXT_NOMDANIM;
|
|
||||||
break;
|
|
||||||
case T_PITCH: // pitch <xx>
|
|
||||||
{
|
|
||||||
int32_t pitch;
|
|
||||||
if (scriptfile_getnumber(script, &pitch)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring pitch directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].pitch = (int16_t) pitch;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_ROLL: // roll <xx>
|
|
||||||
{
|
|
||||||
int32_t roll;
|
|
||||||
if (scriptfile_getnumber(script, &roll)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring roll directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].roll = (int16_t) roll;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPIVOTXOFF: // mdpivxoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdxoff/mdpivxoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].pivot_offset.x = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPIVOTYOFF: // mdpivyoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdyoff/mdpivyoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].pivot_offset.y = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPIVOTZOFF: // mdpivzoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdzoff/mdpivzoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].pivot_offset.z = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPOSITIONXOFF: // mdposxoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdposxoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].position_offset.x = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPOSITIONYOFF: // mdposyoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdposyoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].position_offset.y = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_MDPOSITIONZOFF: // mdposzoff <xx>
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
if (scriptfile_getnumber(script, &i)) break;
|
|
||||||
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mdposzoff directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].position_offset.z = i;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_AWAY1: // away1
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring moving away directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].flags |= SPREXT_AWAY1;
|
|
||||||
break;
|
|
||||||
case T_AWAY2: // away2
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring moving away directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spriteext[whichsprite].flags |= SPREXT_AWAY2;
|
|
||||||
break;
|
|
||||||
case T_MHKRESET: // mhkreset
|
|
||||||
{
|
|
||||||
if (whichsprite < 0)
|
|
||||||
{
|
|
||||||
// no sprite directive preceeding
|
|
||||||
Printf("Ignoring mhkreset directive because of absent/invalid sprite number on line %s:%d\n",
|
|
||||||
script->filename, scriptfile_getlinum(script, cmdtokptr));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto pSpriteExt = &spriteext[whichsprite];
|
|
||||||
pSpriteExt->angoff = 0;
|
|
||||||
pSpriteExt->flags &= ~(SPREXT_NOTMD|SPREXT_NOMDANIM|SPREXT_AWAY1|SPREXT_AWAY2);
|
|
||||||
pSpriteExt->pitch = 0;
|
|
||||||
pSpriteExt->roll = 0;
|
|
||||||
pSpriteExt->pivot_offset = {};
|
|
||||||
pSpriteExt->position_offset = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef POLYMER
|
|
||||||
case T_LIGHT: // light sector x y z range r g b radius faderadius angle horiz minshade maxshade priority tilenum
|
|
||||||
{
|
|
||||||
int32_t value;
|
|
||||||
int16_t lightid;
|
|
||||||
#pragma pack(push,1)
|
|
||||||
_prlight light;
|
|
||||||
#pragma pack(pop)
|
|
||||||
if (toomanylights)
|
|
||||||
break; // ignore further light defs
|
|
||||||
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.sector = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.x = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.y = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.z = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.range = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.color[0] = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.color[1] = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.color[2] = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.radius = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.faderadius = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.angle = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.horiz = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.minshade = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.maxshade = value;
|
|
||||||
scriptfile_getnumber(script, &value);
|
|
||||||
light.priority = value;
|
|
||||||
scriptfile_getsymbol(script, &value);
|
|
||||||
light.tilenum = value;
|
|
||||||
|
|
||||||
light.publicflags.emitshadow = 1;
|
|
||||||
light.publicflags.negative = 0;
|
|
||||||
|
|
||||||
if (videoGetRenderMode() == REND_POLYMER)
|
|
||||||
{
|
|
||||||
if (maphacklightcnt == PR_MAXLIGHTS)
|
|
||||||
{
|
|
||||||
Printf("warning: max light count %d exceeded, "
|
|
||||||
"ignoring further light defs\n", PR_MAXLIGHTS);
|
|
||||||
toomanylights = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lightid = polymer_addlight(&light);
|
|
||||||
if (lightid>=0)
|
|
||||||
maphacklight[maphacklightcnt++] = lightid;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif // POLYMER
|
|
||||||
|
|
||||||
default:
|
|
||||||
// unrecognised token
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptfile_close(script);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// taken out of the game modules - this code was repeated in all of them.
|
|
||||||
static int G_TryMapHack(const char* mhkfile)
|
|
||||||
{
|
|
||||||
int const failure = engineLoadMHK(mhkfile);
|
|
||||||
|
|
||||||
if (!failure)
|
|
||||||
Printf("Loaded map hack file \"%s\"\n", mhkfile);
|
|
||||||
|
|
||||||
return failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
void G_LoadMapHack(const char* filename)
|
|
||||||
{
|
|
||||||
FString hack = StripExtension(filename) + ".mhk";
|
|
||||||
|
|
||||||
if (G_TryMapHack(hack) && usermaphacks != NULL)
|
|
||||||
{
|
|
||||||
auto pMapInfo = (usermaphack_t*)bsearch(&g_loadedMapHack, usermaphacks, num_usermaphacks,
|
|
||||||
sizeof(usermaphack_t), compare_usermaphacks);
|
|
||||||
if (pMapInfo)
|
|
||||||
G_TryMapHack(pMapInfo->mhkfile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
219
source/core/maphack.cpp
Normal file
219
source/core/maphack.cpp
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
** maphack.cpp
|
||||||
|
**
|
||||||
|
** Newly implemented map hack loader, based on sc_man.
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2020 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
#include "build.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
|
usermaphack_t g_loadedMapHack; // used only for the MD4 part
|
||||||
|
static TArray<usermaphack_t> usermaphacks;
|
||||||
|
|
||||||
|
void AddUserMapHack(usermaphack_t& mhk)
|
||||||
|
{
|
||||||
|
usermaphacks.Push(mhk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t LoadMapHack(const char *filename)
|
||||||
|
{
|
||||||
|
int32_t currentsprite = -1;
|
||||||
|
|
||||||
|
FScanner sc;
|
||||||
|
int lump = fileSystem.FindFile(filename);
|
||||||
|
if (lump < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sc.OpenLumpNum(lump);
|
||||||
|
|
||||||
|
while (sc.GetString())
|
||||||
|
{
|
||||||
|
FString token = sc.String;
|
||||||
|
int currentsprite = -1;
|
||||||
|
auto validateSprite = [&]()
|
||||||
|
{
|
||||||
|
if (currentsprite < 0)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Using %s without a valid sprite", token.GetChars());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sc.Compare("sprite"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber())
|
||||||
|
{
|
||||||
|
currentsprite = sc.Number;
|
||||||
|
if (currentsprite < 0 || currentsprite >= MAXSPRITES)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Invalid sprite number %d", currentsprite);
|
||||||
|
currentsprite = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else currentsprite = -1;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("angleoff") || sc.Compare("angoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].angoff = (int16_t)sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("notmd") || sc.Compare("motmd2") || sc.Compare("motmd3"))
|
||||||
|
{
|
||||||
|
if (validateSprite())
|
||||||
|
spriteext[currentsprite].flags |= SPREXT_NOTMD;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("nomdanim") || sc.Compare("nomd2anim") || sc.Compare("nomd3anim"))
|
||||||
|
{
|
||||||
|
if (validateSprite())
|
||||||
|
spriteext[currentsprite].flags |= SPREXT_NOMDANIM;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("pitch"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].pitch = (int16_t)sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("roll"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].pitch = (int16_t)sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdxoff") || sc.Compare("mdpivxoff") || sc.Compare("mdpivotxoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].pivot_offset.x = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdyoff") || sc.Compare("mdpivyoff") || sc.Compare("mdpivotyoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].pivot_offset.y = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdzoff") || sc.Compare("mdpivzoff") || sc.Compare("mdpivotzoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].pivot_offset.z = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdposxoff") || sc.Compare("mdpositionxoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].position_offset.x = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdposyoff") || sc.Compare("mdpositionyoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].position_offset.x = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mdposzoff") || sc.Compare("mdpositionzoff"))
|
||||||
|
{
|
||||||
|
if (sc.CheckNumber() && validateSprite())
|
||||||
|
spriteext[currentsprite].position_offset.x = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("away1"))
|
||||||
|
{
|
||||||
|
if (validateSprite())
|
||||||
|
spriteext[currentsprite].flags |= SPREXT_AWAY1;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("away2"))
|
||||||
|
{
|
||||||
|
if (validateSprite())
|
||||||
|
spriteext[currentsprite].flags |= SPREXT_AWAY2;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("mhkreset"))
|
||||||
|
{
|
||||||
|
if (validateSprite())
|
||||||
|
{
|
||||||
|
auto& sx = spriteext[currentsprite];
|
||||||
|
sx.angoff = 0;
|
||||||
|
sx.flags &= ~(SPREXT_NOTMD | SPREXT_NOMDANIM | SPREXT_AWAY1 | SPREXT_AWAY2);
|
||||||
|
sx.pitch = 0;
|
||||||
|
sx.roll = 0;
|
||||||
|
sx.pivot_offset = {};
|
||||||
|
sx.position_offset = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sc.Compare("light"))
|
||||||
|
{
|
||||||
|
// skip over it - once lights are working this should be reactivated. Assignments were kept as documentation.
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.sector= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.x= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.y= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.z= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.range= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.color[0]= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.color[1]= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.color[2]= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.radius= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.faderadius= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.angle= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.horiz= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.minshade= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.maxshade= sc.Number;
|
||||||
|
sc.MustGetNumber();
|
||||||
|
//light.priority= sc.Number;
|
||||||
|
sc.MustGetString();
|
||||||
|
//light.tilenum= sc.Number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void G_LoadMapHack(const char* filename)
|
||||||
|
{
|
||||||
|
FString hack = StripExtension(filename) + ".mhk";
|
||||||
|
|
||||||
|
if (LoadMapHack(hack))
|
||||||
|
{
|
||||||
|
for (auto& mhk : usermaphacks)
|
||||||
|
{
|
||||||
|
if (!memcmp(g_loadedMapHack.md4, mhk.md4, 16))
|
||||||
|
{
|
||||||
|
LoadMapHack(mhk.mhkfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -871,7 +871,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
|
||||||
currentLevel = mi;
|
currentLevel = mi;
|
||||||
SECRET_SetMapName(mi->DisplayName(), mi->name);
|
SECRET_SetMapName(mi->DisplayName(), mi->name);
|
||||||
STAT_NewLevel(mi->fileName);
|
STAT_NewLevel(mi->fileName);
|
||||||
G_LoadMapHack(mi->fileName);
|
|
||||||
|
|
||||||
if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1))
|
if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue