diff --git a/source/rr/src/game.cpp b/source/rr/src/game.cpp index ad55e1edc..8a29af760 100644 --- a/source/rr/src/game.cpp +++ b/source/rr/src/game.cpp @@ -6684,6 +6684,9 @@ static void G_Cleanup(void) // Xfree(MusicPtr); + Gv_Clear(); + + hash_free(&h_gamevars); hash_free(&h_labels); } diff --git a/source/rr/src/gamedef.cpp b/source/rr/src/gamedef.cpp index 5afc3da78..aee4eb52c 100644 --- a/source/rr/src/gamedef.cpp +++ b/source/rr/src/gamedef.cpp @@ -41,6 +41,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_RR_NS +#define LINE_NUMBER (g_lineNumber << 12) + int32_t g_scriptVersion = 14; // 13 = 1.3D-style CON files, 14 = 1.4/1.5 style CON files char g_scriptFileName[BMAX_PATH] = "(none)"; // file we're currently compiling @@ -61,7 +63,11 @@ static int32_t g_numBraces = 0; static int32_t C_ParseCommand(int32_t loop); static int32_t C_SetScriptSize(int32_t size); +static intptr_t apScriptGameEventEnd[MAXEVENTS]; static intptr_t g_parsingActorPtr; +static intptr_t g_scriptEventBreakOffset; +static intptr_t g_scriptEventChainOffset; +static intptr_t g_scriptEventOffset; static char *textptr; int32_t g_errorCnt,g_warningCnt; @@ -99,8 +105,12 @@ static tokenmap_t const vm_keywords[] = { "addammo", CON_ADDAMMO }, { "addinventory", CON_ADDINVENTORY }, { "addkills", CON_ADDKILLS }, + { "addlog", CON_ADDLOGVAR }, + { "addlogvar", CON_ADDLOGVAR }, { "addphealth", CON_ADDPHEALTH }, { "addstrength", CON_ADDSTRENGTH }, + { "addvar", CON_ADDVAR }, + { "addvarvar", CON_ADDVARVAR }, { "addweapon", CON_ADDWEAPON }, { "ai", CON_AI }, { "betaname", CON_BETANAME }, @@ -121,12 +131,14 @@ static tokenmap_t const vm_keywords[] = { "destroyit", CON_DESTROYIT }, { "else", CON_ELSE }, { "enda", CON_ENDA }, + { "endevent", CON_ENDEVENT }, { "endofgame", CON_ENDOFGAME }, { "ends", CON_ENDS }, { "fakebubba", CON_FAKEBUBBA }, { "fall", CON_FALL }, { "feathers", CON_MAIL }, { "gamestartup", CON_GAMESTARTUP }, + { "gamevar", CON_GAMEVAR }, { "garybanjo", CON_GARYBANJO }, { "getlastpal", CON_GETLASTPAL }, { "globalsound", CON_GLOBALSOUND }, @@ -185,6 +197,12 @@ static tokenmap_t const vm_keywords[] = { "ifsquished", CON_IFSQUISHED }, { "ifstrength", CON_IFSTRENGTH }, { "iftipcow", CON_IFTIPCOW }, + { "ifvare", CON_IFVARE }, + { "ifvarg", CON_IFVARG }, + { "ifvarl", CON_IFVARL }, + { "ifvarvare", CON_IFVARVARE }, + { "ifvarvarg", CON_IFVARVARG }, + { "ifvarvarl", CON_IFVARVARL }, { "ifwasweapon", CON_IFWASWEAPON }, { "ifwind", CON_IFWIND }, { "include", CON_INCLUDE }, @@ -205,6 +223,7 @@ static tokenmap_t const vm_keywords[] = { "music", CON_MUSIC }, { "newpic", CON_NEWPIC }, { "nullop", CON_NULLOP }, + { "onevent", CON_ONEVENT }, { "operate", CON_OPERATE }, { "palfrom", CON_PALFROM }, { "paper", CON_PAPER }, @@ -216,6 +235,8 @@ static tokenmap_t const vm_keywords[] = { "resetplayer", CON_RESETPLAYER }, { "respawnhitag", CON_RESPAWNHITAG }, { "rndmove", CON_RNDMOVE }, + { "setvar", CON_SETVAR }, + { "setvarvar", CON_SETVARVAR }, { "sizeat", CON_SIZEAT }, { "sizeto", CON_SIZETO }, { "slapplayer", CON_SLAPPLAYER }, @@ -243,6 +264,22 @@ static tokenmap_t const vm_keywords[] = { "}", CON_RIGHTBRACE }, }; +static const vec2_t varvartable[] = +{ + { CON_IFVARVARE, CON_IFVARE }, + { CON_IFVARVARG, CON_IFVARG }, + { CON_IFVARVARL, CON_IFVARL }, + + { CON_ADDVARVAR, CON_ADDVAR }, + { CON_SETVARVAR, CON_SETVAR }, +}; + +static inthashtable_t h_varvar = { NULL, INTHASH_SIZE(ARRAY_SIZE(varvartable)) }; + +static inthashtable_t *const inttables[] = { + &h_varvar, +}; + char const * VM_GetKeywordForID(int32_t id) { // could be better but this is only called for diagnostics, ayy lmao @@ -258,12 +295,13 @@ char *bitptr; // pointer to bitmap of which bytecode positions contain pointers #define BITPTR_CLEAR(x) (bitptr[(x)>>3] &= ~(1<<((x)&7))) #define BITPTR_IS_POINTER(x) (bitptr[(x)>>3] & (1<<((x) &7))) -hashtable_t h_labels = { 11264>>1, NULL }; +hashtable_t h_gamevars = { MAXGAMEVARS >> 1, NULL }; +hashtable_t h_labels = { 11264>>1, NULL }; static hashtable_t h_keywords = { CON_END>>1, NULL };; static hashtable_t * const tables[] = { - &h_labels, &h_keywords + &h_labels, &h_keywords, &h_gamevars }; static hashtable_t * const tables_free [] = { @@ -276,8 +314,14 @@ void C_InitHashes() { uint32_t i; - for (i=0; i < ARRAY_SIZE(tables); i++) - hash_init(tables[i]); + for (auto table : tables) + hash_init(table); + + for (auto table : inttables) + inthash_init(table); + + for (auto &varvar : varvartable) + inthash_add(&h_varvar, varvar.x, varvar.y, 0); //inithashnames(); initsoundhashnames(); @@ -290,7 +334,7 @@ void C_InitHashes() // "magic" number for { and }, overrides line number in compiled code for later detection #define IFELSE_MAGIC 31337 -static int32_t g_ifElseAborted; +static int32_t g_skipBranch; static int32_t C_SetScriptSize(int32_t newsize) { @@ -458,6 +502,9 @@ static int32_t C_SkipComments(void) while (1); } +static inline int GetDefID(char const *label) { return hash_find(&h_gamevars, label); } + +#define LAST_LABEL (label+(g_labelCnt<<6)) static inline int32_t isaltok(const char c) { return (isalnum(c) || c == '{' || c == '}' || c == '/' || c == '\\' || c == '*' || c == '-' || c == '_' || @@ -502,6 +549,25 @@ static void C_GetNextLabelName(void) initprintf("%s:%d: debug: label `%s'.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6)); } +static inline void scriptWriteValue(int32_t const value) +{ + BITPTR_CLEAR(g_scriptPtr-apScript); + *g_scriptPtr++ = value; +} + +// addresses passed to these functions must be within the block of memory pointed to by apScript +static inline void scriptWriteAtOffset(int32_t const value, intptr_t * const addr) +{ + BITPTR_CLEAR(addr-apScript); + *(addr) = value; +} + +static inline void scriptWritePointer(intptr_t const value, intptr_t * const addr) +{ + BITPTR_SET(addr-apScript); + *(addr) = value; +} + static int32_t C_GetKeyword(void) { int32_t i; @@ -549,12 +615,10 @@ static int32_t C_GetNextKeyword(void) //Returns its code # if (EDUKE32_PREDICT_TRUE((i = hash_find(&h_keywords,tempbuf)) >= 0)) { if (i == CON_LEFTBRACE || i == CON_RIGHTBRACE || i == CON_NULLOP) - *g_scriptPtr = i + (IFELSE_MAGIC<<12); - else *g_scriptPtr = i + (g_lineNumber<<12); + scriptWriteValue(i | (VM_IFELSE_MAGIC<<12)); + else scriptWriteValue(i | LINE_NUMBER); - BITPTR_CLEAR(g_scriptPtr-apScript); textptr += l; - g_scriptPtr++; if (!(g_errorCnt || g_warningCnt) && g_scriptDebug) initprintf("%s:%d: debug: keyword `%s'.\n", g_scriptFileName, g_lineNumber, tempbuf); @@ -620,6 +684,115 @@ static int32_t parse_hex_constant(const char *hexnum) return x; } +static void C_GetNextVarType(int32_t type) +{ + int32_t id = 0; + int32_t flags = 0; + + auto varptr = g_scriptPtr; + + C_SkipComments(); + + if (!type && !g_labelsOnly && (isdigit(*textptr) || ((*textptr == '-') && (isdigit(*(textptr+1)))))) + { + scriptWriteValue(GV_FLAG_CONSTANT); + + if (tolower(textptr[1])=='x') // hex constants + scriptWriteValue(parse_hex_constant(textptr+2)); + else + scriptWriteValue(parse_decimal_number()); + + if (!(g_errorCnt || g_warningCnt) && g_scriptDebug) + initprintf("%s:%d: debug: constant %ld in place of gamevar.\n", g_scriptFileName, g_lineNumber, (long)(g_scriptPtr[-1])); +#if 1 + while (!ispecial(*textptr) && *textptr != ']') textptr++; +#else + C_GetNextLabelName(); +#endif + return; + } + else if (*textptr == '-'/* && !isdigit(*(textptr+1))*/) + { + if (EDUKE32_PREDICT_FALSE(type)) + { + g_errorCnt++; + C_ReportError(ERROR_SYNTAXERROR); + C_GetNextLabelName(); + return; + } + + if (!(g_errorCnt || g_warningCnt) && g_scriptDebug) + initprintf("%s:%d: debug: flagging gamevar as negative.\n", g_scriptFileName, g_lineNumber); //,Batol(textptr)); + + flags = GV_FLAG_NEGATIVE; + textptr++; + } + + C_GetNextLabelName(); + + if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0)) + { + g_errorCnt++; + C_ReportError(ERROR_ISAKEYWORD); + return; + } + + C_SkipComments(); + + id=GetDefID(LAST_LABEL); + if (id<0) //gamevar not found + { + if (EDUKE32_PREDICT_TRUE(!type && !g_labelsOnly)) + { + //try looking for a define instead + Bstrcpy(tempbuf,LAST_LABEL); + id = hash_find(&h_labels,tempbuf); + + if (EDUKE32_PREDICT_TRUE(id>=0 && labeltype[id] & LABEL_DEFINE)) + { + if (!(g_errorCnt || g_warningCnt) && g_scriptDebug) + initprintf("%s:%d: debug: label `%s' in place of gamevar.\n",g_scriptFileName,g_lineNumber,label+(id<<6)); + + scriptWriteValue(GV_FLAG_CONSTANT); + scriptWriteValue(labelcode[id]); + return; + } + } + + g_errorCnt++; + C_ReportError(ERROR_NOTAGAMEVAR); + return; + } + + if (EDUKE32_PREDICT_FALSE(type == GAMEVAR_READONLY && aGameVars[id].flags & GAMEVAR_READONLY)) + { + g_errorCnt++; + C_ReportError(ERROR_VARREADONLY); + return; + } + else if (EDUKE32_PREDICT_FALSE(aGameVars[id].flags & type)) + { + g_errorCnt++; + C_ReportError(ERROR_VARTYPEMISMATCH); + return; + } + + if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt) + initprintf("%s:%d: debug: gamevar `%s'.\n",g_scriptFileName,g_lineNumber,LAST_LABEL); + + scriptWriteValue(id|flags); +} + +#define C_GetNextVar() C_GetNextVarType(0) + +static FORCE_INLINE void C_GetManyVarsType(int32_t type, int num) +{ + for (; num>0; --num) + C_GetNextVarType(type); +} + +#define C_GetManyVars(num) C_GetManyVarsType(0,num) + // returns: // -1 on EOF or wrong type or error // 0 if literal value @@ -742,7 +915,7 @@ static int32_t C_CheckMalformedBranch(intptr_t lastScriptPtr) case CON_ENDS: case CON_ELSE: g_scriptPtr = lastScriptPtr + &apScript[0]; - g_ifElseAborted = 1; + g_skipBranch = 1; C_ReportError(-1); g_warningCnt++; initprintf("%s:%d: warning: malformed `%s' branch\n",g_scriptFileName,g_lineNumber, @@ -759,14 +932,14 @@ static int32_t C_CheckEmptyBranch(int32_t tw, intptr_t lastScriptPtr) tw == CON_IFRND || tw == CON_IFHITWEAPON || tw == CON_IFCANSEE || tw == CON_IFCANSEETARGET || tw == CON_IFPDISTL || tw == CON_IFPDISTG || tw == CON_IFGOTWEAPONCE) { - g_ifElseAborted = 0; + g_skipBranch = 0; return 0; } if ((*(g_scriptPtr) & VM_INSTMASK) != CON_NULLOP || *(g_scriptPtr)>>12 != IFELSE_MAGIC) - g_ifElseAborted = 0; + g_skipBranch = 0; - if (EDUKE32_PREDICT_FALSE(g_ifElseAborted)) + if (EDUKE32_PREDICT_FALSE(g_skipBranch)) { C_ReportError(-1); g_warningCnt++; @@ -942,6 +1115,22 @@ static inline void C_FinishBitOr(int32_t value) *g_scriptPtr++ = value; } +static void scriptUpdateOpcodeForVariableType(intptr_t *ins) +{ + int opcode = -1; + + if (opcode != -1) + { + if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt) + { + initprintf("%s:%d: %s -> %s for var %s\n", g_scriptFileName, g_lineNumber, + VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode), aGameVars[ins[1] & (MAXGAMEVARS-1)].szLabel); + } + + scriptWriteAtOffset(opcode | LINE_NUMBER, ins); + } +} + static int32_t C_ParseCommand(int32_t loop) { int32_t i, j=0, k=0, tw; @@ -1042,6 +1231,60 @@ static int32_t C_ParseCommand(int32_t loop) } continue; + case CON_GAMEVAR: + { + // syntax: gamevar + // defines var1 and sets initial value. + // flags are used to define usage + // (see top of this files for flags) + + if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) || (*textptr == '-'))) + { + g_errorCnt++; + C_ReportError(ERROR_SYNTAXERROR); + C_NextLine(); + continue; + } + + g_scriptPtr--; + + C_GetNextLabelName(); + + if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords, LAST_LABEL)>=0)) + { + g_warningCnt++; + C_ReportError(WARNING_VARMASKSKEYWORD); + hash_delete(&h_keywords, LAST_LABEL); + } + + int32_t defaultValue = 0; + int32_t varFlags = 0; + + if (C_GetKeyword() == -1) + { + C_GetNextValue(LABEL_DEFINE); // get initial value + defaultValue = *(--g_scriptPtr); + + j = 0; + + while (C_GetKeyword() == -1) + C_BitOrNextValue(&j); + + C_FinishBitOr(j); + varFlags = *(--g_scriptPtr); + + if (EDUKE32_PREDICT_FALSE((*(g_scriptPtr)&GAMEVAR_USER_MASK)==(GAMEVAR_PERPLAYER|GAMEVAR_PERACTOR))) + { + g_warningCnt++; + varFlags ^= GAMEVAR_PERPLAYER; + C_ReportError(WARNING_BADGAMEVAR); + } + } + + Gv_NewVar(LAST_LABEL, defaultValue, varFlags); + continue; + } + case CON_DEFINE: { C_GetNextLabelName(); @@ -1461,6 +1704,54 @@ static int32_t C_ParseCommand(int32_t loop) g_checkingIfElse = 0; continue; + case CON_ONEVENT: + if (EDUKE32_PREDICT_FALSE(g_processingState || g_parsingActorPtr)) + { + C_ReportError(ERROR_FOUNDWITHIN); + g_errorCnt++; + } + + g_numBraces = 0; + g_scriptPtr--; + g_scriptEventOffset = g_parsingActorPtr = g_scriptPtr - apScript; + + C_SkipComments(); + j = 0; + while (isaltok(*(textptr+j))) + { + g_szCurrentBlockName[j] = textptr[j]; + j++; + } + g_szCurrentBlockName[j] = 0; + // g_labelsOnly = 1; + C_GetNextValue(LABEL_EVENT); + g_labelsOnly = 0; + g_scriptPtr--; + j= *g_scriptPtr; // type of event + g_currentEvent = j; + //Bsprintf(g_szBuf,"Adding Event for %d at %lX",j, g_parsingEventPtr); + //AddLog(g_szBuf); + if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXEVENTS-1)) + { + initprintf("%s:%d: error: invalid event ID.\n",g_scriptFileName,g_lineNumber); + g_errorCnt++; + continue; + } + // if event has already been declared then store previous script location + if (!apScriptEvents[j]) + { + apScriptEvents[j] = g_scriptEventOffset; + } + else + { + g_scriptEventChainOffset = apScriptEvents[j]; + apScriptEvents[j] = g_scriptEventOffset; + } + + g_checkingIfElse = 0; + + continue; + case CON_CSTAT: C_GetNextValue(LABEL_DEFINE); @@ -1551,7 +1842,7 @@ static int32_t C_ParseCommand(int32_t loop) intptr_t const lastScriptPtr = g_scriptPtr - apScript - 1; - g_ifElseAborted = 0; + g_skipBranch = 0; g_checkingIfElse--; if (C_CheckMalformedBranch(lastScriptPtr)) @@ -1573,6 +1864,143 @@ static int32_t C_ParseCommand(int32_t loop) continue; } + case CON_ADDLOGVAR: + g_labelsOnly = 1; + C_GetNextVar(); + g_labelsOnly = 0; + continue; + case CON_ADDVAR: + case CON_SETVAR: +setvar: + { + auto ins = &g_scriptPtr[-1]; + + C_GetNextVarType(GAMEVAR_READONLY); + C_GetNextValue(LABEL_DEFINE); + // replace instructions with special versions for specific var types + scriptUpdateOpcodeForVariableType(ins); + continue; + } + case CON_ADDVARVAR: + case CON_SETVARVAR: + { +setvarvar: + auto ins = &g_scriptPtr[-1]; + auto tptr = textptr; + int const lnum = g_lineNumber; + + C_GetNextVarType(GAMEVAR_READONLY); + C_GetNextVar(); + + int const opcode = inthash_find(&h_varvar, *ins & VM_INSTMASK); + + if (ins[2] == GV_FLAG_CONSTANT && opcode != -1) + { + if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt) + { + initprintf("%s:%d: %s -> %s\n", g_scriptFileName, g_lineNumber, + VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode)); + } + + tw = opcode; + scriptWriteAtOffset(opcode | LINE_NUMBER, ins); + g_scriptPtr = &ins[1]; + textptr = tptr; + g_lineNumber = lnum; + goto setvar; + } + + continue; + } + + case CON_IFVARVARE: + case CON_IFVARVARG: + case CON_IFVARVARL: + { + auto const ins = &g_scriptPtr[-1]; + auto const lastScriptPtr = &g_scriptPtr[-1] - apScript; + auto const lasttextptr = textptr; + int const lnum = g_lineNumber; + + g_skipBranch = false; + + C_GetNextVar(); + auto const var = g_scriptPtr; + C_GetNextVar(); + + if (*var == GV_FLAG_CONSTANT) + { + int const opcode = inthash_find(&h_varvar, tw); + + if (opcode != -1) + { + if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt) + { + initprintf("%s:%d: replacing %s with %s\n", g_scriptFileName, g_lineNumber, + VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode)); + } + + scriptWriteAtOffset(opcode | LINE_NUMBER, ins); + tw = opcode; + g_scriptPtr = &ins[1]; + textptr = lasttextptr; + g_lineNumber = lnum; + goto ifvar; + } + } + + if (C_CheckMalformedBranch(lastScriptPtr)) + continue; + + auto const offset = g_scriptPtr - apScript; + g_scriptPtr++; // Leave a spot for the fail location + + C_ParseCommand(0); + + if (C_CheckEmptyBranch(tw, lastScriptPtr)) + continue; + + auto const tempscrptr = apScript + offset; + scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr); + continue; + } + + case CON_IFVARE: + case CON_IFVARG: + case CON_IFVARL: + { +ifvar: + auto const ins = &g_scriptPtr[-1]; + auto const lastScriptPtr = &g_scriptPtr[-1] - apScript; + + g_skipBranch = false; + + C_GetNextVar(); + C_GetNextValue(LABEL_DEFINE); + + if (C_CheckMalformedBranch(lastScriptPtr)) + continue; + + scriptUpdateOpcodeForVariableType(ins); + + auto const offset = g_scriptPtr - apScript; + g_scriptPtr++; //Leave a spot for the fail location + + C_ParseCommand(0); + + if (C_CheckEmptyBranch(tw, lastScriptPtr)) + continue; + + auto const tempscrptr = apScript + offset; + scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr); + + j = C_GetKeyword(); + + if (j == CON_ELSE) + g_checkingIfElse++; + + continue; + } case CON_IFRND: case CON_IFPDISTL: @@ -1603,7 +2031,7 @@ static int32_t C_ParseCommand(int32_t loop) intptr_t offset; intptr_t lastScriptPtr = (g_scriptPtr-&apScript[0]-1); - g_ifElseAborted = 0; + g_skipBranch = 0; switch (tw) { @@ -1696,7 +2124,7 @@ static int32_t C_ParseCommand(int32_t loop) intptr_t offset; intptr_t lastScriptPtr = (g_scriptPtr-&apScript[0]-1); - g_ifElseAborted = 0; + g_skipBranch = 0; if (C_CheckMalformedBranch(lastScriptPtr)) continue; @@ -1745,8 +2173,8 @@ static int32_t C_ParseCommand(int32_t loop) g_scriptPtr -= 2; if (C_GetKeyword() != CON_ELSE && (*(g_scriptPtr-2) & VM_INSTMASK) != CON_ELSE) - g_ifElseAborted = 1; - else g_ifElseAborted = 0; + g_skipBranch = 1; + else g_skipBranch = 0; j = C_GetKeyword(); @@ -2076,7 +2504,7 @@ static int32_t C_ParseCommand(int32_t loop) g_warningCnt++; initprintf("%s:%d: warning: `nullop' found without `else'\n",g_scriptFileName,g_lineNumber); g_scriptPtr--; - g_ifElseAborted = 1; + g_skipBranch = 1; } continue; @@ -2190,6 +2618,9 @@ void C_PrintStats(void) void C_Compile(const char *fileName) { + Bmemset(apScriptEvents, 0, sizeof(apScriptEvents)); + Bmemset(apScriptGameEventEnd, 0, sizeof(apScriptGameEventEnd)); + for (int i=0; i> 12)) #define C_CUSTOMERROR(Text, ...) \ do \ @@ -66,8 +70,19 @@ enum extern intptr_t const * insptr; extern void VM_ScriptInfo(intptr_t const *ptr, int range); +extern hashtable_t h_gamevars; extern hashtable_t h_labels; +extern int32_t g_aimAngleVarID; // var ID of "AUTOAIMANGLE" +extern int32_t g_angRangeVarID; // var ID of "ANGRANGE" +extern int32_t g_returnVarID; // var ID of "RETURN" +extern int32_t g_weaponVarID; // var ID of "WEAPON" +extern int32_t g_worksLikeVarID; // var ID of "WORKSLIKE" +extern int32_t g_zRangeVarID; // var ID of "ZRANGE" + +#include "events_defs.h" +extern intptr_t apScriptEvents[MAXEVENTS]; + extern char g_scriptFileName[BMAX_PATH]; extern const uint32_t CheatFunctionFlags[]; @@ -151,9 +166,15 @@ enum ScriptError_t ERROR_FOUNDWITHIN, ERROR_ISAKEYWORD, ERROR_OPENBRACKET, + ERROR_NOTAGAMEVAR, ERROR_PARAMUNDEFINED, ERROR_SYNTAXERROR, - WARNING_LABELSONLY + ERROR_VARREADONLY, + ERROR_VARTYPEMISMATCH, + WARNING_BADGAMEVAR, + WARNING_DUPLICATEDEFINITION, + WARNING_LABELSONLY, + WARNING_VARMASKSKEYWORD, }; enum ScriptKeywords_t @@ -306,6 +327,20 @@ enum ScriptKeywords_t CON_MOTOLOOPSND, // 145 CON_IFSIZEDOWN, // 146 CON_RNDMOVE, // 147 + CON_GAMEVAR, // 148 + CON_IFVARL, // 149 + CON_IFVARG, // 150 + CON_SETVARVAR, // 151 + CON_SETVAR, // 152 + CON_ADDVARVAR, // 153 + CON_ADDVAR, // 154 + CON_IFVARVARL, // 155 + CON_IFVARVARG, // 156 + CON_ADDLOGVAR, // 157 + CON_ONEVENT, // 158 + CON_ENDEVENT, // 159 + CON_IFVARE, // 160 + CON_IFVARVARE, // 161 CON_END }; // KEEPINSYNC with the keyword list in lunatic/con_lang.lua diff --git a/source/rr/src/gameexec.cpp b/source/rr/src/gameexec.cpp index 3fac38e7e..4945d370f 100644 --- a/source/rr/src/gameexec.cpp +++ b/source/rr/src/gameexec.cpp @@ -57,10 +57,18 @@ enum vmflags_t }; int32_t g_tw; +int32_t g_currentEvent = -1; int32_t g_errorLineNum; intptr_t const *insptr; +int32_t g_returnVarID = -1; // var ID of "RETURN" +int32_t g_weaponVarID = -1; // var ID of "WEAPON" +int32_t g_worksLikeVarID = -1; // var ID of "WORKSLIKE" +int32_t g_zRangeVarID = -1; // var ID of "ZRANGE" +int32_t g_angRangeVarID = -1; // var ID of "ANGRANGE" +int32_t g_aimAngleVarID = -1; // var ID of "AUTOAIMANGLE" + // for timing events and actors uint32_t g_actorCalls[MAXTILES]; double g_actorTotalMs[MAXTILES], g_actorMinMs[MAXTILES], g_actorMaxMs[MAXTILES]; @@ -122,6 +130,8 @@ static void VM_DeleteSprite(int const spriteNum, int const playerNum) A_DeleteSprite(spriteNum); } +intptr_t apScriptEvents[MAXEVENTS]; + static int32_t VM_CheckSquished(void) { if (RR) @@ -1188,7 +1198,8 @@ GAMEEXEC_STATIC void VM_Execute(native_t loop) { case CON_ENDA: case CON_BREAK: - case CON_ENDS: return; + case CON_ENDS: + case CON_ENDEVENT: return; case CON_IFRND: VM_CONDITIONAL(rnd(*(++insptr))); continue; @@ -2515,6 +2526,128 @@ GAMEEXEC_STATIC void VM_Execute(native_t loop) continue; case CON_IFNOSOUNDS: VM_CONDITIONAL(!A_CheckAnySoundPlaying(vm.spriteNum)); continue; + + + case CON_IFVARG: + insptr++; + tw = Gv_GetVar(*insptr++); + VM_CONDITIONAL(tw > *insptr); + continue; + + case CON_IFVARL: + insptr++; + tw = Gv_GetVar(*insptr++); + VM_CONDITIONAL(tw < *insptr); + continue; + + case CON_SETVARVAR: + insptr++; + { + tw = *insptr++; + int const nValue = Gv_GetVar(*insptr++); + + if ((aGameVars[tw].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0) + aGameVars[tw].global = nValue; + else + Gv_SetVar(tw, nValue); + } + continue; + + case CON_SETVAR: + Gv_SetVar(insptr[1], insptr[2]); + insptr += 3; + continue; + + case CON_ADDVARVAR: + insptr++; + tw = *insptr++; + Gv_AddVar(tw, Gv_GetVar(*insptr++)); + continue; + + case CON_ADDVAR: + Gv_AddVar(insptr[1], insptr[2]); + insptr += 3; + continue; + + case CON_IFVARVARL: + insptr++; + tw = Gv_GetVar(*insptr++); + tw = (tw < Gv_GetVar(*insptr++)); + insptr--; + VM_CONDITIONAL(tw); + continue; + + case CON_IFVARVARG: + insptr++; + tw = Gv_GetVar(*insptr++); + tw = (tw > Gv_GetVar(*insptr++)); + insptr--; + VM_CONDITIONAL(tw); + continue; + + case CON_ADDLOGVAR: + insptr++; + { + int32_t m = 1; + char szBuf[256]; + int32_t lVarID = *insptr; + + if ((lVarID >= g_gameVarCount) || lVarID < 0) + { + if (*insptr == MAXGAMEVARS) // addlogvar for a constant? Har. + insptr++; + else if (EDUKE32_PREDICT_TRUE(*insptr & GV_FLAG_NEGATIVE)) + { + m = -m; + lVarID ^= GV_FLAG_NEGATIVE; + } + else + { + // invalid varID + CON_ERRPRINTF("invalid variable\n"); + continue; + } + } + Bsprintf(tempbuf, "CONLOGVAR: L=%d %s ", VM_DECODE_LINE_NUMBER(g_tw), aGameVars[lVarID].szLabel); + + if (aGameVars[lVarID].flags & GAMEVAR_READONLY) + { + Bsprintf(szBuf, " (read-only)"); + Bstrcat(tempbuf, szBuf); + } + if (aGameVars[lVarID].flags & GAMEVAR_PERPLAYER) + { + Bsprintf(szBuf, " (Per Player. Player=%d)", vm.playerNum); + } + else if (aGameVars[lVarID].flags & GAMEVAR_PERACTOR) + { + Bsprintf(szBuf, " (Per Actor. Actor=%d)", vm.spriteNum); + } + else + { + Bsprintf(szBuf, " (Global)"); + } + Bstrcat(tempbuf, szBuf); + Bsprintf(szBuf, " =%d\n", Gv_GetVar(lVarID) * m); + Bstrcat(tempbuf, szBuf); + initprintf(OSDTEXT_GREEN "%s", tempbuf); + insptr++; + continue; + } + + case CON_IFVARE: + insptr++; + tw = Gv_GetVar(*insptr++); + VM_CONDITIONAL(tw >= *insptr); + continue; + + case CON_IFVARVARE: + insptr++; + tw = Gv_GetVar(*insptr++); + tw = (tw == Gv_GetVar(*insptr++)); + insptr--; + VM_CONDITIONAL(tw); + continue; default: // you aren't supposed to be here! if (RR && ud.recstat == 2) @@ -2537,37 +2670,6 @@ GAMEEXEC_STATIC void VM_Execute(native_t loop) } } -// NORECURSE -void A_LoadActor(int32_t spriteNum) -{ - vm.spriteNum = spriteNum; // Sprite ID - vm.pSprite = &sprite[spriteNum]; // Pointer to sprite structure - vm.pActor = &actor[spriteNum]; - - if (g_tile[vm.pSprite->picnum].loadPtr == NULL) - return; - - vm.pData = &actor[spriteNum].t_data[0]; // Sprite's 'extra' data - vm.playerNum = -1; // Player ID - vm.playerDist = -1; // Distance - vm.pPlayer = g_player[0].ps; - - vm.flags &= ~(VM_RETURN | VM_KILL | VM_NOEXECUTE); - - if ((unsigned)vm.pSprite->sectnum >= MAXSECTORS) - { - A_DeleteSprite(vm.spriteNum); - return; - } - - insptr = g_tile[vm.pSprite->picnum].loadPtr; - VM_Execute(1); - insptr = NULL; - - if (vm.flags & VM_KILL) - A_DeleteSprite(vm.spriteNum); -} - void VM_UpdateAnim(int spriteNum, int32_t *pData) { size_t const actionofs = AC_ACTION_ID(pData); diff --git a/source/rr/src/gameexec.h b/source/rr/src/gameexec.h index 763d955f0..c4d59afe7 100644 --- a/source/rr/src/gameexec.h +++ b/source/rr/src/gameexec.h @@ -34,10 +34,9 @@ extern int32_t ticrandomseed; extern vmstate_t vm; extern int32_t g_tw; +extern int32_t g_currentEvent; extern int32_t g_errorLineNum; -void A_LoadActor(int32_t spriteNum); - extern uint32_t g_actorCalls[MAXTILES]; extern double g_actorTotalMs[MAXTILES], g_actorMinMs[MAXTILES], g_actorMaxMs[MAXTILES]; diff --git a/source/rr/src/gamevars.h b/source/rr/src/gamevars.h index df132082d..c2151b57c 100644 --- a/source/rr/src/gamevars.h +++ b/source/rr/src/gamevars.h @@ -23,10 +23,124 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifndef gamevars_h_ #define gamevars_h_ +#define MAXGAMEVARS 2048 // must be a power of two +#define MAXVARLABEL 26 + +#define GV_FLAG_CONSTANT (MAXGAMEVARS) +#define GV_FLAG_NEGATIVE (MAXGAMEVARS<<1) + +// store global game definitions +enum GamevarFlags_t +{ + GAMEVAR_PERPLAYER = 0x00000001, // per-player variable + GAMEVAR_PERACTOR = 0x00000002, // per-actor variable + GAMEVAR_USER_MASK = (GAMEVAR_PERPLAYER | GAMEVAR_PERACTOR), + GAMEVAR_RESET = 0x00000008, // INTERNAL, don't use + GAMEVAR_DEFAULT = 0x00000100, // UNUSED, but always cleared for user-defined gamevars + GAMEVAR_NODEFAULT = 0x00000400, // don't reset on actor spawn + GAMEVAR_SYSTEM = 0x00000800, // cannot change mode flags...(only default value) + GAMEVAR_READONLY = 0x00001000, // values are read-only (no setvar allowed) + GAMEVAR_INT32PTR = 0x00002000, // plValues is a pointer to an int32_t + GAMEVAR_INT16PTR = 0x00008000, // plValues is a pointer to a short + GAMEVAR_NORESET = 0x00020000, // var values are not reset when restoring map state + GAMEVAR_SPECIAL = 0x00040000, // flag for structure member shortcut vars + GAMEVAR_NOMULTI = 0x00080000, // don't attach to multiplayer packets + GAMEVAR_Q16PTR = 0x00100000, // plValues is a pointer to a q16.16 + GAMEVAR_SERIALIZE = 0x00200000, // write into permasaves + + GAMEVAR_RAWQ16PTR = GAMEVAR_Q16PTR | GAMEVAR_SPECIAL, // plValues is a pointer to a q16.16 but we don't want conversion + GAMEVAR_PTR_MASK = GAMEVAR_INT32PTR | GAMEVAR_INT16PTR | GAMEVAR_Q16PTR | GAMEVAR_RAWQ16PTR, +}; + // Alignments for per-player and per-actor variables. #define PLAYER_VAR_ALIGNMENT (sizeof(intptr_t)) #define ACTOR_VAR_ALIGNMENT 16 #define ARRAY_ALIGNMENT 16 +#pragma pack(push,1) +typedef struct +{ + union { + intptr_t global; + intptr_t *pValues; // array of values when 'per-player', or 'per-actor' + }; + intptr_t defaultValue; + uintptr_t flags; + char * szLabel; +} gamevar_t; +#pragma pack(pop) + +extern gamevar_t aGameVars[MAXGAMEVARS]; +extern int32_t g_gameVarCount; + +int __fastcall Gv_GetVar(int const gameVar, int const spriteNum, int const playerNum); +void __fastcall Gv_SetVar(int const gameVar, int const newValue, int const spriteNum, int const playerNum); +int __fastcall Gv_GetVar(int const gameVar); +void __fastcall Gv_GetManyVars(int const numVars, int32_t * const outBuf); +void __fastcall Gv_SetVar(int const gameVar, int const newValue); + +template +static FORCE_INLINE void Gv_FillWithVars(T & rv) +{ + EDUKE32_STATIC_ASSERT(sizeof(T) % sizeof(int32_t) == 0); + EDUKE32_STATIC_ASSERT(sizeof(T) > sizeof(int32_t)); + Gv_GetManyVars(sizeof(T)/sizeof(int32_t), (int32_t *)&rv); +} + +int Gv_GetVarByLabel(const char *szGameLabel,int defaultValue,int spriteNum,int playerNum); +void Gv_NewVar(const char *pszLabel,intptr_t lValue,uint32_t dwFlags); + +static FORCE_INLINE void A_ResetVars(int const spriteNum) +{ + for (auto &gv : aGameVars) + { + if ((gv.flags & (GAMEVAR_PERACTOR|GAMEVAR_NODEFAULT)) == GAMEVAR_PERACTOR) + gv.pValues[spriteNum] = gv.defaultValue; + } +} +void Gv_DumpValues(void); +void Gv_InitWeaponPointers(void); +void Gv_RefreshPointers(void); +void Gv_ResetVars(void); +int Gv_ReadSave(buildvfs_kfd kFile); +void Gv_WriteSave(buildvfs_FILE fil); +void Gv_Clear(void); + +void Gv_ResetSystemDefaults(void); +void Gv_Init(void); +void Gv_FinalizeWeaponDefaults(void); + +#define VM_GAMEVAR_OPERATOR(func, operator) \ + static FORCE_INLINE ATTRIBUTE((flatten)) void __fastcall func(int const id, int32_t const operand) \ + { \ + auto &var = aGameVars[id]; \ + \ + switch (var.flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) \ + { \ + default: \ + var.global operator operand; \ + break; \ + case GAMEVAR_PERPLAYER: \ + var.pValues[vm.playerNum & (MAXPLAYERS-1)] operator operand; \ + break; \ + case GAMEVAR_PERACTOR: \ + var.pValues[vm.spriteNum & (MAXSPRITES-1)] operator operand; \ + break; \ + case GAMEVAR_INT32PTR: *(int32_t *)var.pValues operator(int32_t) operand; break; \ + case GAMEVAR_INT16PTR: *(int16_t *)var.pValues operator(int16_t) operand; break; \ + case GAMEVAR_Q16PTR: \ + { \ + Fix16 *pfix = (Fix16 *)var.global; \ + *pfix operator fix16_from_int(operand); \ + break; \ + } \ + } \ + } + +VM_GAMEVAR_OPERATOR(Gv_AddVar, +=) +VM_GAMEVAR_OPERATOR(Gv_SubVar, -=) + +#undef VM_GAMEVAR_OPERATOR + #endif diff --git a/source/rr/src/player.h b/source/rr/src/player.h index 41a4209d8..135c5efb7 100644 --- a/source/rr/src/player.h +++ b/source/rr/src/player.h @@ -56,6 +56,24 @@ extern int32_t PHEIGHT; #define MAX_WEAPON_RECS 256 +enum weaponflags_t { + WEAPON_SPAWNTYPE1 = 0x00000000, // just spawn + WEAPON_HOLSTER_CLEARS_CLIP = 0x00000001, // 'holstering' clears the current clip + WEAPON_GLOWS = 0x00000002, // weapon 'glows' (shrinker and grower) + WEAPON_AUTOMATIC = 0x00000004, // automatic fire (continues while 'fire' is held down + WEAPON_FIREEVERYOTHER = 0x00000008, // during 'hold time' fire every frame + WEAPON_FIREEVERYTHIRD = 0x00000010, // during 'hold time' fire every third frame + WEAPON_RANDOMRESTART = 0x00000020, // restart for automatic is 'randomized' by RND 3 + WEAPON_AMMOPERSHOT = 0x00000040, // uses ammo for each shot (for automatic) + WEAPON_BOMB_TRIGGER = 0x00000080, // weapon is the 'bomb' trigger + WEAPON_NOVISIBLE = 0x00000100, // weapon use does not cause user to become 'visible' + WEAPON_THROWIT = 0x00000200, // weapon 'throws' the 'shoots' item... + WEAPON_CHECKATRELOAD = 0x00000400, // check weapon availability at 'reload' time + WEAPON_STANDSTILL = 0x00000800, // player stops jumping before actual fire (like tripbomb in duke) + WEAPON_SPAWNTYPE2 = 0x00001000, // spawn like shotgun shells + WEAPON_SPAWNTYPE3 = 0x00002000, // spawn like chaingun shells +}; + enum gamemode_t { MODE_MENU = 0x00000001, MODE_DEMO = 0x00000002, @@ -223,6 +241,51 @@ typedef struct { } playerdata_t; #pragma pack(pop) +// KEEPINSYNC lunatic/con_lang.lua +typedef struct +{ + // NOTE: the member names must be identical to aplWeapon* suffixes. + int32_t WorksLike; // What the original works like + int32_t Clip; // number of items in magazine + int32_t Reload; // delay to reload (include fire) + int32_t FireDelay; // delay to fire + int32_t TotalTime; // The total time the weapon is cycling before next fire. + int32_t HoldDelay; // delay after release fire button to fire (0 for none) + int32_t Flags; // Flags for weapon + int32_t Shoots; // what the weapon shoots + int32_t SpawnTime; // the frame at which to spawn an item + int32_t Spawn; // the item to spawn + int32_t ShotsPerBurst; // number of shots per 'burst' (one ammo per 'burst') + int32_t InitialSound; // Sound made when weapon starts firing. zero for no sound + int32_t FireSound; // Sound made when firing (each time for automatic) + int32_t Sound2Time; // Alternate sound time + int32_t Sound2Sound; // Alternate sound sound ID + int32_t FlashColor; // Muzzle flash color +} weapondata_t; + +#ifdef LUNATIC +# define PWEAPON(Player, Weapon, Wmember) (g_playerWeapon[Player][Weapon].Wmember) +extern weapondata_t g_playerWeapon[MAXPLAYERS][MAX_WEAPONS]; +#else +# define PWEAPON(Player, Weapon, Wmember) (aplWeapon ## Wmember [Weapon][Player]) +extern intptr_t *aplWeaponClip[MAX_WEAPONS]; // number of items in clip +extern intptr_t *aplWeaponReload[MAX_WEAPONS]; // delay to reload (include fire) +extern intptr_t *aplWeaponFireDelay[MAX_WEAPONS]; // delay to fire +extern intptr_t *aplWeaponHoldDelay[MAX_WEAPONS]; // delay after release fire button to fire (0 for none) +extern intptr_t *aplWeaponTotalTime[MAX_WEAPONS]; // The total time the weapon is cycling before next fire. +extern intptr_t *aplWeaponFlags[MAX_WEAPONS]; // Flags for weapon +extern intptr_t *aplWeaponShoots[MAX_WEAPONS]; // what the weapon shoots +extern intptr_t *aplWeaponSpawnTime[MAX_WEAPONS]; // the frame at which to spawn an item +extern intptr_t *aplWeaponSpawn[MAX_WEAPONS]; // the item to spawn +extern intptr_t *aplWeaponShotsPerBurst[MAX_WEAPONS]; // number of shots per 'burst' (one ammo per 'burst' +extern intptr_t *aplWeaponWorksLike[MAX_WEAPONS]; // What original the weapon works like +extern intptr_t *aplWeaponInitialSound[MAX_WEAPONS]; // Sound made when initialy firing. zero for no sound +extern intptr_t *aplWeaponFireSound[MAX_WEAPONS]; // Sound made when firing (each time for automatic) +extern intptr_t *aplWeaponSound2Time[MAX_WEAPONS]; // Alternate sound time +extern intptr_t *aplWeaponSound2Sound[MAX_WEAPONS]; // Alternate sound sound ID +extern intptr_t *aplWeaponFlashColor[MAX_WEAPONS]; // Color for polymer muzzle flash +#endif + // KEEPINSYNC lunatic/_defs_game.lua typedef struct { int32_t cur, count; // "cur" is the only member that is *used* diff --git a/source/rr/src/premap.cpp b/source/rr/src/premap.cpp index 3b1d996ab..141cf4434 100644 --- a/source/rr/src/premap.cpp +++ b/source/rr/src/premap.cpp @@ -1935,6 +1935,11 @@ end_vol4a: pPlayer->gm = 0; M_ClearMenus(); + Gv_ResetVars(); + Gv_InitWeaponPointers(); + Gv_RefreshPointers(); + Gv_ResetSystemDefaults(); + //AddLog("Newgame"); for (bssize_t i=0; i<(MAXVOLUMES*MAXLEVELS); i++) diff --git a/source/rr/src/savegame.cpp b/source/rr/src/savegame.cpp index 618e64e00..358be3e3e 100644 --- a/source/rr/src/savegame.cpp +++ b/source/rr/src/savegame.cpp @@ -348,6 +348,19 @@ typedef struct dataspec_ intptr_t cnt; } dataspec_t; +typedef struct dataspec_gv_ +{ + uint32_t flags; + void * ptr; + uint32_t size; + intptr_t cnt; +} dataspec_gv_t; + +#define SV_DEFAULTCOMPRTHRES 8 +static uint8_t savegame_diffcompress; // 0:none, 1:Ken's LZW in cache1d.c +static uint8_t savegame_comprthres; + + #define DS_DYNAMIC 1 // dereference .ptr one more time #define DS_STRING 2 #define DS_CMP 4 @@ -970,6 +983,7 @@ static const dataspec_t svgm_anmisc[] = { DS_END, 0, 0, 0 } }; +static dataspec_gv_t *svgm_vars=NULL; static uint8_t *dosaveplayer2(FileWriter *fil, uint8_t *mem); static int32_t doloadplayer2(FileReader &fil, uint8_t **memptr); static void postloadplayer(int32_t savegamep); @@ -983,6 +997,46 @@ static uint8_t *svdiff; #include "gamedef.h" +#define SV_SKIPMASK (/*GAMEVAR_SYSTEM|*/ GAMEVAR_READONLY | GAMEVAR_PTR_MASK | /*GAMEVAR_NORESET |*/ GAMEVAR_SPECIAL) + +static char svgm_vars_string [] = "blK:vars"; +// setup gamevar data spec for snapshotting and diffing... gamevars must be loaded when called +static void sv_makevarspec() +{ + int vcnt = 0; + + for (int i = 0; i < g_gameVarCount; i++) + vcnt += (aGameVars[i].flags & SV_SKIPMASK) ? 0 : 1; + + svgm_vars = (dataspec_gv_t *)Xrealloc(svgm_vars, (vcnt + 2) * sizeof(dataspec_gv_t)); + + svgm_vars[0].flags = DS_STRING; + svgm_vars[0].ptr = svgm_vars_string; + svgm_vars[0].cnt = 1; + + vcnt = 1; + + for (int i = 0; i < g_gameVarCount; i++) + { + if (aGameVars[i].flags & SV_SKIPMASK) + continue; + + unsigned const per = aGameVars[i].flags & GAMEVAR_USER_MASK; + + svgm_vars[vcnt].flags = 0; + svgm_vars[vcnt].ptr = (per == 0) ? &aGameVars[i].global : aGameVars[i].pValues; + svgm_vars[vcnt].size = sizeof(intptr_t); + svgm_vars[vcnt].cnt = (per == 0) ? 1 : (per == GAMEVAR_PERPLAYER ? MAXPLAYERS : MAXSPRITES); + + ++vcnt; + } + + svgm_vars[vcnt].flags = DS_END; + svgm_vars[vcnt].ptr = NULL; + svgm_vars[vcnt].size = 0; + svgm_vars[vcnt].cnt = 0; +} + void sv_freemem() { DO_FREE_AND_NULL(svsnapshot); @@ -1007,7 +1061,9 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, int8_t spot, bool isAutoSave) savehead_t h; // calculate total snapshot size - svsnapsiz = calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc); + sv_makevarspec(); + svsnapsiz = calcsz((const dataspec_t *)svgm_vars); + svsnapsiz += calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc); // create header @@ -1225,6 +1281,7 @@ uint32_t sv_writediff(FileWriter *fil) cmpspecdata(svgm_secwsp, &p, &d); cmpspecdata(svgm_script, &p, &d); cmpspecdata(svgm_anmisc, &p, &d); + cmpspecdata((const dataspec_t *)svgm_vars, &p, &d); if (p != svsnapshot+svsnapsiz) OSD_Printf("sv_writediff: dump+siz=%p, p=%p!\n", svsnapshot+svsnapsiz, p); @@ -1256,6 +1313,7 @@ int32_t sv_readdiff(FileReader &fil) if (applydiff(svgm_secwsp, &p, &d)) return -4; if (applydiff(svgm_script, &p, &d)) return -5; if (applydiff(svgm_anmisc, &p, &d)) return -6; + if (applydiff((const dataspec_t *)svgm_vars, &p, &d)) return -7; int i = 0; @@ -1369,6 +1427,9 @@ static uint8_t *dosaveplayer2(FileWriter *fil, uint8_t *mem) PRINTSIZE("script"); mem=writespecdata(svgm_anmisc, fil, mem); // animates, quotes & misc. PRINTSIZE("animisc"); + Gv_WriteSave(fil); // gamevars + mem=writespecdata((const dataspec_t *)svgm_vars, 0, mem); + PRINTSIZE("vars"); return mem; } @@ -1389,6 +1450,23 @@ static int32_t doloadplayer2(FileReader &fil, uint8_t **memptr) if (readspecdata(svgm_anmisc, &fil, &mem)) return -6; PRINTSIZE("animisc"); + int i; + + if ((i = Gv_ReadSave(fil))) return i; + + if (mem) + { + int32_t i; + + sv_makevarspec(); + for (i=1; svgm_vars[i].flags!=DS_END; i++) + { + Bmemcpy(mem, svgm_vars[i].ptr, svgm_vars[i].size*svgm_vars[i].cnt); // careful! works because there are no DS_DYNAMIC's! + mem += svgm_vars[i].size*svgm_vars[i].cnt; + } + } + PRINTSIZE("vars"); + if (memptr) *memptr = mem; return 0; @@ -1405,6 +1483,7 @@ int32_t sv_updatestate(int32_t frominit) if (readspecdata(svgm_secwsp, nullptr, &p)) return -4; if (readspecdata(svgm_script, nullptr, &p)) return -5; if (readspecdata(svgm_anmisc, nullptr, &p)) return -6; + if (readspecdata((const dataspec_t *)svgm_vars, -1, &p)) return -8; if (p != pbeg+svsnapsiz) { @@ -1529,6 +1608,12 @@ static void postloadplayer(int32_t savegamep) // if (savegamep) ? G_ResetTimers(0); +#ifdef USE_STRUCT_TRACKERS + Bmemset(sectorchanged, 0, sizeof(sectorchanged)); + Bmemset(spritechanged, 0, sizeof(spritechanged)); + Bmemset(wallchanged, 0, sizeof(wallchanged)); +#endif + #ifdef POLYMER //9 if (videoGetRenderMode() == REND_POLYMER) diff --git a/source/rr/src/sector.h b/source/rr/src/sector.h index f167be4a2..ab9db8223 100644 --- a/source/rr/src/sector.h +++ b/source/rr/src/sector.h @@ -88,6 +88,7 @@ typedef struct { #ifndef NEW_MAP_FORMAT wallext_t wallext[MAXWALLS]; #endif + intptr_t *vars[MAXGAMEVARS]; #ifdef YAX_ENABLE int32_t numyaxbunches; # if !defined NEW_MAP_FORMAT