raze/source/games/duke/src/gamedef.cpp

3266 lines
71 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
2020-05-13 14:19:39 +00:00
Copyright (C) 2020 - Christoph Oelckers
This file is part of Enhanced Duke Nukem 3D version 1.5 - Atomic Edition
Duke Nukem 3D is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Original Source: 1996 - Todd Replogle
Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
EDuke enhancements integrated: 04/13/2003 - Matt Saettler
Note: This source file IS NOT USED in EDuke source. It has been split
into many sub-files.
Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include <string.h>
#include "concmd.h"
#include "cmdlib.h"
#include "memarena.h"
#include "printf.h"
#include "filesystem.h"
#include "mapinfo.h"
#include "razemenu.h"
#include "global.h"
#include "m_argv.h"
#include "sounds.h"
#include "conlabel.h"
#include "conlabeldef.h"
#include "gi.h"
extern TArray<TPointer<MapRecord>> mapList;
BEGIN_DUKE_NS
enum { VERSIONCHECK = 41 };
//---------------------------------------------------------------------------
//
// definitions needed by the parser.
//
//---------------------------------------------------------------------------
enum labeltypes {
LABEL_ANY = -1,
LABEL_DEFINE = 1,
LABEL_STATE = 2,
LABEL_ACTOR = 4,
LABEL_ACTION = 8,
LABEL_AI = 16,
LABEL_MOVE = 32,
};
class labelstring
{
char text[64];
public:
char& operator[](size_t pos)
{
return text[pos];
}
operator const char* () { return text; }
const char* GetChars() { return text; }
int compare(const char* c) const { return strcmp(text, c); }
int comparei(const char* c) const { return stricmp(text, c); }
2020-07-23 14:31:26 +00:00
labelstring& operator=(const char* c) { strncpy(text, c, 64); text[63] = 0; return *this; }
};
struct TempMusic
{
int volnum;
int levlnum;
FString music;
};
//---------------------------------------------------------------------------
//
// the actual parser
//
//---------------------------------------------------------------------------
class ConCompiler
{
char* textptr = nullptr;
int line_number = 0;
int errorcount = 0, warningcount = 0; // was named 'error' and 'warning' which is too generic for public variables and may clash with other code.
int currentsourcefile = -1;
unsigned parsing_actor = 0, parsing_event = 0;
int parsing_state = 0;
int num_squigilly_brackets = 0;
int checking_ifelse = 0;
int checking_switch = 0;
labelstring parselabel= {};
// This is for situations where the music gets defined before the map. Since the map records do not exist yet, we need a temporary buffer.
TArray<TempMusic> tempMusic;
char parsebuf[1024];
TArray<char> parsebuffer; // global so that the storage is persistent across calls.
int casecount = 0;
int casescriptptr;
void ReportError(int error);
int getkeyword(const char* text);
FString translatelabeltype(int type);
bool ispecial(char c);
void skiptoendofline();
void skipwhitespace();
void skipblockcomment();
bool skipcomments();
int keyword(void);
void getlabel(void);
void appendlabelvalue(labeltypes type, int value);
void appendlabeladdress(labeltypes type, int offset = 0);
int transword(void);
int transnum(int type);
void checkforkeyword();
int parsecommand();
// EDuke 2.x additions
int CountCaseStatements();
public:
void compilecon(const char* filenam);
void setmusic();
int getErrorCount() { return errorcount; }
};
//---------------------------------------------------------------------------
//
// label data
//
//---------------------------------------------------------------------------
static const char* labeltypenames[] = {
"define",
"state",
"actor",
"action",
"ai",
"move"
};
struct labeldef
{
labelstring name;
labeltypes type;
int value;
int compare(const char* c) const { return name.compare(c); }
const char* GetChars() { return name.GetChars(); }
};
// These arrays contain the global output from the compiler.
static TArray<labeldef> labels;
TArray<int> ScriptCode;
//---------------------------------------------------------------------------
//
// synthesize the instruction list
//
//---------------------------------------------------------------------------
#define cmd(a) { #a, concmd_ ## a },
#define cmdx(a, b) { b, concmd_ ## a },
#define cmda(a,b) { #a, concmd_ ## b },
struct ConCommand
{
char cmd[20]; // the longest instruction name is 'ifactornotstayput' at 17 characters so if this changes this field must be enlarged.
int instr;
};
static ConCommand cmdList[] =
{
#include "condef.h"
};
#undef cmd
#undef cmdx
#undef cmda
static int cmdCmp(const void *a, const void *b)
{
auto A = (ConCommand*)a;
auto B = (ConCommand*)b;
return strcmp(A->cmd, B->cmd);
}
void SortCommands()
{
qsort(cmdList, countof(cmdList), sizeof(ConCommand), cmdCmp);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
enum
{
ERROR_ISAKEYWORD = 1,
ERROR_PARMUNDEFINED,
WARNING_DUPLICATEDEFINITION,
ERROR_COULDNOTFIND,
ERROR_VARREADONLY,
ERROR_NOTAGAMEDEF,
ERROR_NOTAGAMEVAR,
ERROR_OPENBRACKET,
ERROR_CLOSEBRACKET,
2020-05-15 20:59:13 +00:00
ERROR_NOENDSWITCH,
ERROR_SYMBOLNOTRECOGNIZED
};
void ConCompiler::ReportError(int error)
{
const char* fn = fileSystem.GetFileFullName(currentsourcefile);
switch (error)
{
case ERROR_ISAKEYWORD:
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Symbol '%s' is a key word.\n",
fn, line_number, parsebuf);
break;
case ERROR_PARMUNDEFINED:
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Parameter '%s' is undefined.\n",
fn, line_number, parsebuf);
break;
case WARNING_DUPLICATEDEFINITION:
Printf(TEXTCOLOR_YELLOW " * WARNING.(%s, line %d) Duplicate definition '%s' ignored.\n",
fn, line_number, parselabel.GetChars());
break;
case ERROR_COULDNOTFIND:
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Could not find '%s'.\n",
fn, line_number, parselabel.GetChars());
break;
case ERROR_VARREADONLY:
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Variable '%s' is read-only.\n",
fn, line_number, parselabel.GetChars());
break;
case ERROR_NOTAGAMEDEF:
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Symbol '%s' is not a Game Definition.\n",
fn, line_number, parselabel.GetChars());
break;
case ERROR_NOTAGAMEVAR:
Printf(TEXTCOLOR_RED " * ERROR! (%s, line %d) Symbol '%s' is not a defined Game Variable.\n",
fn, line_number, parselabel.GetChars());
break;
case ERROR_OPENBRACKET:
Printf(TEXTCOLOR_RED " * ERROR! (%s, line %d) Found more '{' than '}' before '%s'.\n",
fn, line_number, parsebuf);
break;
case ERROR_CLOSEBRACKET:
Printf(TEXTCOLOR_RED " * ERROR! (%s, line %d) Found more '}' than '{' before '%s'.\n",
fn, line_number, parsebuf);
break;
case ERROR_NOENDSWITCH:
Printf(TEXTCOLOR_RED " * ERROR! (%s, line %d) Did not find endswitch before '%s'.\n",
fn, line_number, parsebuf);
break;
case ERROR_SYMBOLNOTRECOGNIZED:
Printf(TEXTCOLOR_RED " * ERROR! (%s, line %d) Symbol '%s' is not recognized.\n",
fn, line_number, parsebuf);
break;
}
}
//---------------------------------------------------------------------------
//
// binary search for keyword
//
//---------------------------------------------------------------------------
int ConCompiler::getkeyword(const char* text)
{
ptrdiff_t min = 0;
ptrdiff_t max = countof(cmdList) - 1;
while (min <= max)
{
int mid = (min + max) >> 1;
const int comp = strcmp(text, cmdList[mid].cmd);
if (comp == 0)
{
return cmdList[mid].instr;
}
else if (comp > 0)
{
min = mid + 1;
}
else
{
max = mid - 1;
}
}
return -1;
}
//---------------------------------------------------------------------------
//
// label management
//
//---------------------------------------------------------------------------
FString ConCompiler::translatelabeltype(int type)
{
FString buf;
for (int i = 0; i < 6; i++)
{
if (!(type & (1 << i))) continue;
if (buf.Len()) buf += " or ";
buf += labeltypenames[i];
}
return buf;
}
int findlabel(const char* text, bool ignorecase = false)
{
for (unsigned j = 0; j < labels.Size(); j++)
{
if (labels[j].compare(text) == 0)
{
2020-05-12 21:55:33 +00:00
return j;
}
}
if (ignorecase)
{
for (unsigned j = 0; j < labels.Size(); j++)
{
if (labels[j].name.comparei(text) == 0)
{
return j;
}
}
}
return -1;
}
// This is for the 'spawn' CCMD.
int getlabelvalue(const char* text)
{
int lnum = findlabel(text, true);
if (lnum >= 0 && labels[lnum].type != LABEL_DEFINE) return -1;
return lnum < 0 ? -1 : labels[lnum].value;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool ConCompiler::ispecial(char c)
{
if (c == 0x0a)
{
line_number++;
return true;
}
// oh joy - we need to skip some more characters here to allow running broken scripts.
if (c == ' ' || c == '\t' || c == 0x0d || c == '(' || c == ')' || c == ',' || c == ';')
return true;
return false;
}
static bool isaltok(char c)
{
// isalnum pukes on negative input.
return c > 0 && (isalnum(c) || c == '{' || c == '}' || c == '/' || c == '*' || c == '-' || c == '_' || c == '.');
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ConCompiler::skiptoendofline()
{
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
textptr++;
}
void ConCompiler::skipwhitespace()
{
while (*textptr == ' ' || *textptr == '\t' || *textptr == '\r' || *textptr == '\n')
{
if (*textptr == '\n') line_number++;
textptr++;
}
}
void ConCompiler::skipblockcomment()
{
while (*textptr != '*' || textptr[1] != '/')
{
if (*textptr == '\n') line_number++;
if (*textptr == 0) return; // reached the end of the file
textptr++;
}
2020-05-12 20:52:49 +00:00
textptr += 2;
}
bool ConCompiler::skipcomments()
{
while (true)
{
skipwhitespace();
if (*textptr == '/' && textptr[1] == '/')
{
skiptoendofline();
continue;
}
if (*textptr == '/' && textptr[1] == '*')
{
skipblockcomment();
continue;
}
// stop if we got something else
// this line got added to skip over a stray semicolon in RR's COOT.CON.
if (ispecial(*textptr)) textptr++;
break;
}
return *textptr != 0;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int ConCompiler::keyword(void)
{
int i;
const char* temptextptr;
skipcomments();
temptextptr = textptr;
while (isaltok(*temptextptr) == 0)
{
temptextptr++;
if (*temptextptr == 0)
return 0;
}
i = 0;
while (isaltok(*temptextptr))
{
parsebuf[i] = *(temptextptr++);
i++;
}
parsebuf[i] = 0;
return getkeyword(parsebuf);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static int getlabeloffset(LABELS* pLabel, const char* psz)
{
// find the label psz in the table pLabel.
// returns the offset in the array for the label, or -1
2020-10-02 20:14:20 +00:00
int l = -1;
int i;
for (i = 0; pLabel[i].lId >= 0; i++)
{
if (!stricmp(pLabel[i].name, psz))
{
// printf("Label has flags of %02X\n",pLabel[i].flags);
return i;
}
}
return -1;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ConCompiler::getlabel(void)
{
2020-07-23 14:31:26 +00:00
int i;
skipcomments();
while (isalnum(*textptr & 0xff) == 0)
{
if (*textptr == 0x0a) line_number++;
textptr++;
if (*textptr == 0)
return;
}
i = 0;
while (ispecial(*textptr) == 0 && *textptr != ']')
parselabel[i++] = *(textptr++);
parselabel[i] = 0;
}
//---------------------------------------------------------------------------
//
// script buffer access wrappers.
//
//---------------------------------------------------------------------------
static void setscriptvalue(int offset, int value)
{
ScriptCode[offset] = value;
}
static int scriptpos()
2020-05-12 20:52:49 +00:00
{
return ScriptCode.Size();
2020-05-12 20:52:49 +00:00
}
static void appendscriptvalue(int value)
{
ScriptCode.Push(value);
}
static int popscriptvalue()
{
decltype(ScriptCode)::value_type p;
ScriptCode.Pop(p);
return p;
}
2020-05-12 20:52:49 +00:00
void reservescriptspace(int space)
{
for (int i = 0; i < space; i++) ScriptCode.Push(0);
}
/*
void pushlabeladdress()
{
labelcode.Push(script.Size());
}
*/
void ConCompiler::appendlabelvalue(labeltypes type, int value)
{
labels.Reserve(1);
labels.Last().type = type;
labels.Last().name = parselabel;
labels.Last().value = value;
}
void ConCompiler::appendlabeladdress(labeltypes type, int offset)
{
appendlabelvalue(type, ScriptCode.Size() + offset);
}
//---------------------------------------------------------------------------
//
//Returns its code #
//
//---------------------------------------------------------------------------
int ConCompiler::transword(void)
{
int i, l;
skipcomments();
while (isaltok(*textptr) == 0)
{
if (*textptr == 0x0a) line_number++;
if (*textptr == 0)
return -1;
textptr++;
}
l = 0;
while (isaltok(*(textptr + l)) && !(*(textptr + l) == '.'))
{
if (l < 1023)
{
parsebuf[l] = textptr[l];
}
l++;
}
parsebuf[l] = 0;
i = getkeyword(parsebuf);
if (i >= 0)
{
appendscriptvalue(i);
textptr += l;
return i;
}
textptr += l;
const char* fn = fileSystem.GetFileFullName(currentsourcefile);
if (parsebuf[0] == '{' && parsebuf[1] != 0)
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Expecting a SPACE or CR between '{' and '%s'.\n", fn, line_number, parsebuf + 1);
else if (parsebuf[0] == '}' && parsebuf[1] != 0)
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Expecting a SPACE or CR between '}' and '%s'.\n", fn, line_number, parsebuf + 1);
else if (parsebuf[0] == '/' && parsebuf[1] == '/' && parsebuf[2] != 0)
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Expecting a SPACE between '//' and '%s'.\n", fn, line_number, parsebuf + 2);
else if (parsebuf[0] == '/' && parsebuf[1] == '*' && parsebuf[2] != 0)
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Expecting a SPACE between '/*' and '%s'.\n", fn, line_number, parsebuf + 2);
else if (parsebuf[0] == '*' && parsebuf[1] == '/' && parsebuf[2] != 0)
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Expecting a SPACE between '*/' and '%s'.\n", fn, line_number, parsebuf + 2);
else
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Keyword expected, got '%s'.\n", fn, line_number, parsebuf + 2);
errorcount++;
return -1;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int ConCompiler::transnum(int type)
{
int l;
while (isaltok(*textptr) == 0)
{
if (*textptr == 0x0a) line_number++;
textptr++;
if (*textptr == 0)
return 0;
}
l = 0;
while (isaltok(*(textptr + l)))
{
if (l < 1023)
{
parsebuf[l] = textptr[l];
}
l++;
}
parsebuf[l] = 0;
if (getkeyword(parsebuf) >= 0)
{
errorcount++;
ReportError(ERROR_ISAKEYWORD);
textptr += l;
}
for (unsigned i = 0; i < labels.Size(); i++)
{
if (labels[i].compare(parsebuf) == 0)
{
// Non-values can be compared with 0.
if (labels[i].type & type || (labels[i].value == 0))
{
appendscriptvalue(labels[i].value);
textptr += l;
return labels[i].value;
}
appendscriptvalue(0);
textptr += l;
auto el = translatelabeltype(type);
auto gl = translatelabeltype(labels[i].type);
const char* fn = fileSystem.GetFileFullName(currentsourcefile);
Printf(TEXTCOLOR_YELLOW " * WARNING.(%s, line %d) %s: Expected a '%s' label but found a '%s' label instead.\n", fn, line_number, labels[i].GetChars(), el.GetChars(), gl.GetChars());
return -1; // valid label name, but wrong type
}
}
if (isdigit(*textptr) == 0 && *textptr != '-')
{
ReportError(ERROR_PARMUNDEFINED);
errorcount++;
textptr += l;
#ifdef FOR_LATER
if (GetDefID(parsebuf) >= 0)
{
Printf(TEXTCOLOR_ORANGE " Game Variable not expected\n");
}
#endif
return 0;
}
// Now it's getting nasty... With all of C's integer conversion functions we have to look for undefined behavior and truncation problems. This one's the least problematic approach
// that ignores octal conversion.
int64_t value;
char *outp;
bool ishex = (textptr[0] == '0' && tolower(textptr[1]) == 'x') || (textptr[0] == '-' && textptr[1] == '0' && tolower(textptr[2]) == 'x');
if (*textptr == '-') value = strtoll(textptr, &outp, ishex? 0 : 10);
else value = strtoull(textptr, &outp, ishex ? 0 : 10);
if (*outp != 0)
{
// conversion was not successful.
}
appendscriptvalue(int(value)); // truncate the parsed value to 32 bit.
if (type != LABEL_DEFINE && value != 0)
{
const char* fn = fileSystem.GetFileFullName(currentsourcefile);
Printf(TEXTCOLOR_YELLOW " * WARNING.(%s, line %d) Expected an identifier, got a numeric literal %d.\n", fn, line_number, (int)value);
}
textptr += l;
return int(value);
}
//---------------------------------------------------------------------------
//
// just to reduce some excessive boilerplate. This block reappeared
// endlessly in parsecommand
//
//---------------------------------------------------------------------------
void ConCompiler::checkforkeyword()
{
if (getkeyword(parselabel) >= 0)
{
errorcount++;
ReportError(ERROR_ISAKEYWORD);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int ConCompiler::CountCaseStatements()
{
int lCount;
char* temptextptr;
int savescript;
int savecase;
int temp_line_number;
temp_line_number = line_number;
casecount = 0;
temptextptr = textptr;
savescript = scriptpos();
savecase = casescriptptr;
casescriptptr = 0;
while (parsecommand() == 0)
{
;
}
// since we processed the endswitch, we need to re-increment checking_switch
checking_switch++;
textptr = temptextptr;
ScriptCode.Resize(savescript);
line_number = temp_line_number;
lCount = casecount;
casecount = 0;
casescriptptr = savecase;
return lCount;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int ConCompiler::parsecommand()
{
const char* fn = fileSystem.GetFileFullName(currentsourcefile);
int i, j, k;
int tempscrptr;
uint8_t done, temp_ifelse_check;// , tw;
int temp_line_number;
int temp_current_file;
int lnum;
// Do not count warnings here and allow more errors before bailing out.
if ((errorcount) > 64 || (*textptr == '\0') || (*(textptr + 1) == '\0')) return 1;
int tw = transword();
switch (tw)
{
default:
case -1:
return 0; //End
case concmd_state:
if (parsing_actor == 0 && parsing_state == 0)
{
getlabel();
popscriptvalue();
appendlabeladdress(LABEL_STATE);
parsing_state = 1;
return 0;
}
getlabel();
checkforkeyword();
lnum = findlabel(parselabel);
if (lnum < 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) State '%s' not found.\n", fn, line_number, parselabel.GetChars());
errorcount++;
return 0;
}
appendscriptvalue(labels[lnum].value);
return 0;
case concmd_ends:
if (parsing_state == 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'ends' with no 'state'.\n", fn, line_number);
errorcount++;
}
// else
{
if (num_squigilly_brackets > 0)
{
2020-05-12 16:44:58 +00:00
ReportError(ERROR_OPENBRACKET);
errorcount++;
}
if (num_squigilly_brackets < 0)
{
ReportError(ERROR_CLOSEBRACKET);
errorcount++;
}
parsing_state = 0;
}
return 0;
case concmd_gamevar:
{
// syntax: gamevar <var1> <initial value> <flags>
// defines var1 and sets initial value.
// flags are used to define usage
// (see top of this files for flags)
getlabel();
// Check to see it's already defined
popscriptvalue();
if (getkeyword(parselabel) >= 0)
{
errorcount++;
ReportError(ERROR_ISAKEYWORD);
return 0;
}
transnum(LABEL_DEFINE); // get initial value
j = popscriptvalue();
transnum(LABEL_DEFINE); // get flags
lnum = popscriptvalue();
if (strlen(parselabel) > (MAXVARLABEL - 1))
{
warningcount++;
Printf(TEXTCOLOR_RED " * WARNING.(%s, line %d) Variable Name '%s' too int (max is %d)\n", fn, line_number, parselabel.GetChars(), MAXVARLABEL - 1);
return 0;
}
int res = AddGameVar(parselabel, j, lnum & (~(GAMEVAR_FLAG_DEFAULT | GAMEVAR_FLAG_SECRET)));
if (res < 0)
{
errorcount++;
if (res == -1) Printf(TEXTCOLOR_RED " * ERROR.(%s, line %d) Duplicate game variable definition '%s'.\n", fn, line_number, parselabel.GetChars());
else if (res == -2) Printf(TEXTCOLOR_RED " * ERROR.(%s, line %d) '%s' maximum number of game variables exceeded.\n", fn, line_number, parselabel.GetChars());
return 0;
}
return 0;
}
case concmd_define:
getlabel();
checkforkeyword();
lnum = findlabel(parselabel);
if (lnum >= 0)
{
warningcount++;
ReportError(WARNING_DUPLICATEDEFINITION);
break;
}
transnum(LABEL_DEFINE);
i = popscriptvalue();
if (lnum < 0)
{
appendlabelvalue(LABEL_DEFINE, i);
}
popscriptvalue();
return 0;
case concmd_palfrom:
for (j = 0; j < 4; j++)
{
if (keyword() == -1)
transnum(LABEL_DEFINE);
else break;
}
while (j < 4)
{
appendscriptvalue(0);
j++;
}
return 0;
case concmd_move:
if (parsing_actor || parsing_state)
{
transnum(LABEL_MOVE);
j = 0;
while (keyword() == -1)
{
transnum(LABEL_DEFINE);
j |= popscriptvalue();
}
appendscriptvalue(j);
}
else
{
popscriptvalue();
getlabel();
// Check to see it's already defined
checkforkeyword();
for (i = 0; i < (int)labels.Size(); i++)
if (labels[i].compare(parselabel) == 0)
{
warningcount++;
Printf(TEXTCOLOR_RED " * WARNING.(%s, line %d) Duplicate move '%s' ignored.\n", fn, line_number, parselabel.GetChars());
break;
}
if (i == labels.Size())
appendlabeladdress(LABEL_MOVE);
for (j = 0; j < 2; j++)
{
if (keyword() >= 0) break;
transnum(LABEL_DEFINE);
}
for (k = j; k < 2; k++)
{
appendscriptvalue(0);
}
}
return 0;
case concmd_music:
{
popscriptvalue();
transnum(LABEL_DEFINE); // Volume Number (0/4)
k = popscriptvalue() - 1;
if (k < 0) specialmusic.Clear();
i = 0;
// get the file name...
while (keyword() == -1)
{
while (isaltok(*textptr) == 0)
{
if (*textptr == 0x0a) line_number++;
textptr++;
if (*textptr == 0) break;
}
j = 0;
parsebuffer.Clear();
while (isaltok(*(textptr + j)))
{
parsebuffer.Push(textptr[j]);
j++;
}
parsebuffer.Push(0);
if (k >= 0)
{
tempMusic.Reserve(1);
tempMusic.Last().volnum = k + 1;
tempMusic.Last().levlnum = i + 1;
tempMusic.Last().music = parsebuffer.Data();
}
else
{
specialmusic.Push(parsebuffer.Data());
}
textptr += j;
i++;
}
return 0;
}
case concmd_include:
{
popscriptvalue();
skipcomments();
parsebuffer.Clear();
while (isaltok(*textptr) == 0)
{
if (*textptr == 0x0a) line_number++;
textptr++;
if (*textptr == 0) break;
}
j = 0;
while (isaltok(*textptr))
{
parsebuffer.Push(*(textptr++));
j++;
}
parsebuffer.Push(0);
2020-05-13 22:04:14 +00:00
auto fni = fileSystem.FindFile(parsebuffer.Data());
if (fni < 0)
{
errorcount++;
2020-05-13 22:04:14 +00:00
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Could not find '%s'.\n", fn, line_number, parsebuffer.Data());
ReportError(ERROR_COULDNOTFIND);
return 0;
}
2020-05-13 22:04:14 +00:00
auto data = fileSystem.GetFileData(fni, 1);
temp_current_file = currentsourcefile;
currentsourcefile = fni;
temp_line_number = line_number;
line_number = 1;
temp_ifelse_check = checking_ifelse;
checking_ifelse = 0;
auto origtptr = textptr;
textptr = (char*)data.Data();
do
done = parsecommand();
while (done == 0);
textptr = origtptr;
line_number = temp_line_number;
checking_ifelse = temp_ifelse_check;
currentsourcefile = temp_current_file;
2020-05-13 22:04:14 +00:00
if (*textptr == '"') textptr++; // needed for RR.
return 0;
}
case concmd_ai:
if (parsing_actor || parsing_state)
transnum(LABEL_AI);
else
{
popscriptvalue();
getlabel();
checkforkeyword();
lnum = findlabel(parselabel);
2020-05-12 20:52:49 +00:00
if (lnum >= 0)
{
warningcount++;
Printf(TEXTCOLOR_RED " * WARNING.(%s, line %d) Duplicate ai '%s' ignored.\n", fn, line_number, parselabel.GetChars());
2020-05-12 20:52:49 +00:00
}
else appendlabeladdress(LABEL_AI);
for (j = 0; j < 3; j++)
{
if (keyword() >= 0) break;
if (j == 2)
{
k = 0;
while (keyword() == -1)
{
transnum(LABEL_DEFINE);
k |= popscriptvalue();
}
appendscriptvalue(k);
return 0;
}
else transnum(j==0? LABEL_ACTION : LABEL_MOVE);
}
for (k = j; k < 3; k++)
{
appendscriptvalue(0);
}
}
return 0;
case concmd_action:
if (parsing_actor || parsing_state)
transnum(LABEL_ACTION);
else
{
popscriptvalue();
getlabel();
2020-05-12 20:52:49 +00:00
checkforkeyword();
lnum = findlabel(parselabel);
2020-05-12 20:52:49 +00:00
if (lnum >= 0)
{
2020-05-12 20:52:49 +00:00
warningcount++;
Printf(TEXTCOLOR_RED " * WARNING.(%s, line %d) Duplicate event '%s' ignored.\n", fn, line_number, parselabel.GetChars());
}
else appendlabeladdress(LABEL_ACTION);
for (j = 0; j < 5; j++)
{
if (keyword() >= 0) break;
transnum(LABEL_DEFINE);
}
for (k = j; k < 5; k++)
{
appendscriptvalue(0);
}
}
return 0;
case concmd_actor:
2020-05-12 21:55:33 +00:00
case concmd_useractor: // merged with 'actor' because the code is identical except for the added type parameter.
2020-05-12 20:52:49 +00:00
{
if (parsing_state)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'actor' within 'state'.\n", fn, line_number);
errorcount++;
}
if (parsing_actor)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'actor' within 'actor'.\n", fn, line_number);
errorcount++;
}
num_squigilly_brackets = 0;
popscriptvalue();
2020-05-12 20:52:49 +00:00
parsing_actor = scriptpos();
2020-05-12 21:55:33 +00:00
if (tw == concmd_useractor)
{
transnum(LABEL_DEFINE);
2020-05-12 21:55:33 +00:00
j = popscriptvalue();
}
transnum(LABEL_DEFINE);
2020-05-12 20:52:49 +00:00
lnum = popscriptvalue();
gs.actorinfo[lnum].scriptaddress = parsing_actor; // TRANSITIONAL should only store an index
2020-05-12 21:55:33 +00:00
if (tw == concmd_useractor)
{
if (j & 1)
gs.actorinfo[lnum].flags |= SFLAG_BADGUY;
2020-05-12 21:55:33 +00:00
if (j & 2)
gs.actorinfo[lnum].flags |= (SFLAG_BADGUY | SFLAG_BADGUYSTAYPUT);
2020-05-12 21:55:33 +00:00
}
for (j = 0; j < 4; j++)
{
if (j == 3)
{
j = 0;
while (keyword() == -1)
{
transnum(LABEL_DEFINE);
2020-05-12 20:52:49 +00:00
j |= popscriptvalue();
}
appendscriptvalue(j);
break;
}
else
{
if (keyword() >= 0)
{
2020-05-12 20:52:49 +00:00
reservescriptspace(4 - j);
break;
}
switch (j)
{
case 0: transnum(LABEL_DEFINE); break;
case 1: transnum(LABEL_ACTION); break;
case 2: transnum(LABEL_MOVE | LABEL_DEFINE); break;
}
2020-05-12 21:55:33 +00:00
// This code was originally here but is a no-op, because both source and destination are the same here.
//*(parsing_actor + j) = *(scriptaddress - 1);
}
}
checking_ifelse = 0;
return 0;
2020-05-12 20:52:49 +00:00
}
case concmd_onevent:
if (parsing_state)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'onevent' within 'state'.\n", fn, line_number);
errorcount++;
}
if (parsing_actor)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'onevent' within 'actor'.\n", fn, line_number);
errorcount++;
}
num_squigilly_brackets = 0;
popscriptvalue();
parsing_event = parsing_actor = scriptpos();
transnum(LABEL_DEFINE);
j = popscriptvalue();
if (j< 0 || j> EVENT_MAXEVENT)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Invalid Event ID.\n", fn, line_number);
errorcount++;
return 0;
}
apScriptGameEvent[j] = parsing_event;
checking_ifelse = 0;
return 0;
2020-05-12 21:55:33 +00:00
case concmd_cstat:
transnum(LABEL_DEFINE);
2020-05-12 21:55:33 +00:00
#if 0
// the following checks are being performed by EDuke32 and RedNukem - not sure if this really should be done.
// DukeGDX and RedneckGDX do not perform these checks. Code pasted here for making a decision later.
2020-05-12 21:55:33 +00:00
i = popscriptvalue();
if (i == 32767)
{
2020-05-12 21:55:33 +00:00
i = 32768;
Printf(TEXTCOLOR_RED " * WARNING!(%s, line %d) tried to set cstat 32767, using 32768 instead.\n", fn, line_number);
warningcount++;
}
2020-05-12 21:55:33 +00:00
else if ((i & 48) == 48)
{
2020-05-12 21:55:33 +00:00
Printf(TEXTCOLOR_RED " * WARNING!(%s, line %d) tried to set cstat %d, using %d instead.\n", fn, line_number, i, i ^ 48);
i ^= 48;
warningcount++;
}
2020-05-12 21:55:33 +00:00
appendscriptvalue(i);
#endif
return 0;
2020-05-12 21:55:33 +00:00
case concmd_sound:
case concmd_globalsound:
case concmd_soundonce:
case concmd_stopsound:
case concmd_lotsofglass:
case concmd_strength:
case concmd_shoot:
case concmd_addphealth:
case concmd_spawn:
case concmd_count:
case concmd_endofgame:
case concmd_spritepal:
case concmd_cactor:
case concmd_quote:
case concmd_money:
case concmd_addkills:
case concmd_debug:
case concmd_addstrength:
case concmd_cstator:
case concmd_mail:
case concmd_paper:
case concmd_sleeptime:
case concmd_clipdist:
case concmd_isdrunk:
case concmd_iseat:
case concmd_newpic:
case concmd_espawn:
transnum(LABEL_DEFINE);
return 0;
case concmd_addammo:
case concmd_addweapon:
case concmd_sizeto:
case concmd_sizeat:
case concmd_debris:
case concmd_addinventory:
case concmd_guts:
transnum(LABEL_DEFINE);
transnum(LABEL_DEFINE);
2020-05-12 21:55:33 +00:00
return 0;
case concmd_hitradius:
transnum(LABEL_DEFINE);
transnum(LABEL_DEFINE);
transnum(LABEL_DEFINE);
transnum(LABEL_DEFINE);
transnum(LABEL_DEFINE);
break;
case concmd_else:
if (checking_ifelse)
{
checking_ifelse--;
tempscrptr = scriptpos();
reservescriptspace(1); //Leave a spot for the fail location
parsecommand();
setscriptvalue(tempscrptr, scriptpos());
}
else
{
popscriptvalue();
warningcount++;
Printf(TEXTCOLOR_YELLOW " * WARNING.(%s, line %d) Found 'else' with no 'if', ignored.\n", fn, line_number);
}
return 0;
case concmd_setvar:
case concmd_addvar:
case concmd_subvar:
case concmd_randvar:
case concmd_mulvar:
case concmd_divvar:
case concmd_modvar:
case concmd_andvar:
case concmd_orvar:
case concmd_xorvar:
// syntax: [rand|add|set]var <var1> <const1>
// sets var1 to const1
// adds const1 to var1 (const1 can be negative...)
// get the ID of the DEF
getlabel();
// Check to see if it's a keyword
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
transnum(LABEL_DEFINE); // the number to check against...
return 0;
case concmd_setvarvar:
case concmd_addvarvar:
case concmd_subvarvar:
case concmd_mulvarvar:
case concmd_divvarvar:
case concmd_modvarvar:
case concmd_andvarvar:
case concmd_orvarvar:
case concmd_xorvarvar:
case concmd_randvarvar:
case concmd_sin:
case concmd_gmaxammo:
case concmd_smaxammo:
// syntax: [add|set]varvar <var1> <var2>
// sets var1 = var2
// adds var1 and var2 with result in var1
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
return 0;
case concmd_ifvarvarg:
case concmd_ifvarvarl:
case concmd_ifvarvare:
case concmd_ifvarvarn:
case concmd_ifvarvarand:
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
goto if_common;
case concmd_ifvarl:
case concmd_ifvarg:
case concmd_ifvare:
case concmd_ifvarn:
case concmd_ifvarand:
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
transnum(LABEL_DEFINE); // the number to check against...
goto if_common;
case concmd_addlogvar:
// syntax: addlogvar <var>
appendscriptvalue(currentsourcefile);
appendscriptvalue(line_number);
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
return 0;
case concmd_addlog:
// syntax: addlog
// source file.
appendscriptvalue(currentsourcefile);
// prints the line number in the log file.
appendscriptvalue(line_number);
return 0;
case concmd_ifp:
j = 0;
do
{
transnum(LABEL_DEFINE);
j |= popscriptvalue();
} while (keyword() == -1);
appendscriptvalue(j);
goto if_common;
case concmd_ifsound:
transnum(LABEL_DEFINE);
goto if_common;
case concmd_ifpinventory:
transnum(LABEL_DEFINE);
case concmd_ifrnd:
case concmd_ifpdistl:
case concmd_ifpdistg:
case concmd_ifai:
case concmd_ifwasweapon:
case concmd_ifaction:
case concmd_ifactioncount:
case concmd_ifmove:
case concmd_ifcount:
case concmd_ifactor:
case concmd_ifstrength:
case concmd_ifspawnedby:
case concmd_ifgapzl:
case concmd_iffloordistl:
case concmd_ifceilingdistl:
// case 74:
case concmd_ifphealthl:
case concmd_ifspritepal:
case concmd_ifgotweaponce:
case concmd_ifangdiffl:
case concmd_ifactorhealthg:
case concmd_ifactorhealthl:
case concmd_ifsoundid:
case concmd_ifsounddist:
transnum(tw == concmd_ifai? LABEL_AI : tw == concmd_ifaction? LABEL_ACTION : tw == concmd_ifmove? LABEL_MOVE : LABEL_DEFINE);
case concmd_ifonwater:
case concmd_ifinwater:
case concmd_ifactornotstayput:
case concmd_ifcansee:
case concmd_ifhitweapon:
case concmd_ifsquished:
case concmd_ifdead:
case concmd_ifcanshoottarget:
case concmd_ifhitspace:
case concmd_ifoutside:
case concmd_ifmultiplayer:
case concmd_ifinspace:
case concmd_ifbulletnear:
case concmd_ifrespawn:
case concmd_ifinouterspace:
case concmd_ifnotmoving:
case concmd_ifawayfromwall:
case concmd_ifcanseetarget:
case concmd_ifnosounds:
case concmd_ifnocover:
case concmd_ifhittruck:
case concmd_iftipcow:
case concmd_ifonmud:
case concmd_ifcoop:
case concmd_ifmotofast:
case concmd_ifwind:
case concmd_ifonmoto:
case concmd_ifonboat:
case concmd_ifsizedown:
case concmd_ifplaybackon:
// case concmd_iffindnewspot: // RRDH
// case concmd_ifpupwind:
if_common: // this code is identical for all 'if...'instructions.
{
tempscrptr = scriptpos();
reservescriptspace(1); //Leave a spot for the fail location
skipcomments();
parsecommand();
setscriptvalue(tempscrptr, scriptpos());
auto k = keyword();
// Cannot be done - the code starts misbehaving with this check, it is especially noticeable on the soldiers in NAM.
// Unfortunately this means one less error check, but ultimately CON is too broken to begin with anyway
#if 0
if (k == concmd_else) / only increment checking_ifelse if there actually is an else. Otherwise this would break the entire checking logic and render it non-functional
#endif
checking_ifelse++;
return 0;
}
case concmd_leftbrace:
num_squigilly_brackets++;
do
done = parsecommand();
while (done == 0);
return 0;
case concmd_rightbrace:
num_squigilly_brackets--;
if (num_squigilly_brackets < 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found more '}' than '{'.\n", fn, line_number);
errorcount++;
}
return 1;
case concmd_betaname:
popscriptvalue();
// not used anywhere, just parse over it.
skiptoendofline();
return 0;
case concmd_definevolumename:
2021-05-01 22:35:56 +00:00
{
popscriptvalue();
transnum(LABEL_DEFINE);
j = popscriptvalue();
while (*textptr == ' ' || *textptr == '\t') textptr++;
i = 0;
parsebuffer.Clear();
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
2021-05-01 22:35:56 +00:00
// We need both a volume and a cluster for this new episode.
auto vol = MustFindVolume(j);
auto clust = MustFindCluster(j + 1);
vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i);
if (j > 0) vol->flags |= VF_SHAREWARELOCK;
return 0;
2021-05-01 22:35:56 +00:00
}
case concmd_defineskillname:
popscriptvalue();
transnum(LABEL_DEFINE);
j = popscriptvalue();
while (*textptr == ' ' || *textptr == '\t') textptr++;
i = 0;
parsebuffer.Clear();
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
gSkillNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i);
return 0;
case concmd_definelevelname:
{
popscriptvalue();
transnum(LABEL_DEFINE);
j = popscriptvalue();
transnum(LABEL_DEFINE);
k = popscriptvalue();
while (*textptr == ' ' || *textptr == '\t') textptr++;
i = 0;
parsebuffer.Clear();
while (*textptr != ' ' && *textptr != '\t' && *textptr != 0x0a && *textptr != 0x0d && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
auto map = FindMapByIndexOnly(j + 1, k + 1);
if (!map) map = AllocateMap();
map->SetFileName(parsebuffer.Data());
2021-05-01 22:35:56 +00:00
if (k == 0)
{
auto vol = MustFindVolume(j);
vol->startmap = map->labelName;
}
while (*textptr == ' ' || *textptr == '\t') textptr++;
map->parTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++;
map->designerTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
SetLevelNum(map, makelevelnum(j + 1, k + 1));
map->mapindex = k + 1;
map->cluster = j + 1;
textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++;
i = 0;
parsebuffer.Clear();
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
2020-09-05 20:14:00 +00:00
map->name = FStringTable::MakeMacro(parsebuffer.Data());
return 0;
}
case concmd_definequote:
popscriptvalue();
transnum(LABEL_DEFINE);
k = popscriptvalue();
if (k >= MAXQUOTES)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Quote number exceeds limit of %d.\n", fn, line_number, MAXQUOTES);
errorcount++;
}
i = 0;
while (*textptr == ' ' || *textptr == '\t') textptr++;
parsebuffer.Clear();
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
quoteMgr.InitializeQuote(k, parsebuffer.Data(), true);
return 0;
case concmd_definesound:
{
popscriptvalue();
transnum(LABEL_DEFINE);
k = popscriptvalue();
i = 0;
while (*textptr == ' ' || *textptr == '\t') textptr++;
parsebuffer.Clear();
while (*textptr != ' ' && *textptr != '\t' && *textptr != 0) // JBF 20040127: end of file checked
{
parsebuffer.Push(*textptr);
textptr++, i++;
}
parsebuffer.Push(0);
transnum(LABEL_DEFINE);
int ps = popscriptvalue();
transnum(LABEL_DEFINE);
int pe = popscriptvalue();
transnum(LABEL_DEFINE);
int pr = popscriptvalue();
transnum(LABEL_DEFINE);
int m = popscriptvalue();
transnum(LABEL_DEFINE);
int vo = popscriptvalue();
S_DefineSound(k, parsebuffer.Data(), ps, pe, pr, m, vo, 1.f);
return 0;
}
case concmd_endevent:
if (parsing_event == 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'endevent' without defining 'onevent'.\n", fn, line_number);
errorcount++;
}
// else
{
if (num_squigilly_brackets > 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found more '{' than '}' before 'endevent'.\n", fn, line_number);
errorcount++;
}
parsing_event = 0;
parsing_actor = 0;
}
return 0;
case concmd_enda:
if (parsing_actor == 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'enda' without defining 'actor'.\n", fn, line_number);
errorcount++;
}
// else
{
if (num_squigilly_brackets > 0)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found more '{' than '}' before 'enda'.\n", fn, line_number);
errorcount++;
}
parsing_actor = 0;
}
return 0;
case concmd_break:
if (checking_switch)
{
return 1;
}
case concmd_fall:
case concmd_tip:
// case 21:
case concmd_killit: //KILLIT
case concmd_resetactioncount:
case concmd_pstomp:
case concmd_resetplayer:
case concmd_resetcount:
case concmd_wackplayer:
case concmd_operate:
case concmd_respawnhitag:
case concmd_getlastpal:
case concmd_pkick:
case concmd_mikesnd:
case concmd_tossweapon:
case concmd_nullop:
case concmd_destroyit:
case concmd_larrybird:
case concmd_strafeleft:
case concmd_straferight:
case concmd_slapplayer:
//case 122:
case concmd_tearitup:
case concmd_smackbubba:
case concmd_soundtagonce:
case concmd_soundtag:
case concmd_smacksprite:
case concmd_fakebubba:
case concmd_mamatrigger:
case concmd_mamaspawn:
case concmd_mamaquake:
case concmd_mamaend:
case concmd_garybanjo:
case concmd_motoloopsnd:
case concmd_rndmove:
//case concmd_leavetrax: // RRDH
//case concmd_leavedroppings:
//case concmd_deploybias:
return 0;
case concmd_gamestartup:
{
// What a mess. The only way to detect which game version we are running is to count the parsed values here.
int params[34]; // 34 is the maximum for RRRA.
int pcount = 0;
for (int i = 0; i < 34; i++)
{
transnum(LABEL_DEFINE);
params[pcount++] = popscriptvalue();
if (keyword() != -1) break;
}
int pget = 0;
if (!isRR())
{
if (pcount == 30) g_gameType |= GAMEFLAG_PLUTOPAK;
else if (pcount == 31) g_gameType |= GAMEFLAG_PLUTOPAK | GAMEFLAG_WORLDTOUR;
else if (pcount != 26) I_FatalError("Invalid CONs. Cannot detect version. gamestartup has %d entries", pcount);
}
gameinfo.gametype = g_gameType;
popscriptvalue();
auto parseone = [&]() { return params[pget++]; };
ud.const_visibility = parseone();
gs.impact_damage = parseone();
gs.max_player_health = parseone();
gs.max_armour_amount = parseone();
gs.respawnactortime = parseone();
gs.respawnitemtime = parseone();
gs.playerfriction = parseone();
if (isPlutoPak() || isRR()) gs.gravity = parseone();
gs.rpgblastradius = parseone();
gs.pipebombblastradius = parseone();
gs.shrinkerblastradius = parseone();
gs.tripbombblastradius = parseone();
gs.morterblastradius = parseone();
gs.bouncemineblastradius = parseone();
gs.seenineblastradius = parseone();
gs.max_ammo_amount[1] = parseone();
gs.max_ammo_amount[2] = parseone();
gs.max_ammo_amount[3] = parseone();
gs.max_ammo_amount[4] = parseone();
gs.max_ammo_amount[5] = parseone();
gs.max_ammo_amount[6] = parseone();
gs.max_ammo_amount[7] = parseone();
gs.max_ammo_amount[8] = parseone();
gs.max_ammo_amount[9] = parseone();
if (isPlutoPak() || isRR()) gs.max_ammo_amount[11] = parseone();
if (isRR()) gs.max_ammo_amount[12] = parseone();
gs.camerashitable = parseone();
gs.numfreezebounces = parseone();
gs.freezerhurtowner = parseone();
if (isPlutoPak() || isRR())
{
spriteqamount = clamp(parseone(), 0, 1024);
gs.lasermode = parseone();
}
if (isWorldTour()) gs.max_ammo_amount[12] = parseone();
if (isRRRA())
{
gs.max_ammo_amount[13] = parseone();
gs.max_ammo_amount[14] = parseone();
gs.max_ammo_amount[16] = parseone();
}
return 0;
}
case concmd_eventloadactor:
{
if (parsing_state)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'eventloadactor' within 'state'.\n", fn, line_number);
errorcount++;
}
if (parsing_actor)
{
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Found 'eventloadactor' within 'actor'.\n", fn, line_number);
errorcount++;
}
num_squigilly_brackets = 0;
popscriptvalue();
parsing_actor = scriptpos();
transnum(LABEL_DEFINE);
int n = popscriptvalue();
gs.tileinfo[n].loadeventscriptptr = parsing_actor;
checking_ifelse = 0;
return 0;
}
case concmd_setsector:
case concmd_getsector:
{
int lLabelID;
// syntax getsector[<var>].x <VAR>
// gets the value of sector[<var>].xxx into <VAR>
// now get name of .xxx
while ((*textptr != '['))
{
textptr++;
}
if (*textptr == '[')
textptr++;
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// now get name of .xxx
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
lLabelID = getlabeloffset(sectorlabels, parselabel);
if (lLabelID == -1)
{
errorcount++;
ReportError(ERROR_SYMBOLNOTRECOGNIZED);
return 0;
}
appendscriptvalue(sectorlabels[lLabelID].lId);
if (sectorlabels[lLabelID].flags & LABEL_HASPARM2)
{
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
}
else appendscriptvalue(0);
// now at target VAR...
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getsector && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_findnearactor:
{
// syntax findnearactor <type> <maxdist> <getvar>
// gets the sprite ID of the nearest actor within max dist
// that is of <type> into <getvar>
// -1 for none found
transnum(LABEL_DEFINE); // get <type>
transnum(LABEL_DEFINE); // get maxdist
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_findnearactorvar:
{
// syntax findnearactorvar <type> <maxdistvar> <getvar>
// gets the sprite ID of the nearest actor within max dist
// that is of <type> into <getvar>
// -1 for none found
transnum(LABEL_DEFINE); // get <type>
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
// target var
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_sqrt:
{
// syntax sqrt <invar> <outvar>
// gets the sqrt of invar into outvar
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// target var
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_setwall:
case concmd_getwall:
{
int lLabelID;
// syntax getwall[<var>].x <VAR>
// gets the value of wall[<var>].xxx into <VAR>
// now get name of .xxx
while ((*textptr != '['))
{
textptr++;
}
if (*textptr == '[')
textptr++;
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// now get name of .xxx
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
lLabelID = getlabeloffset(walllabels, parselabel);
if (lLabelID == -1)
{
errorcount++;
ReportError(ERROR_SYMBOLNOTRECOGNIZED);
return 0;
}
appendscriptvalue(walllabels[lLabelID].lId);
if (walllabels[lLabelID].flags & LABEL_HASPARM2)
{
// get parm2
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
}
else appendscriptvalue(0);
// now at target VAR...
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getwall && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_setplayer:
case concmd_getplayer:
{
int lLabelID;
// syntax getwall[<var>].x <VAR>
// gets the value of wall[<var>].xxx into <VAR>
// now get name of .xxx
while ((*textptr != '['))
{
textptr++;
}
if (*textptr == '[')
textptr++;
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// now get name of .xxx
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
lLabelID = getlabeloffset(playerlabels, parselabel);
if (lLabelID == -1)
{
errorcount++;
ReportError(ERROR_SYMBOLNOTRECOGNIZED);
return 0;
}
appendscriptvalue(playerlabels[lLabelID].lId);
if (playerlabels[lLabelID].flags & LABEL_HASPARM2)
{
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
}
else appendscriptvalue(0);
// now at target VAR...
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getplayer && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_setuserdef:
case concmd_getuserdef:
{
int lLabelID;
// syntax [gs]etuserdef.x <VAR>
// gets the value of ud.xxx into <VAR>
// now get name of .xxx
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
lLabelID = getlabeloffset(userdefslabels, parselabel);
if (lLabelID == -1)
{
errorcount++;
ReportError(ERROR_SYMBOLNOTRECOGNIZED);
return 0;
}
appendscriptvalue(userdefslabels[lLabelID].lId);
if (userdefslabels[lLabelID].flags & LABEL_HASPARM2)
{
// get parm2
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
}
else appendscriptvalue(0);
// now at target VAR...
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getplayer && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_setactorvar:
case concmd_getactorvar:
{
// syntax [gs]etactorvar[<var>].<varx> <VAR>
// gets the value of the per-actor variable varx into VAR
// now get name of <var>
while ((*textptr != '['))
{
textptr++;
}
if (*textptr == '[')
textptr++;
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// now get name of .<varx>
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_setactorvar && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
if (tw == concmd_setactorvar && !(aGameVars[i].dwFlags & GAMEVAR_FLAG_PERACTOR))
{
errorcount++;
2020-09-22 21:07:11 +00:00
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Variable '%s' is not per-actor.\n", fn, line_number, parselabel.GetChars());
return 0;
}
appendscriptvalue(i);
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getactorvar && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_setactor:
case concmd_getactor:
{
int lLabelID;
// syntax getsector[<var>].x <VAR>
// gets the value of sector[<var>].xxx into <VAR>
// now get name of .xxx
while ((*textptr != '['))
{
textptr++;
}
if (*textptr == '[')
textptr++;
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// now get name of .xxx
while (*textptr != '.')
{
if (*textptr == 0xa)
break;
if (!*textptr)
break;
textptr++;
}
if (*textptr != '.')
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Syntax error.\n", fn, line_number);
return 0;
}
textptr++;
/// now pointing at 'xxx'
getlabel();
lLabelID = getlabeloffset(actorlabels, parselabel);
if (lLabelID == -1)
{
errorcount++;
ReportError(ERROR_SYMBOLNOTRECOGNIZED);
return 0;
}
appendscriptvalue(actorlabels[lLabelID].lId);
if (actorlabels[lLabelID].flags & LABEL_HASPARM2)
{
// get parm2
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
2020-09-22 21:07:11 +00:00
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Symbol '%s' is not a Game Definition.\n", fn, line_number, parselabel.GetChars());
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
}
else appendscriptvalue(0);
// now at target VAR...
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (tw == concmd_getactor && aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i);
break;
}
case concmd_espawnvar:
// syntax: espawnvar <Var1>
// spawns the sprite of type ID and sets RETURN to spawned sprite ID
// FALL THROUGH:
case concmd_lockplayer:
// syntax: lockplayer <var1>
// sets locks the player controls for <var1> ticks
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_READONLY)
{
errorcount++;
ReportError(ERROR_VARREADONLY);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
return 0;
case concmd_enhanced:
{
popscriptvalue();
transnum(LABEL_DEFINE);
int k = popscriptvalue();
if (k > VERSIONCHECK)
{
2020-09-22 21:07:11 +00:00
Printf(TEXTCOLOR_RED " * ERROR: This CON Code requires at least Build %d, but we are only Build %d\n", k, (int)VERSIONCHECK);
errorcount++;
}
break;
}
case concmd_spgetlotag:
case concmd_spgethitag:
case concmd_sectgetlotag:
case concmd_sectgethitag:
case concmd_gettexturefloor:
case concmd_gettextureceiling:
// no paramaters...
return 0;
case concmd_starttrack:
// one parameter (track#)
transnum(LABEL_DEFINE);
return 0;
case concmd_gettexturewall:
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) Command 'gettexturewall' is not yet implemented.\n", fn, line_number);
return 0;
case concmd_displayrand:
// syntax: displayrand <var>
// gets rand (not game rand) into <var>
// Get The ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i); // the ID of the DEF (offset into array...)
break;
case concmd_switch:
checking_switch++; // allow nesting (if other things work)
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
tempscrptr = scriptpos();
appendscriptvalue(0); // leave spot for end location (for after processing)
appendscriptvalue(0); // count of case statements
casescriptptr = scriptpos(); // the first case's pointer.
appendscriptvalue(0); // leave spot for 'default' location (null if none)
j = keyword();
while (j == 20) // '//'
{
while (*textptr != 0x0a)
textptr++;
j = keyword();
}
// probably does not allow nesting...
j = CountCaseStatements();
if (checking_switch > 1)
{
// sprintf(g_szBuf,"ERROR::%s %d: Checking_switch=",__FILE__,__LINE__, checking_switch);
// AddLog(g_szBuf);
}
if (j < 0)
{
return 1;
}
if (tempscrptr)
{
setscriptvalue(tempscrptr + 1, j); // save count of cases
}
while (j--)
{
// leave room for statements
appendscriptvalue(0); // value check
appendscriptvalue(0); // code offset
}
casecount = 0;
while (parsecommand() == 0);
casecount = 0;
if (tempscrptr)
{
setscriptvalue(tempscrptr, scriptpos()); // save 'end' location
}
casescriptptr = NULL;
// decremented in endswitch. Don't decrement here...
// checking_switch--; // allow nesting (maybe if other things work)
tempscrptr = NULL;
break;
case concmd_case:
//AddLog("Found Case");
repeatcase:
popscriptvalue(); // don't save in code
if (checking_switch < 1)
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) case statement when not in switch\n", fn, line_number);
return 1;
}
casecount++;
transnum(LABEL_DEFINE);
j = popscriptvalue();
if (casescriptptr)
{
setscriptvalue(casescriptptr + (casecount++), j);
setscriptvalue(casescriptptr + casecount, scriptpos());
}
j = keyword();
while (j == 20) // '//'
{
while (*textptr != 0x0a)
textptr++;
j = keyword();
}
if (j == concmd_case)
{
transword(); // eat 'case'
goto repeatcase;
}
while (parsecommand() == 0);
return 0;
case concmd_default:
popscriptvalue(); // don't save
if (casescriptptr != 0 && ScriptCode[casescriptptr] != 0)
{
// duplicate default statement
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) multiple default statements found in switch\n", fn, line_number);
}
if (casescriptptr)
{
setscriptvalue(casescriptptr, scriptpos());
}
while (parsecommand() == 0);
break;
case concmd_endswitch:
checking_switch--;
if (checking_switch < 0)
{
errorcount++;
Printf(TEXTCOLOR_RED " * ERROR!(%s, line %d) endswitch without matching switch\n", fn, line_number);
}
return 1; // end of block
break;
case concmd_startlevel:
// start at specified level
// startlevel <episode> <level>
#if 0
if (!g_bEnhanced)
{
errorcount++;
printf(" * ERROR!(L%ld) Command '%s' is enhanced only. Define enhance with the version number your code needs before using this commands.\n", line_number, tempbuf);
return 0;
}
#endif
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
break;
case concmd_mapvoxel:
// map a tilenum to a voxel.
// syntax: mapvoxel <tilenum> <filename (8.3)>
#if 0
if (!g_bEnhanced)
{
errorcount++;
printf(" * ERROR!(L%ld) Command '%s' is enhanced only. Define enhance with the version number your code needs before using this commands.\n", line_number, tempbuf);
return 0;
}
#endif
popscriptvalue(); // don't save in compiled code
transnum(LABEL_DEFINE);
j = popscriptvalue();
while (*textptr == ' ' || *textptr == '\t') textptr++;
i = 0;
#if 0
voxel_map[j].name[i] = 0;
voxel_map[j].voxnum = -2; // flag to load later
while ((i < 12) && (isalnum(*textptr) || *textptr == '.'))
{
voxel_map[j].name[i++] = *textptr++;
}
voxel_map[j].name[i] = 0;
#endif
break;
case concmd_myos:
case concmd_myospal:
case concmd_myosx:
case concmd_myospalx:
#if 0
if (!g_bEnhanced)
{
errorcount++;
printf(" * ERROR!(L%ld) Command '%s' is enhanced only. Define enhance with the version number your code needs before using this commands.\n", line_number, tempbuf);
return 0;
}
#endif
// syntax:
// int x, int y, short tilenum, signed char shade, char orientation
// myospal adds char pal
// Parse: x
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
// Parse: Y
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
// Parse: tilenum
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
// Parse: shade
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
// Parse: orientation
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
if (tw == concmd_myospal || tw == concmd_myospalx)
{
// Parse: pal
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEVAR);
return 0;
}
appendscriptvalue(i);
}
break;
case concmd_getangletotarget:
case concmd_getactorangle:
case concmd_setactorangle:
case concmd_getplayerangle:
case concmd_setplayerangle:
// Syntax: <command> <var>
// get the ID of the DEF
getlabel();
checkforkeyword();
i = GetDefID(parselabel);
//printf("Label '%s' ID is %d\n",label+(labelcnt<<6), i);
if (i < 0)
{ // not a defined DEF
errorcount++;
ReportError(ERROR_NOTAGAMEDEF);
return 0;
}
appendscriptvalue(i);
return 0;
}
return 0;
}
//---------------------------------------------------------------------------
//
// split in two to allow multiple CON files.
//
//---------------------------------------------------------------------------
void ConCompiler::compilecon(const char *filenam)
{
currentsourcefile = fileSystem.FindFile(filenam);
if (currentsourcefile < 0)
{
I_FatalError("%s: Missing con file(s).", filenam);
}
Printf("Compiling: '%s'.\n", filenam);
auto data = fileSystem.GetFileData(currentsourcefile, 1);
textptr = (char*)data.Data();
line_number = 1;
errorcount = warningcount = 0;
while (parsecommand() == 0);
if ((errorcount) > 64)
Printf(TEXTCOLOR_RED " * ERROR! Too many errors.");
else if (warningcount || errorcount)
Printf(TEXTCOLOR_ORANGE "Found %d warning(s), %d error(s).\n", warningcount, errorcount);
if (errorcount > 0) I_FatalError("Failed to compile %s", filenam);
// Install the crosshair toggle messages in the CVAR.
cl_crosshair.SetToggleMessages(quoteMgr.GetRawQuote(QUOTE_CROSSHAIR_OFF), quoteMgr.GetRawQuote(QUOTE_CROSSHAIR_OFF-1));
}
//==========================================================================
//
// Fallback in case nothing got defined.
//
//==========================================================================
static const char* ConFile(void)
{
if (userConfig.DefaultCon.IsNotEmpty()) return userConfig.DefaultCon.GetChars();
// WW2GI anf NAM special con names got introduced by EDuke32.
// Do we really need these?
if (g_gameType & GAMEFLAG_WW2GI)
{
if (fileSystem.FindFile("ww2gi.con") >= 0) return "ww2gi.con";
}
if (g_gameType & GAMEFLAG_NAM)
{
if (fileSystem.FindFile("nam.con") >= 0) return "nam.con";
if (fileSystem.FindFile("napalm.con") >= 0) return "napalm.con";
}
if (g_gameType & GAMEFLAG_NAPALM)
{
if (fileSystem.FindFile("napalm.con") >= 0) return "napalm.con";
if (fileSystem.FindFile("nam.con") >= 0) return "nam.con";
}
// This got introduced by EDuke 2.0.
if (g_gameType & GAMEFLAG_DUKE)
{
if (fileSystem.FindFile("eduke.con") >= 0) return "eduke.con";
}
// the other games only use game.con.
return "game.con";
}
//---------------------------------------------------------------------------
//
// process the music definitions after all map records are set up.
//
//---------------------------------------------------------------------------
void ConCompiler::setmusic()
{
for (auto& tm : tempMusic)
{
auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum);
if (map) map->music = tm.music;
}
tempMusic.Clear();
}
//---------------------------------------------------------------------------
//
// why was this called loadefs?
//
//---------------------------------------------------------------------------
void loadcons()
{
gs = {};
gs.respawnactortime = 768;
gs.bouncemineblastradius = 2500;
gs.respawnitemtime = 768;
gs.morterblastradius = 2500;
gs.numfreezebounces = 3;
gs.pipebombblastradius = 2500;
gs.playerfriction = 0xCFD0;
gs.rpgblastradius = 1780;
gs.seenineblastradius = 2048;
gs.shrinkerblastradius = 650;
gs.gravity = 176;
gs.tripbombblastradius = 3880;
gs.playerheight = PHEIGHT_DUKE;
gs.displayflags = DUKE3D_NO_WIDESCREEN_PINNING;
ScriptCode.Clear();
labels.Clear();
SortCommands();
ClearGameEvents();
ClearGameVars();
AddSystemVars();
auto before = I_nsTime();
ScriptCode.Push(0);
ConCompiler comp;
2021-05-01 22:35:56 +00:00
if (fileSystem.FileExists("engine/engine.con"))
{
comp.compilecon("engine/engine.con");
}
comp.compilecon(ConFile()); //Tokenize
if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get())
{
comp.compilecon(m);
}
ScriptCode.ShrinkToFit();
labels.ShrinkToFit();
userConfig.AddCons.reset();
setscriptvalue(0, scriptpos());
if (comp.getErrorCount())
{
I_FatalError("Failed to compile CONs.");
}
else
{
auto after = I_nsTime();
Printf("Compilation time:%.2f ms, Code Size:%u bytes. %u labels. %d/%d Variables.\n", (after-before) / 1000000.,
(ScriptCode.Size() << 2) - 4,
labels.Size(),
0,//iGameVarCount,
MAXGAMEVARS
);
}
// These can only be retrieved AFTER loading the scripts.
InitGameVarPointers();
ResetSystemDefaults();
S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour.
S_CacheAllSounds();
comp.setmusic();
// RR must link the last map of E1 to the first map of E2.
if (isRR()) for (auto& map : mapList)
{
if (map->cluster == 1)
{
if (!FindMapByIndexOnly(map->cluster, map->mapindex + 1))
{
auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1);
if (nextmap)
{
map->NextMap = nextmap->labelName;
map->flags |= LEVEL_FORCENOEOG;
}
}
}
}
if (isWorldTour())
{
// fix broken secret exit in WT's super secret map.
// This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps.
int num = fileSystem.CheckNumForName("e1l7.map");
int file = fileSystem.GetFileContainer(num);
if (file <= fileSystem.GetMaxIwadNum())
{
auto maprec = FindMapByName("e1l7");
if (maprec) maprec->NextMap = "e1l5";
}
}
}
END_DUKE_NS