From eb7c36d72c04628aacea18c79dc95e87fc1a7581 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sat, 16 Jan 2016 19:31:36 -0600 Subject: [PATCH 001/193] Reimplement I/O library Note: I/O library is fully functioning in this state, but lacks security restrictions. We'll get to those later ;) --- src/blua/Makefile.cfg | 1 + src/blua/linit.c | 1 + src/blua/liolib.c | 553 ++++++++++++++++++++++++++++++++++++++++++ src/blua/lualib.h | 3 + 4 files changed, 558 insertions(+) create mode 100644 src/blua/liolib.c diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index e3fb3df46..895f9362d 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -18,6 +18,7 @@ OBJS:=$(OBJS) \ $(OBJDIR)/ldo.o \ $(OBJDIR)/lfunc.o \ $(OBJDIR)/linit.o \ + $(OBJDIR)/liolib.o \ $(OBJDIR)/llex.o \ $(OBJDIR)/lmem.o \ $(OBJDIR)/lobject.o \ diff --git a/src/blua/linit.c b/src/blua/linit.c index 52b02dbe7..d17390b20 100644 --- a/src/blua/linit.c +++ b/src/blua/linit.c @@ -17,6 +17,7 @@ static const luaL_Reg lualibs[] = { {"", luaopen_base}, {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, {LUA_STRLIBNAME, luaopen_string}, {NULL, NULL} }; diff --git a/src/blua/liolib.c b/src/blua/liolib.c new file mode 100644 index 000000000..e79ed1cb2 --- /dev/null +++ b/src/blua/liolib.c @@ -0,0 +1,553 @@ +/* +** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 + + +static const char *const fnames[] = {"input", "output"}; + + +static int pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + else + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static void fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((FILE **)ud) == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L) { + FILE **f = tofilep(L); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = lua_pclose(L, *p); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = (fclose(*p) == 0); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +static int aux_close (lua_State *L) { + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + +static int io_gc (lua_State *L) { + FILE *f = *tofilep(L); + /* ignore closed files */ + if (f != NULL) + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + FILE *f = *tofilep(L); + if (f == NULL) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", f); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +/* +** this function has a separated environment, which defines the +** correct __close for 'popen' files +*/ +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = lua_popen(L, filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, int findex) { + FILE *f; + lua_rawgeti(L, LUA_ENVIRONINDEX, findex); + f = *(FILE **)lua_touserdata(L, -1); + if (f == NULL) + luaL_error(L, "standard %s file is closed", fnames[findex - 1]); + return f; +} + + +static int g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawseti(L, LUA_ENVIRONINDEX, f); + } + /* return current value */ + lua_rawgeti(L, LUA_ENVIRONINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + if (*pf == NULL) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + sucess = read_line(L, f); + if (ferror(f)) + return luaL_error(L, "%s", strerror(errno)); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], sz); + return pushresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L)) == 0, NULL); +} + + +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_Reg flib[] = { + {"close", io_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, + {"seek", f_seek}, + {"setvbuf", f_setvbuf}, + {"write", f_write}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +} + + +static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { + *newfile(L) = f; + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +} + + +static void newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} + + +LUALIB_API int luaopen_io (lua_State *L) { + createmeta(L); + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + /* create (and set) default files */ + newfenv(L, io_noclose); /* close function for default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, 0, "stderr"); + lua_pop(L, 1); /* pop environment for default files */ + lua_getfield(L, -1, "popen"); + newfenv(L, io_pclose); /* create environment for 'popen' */ + lua_setfenv(L, -2); /* set fenv for 'popen' */ + lua_pop(L, 1); /* pop 'popen' */ + return 1; +} + diff --git a/src/blua/lualib.h b/src/blua/lualib.h index 6ebe27287..4ea97edf3 100644 --- a/src/blua/lualib.h +++ b/src/blua/lualib.h @@ -21,6 +21,9 @@ LUALIB_API int (luaopen_base) (lua_State *L); #define LUA_TABLIBNAME "table" LUALIB_API int (luaopen_table) (lua_State *L); +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + #define LUA_STRLIBNAME "string" LUALIB_API int (luaopen_string) (lua_State *L); From c9fe83b95daa3189465049e53c058bdfa1ce51b4 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sun, 17 Jan 2016 01:37:19 -0600 Subject: [PATCH 002/193] Block the use of ../ and ..\\ ...and remove io.popen(), cause that shit is DANGEROUS. --- src/blua/liolib.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index e79ed1cb2..5c48add74 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -160,6 +160,11 @@ static int io_tostring (lua_State *L) { static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); + if (strstr(filename, "../") || strstr(filename, "..\\")) + { + luaL_error(L,"access denied to %s", filename); + return pushresult(L,0,filename); + } const char *mode = luaL_optstring(L, 2, "r"); FILE **pf = newfile(L); *pf = fopen(filename, mode); @@ -167,19 +172,6 @@ static int io_open (lua_State *L) { } -/* -** this function has a separated environment, which defines the -** correct __close for 'popen' files -*/ -static int io_popen (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = lua_popen(L, filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - static int io_tmpfile (lua_State *L) { FILE **pf = newfile(L); *pf = tmpfile(); @@ -481,7 +473,6 @@ static const luaL_Reg iolib[] = { {"lines", io_lines}, {"open", io_open}, {"output", io_output}, - {"popen", io_popen}, {"read", io_read}, {"tmpfile", io_tmpfile}, {"type", io_type}, From a68e92690f8cc176e0c0e92b73e9c38ead20d3b3 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Wed, 20 Jan 2016 02:26:15 -0600 Subject: [PATCH 003/193] Implement file type whitelist, completely remove popen and pclose This is probably super inefficient. Someone please teach me how2C. --- src/blua/liolib.c | 52 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 5c48add74..d2303415e 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -25,6 +25,13 @@ static const char *const fnames[] = {"input", "output"}; +static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder + ".txt", + ".sav2", + ".cfg", + ".png", + ".bmp" +}; static int pushresult (lua_State *L, int i, const char *filename) { @@ -102,17 +109,6 @@ static int io_noclose (lua_State *L) { } -/* -** function to close 'popen' files -*/ -static int io_pclose (lua_State *L) { - FILE **p = tofilep(L); - int ok = lua_pclose(L, *p); - *p = NULL; - return pushresult(L, ok, NULL); -} - - /* ** function to close regular files */ @@ -159,16 +155,26 @@ static int io_tostring (lua_State *L) { static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - if (strstr(filename, "../") || strstr(filename, "..\\")) - { - luaL_error(L,"access denied to %s", filename); - return pushresult(L,0,filename); - } - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; + const char *filename = luaL_checkstring(L, 1); + int pass = 0; int i; + int length = strlen(filename) - 1; + for (i = 0; i < 5; i++) // wolfs == noobcoder, so manually change this with any added file types + { + if (!stricmp(&filename[length - (strlen(whitelist[i]) - 1)], whitelist[i])) + { + pass = 1; + break; + } + } + if (strstr(filename, "../") || strstr(filename, "..\\") || !pass) + { + luaL_error(L,"access denied to %s", filename); + return pushresult(L,0,filename); + } + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } @@ -535,10 +541,6 @@ LUALIB_API int luaopen_io (lua_State *L) { createstdfile(L, stdout, IO_OUTPUT, "stdout"); createstdfile(L, stderr, 0, "stderr"); lua_pop(L, 1); /* pop environment for default files */ - lua_getfield(L, -1, "popen"); - newfenv(L, io_pclose); /* create environment for 'popen' */ - lua_setfenv(L, -2); /* set fenv for 'popen' */ - lua_pop(L, 1); /* pop 'popen' */ return 1; } From 569f7d15d1992a437ad48a881feb62ec80d936bd Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Wed, 20 Jan 2016 16:59:55 -0600 Subject: [PATCH 004/193] Efficiency improvement in whitelist check Thanks Inu! --- src/blua/liolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index d2303415e..6dd877046 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -158,7 +158,7 @@ static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); int pass = 0; int i; int length = strlen(filename) - 1; - for (i = 0; i < 5; i++) // wolfs == noobcoder, so manually change this with any added file types + for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++) { if (!stricmp(&filename[length - (strlen(whitelist[i]) - 1)], whitelist[i])) { From de03db99e7110b65190d6d3fe540dfed623b33f2 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Wed, 20 Jan 2016 21:31:00 -0600 Subject: [PATCH 005/193] Block possible methods of accessing folders outside of SRB2 --- src/blua/liolib.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 6dd877046..100dbb6c1 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -153,6 +153,12 @@ static int io_tostring (lua_State *L) { return 1; } +static int StartsWith(const char *a, const char *b) // this is wolfs being lazy yet again +{ + if(strncmp(a, b, strlen(b)) == 0) return 1; + return 0; +} + static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); @@ -166,7 +172,8 @@ static int io_open (lua_State *L) { break; } } - if (strstr(filename, "../") || strstr(filename, "..\\") || !pass) + if (strstr(filename, "..") || strchr(filename, ':') || StartsWith(filename, "\\") + || StartsWith(filename, "/") || !pass) { luaL_error(L,"access denied to %s", filename); return pushresult(L,0,filename); From 5579fb5240347ddc86987a8aedbae20d8602c4bd Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Wed, 20 Jan 2016 22:35:53 -0600 Subject: [PATCH 006/193] Limit file creation/manipulation to luafiles subfolder Also block % in filename strings --- src/blua/liolib.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 100dbb6c1..ac168ef18 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -17,6 +17,9 @@ #include "lauxlib.h" #include "lualib.h" +#include "../i_system.h" +#include "../doomdef.h" +#include "../m_misc.h" @@ -173,11 +176,14 @@ static int io_open (lua_State *L) { } } if (strstr(filename, "..") || strchr(filename, ':') || StartsWith(filename, "\\") - || StartsWith(filename, "/") || !pass) + || StartsWith(filename, "/") || strchr(filename, '%') || !pass) { luaL_error(L,"access denied to %s", filename); return pushresult(L,0,filename); } + I_mkdir("luafiles", 0755); + char* destFilename = va("luafiles"PATHSEP"%s", filename); + filename = destFilename; const char *mode = luaL_optstring(L, 2, "r"); FILE **pf = newfile(L); *pf = fopen(filename, mode); From e27494652850945f8e69383b8c726639cb4a54dd Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Fri, 22 Jan 2016 00:46:32 -0600 Subject: [PATCH 007/193] Call I_mkdir on subdirectories [NEEDS CLEANUP] I got lazy and used a weird inefficient method, but I don't know how to clean this mess up :c At least it works. --- src/blua/liolib.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index ac168ef18..b209eec91 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -182,6 +182,22 @@ static int io_open (lua_State *L) { return pushresult(L,0,filename); } I_mkdir("luafiles", 0755); + char *splitter = filename; + while ((splitter = strchr(splitter, '/'))) + { + *splitter = 0; + I_mkdir(va("luafiles"PATHSEP"%s", filename), 0755); + *splitter = '/'; + splitter++; + } + char *splitter2 = filename; + while ((splitter2 = strchr(splitter2, '\\'))) + { + *splitter2 = 0; + I_mkdir(va("luafiles"PATHSEP"%s", filename), 0755); + *splitter2 = '\\'; + splitter2++; + } char* destFilename = va("luafiles"PATHSEP"%s", filename); filename = destFilename; const char *mode = luaL_optstring(L, 2, "r"); From 591b8035663fc60490229b63b4c43f55b0c0a81c Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Fri, 22 Jan 2016 21:45:48 -0600 Subject: [PATCH 008/193] Limit file write size to 1MB If the total file size is above 1MB after writing, discard all changes. --- src/blua/liolib.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index b209eec91..2d20f5292 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -26,6 +26,8 @@ #define IO_INPUT 1 #define IO_OUTPUT 2 +#define FILELIMIT 1024*1024 // Size limit for reading/writing files + static const char *const fnames[] = {"input", "output"}; static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder @@ -437,6 +439,7 @@ static int io_readline (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - 1; int status = 1; + size_t count; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ @@ -446,6 +449,12 @@ static int g_write (lua_State *L, FILE *f, int arg) { else { size_t l; const char *s = luaL_checklstring(L, arg, &l); + count += l; + if (ftell(f) + l > FILELIMIT) + { + luaL_error(L,"write limit bypassed in file. Changes have been discarded."); + break; + } status = status && (fwrite(s, sizeof(char), l, f) == l); } } From 2639bf4c0ba035bcd79f12ae4f6f00d86feeb662 Mon Sep 17 00:00:00 2001 From: Nipples the Enchilada Date: Sun, 24 Jan 2016 00:24:26 -0500 Subject: [PATCH 009/193] Add liolib.c to CMakeLists --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb4f9a4a6..9a8b3baca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -265,6 +265,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) blua/lfunc.c blua/lgc.c blua/linit.c + blua/liolib.c blua/llex.c blua/lmem.c blua/lobject.c From 0f2a0927418334376a1675590ba7313dde44606e Mon Sep 17 00:00:00 2001 From: Inuyasha Date: Wed, 20 Jan 2016 01:13:21 -0800 Subject: [PATCH 010/193] objectplace stability fix Objectplace reallocates the mapthings list to add one more mapthing. By itself there's no problem with this. But, mobj->spawnpoint is a pointer to the mapthing's location in the mapthings list. So by reallocating the mapthings list, all references to mobj->spawnpoints point to freed memory. ... Oops. Now when objectplace reallocates the mapthings list it actually corrects the locations of all mobj's spawnpoints to point to the new list. Hooray, you can use NiGHTS objectplace again if you really want to. --- src/m_cheat.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index bc32e6cfa..473fbbf75 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -880,12 +880,33 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling) static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean ceiling) { - mapthing_t *mt; + mapthing_t *mt = mapthings; + #ifdef HAVE_BLUA LUA_InvalidateMapthings(); #endif mapthings = Z_Realloc(mapthings, ++nummapthings * sizeof (*mapthings), PU_LEVEL, NULL); + + // as Z_Realloc can relocate mapthings, quickly go through thinker list and correct + // the spawnpoints of any objects that have them to the new location + if (mt != mapthings) + { + thinker_t *th; + mobj_t *mo; + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo = (mobj_t *)th; + // get offset from mt, which points to old mapthings, then add new location + if (mo->spawnpoint) + mo->spawnpoint = (mo->spawnpoint - mt) + mapthings; + } + } + mt = (mapthings+nummapthings-1); mt->type = type; From f2f8906a19cd17163853f402f828f609df4c8069 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 18 Jan 2016 19:46:00 +0000 Subject: [PATCH 011/193] Use modulo, not bitwise AND. My fault once again, whoops. The point here is ColorOpposite(MAXSKINCOLORS) would have given an actual result of its own since MAXSKINCOLORS & MAXSKINCOLORS is still MAXSKINCOLORS. This shouldn't happen though, as both Color_Opposite[MAXSKINCOLORS*2] and Color_Opposite[MAXSKINCOLOR*2+1] aren't defined. --- src/lua_mathlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c index f4b5ca5fe..fd00180d5 100644 --- a/src/lua_mathlib.c +++ b/src/lua_mathlib.c @@ -166,7 +166,7 @@ static int lib_all7emeralds(lua_State *L) // Returns both color and frame numbers! static int lib_coloropposite(lua_State *L) { - int colornum = ((int)luaL_checkinteger(L, 1)) & MAXSKINCOLORS; + int colornum = ((int)luaL_checkinteger(L, 1)) % MAXSKINCOLORS; lua_pushinteger(L, Color_Opposite[colornum*2]); // push color lua_pushinteger(L, Color_Opposite[colornum*2+1]); // push frame return 2; From f559b50995ea7be5e7b3119a095aeab8cc235773 Mon Sep 17 00:00:00 2001 From: Inuyasha Date: Wed, 20 Jan 2016 09:25:28 -0800 Subject: [PATCH 012/193] fix bad lstring usage in map header lua This is not how you use pushlstring! This is actually sending uninitialized memory to Lua, which is making scripts have inconsistent results (duh?) c/o JTE: "Tell Red they're a doofus." --- src/lua_maplib.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 0a12478ca..85c3b094c 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1162,9 +1162,9 @@ static int mapheaderinfo_get(lua_State *L) //for (i = 0; i < 21; i++) // if (!header->lvlttl[i]) // break; - lua_pushlstring(L, header->lvlttl, 21); + lua_pushstring(L, header->lvlttl); } else if (fastcmp(field,"subttl")) - lua_pushlstring(L, header->subttl, 32); + lua_pushstring(L, header->subttl); else if (fastcmp(field,"actnum")) lua_pushinteger(L, header->actnum); else if (fastcmp(field,"typeoflevel")) @@ -1176,7 +1176,7 @@ static int mapheaderinfo_get(lua_State *L) else if (fastcmp(field,"musicslottrack")) lua_pushinteger(L, header->musicslottrack); else if (fastcmp(field,"forcecharacter")) - lua_pushlstring(L, header->forcecharacter, 16); + lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) lua_pushinteger(L, header->weather); else if (fastcmp(field,"skynum")) @@ -1188,11 +1188,11 @@ static int mapheaderinfo_get(lua_State *L) else if (fastcmp(field,"skybox_scalez")) lua_pushinteger(L, header->skybox_scalez); else if (fastcmp(field,"interscreen")) - lua_pushlstring(L, header->interscreen, 8); + lua_pushstring(L, header->interscreen); else if (fastcmp(field,"runsoc")) - lua_pushlstring(L, header->runsoc, 32); + lua_pushstring(L, header->runsoc); else if (fastcmp(field,"scriptname")) - lua_pushlstring(L, header->scriptname, 32); + lua_pushstring(L, header->scriptname); else if (fastcmp(field,"precutscenenum")) lua_pushinteger(L, header->precutscenenum); else if (fastcmp(field,"cutscenenum")) @@ -1221,7 +1221,7 @@ static int mapheaderinfo_get(lua_State *L) for (;i < header->numCustomOptions && !fastcmp(field, header->customopts[i].option); ++i); if(i < header->numCustomOptions) - lua_pushlstring(L, header->customopts[i].value, 255); + lua_pushstring(L, header->customopts[i].value); else lua_pushnil(L); } From c30188f9fdfa6cf674e998b962d99e3f203a2606 Mon Sep 17 00:00:00 2001 From: Inuyasha Date: Wed, 20 Jan 2016 09:42:35 -0800 Subject: [PATCH 013/193] interscreen is a lump name and thus needs lstring ... not just lstring though, but the behavior with i that is used elsewhere. --- src/lua_maplib.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 85c3b094c..6f28997ac 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1157,13 +1157,10 @@ static int mapheaderinfo_get(lua_State *L) { mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER)); const char *field = luaL_checkstring(L, 2); - //INT16 i; - if (fastcmp(field,"lvlttl")) { - //for (i = 0; i < 21; i++) - // if (!header->lvlttl[i]) - // break; + INT16 i; + if (fastcmp(field,"lvlttl")) lua_pushstring(L, header->lvlttl); - } else if (fastcmp(field,"subttl")) + else if (fastcmp(field,"subttl")) lua_pushstring(L, header->subttl); else if (fastcmp(field,"actnum")) lua_pushinteger(L, header->actnum); @@ -1187,9 +1184,12 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->skybox_scaley); else if (fastcmp(field,"skybox_scalez")) lua_pushinteger(L, header->skybox_scalez); - else if (fastcmp(field,"interscreen")) - lua_pushstring(L, header->interscreen); - else if (fastcmp(field,"runsoc")) + else if (fastcmp(field,"interscreen")) { + for (i = 0; i < 8; i++) + if (!header->interscreen[i]) + break; + lua_pushlstring(L, header->interscreen, i); + } else if (fastcmp(field,"runsoc")) lua_pushstring(L, header->runsoc); else if (fastcmp(field,"scriptname")) lua_pushstring(L, header->scriptname); From c7e540a870f53711a3580b3510de83e33d8e14bc Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Thu, 21 Jan 2016 13:50:05 -0500 Subject: [PATCH 014/193] whitespace cleanup --- src/r_bsp.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/r_bsp.c b/src/r_bsp.c index badf8bdac..c547713be 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -935,14 +935,14 @@ static void R_Subsector(size_t num) ffloor[numffloors].plane = NULL; ffloor[numffloors].polyobj = NULL; - - floorcenterz = + + floorcenterz = #ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) : #endif frontsector->floorheight; - - ceilingcenterz = + + ceilingcenterz = #ifdef ESLOPE frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) : #endif @@ -953,8 +953,8 @@ static void R_Subsector(size_t num) *rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) : #endif *rover->bottomheight; - - planecenterz = + + planecenterz = #ifdef ESLOPE *rover->b_slope ? P_GetZAt(*rover->b_slope, frontsector->soundorg.x, frontsector->soundorg.y) : #endif @@ -966,7 +966,7 @@ static void R_Subsector(size_t num) { light = R_GetPlaneLight(frontsector, planecenterz, viewz < *rover->bottomheight); - + ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic, *frontsector->lightlist[light].lightlevel, *rover->bottomxoffs, *rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover @@ -1002,8 +1002,8 @@ static void R_Subsector(size_t num) *rover->t_slope ? P_GetZAt(*rover->t_slope, viewx, viewy) : #endif *rover->topheight; - - planecenterz = + + planecenterz = #ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, frontsector->soundorg.x, frontsector->soundorg.y) : #endif @@ -1014,7 +1014,7 @@ static void R_Subsector(size_t num) || (viewz < heightcheck && (rover->flags & FF_BOTHPLANES)))) { light = R_GetPlaneLight(frontsector, planecenterz, viewz < *rover->topheight); - + ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic, *frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle, frontsector->lightlist[light].extra_colormap, rover From f65c5c016adb2456b020d3a8b280cc963370aeca Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 21 Jan 2016 20:27:35 +0000 Subject: [PATCH 015/193] Fix shadowing in mapheaderinfo_get --- src/lua_maplib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 6f28997ac..38920c223 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1217,11 +1217,11 @@ static int mapheaderinfo_get(lua_State *L) else { // Read custom vars now // (note: don't include the "LUA." in your lua scripts!) - UINT8 i = 0; - for (;i < header->numCustomOptions && !fastcmp(field, header->customopts[i].option); ++i); + UINT8 j = 0; + for (;j < header->numCustomOptions && !fastcmp(field, header->customopts[j].option); ++j); - if(i < header->numCustomOptions) - lua_pushstring(L, header->customopts[i].value); + if(j < header->numCustomOptions) + lua_pushstring(L, header->customopts[j].value); else lua_pushnil(L); } From 63a3e03bb0ed8e57860892acc4c8444e8be06d54 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 23 Jan 2016 18:59:17 +0000 Subject: [PATCH 016/193] Fixed math for calculating current texture in texture animations --- src/p_spec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index cac822ac8..81994d46c 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4671,11 +4671,11 @@ void P_UpdateSpecials(void) // ANIMATE TEXTURES for (anim = anims; anim < lastanim; anim++) { - for (i = anim->basepic; i < anim->basepic + anim->numpics; i++) + for (i = 0; i < anim->numpics; i++) { pic = anim->basepic + ((leveltime/anim->speed + i) % anim->numpics); if (anim->istexture) - texturetranslation[i] = pic; + texturetranslation[anim->basepic+i] = pic; } } From 14dcd2404b1f5972cee0b6dc4c559f2506c00a02 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 25 Jan 2016 16:45:08 +0000 Subject: [PATCH 017/193] Fixed compiler warnings to the best of my ability Hopefully this all still works properly, try not to kill me if it doesn't =V --- src/blua/liolib.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 2d20f5292..e9a50f370 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -167,8 +167,12 @@ static int StartsWith(const char *a, const char *b) // this is wolfs being lazy static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); - int pass = 0; int i; + int pass = 0; size_t i; int length = strlen(filename) - 1; + char *splitter, *splitter2; + char *destFilename = NULL; + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf; for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++) { if (!stricmp(&filename[length - (strlen(whitelist[i]) - 1)], whitelist[i])) @@ -184,26 +188,27 @@ static int io_open (lua_State *L) { return pushresult(L,0,filename); } I_mkdir("luafiles", 0755); - char *splitter = filename; + + strcpy(destFilename, filename); // copy file name to temp string + splitter = destFilename; while ((splitter = strchr(splitter, '/'))) { *splitter = 0; - I_mkdir(va("luafiles"PATHSEP"%s", filename), 0755); + I_mkdir(va("luafiles"PATHSEP"%s", destFilename), 0755); *splitter = '/'; splitter++; } - char *splitter2 = filename; + splitter2 = destFilename; while ((splitter2 = strchr(splitter2, '\\'))) { *splitter2 = 0; - I_mkdir(va("luafiles"PATHSEP"%s", filename), 0755); + I_mkdir(va("luafiles"PATHSEP"%s", destFilename), 0755); *splitter2 = '\\'; splitter2++; } - char* destFilename = va("luafiles"PATHSEP"%s", filename); + destFilename = va("luafiles"PATHSEP"%s", destFilename); filename = destFilename; - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); + pf = newfile(L); *pf = fopen(filename, mode); return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } From 9c299e5ad484a153d16e127881f4f7701295328e Mon Sep 17 00:00:00 2001 From: Nipples the Enchilada Date: Mon, 25 Jan 2016 22:48:45 -0500 Subject: [PATCH 018/193] Fix and clean up io_open after MI broke it horribly strcpy(NULL, filename); congrats --- src/blua/liolib.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index e9a50f370..dee512bf1 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -166,16 +166,18 @@ static int StartsWith(const char *a, const char *b) // this is wolfs being lazy static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - int pass = 0; size_t i; - int length = strlen(filename) - 1; - char *splitter, *splitter2; - char *destFilename = NULL; - const char *mode = luaL_optstring(L, 2, "r"); FILE **pf; + const char *filename = luaL_checkstring(L, 1); + int pass = 0; + size_t i; + int length = strlen(filename); + char *splitter, *forward, *backward; + char *destFilename; + const char *mode = luaL_optstring(L, 2, "r"); + for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++) { - if (!stricmp(&filename[length - (strlen(whitelist[i]) - 1)], whitelist[i])) + if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i])) { pass = 1; break; @@ -187,29 +189,27 @@ static int io_open (lua_State *L) { luaL_error(L,"access denied to %s", filename); return pushresult(L,0,filename); } - I_mkdir("luafiles", 0755); - - strcpy(destFilename, filename); // copy file name to temp string + + destFilename = va("luafiles"PATHSEP"%s", filename); + + // Make directories as needed splitter = destFilename; - while ((splitter = strchr(splitter, '/'))) + + forward = strchr(splitter, '/'); + backward = strchr(splitter, '\\'); + while ((splitter = (forward && backward) ? min(forward, backward) : (forward ?: backward))) { *splitter = 0; - I_mkdir(va("luafiles"PATHSEP"%s", destFilename), 0755); + I_mkdir(destFilename, 0755); *splitter = '/'; splitter++; + + forward = strchr(splitter, '/'); + backward = strchr(splitter, '\\'); } - splitter2 = destFilename; - while ((splitter2 = strchr(splitter2, '\\'))) - { - *splitter2 = 0; - I_mkdir(va("luafiles"PATHSEP"%s", destFilename), 0755); - *splitter2 = '\\'; - splitter2++; - } - destFilename = va("luafiles"PATHSEP"%s", destFilename); - filename = destFilename; + pf = newfile(L); - *pf = fopen(filename, mode); + *pf = fopen(destFilename, mode); return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } From 2eedecb93ac87e91277c076e39b4c3358e7c42eb Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Tue, 26 Jan 2016 12:07:28 +0000 Subject: [PATCH 019/193] Get rid of unneeded tab spaces Least I can do to make up for breaking io.open in the first place :E --- src/blua/liolib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index dee512bf1..27ff74492 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -174,7 +174,7 @@ static int io_open (lua_State *L) { char *splitter, *forward, *backward; char *destFilename; const char *mode = luaL_optstring(L, 2, "r"); - + for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++) { if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i])) @@ -189,12 +189,12 @@ static int io_open (lua_State *L) { luaL_error(L,"access denied to %s", filename); return pushresult(L,0,filename); } - + destFilename = va("luafiles"PATHSEP"%s", filename); - + // Make directories as needed splitter = destFilename; - + forward = strchr(splitter, '/'); backward = strchr(splitter, '\\'); while ((splitter = (forward && backward) ? min(forward, backward) : (forward ?: backward))) @@ -203,11 +203,11 @@ static int io_open (lua_State *L) { I_mkdir(destFilename, 0755); *splitter = '/'; splitter++; - + forward = strchr(splitter, '/'); backward = strchr(splitter, '\\'); } - + pf = newfile(L); *pf = fopen(destFilename, mode); return (*pf == NULL) ? pushresult(L, 0, filename) : 1; From db7da456d2d330f3c116e69017a61f3c82012614 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Fri, 29 Jan 2016 01:38:41 -0600 Subject: [PATCH 020/193] Remove block on % in filename strings According to Alam, supporting this shouldn't cause any issues. --- src/blua/liolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 27ff74492..a9e71f74a 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -184,7 +184,7 @@ static int io_open (lua_State *L) { } } if (strstr(filename, "..") || strchr(filename, ':') || StartsWith(filename, "\\") - || StartsWith(filename, "/") || strchr(filename, '%') || !pass) + || StartsWith(filename, "/") || !pass) { luaL_error(L,"access denied to %s", filename); return pushresult(L,0,filename); From d1981b0a654b583c3c85d25f9f87033ed94f18b5 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 22 Oct 2018 00:34:45 -0400 Subject: [PATCH 021/193] Half-done Discord Rich Presence support It's soooo close to being awesome, but so far away. Making game invitations as the host meant that it would redirect to "connect self" for other people, because we can't get the IP address from the host. If anyone has a solution, you've got my gratitude; this was going so smoothly until it hit that brick wall. --- .../win32-dynamic/bin/discord-rpc.dll | Bin 0 -> 307720 bytes .../win32-dynamic/include/discord_register.h | 26 +++ .../win32-dynamic/include/discord_rpc.h | 87 +++++++++ .../win32-dynamic/lib/discord-rpc.lib | Bin 0 -> 3656 bytes src/CMakeLists.txt | 15 ++ src/Makefile | 7 + src/d_clisrv.c | 12 ++ src/d_main.c | 8 + src/d_netcmd.c | 17 ++ src/d_netfil.c | 2 +- src/discord.c | 170 ++++++++++++++++++ src/discord.h | 20 +++ src/discord_pass.c | 0 src/discord_pass.h | 3 + src/g_game.c | 11 ++ src/mserv.c | 13 +- src/sdl/i_system.c | 6 +- src/win32/Makefile.cfg | 8 + 18 files changed, 400 insertions(+), 5 deletions(-) create mode 100644 libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll create mode 100644 libs/discord-rpc/win32-dynamic/include/discord_register.h create mode 100644 libs/discord-rpc/win32-dynamic/include/discord_rpc.h create mode 100644 libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib create mode 100644 src/discord.c create mode 100644 src/discord.h create mode 100644 src/discord_pass.c create mode 100644 src/discord_pass.h diff --git a/libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll b/libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll new file mode 100644 index 0000000000000000000000000000000000000000..4254cfa278673e877a605f31c5b462c8e355d3cb GIT binary patch literal 307720 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 + +// clang-format off + +#if defined(DISCORD_DYNAMIC_LIB) +# if defined(_WIN32) +# if defined(DISCORD_BUILDING_SDK) +# define DISCORD_EXPORT __declspec(dllexport) +# else +# define DISCORD_EXPORT __declspec(dllimport) +# endif +# else +# define DISCORD_EXPORT __attribute__((visibility("default"))) +# endif +#else +# define DISCORD_EXPORT +#endif + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DiscordRichPresence { + const char* state; /* max 128 bytes */ + const char* details; /* max 128 bytes */ + int64_t startTimestamp; + int64_t endTimestamp; + const char* largeImageKey; /* max 32 bytes */ + const char* largeImageText; /* max 128 bytes */ + const char* smallImageKey; /* max 32 bytes */ + const char* smallImageText; /* max 128 bytes */ + const char* partyId; /* max 128 bytes */ + int partySize; + int partyMax; + const char* matchSecret; /* max 128 bytes */ + const char* joinSecret; /* max 128 bytes */ + const char* spectateSecret; /* max 128 bytes */ + int8_t instance; +} DiscordRichPresence; + +typedef struct DiscordUser { + const char* userId; + const char* username; + const char* discriminator; + const char* avatar; +} DiscordUser; + +typedef struct DiscordEventHandlers { + void (*ready)(const DiscordUser* request); + void (*disconnected)(int errorCode, const char* message); + void (*errored)(int errorCode, const char* message); + void (*joinGame)(const char* joinSecret); + void (*spectateGame)(const char* spectateSecret); + void (*joinRequest)(const DiscordUser* request); +} DiscordEventHandlers; + +#define DISCORD_REPLY_NO 0 +#define DISCORD_REPLY_YES 1 +#define DISCORD_REPLY_IGNORE 2 + +DISCORD_EXPORT void Discord_Initialize(const char* applicationId, + DiscordEventHandlers* handlers, + int autoRegister, + const char* optionalSteamId); +DISCORD_EXPORT void Discord_Shutdown(void); + +/* checks for incoming messages, dispatches callbacks */ +DISCORD_EXPORT void Discord_RunCallbacks(void); + +/* If you disable the lib starting its own io thread, you'll need to call this from your own */ +#ifdef DISCORD_DISABLE_IO_THREAD +DISCORD_EXPORT void Discord_UpdateConnection(void); +#endif + +DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); +DISCORD_EXPORT void Discord_ClearPresence(void); + +DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); + +DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib b/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib new file mode 100644 index 0000000000000000000000000000000000000000..367920b36768f4de42c5b1bb697133d6f1e495d8 GIT binary patch literal 3656 zcmcIn%}*0S6n{%8tu1IH>dmB>MlePwg*K3=DFTs(@{zU>!hv<^uCTFR*_N1iG2!fi z8y5~<{Ub~`bHYRqUQOiB0N-?XW_REAgGQL-b!OhYncuuOzc;fTd8ru1{nw$1sJl!i z6H~MC$@t8STOYT&smW=p^)3Ve4*~p}0D&Wb;2A*Z#FW7cfTq;2Mj1LcB^1MPl<=h~ zLtCgv2}dkB#QIQ{Gb`(Bxq`f~n4iyO)(dMnS=H;ss-eCxYDJLc)s1Y{YXqMiR;Ep1 zX=8O+UYIM)fxKWYA!jOjLsv@rCk2)EAfA2ihJ#|pKz_1rRdP?=XsrCp`? zp-!#cV|>O&O;sA&x}nvzO3||$I?89wmo&v_H;D}LIXSIft5#Icwz!(xnV3sTMJ;KD z#|t{jXXUg#z249a@A6zvE%OacDZf?99;f+Iwn5JfP1p<_M2c?u0geIyXF-4yDqw6Ib$sOW<`^0jOaL?OO$L0aS6LBHyxuWA9vt#7e(@3=% z75Ucjj#2GfYFsUqCc53G$@r|eO|y~@zW({SJuET)HH$Q^YP$z}vAo#@Sj5H`-m&U5 zMBa?+c#$Zv1Rr{UjUOm~Erk#3G{jFWyOgB{ymrBdc-)TZ(-1xF!}<}jpOt&WsZkh? zU^)R?la;$D$2<2|^rBI%SKl|HW4UM1Y-V-2IZA$5Lb8c$0l*|Q)ZW`hb4Hc4B<^!PG~!d3@(CHf3& z8+*6&IE9GoU73?e+P7R*>T5FRJ8sY1Hne1p>TrfN9n6{HLyS4!c*m+4bJ)7K!iejf z@nZyU`{?%^xtWwF=YAtVr^t24xug}OCYf2zIg2Cbrn#Qc_ZRP2^^HgPI#C@#@SDBO z_+=Jl--awq8IF{YrT-!=b4`Vq`0 zh{?Vb1g3c42m-zAx+12gEZP#p=ZNTQg=jyW6uS2ax4~u<>UU$C-username, user->discriminator, user->userId); +} + +static inline void DRPC_HandleDisconnect(int err, const char *msg) +{ + CONS_Printf("Discord: disconnected (%d: %s)\n", err, msg); +} + +static inline void DRPC_HandleError(int err, const char *msg) +{ + CONS_Printf("Discord: error (%d, %s)\n", err, msg); +} + +static inline void DRPC_HandleJoin(const char *secret) +{ + CONS_Printf("Discord: connecting to %s\n", secret); + COM_BufAddText(va("connect \"%s\"\n", secret)); +} + +// +// DRPC_Init: starting up the handles, call Discord_initalize +// +void DRPC_Init(void) +{ + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + + if (!discordappid) + return; + + handlers.ready = DRPC_HandleReady; + handlers.disconnected = DRPC_HandleDisconnect; + handlers.errored = DRPC_HandleError; + handlers.joinGame = DRPC_HandleJoin; + + Discord_Initialize(discordappid, &handlers, 1, NULL); + I_AddExitFunc(Discord_Shutdown); + DRPC_UpdatePresence(); +} + +// +// DRPC_UpdatePresence: Called whenever anything changes about server info +// +void DRPC_UpdatePresence(void) +{ + char mapimg[8]; + char mapname[48]; + char charimg[21]; + char charname[28]; + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + + if (discordappid) + { + // Server info + if (netgame) + { + const char *address; + + switch (ms_RoomId) + { + case -1: discordPresence.state = "Private"; break; // Private server + case 33: discordPresence.state = "Standard"; break; + case 28: discordPresence.state = "Casual"; break; + default: discordPresence.state = "???"; break; // How? + } + + discordPresence.partyId = "1"; // We don't really have "party" IDs, so to make invites expire we just let it reset to 0 outside of servers + + // Grab the host's IP for joining. + if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode, true)) != NULL) + { + discordPresence.joinSecret = address; + CONS_Printf("%s\n", address); + } + + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = cv_maxplayers.value; // Max players + } + else + discordPresence.state = "Offline"; + + // Gametype info + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) + { + if (modeattacking) + discordPresence.details = "Record Attack"; + else + discordPresence.details = gametype_cons_t[gametype].strvalue; + } + + // Map info + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + { + snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); + strlwr(mapimg); + + discordPresence.largeImageKey = mapimg; // Map image + + if (mapheaderinfo[gamemap-1]->lvlttl[0] != '\0') + snprintf(mapname, 48, "Map: %s%s%s", + mapheaderinfo[gamemap-1]->lvlttl, + (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart + ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE"), + (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->actnum) : ""); + else + snprintf(mapname, 48, "???"); + + discordPresence.largeImageText = mapname; // Map name + + //if (cv_timelimit.value) + //discordPresence.endTimestamp = levelstarttime + (cv_timelimit.value*60); // Time limit if applicable + } + else if (gamestate == GS_VOTING) + { + discordPresence.largeImageText = "Voting"; + } + + // Player info + if (playeringame[consoleplayer]) + { + //discordPresence.startTimestamp = levelstarttime; // Time in level + if (!players[consoleplayer].spectator) + { + snprintf(charimg, 21, "char%s", skins[players[consoleplayer].skin].name); + discordPresence.smallImageKey = charimg; // Character image + + snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname); + discordPresence.smallImageText = charname; // Character name + } + } + } + + Discord_UpdatePresence(&discordPresence); +} + +#endif diff --git a/src/discord.h b/src/discord.h new file mode 100644 index 000000000..f9873d25e --- /dev/null +++ b/src/discord.h @@ -0,0 +1,20 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2012-2018 by Sally "TehRealSalt" Cochenour. +// Copyright (C) 2012-2016 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file discord.h +/// \brief Discord Rich Presence handling + +#ifdef HAVE_DISCORDRPC + +#include "discord_rpc.h" + +void DRPC_Init(void); +void DRPC_UpdatePresence(void); + +#endif diff --git a/src/discord_pass.c b/src/discord_pass.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/discord_pass.h b/src/discord_pass.h new file mode 100644 index 000000000..4adeda90b --- /dev/null +++ b/src/discord_pass.h @@ -0,0 +1,3 @@ +// This file is .gitignore'd for a reason! This is very sensitive info! +// Do not push any change that makes this a valid app ID! +const char* discordappid = "503531144395096085"; diff --git a/src/g_game.c b/src/g_game.c index 617712f7d..9b495a803 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -47,6 +47,10 @@ #include "md5.h" // demo checksums #include "k_kart.h" // SRB2kart +#ifdef HAVE_DISCORDRPC +#include "discord.h" +#endif + gameaction_t gameaction; gamestate_t gamestate = GS_NULL; UINT8 ultimatemode = false; @@ -2252,6 +2256,10 @@ void G_Ticker(boolean run) if (spectatedelay4) spectatedelay4--; } + +#ifdef HAVE_DISCORDRPC + Discord_RunCallbacks(); +#endif } // @@ -6347,6 +6355,9 @@ boolean G_CheckDemoStatus(void) void G_SetGamestate(gamestate_t newstate) { gamestate = newstate; +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#endif } /* These functions handle the exitgame flag. Before, when the user diff --git a/src/mserv.c b/src/mserv.c index 98849df4e..5a99fe148 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -97,6 +97,10 @@ #include "i_addrinfo.h" +#ifdef HAVE_DISCORDRPC +#include "discord.h" +#endif + // ================================ DEFINITIONS =============================== #define PACKET_SIZE 1024 @@ -845,6 +849,9 @@ void RegisterServer(void) MSOpenUDPSocket(); // keep the TCP connection open until AddToMasterServer() is completed; +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#endif } static inline void SendPingToMasterServer(void) @@ -896,7 +903,7 @@ void SendAskInfoViaMS(INT32 node, tic_t asktime) // This must be called after calling MSOpenUDPSocket, due to the // static buffer. - address = I_GetNodeAddress(node); + address = I_GetNodeAddress(node, false); // no address? if (!address) @@ -946,6 +953,10 @@ void UnregisterServer(void) CloseConnection(); MSCloseUDPSocket(); MSLastPing = 0; + +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#endif } void MasterClient_Ticker(void) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 871de3922..eb735382e 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2850,11 +2850,11 @@ void I_Error(const char *error, ...) I_ShutdownGraphics(); if (errorcount == 6) I_ShutdownInput(); - if (errorcount == 7) - I_ShutdownSystem(); if (errorcount == 8) - SDL_Quit(); + I_ShutdownSystem(); if (errorcount == 9) + SDL_Quit(); + if (errorcount == 10) { M_SaveConfig(NULL); G_SaveGameData(false); diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index da66e996a..e3dfa3d6e 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -25,6 +25,8 @@ endif HAVE_MINIUPNPC=1 + HAVE_DISCORDRPC=1 + OPTS=-DSTDC_HEADERS ifndef GCC44 @@ -133,3 +135,9 @@ else LDFLAGS+=-L../libs/miniupnpc/mingw32 endif #MINGW64 endif + +ifdef HAVE_DISCORDRPC + CPPFLAGS+=-I../libs/discord-rpc/win32-dynamic/include + LDFLAGS+=-L../libs/discord-rpc/win32-dynamic/lib + LIBS+=-ldiscord-rpc +endif From 27d28040a7411861f1dbee2d8106c2ff10d4a151 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 22 Oct 2018 14:57:02 -0400 Subject: [PATCH 022/193] Missed these from an old experiment --- src/discord.c | 2 +- src/mserv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/discord.c b/src/discord.c index 12715cc88..f508cce25 100644 --- a/src/discord.c +++ b/src/discord.c @@ -101,7 +101,7 @@ void DRPC_UpdatePresence(void) discordPresence.partyId = "1"; // We don't really have "party" IDs, so to make invites expire we just let it reset to 0 outside of servers // Grab the host's IP for joining. - if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode, true)) != NULL) + if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) { discordPresence.joinSecret = address; CONS_Printf("%s\n", address); diff --git a/src/mserv.c b/src/mserv.c index 5a99fe148..d11141e62 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -903,7 +903,7 @@ void SendAskInfoViaMS(INT32 node, tic_t asktime) // This must be called after calling MSOpenUDPSocket, due to the // static buffer. - address = I_GetNodeAddress(node, false); + address = I_GetNodeAddress(node); // no address? if (!address) From 6efab5bae5b062942e4d71973d053245ea9ebbb8 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 30 Oct 2018 05:44:29 -0400 Subject: [PATCH 023/193] House keeping - Remove the potentially GPL-infringing discord_pass.h file. - Let DISCORD_APPID be a public #define. - Use server_context as party ID. - Add more states ("Watching Demo", "Menu"). - Only show map images on supported maps. Falls back to an image of a dice. - Displays "???" as the map name for Hell maps. - Voting displays an image of the BG planet, depending on the gamemode. - Added a fallback title screen large image. - Added a fallback character image. - General code cleanup & safety checks. - Give CV_NETVAR to cv_maxplayers (I should come up with a better way of sending this information without overwriting user settings, but this'll do for now) --- src/Makefile | 1 - src/d_clisrv.c | 2 +- src/d_netfil.c | 2 +- src/discord.c | 151 ++++++++++++++++++++++++--------------------- src/discord_pass.c | 0 src/discord_pass.h | 3 - 6 files changed, 84 insertions(+), 75 deletions(-) delete mode 100644 src/discord_pass.c delete mode 100644 src/discord_pass.h diff --git a/src/Makefile b/src/Makefile index 693de695f..9c65dd360 100644 --- a/src/Makefile +++ b/src/Makefile @@ -371,7 +371,6 @@ ifdef HAVE_DISCORDRPC LIBS+=-ldiscord-rpc CFLAGS+=-DHAVE_DISCORDRPC OBJS+=$(OBJDIR)/discord.o -OBJS+=$(OBJDIR)/discord_pass.o endif ifndef NO_LUA diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9df160aa6..2f74b2c80 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2944,7 +2944,7 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done #endif static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "10", 0, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; diff --git a/src/d_netfil.c b/src/d_netfil.c index 47d4d276e..7927c4ecf 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -475,7 +475,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) char wadfilename[MAX_WADPATH]; if (cv_noticedownload.value) - CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node, false)); + CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node)); // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; diff --git a/src/discord.c b/src/discord.c index f508cce25..641c8eb09 100644 --- a/src/discord.c +++ b/src/discord.c @@ -23,9 +23,10 @@ #include "mserv.h" // ms_RoomId #include "discord.h" -#include "discord_pass.h" // .gitignore'd file for volitile information; DO NOT push this info #include "doomdef.h" +#define DISCORD_APPID "503531144395096085" // Feel free to provide your own, if you care. + // // DRPC_Handle's // @@ -58,15 +59,12 @@ void DRPC_Init(void) DiscordEventHandlers handlers; memset(&handlers, 0, sizeof(handlers)); - if (!discordappid) - return; - handlers.ready = DRPC_HandleReady; handlers.disconnected = DRPC_HandleDisconnect; handlers.errored = DRPC_HandleError; handlers.joinGame = DRPC_HandleJoin; - Discord_Initialize(discordappid, &handlers, 1, NULL); + Discord_Initialize(DISCORD_APPID, &handlers, 1, NULL); I_AddExitFunc(Discord_Shutdown); DRPC_UpdatePresence(); } @@ -83,85 +81,100 @@ void DRPC_UpdatePresence(void) DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); - if (discordappid) + // Server info + if (netgame) { - // Server info - if (netgame) + const char *address; + + switch (ms_RoomId) { - const char *address; - - switch (ms_RoomId) - { - case -1: discordPresence.state = "Private"; break; // Private server - case 33: discordPresence.state = "Standard"; break; - case 28: discordPresence.state = "Casual"; break; - default: discordPresence.state = "???"; break; // How? - } - - discordPresence.partyId = "1"; // We don't really have "party" IDs, so to make invites expire we just let it reset to 0 outside of servers - - // Grab the host's IP for joining. - if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) - { - discordPresence.joinSecret = address; - CONS_Printf("%s\n", address); - } - - discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players + case -1: discordPresence.state = "Private"; break; // Private server + case 33: discordPresence.state = "Standard"; break; + case 28: discordPresence.state = "Casual"; break; + default: discordPresence.state = "???"; break; // How? } + + discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! + + // Grab the host's IP for joining. + if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) + { + discordPresence.joinSecret = address; + CONS_Printf("%s\n", address); + } + + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = cv_maxplayers.value; // Max players (turned into a netvar for this, FOR NOW!) + } + else if (Playing()) + discordPresence.state = "Offline"; + else if (demoplayback) + discordPresence.state = "Watching Demo"; + else + discordPresence.state = "Menu"; + + // Gametype info + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) + { + if (modeattacking) + discordPresence.details = "Record Attack"; else - discordPresence.state = "Offline"; + discordPresence.details = gametype_cons_t[gametype].strvalue; + } - // Gametype info - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) - { - if (modeattacking) - discordPresence.details = "Record Attack"; - else - discordPresence.details = gametype_cons_t[gametype].strvalue; - } - - // Map info - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info + { + if ((gamemap >= 1 && gamemap <= 55) // supported race maps + || (gamemap >= 136 && gamemap <= 164) // supported battle maps + //|| (gamemap >= 352 && gamemap <= 367) // supported hell maps (none of them) + ) { snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); strlwr(mapimg); - discordPresence.largeImageKey = mapimg; // Map image + } + else // Fallback, since no image looks crappy! + discordPresence.largeImageKey = "miscdice"; - if (mapheaderinfo[gamemap-1]->lvlttl[0] != '\0') - snprintf(mapname, 48, "Map: %s%s%s", - mapheaderinfo[gamemap-1]->lvlttl, - (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart - ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE"), - (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->actnum) : ""); - else - snprintf(mapname, 48, "???"); - + if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) // hell map, hide the name + discordPresence.largeImageText = "Map: ???"; + else + { + snprintf(mapname, 48, "Map: %s%s%s", + mapheaderinfo[gamemap-1]->lvlttl, + (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart + ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone"), + (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->actnum) : ""); discordPresence.largeImageText = mapname; // Map name - - //if (cv_timelimit.value) - //discordPresence.endTimestamp = levelstarttime + (cv_timelimit.value*60); // Time limit if applicable } - else if (gamestate == GS_VOTING) + + // discordPresence.startTimestamp & endTimestamp could be used to show leveltime & timelimit respectively, + // but would need converted to epoch seconds somehow + } + else if (gamestate == GS_VOTING) + { + discordPresence.largeImageKey = (G_BattleGametype() ? "miscredplanet" : "miscblueplanet"); + discordPresence.largeImageText = "Voting"; + } + else + { + discordPresence.largeImageKey = "misctitle"; + discordPresence.largeImageText = "Title Screen"; + } + + // Character info + if (Playing() && playeringame[consoleplayer] && !players[consoleplayer].spectator) + { + if (players[consoleplayer].skin < 5) // supported skins { - discordPresence.largeImageText = "Voting"; + snprintf(charimg, 21, "char%s", skins[players[consoleplayer].skin].name); + discordPresence.smallImageKey = charimg; // Character image } + else + discordPresence.smallImageKey = "charnull"; // Just so that you can still see the name of custom chars - // Player info - if (playeringame[consoleplayer]) - { - //discordPresence.startTimestamp = levelstarttime; // Time in level - if (!players[consoleplayer].spectator) - { - snprintf(charimg, 21, "char%s", skins[players[consoleplayer].skin].name); - discordPresence.smallImageKey = charimg; // Character image - - snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname); - discordPresence.smallImageText = charname; // Character name - } - } + snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname); + discordPresence.smallImageText = charname; // Character name } Discord_UpdatePresence(&discordPresence); diff --git a/src/discord_pass.c b/src/discord_pass.c deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/discord_pass.h b/src/discord_pass.h deleted file mode 100644 index 4adeda90b..000000000 --- a/src/discord_pass.h +++ /dev/null @@ -1,3 +0,0 @@ -// This file is .gitignore'd for a reason! This is very sensitive info! -// Do not push any change that makes this a valid app ID! -const char* discordappid = "503531144395096085"; From 566cc99cab4764ac515250618740a7446430bc80 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 27 Jun 2020 20:58:33 -0400 Subject: [PATCH 024/193] Load one file per tic Prevents game from locking up when loading large file lists --- src/d_clisrv.c | 19 ++++++++++++------- src/d_netfil.c | 4 +++- src/d_netfil.h | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 251f413ab..e0bba6bf8 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1095,6 +1095,7 @@ typedef enum CL_SEARCHING, CL_DOWNLOADFILES, CL_ASKJOIN, + CL_LOADFILES, CL_WAITJOINRESPONSE, #ifdef JOININGAME CL_DOWNLOADSAVEGAME, @@ -1202,7 +1203,10 @@ static inline void CL_DrawConnectionStatus(void) break; #endif case CL_ASKFULLFILELIST: - cltext = M_GetText("This server has a LOT of files!"); + cltext = M_GetText("This server has a LOT of addons!"); + break; + case CL_LOADFILES: + cltext = M_GetText("Loading server addons..."); break; case CL_ASKJOIN: case CL_WAITJOINRESPONSE: @@ -1933,7 +1937,7 @@ static boolean CL_FinishedFileList(void) return false; } else if (i == 1) - cl_mode = CL_ASKJOIN; + cl_mode = CL_LOADFILES; else { // must download something @@ -2143,7 +2147,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic } if (!curl_transfers) - cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now + cl_mode = CL_LOADFILES; // don't break case continue to cljoin request now break; #endif @@ -2159,11 +2163,12 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (waitmore) break; // exit the case - cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now - /* FALLTHRU */ - + cl_mode = CL_LOADFILES; // don't break case continue to cljoin request now + break; + case CL_LOADFILES: + if (!CL_LoadServerFiles()) + break; case CL_ASKJOIN: - CL_LoadServerFiles(); #ifdef JOININGAME // prepare structures to save the file // WARNING: this can be useless in case of server not in GS_LEVEL diff --git a/src/d_netfil.c b/src/d_netfil.c index 821d2a5bc..f65959dce 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -457,7 +457,7 @@ INT32 CL_CheckFiles(void) } // Load it now -void CL_LoadServerFiles(void) +boolean CL_LoadServerFiles(void) { INT32 i; @@ -473,6 +473,7 @@ void CL_LoadServerFiles(void) P_AddWadFile(fileneeded[i].filename); G_SetGameModified(true, false); fileneeded[i].status = FS_OPEN; + return false; } else if (fileneeded[i].status == FS_MD5SUMBAD) I_Error("Wrong version of file %s", fileneeded[i].filename); @@ -498,6 +499,7 @@ void CL_LoadServerFiles(void) fileneeded[i].status, s); } } + return true; } // Number of files to send diff --git a/src/d_netfil.h b/src/d_netfil.h index f37df371f..3bc2d0d34 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -65,7 +65,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi void CL_PrepareDownloadSaveGame(const char *tmpsave); INT32 CL_CheckFiles(void); -void CL_LoadServerFiles(void); +boolean CL_LoadServerFiles(void); void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid); From f8f6dff74822343e485008356d8ae873e0dfc057 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 21 Mar 2020 21:32:15 -0400 Subject: [PATCH 025/193] New "Overall Download Progress" bar Shows progress of all file downloads pre-join --- src/d_clisrv.c | 49 +++++++++++++++++++++++++++++++++++++------------ src/d_netfil.c | 5 +++++ src/d_netfil.h | 2 ++ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e0bba6bf8..0579eb23e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1167,10 +1167,6 @@ static inline void CL_DrawConnectionStatus(void) // Draw background fade V_DrawFadeScreen(0xFF00, 16); - // Draw the bottom box. - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); - if (cl_mode != CL_DOWNLOADFILES #ifdef HAVE_CURL && cl_mode != CL_DOWNLOADHTTPFILES @@ -1182,6 +1178,10 @@ static inline void CL_DrawConnectionStatus(void) // 15 pal entries total. const char *cltext; + //Draw bottom box + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); + for (i = 0; i < 16; ++i) V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15)); @@ -1221,13 +1221,18 @@ static inline void CL_DrawConnectionStatus(void) cltext = M_GetText("Connecting to server..."); break; } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, cltext); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-32, V_YELLOWMAP, cltext); } else { if (lastfilenum != -1) { + // Draw the bottom box. + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-58-8, 32, 1); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-14, V_YELLOWMAP, "Press ESC to abort"); + INT32 dldlength; + INT32 totalfileslength; static char tempname[28]; fileneeded_t *file = &fileneeded[lastfilenum]; char *filename = file->filename; @@ -1236,8 +1241,10 @@ static inline void CL_DrawConnectionStatus(void) dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); if (dldlength > 256) dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 160); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, 256, 8, 175); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, dldlength, 8, 160); + + memset(tempname, 0, sizeof(tempname)); // offset filename to just the name only part @@ -1255,15 +1262,24 @@ static inline void CL_DrawConnectionStatus(void) strncpy(tempname, filename, sizeof(tempname)-1); } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP, va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, va("%3.1fK/s ", ((double)getbps)/1024)); + + // Download progress + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Total File Download Progress"); + totalfileslength = (INT32)((downloadcompletednum/(double)totalfilesrequestednum) * 256); + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %2u/%2u",downloadcompletednum,totalfilesrequestednum)); } else - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-32, V_YELLOWMAP, M_GetText("Waiting to download files...")); } } @@ -1963,7 +1979,11 @@ static boolean CL_FinishedFileList(void) } if (CL_SendRequestFile()) + { cl_mode = CL_DOWNLOADFILES; + downloadcompletednum = 0; + totalfilesrequestednum = 0; + } } #ifdef HAVE_CURL else @@ -2108,10 +2128,15 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic case CL_PREPAREHTTPFILES: if (http_source[0]) { + downloadcompletednum = 0; + totalfilesrequestednum = 0; for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + { curl_transfers++; - + totalfilesrequestednum++; + } + cl_mode = CL_DOWNLOADHTTPFILES; } break; diff --git a/src/d_netfil.c b/src/d_netfil.c index f65959dce..7fd9e294e 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -108,6 +108,8 @@ char downloaddir[512] = "DOWNLOAD"; #ifdef CLIENT_LOADINGSCREEN // for cl loading screen INT32 lastfilenum = -1; +INT32 downloadcompletednum = 0; +INT32 totalfilesrequestednum = 0; #endif #ifdef HAVE_CURL @@ -330,6 +332,7 @@ boolean CL_SendRequestFile(void) // put it in download dir strcatbf(fileneeded[i].filename, downloaddir, "/"); fileneeded[i].status = FS_REQUESTED; + totalfilesrequestednum++; } WRITEUINT8(p, 0xFF); I_GetDiskFreeSpace(&availablefreespace); @@ -860,6 +863,7 @@ void Got_Filetxpak(void) file->status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), filename); + downloadcompletednum++; } } else @@ -1171,6 +1175,7 @@ void CURLGetFile(void) { nameonly(curl_realname); CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname); + downloadcompletednum++; curl_curfile->status = FS_FOUND; fclose(curl_curfile->file); } diff --git a/src/d_netfil.h b/src/d_netfil.h index 3bc2d0d34..b9fdd00af 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -52,6 +52,8 @@ extern char downloaddir[512]; #ifdef CLIENT_LOADINGSCREEN extern INT32 lastfilenum; +extern INT32 downloadcompletednum; +extern INT32 totalfilesrequestednum; #endif #ifdef HAVE_CURL From 31ea4637d85e251a412446d6a9ea2fbf14c5755e Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 21 Mar 2020 21:35:02 -0400 Subject: [PATCH 026/193] Adjustment to new progress text --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0579eb23e..5ef11c1ac 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1270,13 +1270,13 @@ static inline void CL_DrawConnectionStatus(void) va("%3.1fK/s ", ((double)getbps)/1024)); // Download progress - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Total File Download Progress"); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Overall Download Progress"); totalfileslength = (INT32)((downloadcompletednum/(double)totalfilesrequestednum) * 256); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u",downloadcompletednum,totalfilesrequestednum)); + va(" %2u/%2u Files",downloadcompletednum,totalfilesrequestednum)); } else V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-32, V_YELLOWMAP, From 4376ab84dcf03f6d6b104547ade368c93a33d228 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 27 Jun 2020 23:26:38 -0400 Subject: [PATCH 027/193] Add progress bar for file loading --- src/d_clisrv.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5ef11c1ac..b9a3294aa 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1167,7 +1167,7 @@ static inline void CL_DrawConnectionStatus(void) // Draw background fade V_DrawFadeScreen(0xFF00, 16); - if (cl_mode != CL_DOWNLOADFILES + if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES #ifdef HAVE_CURL && cl_mode != CL_DOWNLOADHTTPFILES #endif @@ -1221,11 +1221,32 @@ static inline void CL_DrawConnectionStatus(void) cltext = M_GetText("Connecting to server..."); break; } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-32, V_YELLOWMAP, cltext); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, cltext); } else { - if (lastfilenum != -1) + if (cl_mode == CL_LOADFILES) + { + INT32 totalfileslength; + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Press ESC to abort"); + + //ima just count files here + INT32 loadcompletednum = 0; + INT32 i; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_OPEN) + loadcompletednum++; + + // Loading progress + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Loading server addons..."); + totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum+1)) * 256); + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %2u/%2u Files",loadcompletednum,fileneedednum+1)); + } + else if (lastfilenum != -1) { // Draw the bottom box. M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-58-8, 32, 1); From d1a2e4ce895364c3042129214c3ccf07ed20c95a Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 27 Jun 2020 23:40:13 -0400 Subject: [PATCH 028/193] Adjust text position --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b9a3294aa..d529aebf3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1228,7 +1228,7 @@ static inline void CL_DrawConnectionStatus(void) if (cl_mode == CL_LOADFILES) { INT32 totalfileslength; - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Press ESC to abort"); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); //ima just count files here INT32 loadcompletednum = 0; @@ -1238,7 +1238,7 @@ static inline void CL_DrawConnectionStatus(void) loadcompletednum++; // Loading progress - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Loading server addons..."); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons..."); totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum+1)) * 256); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); From 3cb4e3cc18201c8859c53b134302e37d6ec6e589 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 1 Jul 2020 21:40:21 -0400 Subject: [PATCH 029/193] Fix X/0 files display bug for legacy downloader Also made "Waiting to Download" consistent with other states --- src/d_clisrv.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d529aebf3..afe518e01 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1300,8 +1300,21 @@ static inline void CL_DrawConnectionStatus(void) va(" %2u/%2u Files",downloadcompletednum,totalfilesrequestednum)); } else - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-32, V_YELLOWMAP, + { + INT32 i, animtime = ((ccstime / 4) & 15) + 16; + UINT8 palstart = (cl_mode == CL_SEARCHING) ? 128 : 160; + // 15 pal entries total. + + //Draw bottom box + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); + + for (i = 0; i < 16; ++i) + V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15)); + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, M_GetText("Waiting to download files...")); + } } } #endif @@ -1999,11 +2012,11 @@ static boolean CL_FinishedFileList(void) return false; } + downloadcompletednum = 0; + totalfilesrequestednum = 0; if (CL_SendRequestFile()) { cl_mode = CL_DOWNLOADFILES; - downloadcompletednum = 0; - totalfilesrequestednum = 0; } } #ifdef HAVE_CURL @@ -2147,10 +2160,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: + downloadcompletednum = 0; + totalfilesrequestednum = 0; if (http_source[0]) { - downloadcompletednum = 0; - totalfilesrequestednum = 0; for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) { From 8cfc3520e5ca9479a3df8309513111dc5cded6c6 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 1 Jul 2020 23:48:14 -0400 Subject: [PATCH 030/193] Connection screen no longer freezes up while searching for addons on disk Game will run one tic after every file status is updated --- src/d_clisrv.c | 6 +++++- src/d_netcmd.c | 6 +++--- src/d_netfil.c | 37 ++++++++++++++++++++++++------------- src/d_netfil.h | 1 + 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index afe518e01..7b4193169 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1958,7 +1958,11 @@ static boolean CL_FinishedFileList(void) INT32 i; CONS_Printf(M_GetText("Checking files...\n")); i = CL_CheckFiles(); - if (i == 3) // too many files + if (i == 4) // still checking ... + { + return true; + } + else if (i == 3) // too many files { D_QuitNetGame(); CL_Reset(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7d94d2ed2..8d778624f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4088,7 +4088,7 @@ static void Command_RunSOC(void) static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum) { char filename[256]; - filestatus_t ncs = FS_NOTFOUND; + filestatus_t ncs = FS_NOTCHECKED; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -4260,7 +4260,7 @@ static void Command_Delfile(void) static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) { char filename[241]; - filestatus_t ncs = FS_NOTFOUND; + filestatus_t ncs = FS_NOTCHECKED; UINT8 md5sum[16]; boolean kick = false; boolean toomany = false; @@ -4355,7 +4355,7 @@ static void Got_Delfilecmd(UINT8 **cp, INT32 playernum) static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) { char filename[241]; - filestatus_t ncs = FS_NOTFOUND; + filestatus_t ncs = FS_NOTCHECKED; UINT8 md5sum[16]; READSTRINGN(*cp, filename, 240); diff --git a/src/d_netfil.c b/src/d_netfil.c index 7fd9e294e..c4c2edd63 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -209,7 +209,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi p = (UINT8 *)fileneededstr; for (i = firstfile; i < fileneedednum; i++) { - fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet + fileneeded[i].status = FS_NOTCHECKED; // We haven't even started looking for the file yet filestatus = READUINT8(p); // The first byte is the file status fileneeded[i].willsend = (UINT8)(filestatus >> 4); fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size @@ -373,6 +373,8 @@ boolean Got_RequestFilePak(INT32 node) * \return 0 if some files are missing * 1 if all files exist * 2 if some already loaded files are not requested or are in a different order + * 3 too many files, over WADLIMIT + * 4 still checking, continuing next tic * */ INT32 CL_CheckFiles(void) @@ -381,7 +383,8 @@ INT32 CL_CheckFiles(void) char wadfilename[MAX_WADPATH]; INT32 ret = 1; size_t packetsize = 0; - size_t filestoget = 0; + size_t filestoload = 0; + boolean downloadrequired = false; // if (M_CheckParm("-nofiles")) // return 1; @@ -427,6 +430,15 @@ INT32 CL_CheckFiles(void) for (i = 0; i < fileneedednum; i++) { + if (fileneeded[i].status != FS_OPEN) //little messy, but this will count right by the time we get through the last file + filestoload++; + + if (fileneeded[i].status == FS_NOTFOUND) + downloadrequired = true; + + if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics + continue; + CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); // Check in already loaded files @@ -438,25 +450,24 @@ INT32 CL_CheckFiles(void) { CONS_Debug(DBG_NETPLAY, "already loaded\n"); fileneeded[i].status = FS_OPEN; - break; + return 4; } } - if (fileneeded[i].status != FS_NOTFOUND) - continue; packetsize += nameonlylength(fileneeded[i].filename) + 22; - if (mainwads+filestoget >= MAX_WADFILES) - return 3; - - filestoget++; - fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true); CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status); - if (fileneeded[i].status != FS_FOUND) - ret = 0; + return 4; } - return ret; + + //now making it here means we've checked the entire list and no FS_NOTCHECKED files remain + if (mainwads+filestoload >= MAX_WADFILES) + return 3; //ensure we wouldn't go over the wad limit + else if (downloadrequired) + return 0; //some stuff is FS_NOTFOUND, needs download + else + return 1; //everything is FS_OPEN or FS_FOUND, proceed to loading } // Load it now diff --git a/src/d_netfil.h b/src/d_netfil.h index b9fdd00af..58ee8b7ed 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -25,6 +25,7 @@ typedef enum typedef enum { + FS_NOTCHECKED, FS_NOTFOUND, FS_FOUND, FS_REQUESTED, From a4c004fee37c19e7b221f5af073d85777a9b797a Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 1 Jul 2020 23:52:43 -0400 Subject: [PATCH 031/193] Changed "this server has a lot of addons!" to "Checking server addon list ..." to be more informative --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7b4193169..15b155bfc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1203,7 +1203,7 @@ static inline void CL_DrawConnectionStatus(void) break; #endif case CL_ASKFULLFILELIST: - cltext = M_GetText("This server has a LOT of addons!"); + cltext = M_GetText("Checking server addon list ..."); break; case CL_LOADFILES: cltext = M_GetText("Loading server addons..."); From 8df8d1d02859d795c946eebdf674df8d5dac51cb Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 2 Jul 2020 17:47:10 -0400 Subject: [PATCH 032/193] Correct total files to load off by one --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 15b155bfc..a73df38ac 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1239,12 +1239,12 @@ static inline void CL_DrawConnectionStatus(void) // Loading progress V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons..."); - totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum+1)) * 256); + totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u Files",loadcompletednum,fileneedednum+1)); + va(" %2u/%2u Files",loadcompletednum,fileneedednum)); } else if (lastfilenum != -1) { From f2990180f6339ca794541f4881707abb71c94c46 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 2 Jul 2020 20:57:33 -0400 Subject: [PATCH 033/193] Fix file number accounting Should ensure appropriate accounting and prevent loading of server files if it would put us over MAX_WADFILES --- src/d_netfil.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index c4c2edd63..25825d524 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -143,7 +143,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile) char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus; - for (i = mainwads; i < numwadfiles; i++) + for (i = mainwads+1; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad { // If it has only music/sound lumps, don't put it in the list if (!wadfiles[i]->important) @@ -430,11 +430,11 @@ INT32 CL_CheckFiles(void) for (i = 0; i < fileneedednum; i++) { - if (fileneeded[i].status != FS_OPEN) //little messy, but this will count right by the time we get through the last file - filestoload++; - if (fileneeded[i].status == FS_NOTFOUND) downloadrequired = true; + + if (fileneeded[i].status == FS_FOUND || fileneeded[i].status == FS_NOTFOUND) + filestoload++; if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics continue; @@ -462,8 +462,8 @@ INT32 CL_CheckFiles(void) } //now making it here means we've checked the entire list and no FS_NOTCHECKED files remain - if (mainwads+filestoload >= MAX_WADFILES) - return 3; //ensure we wouldn't go over the wad limit + if (numwadfiles+filestoload > MAX_WADFILES) + return 3; else if (downloadrequired) return 0; //some stuff is FS_NOTFOUND, needs download else From 8790661b20dc940df4cf2169967bdb52161fb67c Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 2 Jul 2020 21:48:45 -0400 Subject: [PATCH 034/193] Remove Checking Files spam --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a73df38ac..13a0ab369 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1956,7 +1956,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) static boolean CL_FinishedFileList(void) { INT32 i; - CONS_Printf(M_GetText("Checking files...\n")); + //CONS_Printf(M_GetText("Checking files...\n")); i = CL_CheckFiles(); if (i == 4) // still checking ... { From 44b9f8e25af8a2319dbc53c30f5eba9d08f01ac1 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 4 Jul 2020 01:12:38 -0400 Subject: [PATCH 035/193] Let's just ALWAYS go into ask file list mode Easier to maintain and understand a single code path Rather than switching behavior based on amount of addons --- src/d_clisrv.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 13a0ab369..cc934173a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2085,17 +2085,9 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) if (serverlist[i].info.httpsource[0]) CONS_Printf("We received a http url from the server, however it will not be used as this build lacks curl support (%s)\n", serverlist[i].info.httpsource); #endif - - D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded, 0); - if (serverlist[i].info.kartvars & SV_LOTSOFADDONS) - { - cl_mode = CL_ASKFULLFILELIST; - cl_lastcheckedfilecount = 0; - return true; - } - - if (!CL_FinishedFileList()) - return false; + cl_mode = CL_ASKFULLFILELIST; + cl_lastcheckedfilecount = 0; + return true; } else cl_mode = CL_ASKJOIN; // files need not be checked for the server. From 71742996355673b69a7ec826fe474f8e658bafe3 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 4 Jul 2020 22:25:02 -0400 Subject: [PATCH 036/193] Remove now unused ret var --- src/d_netfil.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 25825d524..3b9d9081b 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -381,7 +381,6 @@ INT32 CL_CheckFiles(void) { INT32 i, j; char wadfilename[MAX_WADPATH]; - INT32 ret = 1; size_t packetsize = 0; size_t filestoload = 0; boolean downloadrequired = false; From 6ff69726a6b038ae451b40adcd34d37703bcd20f Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sun, 9 Aug 2020 00:47:55 -0400 Subject: [PATCH 037/193] Redo logic for lots of files vs not Now new CL state for checking files so it can spread over tics whether we ask for the extended list or not --- src/d_clisrv.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index cc934173a..06684f832 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1093,6 +1093,7 @@ static INT16 Consistancy(void); typedef enum { CL_SEARCHING, + CL_CHECKFILES, CL_DOWNLOADFILES, CL_ASKJOIN, CL_LOADFILES, @@ -2085,9 +2086,15 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) if (serverlist[i].info.httpsource[0]) CONS_Printf("We received a http url from the server, however it will not be used as this build lacks curl support (%s)\n", serverlist[i].info.httpsource); #endif - cl_mode = CL_ASKFULLFILELIST; - cl_lastcheckedfilecount = 0; - return true; + D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded, 0); + if (serverlist[i].info.kartvars & SV_LOTSOFADDONS) + { + cl_mode = CL_ASKFULLFILELIST; + cl_lastcheckedfilecount = 0; + return true; + } + + cl_mode = CL_CHECKFILES; } else cl_mode = CL_ASKJOIN; // files need not be checked for the server. @@ -2140,10 +2147,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic case CL_ASKFULLFILELIST: if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved - { - if (!CL_FinishedFileList()) - return false; - } + cl_mode = CL_CHECKFILES; else if (fileneedednum != cl_lastcheckedfilecount || *asksent + NEWTICRATE < I_GetTime()) { if (CL_AskFileList(fileneedednum)) @@ -2153,6 +2157,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic } } break; + case CL_CHECKFILES: + if (!CL_FinishedFileList()) + return false; + break; #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: From 6af76ee9484770d7b7fc5ada0b62cb7143b67a7a Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sun, 9 Aug 2020 00:48:53 -0400 Subject: [PATCH 038/193] Now that the server doesn't erroneously send the last mainwad As the first part of fileneeded, we must load file 0 --- src/d_netfil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 3b9d9081b..6bdeab4b8 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -477,7 +477,7 @@ boolean CL_LoadServerFiles(void) // if (M_CheckParm("-nofiles")) // return; - for (i = 1; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) { if (fileneeded[i].status == FS_OPEN) continue; // Already loaded From b426ed81dde695458bbc24fe857eed073f2e2014 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 08:25:12 -0700 Subject: [PATCH 039/193] dumbass doesn't test changes before pushing directly to next (cherry picked from commit be14b8a564a89a5afb84ac19f1586f3db7f68367) --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51e477818..cbb0fa203 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,7 +100,6 @@ set(SRB2_CORE_HEADERS m_swap.h md5.h mserv.h - http-mserv.h p5prof.h s_sound.h screen.h From c91d00440958ecd94f2da43041decaa9192596bc Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 15 Aug 2020 01:03:45 -0400 Subject: [PATCH 040/193] Improve total downloads bar Now has total file size AND number of files --- src/d_clisrv.c | 26 +++++++++++++++++++++++--- src/d_netfil.c | 5 +++++ src/d_netfil.h | 2 ++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 06684f832..186848fcc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1255,6 +1255,7 @@ static inline void CL_DrawConnectionStatus(void) INT32 dldlength; INT32 totalfileslength; + UINT32 totaldldsize; static char tempname[28]; fileneeded_t *file = &fileneeded[lastfilenum]; char *filename = file->filename; @@ -1292,13 +1293,27 @@ static inline void CL_DrawConnectionStatus(void) va("%3.1fK/s ", ((double)getbps)/1024)); // Download progress + + if (fileneeded[lastfilenum].currentsize != fileneeded[lastfilenum].totalsize) + totaldldsize = downloadcompletedsize+fileneeded[lastfilenum].currentsize; //Add in single file progress download if applicable + else + totaldldsize = downloadcompletedsize; + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Overall Download Progress"); - totalfileslength = (INT32)((downloadcompletednum/(double)totalfilesrequestednum) * 256); + totalfileslength = (INT32)((totaldldsize/(double)totalfilesrequestedsize) * 256); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u Files",downloadcompletednum,totalfilesrequestednum)); + + if (totalfilesrequestedsize>>20 >= 100) //display in MB if over 100MB + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %4uM/%4uM",totaldldsize>>20,totalfilesrequestedsize>>20)); + else + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",totaldldsize>>10,totalfilesrequestedsize>>10)); + + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va("%2u/%2u Files ",downloadcompletednum,totalfilesrequestednum)); } else { @@ -2018,7 +2033,9 @@ static boolean CL_FinishedFileList(void) } downloadcompletednum = 0; + downloadcompletedsize = 0; totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; if (CL_SendRequestFile()) { cl_mode = CL_DOWNLOADFILES; @@ -2165,7 +2182,9 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: downloadcompletednum = 0; + downloadcompletedsize = 0; totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; if (http_source[0]) { for (i = 0; i < fileneedednum; i++) @@ -2173,6 +2192,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic { curl_transfers++; totalfilesrequestednum++; + totalfilesrequestedsize += fileneeded[i].totalsize; } cl_mode = CL_DOWNLOADHTTPFILES; diff --git a/src/d_netfil.c b/src/d_netfil.c index 6bdeab4b8..0a2b15797 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -109,7 +109,9 @@ char downloaddir[512] = "DOWNLOAD"; // for cl loading screen INT32 lastfilenum = -1; INT32 downloadcompletednum = 0; +UINT32 downloadcompletedsize = 0; INT32 totalfilesrequestednum = 0; +UINT32 totalfilesrequestedsize = 0; #endif #ifdef HAVE_CURL @@ -333,6 +335,7 @@ boolean CL_SendRequestFile(void) strcatbf(fileneeded[i].filename, downloaddir, "/"); fileneeded[i].status = FS_REQUESTED; totalfilesrequestednum++; + totalfilesrequestedsize += fileneeded[i].totalsize; } WRITEUINT8(p, 0xFF); I_GetDiskFreeSpace(&availablefreespace); @@ -874,6 +877,7 @@ void Got_Filetxpak(void) CONS_Printf(M_GetText("Downloading %s...(done)\n"), filename); downloadcompletednum++; + downloadcompletedsize += file->totalsize; } } else @@ -1186,6 +1190,7 @@ void CURLGetFile(void) nameonly(curl_realname); CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname); downloadcompletednum++; + downloadcompletedsize += curl_curfile->totalsize; curl_curfile->status = FS_FOUND; fclose(curl_curfile->file); } diff --git a/src/d_netfil.h b/src/d_netfil.h index 58ee8b7ed..905364e30 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -54,7 +54,9 @@ extern char downloaddir[512]; #ifdef CLIENT_LOADINGSCREEN extern INT32 lastfilenum; extern INT32 downloadcompletednum; +extern UINT32 downloadcompletedsize; extern INT32 totalfilesrequestednum; +extern UINT32 totalfilesrequestedsize; #endif #ifdef HAVE_CURL From 4548662536a1f68988144a15f6fed891da40bac7 Mon Sep 17 00:00:00 2001 From: Kimberly Wilber Date: Sat, 15 Aug 2020 12:09:41 -0400 Subject: [PATCH 041/193] [OSX] Fix "SIGILL: illegal instruction" on macOS Catalina on OSX, strncpy may copy to overlapping (protected) memory. this sometimes happens when loading WAD files. This patch eliminates these problems for me --- src/w_wad.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index 7fd7ac125..77d0d6d2e 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -150,7 +150,10 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors) { FILE *handle; - strncpy(filenamebuf, *filename, MAX_WADPATH); + if (filenamebuf != *filename) { + // avoid overlap + strncpy(filenamebuf, *filename, MAX_WADPATH); + } filenamebuf[MAX_WADPATH - 1] = '\0'; *filename = filenamebuf; From d31c33e03557c3507b1d07ee01b34ded05fb4434 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 16 Aug 2020 21:18:33 -0400 Subject: [PATCH 042/193] No previous prototype for K_DropKitchenSink --- src/k_kart.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.h b/src/k_kart.h index e6241f8ae..796065404 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -56,6 +56,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player); void K_KartUpdatePosition(player_t *player); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); +void K_DropKitchenSink(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); From 7a9e781beacf98b5cb832eb3dd17fc5f98760d89 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 16 Aug 2020 21:31:03 -0400 Subject: [PATCH 043/193] Little fixes --- debian-template/source/options | 3 --- src/discord.c | 37 ++++++++++++++++++---------------- src/k_kart.h | 1 + 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/debian-template/source/options b/debian-template/source/options index 7829e2976..9532ff202 100644 --- a/debian-template/source/options +++ b/debian-template/source/options @@ -2,10 +2,7 @@ tar-ignore = "assets/*.srb" tar-ignore = "assets/*.pk3" tar-ignore = "assets/*.dta" tar-ignore = "assets/*.wad" -<<<<<<< HEAD:debian-template/source/options tar-ignore = "assets/*.kart" -======= ->>>>>>> e251f9c230beda984cdcdea7e903d765f1c68f6f:debian-template/source/options tar-ignore = "assets/debian/${PACKAGE_NAME}-data/*" tar-ignore = "assets/debian/tmp/*" tar-ignore = "*.obj" diff --git a/src/discord.c b/src/discord.c index 641c8eb09..ba422a70e 100644 --- a/src/discord.c +++ b/src/discord.c @@ -1,7 +1,7 @@ -// SONIC ROBO BLAST 2 +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- -// Copyright (C) 2012-2018 by Sally "TehRealSalt" Cochenour. -// Copyright (C) 2012-2016 by Sonic Team Junior. +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour. +// Copyright (C) 2018-2020 by Kart Krew. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,7 +25,7 @@ #include "discord.h" #include "doomdef.h" -#define DISCORD_APPID "503531144395096085" // Feel free to provide your own, if you care. +#define DISCORD_APPID "503531144395096085" // Feel free to use your own, if you care. // // DRPC_Handle's @@ -42,7 +42,7 @@ static inline void DRPC_HandleDisconnect(int err, const char *msg) static inline void DRPC_HandleError(int err, const char *msg) { - CONS_Printf("Discord: error (%d, %s)\n", err, msg); + CONS_Alert(CONS_WARNING, "Discord: error (%d, %s)\n", err, msg); } static inline void DRPC_HandleJoin(const char *secret) @@ -91,25 +91,28 @@ void DRPC_UpdatePresence(void) case -1: discordPresence.state = "Private"; break; // Private server case 33: discordPresence.state = "Standard"; break; case 28: discordPresence.state = "Casual"; break; - default: discordPresence.state = "???"; break; // How? + default: discordPresence.state = "???"; break; // HOW } - discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! - - // Grab the host's IP for joining. - if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) + if (ms_RoomId != -1) { - discordPresence.joinSecret = address; - CONS_Printf("%s\n", address); - } + discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! - discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players (turned into a netvar for this, FOR NOW!) + // Grab the host's IP for joining. + if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) + { + discordPresence.joinSecret = address; + CONS_Printf("%s\n", address); + } + + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = cv_maxplayers.value; // Max players (turned into a netvar for this, FOR NOW!) + } } else if (Playing()) discordPresence.state = "Offline"; - else if (demoplayback) - discordPresence.state = "Watching Demo"; + else if (demo.playback) + discordPresence.state = "Watching Replay"; else discordPresence.state = "Menu"; diff --git a/src/k_kart.h b/src/k_kart.h index e6241f8ae..796065404 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -56,6 +56,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player); void K_KartUpdatePosition(player_t *player); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); +void K_DropKitchenSink(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); From c70f706c2a440f20080054bdb975ef7c11d97524 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 16 Aug 2020 22:13:21 -0400 Subject: [PATCH 044/193] Allow lowercase level name / zone title Might have some other side-effects, we'll need to see --- src/dehacked.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 96d82fff4..5aa1f71a5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -968,6 +968,16 @@ static void readlevelheader(MYFILE *f, INT32 num) #endif continue; } + else if (fastcmp(word, "LEVELNAME")) + { + deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, + sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); + } + else if (fastcmp(word, "ZONETITLE")) + { + deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, + sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); + } // Now go to uppercase strupr(word2); @@ -987,16 +997,6 @@ static void readlevelheader(MYFILE *f, INT32 num) } // Strings that can be truncated - else if (fastcmp(word, "LEVELNAME")) - { - deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, - sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); - } - else if (fastcmp(word, "ZONETITLE")) - { - deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, - sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); - } else if (fastcmp(word, "SCRIPTNAME")) { deh_strlcpy(mapheaderinfo[num-1]->scriptname, word2, From 5d739bec6dfc1f3874f0cd192ae816401dd21bb7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Aug 2020 20:39:46 -0700 Subject: [PATCH 045/193] Remove HAVE_CURL guards on sending httpsource --- src/d_clisrv.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c4df72f56..204188091 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1309,10 +1309,8 @@ static boolean CL_SendJoin(void) static void SV_SendServerInfo(INT32 node, tic_t servertime) { UINT8 *p; -#ifdef HAVE_CURL size_t mirror_length; const char *httpurl = cv_httpsource.string; -#endif netbuffer->packettype = PT_SERVERINFO; netbuffer->u.serverinfo._255 = 255; @@ -1401,7 +1399,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.actnum = 0; //mapheaderinfo[gamemap-1]->actnum -#ifdef HAVE_CURL mirror_length = strlen(httpurl); if (mirror_length > MAX_MIRROR_LENGTH) mirror_length = MAX_MIRROR_LENGTH; @@ -1411,7 +1408,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) strncpy(netbuffer->u.serverinfo.httpsource, "", mirror_length); netbuffer->u.serverinfo.httpsource[MAX_MIRROR_LENGTH-1] = '\0'; -#endif p = PutFileNeeded(0); From 5f86025f052f588fc909bd7f2bb5e90f07f044f5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 00:29:52 -0400 Subject: [PATCH 046/193] Use curl to retrieve your own IP address properly --- src/dehacked.c | 22 +++++---- src/discord.c | 130 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 124 insertions(+), 28 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 5aa1f71a5..4e613c1ce 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -925,6 +925,18 @@ static void readlevelheader(MYFILE *f, INT32 num) sizeof(mapheaderinfo[num-1]->subttl), va("Level header %d: subtitle", num)); continue; } + else if (fastcmp(word, "LEVELNAME")) + { + deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, + sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); + continue; + } + else if (fastcmp(word, "ZONETITLE")) + { + deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, + sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); + continue; + } // Lua custom options also go above, contents may be case sensitive. if (fastncmp(word, "LUA.", 4)) @@ -968,16 +980,6 @@ static void readlevelheader(MYFILE *f, INT32 num) #endif continue; } - else if (fastcmp(word, "LEVELNAME")) - { - deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, - sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); - } - else if (fastcmp(word, "ZONETITLE")) - { - deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, - sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); - } // Now go to uppercase strupr(word2); diff --git a/src/discord.c b/src/discord.c index ba422a70e..68c1d14d2 100644 --- a/src/discord.c +++ b/src/discord.c @@ -12,6 +12,10 @@ #ifdef HAVE_DISCORDRPC +#ifdef HAVE_CURL +#include +#endif + #include "i_system.h" #include "d_clisrv.h" #include "d_netcmd.h" @@ -69,6 +73,101 @@ void DRPC_Init(void) DRPC_UpdatePresence(); } +#ifdef HAVE_CURL +#define IP_SIZE 16 +static char self_ip[IP_SIZE]; + +struct SelfIPbuffer +{ + CURL *curl; + char *pointer; + size_t length; +}; + +static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata ) +{ + struct SelfIPbuffer *buffer; + size_t newlength; + + buffer = userdata; + + newlength = buffer->length + size*n; + buffer->pointer = realloc(buffer->pointer, newlength+1); + + memcpy(buffer->pointer + buffer->length, s, size*n); + + buffer->pointer[newlength] = '\0'; + buffer->length = newlength; + + return size*n; +} +#endif + +// +// DRPC_GetServerIP: Gets the server's IP address, used to +// +static const char *DRPC_GetServerIP(void) +{ + const char *address; + + // If you're connected + if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) + { + if (strcmp(address, "self")) + return address; // We're not the server, so we could successfully get the IP! No problem here :) + } + +#ifdef HAVE_CURL + // This is a little bit goofy, but + // there's practically no good way to get your own public IP address, + // so we've gotta break out curl for this :V + if (!self_ip[0]) + { + CURL *curl; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + + if (curl) + { + const char *api = "http://ip4only.me/api/"; // API to get your public IP address from + struct SelfIPbuffer buffer; + CURLcode success; + + buffer.length = 0; + buffer.pointer = malloc(buffer.length+1); + buffer.pointer[0] = '\0'; + + curl_easy_setopt(curl, CURLOPT_URL, api); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DRPC_WriteServerIP); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + + success = curl_easy_perform(curl); + + if (success == CURLE_OK) + { + char *tmp; + tmp = strtok(buffer.pointer, ","); + + if (!strcmp(tmp, "IPv4")) // ensure correct type of IP + { + tmp = strtok(NULL, ","); + strncpy(self_ip, tmp, IP_SIZE); // Yay, we have the IP :) + } + } + + free(buffer.pointer); + curl_easy_cleanup(curl); + } + } + + if (self_ip[0]) + return self_ip; + else +#endif + return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites +} + // // DRPC_UpdatePresence: Called whenever anything changes about server info // @@ -78,36 +177,33 @@ void DRPC_UpdatePresence(void) char mapname[48]; char charimg[21]; char charname[28]; + DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); // Server info if (netgame) { - const char *address; + const char *join; switch (ms_RoomId) { case -1: discordPresence.state = "Private"; break; // Private server case 33: discordPresence.state = "Standard"; break; case 28: discordPresence.state = "Casual"; break; - default: discordPresence.state = "???"; break; // HOW + case 38: discordPresence.state = "Custom Gametypes"; break; + //case ??: discordPresence.state = "OLDC"; break; // If I remembered this one's room ID, I would add it :V + default: discordPresence.state = "Unknown Room"; break; // HOW } - if (ms_RoomId != -1) - { - discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! + discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! - // Grab the host's IP for joining. - if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) - { - discordPresence.joinSecret = address; - CONS_Printf("%s\n", address); - } + // Grab the host's IP for joining. + if ((join = DRPC_GetServerIP()) != NULL) + discordPresence.joinSecret = join; - discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players (turned into a netvar for this, FOR NOW!) - } + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: use another variable to hold this, so maxplayers doesn't have to be a netvar!) } else if (Playing()) discordPresence.state = "Offline"; @@ -127,10 +223,8 @@ void DRPC_UpdatePresence(void) if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info { - if ((gamemap >= 1 && gamemap <= 55) // supported race maps - || (gamemap >= 136 && gamemap <= 164) // supported battle maps - //|| (gamemap >= 352 && gamemap <= 367) // supported hell maps (none of them) - ) + if ((gamemap >= 1 && gamemap <= 60) // supported race maps + || (gamemap >= 136 && gamemap <= 164)) // supported battle maps { snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); strlwr(mapimg); From b681b6e9e619c965ce7dff00ff95338247e989e3 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Aug 2020 21:42:58 -0700 Subject: [PATCH 047/193] Do not do master server things when NOCURL --- src/d_clisrv.c | 21 ++++++++++++---- src/doomdef.h | 6 +++++ src/http-mserv.c | 8 ++++++ src/m_menu.c | 27 +++++++++++++------- src/mserv.c | 65 ++++++++++++++++++++++++++++++++---------------- 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c4df72f56..9e399ce76 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1824,7 +1824,7 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) M_SortServerList(); } -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) struct Fetch_servers_ctx { int room; @@ -1869,7 +1869,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx) free(ctx); } -#endif/*HAVE_THREADS*/ +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ void CL_QueryServerList (msg_server_t *server_list) { @@ -1906,9 +1906,8 @@ void CL_QueryServerList (msg_server_t *server_list) void CL_UpdateServerList(boolean internetsearch, INT32 room) { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; -#endif + (void)internetsearch; + (void)room; SL_ClearServerList(0); @@ -1925,9 +1924,12 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) if (netgame) SendAskInfo(BROADCASTADDR); +#ifdef MASTERSERVER if (internetsearch) { #ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; + ctx = malloc(sizeof *ctx); /* This called from M_Refresh so I don't use a mutex */ @@ -1954,6 +1956,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) } #endif } +#endif/*MASTERSERVER*/ } #endif // ifndef NONET @@ -3427,8 +3430,10 @@ void D_QuitNetGame(void) for (i = 0; i < MAXNETNODES; i++) if (nodeingame[i]) HSendPacket(i, true, 0, 0); +#ifdef MASTERSERVER if (serverrunning && ms_RoomId > 0) UnregisterServer(); +#endif } else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) { @@ -3676,8 +3681,10 @@ boolean SV_SpawnServer(void) if (netgame && I_NetOpenSocket) { I_NetOpenSocket(); +#ifdef MASTERSERVER if (ms_RoomId > 0) RegisterServer(); +#endif } // non dedicated server just connect to itself @@ -5395,7 +5402,9 @@ FILESTAMP GetPackets(); FILESTAMP +#ifdef MASTERSERVER MasterClient_Ticker(); +#endif if (client) { @@ -5452,7 +5461,9 @@ FILESTAMP // client send the command after a receive of the server // the server send before because in single player is beter +#ifdef MASTERSERVER MasterClient_Ticker(); // Acking the Master Server +#endif if (client) { diff --git a/src/doomdef.h b/src/doomdef.h index 9ee55d942..f8eade65a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -681,4 +681,10 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Hardware renderer: OpenGL #define GL_SHADERS +#ifdef HAVE_CURL +#define MASTERSERVER +#else +#undef UPDATE_ALERT +#endif + #endif // __DOOMDEF__ diff --git a/src/http-mserv.c b/src/http-mserv.c index 9d05b80e5..13c7b43d3 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -14,7 +14,9 @@ Documentation available here. */ +#ifdef HAVE_CURL #include +#endif #include "doomdef.h" #include "d_clisrv.h" @@ -49,6 +51,8 @@ consvar_t cv_masterserver_token = { NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; +#ifdef MASTERSERVER + static int hms_started; static char *hms_api; @@ -666,10 +670,14 @@ HMS_set_api (char *api) #endif } +#endif/*MASTERSERVER*/ + static void MasterServer_Debug_OnChange (void) { +#ifdef MASTERSERVER /* TODO: change to 'latest-log.txt' for log files revision. */ if (cv_masterserver_debug.value) CONS_Printf("Master server debug messages will appear in log.txt\n"); +#endif } diff --git a/src/m_menu.c b/src/m_menu.c index 850b8d745..3bdb78d31 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3310,7 +3310,7 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef) { I_lock_mutex(&ms_QueryId_mutex); @@ -3392,7 +3392,7 @@ void M_Ticker(void) setmodeneeded = vidm_previousmode + 1; } -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) I_lock_mutex(&ms_ServerList_mutex); { if (ms_ServerList) @@ -8603,8 +8603,9 @@ static boolean M_CheckMODVersion(int id) } else return true; } +#endif/*UPDATE_ALERT*/ -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) static void Check_new_version_thread (int *id) { @@ -8613,7 +8614,9 @@ Check_new_version_thread (int *id) okay = 0; +#ifdef UPDATE_ALERT if (M_CheckMODVersion(*id)) +#endif { I_lock_mutex(&ms_QueryId_mutex); { @@ -8657,8 +8660,7 @@ Check_new_version_thread (int *id) free(id); } -#endif/*HAVE_THREADS*/ -#endif/*UPDATE_ALERT*/ +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ static void M_ConnectMenu(INT32 choice) { @@ -8699,7 +8701,7 @@ UINT32 roomIds[NUM_LIST_ROOMS]; static void M_RoomMenu(INT32 choice) { INT32 i; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) int *id; #endif @@ -8721,9 +8723,14 @@ static void M_RoomMenu(INT32 choice) MP_RoomDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_RoomDef); -#ifdef UPDATE_ALERT +#ifdef MASTERSERVER #ifdef HAVE_THREADS +#ifdef UPDATE_ALERT m_waiting_mode = M_WAITING_VERSION; +#else/*UPDATE_ALERT*/ + m_waiting_mode = M_WAITING_ROOMS; +#endif/*UPDATE_ALERT*/ + MP_RoomMenu[0].text = ""; id = malloc(sizeof *id); @@ -8737,17 +8744,19 @@ static void M_RoomMenu(INT32 choice) I_spawn_thread("check-new-version", (I_thread_fn)Check_new_version_thread, id); #else/*HAVE_THREADS*/ +#ifdef UPDATE_ALERT if (M_CheckMODVersion(0)) +#endif/*UPDATE_ALERT*/ { GetRoomsList(currentMenu->prevMenu == &MP_ServerDef, 0); } #endif/*HAVE_THREADS*/ -#endif/*UPDATE_ALERT*/ +#endif/*MASTERSERVER*/ } static void M_ChooseRoom(INT32 choice) { -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) I_lock_mutex(&ms_QueryId_mutex); { ms_QueryId++; diff --git a/src/mserv.c b/src/mserv.c index f3d414c92..4c044fed2 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -23,6 +23,8 @@ #include "m_menu.h" #include "z_zone.h" +#ifdef MASTERSERVER + static int MSId; static int MSRegisteredId = -1; @@ -43,11 +45,14 @@ static I_cond MSCond; # define Unlock_state() #endif/*HAVE_THREADS*/ -static void Update_parameters (void); - #ifndef NONET static void Command_Listserv_f(void); #endif + +#endif/*MASTERSERVER*/ + +static void Update_parameters (void); + static void MasterServer_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { @@ -63,7 +68,7 @@ consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SA INT16 ms_RoomId = -1; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; I_mutex ms_QueryId_mutex; @@ -91,10 +96,14 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_servername); +#ifdef MASTERSERVER COM_AddCommand("listserv", Command_Listserv_f); #endif +#endif } +#ifdef MASTERSERVER + static void WarnGUI (void) { #ifdef HAVE_THREADS @@ -395,6 +404,7 @@ Change_masterserver_thread (char *api) void RegisterServer(void) { +#ifdef MASTERSERVER #ifdef HAVE_THREADS I_spawn_thread( "register-server", @@ -404,6 +414,7 @@ void RegisterServer(void) #else Finish_registration(); #endif +#endif/*MASTERSERVER*/ } static void UpdateServer(void) @@ -421,6 +432,7 @@ static void UpdateServer(void) void UnregisterServer(void) { +#ifdef MASTERSERVER #ifdef HAVE_THREADS I_spawn_thread( "unlist-server", @@ -430,6 +442,7 @@ void UnregisterServer(void) #else Finish_unlist(); #endif +#endif/*MASTERSERVER*/ } static boolean @@ -465,9 +478,33 @@ static inline void SendPingToMasterServer(void) } } +void MasterClient_Ticker(void) +{ +#ifdef MASTERSERVER + SendPingToMasterServer(); +#endif +} + +static void +Set_api (const char *api) +{ +#ifdef HAVE_THREADS + I_spawn_thread( + "change-masterserver", + (I_thread_fn)Change_masterserver_thread, + strdup(api) + ); +#else + HMS_set_api(strdup(api)); +#endif +} + +#endif/*MASTERSERVER*/ + static void Update_parameters (void) { +#ifdef MASTERSERVER int registered; int delayed; @@ -487,29 +524,12 @@ Update_parameters (void) if (! delayed && registered) UpdateServer(); } -} - -void MasterClient_Ticker(void) -{ - SendPingToMasterServer(); -} - -static void -Set_api (const char *api) -{ -#ifdef HAVE_THREADS - I_spawn_thread( - "change-masterserver", - (I_thread_fn)Change_masterserver_thread, - strdup(api) - ); -#else - HMS_set_api(strdup(api)); -#endif +#endif/*MASTERSERVER*/ } static void MasterServer_OnChange(void) { +#ifdef MASTERSERVER UnregisterServer(); /* @@ -527,4 +547,5 @@ static void MasterServer_OnChange(void) if (Online()) RegisterServer(); +#endif/*MASTERSERVER*/ } From 352449010e95f6e638a50da7c8051a8285330a77 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 02:05:16 -0400 Subject: [PATCH 048/193] Display time elasped/time limit --- src/discord.c | 36 +++++++++++++++++++++++++----------- src/mserv.c | 16 ++++++++-------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/discord.c b/src/discord.c index 68c1d14d2..0a57b15fb 100644 --- a/src/discord.c +++ b/src/discord.c @@ -192,25 +192,28 @@ void DRPC_UpdatePresence(void) case 33: discordPresence.state = "Standard"; break; case 28: discordPresence.state = "Casual"; break; case 38: discordPresence.state = "Custom Gametypes"; break; - //case ??: discordPresence.state = "OLDC"; break; // If I remembered this one's room ID, I would add it :V + case 31: discordPresence.state = "OLDC"; break; default: discordPresence.state = "Unknown Room"; break; // HOW } discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: use another variable to hold this, so maxplayers doesn't have to be a netvar!) // Grab the host's IP for joining. if ((join = DRPC_GetServerIP()) != NULL) discordPresence.joinSecret = join; - - discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: use another variable to hold this, so maxplayers doesn't have to be a netvar!) } - else if (Playing()) - discordPresence.state = "Offline"; - else if (demo.playback) - discordPresence.state = "Watching Replay"; else - discordPresence.state = "Menu"; + { + // Offline info + if (Playing()) + discordPresence.state = "Offline"; + else if (demo.playback && !demo.title) + discordPresence.state = "Watching Replay"; + else + discordPresence.state = "Menu"; + } // Gametype info if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) @@ -245,8 +248,19 @@ void DRPC_UpdatePresence(void) discordPresence.largeImageText = mapname; // Map name } - // discordPresence.startTimestamp & endTimestamp could be used to show leveltime & timelimit respectively, - // but would need converted to epoch seconds somehow + if (gamestate == GS_LEVEL) + { + const time_t currentTime = time(NULL); + const time_t mapTimeStart = currentTime - (leveltime / TICRATE); + + discordPresence.startTimestamp = mapTimeStart; + + if (timelimitintics > 0) + { + const time_t mapTimeEnd = mapTimeStart + ((timelimitintics + starttime + 1) / TICRATE); + discordPresence.endTimestamp = mapTimeEnd; + } + } } else if (gamestate == GS_VOTING) { diff --git a/src/mserv.c b/src/mserv.c index 7311227ad..2c91781cf 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -261,6 +261,10 @@ Finish_update (void) if (! done) Finish_update(); +#ifdef HAVE_DISCORDRPC + else + DRPC_UpdatePresence(); +#endif } static void @@ -298,6 +302,10 @@ Finish_unlist (void) MSId++; } Unlock_state(); + +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#endif } #ifdef HAVE_THREADS @@ -434,10 +442,6 @@ void UnregisterServer(void) #else Finish_unlist(); #endif - -#ifdef HAVE_DISCORDRPC - DRPC_UpdatePresence(); -#endif } static boolean @@ -495,10 +499,6 @@ Update_parameters (void) if (! delayed && registered) UpdateServer(); } - -#ifdef HAVE_DISCORDRPC - DRPC_UpdatePresence(); -#endif } void MasterClient_Ticker(void) From 59ebf07dd5665887fc0743a89dcfa8f1f30b67b9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 02:25:05 -0400 Subject: [PATCH 049/193] Guard discord.h, add cvar to disable rich presence --- src/d_netcmd.c | 4 ++++ src/discord.c | 17 ++++++++++++++--- src/discord.h | 9 ++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7d6c104e4..213ba5d48 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1004,6 +1004,10 @@ void D_RegisterClientCommands(void) #if defined(HAVE_BLUA) && defined(LUA_ALLOW_BYTECODE) COM_AddCommand("dumplua", Command_Dumplua_f); #endif + +#ifdef HAVE_DISCORDRPC + CV_RegisterVar(&cv_discordrp); +#endif } /** Checks if a name (as received from another player) is okay. diff --git a/src/discord.c b/src/discord.c index 0a57b15fb..958abb6a4 100644 --- a/src/discord.c +++ b/src/discord.c @@ -29,7 +29,10 @@ #include "discord.h" #include "doomdef.h" -#define DISCORD_APPID "503531144395096085" // Feel free to use your own, if you care. +// Feel free to provide your own, if you care enough to create another Discord app for this :P +#define DISCORD_APPID "503531144395096085" + +consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; // // DRPC_Handle's @@ -181,6 +184,14 @@ void DRPC_UpdatePresence(void) DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); + if (!cv_discordrp.value) + { + // User doesn't want to show their game information, so update with empty presence. + // This just shows that they're playing SRB2Kart. (If that's too much, then they should disable game activity :V) + Discord_UpdatePresence(&discordPresence); + return; + } + // Server info if (netgame) { @@ -198,7 +209,7 @@ void DRPC_UpdatePresence(void) discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: use another variable to hold this, so maxplayers doesn't have to be a netvar!) + discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: another variable should hold this, so that maxplayers doesn't have to be a netvar) // Grab the host's IP for joining. if ((join = DRPC_GetServerIP()) != NULL) @@ -219,7 +230,7 @@ void DRPC_UpdatePresence(void) if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) { if (modeattacking) - discordPresence.details = "Record Attack"; + discordPresence.details = "Time Attack"; else discordPresence.details = gametype_cons_t[gametype].strvalue; } diff --git a/src/discord.h b/src/discord.h index f9873d25e..e0ba67f5b 100644 --- a/src/discord.h +++ b/src/discord.h @@ -10,11 +10,18 @@ /// \file discord.h /// \brief Discord Rich Presence handling +#ifndef __DISCORD__ +#define __DISCORD__ + #ifdef HAVE_DISCORDRPC #include "discord_rpc.h" +extern consvar_t cv_discordrp; + void DRPC_Init(void); void DRPC_UpdatePresence(void); -#endif +#endif // HAVE_DISCORDRPC + +#endif // __DISCORD__ From 9bf325eec611e307becaadda41170f440cd151cd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 02:31:30 -0400 Subject: [PATCH 050/193] Put the cvar in the menu --- src/discord.h | 6 +++--- src/m_menu.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/discord.h b/src/discord.h index e0ba67f5b..2b2bccc80 100644 --- a/src/discord.h +++ b/src/discord.h @@ -1,7 +1,7 @@ -// SONIC ROBO BLAST 2 +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- -// Copyright (C) 2012-2018 by Sally "TehRealSalt" Cochenour. -// Copyright (C) 2012-2016 by Sonic Team Junior. +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour. +// Copyright (C) 2018-2020 by Kart Krew. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/m_menu.c b/src/m_menu.c index 850b8d745..9bdfbf81d 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -81,6 +81,10 @@ int snprintf(char *str, size_t n, const char *fmt, ...); //int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); #endif +#ifdef HAVE_DISCORDRPC +#include "discord.h" +#endif + #define SKULLXOFF -32 #define LINEHEIGHT 16 #define STRINGHEIGHT 8 @@ -1343,11 +1347,21 @@ static menuitem_t OP_SoundOptionsMenu[] = static menuitem_t OP_DataOptionsMenu[] = { +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_CVAR, NULL, "Discord Rich Presence", &cv_discordrp, 10}, + + {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 30}, + {IT_STRING | IT_CALL, NULL, "Addon Options...", M_AddonsOptions, 40}, + {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 50}, + + {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 70}, +#else {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, {IT_STRING | IT_CALL, NULL, "Addon Options...", M_AddonsOptions, 20}, {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, +#endif }; static menuitem_t OP_ScreenshotOptionsMenu[] = From 196514ff2142745c1b8a388c587452ecc531851c Mon Sep 17 00:00:00 2001 From: Ashnal Date: Mon, 17 Aug 2020 03:47:23 -0400 Subject: [PATCH 051/193] New message boxes for downloads and full server waiting Downloads must be confirmed before proceeding Allows downloading mods for full servers Client will wait for a slot to open on a full server to join This has a 5 minute timeout --- src/d_clisrv.c | 212 +++++++++++++++++++++++++++++++++++++++---------- src/d_netfil.c | 4 +- 2 files changed, 170 insertions(+), 46 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 186848fcc..3d910d9e6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -141,6 +141,9 @@ char connectedservername[MAXSERVERNAME]; /// \todo WORK! boolean acceptnewnode = true; +boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not +tic_t firstconnectattempttime = 0; + // engine // Must be a power of two @@ -1104,6 +1107,7 @@ typedef enum CL_CONNECTED, CL_ABORTED, CL_ASKFULLFILELIST, + CL_CONFIRMCONNECT, #ifdef HAVE_CURL CL_PREPAREHTTPFILES, CL_DOWNLOADHTTPFILES, @@ -1204,14 +1208,22 @@ static inline void CL_DrawConnectionStatus(void) break; #endif case CL_ASKFULLFILELIST: + case CL_CHECKFILES: cltext = M_GetText("Checking server addon list ..."); break; + case CL_CONFIRMCONNECT: + cltext = ""; + break; case CL_LOADFILES: cltext = M_GetText("Loading server addons..."); break; case CL_ASKJOIN: case CL_WAITJOINRESPONSE: - cltext = M_GetText("Requesting to join..."); + if (serverisfull) + cltext = M_GetText("Server full, waiting for a slot..."); + else + cltext = M_GetText("Requesting to join..."); + break; #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: @@ -1969,9 +1981,42 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET +static void M_ConfirmConnect(INT32 ch) +{ + if (ch == ' ' || ch == 'y' || ch == KEY_ENTER) + { + if (totalfilesrequestednum > 0) + { +#ifdef HAVE_CURL + if (http_source[0] == '\0' || curl_failedwebdownload) +#endif + { + if (CL_SendRequestFile()) + { + cl_mode = CL_DOWNLOADFILES; + } + } +#ifdef HAVE_CURL + else + cl_mode = CL_PREPAREHTTPFILES; +#endif + } + else + cl_mode = CL_LOADFILES; + + M_ClearMenus(true); + } + else if (ch == 'n' || ch == KEY_ESCAPE) + { + cl_mode = CL_ABORTED; + M_ClearMenus(true); + } +} + static boolean CL_FinishedFileList(void) { INT32 i; + char *downloadsize; //CONS_Printf(M_GetText("Checking files...\n")); i = CL_CheckFiles(); if (i == 4) // still checking ... @@ -2007,7 +2052,21 @@ static boolean CL_FinishedFileList(void) return false; } else if (i == 1) - cl_mode = CL_LOADFILES; + { + if (serverisfull) + { + M_StartMessage(M_GetText( + "This server is full!\n" + "\n" + "You may load server addons (if any), and wait for a slot.\n" + "\n" + "Press ACCEL to continue or BRAKE to cancel.\n\n" + ), M_ConfirmConnect, MM_YESNO); + cl_mode = CL_CONFIRMCONNECT; + } + else + cl_mode = CL_LOADFILES; + } else { // must download something @@ -2031,20 +2090,55 @@ static boolean CL_FinishedFileList(void) ), NULL, MM_NOTHING); return false; } + } +#ifdef HAVE_CURL + if (!curl_failedwebdownload) +#endif + { downloadcompletednum = 0; downloadcompletedsize = 0; totalfilesrequestednum = 0; totalfilesrequestedsize = 0; - if (CL_SendRequestFile()) - { - cl_mode = CL_DOWNLOADFILES; - } + + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + { + totalfilesrequestednum++; + totalfilesrequestedsize += fileneeded[i].totalsize; + } + + if (totalfilesrequestedsize>>20 >= 100) + downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); + else + downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); + + if (serverisfull) + M_StartMessage(va(M_GetText( + "This server is full!\n" + "Download of %s additional content is required to join.\n" + "\n" + "You may download, load server addons, and wait for a slot.\n" + "\n" + "Press ACCEL to continue or BRAKE to cancel.\n\n" + ), downloadsize), M_ConfirmConnect, MM_YESNO); + else + M_StartMessage(va(M_GetText( + "Download of %s additional content is required to join.\n" + "\n" + "Press ACCEL to continue or BRAKE to cancel.\n\n" + ), downloadsize), M_ConfirmConnect, MM_YESNO); + + Z_Free(downloadsize); + cl_mode = CL_CONFIRMCONNECT; } #ifdef HAVE_CURL else { - cl_mode = CL_PREPAREHTTPFILES; + if (CL_SendRequestFile()) + { + cl_mode = CL_DOWNLOADFILES; + } } #endif } @@ -2085,11 +2179,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) // Quit here rather than downloading files and being refused later. if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer) { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING); - return false; + serverisfull = true; } if (client) @@ -2150,7 +2240,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic { boolean waitmore; INT32 i; - + #ifdef NONET (void)tmpsave; #endif @@ -2178,21 +2268,14 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (!CL_FinishedFileList()) return false; break; - #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: - downloadcompletednum = 0; - downloadcompletedsize = 0; - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; if (http_source[0]) { for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) { curl_transfers++; - totalfilesrequestednum++; - totalfilesrequestedsize += fileneeded[i].totalsize; } cl_mode = CL_DOWNLOADHTTPFILES; @@ -2218,19 +2301,13 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (curl_failedwebdownload && !curl_transfers) { - if (!CL_FinishedFileList()) - break; - CONS_Printf("One or more files failed to download, falling back to internal downloader\n"); - if (CL_SendRequestFile()) - { - cl_mode = CL_DOWNLOADFILES; - break; - } + cl_mode = CL_CHECKFILES; + break; } if (!curl_transfers) - cl_mode = CL_LOADFILES; // don't break case continue to cljoin request now + cl_mode = CL_LOADFILES; break; #endif @@ -2246,22 +2323,49 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (waitmore) break; // exit the case - cl_mode = CL_LOADFILES; // don't break case continue to cljoin request now + cl_mode = CL_LOADFILES; break; case CL_LOADFILES: - if (!CL_LoadServerFiles()) - break; + if (CL_LoadServerFiles()) + { + *asksent = I_GetTime() - (NEWTICRATE*5); //This ensure the first join ask is right away + firstconnectattempttime = I_GetTime(); + cl_mode = CL_ASKJOIN; + } + break; case CL_ASKJOIN: + if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime()) + { + CONS_Printf(M_GetText("5 minute wait time exceeded.\n")); + CONS_Printf(M_GetText("Network game synchronization aborted.\n")); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "5 minute wait time exceeded.\n" + "You may retry connection.\n" + "\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + } #ifdef JOININGAME // prepare structures to save the file // WARNING: this can be useless in case of server not in GS_LEVEL // but since the network layer doesn't provide ordered packets... CL_PrepareDownloadSaveGame(tmpsave); #endif - if (CL_SendJoin()) + if ((*asksent + NEWTICRATE*3) < I_GetTime() && CL_SendJoin()) + { + *asksent = I_GetTime(); cl_mode = CL_WAITJOINRESPONSE; + } + break; + case CL_WAITJOINRESPONSE: + if ((*asksent + NEWTICRATE*3) < I_GetTime()) + { + cl_mode = CL_ASKJOIN; + } break; - #ifdef JOININGAME case CL_DOWNLOADSAVEGAME: // At this state, the first (and only) needed file is the gamestate @@ -2270,13 +2374,13 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic // Gamestate is now handled within CL_LoadReceivedSavegame() CL_LoadReceivedSavegame(); cl_mode = CL_CONNECTED; + break; } // don't break case continue to CL_CONNECTED else break; #endif - - case CL_WAITJOINRESPONSE: case CL_CONNECTED: + case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect default: break; @@ -2297,9 +2401,13 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic INT32 key; I_OsPolling(); + + if (cl_mode == CL_CONFIRMCONNECT) + D_ProcessEvents(); //needed for menu system to receive inputs + key = I_GetKey(); // Only ESC and non-keyboard keys abort connection - if (key == KEY_ESCAPE || key >= KEY_MOUSE1) + if (key == KEY_ESCAPE || key >= KEY_MOUSE1 || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); D_QuitNetGame(); @@ -2316,6 +2424,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic F_TitleScreenTicker(true); F_TitleScreenDrawer(); CL_DrawConnectionStatus(); + M_Drawer(); //Needed for drawing messageboxes on the connection screen I_UpdateNoVsync(); // page flip or blit buffer if (moviemode) M_SaveFrame(); @@ -2824,6 +2933,12 @@ void CL_Reset(void) fileneedednum = 0; memset(fileneeded, 0, sizeof(fileneeded)); + totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; + firstconnectattempttime = 0; + serverisfull = false; + connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack + #ifdef HAVE_CURL curl_failedwebdownload = false; curl_transfers = 0; @@ -3746,7 +3861,7 @@ void SV_StopServer(void) D_Clearticcmd(i); consoleplayer = 0; - cl_mode = CL_SEARCHING; + cl_mode = CL_ABORTED; maketic = gametic+1; neededtic = maketic; serverrunning = false; @@ -3772,7 +3887,7 @@ static void SV_SendRefuse(INT32 node, const char *reason) strcpy(netbuffer->u.serverrefuse.reason, reason); netbuffer->packettype = PT_SERVERREFUSE; - HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1); + HSendPacket(node, false, 0, strlen(netbuffer->u.serverrefuse.reason) + 1); Net_CloseConnection(node); } @@ -4044,13 +4159,24 @@ static void HandlePacketFromAwayNode(SINT8 node) if (!reason) I_Error("Out of memory!\n"); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + if (strstr(reason, "Maximum players reached")) + { + serverisfull = true; + //Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer + //We set it back to the value of cv_nettimeout.value in CL_Reset + connectiontimeout = NEWTICRATE*7; + cl_mode = CL_ASKJOIN; + free(reason); + break; + } M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), reason), NULL, MM_NOTHING); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + free(reason); // Will be reset by caller. Signals refusal. @@ -4070,7 +4196,7 @@ static void HandlePacketFromAwayNode(SINT8 node) } SERVERONLY /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != CL_WAITJOINRESPONSE) + if (!(cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_ASKJOIN)) break; if (client) diff --git a/src/d_netfil.c b/src/d_netfil.c index 0a2b15797..eca0db698 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -334,8 +334,6 @@ boolean CL_SendRequestFile(void) // put it in download dir strcatbf(fileneeded[i].filename, downloaddir, "/"); fileneeded[i].status = FS_REQUESTED; - totalfilesrequestednum++; - totalfilesrequestedsize += fileneeded[i].totalsize; } WRITEUINT8(p, 0xFF); I_GetDiskFreeSpace(&availablefreespace); @@ -432,7 +430,7 @@ INT32 CL_CheckFiles(void) for (i = 0; i < fileneedednum; i++) { - if (fileneeded[i].status == FS_NOTFOUND) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_FALLBACK) downloadrequired = true; if (fileneeded[i].status == FS_FOUND || fileneeded[i].status == FS_NOTFOUND) From 30f687d0df55ae259c8af37969d94a3be8023c6c Mon Sep 17 00:00:00 2001 From: Ashnal Date: Mon, 17 Aug 2020 03:51:19 -0400 Subject: [PATCH 052/193] Fixes for file accounting mainwads+1 to not use the first mainwad --- src/d_netfil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index eca0db698..fd28413b3 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -401,7 +401,7 @@ INT32 CL_CheckFiles(void) if (modifiedgame) { CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n"); - for (i = 0, j = mainwads; i < fileneedednum || j < numwadfiles;) + for (i = 0, j = mainwads+1; i < fileneedednum || j < numwadfiles;) { if (j < numwadfiles && !wadfiles[j]->important) { @@ -442,7 +442,7 @@ INT32 CL_CheckFiles(void) CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); // Check in already loaded files - for (j = mainwads; wadfiles[j]; j++) + for (j = mainwads+1; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); if (!stricmp(wadfilename, fileneeded[i].filename) && From 42f677447dcc00a8334877ecae90f4bdf288b75c Mon Sep 17 00:00:00 2001 From: Ashnal Date: Mon, 17 Aug 2020 03:56:13 -0400 Subject: [PATCH 053/193] Move declaration to appease C90 also remove some extra tab characters --- src/d_clisrv.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3d910d9e6..bb3fb3e19 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1241,15 +1241,16 @@ static inline void CL_DrawConnectionStatus(void) if (cl_mode == CL_LOADFILES) { INT32 totalfileslength; + INT32 loadcompletednum = 0; + INT32 i; + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); //ima just count files here - INT32 loadcompletednum = 0; - INT32 i; for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_OPEN) loadcompletednum++; - + // Loading progress V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons..."); totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); @@ -1279,8 +1280,6 @@ static inline void CL_DrawConnectionStatus(void) V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, 256, 8, 175); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, dldlength, 8, 160); - - memset(tempname, 0, sizeof(tempname)); // offset filename to just the name only part filename += strlen(filename) - nameonlylength(filename); @@ -1323,7 +1322,7 @@ static inline void CL_DrawConnectionStatus(void) else V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, va(" %4uK/%4uK",totaldldsize>>10,totalfilesrequestedsize>>10)); - + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, va("%2u/%2u Files ",downloadcompletednum,totalfilesrequestednum)); } From 98ad0248f97bd40bee5e20071de5a68fb23efe6d Mon Sep 17 00:00:00 2001 From: Ashnal Date: Mon, 17 Aug 2020 04:03:02 -0400 Subject: [PATCH 054/193] Move code down so declarations are above to appease C90 --- src/d_clisrv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index bb3fb3e19..2212a64bd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1262,10 +1262,6 @@ static inline void CL_DrawConnectionStatus(void) } else if (lastfilenum != -1) { - // Draw the bottom box. - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-58-8, 32, 1); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-14, V_YELLOWMAP, "Press ESC to abort"); - INT32 dldlength; INT32 totalfileslength; UINT32 totaldldsize; @@ -1273,6 +1269,10 @@ static inline void CL_DrawConnectionStatus(void) fileneeded_t *file = &fileneeded[lastfilenum]; char *filename = file->filename; + // Draw the bottom box. + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-58-8, 32, 1); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-14, V_YELLOWMAP, "Press ESC to abort"); + Net_GetNetStat(); dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); if (dldlength > 256) From bd29d0c6bba75b5545c01bf4b590fec48d1baad0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 04:42:05 -0400 Subject: [PATCH 055/193] Check supported skins by name instead of number, so we can have bonuschars images --- src/discord.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/src/discord.c b/src/discord.c index 958abb6a4..ed905d201 100644 --- a/src/discord.c +++ b/src/discord.c @@ -287,13 +287,70 @@ void DRPC_UpdatePresence(void) // Character info if (Playing() && playeringame[consoleplayer] && !players[consoleplayer].spectator) { - if (players[consoleplayer].skin < 5) // supported skins + // Supported skin names + static const char *supportedSkins[] = { + // base game + "sonic", + "tails", + "knuckles", + "eggman", + "metalsonic", + // bonus chars + "flicky", + "motobug", + "amy", + "mighty", + "ray", + "espio", + "vector", + "chao", + "gamma", + "chaos", + "shadow", + "rouge", + "herochao", + "darkchao", + "cream", + "omega", + "blaze", + "silver", + "wonderboy", + "arle", + "nights", + "sakura", + "ulala", + "beat", + "vyse", + "aiai", + "kiryu", + "aigis", + "miku", + "doom", + NULL + }; + + boolean customChar = true; + UINT8 checkSkin = 0; + + // Character image + while (supportedSkins[checkSkin] != NULL) { - snprintf(charimg, 21, "char%s", skins[players[consoleplayer].skin].name); - discordPresence.smallImageKey = charimg; // Character image + if (!strcmp(skins[players[consoleplayer].skin].name, supportedSkins[checkSkin])) + { + snprintf(charimg, 21, "char%s", supportedSkins[checkSkin]); + discordPresence.smallImageKey = charimg; + customChar = false; + break; + } + + checkSkin++; + } + + if (customChar == true) + { + // Use the custom character icon! + discordPresence.smallImageKey = "charcustom"; } - else - discordPresence.smallImageKey = "charnull"; // Just so that you can still see the name of custom chars snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname); discordPresence.smallImageText = charname; // Character name From 8fd0424f150bcdcbbd5473ef9d832ff899dd76da Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 04:42:22 -0400 Subject: [PATCH 056/193] Add distinct images for map hell & normal custom maps --- src/discord.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/discord.c b/src/discord.c index ed905d201..2ee6d8bd9 100644 --- a/src/discord.c +++ b/src/discord.c @@ -176,10 +176,11 @@ static const char *DRPC_GetServerIP(void) // void DRPC_UpdatePresence(void) { - char mapimg[8]; - char mapname[48]; - char charimg[21]; - char charname[28]; + char mapimg[8+1]; + char mapname[5+21+21+2+1]; + + char charimg[4+SKINNAMESIZE+1]; + char charname[11+SKINNAMESIZE+1]; DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); @@ -244,11 +245,22 @@ void DRPC_UpdatePresence(void) strlwr(mapimg); discordPresence.largeImageKey = mapimg; // Map image } - else // Fallback, since no image looks crappy! - discordPresence.largeImageKey = "miscdice"; + else if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) + { + // Hell map, use the method that got you here :P + discordPresence.largeImageKey = "maphell"; + } + else + { + // This is probably a custom map! + discordPresence.largeImageKey = "mapcustom"; + } - if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) // hell map, hide the name + if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) + { + // Hell map, hide the name discordPresence.largeImageText = "Map: ???"; + } else { snprintf(mapname, 48, "Map: %s%s%s", From 1598bfa143206245ddcf1640de6ab30185476347 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 17 Aug 2020 12:01:16 +0200 Subject: [PATCH 057/193] Kill line riding for offroad --- src/k_kart.c | 78 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b4c2992e0..67a7ae918 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1377,30 +1377,72 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) } } -/** \brief Checks that the player is on an offroad subsector for realsies +/** \brief Checks that the player is on an offroad subsector for realsies. Also accounts for line riding to prevent cheese. \param mo player mobj object \return boolean */ -static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec) +static UINT8 K_CheckOffroadCollide(mobj_t *mo) { - UINT8 i; - sector_t *sec2; + // Check for sectors in touching_sectorlist + UINT8 i; // special type iter + msecnode_t *node; // touching_sectorlist iter + sector_t *s; // main sector shortcut + sector_t *s2; // FOF sector shortcut + ffloor_t *rover; // FOF + + fixed_t flr; + fixed_t cel; // floor & ceiling for height checks to make sure we're touching the offroad sector. I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); - sec2 = P_ThingOnSpecial3DFloor(mo); - - for (i = 2; i < 5; i++) + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) { - if ((sec2 && GETSECSPECIAL(sec2->special, 1) == i) - || (P_IsObjectOnRealGround(mo, sec) && GETSECSPECIAL(sec->special, 1) == i)) - return i-1; - } + if (!node->m_sector) + break; // shouldn't happen. - return 0; + s = node->m_sector; + // 1: Check for the main sector, make sure we're on the floor of that sector and see if we can apply offroad. + // Make arbitrary Z checks because we want to check for 1 sector in particular, we don't want to affect the player if the offroad sector is way below them and they're lineriding a normal sector above. + + flr = P_MobjFloorZ(mo, s, s, mo->x, mo->y, NULL, false, true); + cel = P_MobjCeilingZ(mo, s, s, mo->x, mo->y, NULL, true, true); // get Z coords of both floors and ceilings for this sector (this accounts for slopes properly.) + // NOTE: we don't use P_GetZAt with our x/y directly because the mobj won't have the same height because of its hitbox on the slope. Complex garbage but tldr it doesn't work. + + if ( ((s->flags & SF_FLIPSPECIAL_FLOOR) && mo->z == flr) // floor check + || ((mo->eflags & MFE_VERTICALFLIP && (s->flags & SF_FLIPSPECIAL_CEILING) && (mo->z + mo->height) == cel)) ) // ceiling check. + + for (i = 2; i < 5; i++) // check for sector special + + if (GETSECSPECIAL(s->special, 1) == i) + return i-1; // return offroad type + + // 2: If we're here, we haven't found anything. So let's try looking for FOFs in the sectors using the same logic. + for (rover = s->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS)) // This FOF doesn't exist anymore. + continue; + + s2 = §ors[rover->secnum]; // makes things easier for us + + flr = P_GetFOFBottomZ(mo, s, rover, mo->x, mo->y, NULL); + cel = P_GetFOFTopZ(mo, s, rover, mo->x, mo->y, NULL); // Z coords for fof top/bottom. + + // we will do essentially the same checks as above instead of bothering with top/bottom height of the FOF. + // Reminder that an FOF's floor is its bottom, silly! + if ( ((s2->flags & SF_FLIPSPECIAL_FLOOR) && mo->z == cel) // "floor" check + || ((s2->flags & SF_FLIPSPECIAL_CEILING) && (mo->z + mo->height) == flr) ) // "ceiling" check. + + for (i = 2; i < 5; i++) // check for sector special + + if (GETSECSPECIAL(s2->special, 1) == i) + return i-1; // return offroad type + + } + } + return 0; // couldn't find any offroad } /** \brief Updates the Player's offroad value once per frame @@ -1412,14 +1454,12 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec) static void K_UpdateOffroad(player_t *player) { fixed_t offroad; - sector_t *nextsector = R_PointInSubsector( - player->mo->x + player->mo->momx*2, player->mo->y + player->mo->momy*2)->sector; - UINT8 offroadstrength = K_CheckOffroadCollide(player->mo, nextsector); + UINT8 offroadstrength = K_CheckOffroadCollide(player->mo); // If you are in offroad, a timer starts. if (offroadstrength) { - if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0) + if (/*K_CheckOffroadCollide(player->mo) &&*/ player->kartstuff[k_offroad] == 0) // With the way offroad is detected now that first check is no longer necessary. -Lat' player->kartstuff[k_offroad] = (TICRATE/2); if (player->kartstuff[k_offroad] > 0) @@ -3326,13 +3366,13 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) mine->flags2 = thismine->flags2; mine->floorz = thismine->floorz; mine->ceilingz = thismine->ceilingz; - + //Since we aren't using P_KillMobj, we need to clean up the hnext reference { P_SetTarget(&thismine->target->hnext, NULL); //target is the player who owns the mine thismine->target->player->kartstuff[k_bananadrag] = 0; thismine->target->player->kartstuff[k_itemheld] = 0; - + if (--thismine->target->player->kartstuff[k_itemamount] <= 0) thismine->target->player->kartstuff[k_itemtype] = KITEM_NONE; } @@ -3916,7 +3956,7 @@ void K_DropRocketSneaker(player_t *player) flingangle = -(ANG60); else flingangle = ANG60; - + S_StartSound(shoe, shoe->info->deathsound); P_SetObjectMomZ(shoe, 8*FRACUNIT, false); P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT); From 1d5251e1f151bba6b1b11cf64768430072b5cc70 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 06:13:32 -0400 Subject: [PATCH 058/193] Fix ZONE being all caps, standardize the function comments --- src/d_clisrv.c | 8 +-- src/discord.c | 142 ++++++++++++++++++++++++++++++++++++++----------- src/discord.h | 18 +++++++ src/g_game.c | 2 +- src/m_menu.c | 2 +- src/p_setup.c | 2 +- src/st_stuff.c | 4 +- src/y_inter.c | 2 +- 8 files changed, 139 insertions(+), 41 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6f5954c5f..3b29b2b04 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1382,7 +1382,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl, mapheaderinfo[gamemap-1]->actnum) < 0) { // If there's an encoding error, send UNKNOWN, we accept that the above may be truncated - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33); + strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", 33); } } else @@ -1393,13 +1393,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl) < 0) { // If there's an encoding error, send UNKNOWN, we accept that the above may be truncated - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33); + strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", 33); } } } } else - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33); + strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", 33); netbuffer->u.serverinfo.maptitle[32] = '\0'; @@ -1725,7 +1725,7 @@ static void CL_LoadReceivedSavegame(void) if (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->zonttl)); else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CON_LogMessage(M_GetText(" ZONE")); + CON_LogMessage(M_GetText(" Zone")); if (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->actnum)); } diff --git a/src/discord.c b/src/discord.c index 2ee6d8bd9..f4789794a 100644 --- a/src/discord.c +++ b/src/discord.c @@ -34,33 +34,96 @@ consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; -// -// DRPC_Handle's -// -static inline void DRPC_HandleReady(const DiscordUser *user) +#ifdef HAVE_CURL +struct SelfIPbuffer +{ + CURL *curl; + char *pointer; + size_t length; +}; + +#define IP_SIZE 16 +static char self_ip[IP_SIZE]; +#endif + +/*-------------------------------------------------- + static void DRPC_HandleReady(const DiscordUser *user) + + Handler function, ran when the game connects to Discord. + + Input Arguments:- + user - Struct containing Discord user info. + + Return:- + None +--------------------------------------------------*/ +static void DRPC_HandleReady(const DiscordUser *user) { CONS_Printf("Discord: connected to %s#%s - %s\n", user->username, user->discriminator, user->userId); } -static inline void DRPC_HandleDisconnect(int err, const char *msg) +/*-------------------------------------------------- + static void DRPC_HandleDisconnect(int err, const char *msg) + + Handler function, ran when disconnecting from Discord. + + Input Arguments:- + err - Error type + msg - Error message + + Return:- + None +--------------------------------------------------*/ +static void DRPC_HandleDisconnect(int err, const char *msg) { CONS_Printf("Discord: disconnected (%d: %s)\n", err, msg); } -static inline void DRPC_HandleError(int err, const char *msg) +/*-------------------------------------------------- + static void DRPC_HandleError(int err, const char *msg) + + Handler function, ran when Discord outputs an error. + + Input Arguments:- + err - Error type + msg - Error message + + Return:- + None +--------------------------------------------------*/ +static void DRPC_HandleError(int err, const char *msg) { - CONS_Alert(CONS_WARNING, "Discord: error (%d, %s)\n", err, msg); + CONS_Alert(CONS_WARNING, "Discord error (%d: %s)\n", err, msg); } -static inline void DRPC_HandleJoin(const char *secret) +/*-------------------------------------------------- + static void DRPC_HandleJoin(const char *secret) + + Handler function, ran when Discord wants to + connect a player to the game via a channel invite + or a join request. + + Input Arguments:- + secret - Value that links you to the server. + + Return:- + None +--------------------------------------------------*/ +static void DRPC_HandleJoin(const char *secret) { - CONS_Printf("Discord: connecting to %s\n", secret); + // Yes, this is called a "secret", but we send & use it + // directly, because if you're hosting SRB2, then you've + // already made your IP address totally public. + + CONS_Printf("Connecting to %s via Discord\n", secret); COM_BufAddText(va("connect \"%s\"\n", secret)); } -// -// DRPC_Init: starting up the handles, call Discord_initalize -// +/*-------------------------------------------------- + void DRPC_Init(void) + + See header file for description. +--------------------------------------------------*/ void DRPC_Init(void) { DiscordEventHandlers handlers; @@ -77,17 +140,21 @@ void DRPC_Init(void) } #ifdef HAVE_CURL -#define IP_SIZE 16 -static char self_ip[IP_SIZE]; +/*-------------------------------------------------- + static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) -struct SelfIPbuffer -{ - CURL *curl; - char *pointer; - size_t length; -}; + Writing function for use with curl. Only intended to be used with simple text. -static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata ) + Input Arguments:- + s - Data to write + size - Always 1. + n - Length of data + userdata - Passed in from CURLOPT_WRITEDATA, intended to be SelfIPbuffer + + Return:- + Number of bytes wrote in this pass. +--------------------------------------------------*/ +static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) { struct SelfIPbuffer *buffer; size_t newlength; @@ -106,9 +173,13 @@ static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata } #endif -// -// DRPC_GetServerIP: Gets the server's IP address, used to -// +/*-------------------------------------------------- + static const char *DRPC_GetServerIP(void) + + Retrieves the IP address of the server that you're + connected to. Will attempt to use curl for getting your + own IP address, if it's not yours. +--------------------------------------------------*/ static const char *DRPC_GetServerIP(void) { const char *address; @@ -117,7 +188,11 @@ static const char *DRPC_GetServerIP(void) if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL) { if (strcmp(address, "self")) - return address; // We're not the server, so we could successfully get the IP! No problem here :) + { + // We're not the server, so we could successfully get the IP! + // No need to do anything else :) + return address; + } } #ifdef HAVE_CURL @@ -133,7 +208,10 @@ static const char *DRPC_GetServerIP(void) if (curl) { - const char *api = "http://ip4only.me/api/"; // API to get your public IP address from + // The API to get your public IP address from. + // Picked because it's stupid simple and it's been up for a long time. + const char *api = "http://ip4only.me/api/"; + struct SelfIPbuffer buffer; CURLcode success; @@ -171,9 +249,11 @@ static const char *DRPC_GetServerIP(void) return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites } -// -// DRPC_UpdatePresence: Called whenever anything changes about server info -// +/*-------------------------------------------------- + void DRPC_UpdatePresence(void) + + See header file for description. +--------------------------------------------------*/ void DRPC_UpdatePresence(void) { char mapimg[8+1]; @@ -271,10 +351,10 @@ void DRPC_UpdatePresence(void) discordPresence.largeImageText = mapname; // Map name } - if (gamestate == GS_LEVEL) + if (Playing()) { const time_t currentTime = time(NULL); - const time_t mapTimeStart = currentTime - (leveltime / TICRATE); + const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE); discordPresence.startTimestamp = mapTimeStart; diff --git a/src/discord.h b/src/discord.h index 2b2bccc80..aa2c1468b 100644 --- a/src/discord.h +++ b/src/discord.h @@ -19,9 +19,27 @@ extern consvar_t cv_discordrp; +/*-------------------------------------------------- + void DRPC_Init(void); + + Initalizes Discord Rich Presence by linking the Application ID + and setting the handler functions. +--------------------------------------------------*/ + void DRPC_Init(void); + + +/*-------------------------------------------------- + void DRPC_UpdatePresence(void); + + Updates what is displayed by Rich Presence on the user's profile. + Should be called whenever something that is displayed is + changed in-game. +--------------------------------------------------*/ + void DRPC_UpdatePresence(void); + #endif // HAVE_DISCORDRPC #endif // __DISCORD__ diff --git a/src/g_game.c b/src/g_game.c index 765a0d685..fec543945 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4725,7 +4725,7 @@ char *G_BuildMapTitle(INT32 mapnum) } else if (!(mapheaderinfo[mapnum-1]->levelflags & LF_NOZONE)) { - zonetext = M_GetText("ZONE"); + zonetext = M_GetText("Zone"); len += strlen(zonetext) + 1; // ' ' + zonetext } if (strlen(mapheaderinfo[mapnum-1]->actnum) > 0) diff --git a/src/m_menu.c b/src/m_menu.c index 9bdfbf81d..791ab34b8 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7474,7 +7474,7 @@ static void M_DrawStatsMaps(int location) else V_DrawString(20, y, 0, va("%s %s %s", mapheaderinfo[mnum]->lvlttl, - (mapheaderinfo[mnum]->zonttl[0] ? mapheaderinfo[mnum]->zonttl : "ZONE"), + (mapheaderinfo[mnum]->zonttl[0] ? mapheaderinfo[mnum]->zonttl : "Zone"), mapheaderinfo[mnum]->actnum)); y += 8; diff --git a/src/p_setup.c b/src/p_setup.c index 85243d504..6cfbe9123 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2974,7 +2974,7 @@ boolean P_SetupLevel(boolean skipprecip) snprintf(tx, 63, "%s%s%s", mapheaderinfo[gamemap-1]->lvlttl, (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart - ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE"), + ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone"), (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(", Act %s",mapheaderinfo[gamemap-1]->actnum) : ""); V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx); I_UpdateNoVsync(); diff --git a/src/st_stuff.c b/src/st_stuff.c index 9a9af6359..252457f98 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -776,7 +776,7 @@ static void ST_drawLevelTitle(void) if (zonttl[0]) zonexpos -= V_LevelNameWidth(zonttl); // SRB2kart else - zonexpos -= V_LevelNameWidth(M_GetText("ZONE")); + zonexpos -= V_LevelNameWidth(M_GetText("Zone")); } if (lvlttlxpos < 0) @@ -813,7 +813,7 @@ static void ST_drawLevelTitle(void) if (strlen(zonttl) > 0) V_DrawLevelTitle(zonexpos, bary+6, 0, zonttl); else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - V_DrawLevelTitle(zonexpos, bary+6, 0, M_GetText("ZONE")); + V_DrawLevelTitle(zonexpos, bary+6, 0, M_GetText("Zone")); if (actnum[0]) V_DrawLevelTitle(ttlnumxpos+12, bary+6, 0, actnum); diff --git a/src/y_inter.c b/src/y_inter.c index 29b681f78..c01c611ee 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -233,7 +233,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) } else { - const char *zonttl = (mapheaderinfo[prevmap]->zonttl[0] ? mapheaderinfo[prevmap]->zonttl : "ZONE"); + const char *zonttl = (mapheaderinfo[prevmap]->zonttl[0] ? mapheaderinfo[prevmap]->zonttl : "Zone"); if (mapheaderinfo[prevmap]->actnum[0]) snprintf(data.match.levelstring, sizeof data.match.levelstring, From 0576729ff453fd6e400dd8c8affcdde3610a3c5c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 06:52:46 -0400 Subject: [PATCH 059/193] Update presence when maxplayers is changed --- src/d_clisrv.c | 14 +++++++++++++- src/discord.c | 4 ---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3b29b2b04..16ed731e5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3261,8 +3261,11 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, N #ifdef VANILLAJOINNEXTROUND consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done #endif + +static void MaxPlayers_OnChange(void); static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_NETVAR|CV_CALL, maxplayers_cons_t, MaxPlayers_OnChange, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; @@ -3279,6 +3282,15 @@ consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons static void Got_AddPlayer(UINT8 **p, INT32 playernum); static void Got_RemovePlayer(UINT8 **p, INT32 playernum); +static void MaxPlayers_OnChange(void) +{ +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#else + return; +#endif +} + // called one time at init void D_ClientServerInit(void) { diff --git a/src/discord.c b/src/discord.c index f4789794a..718f20c7c 100644 --- a/src/discord.c +++ b/src/discord.c @@ -111,10 +111,6 @@ static void DRPC_HandleError(int err, const char *msg) --------------------------------------------------*/ static void DRPC_HandleJoin(const char *secret) { - // Yes, this is called a "secret", but we send & use it - // directly, because if you're hosting SRB2, then you've - // already made your IP address totally public. - CONS_Printf("Connecting to %s via Discord\n", secret); COM_BufAddText(va("connect \"%s\"\n", secret)); } From 1a966f4a112ebe6faf9a3f18e0da1d383654ca5a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 06:55:43 -0400 Subject: [PATCH 060/193] Don't allow invites if allowjoin is off --- src/discord.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord.c b/src/discord.c index 718f20c7c..782e475bb 100644 --- a/src/discord.c +++ b/src/discord.c @@ -289,7 +289,7 @@ void DRPC_UpdatePresence(void) discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: another variable should hold this, so that maxplayers doesn't have to be a netvar) // Grab the host's IP for joining. - if ((join = DRPC_GetServerIP()) != NULL) + if (cv_allownewplayer.value && ((join = DRPC_GetServerIP()) != NULL)) discordPresence.joinSecret = join; } else From 9d304d3d16c05f8d5978d27dffabe222ef297b10 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 09:08:05 -0400 Subject: [PATCH 061/193] Show game speed and encore --- src/discord.c | 44 +++++++++++++++++++++++++++++++------------- src/g_game.c | 2 +- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/discord.c b/src/discord.c index 782e475bb..81a5f7f59 100644 --- a/src/discord.c +++ b/src/discord.c @@ -14,7 +14,7 @@ #ifdef HAVE_CURL #include -#endif +#endif // HAVE_CURL #include "i_system.h" #include "d_clisrv.h" @@ -44,7 +44,7 @@ struct SelfIPbuffer #define IP_SIZE 16 static char self_ip[IP_SIZE]; -#endif +#endif // HAVE_CURL /*-------------------------------------------------- static void DRPC_HandleReady(const DiscordUser *user) @@ -167,7 +167,7 @@ static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) return size*n; } -#endif +#endif // HAVE_CURL /*-------------------------------------------------- static const char *DRPC_GetServerIP(void) @@ -241,7 +241,7 @@ static const char *DRPC_GetServerIP(void) if (self_ip[0]) return self_ip; else -#endif +#endif // HAVE_CURL return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites } @@ -252,6 +252,8 @@ static const char *DRPC_GetServerIP(void) --------------------------------------------------*/ void DRPC_UpdatePresence(void) { + char detailstr[48+1]; + char mapimg[8+1]; char mapname[5+21+21+2+1]; @@ -269,6 +271,18 @@ void DRPC_UpdatePresence(void) return; } +/* +#ifdef DEVELOP + // This way, we can use the invite feature in-dev, but not have snoopers seeing any potential secrets! :P + discordPresence.largeImageKey = "miscdevelop"; + discordPresence.largeImageText = "Nope."; + discordPresence.state = "Shh! We're testing!"; + + Discord_UpdatePresence(&discordPresence); + return; +#endif // DEVELOP +*/ + // Server info if (netgame) { @@ -309,7 +323,14 @@ void DRPC_UpdatePresence(void) if (modeattacking) discordPresence.details = "Time Attack"; else - discordPresence.details = gametype_cons_t[gametype].strvalue; + { + snprintf(detailstr, 48, "%s%s%s", + gametype_cons_t[gametype].strvalue, + (gametype == GT_RACE) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "", + (encoremode == true) ? " | Encore" : "" + ); + discordPresence.details = detailstr; + } } if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info @@ -324,7 +345,7 @@ void DRPC_UpdatePresence(void) else if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) { // Hell map, use the method that got you here :P - discordPresence.largeImageKey = "maphell"; + discordPresence.largeImageKey = "miscdice"; } else { @@ -339,12 +360,9 @@ void DRPC_UpdatePresence(void) } else { - snprintf(mapname, 48, "Map: %s%s%s", - mapheaderinfo[gamemap-1]->lvlttl, - (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart - ((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone"), - (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->actnum) : ""); - discordPresence.largeImageText = mapname; // Map name + // Map name on tool tip + snprintf(mapname, 48, "Map: %s", G_BuildMapTitle(gamemap)); + discordPresence.largeImageText = mapname; } if (Playing()) @@ -447,4 +465,4 @@ void DRPC_UpdatePresence(void) Discord_UpdatePresence(&discordPresence); } -#endif +#endif // HAVE_DISCORDRPC diff --git a/src/g_game.c b/src/g_game.c index fec543945..919d3d9de 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6389,7 +6389,7 @@ void G_BeginRecording(void) // Full replay title demo_p += 64; - snprintf(demo.titlename, 64, "%s - %s", G_BuildMapTitle(gamemap), modeattacking ? "Record Attack" : connectedservername); + snprintf(demo.titlename, 64, "%s - %s", G_BuildMapTitle(gamemap), modeattacking ? "Time Attack" : connectedservername); // demo checksum demo_p += 16; From a29785aec3d18c50e3d46610422b1b9741ef9b16 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Aug 2020 20:11:32 -0700 Subject: [PATCH 062/193] Save srb2path to %LOCALAPPDATA%\SRB2Kart\lastwaddir, and chdir here if srb2.srb cannot be found Windows code goes brrr --- src/d_main.c | 33 +++++++++++++++++++++---- src/i_system.h | 9 +++++++ src/sdl/i_system.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 6029c648c..d05e0a591 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -845,6 +845,24 @@ static inline void D_CleanFile(char **filearray) // Identify the SRB2 version, and IWAD file to use. // ========================================================================== +static boolean AddIWAD(char *srb2wad1, char *srb2wad2) +{ + if (srb2wad2 != NULL && FIL_ReadFileOK(srb2wad2)) + { + D_AddFile(srb2wad2, startupwadfiles); + return true; + } + else if (srb2wad1 != NULL && FIL_ReadFileOK(srb2wad1)) + { + D_AddFile(srb2wad1, startupwadfiles); + return true; + } + else + { + return false; + } +} + static void IdentifyVersion(void) { char *srb2wad1, *srb2wad2; @@ -896,12 +914,17 @@ static void IdentifyVersion(void) configfile[sizeof configfile - 1] = '\0'; // Load the IWAD - if (srb2wad2 != NULL && FIL_ReadFileOK(srb2wad2)) - D_AddFile(srb2wad2, startupwadfiles); - else if (srb2wad1 != NULL && FIL_ReadFileOK(srb2wad1)) - D_AddFile(srb2wad1, startupwadfiles); + if (AddIWAD(srb2wad1, srb2wad2)) + { + I_SaveCurrentWadDirectory(); + } else - I_Error("SRB2.SRB/SRB2.WAD not found! Expected in %s, ss files: %s or %s\n", srb2waddir, srb2wad1, srb2wad2); + { + if (!( I_UseSavedWadDirectory() && AddIWAD(srb2wad1, srb2wad2) )) + { + I_Error("SRB2.SRB/SRB2.WAD not found! Expected in %s, ss files: %s or %s\n", srb2waddir, srb2wad1, srb2wad2); + } + } if (srb2wad1) free(srb2wad1); diff --git a/src/i_system.h b/src/i_system.h index 3e589c69a..2f4b1b5c8 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -318,6 +318,15 @@ const CPUInfoFlags *I_CPUInfo(void); */ const char *I_LocateWad(void); +/** \brief Save current wad directory to appdata +*/ +void I_SaveCurrentWadDirectory(void); + +/** \brief Change directory to last known directory saved in appdata + \return whether the directory could be saved +*/ +boolean I_UseSavedWadDirectory(void); + /** \brief First Joystick's events */ void I_GetJoystickEvents(void); diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 1154e332e..d37e688ad 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -34,6 +34,7 @@ #ifdef _WIN32 #define RPC_NO_WINDOWS_H #include +#include #include "../doomtype.h" typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD); @@ -3758,6 +3759,66 @@ static const char *locateWad(void) return NULL; } +#ifdef _WIN32 +static FILE * openAppDataFile(const char *filename, const char *mode) +{ + FILE * file = NULL; + char kdir[MAX_PATH]; + + if (SHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0, "SRB2Kart", kdir) == S_OK) + { + strcat(kdir, "\\"); + strcat(kdir, filename); + file = fopen(kdir, mode); + } + + return file; +} +#endif + +void I_SaveCurrentWadDirectory(void) +{ +#ifdef _WIN32 + char path[MAX_PATH]; + FILE * file = openAppDataFile("lastwaddir", "w"); + if (file != NULL) + { + if (strcmp(srb2path, ".") == 0) + { + GetCurrentDirectoryA(sizeof path, path); + fputs(path, file); + } + else + { + fputs(srb2path, file); + } + fclose(file); + } +#endif +} + +boolean I_UseSavedWadDirectory(void) +{ + boolean ok = false; +#ifdef _WIN32 + char path[MAX_PATH]; + FILE * file = openAppDataFile("lastwaddir", "r"); + if (file != NULL) + { + if (fgets(path, sizeof path, file) != NULL) + { + I_OutputMsg( + "Going to the last known directory with srb2.srb: %s\n", + path); + ok = SetCurrentDirectoryA(path); + } + fclose(file); + } +#endif + return ok; +} + const char *I_LocateWad(void) { const char *waddir; From 5af0eb707639871af1fe81d2677dbe97f7f52b00 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 17 Aug 2020 23:33:55 -0400 Subject: [PATCH 063/193] Don't show gametype when watching replays --- src/discord.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/discord.c b/src/discord.c index 81a5f7f59..a0d0db96d 100644 --- a/src/discord.c +++ b/src/discord.c @@ -318,7 +318,8 @@ void DRPC_UpdatePresence(void) } // Gametype info - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) + && !demo.playback) { if (modeattacking) discordPresence.details = "Time Attack"; From 45091555b25e6b489b26004d144afa8a8dda6bb2 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Aug 2020 21:22:25 -0700 Subject: [PATCH 064/193] hate hate hate hate --- src/d_main.c | 48 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index d05e0a591..9d5d29a29 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -845,16 +845,13 @@ static inline void D_CleanFile(char **filearray) // Identify the SRB2 version, and IWAD file to use. // ========================================================================== -static boolean AddIWAD(char *srb2wad1, char *srb2wad2) +static boolean AddIWAD(void) { - if (srb2wad2 != NULL && FIL_ReadFileOK(srb2wad2)) + char * path = va(pandf,srb2path,"srb2.srb"); + + if (FIL_ReadFileOK(path)) { - D_AddFile(srb2wad2, startupwadfiles); - return true; - } - else if (srb2wad1 != NULL && FIL_ReadFileOK(srb2wad1)) - { - D_AddFile(srb2wad1, startupwadfiles); + D_AddFile(path, startupwadfiles); return true; } else @@ -865,7 +862,6 @@ static boolean AddIWAD(char *srb2wad1, char *srb2wad2) static void IdentifyVersion(void) { - char *srb2wad1, *srb2wad2; const char *srb2waddir = NULL; #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) @@ -889,47 +885,27 @@ static void IdentifyVersion(void) #ifdef _arch_dreamcast srb2waddir = "/cd"; #else - srb2waddir = "."; + srb2waddir = srb2path; #endif } } -#if defined (macintosh) && !defined (HAVE_SDL) - // cwd is always "/" when app is dbl-clicked - if (!stricmp(srb2waddir, "/")) - srb2waddir = I_GetWadDir(); -#endif - // Commercial. - srb2wad1 = malloc(strlen(srb2waddir)+1+8+1); - srb2wad2 = malloc(strlen(srb2waddir)+1+8+1); - if (srb2wad1 == NULL && srb2wad2 == NULL) - I_Error("No more free memory to look in %s", srb2waddir); - if (srb2wad1 != NULL) - sprintf(srb2wad1, pandf, srb2waddir, "srb2.srb"); - if (srb2wad2 != NULL) - sprintf(srb2wad2, pandf, srb2waddir, "srb2.wad"); - - // will be overwritten in case of -cdrom or unix/win home - snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir); - configfile[sizeof configfile - 1] = '\0'; - // Load the IWAD - if (AddIWAD(srb2wad1, srb2wad2)) + if (AddIWAD()) { I_SaveCurrentWadDirectory(); } else { - if (!( I_UseSavedWadDirectory() && AddIWAD(srb2wad1, srb2wad2) )) + if (!( I_UseSavedWadDirectory() && AddIWAD() )) { - I_Error("SRB2.SRB/SRB2.WAD not found! Expected in %s, ss files: %s or %s\n", srb2waddir, srb2wad1, srb2wad2); + I_Error("SRB2.SRB not found! Expected in %s\n", srb2waddir); } } - if (srb2wad1) - free(srb2wad1); - if (srb2wad2) - free(srb2wad2); + // will be overwritten in case of -cdrom or unix/win home + snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir); + configfile[sizeof configfile - 1] = '\0'; // if you change the ordering of this or add/remove a file, be sure to update the md5 // checking in D_SRB2Main From 2492bfe335dba30e6ba9f3474a8c24c9f9cf1b3d Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Aug 2020 21:27:59 -0700 Subject: [PATCH 065/193] Copy the saved wad directory to srb2path --- src/sdl/i_system.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d37e688ad..419204cd1 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -3802,16 +3802,15 @@ boolean I_UseSavedWadDirectory(void) { boolean ok = false; #ifdef _WIN32 - char path[MAX_PATH]; FILE * file = openAppDataFile("lastwaddir", "r"); if (file != NULL) { - if (fgets(path, sizeof path, file) != NULL) + if (fgets(srb2path, sizeof srb2path, file) != NULL) { I_OutputMsg( "Going to the last known directory with srb2.srb: %s\n", - path); - ok = SetCurrentDirectoryA(path); + srb2path); + ok = SetCurrentDirectoryA(srb2path); } fclose(file); } From b53181a62ad18ffb2ed994faf65c6afc23d2b64d Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 19 Aug 2020 18:01:56 -0400 Subject: [PATCH 066/193] COnfirm box now respects rebindable accelerate and brake on keyboards --- src/d_clisrv.c | 53 ++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2212a64bd..5b02fc5e9 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1980,35 +1980,38 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET -static void M_ConfirmConnect(INT32 ch) +static void M_ConfirmConnect(event_t *ev) { - if (ch == ' ' || ch == 'y' || ch == KEY_ENTER) + if (ev->type == ev_keydown) { - if (totalfilesrequestednum > 0) + if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[gc_accelerate][0] || ev->data1 == gamecontrol[gc_accelerate][1]) { -#ifdef HAVE_CURL - if (http_source[0] == '\0' || curl_failedwebdownload) -#endif + if (totalfilesrequestednum > 0) { - if (CL_SendRequestFile()) - { - cl_mode = CL_DOWNLOADFILES; - } - } #ifdef HAVE_CURL - else - cl_mode = CL_PREPAREHTTPFILES; + if (http_source[0] == '\0' || curl_failedwebdownload) #endif + { + if (CL_SendRequestFile()) + { + cl_mode = CL_DOWNLOADFILES; + } + } +#ifdef HAVE_CURL + else + cl_mode = CL_PREPAREHTTPFILES; +#endif + } + else + cl_mode = CL_LOADFILES; + + M_ClearMenus(true); + } + else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[gc_brake][0] || ev->data1 == gamecontrol[gc_brake][1]) + { + cl_mode = CL_ABORTED; + M_ClearMenus(true); } - else - cl_mode = CL_LOADFILES; - - M_ClearMenus(true); - } - else if (ch == 'n' || ch == KEY_ESCAPE) - { - cl_mode = CL_ABORTED; - M_ClearMenus(true); } } @@ -2060,7 +2063,7 @@ static boolean CL_FinishedFileList(void) "You may load server addons (if any), and wait for a slot.\n" "\n" "Press ACCEL to continue or BRAKE to cancel.\n\n" - ), M_ConfirmConnect, MM_YESNO); + ), M_ConfirmConnect, MM_EVENTHANDLER); cl_mode = CL_CONFIRMCONNECT; } else @@ -2120,13 +2123,13 @@ static boolean CL_FinishedFileList(void) "You may download, load server addons, and wait for a slot.\n" "\n" "Press ACCEL to continue or BRAKE to cancel.\n\n" - ), downloadsize), M_ConfirmConnect, MM_YESNO); + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); else M_StartMessage(va(M_GetText( "Download of %s additional content is required to join.\n" "\n" "Press ACCEL to continue or BRAKE to cancel.\n\n" - ), downloadsize), M_ConfirmConnect, MM_YESNO); + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); Z_Free(downloadsize); cl_mode = CL_CONFIRMCONNECT; From 325ae01e4b4d0a6f8a1ff91f68a6bedcf6532ae6 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Aug 2020 16:46:24 -0700 Subject: [PATCH 067/193] K_DropRocketSneaker: MF2_DONTDRAW -> MFD_DONTDRAW --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 1307d9201..41e33eee1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4601,7 +4601,7 @@ void K_DropRocketSneaker(player_t *player) if (shoe->type != MT_ROCKETSNEAKER) return; //woah, not a rocketsneaker, bail! safeguard in case this gets used when you're holding non-rocketsneakers - shoe->flags2 &= ~MF2_DONTDRAW; + shoe->drawflags &= ~MFD_DONTDRAW; shoe->flags &= ~MF_NOGRAVITY; shoe->angle += ANGLE_45; From 37da22c5ebf1d108c6a09678dad3364a7b3c3387 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 19 Aug 2020 19:52:24 -0400 Subject: [PATCH 068/193] Added m_menu_mutex to M_Drawer in connection screen --- src/d_clisrv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6b8c107b5..3a7626ef5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2484,7 +2484,13 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic F_TitleScreenTicker(true); F_TitleScreenDrawer(); CL_DrawConnectionStatus(); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); //Needed for drawing messageboxes on the connection screen +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif I_UpdateNoVsync(); // page flip or blit buffer if (moviemode) M_SaveFrame(); From 240cc0b1d177c8984f82518696988c5c2cc1aa00 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Aug 2020 17:47:07 -0700 Subject: [PATCH 069/193] Rearrange time comparsion on asksent to make it underflow friendly This fixes '-connect'. NEWTICRATE*5 is subtracted from asksent after CL_LOADFILES. If this happens too early, an underflow will occur. --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3a7626ef5..e947964ca 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2414,14 +2414,14 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic // but since the network layer doesn't provide ordered packets... CL_PrepareDownloadSaveGame(tmpsave); #endif - if ((*asksent + NEWTICRATE*3) < I_GetTime() && CL_SendJoin()) + if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent && CL_SendJoin()) { *asksent = I_GetTime(); cl_mode = CL_WAITJOINRESPONSE; } break; case CL_WAITJOINRESPONSE: - if ((*asksent + NEWTICRATE*3) < I_GetTime()) + if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent && CL_SendJoin()) { cl_mode = CL_ASKJOIN; } From 328dd607d8caa46f4a5714d99def4f75ce5bb71b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Aug 2020 17:55:01 -0700 Subject: [PATCH 070/193] Fix tiny mistake with the last commit --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e947964ca..929a992c4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2421,7 +2421,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic } break; case CL_WAITJOINRESPONSE: - if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent && CL_SendJoin()) + if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent) { cl_mode = CL_ASKJOIN; } From d365ea7981da7b1b7869fba9e2fa76750db9fe93 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 18 Dec 2019 15:34:55 -0800 Subject: [PATCH 071/193] Use a random port when connecting (cherry picked from commit 8368449e6cafcd328ab017aa33f1794cbe994a1c) --- src/d_net.h | 2 ++ src/i_tcp.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/d_net.h b/src/d_net.h index cc80fcaa6..607f3e901 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -43,6 +43,8 @@ extern SINT8 nodetoplayer4[MAXNETNODES]; // Say the numplayer for this node if a extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game +extern boolean serverrunning; + INT32 Net_GetFreeAcks(boolean urgent); void Net_AckTicker(void); diff --git a/src/i_tcp.c b/src/i_tcp.c index f58aa22bc..74144d5ed 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -924,6 +924,7 @@ static boolean UDP_Socket(void) #ifdef HAVE_IPV6 const INT32 b_ipv6 = M_CheckParm("-ipv6"); #endif + const char *serv; for (s = 0; s < mysocketses; s++) @@ -939,11 +940,16 @@ static boolean UDP_Socket(void) hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; + if (serverrunning) + serv = port_name; + else + serv = NULL;/* any port */ + if (M_CheckParm("-bindaddr")) { while (M_IsNextParm()) { - gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai); + gaie = I_getaddrinfo(M_GetNextParm(), serv, &hints, &ai); if (gaie == 0) { runp = ai; @@ -964,7 +970,7 @@ static boolean UDP_Socket(void) } else { - gaie = I_getaddrinfo("0.0.0.0", port_name, &hints, &ai); + gaie = I_getaddrinfo("0.0.0.0", serv, &hints, &ai); if (gaie == 0) { runp = ai; @@ -997,7 +1003,7 @@ static boolean UDP_Socket(void) { while (M_IsNextParm()) { - gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai); + gaie = I_getaddrinfo(M_GetNextParm(), serv, &hints, &ai); if (gaie == 0) { runp = ai; @@ -1018,7 +1024,7 @@ static boolean UDP_Socket(void) } else { - gaie = I_getaddrinfo("::", port_name, &hints, &ai); + gaie = I_getaddrinfo("::", serv, &hints, &ai); if (gaie == 0) { runp = ai; From e3a383aafce7c22c85215be4cae6874a3ae7ce53 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 18 Dec 2019 15:43:29 -0800 Subject: [PATCH 072/193] Use a pointer for port_name Using strcpy is stupid because we don't know how long the argument would be. There's no need for a buffer anyway. (cherry picked from commit 5921e1a567f3819cd3f3c914b99bc6d4583d2058) --- src/i_tcp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index 74144d5ed..d7f767ba9 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -246,7 +246,7 @@ static size_t numbans = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; -static char port_name[8] = DEFAULTPORT; +static const char *port_name = DEFAULTPORT; #ifndef NONET @@ -1485,10 +1485,11 @@ boolean I_InitTcpNetwork(void) // Combined -udpport and -clientport into -port // As it was really redundant having two seperate parms that does the same thing { - if (M_IsNextParm()) - strcpy(port_name, M_GetNextParm()); - else - strcpy(port_name, "0"); + /* + If it's NULL, that's okay! Because then + we'll get a random port from getaddrinfo. + */ + port_name = M_GetNextParm(); } // parse network game options, From 2c3b30de9077735d92f3105e5c6d8e03ec017968 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 18 Dec 2019 15:47:47 -0800 Subject: [PATCH 073/193] -clientport (it's back!) and -serverport, which is an alias to -port If you ever need to, you can change the client port number. (cherry picked from commit 17749b9e3ec64f0490bcc9b7b650ca3bdcf6046a) --- src/i_tcp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index d7f767ba9..1f1cf4f22 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -246,7 +246,8 @@ static size_t numbans = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; -static const char *port_name = DEFAULTPORT; +static const char *serverport_name = DEFAULTPORT; +static const char *clientport_name;/* any port */ #ifndef NONET @@ -941,9 +942,9 @@ static boolean UDP_Socket(void) hints.ai_protocol = IPPROTO_UDP; if (serverrunning) - serv = port_name; + serv = serverport_name; else - serv = NULL;/* any port */ + serv = clientport_name; if (M_CheckParm("-bindaddr")) { @@ -985,8 +986,8 @@ static boolean UDP_Socket(void) #ifdef HAVE_MINIUPNPC if (UPNP_support) { - I_UPnP_rem(port_name, "UDP"); - I_UPnP_add(NULL, port_name, "UDP"); + I_UPnP_rem(serverport_name, "UDP"); + I_UPnP_add(NULL, serverport_name, "UDP"); } #endif } @@ -1481,16 +1482,19 @@ boolean I_InitTcpNetwork(void) if (!I_InitTcpDriver()) return false; - if (M_CheckParm("-port")) + if (M_CheckParm("-port") || M_CheckParm("-serverport")) // Combined -udpport and -clientport into -port // As it was really redundant having two seperate parms that does the same thing + /* Sorry Steel, I'm adding these back. But -udpport is a stupid name. */ { /* If it's NULL, that's okay! Because then we'll get a random port from getaddrinfo. */ - port_name = M_GetNextParm(); + serverport_name = M_GetNextParm(); } + if (M_CheckParm("-clientport")) + clientport_name = M_GetNextParm(); // parse network game options, if (M_CheckParm("-server") || dedicated) From 8ad5eaad2eee4e5f8771924fa49d86c76dd2a14f Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Wed, 19 Aug 2020 22:37:14 -0400 Subject: [PATCH 074/193] GME cleanup and fixes Just some stuff backported from SRB2 2.2, well mostly get_zlib_error function to reduce code duplication Return false if it fails to load the VGZ lump Reduce redundant calls to gme_set_equalizer Fix sometimes loading a VGZ crashing the game --- src/sdl/mixer_sound.c | 123 ++++++++++-------------------------------- 1 file changed, 29 insertions(+), 94 deletions(-) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 1617da2a3..86fc8efb8 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -110,6 +110,27 @@ static void var_cleanup(void) internal_volume = 100; } +static const char* get_zlib_error(int zErr) +{ + switch (zErr) + { + case Z_ERRNO: + return "Z_ERRNO"; + case Z_STREAM_ERROR: + return "Z_STREAM_ERROR"; + case Z_DATA_ERROR: + return "Z_DATA_ERROR"; + case Z_MEM_ERROR: + return "Z_MEM_ERROR"; + case Z_BUF_ERROR: + return "Z_BUF_ERROR"; + case Z_VERSION_ERROR: + return "Z_VERSION_ERROR"; + default: + return "unknown error"; + } +} + /// ------------------------ /// Audio System /// ------------------------ @@ -371,51 +392,11 @@ void *I_GetSfx(sfxinfo_t *sfx) } } else - { - const char *errorType; - switch (zErr) - { - case Z_ERRNO: - errorType = "Z_ERRNO"; break; - case Z_STREAM_ERROR: - errorType = "Z_STREAM_ERROR"; break; - case Z_DATA_ERROR: - errorType = "Z_DATA_ERROR"; break; - case Z_MEM_ERROR: - errorType = "Z_MEM_ERROR"; break; - case Z_BUF_ERROR: - errorType = "Z_BUF_ERROR"; break; - case Z_VERSION_ERROR: - errorType = "Z_VERSION_ERROR"; break; - default: - errorType = "unknown error"; - } - CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg); - } + CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", get_zlib_error(zErr), stream.msg); (void)inflateEnd(&stream); } else // Hold up, zlib's got a problem - { - const char *errorType; - switch (zErr) - { - case Z_ERRNO: - errorType = "Z_ERRNO"; break; - case Z_STREAM_ERROR: - errorType = "Z_STREAM_ERROR"; break; - case Z_DATA_ERROR: - errorType = "Z_DATA_ERROR"; break; - case Z_MEM_ERROR: - errorType = "Z_MEM_ERROR"; break; - case Z_BUF_ERROR: - errorType = "Z_BUF_ERROR"; break; - case Z_VERSION_ERROR: - errorType = "Z_VERSION_ERROR"; break; - default: - errorType = "unknown error"; - } - CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg); - } + CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", get_zlib_error(zErr), stream.msg); Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up #else return NULL; // No zlib support @@ -945,73 +926,25 @@ boolean I_LoadSong(char *data, size_t len) // Run GME on new data if (!gme_open_data(inflatedData, inflatedLen, &gme, 44100)) { - gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; - gme_start_track(gme, 0); - current_track = 0; - gme_set_equalizer(gme, &eq); - Mix_HookMusic(mix_gme, gme); Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around return true; } } else - { - const char *errorType; - switch (zErr) - { - case Z_ERRNO: - errorType = "Z_ERRNO"; break; - case Z_STREAM_ERROR: - errorType = "Z_STREAM_ERROR"; break; - case Z_DATA_ERROR: - errorType = "Z_DATA_ERROR"; break; - case Z_MEM_ERROR: - errorType = "Z_MEM_ERROR"; break; - case Z_BUF_ERROR: - errorType = "Z_BUF_ERROR"; break; - case Z_VERSION_ERROR: - errorType = "Z_VERSION_ERROR"; break; - default: - errorType = "unknown error"; - } - CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg); - } + CONS_Alert(CONS_ERROR, "Encountered %s when running inflate: %s\n", get_zlib_error(zErr), stream.msg); (void)inflateEnd(&stream); } else // Hold up, zlib's got a problem - { - const char *errorType; - switch (zErr) - { - case Z_ERRNO: - errorType = "Z_ERRNO"; break; - case Z_STREAM_ERROR: - errorType = "Z_STREAM_ERROR"; break; - case Z_DATA_ERROR: - errorType = "Z_DATA_ERROR"; break; - case Z_MEM_ERROR: - errorType = "Z_MEM_ERROR"; break; - case Z_BUF_ERROR: - errorType = "Z_BUF_ERROR"; break; - case Z_VERSION_ERROR: - errorType = "Z_VERSION_ERROR"; break; - default: - errorType = "unknown error"; - } - CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg); - } + CONS_Alert(CONS_ERROR, "Encountered %s when running inflateInit: %s\n", get_zlib_error(zErr), stream.msg); Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up + return false; #else CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); - return true; + return false; #endif } else if (!gme_open_data(data, len, &gme, 44100)) - { - gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; - gme_set_equalizer(gme, &eq); return true; - } #endif rw = SDL_RWFromMem(data, len); @@ -1082,6 +1015,8 @@ boolean I_PlaySong(boolean looping) #ifdef HAVE_LIBGME if (gme) { + gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; + gme_set_equalizer(gme, &eq); gme_start_track(gme, 0); current_track = 0; Mix_HookMusic(mix_gme, gme); From 538ec7e61d3516f0398f052ec23856689a16702b Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 20 Aug 2020 00:54:18 -0400 Subject: [PATCH 075/193] Well actually, lets fix all comparisons of askset to use the same comparison If we initialize to I_GetTime() - NEWTICRATE*3 it never underflows and everything is good --- src/d_clisrv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 929a992c4..e22ab463b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2271,7 +2271,7 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) } // Ask the info to the server (askinfo packet) - if (*asksent + NEWTICRATE < I_GetTime()) + if ((I_GetTime() - NEWTICRATE) >= *asksent) { SendAskInfo(servernode); *asksent = I_GetTime(); @@ -2315,7 +2315,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic case CL_ASKFULLFILELIST: if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved cl_mode = CL_CHECKFILES; - else if (fileneedednum != cl_lastcheckedfilecount || *asksent + NEWTICRATE < I_GetTime()) + else if (fileneedednum != cl_lastcheckedfilecount || (I_GetTime() - NEWTICRATE) >= *asksent) { if (CL_AskFileList(fileneedednum)) { @@ -2388,7 +2388,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic case CL_LOADFILES: if (CL_LoadServerFiles()) { - *asksent = I_GetTime() - (NEWTICRATE*5); //This ensure the first join ask is right away + *asksent = I_GetTime() - (NEWTICRATE*3); //This ensure the first join ask is right away firstconnectattempttime = I_GetTime(); cl_mode = CL_ASKJOIN; } @@ -2557,7 +2557,7 @@ static void CL_ConnectToServer(void) pnumnodes = 1; oldtic = I_GetTime() - 1; #ifndef NONET - asksent = (tic_t) - TICRATE; + asksent = I_GetTime() - NEWTICRATE*3; i = SL_SearchServer(servernode); From 49fa372b8da53363cdb93c04b554c29df02b6d16 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 20 Aug 2020 13:41:03 +0800 Subject: [PATCH 076/193] Instantaneous lookback camera (v2 backport) --- src/p_user.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 03f8f0fbe..9926b9803 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7487,13 +7487,14 @@ void P_ResetCamera(player_t *player, camera_t *thiscam) boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled) { - static UINT8 lookbackdelay[4] = {0,0,0,0}; + static boolean lookbackactive[MAXSPLITSCREENPLAYERS]; + static UINT8 lookbackdelay[MAXSPLITSCREENPLAYERS]; UINT8 num; angle_t angle = 0, focusangle = 0, focusaiming = 0; - fixed_t x, y, z, dist, height, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; + fixed_t x, y, z, dist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; fixed_t pan, xpan, ypan; INT32 camrotate; - boolean camstill, lookback; + boolean camstill, lookback, lookbackdown; UINT8 timeover; mobj_t *mo; fixed_t f1, f2; @@ -7684,15 +7685,19 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camstill = true; else if (lookback || lookbackdelay[num]) // SRB2kart - Camera flipper { +#define MAXLOOKBACKDELAY 2 camspeed = FRACUNIT; if (lookback) { camrotate += 180; - lookbackdelay[num] = 2; + lookbackdelay[num] = MAXLOOKBACKDELAY; } else lookbackdelay[num]--; } + lookbackdown = (lookbackdelay[num] == MAXLOOKBACKDELAY) != lookbackactive[num]; + lookbackactive[num] = (lookbackdelay[num] == MAXLOOKBACKDELAY); +#undef MAXLOOKBACKDELAY if (mo->eflags & MFE_VERTICALFLIP) camheight += thiscam->height; @@ -7735,8 +7740,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall thiscam->angle = angle; } - height = camheight; - // sets ideal cam pos dist = camdist; @@ -7745,10 +7748,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist += abs(thiscam->momz)/4; if (player->kartstuff[k_boostcam]) - { dist -= FixedMul(11*dist/16, player->kartstuff[k_boostcam]); - height -= FixedMul(height, player->kartstuff[k_boostcam]); - } x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); @@ -8070,6 +8070,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall thiscam->aiming = ANGLE_22h; } + if (lookbackdown) + P_MoveChaseCamera(player, thiscam, false); + return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming); } From 24244183902156f47d0d656d704680f62cc22532 Mon Sep 17 00:00:00 2001 From: FlykeSpice Date: Thu, 20 Aug 2020 11:33:32 -0400 Subject: [PATCH 077/193] Remove ESLOPE #ifdef(backport from srb2 2.2) --- src/am_map.c | 32 ----- src/dehacked.c | 2 - src/doomdef.h | 2 - src/k_kart.c | 4 - src/lua_baselib.c | 6 - src/lua_libs.h | 2 - src/lua_maplib.c | 26 ---- src/lua_mobjlib.c | 8 -- src/lua_script.c | 14 -- src/m_cheat.c | 28 ---- src/p_enemy.c | 5 - src/p_local.h | 2 - src/p_map.c | 56 -------- src/p_maputl.c | 36 ----- src/p_maputl.h | 2 - src/p_mobj.c | 154 --------------------- src/p_mobj.h | 2 - src/p_saveg.c | 10 -- src/p_setup.c | 6 - src/p_slopes.c | 2 - src/p_slopes.h | 2 - src/p_spec.c | 12 -- src/p_user.c | 88 ------------ src/r_bsp.c | 70 ---------- src/r_defs.h | 10 -- src/r_draw.c | 2 - src/r_draw.h | 4 - src/r_draw8.c | 2 - src/r_main.c | 2 - src/r_plane.c | 18 --- src/r_plane.h | 6 - src/r_segs.c | 333 ---------------------------------------------- src/r_things.c | 12 -- 33 files changed, 960 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 5e73d2ec4..91c68233c 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -862,10 +862,8 @@ static inline void AM_drawWalls(void) { size_t i; static mline_t l; -#ifdef ESLOPE fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends fixed_t backf1 = 0, backf2 = 0, backc1 = 0, backc2 = 0; // back floor ceiling ends -#endif for (i = 0; i < numlines; i++) { @@ -873,7 +871,6 @@ static inline void AM_drawWalls(void) l.a.y = lines[i].v1->y >> FRACTOMAPBITS; l.b.x = lines[i].v2->x >> FRACTOMAPBITS; l.b.y = lines[i].v2->y >> FRACTOMAPBITS; -#ifdef ESLOPE #define SLOPEPARAMS(slope, end1, end2, normalheight) \ if (slope) { \ end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y); \ @@ -888,7 +885,6 @@ static inline void AM_drawWalls(void) SLOPEPARAMS(lines[i].backsector->c_slope, backc1, backc2, lines[i].backsector->ceilingheight) } #undef SLOPEPARAMS -#endif if (!lines[i].backsector) // 1-sided { @@ -897,19 +893,11 @@ static inline void AM_drawWalls(void) else AM_drawMline(&l, WALLCOLORS); } -#ifdef ESLOPE else if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier || (frontf1 == frontc1 && frontf2 == frontc2)) // Front is thok barrier { if (backf1 == backc1 && backf2 == backc2 && frontf1 == frontc1 && frontf2 == frontc2) // BOTH are thok barriers -#else - else if (lines[i].backsector->floorheight == lines[i].backsector->ceilingheight // Back is thok barrier - || lines[i].frontsector->floorheight == lines[i].frontsector->ceilingheight) // Front is thok barrier - { - if (lines[i].backsector->floorheight == lines[i].backsector->ceilingheight - && lines[i].frontsector->floorheight == lines[i].frontsector->ceilingheight) // BOTH are thok barriers -#endif { if (lines[i].flags & ML_NOCLIMB) AM_drawMline(&l, NOCLIMBTSWALLCOLORS); @@ -927,20 +915,10 @@ static inline void AM_drawWalls(void) else { if (lines[i].flags & ML_NOCLIMB) { -#ifdef ESLOPE if (backf1 != frontf1 || backf2 != frontf2) { -#else - if (lines[i].backsector->floorheight - != lines[i].frontsector->floorheight) { -#endif AM_drawMline(&l, NOCLIMBFDWALLCOLORS); // floor level change } -#ifdef ESLOPE else if (backc1 != frontc1 || backc2 != frontc2) { -#else - else if (lines[i].backsector->ceilingheight - != lines[i].frontsector->ceilingheight) { -#endif AM_drawMline(&l, NOCLIMBCDWALLCOLORS); // ceiling level change } else @@ -948,20 +926,10 @@ static inline void AM_drawWalls(void) } else { -#ifdef ESLOPE if (backf1 != frontf1 || backf2 != frontf2) { -#else - if (lines[i].backsector->floorheight - != lines[i].frontsector->floorheight) { -#endif AM_drawMline(&l, FDWALLCOLORS); // floor level change } -#ifdef ESLOPE else if (backc1 != frontc1 || backc2 != frontc2) { -#else - else if (lines[i].backsector->ceilingheight - != lines[i].frontsector->ceilingheight) { -#endif AM_drawMline(&l, CDWALLCOLORS); // ceiling level change } else diff --git a/src/dehacked.c b/src/dehacked.c index 96d82fff4..29beb15ee 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8622,13 +8622,11 @@ struct { {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. -#ifdef ESLOPE // Slope flags {"SL_NOPHYSICS",SL_NOPHYSICS}, // Don't do momentum adjustment with this slope {"SL_NODYNAMIC",SL_NODYNAMIC}, // Slope will never need to move during the level, so don't fuss with recalculating it {"SL_ANCHORVERTEX",SL_ANCHORVERTEX},// Slope is using a Slope Vertex Thing to anchor its position {"SL_VERTEXSLOPE",SL_VERTEXSLOPE}, // Slope is built from three Slope Vertex Things -#endif // Angles {"ANG1",ANG1}, diff --git a/src/doomdef.h b/src/doomdef.h index f8eade65a..e7abf0906 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -606,11 +606,9 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Kalaron/Eternity Engine slope code (SRB2CB ported) #define ESLOPE -#ifdef ESLOPE /// Backwards compatibility with SRB2CB's slope linedef types. /// \note A simple shim that prints a warning. #define ESLOPE_TYPESHIM -#endif /// Delete file while the game is running. /// \note EXTREMELY buggy, tends to crash game. diff --git a/src/k_kart.c b/src/k_kart.c index 2e8ca1773..5c943579c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2893,14 +2893,12 @@ void K_SpawnBoostTrail(player_t *player) { newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); -#ifdef ESLOPE if (player->mo->standingslope) { ground = P_GetZAt(player->mo->standingslope, newx, newy); if (player->mo->eflags & MFE_VERTICALFLIP) ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); } -#endif flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); P_SetTarget(&flame->target, player->mo); @@ -3604,9 +3602,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) if (mo->eflags & MFE_SPRUNG) return; -#ifdef ESLOPE mo->standingslope = NULL; -#endif mo->eflags |= MFE_SPRUNG; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d7292cd60..56b1a5a54 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -14,9 +14,7 @@ #ifdef HAVE_BLUA #include "p_local.h" #include "p_setup.h" // So we can have P_SetupLevelSky -#ifdef ESLOPE #include "p_slopes.h" // P_GetZAt -#endif #include "z_zone.h" #include "r_main.h" #include "r_things.h" @@ -1575,7 +1573,6 @@ static int lib_evCrumbleChain(lua_State *L) return 0; } -#ifdef ESLOPE // P_SLOPES //////////// @@ -1591,7 +1588,6 @@ static int lib_pGetZAt(lua_State *L) lua_pushfixed(L, P_GetZAt(slope, x, y)); return 1; } -#endif // R_DEFS //////////// @@ -3083,10 +3079,8 @@ static luaL_Reg lib[] = { {"P_StartQuake",lib_pStartQuake}, {"EV_CrumbleChain",lib_evCrumbleChain}, -#ifdef ESLOPE // p_slopes {"P_GetZAt",lib_pGetZAt}, -#endif // r_defs {"R_PointToAngle",lib_rPointToAngle}, diff --git a/src/lua_libs.h b/src/lua_libs.h index cb1cb49c7..00dd9c858 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -40,11 +40,9 @@ extern lua_State *gL; #define META_SUBSECTOR "SUBSECTOR_T*" #define META_SECTOR "SECTOR_T*" #define META_FFLOOR "FFLOOR_T*" -#ifdef ESLOPE #define META_SLOPE "PSLOPE_T*" #define META_VECTOR2 "VECTOR2_T" #define META_VECTOR3 "VECTOR3_T" -#endif #define META_MAPHEADER "MAPHEADER_T*" #define META_CVAR "CONSVAR_T*" diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 19292b3d6..41875ea98 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -16,9 +16,7 @@ #include "p_local.h" #include "p_setup.h" #include "z_zone.h" -#ifdef ESLOPE #include "p_slopes.h" -#endif #include "r_main.h" #include "lua_script.h" @@ -43,13 +41,9 @@ enum sector_e { sector_heightsec, sector_camsec, sector_lines, -#ifdef ESLOPE sector_ffloors, sector_fslope, sector_cslope -#else - sector_ffloors -#endif }; static const char *const sector_opt[] = { @@ -66,10 +60,8 @@ static const char *const sector_opt[] = { "camsec", "lines", "ffloors", -#ifdef ESLOPE "f_slope", "c_slope", -#endif NULL}; enum subsector_e { @@ -175,10 +167,8 @@ enum ffloor_e { ffloor_toplightlevel, ffloor_bottomheight, ffloor_bottompic, -#ifdef ESLOPE ffloor_tslope, ffloor_bslope, -#endif ffloor_sector, ffloor_flags, ffloor_master, @@ -195,10 +185,8 @@ static const char *const ffloor_opt[] = { "toplightlevel", "bottomheight", "bottompic", -#ifdef ESLOPE "t_slope", "b_slope", -#endif "sector", // secnum pushed as control sector userdata "flags", "master", // control linedef @@ -208,7 +196,6 @@ static const char *const ffloor_opt[] = { "alpha", NULL}; -#ifdef ESLOPE enum slope_e { slope_valid = 0, slope_o, @@ -247,7 +234,6 @@ static const char *const vector_opt[] = { "y", "z", NULL}; -#endif static const char *const array_opt[] ={"iterate",NULL}; static const char *const valid_opt[] ={"valid",NULL}; @@ -463,14 +449,12 @@ static int sector_get(lua_State *L) LUA_PushUserdata(L, sector->ffloors, META_FFLOOR); lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateFFloors and sector->ffloors as upvalues for the function return 1; -#ifdef ESLOPE case sector_fslope: // f_slope LUA_PushUserdata(L, sector->f_slope, META_SLOPE); return 1; case sector_cslope: // c_slope LUA_PushUserdata(L, sector->c_slope, META_SLOPE); return 1; -#endif } return 0; } @@ -495,10 +479,8 @@ static int sector_set(lua_State *L) case sector_heightsec: // heightsec case sector_camsec: // camsec case sector_ffloors: // ffloors -#ifdef ESLOPE case sector_fslope: // f_slope case sector_cslope: // c_slope -#endif default: return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]); case sector_floorheight: { // floorheight @@ -1134,14 +1116,12 @@ static int ffloor_get(lua_State *L) lua_pushlstring(L, levelflat->name, i); return 1; } -#ifdef ESLOPE case ffloor_tslope: LUA_PushUserdata(L, *ffloor->t_slope, META_SLOPE); return 1; case ffloor_bslope: LUA_PushUserdata(L, *ffloor->b_slope, META_SLOPE); return 1; -#endif case ffloor_sector: LUA_PushUserdata(L, §ors[ffloor->secnum], META_SECTOR); return 1; @@ -1183,10 +1163,8 @@ static int ffloor_set(lua_State *L) switch(field) { case ffloor_valid: // valid -#ifdef ESLOPE case ffloor_tslope: // t_slope case ffloor_bslope: // b_slope -#endif case ffloor_sector: // sector case ffloor_master: // master case ffloor_target: // target @@ -1247,7 +1225,6 @@ static int ffloor_set(lua_State *L) return 0; } -#ifdef ESLOPE static int slope_get(lua_State *L) { pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE)); @@ -1422,7 +1399,6 @@ static int vector3_get(lua_State *L) return 0; } -#endif static int lib_getMapheaderinfo(lua_State *L) { @@ -1614,7 +1590,6 @@ int LUA_MapLib(lua_State *L) lua_setfield(L, -2, "__newindex"); lua_pop(L, 1); -#ifdef ESLOPE luaL_newmetatable(L, META_SLOPE); lua_pushcfunction(L, slope_get); lua_setfield(L, -2, "__index"); @@ -1632,7 +1607,6 @@ int LUA_MapLib(lua_State *L) lua_pushcfunction(L, vector3_get); lua_setfield(L, -2, "__index"); lua_pop(L, 1); -#endif luaL_newmetatable(L, META_MAPHEADER); lua_pushcfunction(L, mapheaderinfo_get); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index dfb344e34..1361f8231 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -82,9 +82,7 @@ enum mobj_e { mobj_extravalue2, mobj_cusval, mobj_cvmem, -#ifdef ESLOPE mobj_standingslope, -#endif mobj_colorized }; @@ -145,9 +143,7 @@ static const char *const mobj_opt[] = { "extravalue2", "cusval", "cvmem", -#ifdef ESLOPE "standingslope", -#endif "colorized", NULL}; @@ -352,11 +348,9 @@ static int mobj_get(lua_State *L) case mobj_cvmem: lua_pushinteger(L, mo->cvmem); break; -#ifdef ESLOPE case mobj_standingslope: LUA_PushUserdata(L, mo->standingslope, META_SLOPE); break; -#endif case mobj_colorized: lua_pushboolean(L, mo->colorized); break; @@ -670,10 +664,8 @@ static int mobj_set(lua_State *L) case mobj_cvmem: mo->cvmem = luaL_checkinteger(L, 3); break; -#ifdef ESLOPE case mobj_standingslope: return NOSET; -#endif case mobj_colorized: mo->colorized = luaL_checkboolean(L, 3); break; diff --git a/src/lua_script.c b/src/lua_script.c index 6961f2e97..7c951efb3 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -22,9 +22,7 @@ #include "byteptr.h" #include "p_saveg.h" #include "p_local.h" -#ifdef ESLOPE #include "p_slopes.h" // for P_SlopeById -#endif #ifdef LUA_ALLOW_BYTECODE #include "d_netfil.h" // for LUA_DumpFile #endif @@ -472,9 +470,7 @@ enum ARCH_SIDE, ARCH_SUBSECTOR, ARCH_SECTOR, -#ifdef ESLOPE ARCH_SLOPE, -#endif ARCH_MAPHEADER, ARCH_TEND=0xFF, @@ -494,9 +490,7 @@ static const struct { {META_SIDE, ARCH_SIDE}, {META_SUBSECTOR,ARCH_SUBSECTOR}, {META_SECTOR, ARCH_SECTOR}, -#ifdef ESLOPE {META_SLOPE, ARCH_SLOPE}, -#endif {META_MAPHEADER, ARCH_MAPHEADER}, {NULL, ARCH_NULL} }; @@ -701,7 +695,6 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) } break; } -#ifdef ESLOPE case ARCH_SLOPE: { pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex)); @@ -713,7 +706,6 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) } break; } -#endif case ARCH_MAPHEADER: { mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex)); @@ -915,7 +907,6 @@ static UINT8 ArchiveValueDemo(int TABLESINDEX, int myindex) } break; } -#ifdef ESLOPE case ARCH_SLOPE: { pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex)); @@ -927,7 +918,6 @@ static UINT8 ArchiveValueDemo(int TABLESINDEX, int myindex) } break; } -#endif case ARCH_MAPHEADER: { mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex)); @@ -1233,11 +1223,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_SECTOR: LUA_PushUserdata(gL, §ors[READUINT16(save_p)], META_SECTOR); break; -#ifdef ESLOPE case ARCH_SLOPE: LUA_PushUserdata(gL, P_SlopeById(READUINT16(save_p)), META_SLOPE); break; -#endif case ARCH_MAPHEADER: LUA_PushUserdata(gL, mapheaderinfo[READUINT16(save_p)], META_MAPHEADER); break; @@ -1336,11 +1324,9 @@ static UINT8 UnArchiveValueDemo(int TABLESINDEX, char field[1024]) case ARCH_SECTOR: LUA_PushUserdata(gL, §ors[READUINT16(demo_p)], META_SECTOR); break; -#ifdef ESLOPE case ARCH_SLOPE: LUA_PushUserdata(gL, P_SlopeById(READUINT16(demo_p)), META_SLOPE); break; -#endif case ARCH_MAPHEADER: LUA_PushUserdata(gL, mapheaderinfo[READUINT16(demo_p)], META_MAPHEADER); break; diff --git a/src/m_cheat.c b/src/m_cheat.c index e7e877ada..a884ff7e4 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -862,13 +862,9 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling) if (ceiling) { -#ifdef ESLOPE // Truncate position to match where mapthing would be when spawned // (this applies to every further P_GetZAt call as well) fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->ceilingheight; -#else - fixed_t cheight = sec->ceilingheight; -#endif if (((cheight - player->mo->z - player->mo->height)>>FRACBITS) >= (1 << (16-ZSHIFT))) { @@ -879,11 +875,7 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling) } else { -#ifdef ESLOPE fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight; -#else - fixed_t fheight = sec->floorheight; -#endif if (((player->mo->z - fheight)>>FRACBITS) >= (1 << (16-ZSHIFT))) { CONS_Printf(M_GetText("Sorry, you're too %s to place this object (max: %d %s).\n"), M_GetText("high"), @@ -931,20 +923,12 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c mt->y = (INT16)(player->mo->y>>FRACBITS); if (ceiling) { -#ifdef ESLOPE fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, mt->x << FRACBITS, mt->y << FRACBITS) : sec->ceilingheight; -#else - fixed_t cheight = sec->ceilingheight; -#endif mt->options = (UINT16)((cheight - player->mo->z - player->mo->height)>>FRACBITS); } else { -#ifdef ESLOPE fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) : sec->floorheight; -#else - fixed_t fheight = sec->floorheight; -#endif mt->options = (UINT16)((player->mo->z - fheight)>>FRACBITS); } mt->options <<= ZSHIFT; @@ -1001,11 +985,7 @@ void OP_NightsObjectplace(player_t *player) UINT16 angle = (UINT16)(player->anotherflyangle % 360); INT16 temp = (INT16)FixedInt(AngleFixed(player->mo->angle)); // Traditional 2D Angle sector_t *sec = player->mo->subsector->sector; -#ifdef ESLOPE fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight; -#else - fixed_t fheight = sec->floorheight; -#endif player->pflags |= PF_ATTACKDOWN; @@ -1162,20 +1142,12 @@ void OP_ObjectplaceMovement(player_t *player) if (!!(mobjinfo[op_currentthing].flags & MF_SPAWNCEILING) ^ !!(cv_opflags.value & MTF_OBJECTFLIP)) { -#ifdef ESLOPE fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->ceilingheight; -#else - fixed_t cheight = sec->ceilingheight; -#endif op_displayflags = (UINT16)((cheight - player->mo->z - mobjinfo[op_currentthing].height)>>FRACBITS); } else { -#ifdef ESLOPE fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight; -#else - fixed_t fheight = sec->floorheight; -#endif op_displayflags = (UINT16)((player->mo->z - fheight)>>FRACBITS); } op_displayflags <<= ZSHIFT; diff --git a/src/p_enemy.c b/src/p_enemy.c index a3bf5491b..c6ac04973 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5795,13 +5795,8 @@ void A_MixUp(mobj_t *actor) P_SetThingPosition(players[i].mo); -#ifdef ESLOPE players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); -#else - players[i].mo->floorz = players[i].mo->subsector->sector->floorheight; - players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight; -#endif P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y); } diff --git a/src/p_local.h b/src/p_local.h index 7216326d0..26035817d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -328,9 +328,7 @@ extern mobj_t *tmfloorthing, *tmhitthing, *tmthing; extern camera_t *mapcampointer; extern fixed_t tmx; extern fixed_t tmy; -#ifdef ESLOPE extern pslope_t *tmfloorslope, *tmceilingslope; -#endif /* cphipps 2004/08/30 */ extern void P_MapStart(void); diff --git a/src/p_map.c b/src/p_map.c index 4664703d4..369b3856b 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -31,9 +31,7 @@ #include "r_splats.h" -#ifdef ESLOPE #include "p_slopes.h" -#endif #include "z_zone.h" @@ -56,9 +54,7 @@ fixed_t tmfloorz, tmceilingz; static fixed_t tmdropoffz, tmdrpoffceilz; // drop-off floor/ceiling heights mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz is from a sector mobj_t *tmhitthing; // the solid thing you bumped into (for collisions) -#ifdef ESLOPE pslope_t *tmfloorslope, *tmceilingslope; -#endif // keep track of the line that lowers the ceiling, // so missiles don't explode against sky hack walls @@ -135,9 +131,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) return false; } -#ifdef ESLOPE object->standingslope = NULL; // Okay, now we can't return - no launching off at silly angles for you. -#endif object->eflags |= MFE_SPRUNG; // apply this flag asap! spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify @@ -275,9 +269,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) zdist = object->z - spring->z; } -#ifdef ESLOPE object->standingslope = NULL; // No launching off at silly angles for you. -#endif switch (spring->type) { @@ -1758,9 +1750,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->z + thing->height > tmfloorz) { tmfloorz = thing->z + thing->height; -#ifdef ESLOPE tmfloorslope = NULL; -#endif } return true; } @@ -1777,17 +1767,13 @@ static boolean PIT_CheckThing(mobj_t *thing) && tmthing->z + tmthing->height < tmthing->ceilingz) { tmfloorz = tmceilingz = topz; // block while in air -#ifdef ESLOPE tmceilingslope = NULL; -#endif tmfloorthing = thing; // needed for side collision } else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height) { tmceilingz = topz; -#ifdef ESLOPE tmceilingslope = NULL; -#endif tmfloorthing = thing; // thing we may stand on } } @@ -1801,9 +1787,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->z < tmceilingz) { tmceilingz = thing->z; -#ifdef ESLOPE tmceilingslope = NULL; -#endif } return true; } @@ -1820,17 +1804,13 @@ static boolean PIT_CheckThing(mobj_t *thing) && tmthing->z > tmthing->floorz) { tmfloorz = tmceilingz = topz; // block while in air -#ifdef ESLOPE tmfloorslope = NULL; -#endif tmfloorthing = thing; // needed for side collision } else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z) { tmfloorz = topz; -#ifdef ESLOPE tmfloorslope = NULL; -#endif tmfloorthing = thing; // thing we may stand on } } @@ -1964,17 +1944,13 @@ if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a { tmceilingz = opentop; ceilingline = ld; -#ifdef ESLOPE tmceilingslope = opentopslope; -#endif } if (openbottom > tmfloorz) { tmfloorz = openbottom; -#ifdef ESLOPE tmfloorslope = openbottomslope; -#endif } if (highceiling > tmdrpoffceilz) @@ -2053,10 +2029,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) // will adjust them. tmfloorz = tmdropoffz = P_GetFloorZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->floorheight; tmceilingz = P_GetCeilingZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->ceilingheight; -#ifdef ESLOPE tmfloorslope = newsubsec->sector->f_slope; tmceilingslope = newsubsec->sector->c_slope; -#endif // Check list of fake floors and see if tmfloorz/tmceilingz need to be altered. if (newsubsec->sector->ffloors) @@ -2096,18 +2070,14 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) { if (tmfloorz < topheight - sinklevel) { tmfloorz = topheight - sinklevel; -#ifdef ESLOPE tmfloorslope = *rover->t_slope; -#endif } } else if (thing->eflags & MFE_VERTICALFLIP && thingtop <= bottomheight + sinklevel && thing->momz >= 0) { if (tmceilingz > bottomheight + sinklevel) { tmceilingz = bottomheight + sinklevel; -#ifdef ESLOPE tmceilingslope = *rover->b_slope; -#endif } } } @@ -2129,9 +2099,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) { if (tmfloorz < thing->z) { tmfloorz = thing->z; -#ifdef ESLOPE tmfloorslope = NULL; -#endif } } // Quicksand blocks never change heights otherwise. @@ -2147,18 +2115,14 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) && !(rover->flags & FF_REVERSEPLATFORM)) { tmfloorz = tmdropoffz = topheight; -#ifdef ESLOPE tmfloorslope = *rover->t_slope; -#endif } if (bottomheight < tmceilingz && abs(delta1) >= abs(delta2) && !(rover->flags & FF_PLATFORM) && !(thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE))) { tmceilingz = tmdrpoffceilz = bottomheight; -#ifdef ESLOPE tmceilingslope = *rover->b_slope; -#endif } } } @@ -2233,16 +2197,12 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) if (polytop > tmfloorz && abs(delta1) < abs(delta2)) { tmfloorz = tmdropoffz = polytop; -#ifdef ESLOPE tmfloorslope = NULL; -#endif } if (polybottom < tmceilingz && abs(delta1) >= abs(delta2)) { tmceilingz = tmdrpoffceilz = polybottom; -#ifdef ESLOPE tmceilingslope = NULL; -#endif } } plink = (polymaplink_t *)(plink->link.next); @@ -2657,10 +2617,8 @@ boolean PIT_PushableMoved(mobj_t *thing) mobj_t *oldthing = tmthing; line_t *oldceilline = ceilingline; line_t *oldblockline = blockingline; -#ifdef ESLOPE pslope_t *oldfslope = tmfloorslope; pslope_t *oldcslope = tmceilingslope; -#endif // Move the player P_TryMove(thing, thing->x+stand->momx, thing->y+stand->momy, true); @@ -2673,10 +2631,8 @@ boolean PIT_PushableMoved(mobj_t *thing) P_SetTarget(&tmthing, oldthing); ceilingline = oldceilline; blockingline = oldblockline; -#ifdef ESLOPE tmfloorslope = oldfslope; tmceilingslope = oldcslope; -#endif thing->momz = stand->momz; } else @@ -2698,9 +2654,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) fixed_t tryy = thing->y; fixed_t radius = thing->radius; fixed_t thingtop = thing->z + thing->height; -#ifdef ESLOPE fixed_t startingonground = P_IsObjectOnGround(thing); -#endif floatok = false; if (radius < MAXRADIUS/2) @@ -2791,26 +2745,22 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height; thing->eflags |= MFE_JUSTSTEPPEDDOWN; } -#ifdef ESLOPE else if (tmceilingz < thingtop && thingtop - tmceilingz <= maxstep) { thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height; thing->eflags |= MFE_JUSTSTEPPEDDOWN; } -#endif } else if (thing->z == thing->floorz && tmfloorz < thing->z && thing->z - tmfloorz <= maxstep) { thing->z = thing->floorz = tmfloorz; thing->eflags |= MFE_JUSTSTEPPEDDOWN; } -#ifdef ESLOPE else if (tmfloorz > thing->z && tmfloorz - thing->z <= maxstep) { thing->z = thing->floorz = tmfloorz; thing->eflags |= MFE_JUSTSTEPPEDDOWN; } -#endif } if (thing->eflags & MFE_VERTICALFLIP) @@ -2872,7 +2822,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; -#ifdef ESLOPE if (!(thing->flags & MF_NOCLIPHEIGHT)) { // Assign thing's standingslope if needed @@ -2893,7 +2842,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) } else // don't set standingslope if you're not going to clip against it thing->standingslope = NULL; -#endif thing->x = x; thing->y = y; @@ -4674,10 +4622,8 @@ fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) sector_t *sec = R_PointInSubsector(x, y)->sector; fixed_t floorz = sec->floorheight; -#ifdef ESLOPE if (sec->f_slope) floorz = P_GetZAt(sec->f_slope, x, y); -#endif // Intercept the stupid 'fall through 3dfloors' bug Tails 03-17-2002 if (sec->ffloors) @@ -4697,12 +4643,10 @@ fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) topheight = *rover->topheight; bottomheight = *rover->bottomheight; -#ifdef ESLOPE if (*rover->t_slope) topheight = P_GetZAt(*rover->t_slope, x, y); if (*rover->b_slope) bottomheight = P_GetZAt(*rover->b_slope, x, y); -#endif if (rover->flags & FF_QUICKSAND) { diff --git a/src/p_maputl.c b/src/p_maputl.c index 355c58db8..97b3386ff 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -308,9 +308,7 @@ fixed_t P_InterceptVector(divline_t *v2, divline_t *v1) // OPTIMIZE: keep this precalculated // fixed_t opentop, openbottom, openrange, lowfloor, highceiling; -#ifdef ESLOPE pslope_t *opentopslope, *openbottomslope; -#endif // P_CameraLineOpening // P_LineOpening, but for camera @@ -337,24 +335,20 @@ void P_CameraLineOpening(line_t *linedef) { frontfloor = sectors[front->camsec].floorheight; frontceiling = sectors[front->camsec].ceilingheight; -#ifdef ESLOPE if (sectors[front->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) frontfloor = P_GetZAt(sectors[front->camsec].f_slope, camera[0].x, camera[0].y); if (sectors[front->camsec].c_slope) frontceiling = P_GetZAt(sectors[front->camsec].c_slope, camera[0].x, camera[0].y); -#endif } else if (front->heightsec >= 0) { frontfloor = sectors[front->heightsec].floorheight; frontceiling = sectors[front->heightsec].ceilingheight; -#ifdef ESLOPE if (sectors[front->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) frontfloor = P_GetZAt(sectors[front->heightsec].f_slope, camera[0].x, camera[0].y); if (sectors[front->heightsec].c_slope) frontceiling = P_GetZAt(sectors[front->heightsec].c_slope, camera[0].x, camera[0].y); -#endif } else { @@ -365,23 +359,19 @@ void P_CameraLineOpening(line_t *linedef) { backfloor = sectors[back->camsec].floorheight; backceiling = sectors[back->camsec].ceilingheight; -#ifdef ESLOPE if (sectors[back->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) frontfloor = P_GetZAt(sectors[back->camsec].f_slope, camera[0].x, camera[0].y); if (sectors[back->camsec].c_slope) frontceiling = P_GetZAt(sectors[back->camsec].c_slope, camera[0].x, camera[0].y); -#endif } else if (back->heightsec >= 0) { backfloor = sectors[back->heightsec].floorheight; backceiling = sectors[back->heightsec].ceilingheight; -#ifdef ESLOPE if (sectors[back->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) frontfloor = P_GetZAt(sectors[back->heightsec].f_slope, camera[0].x, camera[0].y); if (sectors[back->heightsec].c_slope) frontceiling = P_GetZAt(sectors[back->heightsec].c_slope, camera[0].x, camera[0].y); -#endif } else { @@ -527,17 +517,13 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { opentop = frontheight; highceiling = backheight; -#ifdef ESLOPE opentopslope = front->c_slope; -#endif } else { opentop = backheight; highceiling = frontheight; -#ifdef ESLOPE opentopslope = back->c_slope; -#endif } frontheight = P_GetFloorZ(mobj, front, tmx, tmy, linedef); @@ -547,17 +533,13 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { openbottom = frontheight; lowfloor = backheight; -#ifdef ESLOPE openbottomslope = front->f_slope; -#endif } else { openbottom = backheight; lowfloor = frontheight; -#ifdef ESLOPE openbottomslope = back->f_slope; -#endif } } @@ -637,10 +619,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) fixed_t highestfloor = openbottom; fixed_t lowestfloor = lowfloor; fixed_t delta1, delta2; -#ifdef ESLOPE pslope_t *ceilingslope = opentopslope; pslope_t *floorslope = openbottomslope; -#endif // Check for frontsector's fake floors for (rover = front->ffloors; rover; rover = rover->next) @@ -665,9 +645,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { if (bottomheight < lowestceiling) { lowestceiling = bottomheight; -#ifdef ESLOPE ceilingslope = *rover->b_slope; -#endif } else if (bottomheight < highestceiling) highestceiling = bottomheight; @@ -677,9 +655,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { if (topheight > highestfloor) { highestfloor = topheight; -#ifdef ESLOPE floorslope = *rover->t_slope; -#endif } else if (topheight > lowestfloor) lowestfloor = topheight; @@ -709,9 +685,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { if (bottomheight < lowestceiling) { lowestceiling = bottomheight; -#ifdef ESLOPE ceilingslope = *rover->b_slope; -#endif } else if (bottomheight < highestceiling) highestceiling = bottomheight; @@ -721,9 +695,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) { if (topheight > highestfloor) { highestfloor = topheight; -#ifdef ESLOPE floorslope = *rover->t_slope; -#endif } else if (topheight > lowestfloor) lowestfloor = topheight; @@ -740,18 +712,14 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) delta2 = abs(thingtop - (polysec->floorheight + ((polysec->ceilingheight - polysec->floorheight)/2))); if (polysec->floorheight < lowestceiling && delta1 >= delta2) { lowestceiling = polysec->floorheight; -#ifdef ESLOPE ceilingslope = NULL; -#endif } else if (polysec->floorheight < highestceiling && delta1 >= delta2) highestceiling = polysec->floorheight; if (polysec->ceilingheight > highestfloor && delta1 < delta2) { highestfloor = polysec->ceilingheight; -#ifdef ESLOPE floorslope = NULL; -#endif } else if (polysec->ceilingheight > lowestfloor && delta1 < delta2) lowestfloor = polysec->ceilingheight; @@ -762,16 +730,12 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (highestfloor > openbottom) { openbottom = highestfloor; -#ifdef ESLOPE openbottomslope = floorslope; -#endif } if (lowestceiling < opentop) { opentop = lowestceiling; -#ifdef ESLOPE opentopslope = ceilingslope; -#endif } if (lowestfloor > lowfloor) diff --git a/src/p_maputl.h b/src/p_maputl.h index 1fcb68d4c..be69e0265 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -55,9 +55,7 @@ void P_CreatePrecipSecNodeList(precipmobj_t *thing, fixed_t x,fixed_t y); boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y); extern fixed_t opentop, openbottom, openrange, lowfloor, highceiling; -#ifdef ESLOPE extern pslope_t *opentopslope, *openbottomslope; -#endif void P_LineOpening(line_t *plinedef, mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index f7f2afe34..895fd2fbb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -31,9 +31,7 @@ #include "i_video.h" #include "lua_hook.h" #include "b_bot.h" -#ifdef ESLOPE #include "p_slopes.h" -#endif #include "k_kart.h" @@ -646,12 +644,10 @@ boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover) topheight = *rover->topheight; bottomheight = *rover->bottomheight; -#ifdef ESLOPE if (*rover->t_slope) topheight = P_GetZAt(*rover->t_slope, mobj->x, mobj->y); if (*rover->b_slope) bottomheight = P_GetZAt(*rover->b_slope, mobj->x, mobj->y); -#endif if (mobj->z > topheight) return false; @@ -662,7 +658,6 @@ boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover) return true; } -#ifdef ESLOPE // P_GetFloorZ (and its ceiling counterpart) // Gets the floor height (or ceiling height) of the mobj's contact point in sector, assuming object's center if moved to [x, y] // If line is supplied, it's a divider line on the sector. Set it to NULL if you're not checking for collision with a line @@ -766,15 +761,11 @@ static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line, P_GetZAt(slope, v2.x, v2.y) ); } -#endif fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) { -#ifdef ESLOPE I_Assert(mobj != NULL); -#endif I_Assert(sector != NULL); -#ifdef ESLOPE if (sector->f_slope) { fixed_t testx, testy; pslope_t *slope = sector->f_slope; @@ -844,25 +835,13 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t return HighestOnLine(mobj->radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the floor height -#else - (void)mobj; - (void)boundsec; - (void)x; - (void)y; - (void)line; - (void)lowest; - (void)perfect; -#endif return sector->floorheight; } fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) { -#ifdef ESLOPE I_Assert(mobj != NULL); -#endif I_Assert(sector != NULL); -#ifdef ESLOPE if (sector->c_slope) { fixed_t testx, testy; pslope_t *slope = sector->c_slope; @@ -932,26 +911,14 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed return HighestOnLine(mobj->radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the ceiling height -#else - (void)mobj; - (void)boundsec; - (void)x; - (void)y; - (void)line; - (void)lowest; - (void)perfect; -#endif return sector->ceilingheight; } // Now do the same as all above, but for cameras because apparently cameras are special? fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) { -#ifdef ESLOPE I_Assert(mobj != NULL); -#endif I_Assert(sector != NULL); -#ifdef ESLOPE if (sector->f_slope) { fixed_t testx, testy; pslope_t *slope = sector->f_slope; @@ -1021,25 +988,13 @@ fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fix return HighestOnLine(mobj->radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the floor height -#else - (void)mobj; - (void)boundsec; - (void)x; - (void)y; - (void)line; - (void)lowest; - (void)perfect; -#endif return sector->floorheight; } fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) { -#ifdef ESLOPE I_Assert(mobj != NULL); -#endif I_Assert(sector != NULL); -#ifdef ESLOPE if (sector->c_slope) { fixed_t testx, testy; pslope_t *slope = sector->c_slope; @@ -1109,15 +1064,6 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f return HighestOnLine(mobj->radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the ceiling height -#else - (void)mobj; - (void)boundsec; - (void)x; - (void)y; - (void)line; - (void)lowest; - (void)perfect; -#endif return sector->ceilingheight; } static void P_PlayerFlip(mobj_t *mo) @@ -1406,9 +1352,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) else if (abs(player->rmomx) < FixedMul(STOPSPEED, mo->scale) && abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale) && (!(player->cmd.forwardmove && !(twodlevel || mo->flags2 & MF2_TWOD)) && !player->cmd.sidemove && !(player->pflags & PF_SPINNING)) -#ifdef ESLOPE && !(player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && (abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)) -#endif ) { // if in a walking frame, stop moving @@ -1556,11 +1500,9 @@ void P_XYMovement(mobj_t *mo) fixed_t xmove, ymove; fixed_t oldx, oldy; // reducing bobbing/momentum on ice when up against walls boolean moved; -#ifdef ESLOPE pslope_t *oldslope = NULL; vector3_t slopemom; fixed_t predictedz = 0; -#endif I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); @@ -1590,7 +1532,6 @@ void P_XYMovement(mobj_t *mo) oldx = mo->x; oldy = mo->y; -#ifdef ESLOPE // adjust various things based on slope if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) { if (!P_IsObjectOnGround(mo)) { // We fell off at some point? Do the twisty thing! @@ -1612,7 +1553,6 @@ void P_XYMovement(mobj_t *mo) } } else if (P_IsObjectOnGround(mo) && !mo->momz) predictedz = mo->z; -#endif // Pushables can break some blocks if (CheckForBustableBlocks && mo->flags & MF_PUSHABLE) @@ -1799,7 +1739,6 @@ void P_XYMovement(mobj_t *mo) if (P_MobjWasRemoved(mo)) // MF_SPECIAL touched a player! O_o;; return; -#ifdef ESLOPE if (moved && oldslope) { // Check to see if we ran off if (oldslope != mo->standingslope) { // First, compare different slopes @@ -1847,7 +1786,6 @@ void P_XYMovement(mobj_t *mo) //CONS_Printf("Launched off of flat surface running into downward slope\n"); } } -#endif // Check the gravity status. P_CheckGravity(mo, false); @@ -1898,11 +1836,9 @@ void P_XYMovement(mobj_t *mo) if (player && player->pflags & PF_NIGHTSMODE) return; // no friction for NiGHTS players -#ifdef ESLOPE if ((mo->type == MT_BIGTUMBLEWEED || mo->type == MT_LITTLETUMBLEWEED) && (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8)) // Special exception for tumbleweeds on slopes return; -#endif //{ SRB2kart stuff if (mo->type == MT_ORBINAUT || mo->type == MT_JAWZ_DUD || mo->type == MT_JAWZ || mo->type == MT_BALLHOG) //(mo->type == MT_JAWZ && !mo->tracer)) @@ -2140,9 +2076,7 @@ boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover) { fixed_t topheight = - #ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : - #endif *rover->topheight; if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3 @@ -2179,7 +2113,6 @@ static boolean P_ZMovement(mobj_t *mo) } mo->z += mo->momz; -#ifdef ESLOPE if (mo->standingslope) { if (mo->flags & MF_NOCLIPHEIGHT) @@ -2187,7 +2120,6 @@ static boolean P_ZMovement(mobj_t *mo) else if (!P_IsObjectOnGround(mo)) P_SlopeLaunch(mo); } -#endif switch (mo->type) { @@ -2378,7 +2310,6 @@ static boolean P_ZMovement(mobj_t *mo) else mo->z = mo->floorz; -#ifdef ESLOPE if (!(mo->flags & MF_MISSILE) && mo->standingslope) // You're still on the ground; why are we here? { mo->momz = 0; @@ -2391,7 +2322,6 @@ static boolean P_ZMovement(mobj_t *mo) mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope); } -#endif // hit the floor if (mo->type == MT_FIREBALL) // special case for the fireball @@ -2491,13 +2421,11 @@ static boolean P_ZMovement(mobj_t *mo) else mom.y -= FixedMul(6*FRACUNIT, mo->scale); } -#ifdef ESLOPE else if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) { // Pop the object up a bit to encourage bounciness //mom.z = P_MobjFlip(mo)*mo->scale; } -#endif else { mom.x = mom.y = mom.z = 0; @@ -2543,11 +2471,9 @@ static boolean P_ZMovement(mobj_t *mo) || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) mom.z = tmfloorthing->momz; -#ifdef ESLOPE if (mo->standingslope) { // MT_STEAM will never have a standingslope, see above. P_QuantizeMomentumToSlope(&mom, mo->standingslope); } -#endif mo->momx = mom.x; mo->momy = mom.y; @@ -2665,7 +2591,6 @@ static void P_PlayerZMovement(mobj_t *mo) || mo->player->playerstate == PST_REBORN) return; -#ifdef ESLOPE if (mo->standingslope) { if (mo->flags & MF_NOCLIPHEIGHT) @@ -2673,7 +2598,6 @@ static void P_PlayerZMovement(mobj_t *mo) else if (!P_IsObjectOnGround(mo)) P_SlopeLaunch(mo); } -#endif // clip movement if (P_IsObjectOnGround(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) @@ -2702,19 +2626,15 @@ static void P_PlayerZMovement(mobj_t *mo) && mo->player->kartstuff[k_spinouttimer] == 0 && mo->player->kartstuff[k_squishedtimer] == 0) // SRB2kart P_SetPlayerMobjState(mo, S_KART_STND1); -#ifdef ESLOPE if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { // Handle landing on slope during Z movement P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)); } -#endif -#ifdef ESLOPE if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { // Handle landing on slope during Z movement P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)); } -#endif if (P_MobjFlip(mo)*mo->momz < 0) // falling { @@ -2961,7 +2881,6 @@ static boolean P_SceneryZMovement(mobj_t *mo) // set standingslope P_TryMove(mo, mo->x, mo->y, true); mo->momz = -mo->momz; -#ifdef ESLOPE if (mo->standingslope) { if (mo->flags & MF_NOCLIPHEIGHT) @@ -2969,7 +2888,6 @@ static boolean P_SceneryZMovement(mobj_t *mo) else if (!P_IsObjectOnGround(mo)) P_SlopeLaunch(mo); } -#endif S_StartSound(mo, mo->info->activesound); } break; @@ -3138,13 +3056,11 @@ void P_MobjCheckWater(mobj_t *mobj) topheight = *rover->topheight; bottomheight = *rover->bottomheight; -#ifdef ESLOPE if (*rover->t_slope) topheight = P_GetZAt(*rover->t_slope, mobj->x, mobj->y); if (*rover->b_slope) bottomheight = P_GetZAt(*rover->b_slope, mobj->x, mobj->y); -#endif if (mobj->eflags & MFE_VERTICALFLIP) { @@ -3387,13 +3303,11 @@ static void P_SceneryCheckWater(mobj_t *mobj) topheight = *rover->topheight; bottomheight = *rover->bottomheight; -#ifdef ESLOPE if (*rover->t_slope) topheight = P_GetZAt(*rover->t_slope, mobj->x, mobj->y); if (*rover->b_slope) bottomheight = P_GetZAt(*rover->b_slope, mobj->x, mobj->y); -#endif if (topheight <= mobj->z || bottomheight > (mobj->z + FixedMul(mobj->info->height >> 1, mobj->scale))) @@ -3439,13 +3353,9 @@ static boolean P_CameraCheckHeat(camera_t *thiscam) continue; if (halfheight >= ( -#ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, thiscam->x, thiscam->y) : -#endif *rover->topheight) || halfheight <= ( -#ifdef ESLOPE *rover->b_slope ? P_GetZAt(*rover->b_slope, thiscam->x, thiscam->y) : -#endif *rover->bottomheight)) continue; @@ -3475,13 +3385,9 @@ static boolean P_CameraCheckWater(camera_t *thiscam) continue; if (halfheight >= ( -#ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, thiscam->x, thiscam->y) : -#endif *rover->topheight) || halfheight <= ( -#ifdef ESLOPE *rover->b_slope ? P_GetZAt(*rover->b_slope, thiscam->x, thiscam->y) : -#endif *rover->bottomheight)) continue; @@ -3681,9 +3587,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) P_MobjCheckWater(mobj); -#ifdef ESLOPE P_ButteredSlope(mobj); -#endif // momentum movement mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN; @@ -3840,9 +3744,7 @@ static void CalculatePrecipFloor(precipmobj_t *mobj) else return; mobj->floorz = -#ifdef ESLOPE mobjsecsubsec->f_slope ? P_GetZAt(mobjsecsubsec->f_slope, mobj->x, mobj->y) : -#endif mobjsecsubsec->floorheight; if (mobjsecsubsec->ffloors) { @@ -3858,11 +3760,9 @@ static void CalculatePrecipFloor(precipmobj_t *mobj) if (!(rover->flags & FF_BLOCKOTHERS) && !(rover->flags & FF_SWIMMABLE)) continue; -#ifdef ESLOPE if (*rover->t_slope) topheight = P_GetZAt(*rover->t_slope, mobj->x, mobj->y); else -#endif topheight = *rover->topheight; if (topheight > mobj->floorz) @@ -9343,7 +9243,6 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s mobj->eflags &= ~MFE_JUSTHITFLOOR; } -#ifdef ESLOPE // Sliding physics for slidey mobjs! if (mobj->type == MT_FLINGRING || mobj->type == MT_FLINGCOIN || P_WeaponOrPanel(mobj->type) @@ -9356,7 +9255,6 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s //if (mobj->standingslope) CONS_Printf("slope physics on mobj\n"); P_ButteredSlope(mobj); } -#endif if (mobj->flags & (MF_ENEMY|MF_BOSS) && mobj->health && P_CheckDeathPitCollide(mobj)) // extra pit check in case these didn't have momz @@ -9660,14 +9558,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) P_SetScale(mobj, mobj->destscale); mobj->floorz = -#ifdef ESLOPE mobj->subsector->sector->f_slope ? P_GetZAt(mobj->subsector->sector->f_slope, x, y) : -#endif mobj->subsector->sector->floorheight; mobj->ceilingz = -#ifdef ESLOPE mobj->subsector->sector->c_slope ? P_GetZAt(mobj->subsector->sector->c_slope, x, y) : -#endif mobj->subsector->sector->ceilingheight; // Tells MobjCheckWater that the water height was not set. @@ -10184,14 +10078,10 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype P_SetPrecipitationThingPosition(mobj); mobj->floorz = starting_floorz = -#ifdef ESLOPE mobj->subsector->sector->f_slope ? P_GetZAt(mobj->subsector->sector->f_slope, x, y) : -#endif mobj->subsector->sector->floorheight; mobj->ceilingz = -#ifdef ESLOPE mobj->subsector->sector->c_slope ? P_GetZAt(mobj->subsector->sector->c_slope, x, y) : -#endif mobj->subsector->sector->ceilingheight; mobj->z = z; @@ -10710,9 +10600,7 @@ void P_RespawnSpecials(void) if (mthing->options & MTF_OBJECTFLIP) { z = ( -#ifdef ESLOPE ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) : -#endif ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) @@ -10722,9 +10610,7 @@ void P_RespawnSpecials(void) else { z = ( -#ifdef ESLOPE ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) : -#endif ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) @@ -10981,14 +10867,10 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing) sector = R_PointInSubsector(x, y)->sector; floor = -#ifdef ESLOPE sector->f_slope ? P_GetZAt(sector->f_slope, x, y) : -#endif sector->floorheight; ceiling = -#ifdef ESLOPE sector->c_slope ? P_GetZAt(sector->c_slope, x, y) : -#endif sector->ceilingheight; if (mthing) @@ -11060,14 +10942,10 @@ void P_MovePlayerToStarpost(INT32 playernum) sector = R_PointInSubsector(mobj->x, mobj->y)->sector; floor = -#ifdef ESLOPE sector->f_slope ? P_GetZAt(sector->f_slope, mobj->x, mobj->y) : -#endif sector->floorheight; ceiling = -#ifdef ESLOPE sector->c_slope ? P_GetZAt(sector->c_slope, mobj->x, mobj->y) : -#endif sector->ceilingheight; if (mobj->player->kartstuff[k_starpostflip]) @@ -11242,9 +11120,7 @@ void P_SpawnMapThing(mapthing_t *mthing) ss = R_PointInSubsector(mthing->x << FRACBITS, mthing->y << FRACBITS); mthing->z = (INT16)((( -#ifdef ESLOPE ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, mthing->x << FRACBITS, mthing->y << FRACBITS) : -#endif ss->sector->floorheight)>>FRACBITS) + (mthing->options >> ZSHIFT)); if (numhuntemeralds < MAXHUNTEMERALDS) @@ -11360,9 +11236,7 @@ void P_SpawnMapThing(mapthing_t *mthing) if (i == MT_NIGHTSBUMPER) z = ( -#ifdef ESLOPE ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) : -#endif ss->sector->floorheight) + ((mthing->options >> ZSHIFT) << FRACBITS); else if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE) z = ONFLOORZ; @@ -11371,9 +11245,7 @@ void P_SpawnMapThing(mapthing_t *mthing) if (mthing->options & MTF_OBJECTFLIP) { z = ( -#ifdef ESLOPE ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) : -#endif ss->sector->ceilingheight); if (mthing->options & MTF_AMBUSH) // Special flag for rings @@ -11386,9 +11258,7 @@ void P_SpawnMapThing(mapthing_t *mthing) else { z = ( -#ifdef ESLOPE ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) : -#endif ss->sector->floorheight); if (mthing->options & MTF_AMBUSH) // Special flag for rings @@ -11410,15 +11280,11 @@ void P_SpawnMapThing(mapthing_t *mthing) // base positions if (flip) z = ( -#ifdef ESLOPE ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) : -#endif ss->sector->ceilingheight) - mobjinfo[i].height; else z = ( -#ifdef ESLOPE ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) : -#endif ss->sector->floorheight); // offsetting @@ -11985,9 +11851,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) //hoopcenter->flags |= MF_NOTHINK; z += -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight; hoopcenter->z = z - hoopcenter->height/2; @@ -12128,9 +11992,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) hoopcenter->spawnpoint = mthing; z += -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight; hoopcenter->z = z - hoopcenter->height/2; @@ -12250,9 +12112,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) else if (mthing->type == mobjinfo[MT_NIGHTSWING].doomednum) { z = -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12304,9 +12164,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) if (mthing->options & MTF_OBJECTFLIP) { z = ( -#ifdef ESLOPE sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif sec->ceilingheight) - mobjinfo[ringthing].height; if (mthing->options >> ZSHIFT) z -= ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12314,9 +12172,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) else { z = -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12370,9 +12226,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) if (mthing->options & MTF_OBJECTFLIP) { z = ( -#ifdef ESLOPE sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif sec->ceilingheight) - mobjinfo[ringthing].height - dist*r; if (mthing->options >> ZSHIFT) z -= ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12380,9 +12234,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) else { z = ( -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight) + dist*r; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12428,9 +12280,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) if (mthing->options & MTF_OBJECTFLIP) { z = ( -#ifdef ESLOPE sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif sec->ceilingheight) - mobjinfo[ringthing].height - 64*FRACUNIT*r; if (mthing->options >> ZSHIFT) z -= ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12438,9 +12288,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) else { z = ( -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight) + 64*FRACUNIT*r; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); @@ -12473,9 +12321,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) } z = -#ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif sec->floorheight; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); diff --git a/src/p_mobj.h b/src/p_mobj.h index dfc8fc738..99ab0f0cd 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -368,9 +368,7 @@ typedef struct mobj_s INT32 cusval; INT32 cvmem; -#ifdef ESLOPE struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?) -#endif boolean colorized; // Whether the mobj uses the rainbow colormap diff --git a/src/p_saveg.c b/src/p_saveg.c index 5bbba1f28..32be2b3ff 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -30,9 +30,7 @@ #include "r_sky.h" #include "p_polyobj.h" #include "lua_script.h" -#ifdef ESLOPE #include "p_slopes.h" -#endif savedata_t savedata; UINT8 *save_p; @@ -949,9 +947,7 @@ typedef enum MD2_HPREV = 1<<8, MD2_COLORIZED = 1<<9, MD2_WAYPOINTCAP = 1<<10 -#ifdef ESLOPE , MD2_SLOPE = 1<<11 -#endif } mobj_diff2_t; typedef enum @@ -1142,10 +1138,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_HNEXT; if (mobj->hprev) diff2 |= MD2_HPREV; -#ifdef ESLOPE if (mobj->standingslope) diff2 |= MD2_SLOPE; -#endif if (mobj->colorized) diff2 |= MD2_COLORIZED; if (mobj == waypointcap) @@ -1265,10 +1259,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT32(save_p, mobj->hnext->mobjnum); if (diff2 & MD2_HPREV) WRITEUINT32(save_p, mobj->hprev->mobjnum); -#ifdef ESLOPE if (diff2 & MD2_SLOPE) WRITEUINT16(save_p, mobj->standingslope->id); -#endif if (diff2 & MD2_COLORIZED) WRITEUINT8(save_p, mobj->colorized); @@ -2142,10 +2134,8 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->hnext = (mobj_t *)(size_t)READUINT32(save_p); if (diff2 & MD2_HPREV) mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p); -#ifdef ESLOPE if (diff2 & MD2_SLOPE) mobj->standingslope = P_SlopeById(READUINT16(save_p)); -#endif if (diff2 & MD2_COLORIZED) mobj->colorized = READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 85243d504..34d4b22f2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -78,9 +78,7 @@ #include "hardware/hw_light.h" #endif -#ifdef ESLOPE #include "p_slopes.h" -#endif // SRB2Kart #include "k_kart.h" @@ -1004,9 +1002,7 @@ static void P_LoadThings(void) // Z for objects mt->z = (INT16)( -#ifdef ESLOPE mtsector->f_slope ? P_GetZAt(mtsector->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) : -#endif mtsector->floorheight)>>FRACBITS; if (mt->type == 1700 // MT_AXIS @@ -3116,9 +3112,7 @@ boolean P_SetupLevel(boolean skipprecip) P_PrepareThings(lastloadedmaplumpnum + ML_THINGS); } -#ifdef ESLOPE P_ResetDynamicSlopes(); -#endif P_LoadThings(); diff --git a/src/p_slopes.c b/src/p_slopes.c index 76af7bfde..60c852f53 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -23,7 +23,6 @@ #include "p_maputl.h" #include "w_wad.h" -#ifdef ESLOPE static pslope_t *slopelist = NULL; static UINT16 slopecount = 0; @@ -620,7 +619,6 @@ void P_ResetDynamicSlopes(void) { size_t i; #ifdef ESLOPE_TYPESHIM // Rewrite old specials to new ones, and give a console warning boolean warned = false; -#endif slopelist = NULL; slopecount = 0; diff --git a/src/p_slopes.h b/src/p_slopes.h index 708a9107d..3dad65a48 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -13,7 +13,6 @@ #ifndef P_SLOPES_H__ #define P_SLOPES_H__ -#ifdef ESLOPE void P_CalculateSlopeNormal(pslope_t *slope); void P_ResetDynamicSlopes(void); void P_RunDynamicSlopes(void); @@ -41,7 +40,6 @@ void P_SlopeLaunch(mobj_t *mo); void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope); void P_ButteredSlope(mobj_t *mo); -#endif // EOF #endif // #ifdef ESLOPE diff --git a/src/p_spec.c b/src/p_spec.c index 165a2eba8..ffc4bff25 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4984,10 +4984,8 @@ void P_UpdateSpecials(void) // POINT LIMIT P_CheckPointLimit(); -#ifdef ESLOPE // Dynamic slopeness P_RunDynamicSlopes(); -#endif // ANIMATE TEXTURES for (anim = anims; anim < lastanim; anim++) @@ -5132,11 +5130,9 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f ffloor->topyoffs = &sec2->ceiling_yoffs; ffloor->topangle = &sec2->ceilingpic_angle; -#ifdef ESLOPE // Add slopes ffloor->t_slope = &sec2->c_slope; ffloor->b_slope = &sec2->f_slope; -#endif if ((flags & FF_SOLID) && (master->flags & ML_EFFECT1)) // Block player only flags &= ~FF_BLOCKOTHERS; @@ -5583,15 +5579,11 @@ void T_LaserFlash(laserthink_t *flash) sourcesec = ffloor->master->frontsector; // Less to type! -#ifdef ESLOPE top = (*ffloor->t_slope) ? P_GetZAt(*ffloor->t_slope, sector->soundorg.x, sector->soundorg.y) : *ffloor->topheight; bottom = (*ffloor->b_slope) ? P_GetZAt(*ffloor->b_slope, sector->soundorg.x, sector->soundorg.y) : *ffloor->bottomheight; sector->soundorg.z = (top + bottom)/2; -#else - sector->soundorg.z = (*ffloor->topheight + *ffloor->bottomheight)/2; -#endif S_StartSound(§or->soundorg, sfx_laser); // Seek out objects to DESTROY! MUAHAHHAHAHAA!!!*cough* @@ -6688,13 +6680,11 @@ void P_SpawnSpecials(INT32 fromnetsave) sectors[s].midmap = lines[i].frontsector->midmap; break; -#ifdef ESLOPE // Slope copy specials. Handled here for sanity. case 720: case 721: case 722: P_CopySectorSlope(&lines[i]); break; -#endif default: break; @@ -7246,11 +7236,9 @@ void T_Disappear(disappear_t *d) if (!(lines[d->sourceline].flags & ML_NOCLIMB)) { -#ifdef ESLOPE if (*rover->t_slope) sectors[s].soundorg.z = P_GetZAt(*rover->t_slope, sectors[s].soundorg.x, sectors[s].soundorg.y); else -#endif sectors[s].soundorg.z = *rover->topheight; S_StartSound(§ors[s].soundorg, sfx_appear); } diff --git a/src/p_user.c b/src/p_user.c index 9926b9803..ad5bdffc6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1781,13 +1781,8 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (mo->z + (mo->height/2) > topheight) continue; @@ -1821,13 +1816,8 @@ boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand if (!(rover->flags & FF_QUICKSAND)) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (mo->z + flipoffset > topheight) continue; @@ -1966,9 +1956,7 @@ static void P_CheckBouncySectors(player_t *player) fixed_t oldx; fixed_t oldy; fixed_t oldz; -#ifdef ESLOPE vector3_t momentum; -#endif oldx = player->mo->x; oldy = player->mo->y; @@ -2023,7 +2011,6 @@ static void P_CheckBouncySectors(player_t *player) { fixed_t newmom; -#ifdef ESLOPE pslope_t *slope; if (abs(oldz - topheight) < abs(oldz + player->mo->height - bottomheight)) { // Hit top slope = *rover->t_slope; @@ -2039,9 +2026,6 @@ static void P_CheckBouncySectors(player_t *player) P_ReverseQuantizeMomentumToSlope(&momentum, slope); newmom = momentum.z = -FixedMul(momentum.z,linedist)/2; -#else - newmom = -FixedMul(player->mo->momz,linedist); -#endif if (abs(newmom) < (linedist*2)) { @@ -2064,7 +2048,6 @@ static void P_CheckBouncySectors(player_t *player) else if (newmom < -P_GetPlayerHeight(player)/2) newmom = -P_GetPlayerHeight(player)/2; -#ifdef ESLOPE momentum.z = newmom*2; if (slope) @@ -2073,9 +2056,6 @@ static void P_CheckBouncySectors(player_t *player) player->mo->momx = momentum.x; player->mo->momy = momentum.y; player->mo->momz = momentum.z/2; -#else - player->mo->momz = newmom; -#endif if (player->pflags & PF_SPINNING) { @@ -2132,13 +2112,8 @@ static void P_CheckQuicksand(player_t *player) if (!(rover->flags & FF_QUICKSAND)) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (topheight >= player->mo->z && bottomheight < player->mo->z + player->mo->height) { @@ -2506,15 +2481,10 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused floorclimb = true; else { -#ifdef ESLOPE floorheight = glidesector->sector->f_slope ? P_GetZAt(glidesector->sector->f_slope, player->mo->x, player->mo->y) : glidesector->sector->floorheight; ceilingheight = glidesector->sector->c_slope ? P_GetZAt(glidesector->sector->c_slope, player->mo->x, player->mo->y) : glidesector->sector->ceilingheight; -#else - floorheight = glidesector->sector->floorheight; - ceilingheight = glidesector->sector->ceilingheight; -#endif if (glidesector->sector->ffloors) { @@ -2528,13 +2498,8 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused floorclimb = true; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif // Only supports rovers that are moving like an 'elevator', not just the top or bottom. if (rover->master->frontsector->floorspeed && rover->master->frontsector->ceilspeed == 42) @@ -2575,11 +2540,7 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused if (roverbelow == rover) continue; -#ifdef ESLOPE bottomheight2 = *roverbelow->b_slope ? P_GetZAt(*roverbelow->b_slope, player->mo->x, player->mo->y) : *roverbelow->bottomheight; -#else - bottomheight2 = *roverbelow->bottomheight; -#endif if (bottomheight2 < topheight + FixedMul(16*FRACUNIT, player->mo->scale)) foundfof = true; @@ -2625,11 +2586,7 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused if (roverbelow == rover) continue; -#ifdef ESLOPE topheight2 = *roverbelow->t_slope ? P_GetZAt(*roverbelow->t_slope, player->mo->x, player->mo->y) : *roverbelow->topheight; -#else - topheight2 = *roverbelow->topheight; -#endif if (topheight2 > bottomheight - FixedMul(16*FRACUNIT, player->mo->scale)) foundfof = true; @@ -2684,11 +2641,7 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) continue; -#ifdef ESLOPE bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - bottomheight = *rover->bottomheight; -#endif if (bottomheight < floorheight + FixedMul(16*FRACUNIT, player->mo->scale)) { @@ -2729,11 +2682,7 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; -#else - topheight = *rover->topheight; -#endif if (topheight > ceilingheight - FixedMul(16*FRACUNIT, player->mo->scale)) { @@ -3102,12 +3051,10 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused. ceilingheight = sec->ceilingheight; floorheight = sec->floorheight; -#ifdef ESLOPE if (sec->c_slope) ceilingheight = P_GetZAt(sec->c_slope, checkx, checky); if (sec->f_slope) floorheight = P_GetZAt(sec->f_slope, checkx, checky); -#endif highestceilingheight = (ceilingheight > highestceilingheight) ? ceilingheight : highestceilingheight; lowestfloorheight = (floorheight < lowestfloorheight) ? floorheight : lowestfloorheight; @@ -3118,13 +3065,8 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused. { if (!(rover->flags & FF_EXISTS)) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (P_CheckSolidLava(player->mo, rover)) ; @@ -3583,9 +3525,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. && !P_PlayerInPain(player)) // subsequent revs { if ((cmd->buttons & BT_BRAKE) && player->speed < FixedMul(5<mo->scale) && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING) -#ifdef ESLOPE && (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2) -#endif ) { player->mo->momx = player->cmomx; @@ -3616,9 +3556,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. // AKA Just go into a spin on the ground, you idiot. ;) else if ((cmd->buttons & BT_BRAKE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20)) && !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<mo->scale) -#ifdef ESLOPE || (player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) >= FRACUNIT/2) -#endif ) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING)) { player->pflags |= PF_SPINNING; @@ -3632,9 +3570,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. // Rolling normally if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH) && player->speed < FixedMul(5*FRACUNIT,player->mo->scale) -#ifdef ESLOPE && (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2) -#endif ) { if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player))) @@ -4023,12 +3959,10 @@ static void P_3dMovement(player_t *player) //fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale); boolean analogmove = false; fixed_t oldMagnitude, newMagnitude; -#ifdef ESLOPE vector3_t totalthrust; totalthrust.x = totalthrust.y = 0; // I forget if this is needed totalthrust.z = FRACUNIT*P_MobjFlip(player->mo)/3; // A bit of extra push-back on slopes -#endif // ESLOPE // Get the old momentum; this will be needed at the end of the function! -SH oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); @@ -4152,12 +4086,8 @@ static void P_3dMovement(player_t *player) movepushforward = 0; } -#ifdef ESLOPE totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward); totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward); -#else - P_Thrust(player->mo, movepushangle, movepushforward); -#endif } else if (!(player->kartstuff[k_spinouttimer])) { @@ -4172,15 +4102,10 @@ static void P_3dMovement(player_t *player) else movepushside = (cmd->sidemove * FRACUNIT/128) - FixedDiv(player->speed, K_GetKartSpeed(player, true)); -#ifdef ESLOPE totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside); totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside); -#else - P_Thrust(player->mo, movepushsideangle, movepushside); -#endif } -#ifdef ESLOPE if ((totalthrust.x || totalthrust.y) && player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) { // Factor thrust to slope, but only for the part pushing up it! @@ -4200,7 +4125,6 @@ static void P_3dMovement(player_t *player) player->mo->momx += totalthrust.x; player->mo->momy += totalthrust.y; -#endif // Time to ask three questions: // 1) Are we over topspeed? @@ -5601,14 +5525,12 @@ void P_ElementalFireTrail(player_t *player) { newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); -#ifdef ESLOPE if (player->mo->standingslope) { ground = P_GetZAt(player->mo->standingslope, newx, newy); if (player->mo->eflags & MFE_VERTICALFLIP) ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale); } -#endif flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE); P_SetTarget(&flame->target, player->mo); flame->angle = travelangle; @@ -8204,13 +8126,8 @@ static void P_CalcPostImg(player_t *player) if (!(rover->flags & FF_EXISTS)) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (pviewheight >= topheight || pviewheight <= bottomheight) continue; @@ -8232,13 +8149,8 @@ static void P_CalcPostImg(player_t *player) if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || rover->flags & FF_BLOCKPLAYER) continue; -#ifdef ESLOPE topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight; bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight; -#else - topheight = *rover->topheight; - bottomheight = *rover->bottomheight; -#endif if (pviewheight >= topheight || pviewheight <= bottomheight) continue; diff --git a/src/r_bsp.c b/src/r_bsp.c index 43cb6432f..2f1bc9985 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -376,10 +376,8 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back) #endif back->ceilingpic == front->ceilingpic && back->floorpic == front->floorpic -#ifdef ESLOPE && back->f_slope == front->f_slope && back->c_slope == front->c_slope -#endif && back->lightlevel == front->lightlevel && !line->sidedef->midtexture // Check offsets too! @@ -494,7 +492,6 @@ static void R_AddLine(seg_t *line) doorclosed = 0; // Closed door. -#ifdef ESLOPE if (frontsector->f_slope || frontsector->c_slope || backsector->f_slope || backsector->c_slope) { fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends @@ -537,7 +534,6 @@ static void R_AddLine(seg_t *line) } } else -#endif { if (viewsector != backsector && viewsector != frontsector) { @@ -860,15 +856,11 @@ static void R_Subsector(size_t num) floorcolormap = ceilingcolormap = frontsector->extra_colormap; floorcenterz = -#ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) : -#endif frontsector->floorheight; ceilingcenterz = -#ifdef ESLOPE frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) : -#endif frontsector->ceilingheight; // Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps. @@ -896,9 +888,7 @@ static void R_Subsector(size_t num) sub->sector->extra_colormap = frontsector->extra_colormap; if ((( -#ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, viewx, viewy) : -#endif frontsector->floorheight) < viewz || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))) { @@ -907,18 +897,14 @@ static void R_Subsector(size_t num) #ifdef POLYOBJECTS_PLANES , NULL #endif -#ifdef ESLOPE , frontsector->f_slope -#endif , R_NoEncore(frontsector, false)); } else floorplane = NULL; if ((( -#ifdef ESLOPE frontsector->c_slope ? P_GetZAt(frontsector->c_slope, viewx, viewy) : -#endif frontsector->ceilingheight) > viewz || frontsector->ceilingpic == skyflatnum || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))) @@ -929,18 +915,14 @@ static void R_Subsector(size_t num) #ifdef POLYOBJECTS_PLANES , NULL #endif -#ifdef ESLOPE , frontsector->c_slope -#endif , R_NoEncore(frontsector, true)); } else ceilingplane = NULL; numffloors = 0; -#ifdef ESLOPE ffloor[numffloors].slope = NULL; -#endif ffloor[numffloors].plane = NULL; ffloor[numffloors].polyobj = NULL; if (frontsector->ffloors) @@ -966,15 +948,11 @@ static void R_Subsector(size_t num) ffloor[numffloors].polyobj = NULL; heightcheck = -#ifdef ESLOPE *rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) : -#endif *rover->bottomheight; planecenterz = -#ifdef ESLOPE *rover->b_slope ? P_GetZAt(*rover->b_slope, frontsector->soundorg.x, frontsector->soundorg.y) : -#endif *rover->bottomheight; if (planecenterz <= ceilingcenterz && planecenterz >= floorcenterz @@ -990,18 +968,14 @@ static void R_Subsector(size_t num) #ifdef POLYOBJECTS_PLANES , NULL #endif -#ifdef ESLOPE , *rover->b_slope -#endif , R_NoEncore(rover->master->frontsector, true)); -#ifdef ESLOPE ffloor[numffloors].slope = *rover->b_slope; // Tell the renderer this sector has slopes in it. if (ffloor[numffloors].slope) frontsector->hasslope = true; -#endif ffloor[numffloors].height = heightcheck; ffloor[numffloors].ffloor = rover; @@ -1013,15 +987,11 @@ static void R_Subsector(size_t num) ffloor[numffloors].polyobj = NULL; heightcheck = -#ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, viewx, viewy) : -#endif *rover->topheight; planecenterz = -#ifdef ESLOPE *rover->t_slope ? P_GetZAt(*rover->t_slope, frontsector->soundorg.x, frontsector->soundorg.y) : -#endif *rover->topheight; if (planecenterz >= floorcenterz && planecenterz <= ceilingcenterz @@ -1036,18 +1006,14 @@ static void R_Subsector(size_t num) #ifdef POLYOBJECTS_PLANES , NULL #endif -#ifdef ESLOPE , *rover->t_slope -#endif , R_NoEncore(rover->master->frontsector, false)); -#ifdef ESLOPE ffloor[numffloors].slope = *rover->t_slope; // Tell the renderer this sector has slopes in it. if (ffloor[numffloors].slope) frontsector->hasslope = true; -#endif ffloor[numffloors].height = heightcheck; ffloor[numffloors].ffloor = rover; @@ -1086,16 +1052,12 @@ static void R_Subsector(size_t num) polysec->lightlevel, polysec->floor_xoffs, polysec->floor_yoffs, polysec->floorpic_angle-po->angle, NULL, NULL, po -#ifdef ESLOPE , NULL // will ffloors be slopable eventually? -#endif , R_NoEncore(polysec, false)); ffloor[numffloors].height = polysec->floorheight; ffloor[numffloors].polyobj = po; -#ifdef ESLOPE ffloor[numffloors].slope = NULL; -#endif // ffloor[numffloors].ffloor = rover; po->visplane = ffloor[numffloors].plane; numffloors++; @@ -1115,16 +1077,12 @@ static void R_Subsector(size_t num) polysec->lightlevel, polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle, NULL, NULL, po -#ifdef ESLOPE , NULL // will ffloors be slopable eventually? -#endif , R_NoEncore(polysec, true)); ffloor[numffloors].polyobj = po; ffloor[numffloors].height = polysec->ceilingheight; -#ifdef ESLOPE ffloor[numffloors].slope = NULL; -#endif // ffloor[numffloors].ffloor = rover; po->visplane = ffloor[numffloors].plane; numffloors++; @@ -1186,11 +1144,9 @@ void R_Prep3DFloors(sector_t *sector) fixed_t bestheight, maxheight; INT32 count, i, mapnum; sector_t *sec; -#ifdef ESLOPE pslope_t *bestslope = NULL; fixed_t heighttest; // I think it's better to check the Z height at the sector's center // than assume unsloped heights are accurate indicators of order in sloped sectors. -Red -#endif count = 1; for (rover = sector->ffloors; rover; rover = rover->next) @@ -1213,14 +1169,10 @@ void R_Prep3DFloors(sector_t *sector) else memset(sector->lightlist, 0, sizeof (lightlist_t) * count); -#ifdef ESLOPE heighttest = sector->c_slope ? P_GetZAt(sector->c_slope, sector->soundorg.x, sector->soundorg.y) : sector->ceilingheight; sector->lightlist[0].height = heighttest + 1; sector->lightlist[0].slope = sector->c_slope; -#else - sector->lightlist[0].height = sector->ceilingheight + 1; -#endif sector->lightlist[0].lightlevel = §or->lightlevel; sector->lightlist[0].caster = NULL; sector->lightlist[0].extra_colormap = sector->extra_colormap; @@ -1238,7 +1190,6 @@ void R_Prep3DFloors(sector_t *sector) && !(rover->flags & FF_CUTLEVEL) && !(rover->flags & FF_CUTSPRITES))) continue; -#ifdef ESLOPE heighttest = *rover->t_slope ? P_GetZAt(*rover->t_slope, sector->soundorg.x, sector->soundorg.y) : *rover->topheight; if (heighttest > bestheight && heighttest < maxheight) @@ -1260,21 +1211,6 @@ void R_Prep3DFloors(sector_t *sector) continue; } } -#else - if (*rover->topheight > bestheight && *rover->topheight < maxheight) - { - best = rover; - bestheight = *rover->topheight; - continue; - } - if (rover->flags & FF_DOUBLESHADOW && *rover->bottomheight > bestheight - && *rover->bottomheight < maxheight) - { - best = rover; - bestheight = *rover->bottomheight; - continue; - } -#endif } if (!best) { @@ -1285,9 +1221,7 @@ void R_Prep3DFloors(sector_t *sector) sector->lightlist[i].height = maxheight = bestheight; sector->lightlist[i].caster = best; sector->lightlist[i].flags = best->flags; -#ifdef ESLOPE sector->lightlist[i].slope = bestslope; -#endif sec = §ors[best->secnum]; mapnum = sec->midmap; if (mapnum >= 0 && (size_t)mapnum < num_extra_colormaps) @@ -1313,12 +1247,8 @@ void R_Prep3DFloors(sector_t *sector) if (best->flags & FF_DOUBLESHADOW) { -#ifdef ESLOPE heighttest = *best->b_slope ? P_GetZAt(*best->b_slope, sector->soundorg.x, sector->soundorg.y) : *best->bottomheight; if (bestheight == heighttest) ///TODO: do this in a more efficient way -Red -#else - if (bestheight == *best->bottomheight) -#endif { sector->lightlist[i].lightlevel = sector->lightlist[best->lastlight].lightlevel; sector->lightlist[i].extra_colormap = diff --git a/src/r_defs.h b/src/r_defs.h index 00036e9e9..144faac94 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -155,11 +155,9 @@ typedef struct ffloor_s fixed_t *bottomyoffs; angle_t *bottomangle; -#ifdef ESLOPE // Pointers to pointers. Yup. struct pslope_s **t_slope; struct pslope_s **b_slope; -#endif size_t secnum; ffloortype_e flags; @@ -190,9 +188,7 @@ typedef struct lightlist_s extracolormap_t *extra_colormap; INT32 flags; ffloor_t *caster; -#ifdef ESLOPE struct pslope_s *slope; // FF_DOUBLESHADOW makes me have to store this pointer here. Bluh bluh. -#endif } lightlist_t; @@ -226,7 +222,6 @@ typedef struct linechain_s // Slopes -#ifdef ESLOPE typedef enum { SL_NOPHYSICS = 1, // Don't do momentum adjustment with this slope SL_NODYNAMIC = 1<<1, // Slope will never need to move during the level, so don't fuss with recalculating it @@ -269,7 +264,6 @@ typedef struct pslope_s struct pslope_s *next; // Make a linked list of dynamic slopes, for easy reference later } pslope_t; -#endif typedef enum { @@ -376,12 +370,10 @@ typedef struct sector_s precipmobj_t *preciplist; struct mprecipsecnode_s *touching_preciplist; -#ifdef ESLOPE // Eternity engine slope pslope_t *f_slope; // floor slope pslope_t *c_slope; // ceiling slope boolean hasslope; // The sector, or one of its visible FOFs, contains a slope -#endif // these are saved for netgames, so do not let Lua touch these! INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed @@ -639,11 +631,9 @@ typedef struct drawseg_s UINT8 portalpass; // if > 0 and <= portalrender, do not affect sprite clipping -#ifdef ESLOPE fixed_t maskedtextureheight[MAXVIDWIDTH]; // For handling sloped midtextures vertex_t leftpos, rightpos; // Used for rendering FOF walls with slopes -#endif } drawseg_t; typedef enum diff --git a/src/r_draw.c b/src/r_draw.c index 02a912155..a29c26a3c 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -112,11 +112,9 @@ fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; UINT8 *ds_source; // start of a 64*64 tile image UINT8 *ds_transmap; // one of the translucency tables -#ifdef ESLOPE pslope_t *ds_slope; // Current slope being used floatv3_t ds_su, ds_sv, ds_sz; // Vectors for... stuff? float focallengthf, zeroheight; -#endif /** \brief Variable flat sizes */ diff --git a/src/r_draw.h b/src/r_draw.h index 900802cea..a9921028c 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -62,7 +62,6 @@ extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; extern UINT8 *ds_source; // start of a 64*64 tile image extern UINT8 *ds_transmap; -#ifdef ESLOPE typedef struct { float x, y, z; } floatv3_t; @@ -70,7 +69,6 @@ typedef struct { extern pslope_t *ds_slope; // Current slope being used extern floatv3_t ds_su, ds_sv, ds_sz; // Vectors for... stuff? extern float focallengthf, zeroheight; -#endif // Variable flat sizes extern UINT32 nflatxshift; @@ -157,12 +155,10 @@ void ASMCALL R_DrawSpan_8_MMX(void); void R_DrawTranslatedColumn_8(void); void R_DrawTranslatedTranslucentColumn_8(void); void R_DrawSpan_8(void); -#ifdef ESLOPE void R_CalcTiltedLighting(fixed_t start, fixed_t end); void R_DrawTiltedSpan_8(void); void R_DrawTiltedTranslucentSpan_8(void); void R_DrawTiltedSplat_8(void); -#endif void R_DrawSplat_8(void); void R_DrawTranslucentSplat_8(void); void R_DrawTranslucentSpan_8(void); diff --git a/src/r_draw8.c b/src/r_draw8.c index 634ae7075..37d93d162 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -620,7 +620,6 @@ void R_DrawSpan_8 (void) } } -#ifdef ESLOPE // R_CalcTiltedLighting // Exactly what it says on the tin. I wish I wasn't too lazy to explain things properly. static INT32 tiltlighting[MAXVIDWIDTH]; @@ -1058,7 +1057,6 @@ void R_DrawTiltedSplat_8(void) } #endif } -#endif // ESLOPE /** \brief The R_DrawSplat_8 function Just like R_DrawSpan_8, but skips transparent pixels. diff --git a/src/r_main.c b/src/r_main.c index 6f2319b6c..5f3639ded 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -536,9 +536,7 @@ static void R_InitTextureMapping(void) focallength = FixedDiv(projection, FINETANGENT(FINEANGLES/4+/*cv_fov.value*/ FIELDOFVIEW/2)); -#ifdef ESLOPE focallengthf = FIXED_TO_FLOAT(focallength); -#endif for (i = 0; i < FINEANGLES/2; i++) { diff --git a/src/r_plane.c b/src/r_plane.c index 659481ec8..56f75d20d 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -322,11 +322,9 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) if (pindex >= MAXLIGHTZ) pindex = MAXLIGHTZ - 1; -#ifdef ESLOPE if (currentplane->slope) ds_colormap = colormaps; else -#endif ds_colormap = planezlight[pindex]; if (encoremap && !currentplane->noencore) ds_colormap += (256*32); @@ -427,17 +425,13 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, #ifdef POLYOBJECTS_PLANES , polyobj_t *polyobj #endif -#ifdef ESLOPE , pslope_t *slope -#endif , boolean noencore) { visplane_t *check; unsigned hash; -#ifdef ESLOPE if (slope); else // Don't mess with this right now if a slope is involved -#endif { xoff += viewx; yoff -= viewy; @@ -495,9 +489,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, && check->viewx == viewx && check->viewy == viewy && check->viewz == viewz && check->viewangle == viewangle && check->plangle == plangle -#ifdef ESLOPE && check->slope == slope -#endif && check->noencore == noencore) { return check; @@ -523,9 +515,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, #ifdef POLYOBJECTS_PLANES check->polyobj = polyobj; #endif -#ifdef ESLOPE check->slope = slope; -#endif check->noencore = noencore; memset(check->top, 0xff, sizeof (check->top)); @@ -596,9 +586,7 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) #ifdef POLYOBJECTS_PLANES new_pl->polyobj = pl->polyobj; #endif -#ifdef ESLOPE new_pl->slope = pl->slope; -#endif new_pl->noencore = pl->noencore; pl = new_pl; pl->minx = start; @@ -861,9 +849,7 @@ void R_DrawSinglePlane(visplane_t *pl) #ifndef NOWATER if (pl->ffloor->flags & FF_RIPPLE -#ifdef ESLOPE && !pl->slope -#endif ) { INT32 top, bottom; @@ -902,9 +888,7 @@ void R_DrawSinglePlane(visplane_t *pl) } else light = (pl->lightlevel >> LIGHTSEGSHIFT); -#ifdef ESLOPE if (!pl->slope) // Don't mess with angle on slopes! We'll handle this ourselves later -#endif if (viewangle != pl->viewangle+pl->plangle) { memset(cachedheight, 0, sizeof (cachedheight)); @@ -978,7 +962,6 @@ void R_DrawSinglePlane(visplane_t *pl) if (light < 0) light = 0; -#ifdef ESLOPE if (pl->slope) { // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! // I copied ZDoom's code and adapted it to SRB2... -fickle @@ -1118,7 +1101,6 @@ void R_DrawSinglePlane(visplane_t *pl) planezlight = scalelight[light]; } else -#endif // ESLOPE planezlight = zlight[light]; diff --git a/src/r_plane.h b/src/r_plane.h index 8f5a6073d..d7cc14fae 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -48,9 +48,7 @@ typedef struct visplane_s #ifdef POLYOBJECTS_PLANES polyobj_t *polyobj; #endif -#ifdef ESLOPE pslope_t *slope; -#endif boolean noencore; } visplane_t; @@ -86,9 +84,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t #ifdef POLYOBJECTS_PLANES , polyobj_t *polyobj #endif -#ifdef ESLOPE , pslope_t *slope -#endif , boolean noencore); visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop); void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop); @@ -108,13 +104,11 @@ typedef struct planemgr_s INT16 f_clip[MAXVIDWIDTH]; INT16 c_clip[MAXVIDWIDTH]; -#ifdef ESLOPE // For slope rendering; the height at the other end fixed_t f_pos_slope; fixed_t b_pos_slope; struct pslope_s *slope; -#endif struct ffloor_s *ffloor; #ifdef POLYOBJECTS_PLANES diff --git a/src/r_segs.c b/src/r_segs.c index 399f514bc..8cd021ff9 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -51,20 +51,16 @@ static fixed_t rw_offset2; // for splats static fixed_t rw_scale, rw_scalestep; static fixed_t rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid; static INT32 worldtop, worldbottom, worldhigh, worldlow; -#ifdef ESLOPE static INT32 worldtopslope, worldbottomslope, worldhighslope, worldlowslope; // worldtop/bottom at end of slope static fixed_t rw_toptextureslide, rw_midtextureslide, rw_bottomtextureslide; // Defines how to adjust Y offsets along the wall for slopes static fixed_t rw_midtextureback, rw_midtexturebackslide; // Values for masked midtexture height calculation -#endif static fixed_t pixhigh, pixlow, pixhighstep, pixlowstep; static fixed_t topfrac, topstep; static fixed_t bottomfrac, bottomstep; static lighttable_t **walllights; static INT16 *maskedtexturecol; -#ifdef ESLOPE static fixed_t *maskedtextureheight = NULL; -#endif // ========================================================================== // R_Splats Wall Splats Drawer @@ -293,9 +289,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) sector_t *front, *back; INT32 times, repeats; INT64 overflow_test; -#ifdef ESLOPE INT32 range; -#endif // Calculate light table. // Use different light tables @@ -342,9 +336,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) colfunc = fuzzcolfunc; } -#ifdef ESLOPE range = max(ds->x2-ds->x1, 1); -#endif rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; @@ -374,12 +366,9 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) for (i = 0; i < dc_numlights; i++) { -#ifdef ESLOPE fixed_t leftheight, rightheight; -#endif light = &frontsector->lightlist[i]; rlight = &dc_lightlist[i]; -#ifdef ESLOPE if (light->slope) { leftheight = P_GetZAt(light->slope, ds->leftpos.x, ds->leftpos.y); rightheight = P_GetZAt(light->slope, ds->rightpos.x, ds->rightpos.y); @@ -394,10 +383,6 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) rlight->heightstep = (rlight->heightstep-rlight->height)/(range); //if (x1 > ds->x1) //rlight->height -= (x1 - ds->x1)*rlight->heightstep; -#else - rlight->height = (centeryfrac) - FixedMul((light->height - viewz), spryscale); - rlight->heightstep = -FixedMul(rw_scalestep, (light->height - viewz)); -#endif rlight->startheight = rlight->height; // keep starting value here to reset for each repeat rlight->lightlevel = *light->lightlevel; rlight->extra_colormap = light->extra_colormap; @@ -502,40 +487,18 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) } } -#ifndef ESLOPE - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - dc_texturemid = front->floorheight > back->floorheight - ? front->floorheight : back->floorheight; - dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; - } - else - { - dc_texturemid = front->ceilingheight < back->ceilingheight - ? front->ceilingheight : back->ceilingheight; - dc_texturemid = dc_texturemid - viewz; - } - dc_texturemid += curline->sidedef->rowoffset; - - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - dc_texturemid += (textureheight[texnum])*times; - else - dc_texturemid -= (textureheight[texnum])*times; -#endif dc_texheight = textureheight[texnum]>>FRACBITS; // draw the columns for (dc_x = x1; dc_x <= x2; dc_x++) { -#ifdef ESLOPE dc_texturemid = ds->maskedtextureheight[dc_x]; if (!!(curline->linedef->flags & ML_DONTPEGBOTTOM) ^ !!(curline->linedef->flags & ML_EFFECT3)) dc_texturemid += (textureheight[texnum])*times + textureheight[texnum]; else dc_texturemid -= (textureheight[texnum])*times; -#endif // calculate lighting if (maskedtexturecol[dc_x] != INT16_MAX) { @@ -742,14 +705,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) fixed_t offsetvalue = 0; lightlist_t *light; r_lightlist_t *rlight; -#ifdef ESLOPE INT32 range; -#endif -#ifndef ESLOPE - fixed_t lheight; -#endif line_t *newline = NULL; -#ifdef ESLOPE // Render FOF sides kinda like normal sides, with the frac and step and everything // NOTE: INT64 instead of fixed_t because overflow concerns INT64 top_frac, top_step, bottom_frac, bottom_step; @@ -759,7 +716,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) INT32 oldx = -1; fixed_t left_top, left_bottom; // needed here for slope skewing pslope_t *skewslope = NULL; -#endif void (*colfunc_2s) (column_t *); @@ -816,9 +772,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else if (pfloor->flags & FF_FOG) colfunc = R_DrawFogColumn_8; -#ifdef ESLOPE range = max(ds->x2-ds->x1, 1); -#endif //SoM: Moved these up here so they are available for my lightlist calculations rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; @@ -835,14 +789,11 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) for (i = p = 0; i < dc_numlights; i++) { -#ifdef ESLOPE fixed_t leftheight, rightheight; fixed_t pfloorleft, pfloorright; INT64 overflow_test; -#endif light = &frontsector->lightlist[i]; rlight = &dc_lightlist[p]; -#ifdef ESLOPE #define SLOPEPARAMS(slope, end1, end2, normalheight) \ if (slope) { \ @@ -883,21 +834,9 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else if (overflow_test > (INT64)CLAMPMIN) rlight->heightstep = (fixed_t)overflow_test; else rlight->heightstep = CLAMPMIN; rlight->heightstep = (rlight->heightstep-rlight->height)/(range); -#else - if (light->height < *pfloor->bottomheight) - continue; - - if (light->height > *pfloor->topheight && i+1 < dc_numlights && frontsector->lightlist[i+1].height > *pfloor->topheight) - continue; - - lheight = light->height;// > *pfloor->topheight ? *pfloor->topheight + FRACUNIT : light->height; - rlight->heightstep = -FixedMul (rw_scalestep, (lheight - viewz)); - rlight->height = (centeryfrac) - FixedMul((lheight - viewz), spryscale); -#endif rlight->flags = light->flags; if (light->flags & FF_CUTLEVEL) { -#ifdef ESLOPE SLOPEPARAMS(*light->caster->b_slope, leftheight, rightheight, *light->caster->bottomheight) #undef SLOPEPARAMS leftheight -= viewz; @@ -914,11 +853,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else if (overflow_test > (INT64)CLAMPMIN) rlight->botheightstep = (fixed_t)overflow_test; else rlight->botheightstep = CLAMPMIN; rlight->botheightstep = (rlight->botheightstep-rlight->botheight)/(range); -#else - lheight = *light->caster->bottomheight;// > *pfloor->topheight ? *pfloor->topheight + FRACUNIT : *light->caster->bottomheight; - rlight->botheightstep = -FixedMul (rw_scalestep, (lheight - viewz)); - rlight->botheight = (centeryfrac) - FixedMul((lheight - viewz), spryscale); -#endif } rlight->lightlevel = *light->lightlevel; @@ -975,7 +909,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) mceilingclip = ds->sprtopclip; dc_texheight = textureheight[texnum]>>FRACBITS; -#ifdef ESLOPE // calculate both left ends if (*pfloor->t_slope) left_top = P_GetZAt(*pfloor->t_slope, ds->leftpos.x, ds->leftpos.y) - viewz; @@ -998,7 +931,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (slopeskew) dc_texturemid = left_top; else -#endif dc_texturemid = *pfloor->topheight - viewz; if (newline) @@ -1006,12 +938,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) offsetvalue = sides[newline->sidenum[0]].rowoffset; if (newline->flags & ML_DONTPEGBOTTOM) { -#ifdef ESLOPE skewslope = *pfloor->b_slope; // skew using bottom slope if (slopeskew) dc_texturemid = left_bottom; else -#endif offsetvalue -= *pfloor->topheight - *pfloor->bottomheight; } } @@ -1020,17 +950,14 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset; if (curline->linedef->flags & ML_DONTPEGBOTTOM) { -#ifdef ESLOPE skewslope = *pfloor->b_slope; // skew using bottom slope if (slopeskew) dc_texturemid = left_bottom; else -#endif offsetvalue -= *pfloor->topheight - *pfloor->bottomheight; } } -#ifdef ESLOPE if (slopeskew) { angle_t lineangle = R_PointToAngle2(curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y); @@ -1038,7 +965,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (skewslope) ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT)); } -#endif dc_texturemid += offsetvalue; @@ -1055,7 +981,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) column2s_length = textures[texnum]->height; } -#ifdef ESLOPE // Set heights according to plane, or slope, whichever { fixed_t right_top, right_bottom; @@ -1083,24 +1008,20 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) top_frac += top_step * (x1 - ds->x1); bottom_frac += bottom_step * (x1 - ds->x1); } -#endif // draw the columns for (dc_x = x1; dc_x <= x2; dc_x++) { if (maskedtexturecol[dc_x] != INT16_MAX) { -#ifdef ESLOPE if (ffloortextureslide) { // skew FOF walls if (oldx != -1) dc_texturemid += FixedMul(ffloortextureslide, (maskedtexturecol[oldx]-maskedtexturecol[dc_x])< (INT64)CLAMPMAX) sprtopscreen = windowtop = CLAMPMAX; else if (top_frac > (INT64)CLAMPMIN) sprtopscreen = windowtop = (fixed_t)top_frac; else sprtopscreen = windowtop = CLAMPMIN; @@ -1110,10 +1031,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) top_frac += top_step; bottom_frac += bottom_step; -#else - sprtopscreen = windowtop = (centeryfrac - FixedMul((dc_texturemid - offsetvalue), spryscale)); - sprbotscreen = windowbottom = FixedMul(*pfloor->topheight - *pfloor->bottomheight, spryscale) + sprtopscreen; -#endif // SoM: If column is out of range, why bother with it?? if (windowbottom < topbounds || windowtop > bottombounds) @@ -1323,9 +1240,7 @@ static void R_RenderSegLoop (void) INT32 mid; fixed_t texturecolumn = 0; -#ifdef ESLOPE fixed_t oldtexturecolumn = -1; -#endif INT32 top; INT32 bottom; INT32 i; @@ -1451,7 +1366,6 @@ static void R_RenderSegLoop (void) angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; texturecolumn = rw_offset-FixedMul(FINETANGENT(angle),rw_distance); -#ifdef ESLOPE if (oldtexturecolumn != -1) { rw_bottomtexturemid += FixedMul(rw_bottomtextureslide, oldtexturecolumn-texturecolumn); rw_midtexturemid += FixedMul(rw_midtextureslide, oldtexturecolumn-texturecolumn); @@ -1459,7 +1373,6 @@ static void R_RenderSegLoop (void) rw_midtextureback += FixedMul(rw_midtexturebackslide, oldtexturecolumn-texturecolumn); } oldtexturecolumn = texturecolumn; -#endif texturecolumn >>= FRACBITS; @@ -1637,13 +1550,11 @@ static void R_RenderSegLoop (void) // for backdrawing of masked mid texture maskedtexturecol[rw_x] = (INT16)texturecolumn; -#ifdef ESLOPE if (maskedtextureheight != NULL) { maskedtextureheight[rw_x] = (!!(curline->linedef->flags & ML_DONTPEGBOTTOM) ^ !!(curline->linedef->flags & ML_EFFECT3) ? max(rw_midtexturemid, rw_midtextureback) : min(rw_midtexturemid, rw_midtextureback)); } -#endif } if (dc_numlights) @@ -1699,26 +1610,19 @@ void R_StoreWallRange(INT32 start, INT32 stop) fixed_t sineval; angle_t distangle, offsetangle; boolean longboi; -#ifndef ESLOPE - fixed_t vtop; -#endif INT32 lightnum; INT32 i, p; lightlist_t *light; r_lightlist_t *rlight; INT32 range; -#ifdef ESLOPE vertex_t segleft, segright; fixed_t ceilingfrontslide, floorfrontslide, ceilingbackslide, floorbackslide; -#endif static size_t maxdrawsegs = 0; -#ifdef ESLOPE maskedtextureheight = NULL; //initialize segleft and segright memset(&segleft, 0x00, sizeof(segleft)); memset(&segright, 0x00, sizeof(segright)); -#endif if (ds_p == drawsegs+maxdrawsegs) { @@ -1824,7 +1728,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) // calculate texture boundaries // and decide if floor / ceiling marks are needed -#ifdef ESLOPE // Figure out map coordinates of where start and end are mapping to on seg, so we can clip right for slope bullshit if (frontsector->hasslope || (backsector && backsector->hasslope)) // Commenting this out for FOFslop. -Red { @@ -1880,22 +1783,15 @@ void R_StoreWallRange(INT32 start, INT32 stop) worldtopslope = P_GetZAt(frontsector->c_slope, segright.x, segright.y) - viewz; } else { worldtopslope = -#else - { -#endif worldtop = frontsector->ceilingheight - viewz; } -#ifdef ESLOPE if (frontsector->f_slope) { worldbottom = P_GetZAt(frontsector->f_slope, segleft.x, segleft.y) - viewz; worldbottomslope = P_GetZAt(frontsector->f_slope, segright.x, segright.y) - viewz; } else { worldbottomslope = -#else - { -#endif worldbottom = frontsector->floorheight - viewz; } @@ -1919,18 +1815,15 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; #endif -#ifdef ESLOPE if (ffloor[i].slope) { ffloor[i].f_pos = P_GetZAt(ffloor[i].slope, segleft.x, segleft.y) - viewz; ffloor[i].f_pos_slope = P_GetZAt(ffloor[i].slope, segright.x, segright.y) - viewz; } else ffloor[i].f_pos_slope = -#endif ffloor[i].f_pos = ffloor[i].height - viewz; } } -#ifdef ESLOPE // Set up texture Y offset slides for sloped walls rw_toptextureslide = rw_midtextureslide = rw_bottomtextureslide = 0; ceilingfrontslide = floorfrontslide = ceilingbackslide = floorbackslide = 0; @@ -1950,7 +1843,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (backsector && backsector->c_slope) ceilingbackslide = FixedMul(backsector->c_slope->zdelta, FINECOSINE((lineangle-backsector->c_slope->xydirection)>>ANGLETOFINESHIFT)); } -#endif if (!backsector) { @@ -1960,7 +1852,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) texheight = textureheight[midtexture]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; -#ifdef ESLOPE if (linedef->flags & ML_EFFECT2) { if (linedef->flags & ML_DONTPEGBOTTOM) rw_midtexturemid = frontsector->floorheight + texheight - viewz; @@ -1968,25 +1859,16 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_midtexturemid = frontsector->ceilingheight - viewz; } else -#endif if (linedef->flags & ML_DONTPEGBOTTOM) { -#ifdef ESLOPE rw_midtexturemid = worldbottom + texheight; rw_midtextureslide = floorfrontslide; -#else - vtop = frontsector->floorheight + texheight; - // bottom of texture at bottom - rw_midtexturemid = vtop - viewz; -#endif } else { // top of texture at top rw_midtexturemid = worldtop; -#ifdef ESLOPE rw_midtextureslide = ceilingfrontslide; -#endif } rw_midtexturemid += sidedef->rowoffset; @@ -2000,28 +1882,20 @@ void R_StoreWallRange(INT32 start, INT32 stop) { // two sided line -#ifdef ESLOPE if (backsector->c_slope) { worldhigh = P_GetZAt(backsector->c_slope, segleft.x, segleft.y) - viewz; worldhighslope = P_GetZAt(backsector->c_slope, segright.x, segright.y) - viewz; } else { worldhighslope = -#else - { -#endif worldhigh = backsector->ceilingheight - viewz; } -#ifdef ESLOPE if (backsector->f_slope) { worldlow = P_GetZAt(backsector->f_slope, segleft.x, segleft.y) - viewz; worldlowslope = P_GetZAt(backsector->f_slope, segright.x, segright.y) - viewz; } else { worldlowslope = -#else - { -#endif worldlow = backsector->floorheight - viewz; } @@ -2030,9 +1904,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (frontsector->ceilingpic == skyflatnum && backsector->ceilingpic == skyflatnum) { -#ifdef ESLOPE worldtopslope = worldhighslope = -#endif worldtop = worldhigh; } @@ -2040,26 +1912,16 @@ void R_StoreWallRange(INT32 start, INT32 stop) ds_p->silhouette = 0; if ( -#ifdef ESLOPE worldbottomslope > worldlowslope || -#endif worldbottom > worldlow) { ds_p->silhouette = SIL_BOTTOM; -#ifdef ESLOPE if ((backsector->f_slope ? P_GetZAt(backsector->f_slope, viewx, viewy) : backsector->floorheight) > viewz) ds_p->bsilheight = INT32_MAX; else ds_p->bsilheight = (frontsector->f_slope ? INT32_MAX : frontsector->floorheight); -#else - ds_p->bsilheight = frontsector->floorheight; -#endif } -#ifdef ESLOPE else if ((backsector->f_slope ? P_GetZAt(backsector->f_slope, viewx, viewy) : backsector->floorheight) > viewz) -#else - else if (backsector->floorheight > viewz) -#endif { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = INT32_MAX; @@ -2067,26 +1929,16 @@ void R_StoreWallRange(INT32 start, INT32 stop) } if ( -#ifdef ESLOPE worldtopslope < worldhighslope || -#endif worldtop < worldhigh) { ds_p->silhouette |= SIL_TOP; -#ifdef ESLOPE if ((backsector->c_slope ? P_GetZAt(backsector->c_slope, viewx, viewy) : backsector->ceilingheight) < viewz) ds_p->tsilheight = INT32_MIN; else ds_p->tsilheight = (frontsector->c_slope ? INT32_MIN : frontsector->ceilingheight); -#else - ds_p->tsilheight = frontsector->ceilingheight; -#endif } -#ifdef ESLOPE else if ((backsector->c_slope ? P_GetZAt(backsector->c_slope, viewx, viewy) : backsector->ceilingheight) < viewz) -#else - else if (backsector->ceilingheight < viewz) -#endif { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT32_MIN; @@ -2095,22 +1947,14 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (viewsector != frontsector && viewsector != backsector) { -#ifdef ESLOPE if (worldhigh <= worldbottom && worldhighslope <= worldbottomslope) -#else - if (worldhigh <= worldbottom) -#endif { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT32_MAX; ds_p->silhouette |= SIL_BOTTOM; } -#ifdef ESLOPE if (worldlow >= worldtop && worldlowslope >= worldtopslope) -#else - if (worldlow >= worldtop) -#endif { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT32_MIN; @@ -2121,21 +1965,13 @@ void R_StoreWallRange(INT32 start, INT32 stop) // frontsector->ceiling and backsector->floor to see if a door was closed. // Without the following code, sprites get displayed behind closed doors. { -#ifdef ESLOPE if (doorclosed || (worldhigh <= worldbottom && worldhighslope <= worldbottomslope)) -#else - if (doorclosed || backsector->ceilingheight <= frontsector->floorheight) -#endif { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT32_MAX; ds_p->silhouette |= SIL_BOTTOM; } -#ifdef ESLOPE if (doorclosed || (worldlow >= worldtop && worldlowslope >= worldtopslope)) -#else - if (doorclosed || backsector->floorheight >= frontsector->ceilingheight) -#endif { // killough 1/17/98, 2/8/98 ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT32_MIN; @@ -2145,10 +1981,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) } if (worldlow != worldbottom -#ifdef ESLOPE || worldlowslope != worldbottomslope || backsector->f_slope != frontsector->f_slope -#endif || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel //SoM: 3/22/2000: Check floor x and y offsets. @@ -2171,10 +2005,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) } if (worldhigh != worldtop -#ifdef ESLOPE || worldhighslope != worldtopslope || backsector->c_slope != frontsector->c_slope -#endif || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel //SoM: 3/22/2000: Check floor x and y offsets. @@ -2205,9 +2037,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) // check TOP TEXTURE if (worldhigh < worldtop -#ifdef ESLOPE || worldhighslope < worldtopslope -#endif ) { fixed_t texheight; @@ -2228,66 +2058,48 @@ void R_StoreWallRange(INT32 start, INT32 stop) toptexture = R_GetTextureNum(sidedef->toptexture); texheight = textureheight[toptexture]; } -#ifdef ESLOPE if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked if (linedef->flags & ML_DONTPEGTOP) rw_toptexturemid = frontsector->ceilingheight - viewz; else rw_toptexturemid = backsector->ceilingheight - viewz; } else -#endif if (linedef->flags & ML_DONTPEGTOP) { // top of texture at top rw_toptexturemid = worldtop; -#ifdef ESLOPE rw_toptextureslide = ceilingfrontslide; -#endif } else { -#ifdef ESLOPE rw_toptexturemid = worldhigh + texheight; rw_toptextureslide = ceilingbackslide; -#else - vtop = backsector->ceilingheight + texheight; - // bottom of texture - rw_toptexturemid = vtop - viewz; -#endif } } // check BOTTOM TEXTURE if (worldlow > worldbottom -#ifdef ESLOPE || worldlowslope > worldbottomslope -#endif ) //seulement si VISIBLE!!! { // bottom texture bottomtexture = R_GetTextureNum(sidedef->bottomtexture); -#ifdef ESLOPE if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked if (linedef->flags & ML_DONTPEGBOTTOM) rw_bottomtexturemid = frontsector->floorheight - viewz; else rw_bottomtexturemid = backsector->floorheight - viewz; } else -#endif if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom // top of texture at top rw_bottomtexturemid = worldbottom; -#ifdef ESLOPE rw_bottomtextureslide = floorfrontslide; -#endif } else { // top of texture at top rw_bottomtexturemid = worldlow; -#ifdef ESLOPE rw_bottomtextureslide = floorbackslide; -#endif } } @@ -2300,12 +2112,10 @@ void R_StoreWallRange(INT32 start, INT32 stop) ffloor_t *rover; ffloor_t *r2; fixed_t lowcut, highcut; -#ifdef ESLOPE fixed_t lowcutslope, highcutslope; // Used for height comparisons and etc across FOFs and slopes fixed_t high1, highslope1, low1, lowslope1, high2, highslope2, low2, lowslope2; -#endif //markceiling = markfloor = true; maskedtexture = true; @@ -2315,10 +2125,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) lowcut = max(worldbottom, worldlow) + viewz; highcut = min(worldtop, worldhigh) + viewz; -#ifdef ESLOPE lowcutslope = max(worldbottomslope, worldlowslope) + viewz; highcutslope = min(worldtopslope, worldhighslope) + viewz; -#endif if (frontsector->ffloors && backsector->ffloors) { @@ -2333,7 +2141,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (rover->norender == leveltime) continue; -#ifdef ESLOPE if (*rover->t_slope) { high1 = P_GetZAt(*rover->t_slope, segleft.x, segleft.y); highslope1 = P_GetZAt(*rover->t_slope, segright.x, segright.y); @@ -2347,10 +2154,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; -#else - if (*rover->topheight < lowcut || *rover->bottomheight > highcut) - continue; -#endif for (r2 = frontsector->ffloors; r2; r2 = r2->next) { @@ -2374,7 +2177,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; } -#ifdef ESLOPE if (*r2->t_slope) { high2 = P_GetZAt(*r2->t_slope, segleft.x, segleft.y); highslope2 = P_GetZAt(*r2->t_slope, segright.x, segright.y); @@ -2390,12 +2192,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; if ((high1 > high2 || highslope1 > highslope2) || (low1 < low2 || lowslope1 < lowslope2)) continue; -#else - if (*r2->topheight < lowcut || *r2->bottomheight > highcut) - continue; - if (*rover->topheight > *r2->topheight || *rover->bottomheight < *r2->bottomheight) - continue; -#endif break; } @@ -2416,7 +2212,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (rover->norender == leveltime) continue; -#ifdef ESLOPE if (*rover->t_slope) { high1 = P_GetZAt(*rover->t_slope, segleft.x, segleft.y); highslope1 = P_GetZAt(*rover->t_slope, segright.x, segright.y); @@ -2430,10 +2225,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; -#else - if (*rover->topheight < lowcut || *rover->bottomheight > highcut) - continue; -#endif for (r2 = backsector->ffloors; r2; r2 = r2->next) { @@ -2457,7 +2248,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; } -#ifdef ESLOPE if (*r2->t_slope) { high2 = P_GetZAt(*r2->t_slope, segleft.x, segleft.y); highslope2 = P_GetZAt(*r2->t_slope, segright.x, segright.y); @@ -2473,12 +2263,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; if ((high1 > high2 || highslope1 > highslope2) || (low1 < low2 || lowslope1 < lowslope2)) continue; -#else - if (*r2->topheight < lowcut || *r2->bottomheight > highcut) - continue; - if (*rover->topheight > *r2->topheight || *rover->bottomheight < *r2->bottomheight) - continue; -#endif break; } @@ -2498,17 +2282,12 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (rover->norender == leveltime) continue; -#ifdef ESLOPE // Oy vey. if (( (*rover->t_slope ? P_GetZAt(*rover->t_slope, segleft.x, segleft.y) : *rover->topheight) <= worldbottom+viewz && (*rover->t_slope ? P_GetZAt(*rover->t_slope, segright.x, segright.y) : *rover->topheight) <= worldbottomslope+viewz) ||((*rover->b_slope ? P_GetZAt(*rover->b_slope, segleft.x, segleft.y) : *rover->bottomheight) >= worldtop+viewz && (*rover->b_slope ? P_GetZAt(*rover->b_slope, segright.x, segright.y) : *rover->bottomheight) >= worldtopslope+viewz)) continue; -#else - if (*rover->topheight <= frontsector->floorheight || *rover->bottomheight >= frontsector->ceilingheight) - continue; -#endif ds_p->thicksides[i] = rover; i++; @@ -2522,7 +2301,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; if (rover->norender == leveltime) continue; -#ifdef ESLOPE // Oy vey. if (( (*rover->t_slope ? P_GetZAt(*rover->t_slope, segleft.x, segleft.y) : *rover->topheight) <= worldbottom+viewz && (*rover->t_slope ? P_GetZAt(*rover->t_slope, segright.x, segright.y) : *rover->topheight) <= worldbottomslope+viewz) @@ -2535,12 +2313,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) ||((*rover->b_slope ? P_GetZAt(*rover->b_slope, segleft.x, segleft.y) : *rover->bottomheight) >= worldhigh+viewz && (*rover->b_slope ? P_GetZAt(*rover->b_slope, segright.x, segright.y) : *rover->bottomheight) >= worldhighslope+viewz)) continue; -#else - if (*rover->topheight <= frontsector->floorheight || *rover->bottomheight >= frontsector->ceilingheight) - continue; - if (*rover->topheight <= backsector->floorheight || *rover->bottomheight >= backsector->ceilingheight) - continue; -#endif ds_p->thicksides[i] = rover; i++; @@ -2560,7 +2332,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) else ds_p->maskedtexturecol = ds_p->thicksidecol; -#ifdef ESLOPE maskedtextureheight = ds_p->maskedtextureheight; // note to red, this == &(ds_p->maskedtextureheight[0]) #ifdef POLYOBJECTS @@ -2571,7 +2342,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) else rw_midtexturemid = rw_midtextureback = min(curline->frontsector->ceilingheight, curline->backsector->ceilingheight) - viewz; } else -#endif // Set midtexture starting height if (linedef->flags & ML_EFFECT2) { // Ignore slopes when texturing rw_midtextureslide = rw_midtexturebackslide = 0; @@ -2658,9 +2428,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (frontsector->heightsec == -1) { if (( -#ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, viewx, viewy) : -#endif frontsector->floorheight) >= viewz) { // above view plane @@ -2668,9 +2436,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) } if (( -#ifdef ESLOPE frontsector->c_slope ? P_GetZAt(frontsector->c_slope, viewx, viewy) : -#endif frontsector->ceilingheight) <= viewz && frontsector->ceilingpic != skyflatnum) { @@ -2682,10 +2448,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) // calculate incremental stepping values for texture edges worldtop >>= 4; worldbottom >>= 4; -#ifdef ESLOPE worldtopslope >>= 4; worldbottomslope >>= 4; -#endif if (linedef->special == 41) { // HORIZON LINES topstep = bottomstep = 0; @@ -2698,7 +2462,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) bottomstep = -FixedMul (rw_scalestep,worldbottom); bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale); -#ifdef ESLOPE if (frontsector->c_slope) { fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldtopslope, ds_p->scale2); topstep = (topfracend-topfrac)/(range); @@ -2707,7 +2470,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldbottomslope, ds_p->scale2); bottomstep = (bottomfracend-bottomfrac)/(range); } -#endif } dc_numlights = 0; @@ -2723,14 +2485,11 @@ void R_StoreWallRange(INT32 start, INT32 stop) for (i = p = 0; i < dc_numlights; i++) { -#ifdef ESLOPE fixed_t leftheight, rightheight; -#endif light = &frontsector->lightlist[i]; rlight = &dc_lightlist[p]; -#ifdef ESLOPE if (light->slope) { leftheight = P_GetZAt(light->slope, segleft.x, segleft.y); rightheight = P_GetZAt(light->slope, segright.x, segright.y); @@ -2745,38 +2504,23 @@ void R_StoreWallRange(INT32 start, INT32 stop) leftheight >>= 4; rightheight >>= 4; -#endif if (i != 0) { -#ifdef ESLOPE if (leftheight < worldbottom && rightheight < worldbottomslope) continue; if (leftheight > worldtop && rightheight > worldtopslope && i+1 < dc_numlights && frontsector->lightlist[i+1].height > frontsector->ceilingheight) continue; -#else - if (light->height < frontsector->floorheight) - continue; - - if (light->height > frontsector->ceilingheight && i+1 < dc_numlights && frontsector->lightlist[i+1].height > frontsector->ceilingheight) - continue; -#endif } -#ifdef ESLOPE rlight->height = (centeryfrac>>4) - FixedMul(leftheight, rw_scale); rlight->heightstep = (centeryfrac>>4) - FixedMul(rightheight, ds_p->scale2); rlight->heightstep = (rlight->heightstep-rlight->height)/(range); -#else - rlight->height = (centeryfrac>>4) - FixedMul((light->height - viewz) >> 4, rw_scale); - rlight->heightstep = -FixedMul (rw_scalestep, (light->height - viewz) >> 4); -#endif rlight->flags = light->flags; if (light->caster && light->caster->flags & FF_CUTSOLIDS) { -#ifdef ESLOPE if (*light->caster->b_slope) { leftheight = P_GetZAt(*light->caster->b_slope, segleft.x, segleft.y); rightheight = P_GetZAt(*light->caster->b_slope, segright.x, segright.y); @@ -2796,10 +2540,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) rlight->botheightstep = (centeryfrac>>4) - FixedMul(rightheight, ds_p->scale2); rlight->botheightstep = (rlight->botheightstep-rlight->botheight)/(range); -#else - rlight->botheight = (centeryfrac >> 4) - FixedMul((*light->caster->bottomheight - viewz) >> 4, rw_scale); - rlight->botheightstep = -FixedMul (rw_scalestep, (*light->caster->bottomheight - viewz) >> 4); -#endif } rlight->lightlevel = *light->lightlevel; @@ -2815,14 +2555,9 @@ void R_StoreWallRange(INT32 start, INT32 stop) for (i = 0; i < numffloors; i++) { ffloor[i].f_pos >>= 4; -#ifdef ESLOPE ffloor[i].f_pos_slope >>= 4; ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale); ffloor[i].f_step = ((centeryfrac>>4) - FixedMul(ffloor[i].f_pos_slope, ds_p->scale2) - ffloor[i].f_frac)/(range); -#else - ffloor[i].f_step = FixedMul(-rw_scalestep, ffloor[i].f_pos); - ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale); -#endif } } @@ -2830,42 +2565,34 @@ void R_StoreWallRange(INT32 start, INT32 stop) { worldhigh >>= 4; worldlow >>= 4; -#ifdef ESLOPE worldhighslope >>= 4; worldlowslope >>= 4; -#endif if (toptexture) { pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale); pixhighstep = -FixedMul (rw_scalestep,worldhigh); -#ifdef ESLOPE if (backsector->c_slope) { fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldhighslope, ds_p->scale2); pixhighstep = (topfracend-pixhigh)/(range); } -#endif } if (bottomtexture) { pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale); pixlowstep = -FixedMul (rw_scalestep,worldlow); -#ifdef ESLOPE if (backsector->f_slope) { fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldlowslope, ds_p->scale2); pixlowstep = (bottomfracend-pixlow)/(range); } -#endif } { ffloor_t * rover; -#ifdef ESLOPE fixed_t roverleft, roverright; fixed_t planevistest; -#endif i = 0; if (backsector->ffloors) @@ -2877,7 +2604,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (rover->norender == leveltime) continue; -#ifdef ESLOPE // Let the renderer know this sector is sloped. if (*rover->b_slope || *rover->t_slope) backsector->hasslope = true; @@ -2924,34 +2650,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) ffloor[i].b_step = (ffloor[i].b_step-ffloor[i].b_frac)/(range); i++; } -#else - if (*rover->bottomheight <= backsector->ceilingheight && - *rover->bottomheight >= backsector->floorheight && - ((viewz < *rover->bottomheight && !(rover->flags & FF_INVERTPLANES)) || - (viewz > *rover->bottomheight && (rover->flags & FF_BOTHPLANES)))) - { - ffloor[i].b_pos = *rover->bottomheight; - ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; - ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); - ffloor[i].b_frac = (centeryfrac >> 4) - FixedMul(ffloor[i].b_pos, rw_scale); - i++; - } - - if (i >= MAXFFLOORS) - break; - - if (*rover->topheight >= backsector->floorheight && - *rover->topheight <= backsector->ceilingheight && - ((viewz > *rover->topheight && !(rover->flags & FF_INVERTPLANES)) || - (viewz < *rover->topheight && (rover->flags & FF_BOTHPLANES)))) - { - ffloor[i].b_pos = *rover->topheight; - ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; - ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); - ffloor[i].b_frac = (centeryfrac >> 4) - FixedMul(ffloor[i].b_pos, rw_scale); - i++; - } -#endif } } else if (frontsector && frontsector->ffloors) @@ -2964,7 +2662,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) continue; -#ifdef ESLOPE // Let the renderer know this sector is sloped. if (*rover->b_slope || *rover->t_slope) frontsector->hasslope = true; @@ -3011,32 +2708,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) ffloor[i].b_step = (ffloor[i].b_step-ffloor[i].b_frac)/(range); i++; } -#else - if (*rover->bottomheight <= frontsector->ceilingheight && - *rover->bottomheight >= frontsector->floorheight && - ((viewz < *rover->bottomheight && !(rover->flags & FF_INVERTPLANES)) || - (viewz > *rover->bottomheight && (rover->flags & FF_BOTHPLANES)))) - { - ffloor[i].b_pos = *rover->bottomheight; - ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; - ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); - ffloor[i].b_frac = (centeryfrac >> 4) - FixedMul(ffloor[i].b_pos, rw_scale); - i++; - } - if (i >= MAXFFLOORS) - break; - if (*rover->topheight >= frontsector->floorheight && - *rover->topheight <= frontsector->ceilingheight && - ((viewz > *rover->topheight && !(rover->flags & FF_INVERTPLANES)) || - (viewz < *rover->topheight && (rover->flags & FF_BOTHPLANES)))) - { - ffloor[i].b_pos = *rover->topheight; - ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; - ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); - ffloor[i].b_frac = (centeryfrac >> 4) - FixedMul(ffloor[i].b_pos, rw_scale); - i++; - } -#endif } } #ifdef POLYOBJECTS_PLANES @@ -3053,9 +2724,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (ffloor[i].plane->maxx < ds_p->x2) ffloor[i].plane->maxx = ds_p->x2; -#ifdef ESLOPE ffloor[i].slope = NULL; -#endif ffloor[i].b_pos = backsector->floorheight; ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); @@ -3072,9 +2741,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (ffloor[i].plane->maxx < ds_p->x2) ffloor[i].plane->maxx = ds_p->x2; -#ifdef ESLOPE ffloor[i].slope = NULL; -#endif ffloor[i].b_pos = backsector->ceilingheight; ffloor[i].b_pos = (ffloor[i].b_pos - viewz) >> 4; ffloor[i].b_step = FixedMul(-rw_scalestep, ffloor[i].b_pos); diff --git a/src/r_things.c b/src/r_things.c index 59e148837..3b3cbe4dd 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1052,10 +1052,8 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) if (!(sector->lightlist[i].caster->flags & FF_CUTSPRITES)) continue; -#ifdef ESLOPE if (sector->lightlist[i].slope) testheight = P_GetZAt(sector->lightlist[i].slope, sprite->gx, sprite->gy); -#endif if (testheight >= sprite->gzt) continue; @@ -1373,7 +1371,6 @@ static void R_ProjectSprite(mobj_t *thing) if (thing->subsector->sector->numlights) { INT32 lightnum; -#ifdef ESLOPE // R_GetPlaneLight won't work on sloped lights! light = thing->subsector->sector->numlights - 1; for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { @@ -1384,9 +1381,6 @@ static void R_ProjectSprite(mobj_t *thing) break; } } -#else - light = R_GetPlaneLight(thing->subsector->sector, gzt, false); -#endif lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT); if (lightnum < 0) @@ -2052,13 +2046,11 @@ static void R_CreateDrawNodes(void) if (rover->szt > r2->plane->low || rover->sz < r2->plane->high) continue; -#ifdef ESLOPE // Effective height may be different for each comparison in the case of slopes if (r2->plane->slope) { planeobjectz = P_GetZAt(r2->plane->slope, rover->gx, rover->gy); planecameraz = P_GetZAt(r2->plane->slope, viewx, viewy); } else -#endif planeobjectz = planecameraz = r2->plane->height; if (rover->mobjflags & MF_NOCLIPHEIGHT) @@ -2117,20 +2109,16 @@ static void R_CreateDrawNodes(void) if (scale <= rover->sortscale) continue; -#ifdef ESLOPE if (*r2->ffloor->t_slope) { topplaneobjectz = P_GetZAt(*r2->ffloor->t_slope, rover->gx, rover->gy); topplanecameraz = P_GetZAt(*r2->ffloor->t_slope, viewx, viewy); } else -#endif topplaneobjectz = topplanecameraz = *r2->ffloor->topheight; -#ifdef ESLOPE if (*r2->ffloor->b_slope) { botplaneobjectz = P_GetZAt(*r2->ffloor->b_slope, rover->gx, rover->gy); botplanecameraz = P_GetZAt(*r2->ffloor->b_slope, viewx, viewy); } else -#endif botplaneobjectz = botplanecameraz = *r2->ffloor->bottomheight; if ((topplanecameraz > viewz && botplanecameraz < viewz) || From 2d9ec1687c90af7d18167e66e9371e8466bb70d6 Mon Sep 17 00:00:00 2001 From: FlykeSpice Date: Thu, 20 Aug 2020 13:00:54 -0400 Subject: [PATCH 078/193] Remove POLYOBJECTS POLYOBJECTS_PLANES #ifdefs(backport from srb2 2.2) --- src/lua_blockmaplib.c | 4 ---- src/p_map.c | 4 ---- src/p_maputl.c | 10 ---------- src/p_mobj.c | 2 -- src/p_polyobj.c | 2 -- src/p_polyobj.h | 2 -- src/p_saveg.c | 16 ---------------- src/p_setup.c | 8 -------- src/p_sight.c | 11 ----------- src/p_spec.c | 6 ------ src/p_user.c | 14 -------------- src/r_bsp.c | 18 ------------------ src/r_bsp.h | 2 -- src/r_defs.h | 8 -------- src/r_plane.c | 16 ---------------- src/r_plane.h | 6 ------ src/r_segs.c | 12 ------------ src/r_things.c | 4 ---- 18 files changed, 145 deletions(-) diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index dabbdd9f6..0e705e2ea 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -79,9 +79,7 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th { INT32 offset; const INT32 *list; // Big blockmap -#ifdef POLYOBJECTS polymaplink_t *plink; // haleyjd 02/22/06 -#endif line_t *ld; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) @@ -89,7 +87,6 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th offset = y*bmapwidth + x; -#ifdef POLYOBJECTS // haleyjd 02/22/06: consider polyobject lines plink = polyblocklinks[offset]; @@ -132,7 +129,6 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th } plink = (polymaplink_t *)(plink->link.next); } -#endif offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x]; diff --git a/src/p_map.c b/src/p_map.c index 369b3856b..5d8a45afe 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2139,7 +2139,6 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) BMBOUNDFIX(xl, xh, yl, yh); -#ifdef POLYOBJECTS // Check polyobjects and see if tmfloorz/tmceilingz need to be altered { validcount++; @@ -2209,7 +2208,6 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) } } } -#endif // tmfloorthing is set when tmfloorz comes from a thing's top tmfloorthing = NULL; @@ -2367,7 +2365,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam) BMBOUNDFIX(xl, xh, yl, yh); -#ifdef POLYOBJECTS // Check polyobjects and see if tmfloorz/tmceilingz need to be altered { validcount++; @@ -2438,7 +2435,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam) } } } -#endif // check lines for (bx = xl; bx <= xh; bx++) diff --git a/src/p_maputl.c b/src/p_maputl.c index 97b3386ff..260eb3ec6 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -491,14 +491,12 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) } // Treat polyobjects kind of like 3D Floors -#ifdef POLYOBJECTS if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) { front = linedef->frontsector; back = linedef->frontsector; } else -#endif { front = linedef->frontsector; back = linedef->backsector; @@ -607,9 +605,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) // Check for fake floors in the sector. if (front->ffloors || back->ffloors -#ifdef POLYOBJECTS || linedef->polyobj -#endif ) { ffloor_t *rover; @@ -702,7 +698,6 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) } } -#ifdef POLYOBJECTS // Treat polyobj's backsector like a 3D Floor if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) { @@ -724,7 +719,6 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) else if (polysec->ceilingheight > lowestfloor && delta1 < delta2) lowestfloor = polysec->ceilingheight; } -#endif if (highestceiling < highceiling) highceiling = highestceiling; @@ -987,9 +981,7 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *)) { INT32 offset; const INT32 *list; // Big blockmap -#ifdef POLYOBJECTS polymaplink_t *plink; // haleyjd 02/22/06 -#endif line_t *ld; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) @@ -997,7 +989,6 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *)) offset = y*bmapwidth + x; -#ifdef POLYOBJECTS // haleyjd 02/22/06: consider polyobject lines plink = polyblocklinks[offset]; @@ -1021,7 +1012,6 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *)) } plink = (polymaplink_t *)(plink->link.next); } -#endif offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x]; diff --git a/src/p_mobj.c b/src/p_mobj.c index 895fd2fbb..56b049c0b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2652,7 +2652,6 @@ static void P_PlayerZMovement(mobj_t *mo) if (mo->eflags & MFE_JUSTHITFLOOR) { -#ifdef POLYOBJECTS // Check if we're on a polyobject // that triggers a linedef executor. msecnode_t *node; @@ -2712,7 +2711,6 @@ static void P_PlayerZMovement(mobj_t *mo) } if (!stopmovecut) -#endif // Cut momentum in half when you hit the ground and // aren't pressing any controls. diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 03fb10d0f..60c653b16 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -28,7 +28,6 @@ #include "r_state.h" #include "r_defs.h" -#ifdef POLYOBJECTS /* Theory behind Polyobjects: @@ -2864,6 +2863,5 @@ INT32 EV_DoPolyObjFlag(line_t *pfdata) return 1; } -#endif // ifdef POLYOBJECTS // EOF diff --git a/src/p_polyobj.h b/src/p_polyobj.h index 60e996cae..9a47cd150 100644 --- a/src/p_polyobj.h +++ b/src/p_polyobj.h @@ -19,7 +19,6 @@ #include "r_defs.h" // haleyjd: temporary define -#ifdef POLYOBJECTS // // Defines // @@ -303,7 +302,6 @@ extern polyobj_t *PolyObjects; extern INT32 numPolyObjects; extern polymaplink_t **polyblocklinks; // polyobject blockmap -#endif // ifdef POLYOBJECTS #endif diff --git a/src/p_saveg.c b/src/p_saveg.c index 32be2b3ff..f587c6e69 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -981,7 +981,6 @@ typedef enum tc_noenemies, tc_eachtime, tc_disappear, -#ifdef POLYOBJECTS tc_polyrotate, // haleyjd 03/26/06: polyobjects tc_polymove, tc_polywaypoint, @@ -989,7 +988,6 @@ typedef enum tc_polyswingdoor, tc_polyflag, tc_polydisplace, -#endif tc_end } specials_e; @@ -1544,7 +1542,6 @@ static void SaveDisappearThinker(const thinker_t *th, const UINT8 type) WRITEINT32(save_p, ht->exists); } -#ifdef POLYOBJECTS // // SavePolyrotateThinker @@ -1654,7 +1651,6 @@ static void SavePolydisplaceThinker(const thinker_t *th, const UINT8 type) WRITEFIXED(save_p, ht->oldHeights); } -#endif /* // // SaveWhatThinker @@ -1834,7 +1830,6 @@ static void P_NetArchiveThinkers(void) SaveDisappearThinker(th, tc_disappear); continue; } -#ifdef POLYOBJECTS else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate) { SavePolyrotatetThinker(th, tc_polyrotate); @@ -1870,7 +1865,6 @@ static void P_NetArchiveThinkers(void) SavePolydisplaceThinker(th, tc_polydisplace); continue; } -#endif #ifdef PARANOIA else if (th->function.acv != P_RemoveThinkerDelayed) // wait garbage collection I_Error("unknown thinker type %p", th->function.acp1); @@ -2507,7 +2501,6 @@ static inline void LoadDisappearThinker(actionf_p1 thinker) P_AddThinker(&ht->thinker); } -#ifdef POLYOBJECTS // // LoadPolyrotateThinker @@ -2627,7 +2620,6 @@ static inline void LoadPolydisplaceThinker(actionf_p1 thinker) ht->oldHeights = READFIXED(save_p); P_AddThinker(&ht->thinker); } -#endif /* // @@ -2796,7 +2788,6 @@ static void P_NetUnArchiveThinkers(void) case tc_disappear: LoadDisappearThinker((actionf_p1)T_Disappear); break; -#ifdef POLYOBJECTS case tc_polyrotate: LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate); break; @@ -2824,7 +2815,6 @@ static void P_NetUnArchiveThinkers(void) case tc_polydisplace: LoadPolydisplaceThinker((actionf_p1)T_PolyObjDisplace); break; -#endif case tc_scroll: LoadScrollThinker((actionf_p1)T_Scroll); break; @@ -2865,7 +2855,6 @@ static void P_NetUnArchiveThinkers(void) // // haleyjd 03/26/06: PolyObject saving code // -#ifdef POLYOBJECTS #define PD_FLAGS 0x01 #define PD_TRANS 0x02 @@ -2954,7 +2943,6 @@ static inline void P_UnArchivePolyObjects(void) for (i = 0; i < numSavedPolys; ++i) P_UnArchivePolyObj(&PolyObjects[i]); } -#endif // // P_FinishMobjs // @@ -3435,9 +3423,7 @@ void P_SaveNetGame(void) if (gamestate == GS_LEVEL) { P_NetArchiveWorld(); -#ifdef POLYOBJECTS P_ArchivePolyObjects(); -#endif P_NetArchiveThinkers(); P_NetArchiveSpecials(); } @@ -3479,9 +3465,7 @@ boolean P_LoadNetGame(void) if (gamestate == GS_LEVEL) { P_NetUnArchiveWorld(); -#ifdef POLYOBJECTS P_UnArchivePolyObjects(); -#endif P_NetUnArchiveThinkers(); P_NetUnArchiveSpecials(); P_RelinkPointers(); diff --git a/src/p_setup.c b/src/p_setup.c index 34d4b22f2..f9e23a5ea 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1273,9 +1273,7 @@ static void P_LoadRawLineDefs(UINT8 *data, size_t i) if (ld->sidenum[1] != 0xffff && ld->special) sides[ld->sidenum[1]].special = ld->special; -#ifdef POLYOBJECTS ld->polyobj = NULL; -#endif } } @@ -1945,11 +1943,9 @@ static void P_CreateBlockMap(void) blocklinks = Z_Calloc(count, PU_LEVEL, NULL); blockmap = blockmaplump + 4; -#ifdef POLYOBJECTS // haleyjd 2/22/06: setup polyobject blockmap count = sizeof(*polyblocklinks) * bmapwidth * bmapheight; polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL); -#endif } } @@ -2022,11 +2018,9 @@ static boolean P_LoadBlockMap(lumpnum_t lumpnum) blocklinks = Z_Calloc(count, PU_LEVEL, NULL); blockmap = blockmaplump+4; -#ifdef POLYOBJECTS // haleyjd 2/22/06: setup polyobject blockmap count = sizeof(*polyblocklinks) * bmapwidth * bmapheight; polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL); -#endif return true; /* Original blockmaplump = W_CacheLumpNum(lump, PU_LEVEL); @@ -2088,11 +2082,9 @@ static boolean P_LoadRawBlockMap(UINT8 *data, size_t count, const char *lumpname blocklinks = Z_Calloc(count, PU_LEVEL, NULL); blockmap = blockmaplump+4; -#ifdef POLYOBJECTS // haleyjd 2/22/06: setup polyobject blockmap count = sizeof(*polyblocklinks) * bmapwidth * bmapheight; polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL); -#endif return true; #endif } diff --git a/src/p_sight.c b/src/p_sight.c index 626f8bbef..87c52f162 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -99,7 +99,6 @@ static fixed_t P_InterceptVector2(divline_t *v2, divline_t *v1) return frac; } -#ifdef POLYOBJECTS static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) { size_t i; @@ -145,7 +144,6 @@ static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) return true; } -#endif // // P_CrossSubsector @@ -156,9 +154,7 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) { seg_t *seg; INT32 count; -#ifdef POLYOBJECTS polyobj_t *po; // haleyjd 02/23/06 -#endif #ifdef RANGECHECK if (num >= numsubsectors) @@ -168,7 +164,6 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) // haleyjd 02/23/06: this assignment should be after the above check seg = segs + subsectors[num].firstline; -#ifdef POLYOBJECTS // haleyjd 02/23/06: check polyobject lines if ((po = subsectors[num].polyList)) { @@ -183,7 +178,6 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) po = (polyobj_t *)(po->link.next); } } -#endif for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines { @@ -334,15 +328,10 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) // killough 11/98: shortcut for melee situations // same subsector? obviously visible -#ifndef POLYOBJECTS - if (t1->subsector == t2->subsector) - return true; -#else // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec if (!t1->subsector->polyList && t1->subsector == t2->subsector) return true; -#endif // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. diff --git a/src/p_spec.c b/src/p_spec.c index ffc4bff25..04d69c9b9 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1242,7 +1242,6 @@ INT32 P_FindSpecialLineFromTag(INT16 special, INT16 tag, INT32 start) } // haleyjd: temporary define -#ifdef POLYOBJECTS // // PolyDoor @@ -1451,7 +1450,6 @@ static boolean PolyDisplace(line_t *line) return EV_DoPolyObjDisplace(&pdd); } -#endif // ifdef POLYOBJECTS /** Changes a sector's tag. * Used by the linedef executor tag changer and by crumblers. @@ -3200,7 +3198,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; } -#ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing PolyDoor(line); @@ -3227,7 +3224,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 491: PolyTranslucency(line); break; -#endif default: break; @@ -6698,7 +6694,6 @@ void P_SpawnSpecials(INT32 fromnetsave) Z_Free(secthinkers); -#ifdef POLYOBJECTS // haleyjd 02/20/06: spawn polyobjects Polyobj_InitLevel(); @@ -6715,7 +6710,6 @@ void P_SpawnSpecials(INT32 fromnetsave) break; } } -#endif P_RunLevelLoadExecutors(); } diff --git a/src/p_user.c b/src/p_user.c index ad5bdffc6..cb6b7bd62 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3138,7 +3138,6 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused. BMBOUNDFIX(xl, xh, yl, yh); // Polyobjects -#ifdef POLYOBJECTS validcount++; for (by = yl; by <= yh; by++) @@ -3232,7 +3231,6 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused. plink = (polymaplink_t *)(plink->link.next); } } -#endif if (teeter) // only bother with objects as a last resort if you were already teetering { mobj_t *oldtmthing = tmthing; @@ -3809,9 +3807,7 @@ static void P_2dMovement(player_t *player) else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt player->cmomx = player->cmomy = 0; else if (player->onconveyor != 2 && player->onconveyor != 4 -#ifdef POLYOBJECTS && player->onconveyor != 1 -#endif ) player->cmomx = player->cmomy = 0; @@ -4006,9 +4002,7 @@ static void P_3dMovement(player_t *player) else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt player->cmomx = player->cmomy = 0; else if (player->onconveyor != 2 && player->onconveyor != 4 -#ifdef POLYOBJECTS && player->onconveyor != 1 -#endif ) player->cmomx = player->cmomy = 0; @@ -7767,7 +7761,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } } -#ifdef POLYOBJECTS // Check polyobjects and see if floorz/ceilingz need to be altered { INT32 xl, xh, yl, yh, bx, by; @@ -7846,7 +7839,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } } } -#endif // crushed camera if (myceilingz <= myfloorz + thiscam->height && !resetcalled && !cameranoclip) @@ -8425,9 +8417,7 @@ void P_PlayerThink(player_t *player) P_MobjCheckWater(player->mo); #ifndef SECTORSPECIALSAFTERTHINK -#ifdef POLYOBJECTS if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo)) -#endif player->onconveyor = 0; // check special sectors : damage & secrets @@ -8585,10 +8575,8 @@ void P_PlayerThink(player_t *player) // it lasts for one tic. player->pflags &= ~PF_FULLSTASIS; -#ifdef POLYOBJECTS if (player->onconveyor == 1) player->cmomy = player->cmomx = 0; -#endif //P_DoSuperStuff(player); //P_CheckSneakerAndLivesTimer(player); @@ -8836,9 +8824,7 @@ void P_PlayerAfterThink(player_t *player) cmd = &player->cmd; #ifdef SECTORSPECIALSAFTERTHINK -#ifdef POLYOBJECTS if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo)) -#endif player->onconveyor = 0; // check special sectors : damage & secrets diff --git a/src/r_bsp.c b/src/r_bsp.c index 2f1bc9985..6e35fddc9 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -371,9 +371,7 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back) { return ( -#ifdef POLYOBJECTS !line->polyseg && -#endif back->ceilingpic == front->ceilingpic && back->floorpic == front->floorpic && back->f_slope == front->f_slope @@ -650,7 +648,6 @@ static boolean R_CheckBBox(const fixed_t *bspcoord) return true; } -#ifdef POLYOBJECTS size_t numpolys; // number of polyobjects in current subsector size_t num_po_ptrs; // number of polyobject pointers allocated @@ -815,7 +812,6 @@ static void R_AddPolyObjects(subsector_t *sub) R_AddLine(po_ptrs[i]->segs[j]); } } -#endif // // R_Subsector @@ -894,9 +890,7 @@ static void R_Subsector(size_t num) { floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel, frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL -#ifdef POLYOBJECTS_PLANES , NULL -#endif , frontsector->f_slope , R_NoEncore(frontsector, false)); } @@ -912,9 +906,7 @@ static void R_Subsector(size_t num) ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, ceilingcolormap, NULL -#ifdef POLYOBJECTS_PLANES , NULL -#endif , frontsector->c_slope , R_NoEncore(frontsector, true)); } @@ -965,9 +957,7 @@ static void R_Subsector(size_t num) ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic, *frontsector->lightlist[light].lightlevel, *rover->bottomxoffs, *rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover -#ifdef POLYOBJECTS_PLANES , NULL -#endif , *rover->b_slope , R_NoEncore(rover->master->frontsector, true)); @@ -1003,9 +993,7 @@ static void R_Subsector(size_t num) ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic, *frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle, frontsector->lightlist[light].extra_colormap, rover -#ifdef POLYOBJECTS_PLANES , NULL -#endif , *rover->t_slope , R_NoEncore(rover->master->frontsector, false)); @@ -1022,7 +1010,6 @@ static void R_Subsector(size_t num) } } -#ifdef POLYOBJECTS_PLANES // Polyobjects have planes, too! if (sub->polyList) { @@ -1091,7 +1078,6 @@ static void R_Subsector(size_t num) po = (polyobj_t *)(po->link.next); } } -#endif #ifdef FLOORSPLATS if (sub->splats) @@ -1114,18 +1100,14 @@ static void R_Subsector(size_t num) firstseg = NULL; -#ifdef POLYOBJECTS // haleyjd 02/19/06: draw polyobjects before static lines if (sub->polyList) R_AddPolyObjects(sub); -#endif while (count--) { // CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime); -#ifdef POLYOBJECTS if (!line->polyseg) // ignore segs that belong to polyobjects -#endif R_AddLine(line); line++; curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so stuff doesn't try using it for other things */ diff --git a/src/r_bsp.h b/src/r_bsp.h index 1e4ca68fc..f0db9066f 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -44,13 +44,11 @@ void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2); // no longer a static since this is used for encore in hw_main.c as well now: boolean R_NoEncore(sector_t *sector, boolean ceiling); -#ifdef POLYOBJECTS void R_SortPolyObjects(subsector_t *sub); extern size_t numpolys; // number of polyobjects in current subsector extern size_t num_po_ptrs; // number of polyobject pointers allocated extern polyobj_t **po_ptrs; // temp ptr array to sort polyobject pointers -#endif sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, INT32 *ceilinglightlevel, boolean back); diff --git a/src/r_defs.h b/src/r_defs.h index 144faac94..6635e2afc 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -91,9 +91,7 @@ typedef struct fixed_t z; ///< Z coordinate. } degenmobj_t; -#ifdef POLYOBJECTS #include "p_polyobj.h" -#endif // Store fake planes in a resizable array insted of just by // heightsec. Allows for multiple fake planes. @@ -429,9 +427,7 @@ typedef struct line_s void *splats; // wallsplat_t list #endif INT32 firsttag, nexttag; // improves searches for tags. -#ifdef POLYOBJECTS polyobj_t *polyobj; // Belongs to a polyobject? -#endif char *text; // a concatination of all front and back texture names, for linedef specials that require a string. INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0 @@ -473,9 +469,7 @@ typedef struct subsector_s sector_t *sector; INT16 numlines; UINT16 firstline; -#ifdef POLYOBJECTS struct polyobj_s *polyList; // haleyjd 02/19/06: list of polyobjects -#endif #if 1//#ifdef FLOORSPLATS void *splats; // floorsplat_t list #endif @@ -551,10 +545,8 @@ typedef struct seg_s // Why slow things down by calculating lightlists for every thick side? size_t numlights; r_lightlist_t *rlights; -#ifdef POLYOBJECTS polyobj_t *polyseg; boolean dontrenderme; -#endif } seg_t; // diff --git a/src/r_plane.c b/src/r_plane.c index 56f75d20d..d00274262 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -422,9 +422,7 @@ static visplane_t *new_visplane(unsigned hash) visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, ffloor_t *pfloor -#ifdef POLYOBJECTS_PLANES , polyobj_t *polyobj -#endif , pslope_t *slope , boolean noencore) { @@ -446,7 +444,6 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, } } -#ifdef POLYOBJECTS_PLANES if (polyobj) { if (polyobj->angle != 0) @@ -461,7 +458,6 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, yoff += polyobj->centerPt.y; } } -#endif // This appears to fix the Nimbus Ruins sky bug. if (picnum == skyflatnum && pfloor) @@ -475,12 +471,10 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, for (check = visplanes[hash]; check; check = check->next) { -#ifdef POLYOBJECTS_PLANES if (check->polyobj && pfloor) continue; if (polyobj != check->polyobj) continue; -#endif if (height == check->height && picnum == check->picnum && lightlevel == check->lightlevel && xoff == check->xoffs && yoff == check->yoffs @@ -512,9 +506,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, check->viewz = viewz; check->viewangle = viewangle; check->plangle = plangle; -#ifdef POLYOBJECTS_PLANES check->polyobj = polyobj; -#endif check->slope = slope; check->noencore = noencore; @@ -583,9 +575,7 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) new_pl->viewz = pl->viewz; new_pl->viewangle = pl->viewangle; new_pl->plangle = pl->plangle; -#ifdef POLYOBJECTS_PLANES new_pl->polyobj = pl->polyobj; -#endif new_pl->slope = pl->slope; new_pl->noencore = pl->noencore; pl = new_pl; @@ -611,11 +601,9 @@ void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop) INT32 unionl, unionh; // INT32 x; -#ifdef POLYOBJECTS_PLANES // Don't expand polyobject planes here - we do that on our own. if (pl->polyobj) return; -#endif if (start < pl->minx) { @@ -730,9 +718,7 @@ void R_DrawPlanes(void) } if (pl->ffloor != NULL -#ifdef POLYOBJECTS_PLANES || pl->polyobj != NULL -#endif ) continue; @@ -761,7 +747,6 @@ void R_DrawSinglePlane(visplane_t *pl) #endif spanfunc = basespanfunc; -#ifdef POLYOBJECTS_PLANES if (pl->polyobj && pl->polyobj->translucency != 0) { spanfunc = R_DrawTranslucentSpan_8; @@ -783,7 +768,6 @@ void R_DrawSinglePlane(visplane_t *pl) light = LIGHTLEVELS-1; } else -#endif if (pl->ffloor) { // Don't draw planes that shouldn't be drawn. diff --git a/src/r_plane.h b/src/r_plane.h index d7cc14fae..7a069d401 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -45,9 +45,7 @@ typedef struct visplane_s fixed_t xoffs, yoffs; // Scrolling flats. struct ffloor_s *ffloor; -#ifdef POLYOBJECTS_PLANES polyobj_t *polyobj; -#endif pslope_t *slope; boolean noencore; @@ -81,9 +79,7 @@ void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2); void R_DrawPlanes(void); visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, ffloor_t *ffloor -#ifdef POLYOBJECTS_PLANES , polyobj_t *polyobj -#endif , pslope_t *slope , boolean noencore); visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop); @@ -111,9 +107,7 @@ typedef struct planemgr_s struct pslope_s *slope; struct ffloor_s *ffloor; -#ifdef POLYOBJECTS_PLANES polyobj_t *polyobj; -#endif } visffloor_t; extern visffloor_t ffloor[MAXFFLOORS]; diff --git a/src/r_segs.c b/src/r_segs.c index 8cd021ff9..c531be52d 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -1297,7 +1297,6 @@ static void R_RenderSegLoop (void) for (i = 0; i < numffloors; i++) { -#ifdef POLYOBJECTS_PLANES if (ffloor[i].polyobj && (!curline->polyseg || ffloor[i].polyobj != curline->polyseg)) continue; @@ -1308,7 +1307,6 @@ static void R_RenderSegLoop (void) else if (ffloor[i].plane->maxx < rw_x) ffloor[i].plane->maxx = rw_x; } -#endif if (ffloor[i].height < viewz) { @@ -1321,12 +1319,10 @@ static void R_RenderSegLoop (void) if (bottom_w > bottom) bottom_w = bottom; -#ifdef POLYOBJECTS_PLANES // Polyobject-specific hack to fix plane leaking -Red if (curline->polyseg && ffloor[i].polyobj && ffloor[i].polyobj == curline->polyseg && top_w >= bottom_w) { ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF; } else -#endif if (top_w <= bottom_w) { @@ -1345,12 +1341,10 @@ static void R_RenderSegLoop (void) if (bottom_w > bottom) bottom_w = bottom; -#ifdef POLYOBJECTS_PLANES // Polyobject-specific hack to fix plane leaking -Red if (curline->polyseg && ffloor[i].polyobj && ffloor[i].polyobj == curline->polyseg && top_w >= bottom_w) { ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF; } else -#endif if (top_w <= bottom_w) { @@ -1810,10 +1804,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) { for (i = 0; i < numffloors; i++) { -#ifdef POLYOBJECTS_PLANES if (ffloor[i].polyobj && (!ds_p->curline->polyseg || ffloor[i].polyobj != ds_p->curline->polyseg)) continue; -#endif if (ffloor[i].slope) { ffloor[i].f_pos = P_GetZAt(ffloor[i].slope, segleft.x, segleft.y) - viewz; @@ -2334,7 +2326,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) maskedtextureheight = ds_p->maskedtextureheight; // note to red, this == &(ds_p->maskedtextureheight[0]) -#ifdef POLYOBJECTS if (curline->polyseg) { // use REAL front and back floors please, so midtexture rendering isn't mucked up rw_midtextureslide = rw_midtexturebackslide = 0; if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) @@ -2363,7 +2354,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) } rw_midtexturemid += sidedef->rowoffset; rw_midtextureback += sidedef->rowoffset; -#endif maskedtexture = true; } @@ -2710,7 +2700,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) } } } -#ifdef POLYOBJECTS_PLANES if (curline->polyseg && frontsector && (curline->polyseg->flags & POF_RENDERPLANES)) { while (i < numffloors && ffloor[i].polyobj != curline->polyseg) i++; @@ -2749,7 +2738,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) i++; } } -#endif numbackffloors = i; } diff --git a/src/r_things.c b/src/r_things.c index 3b3cbe4dd..25c4edb3d 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1941,7 +1941,6 @@ static void R_CreateDrawNodes(void) entry->ffloor = ds->thicksides[i]; } } -#ifdef POLYOBJECTS_PLANES // Check for a polyobject plane, but only if this is a front line if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) { plane = ds->curline->polyseg->visplane; @@ -1957,7 +1956,6 @@ static void R_CreateDrawNodes(void) } ds->curline->polyseg->visplane = NULL; } -#endif if (ds->maskedtexturecol) { entry = R_CreateDrawNode(&nodehead); @@ -2002,7 +2000,6 @@ static void R_CreateDrawNodes(void) } } -#ifdef POLYOBJECTS_PLANES // find all the remaining polyobject planes and add them on the end of the list // probably this is a terrible idea if we wanted them to be sorted properly // but it works getting them in for now @@ -2023,7 +2020,6 @@ static void R_CreateDrawNodes(void) // note: no seg is set, for what should be obvious reasons PolyObjects[i].visplane = NULL; } -#endif if (visspritecount == 0) return; From 60cfc091e50891f149dc012d06783b4e346ece3b Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 20 Aug 2020 23:09:12 -0400 Subject: [PATCH 079/193] Add return false to the connect timeout --- src/d_clisrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e22ab463b..2a1381775 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2407,6 +2407,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic "\n" "Press ESC\n" ), NULL, MM_NOTHING); + return false; } #ifdef JOININGAME // prepare structures to save the file From 7c5a969473d939f97bbf979edb67c0f34dc87390 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 20 Aug 2020 23:38:04 -0400 Subject: [PATCH 080/193] Fixed retry timeout to no ttrigger when starting a local server --- src/d_clisrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2a1381775..2441659c5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2394,7 +2394,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic } break; case CL_ASKJOIN: - if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime()) + if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime() && !server) { CONS_Printf(M_GetText("5 minute wait time exceeded.\n")); CONS_Printf(M_GetText("Network game synchronization aborted.\n")); @@ -2559,6 +2559,7 @@ static void CL_ConnectToServer(void) oldtic = I_GetTime() - 1; #ifndef NONET asksent = I_GetTime() - NEWTICRATE*3; + firstconnectattempttime = I_GetTime(); i = SL_SearchServer(servernode); From d4a530ac5ec3b94751e16ff2d6aec0de46f92897 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 21 Aug 2020 01:16:11 -0400 Subject: [PATCH 081/193] XOR IP addresses before sending them to Discord Not complex because we aren't sending anything that isn't easily accessible via the Master Server anyway, just means we aren't sending plain-text IPs to Discord. Might be improved by basing the XOR key on other values (base RNG, maybe version) so that it's not like *right* there --- src/discord.c | 49 +++++++++++++++++++++++++++++++++++++++---------- src/discord.h | 2 +- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/discord.c b/src/discord.c index a0d0db96d..3e8da50cc 100644 --- a/src/discord.c +++ b/src/discord.c @@ -34,6 +34,8 @@ consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; +#define IP_SIZE 16 // length of IP strings + #ifdef HAVE_CURL struct SelfIPbuffer { @@ -42,14 +44,34 @@ struct SelfIPbuffer size_t length; }; -#define IP_SIZE 16 static char self_ip[IP_SIZE]; #endif // HAVE_CURL +/*-------------------------------------------------- + static char *DRPC_XORIPString(const char *input) + + Simple XOR encryption/decryption. Not complex or + very secretive because we aren't sending anything + that isn't easily accessible via the Master Server anyway. +--------------------------------------------------*/ +static char *DRPC_XORIPString(const char *input) +{ + const UINT8 xor[IP_SIZE] = {222, 106, 64, 251, 207, 16, 28, 78, 4, 118, 46, 76, 153, 45, 91, 100}; + char *output = malloc(sizeof(char) * IP_SIZE); + UINT8 i; + + for (i = 0; i < IP_SIZE; i++) + { + output[i] = input[i] ^ xor[i]; + } + + return output; +} + /*-------------------------------------------------- static void DRPC_HandleReady(const DiscordUser *user) - Handler function, ran when the game connects to Discord. + Callback function, ran when the game connects to Discord. Input Arguments:- user - Struct containing Discord user info. @@ -65,7 +87,7 @@ static void DRPC_HandleReady(const DiscordUser *user) /*-------------------------------------------------- static void DRPC_HandleDisconnect(int err, const char *msg) - Handler function, ran when disconnecting from Discord. + Callback function, ran when disconnecting from Discord. Input Arguments:- err - Error type @@ -82,7 +104,7 @@ static void DRPC_HandleDisconnect(int err, const char *msg) /*-------------------------------------------------- static void DRPC_HandleError(int err, const char *msg) - Handler function, ran when Discord outputs an error. + Callback function, ran when Discord outputs an error. Input Arguments:- err - Error type @@ -99,7 +121,7 @@ static void DRPC_HandleError(int err, const char *msg) /*-------------------------------------------------- static void DRPC_HandleJoin(const char *secret) - Handler function, ran when Discord wants to + Callback function, ran when Discord wants to connect a player to the game via a channel invite or a join request. @@ -111,8 +133,10 @@ static void DRPC_HandleError(int err, const char *msg) --------------------------------------------------*/ static void DRPC_HandleJoin(const char *secret) { - CONS_Printf("Connecting to %s via Discord\n", secret); - COM_BufAddText(va("connect \"%s\"\n", secret)); + char *ip = DRPC_XORIPString(secret); + CONS_Printf("Connecting to %s via Discord\n", ip); + COM_BufAddText(va("connect \"%s\"\n", ip)); + free(ip); } /*-------------------------------------------------- @@ -129,6 +153,7 @@ void DRPC_Init(void) handlers.disconnected = DRPC_HandleDisconnect; handlers.errored = DRPC_HandleError; handlers.joinGame = DRPC_HandleJoin; + //handlers.joinRequest = DRPC_HandleJoinRequest; Discord_Initialize(DISCORD_APPID, &handlers, 1, NULL); I_AddExitFunc(Discord_Shutdown); @@ -275,8 +300,8 @@ void DRPC_UpdatePresence(void) #ifdef DEVELOP // This way, we can use the invite feature in-dev, but not have snoopers seeing any potential secrets! :P discordPresence.largeImageKey = "miscdevelop"; - discordPresence.largeImageText = "Nope."; - discordPresence.state = "Shh! We're testing!"; + discordPresence.largeImageText = "No peeking!"; + discordPresence.state = "Testing the game"; Discord_UpdatePresence(&discordPresence); return; @@ -304,7 +329,11 @@ void DRPC_UpdatePresence(void) // Grab the host's IP for joining. if (cv_allownewplayer.value && ((join = DRPC_GetServerIP()) != NULL)) - discordPresence.joinSecret = join; + { + char *xorjoin = DRPC_XORIPString(join); + discordPresence.joinSecret = xorjoin; + free(xorjoin); + } } else { diff --git a/src/discord.h b/src/discord.h index aa2c1468b..a065c3d6e 100644 --- a/src/discord.h +++ b/src/discord.h @@ -23,7 +23,7 @@ extern consvar_t cv_discordrp; void DRPC_Init(void); Initalizes Discord Rich Presence by linking the Application ID - and setting the handler functions. + and setting the callback functions. --------------------------------------------------*/ void DRPC_Init(void); From 3949826f89ea15c2dd1bb000d4c8a3476b307332 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 21 Aug 2020 01:18:40 -0400 Subject: [PATCH 082/193] Don't show map image for title screen demos --- src/discord.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/discord.c b/src/discord.c index 3e8da50cc..c208ad6c3 100644 --- a/src/discord.c +++ b/src/discord.c @@ -363,7 +363,8 @@ void DRPC_UpdatePresence(void) } } - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info + && !(demo.playback && demo.title)) { if ((gamemap >= 1 && gamemap <= 60) // supported race maps || (gamemap >= 136 && gamemap <= 164)) // supported battle maps From 368ed6ad1eaa9775216f7ea4986f21d22a5e3a25 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Aug 2020 22:36:41 -0700 Subject: [PATCH 083/193] curl_global_cleanup --- src/discord.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/discord.c b/src/discord.c index c208ad6c3..1e7eb2bbb 100644 --- a/src/discord.c +++ b/src/discord.c @@ -261,6 +261,8 @@ static const char *DRPC_GetServerIP(void) free(buffer.pointer); curl_easy_cleanup(curl); } + + curl_global_cleanup(); } if (self_ip[0]) From 734b09920287249a67c4980cc94b3634f68398d5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 21 Aug 2020 04:14:33 -0400 Subject: [PATCH 084/193] Invite menu started Functions! Is not pretty yet! --- src/discord.c | 98 ++++++++++++++++++++++++++++++++++++++++++--- src/discord.h | 26 ++++++++++++ src/m_menu.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 6 deletions(-) diff --git a/src/discord.c b/src/discord.c index c208ad6c3..ec3d5ab46 100644 --- a/src/discord.c +++ b/src/discord.c @@ -25,6 +25,7 @@ #include "m_menu.h" // gametype_cons_t #include "r_things.h" // skins #include "mserv.h" // ms_RoomId +#include "z_zone.h" #include "discord.h" #include "doomdef.h" @@ -32,9 +33,12 @@ // Feel free to provide your own, if you care enough to create another Discord app for this :P #define DISCORD_APPID "503531144395096085" +// length of IP strings +#define IP_SIZE 16 + consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; -#define IP_SIZE 16 // length of IP strings +discordRequest_t *discordRequestList = NULL; #ifdef HAVE_CURL struct SelfIPbuffer @@ -81,7 +85,7 @@ static char *DRPC_XORIPString(const char *input) --------------------------------------------------*/ static void DRPC_HandleReady(const DiscordUser *user) { - CONS_Printf("Discord: connected to %s#%s - %s\n", user->username, user->discriminator, user->userId); + CONS_Printf("Discord: connected to %s#%s (%s)\n", user->username, user->discriminator, user->userId); } /*-------------------------------------------------- @@ -139,6 +143,89 @@ static void DRPC_HandleJoin(const char *secret) free(ip); } +/*-------------------------------------------------- + static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) + + Callback function, ran when Discord wants to + ask the player if another Discord user can join + or not. + + Input Arguments:- + requestUser - DiscordUser struct for the user trying to connect. + + Return:- + None +--------------------------------------------------*/ +static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) +{ + discordRequest_t *append = discordRequestList; + discordRequest_t *newRequest = Z_Calloc(sizeof (discordRequest_t), PU_STATIC, NULL); + + // Discord requests exprie after 30 seconds, give 1 second of lee-way for connection discrepancies + newRequest->timer = 29*TICRATE; + + newRequest->username = Z_Calloc(344+1+8, PU_STATIC, NULL); + snprintf(newRequest->username, 344+1+8, "%s#%s", + requestUser->username, + requestUser->discriminator + ); + + newRequest->userID = Z_Calloc(32, PU_STATIC, NULL); + snprintf(newRequest->userID, 32, "%s", requestUser->userId); + + if (append != NULL) + { + discordRequest_t *prev = NULL; + + while (append != NULL) + { + prev = append; + append = append->next; + } + + newRequest->prev = prev; + prev->next = newRequest; + } + else + { + discordRequestList = newRequest; + } +} + +/*-------------------------------------------------- + void DRPC_RemoveRequest(discordRequest_t *removeRequest) + + See header file for description. +--------------------------------------------------*/ +void DRPC_RemoveRequest(discordRequest_t *removeRequest) +{ + if (removeRequest->prev != NULL) + { + removeRequest->prev->next = removeRequest->next; + } + + if (removeRequest->next != NULL) + { + removeRequest->next->prev = removeRequest->prev; + + if (removeRequest == discordRequestList) + { + discordRequestList = removeRequest->next; + } + } + else + { + if (removeRequest == discordRequestList) + { + discordRequestList = NULL; + } + } + + Z_Free(removeRequest->username); + Z_Free(removeRequest->userID); + Z_Free(removeRequest); +} + /*-------------------------------------------------- void DRPC_Init(void) @@ -153,7 +240,7 @@ void DRPC_Init(void) handlers.disconnected = DRPC_HandleDisconnect; handlers.errored = DRPC_HandleError; handlers.joinGame = DRPC_HandleJoin; - //handlers.joinRequest = DRPC_HandleJoinRequest; + handlers.joinRequest = DRPC_HandleJoinRequest; Discord_Initialize(DISCORD_APPID, &handlers, 1, NULL); I_AddExitFunc(Discord_Shutdown); @@ -347,8 +434,7 @@ void DRPC_UpdatePresence(void) } // Gametype info - if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) - && !demo.playback) + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) && Playing()) { if (modeattacking) discordPresence.details = "Time Attack"; @@ -396,7 +482,7 @@ void DRPC_UpdatePresence(void) discordPresence.largeImageText = mapname; } - if (Playing()) + if (gamestate == GS_LEVEL && Playing()) { const time_t currentTime = time(NULL); const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE); diff --git a/src/discord.h b/src/discord.h index a065c3d6e..fd306fa8c 100644 --- a/src/discord.h +++ b/src/discord.h @@ -19,6 +19,32 @@ extern consvar_t cv_discordrp; +typedef struct discordRequest_s { + tic_t timer; // Tics left on the request before it expires. + char *username; // Discord user name + their discriminator. + char *userID; // The ID of the Discord user, gets used with Discord_Respond() + + // HAHAHA, no. + // *Maybe* if it was only PNG I would boot up curl just to get AND convert this to Doom GFX, + // but it can be a JEPG, WebP, or GIF too :) + //patch_t *avatar; + + struct discordRequest_s *next; // Next request in the list. + struct discordRequest_s *prev; // Previous request in the list. Not used normally, but just in case something funky happens, this should repair the list. +} discordRequest_t; + +extern discordRequest_t *discordRequestList; + + +/*-------------------------------------------------- + void DRPC_RemoveRequest(void); + + Removes an invite from the list. +--------------------------------------------------*/ + +void DRPC_RemoveRequest(discordRequest_t *removeRequest); + + /*-------------------------------------------------- void DRPC_Init(void); diff --git a/src/m_menu.c b/src/m_menu.c index 791ab34b8..0c3f831b7 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -82,6 +82,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #endif #ifdef HAVE_DISCORDRPC +//#include "discord_rpc.h" #include "discord.h" #endif @@ -192,6 +193,12 @@ static void M_RoomMenu(INT32 choice); // the haxor message menu menu_t MessageDef; +#ifdef HAVE_DISCORDRPC +menu_t DiscordRequestsDef; +static void M_HandleDiscordRequests(INT32 choice); +static void M_DrawDiscordRequests(void); +#endif + menu_t SPauseDef; #define lsheadingheight 16 @@ -662,6 +669,13 @@ typedef enum spause_quit } spause_e; +#ifdef HAVE_DISCORDRPC +static menuitem_t DiscordRequestsMenu[] = +{ + {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleDiscordRequests, 0}, +}; +#endif + // ----------------- // Misc menu options // ----------------- @@ -1663,6 +1677,21 @@ menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); +#ifdef HAVE_DISCORDRPC +static void M_HandleDiscordRequests(INT32 choice); +static void M_DrawDiscordRequests(void); +menu_t DiscordRequestsDef = { + NULL, + sizeof (DiscordRequestsMenu)/sizeof (menuitem_t), + NULL, + DiscordRequestsMenu, + M_DrawDiscordRequests, + 0, 0, + 0, + NULL +}; +#endif + // Misc Main Menu menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40); menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40); @@ -3289,6 +3318,17 @@ void M_StartControlPanel(void) itemOn = mpause_continue; } +#ifdef HAVE_DISCORDRPC + // Kind of hi-jacking this... + if (discordRequestList != NULL) + { + // Gotta take care of these requests first + DiscordRequestsDef.prevMenu = currentMenu; + currentMenu = &DiscordRequestsDef; + itemOn = 0; + } +#endif + CON_ToggleOff(); // move away console } @@ -11268,3 +11308,71 @@ static void M_OGL_DrawColorMenu(void) highlightflags, "Gamma correction"); } #endif + +#ifdef HAVE_DISCORDRPC +static void M_HandleDiscordRequests(INT32 choice) +{ + discordRequest_t *curRequest = discordRequestList; + + if (curRequest == NULL) + { + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + + return; + } + + switch (choice) + { + case KEY_ESCAPE: + Discord_Respond(curRequest->userID, DISCORD_REPLY_NO); + DRPC_RemoveRequest(curRequest); + break; + + case KEY_ENTER: + Discord_Respond(curRequest->userID, DISCORD_REPLY_YES); + DRPC_RemoveRequest(curRequest); + break; + } + + if (curRequest == NULL) + { + // was removed, we can exit menu + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + } +} + +static void M_DrawDiscordRequests(void) +{ + discordRequest_t *curRequest = discordRequestList; + + INT32 x = 64; + INT32 y = 135; + + if (curRequest == NULL) + { + // Uh oh! Shouldn't happen! + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + return; + } + + V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, curRequest->username); + V_DrawThinString(x, y + 24, V_ALLOWLOWERCASE|V_6WIDTHSPACE, "A - Accept B - Decline"); + y -= 16; + + while (curRequest->next != NULL) + { + curRequest = curRequest->next; + V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, curRequest->username); + y -= 8; + } +} +#endif From 42ac9555849474d8714069993df5310c8ac4f664 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 21 Aug 2020 04:17:36 -0400 Subject: [PATCH 085/193] Don't reset menu in drawing function --- src/m_menu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 0c3f831b7..db0dd9a36 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11357,10 +11357,6 @@ static void M_DrawDiscordRequests(void) if (curRequest == NULL) { // Uh oh! Shouldn't happen! - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu); - else - M_ClearMenus(true); return; } From 318b2a84345a2cf1f61dd796d964ff5fe9990c97 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 00:01:56 -0400 Subject: [PATCH 086/193] Ignore any duplicate requests You will still get a message for them, so if they're being annoying you will want to block them anyway, but at least it won't clog up the game. --- src/discord.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/discord.c b/src/discord.c index 82b5ed348..98188990c 100644 --- a/src/discord.c +++ b/src/discord.c @@ -159,10 +159,10 @@ static void DRPC_HandleJoin(const char *secret) static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) { discordRequest_t *append = discordRequestList; - discordRequest_t *newRequest = Z_Calloc(sizeof (discordRequest_t), PU_STATIC, NULL); + discordRequest_t *newRequest = Z_Calloc(sizeof(discordRequest_t), PU_STATIC, NULL); - // Discord requests exprie after 30 seconds, give 1 second of lee-way for connection discrepancies - newRequest->timer = 29*TICRATE; + // Discord requests exprie after 30 seconds + newRequest->timer = (30*TICRATE)-1; newRequest->username = Z_Calloc(344+1+8, PU_STATIC, NULL); snprintf(newRequest->username, 344+1+8, "%s#%s", @@ -179,6 +179,14 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) while (append != NULL) { + // CHECK FOR DUPES!! Ignore any that already exist from the same user. + if (!strcmp(newRequest->userID, append->userID)) + { + Discord_Respond(newRequest->userID, DISCORD_REPLY_IGNORE); + DRPC_RemoveRequest(newRequest); + return; + } + prev = append; append = append->next; } From 56694030ec39d0c01ad193445bc16694d9f7923d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 00:11:33 -0400 Subject: [PATCH 087/193] Base xor off of inital seed --- src/discord.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/discord.c b/src/discord.c index 98188990c..2c2bed5b8 100644 --- a/src/discord.c +++ b/src/discord.c @@ -26,6 +26,7 @@ #include "r_things.h" // skins #include "mserv.h" // ms_RoomId #include "z_zone.h" +#include "m_random.h" // P_GetInitSeed #include "discord.h" #include "doomdef.h" @@ -56,11 +57,12 @@ static char self_ip[IP_SIZE]; Simple XOR encryption/decryption. Not complex or very secretive because we aren't sending anything - that isn't easily accessible via the Master Server anyway. + that isn't easily accessible via our Master Server anyway. --------------------------------------------------*/ static char *DRPC_XORIPString(const char *input) { - const UINT8 xor[IP_SIZE] = {222, 106, 64, 251, 207, 16, 28, 78, 4, 118, 46, 76, 153, 45, 91, 100}; + const UINT32 is = (P_GetInitSeed() % UINT8_MAX); + const UINT8 xor[IP_SIZE] = {is, is+106, is-64, is+251, is-207, is+16, is-28, is+78, is-4, is+118, is-46, is+76, is-153, is+45, is-91, is+100}; char *output = malloc(sizeof(char) * IP_SIZE); UINT8 i; From b7613490762a5d5b5ec72176cc6179169106f055 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 01:53:27 -0400 Subject: [PATCH 088/193] Add some options, maxplayer/allownewplayer dont need to be netvars anymore --- src/d_clisrv.c | 12 ++-- src/d_netcmd.c | 9 ++- src/d_netcmd.h | 3 + src/discord.c | 163 ++++++++++++++++++++++++++++++++++++++++++++----- src/discord.h | 35 ++++++++++- src/m_menu.c | 46 ++++++++++---- 6 files changed, 234 insertions(+), 34 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 16ed731e5..352321e19 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3257,14 +3257,16 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; +static void Joinable_OnChange(void); + +consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_CALL, CV_OnOff, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL}; + #ifdef VANILLAJOINNEXTROUND consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done #endif -static void MaxPlayers_OnChange(void); static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_NETVAR|CV_CALL, maxplayers_cons_t, MaxPlayers_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_CALL, maxplayers_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; @@ -3282,10 +3284,10 @@ consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons static void Got_AddPlayer(UINT8 **p, INT32 playernum); static void Got_RemovePlayer(UINT8 **p, INT32 playernum); -static void MaxPlayers_OnChange(void) +static void Joinable_OnChange(void) { #ifdef HAVE_DISCORDRPC - DRPC_UpdatePresence(); + DRPC_SendDiscordInfo(); #else return; #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 213ba5d48..8778a2420 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -708,10 +708,14 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_showping); #ifdef SEENAMES - CV_RegisterVar(&cv_allowseenames); + CV_RegisterVar(&cv_allowseenames); #endif CV_RegisterVar(&cv_dummyconsvar); + +#ifdef HAVE_DISCORDRPC + RegisterNetXCmd(XD_DISCORD, DRPC_RecieveDiscordInfo); +#endif } // ========================================================================= @@ -1007,6 +1011,9 @@ void D_RegisterClientCommands(void) #ifdef HAVE_DISCORDRPC CV_RegisterVar(&cv_discordrp); + CV_RegisterVar(&cv_discordstreamer); + CV_RegisterVar(&cv_discordasks); + CV_RegisterVar(&cv_discordinvites); #endif } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 2d8e5705a..d6e7e08db 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -181,6 +181,9 @@ typedef enum #ifdef HAVE_BLUA XD_LUACMD, // 26 XD_LUAVAR, // 27 +#endif +#ifdef HAVE_DISCORDRPC + XD_DISCORD, // 28 #endif MAXNETXCMD } netxcmd_t; diff --git a/src/discord.c b/src/discord.c index 2c2bed5b8..7e20cde65 100644 --- a/src/discord.c +++ b/src/discord.c @@ -27,6 +27,7 @@ #include "mserv.h" // ms_RoomId #include "z_zone.h" #include "m_random.h" // P_GetInitSeed +#include "byteptr.h" #include "discord.h" #include "doomdef.h" @@ -38,6 +39,13 @@ #define IP_SIZE 16 consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_discordstreamer = {"discordstreamer", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_discordasks = {"discordasks", "Yes", CV_SAVE|CV_CALL, CV_YesNo, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; +consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, DRPC_SendDiscordInfo, 0, NULL, NULL, 0, 0, NULL}; + +struct discordInfo_s discordInfo; discordRequest_t *discordRequestList = NULL; @@ -87,7 +95,14 @@ static char *DRPC_XORIPString(const char *input) --------------------------------------------------*/ static void DRPC_HandleReady(const DiscordUser *user) { - CONS_Printf("Discord: connected to %s#%s (%s)\n", user->username, user->discriminator, user->userId); + if (cv_discordstreamer.value) + { + CONS_Printf("Discord: connected to %s\n", user->username); + } + else + { + CONS_Printf("Discord: connected to %s#%s (%s)\n", user->username, user->discriminator, user->userId); + } } /*-------------------------------------------------- @@ -145,6 +160,50 @@ static void DRPC_HandleJoin(const char *secret) free(ip); } +/*-------------------------------------------------- + static boolean DRPC_InvitesAreAllowed(void) + + Determines whenever or not invites or + ask to join requests are allowed. + + Input Arguments:- + None + + Return:- + true if invites are allowed, false otherwise. +--------------------------------------------------*/ +static boolean DRPC_InvitesAreAllowed(void) +{ + if (!Playing()) + { + // We're not playing, so we should not be getting invites. + return false; + } + + if (cv_discordasks.value == 0) + { + // Client has the CVar set to off, so never allow invites from this client. + return false; + } + + if (discordInfo.joinsAllowed == true) + { + if (discordInfo.everyoneCanInvite == true) + { + // Everyone's allowed! + return true; + } + else if (consoleplayer == serverplayer || IsPlayerAdmin(consoleplayer)) + { + // Only admins are allowed! + return true; + } + } + + // Did not pass any of the checks + return false; +} + /*-------------------------------------------------- static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) @@ -161,16 +220,25 @@ static void DRPC_HandleJoin(const char *secret) static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) { discordRequest_t *append = discordRequestList; - discordRequest_t *newRequest = Z_Calloc(sizeof(discordRequest_t), PU_STATIC, NULL); + discordRequest_t *newRequest; - // Discord requests exprie after 30 seconds + if (DRPC_InvitesAreAllowed() == false) + { + // Something weird happened if this occurred... + Discord_Respond(requestUser->userId, DISCORD_REPLY_IGNORE); + return; + } + + newRequest = Z_Calloc(sizeof(discordRequest_t), PU_STATIC, NULL); + + // Discord requests expire after 30 seconds newRequest->timer = (30*TICRATE)-1; - newRequest->username = Z_Calloc(344+1+8, PU_STATIC, NULL); - snprintf(newRequest->username, 344+1+8, "%s#%s", - requestUser->username, - requestUser->discriminator - ); + newRequest->username = Z_Calloc(344, PU_STATIC, NULL); + snprintf(newRequest->username, 344, "%s", requestUser->username); + + newRequest->discriminator = Z_Calloc(8, PU_STATIC, NULL); + snprintf(newRequest->discriminator, 8, "%s", requestUser->discriminator); newRequest->userID = Z_Calloc(32, PU_STATIC, NULL); snprintf(newRequest->userID, 32, "%s", requestUser->userId); @@ -257,6 +325,68 @@ void DRPC_Init(void) DRPC_UpdatePresence(); } +/*-------------------------------------------------- + void DRPC_SendDiscordInfo(void) + + See header file for description. +--------------------------------------------------*/ +void DRPC_SendDiscordInfo(void) +{ + UINT8 buf[3]; + UINT8 *p = buf; + UINT8 maxplayer; + + if (!server) + return; + + maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + + WRITEUINT8(p, maxplayer); + WRITEUINT8(p, cv_allownewplayer.value); + WRITEUINT8(p, cv_discordinvites.value); + + SendNetXCmd(XD_DISCORD, &buf, 3); +} + +/*-------------------------------------------------- + void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum) + + See header file for description. +--------------------------------------------------*/ +void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum) +{ + if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) + { + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal Discord info command received from %s\n"), player_names[playernum]); + if (server) + { + XBOXSTATIC UINT8 buf[2]; + + buf[0] = (UINT8)playernum; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + return; + } + + discordInfo.maxPlayers = READUINT8(*p); + discordInfo.joinsAllowed = (boolean)READUINT8(*p); + discordInfo.everyoneCanInvite = (boolean)READUINT8(*p); + + DRPC_UpdatePresence(); + + if (DRPC_InvitesAreAllowed() == false) + { + // Flush the request list, if it still exists + while (discordRequestList != NULL) + { + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE); + DRPC_RemoveRequest(discordRequestList); + } + } +} + #ifdef HAVE_CURL /*-------------------------------------------------- static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) @@ -410,8 +540,6 @@ void DRPC_UpdatePresence(void) // Server info if (netgame) { - const char *join; - switch (ms_RoomId) { case -1: discordPresence.state = "Private"; break; // Private server @@ -426,12 +554,17 @@ void DRPC_UpdatePresence(void) discordPresence.partySize = D_NumPlayers(); // Players in server discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: another variable should hold this, so that maxplayers doesn't have to be a netvar) - // Grab the host's IP for joining. - if (cv_allownewplayer.value && ((join = DRPC_GetServerIP()) != NULL)) + if (DRPC_InvitesAreAllowed() == true) { - char *xorjoin = DRPC_XORIPString(join); - discordPresence.joinSecret = xorjoin; - free(xorjoin); + const char *join; + + // Grab the host's IP for joining. + if ((join = DRPC_GetServerIP()) != NULL) + { + char *xorjoin = DRPC_XORIPString(join); + discordPresence.joinSecret = xorjoin; + free(xorjoin); + } } } else diff --git a/src/discord.h b/src/discord.h index fd306fa8c..6bd081a98 100644 --- a/src/discord.h +++ b/src/discord.h @@ -18,15 +18,26 @@ #include "discord_rpc.h" extern consvar_t cv_discordrp; +extern consvar_t cv_discordstreamer; +extern consvar_t cv_discordasks; +extern consvar_t cv_discordinvites; + +extern struct discordInfo_s { + UINT8 maxPlayers; + boolean joinsAllowed; + boolean everyoneCanInvite; +} discordInfo; typedef struct discordRequest_s { tic_t timer; // Tics left on the request before it expires. - char *username; // Discord user name + their discriminator. + char *username; // Discord user name. + char *discriminator; // Discord discriminator (The little hashtag thing after the username). Separated for a "hide discriminators" cvar. char *userID; // The ID of the Discord user, gets used with Discord_Respond() // HAHAHA, no. // *Maybe* if it was only PNG I would boot up curl just to get AND convert this to Doom GFX, - // but it can be a JEPG, WebP, or GIF too :) + // but it can *also* be a JEPG, WebP, or GIF :) + // Hey, wanna add ImageMagick as a dependency? :dying: //patch_t *avatar; struct discordRequest_s *next; // Next request in the list. @@ -55,6 +66,26 @@ void DRPC_RemoveRequest(discordRequest_t *removeRequest); void DRPC_Init(void); +/*-------------------------------------------------- + void DRPC_SendDiscordInfo(void); + + Sends the server's information needed for + the rich presence state. +--------------------------------------------------*/ + +void DRPC_SendDiscordInfo(void); + + +/*-------------------------------------------------- + void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum); + + Recieves the server's information needed for + the rich presence state. +--------------------------------------------------*/ + +void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum); + + /*-------------------------------------------------- void DRPC_UpdatePresence(void); diff --git a/src/m_menu.c b/src/m_menu.c index db0dd9a36..da88ea831 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -304,6 +304,9 @@ menu_t OP_SoundOptionsDef; //Misc menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef; +#ifdef HAVE_DISCORDRPC +menu_t OP_DiscordOptionsDef; +#endif menu_t OP_HUDOptionsDef, OP_ChatOptionsDef; menu_t OP_GameOptionsDef, OP_ServerOptionsDef; #ifndef NONET @@ -1361,19 +1364,15 @@ static menuitem_t OP_SoundOptionsMenu[] = static menuitem_t OP_DataOptionsMenu[] = { -#ifdef HAVE_DISCORDRPC - {IT_STRING | IT_CVAR, NULL, "Discord Rich Presence", &cv_discordrp, 10}, - {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 30}, - {IT_STRING | IT_CALL, NULL, "Addon Options...", M_AddonsOptions, 40}, - {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 50}, - - {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 70}, -#else {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, {IT_STRING | IT_CALL, NULL, "Addon Options...", M_AddonsOptions, 20}, {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_SUBMENU, NULL, "Discord Options...", &OP_DiscordOptionsDef, 40}, + {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 60}, +#else {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, #endif }; @@ -1424,7 +1423,7 @@ static menuitem_t OP_AddonsOptionsMenu[] = {IT_HEADER, NULL, "Menu", NULL, 0}, {IT_STRING|IT_CVAR, NULL, "Location", &cv_addons_option, 10}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_addons_folder, 20}, - {IT_STRING|IT_CVAR, NULL, "Identify addons via", &cv_addons_md5, 48}, + {IT_STRING|IT_CVAR, NULL, "Identify addons via", &cv_addons_md5, 48}, {IT_STRING|IT_CVAR, NULL, "Show unsupported file types", &cv_addons_showall, 58}, {IT_HEADER, NULL, "Search", NULL, 76}, @@ -1437,6 +1436,19 @@ enum op_addons_folder = 2, }; +#ifdef HAVE_DISCORDRPC +static menuitem_t OP_DiscordOptionsMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Rich Presence", &cv_discordrp, 10}, + + {IT_HEADER, NULL, "Rich Presence Settings", NULL, 30}, + {IT_STRING | IT_CVAR, NULL, "Streamer Mode", &cv_discordstreamer, 40}, + + {IT_STRING | IT_CVAR, NULL, "Allow Ask To Join", &cv_discordasks, 60}, + {IT_STRING | IT_CVAR, NULL, "Allow Invites", &cv_discordinvites, 70}, +}; +#endif + static menuitem_t OP_HUDOptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 10}, @@ -2118,6 +2130,7 @@ menu_t OP_OpenGLColorDef = menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_DataOptionsDef, 30, 30); menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); +menu_t OP_DiscordOptionsDef = DEFAULTMENUSTYLE(NULL, OP_DiscordOptionsMenu, &OP_DataOptionsDef, 30, 30); menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30); // ========================================================================== @@ -6275,7 +6288,12 @@ static void M_Options(INT32 choice) OP_MainMenu[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits + +#ifdef HAVE_DISCORDRPC + OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data +#else OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data +#endif OP_GameOptionsMenu[3].status = (M_SecretUnlocked(SECRET_ENCORE)) ? (IT_CVAR|IT_STRING) : IT_SECRET; // cv_kartencore @@ -11360,14 +11378,20 @@ static void M_DrawDiscordRequests(void) return; } - V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, curRequest->username); + V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, + cv_discordstreamer.value ? curRequest->username : + va("%s#%s", curRequest->username, curRequest->discriminator) + ); V_DrawThinString(x, y + 24, V_ALLOWLOWERCASE|V_6WIDTHSPACE, "A - Accept B - Decline"); y -= 16; while (curRequest->next != NULL) { curRequest = curRequest->next; - V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, curRequest->username); + V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, + cv_discordstreamer.value ? curRequest->username : + va("%s#%s", curRequest->username, curRequest->discriminator) + ); y -= 8; } } From 5cab1b7b23ad88a12aeafd0500993e757a5e273e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 03:52:07 -0400 Subject: [PATCH 089/193] Menu is pretty now --- src/d_clisrv.c | 4 ++ src/m_menu.c | 154 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 120 insertions(+), 38 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 352321e19..93f14c325 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3659,6 +3659,10 @@ static boolean SV_AddWaitingPlayers(void) } } +#ifdef HAVE_DISCORDRPC + DRPC_SendDiscordInfo(); +#endif + return newplayer; } diff --git a/src/m_menu.c b/src/m_menu.c index da88ea831..04ed17fab 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11328,71 +11328,149 @@ static void M_OGL_DrawColorMenu(void) #endif #ifdef HAVE_DISCORDRPC +static const tic_t confirmLength = 3*TICRATE/4; +static tic_t confirmDelay = 0; +static boolean confirmAccept = false; + static void M_HandleDiscordRequests(INT32 choice) { - discordRequest_t *curRequest = discordRequestList; - - if (curRequest == NULL) - { - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu); - else - M_ClearMenus(true); - - return; - } - switch (choice) { case KEY_ESCAPE: - Discord_Respond(curRequest->userID, DISCORD_REPLY_NO); - DRPC_RemoveRequest(curRequest); + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_NO); + confirmAccept = false; + confirmDelay = confirmLength; break; case KEY_ENTER: - Discord_Respond(curRequest->userID, DISCORD_REPLY_YES); - DRPC_RemoveRequest(curRequest); + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_YES); + confirmAccept = true; + confirmDelay = confirmLength; break; } +} - if (curRequest == NULL) +static const char *M_GetDiscordName(discordRequest_t *r) +{ + if (r == NULL) + return ""; + + if (cv_discordstreamer.value) + return r->username; + + return va("%s#%s", r->username, r->discriminator); +} + +// (this goes in k_hud.c when merged into v2) +static void M_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean small) +{ + patch_t *stickerEnd; + INT32 height; + + if (small == true) { - // was removed, we can exit menu - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu); - else - M_ClearMenus(true); + stickerEnd = W_CachePatchName("K_STIKE2", PU_CACHE); + height = 6; } + else + { + stickerEnd = W_CachePatchName("K_STIKEN", PU_CACHE); + height = 11; + } + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, flags, stickerEnd, NULL); + V_DrawFill(x, y, width, height, 24|flags); + V_DrawFixedPatch((x + width)*FRACUNIT, y*FRACUNIT, FRACUNIT, flags|V_FLIP, stickerEnd, NULL); } static void M_DrawDiscordRequests(void) { discordRequest_t *curRequest = discordRequestList; + UINT8 *colormap; + patch_t *hand = NULL; + boolean removeRequest = false; - INT32 x = 64; - INT32 y = 135; + const char *wantText = "...would like to join!"; + const char *controlText = "\x82" "A" "\x80" " - Accept " "\x82" "B" "\x80" " - Decline"; - if (curRequest == NULL) + INT32 x = 100; + INT32 y = 133; + + INT32 slide = 0; + INT32 maxYSlide = 18; + + if (confirmDelay > 0) { - // Uh oh! Shouldn't happen! - return; + if (confirmAccept == true) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GREEN, GTC_MENUCACHE); + hand = W_CachePatchName("K_LAPH02", PU_CACHE); + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_RED, GTC_MENUCACHE); + hand = W_CachePatchName("K_LAPH03", PU_CACHE); + } + + slide = confirmLength - confirmDelay; + + confirmDelay--; + + if (confirmDelay == 0) + removeRequest = true; + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GREY, GTC_MENUCACHE); } - V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, - cv_discordstreamer.value ? curRequest->username : - va("%s#%s", curRequest->username, curRequest->discriminator) - ); - V_DrawThinString(x, y + 24, V_ALLOWLOWERCASE|V_6WIDTHSPACE, "A - Accept B - Decline"); - y -= 16; + V_DrawFixedPatch(56*FRACUNIT, 150*FRACUNIT, FRACUNIT, 0, W_CachePatchName("K_LAPE01", PU_CACHE), colormap); + + if (hand != NULL) + { + fixed_t handoffset = (4 - abs((signed)(skullAnimCounter - 4))) * FRACUNIT; + V_DrawFixedPatch(56*FRACUNIT, 150*FRACUNIT + handoffset, FRACUNIT, 0, hand, NULL); + } + + M_DrawSticker(x + (slide * 32), y - 1, V_ThinStringWidth(M_GetDiscordName(curRequest), V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, false); + V_DrawThinString(x + (slide * 32), y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_YELLOWMAP, M_GetDiscordName(curRequest)); + + M_DrawSticker(x, y + 12, V_ThinStringWidth(wantText, V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, true); + V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE|V_6WIDTHSPACE, wantText); + + M_DrawSticker(x, y + 26, V_ThinStringWidth(controlText, V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, true); + V_DrawThinString(x, y + 24, V_ALLOWLOWERCASE|V_6WIDTHSPACE, controlText); + + y -= 18; while (curRequest->next != NULL) { + INT32 ySlide = min(slide * 4, maxYSlide); + curRequest = curRequest->next; - V_DrawThinString(x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE, - cv_discordstreamer.value ? curRequest->username : - va("%s#%s", curRequest->username, curRequest->discriminator) - ); - y -= 8; + + M_DrawSticker(x, y - 1 + ySlide, V_ThinStringWidth(M_GetDiscordName(curRequest), V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, false); + V_DrawThinString(x, y + ySlide, V_ALLOWLOWERCASE|V_6WIDTHSPACE, M_GetDiscordName(curRequest)); + + y -= 12; + maxYSlide = 12; + } + + if (removeRequest == true) + { + DRPC_RemoveRequest(discordRequestList); + + if (discordRequestList == NULL) + { + // No other requests + + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + + return; + } } } #endif From 1899b31fa736785b3755293fab32c480e171fe57 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 04:54:08 -0400 Subject: [PATCH 090/193] Add envelope icon, add sounds --- src/d_clisrv.c | 4 ++++ src/discord.c | 27 +++++++++++++++++---------- src/m_menu.c | 17 +++++++++++------ src/sdl/i_video.c | 9 +++++++++ src/sounds.c | 2 ++ src/sounds.h | 2 ++ src/st_stuff.c | 22 ++++++++++++++++++++++ src/st_stuff.h | 5 +++++ 8 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 93f14c325..ed016e5c3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -46,6 +46,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "s_sound.h" // sfx_syfail #ifdef CLIENT_LOADINGSCREEN // cl loading screen @@ -3130,6 +3131,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) #endif } + if (msg != KICK_MSG_PLAYER_QUIT) + S_StartSound(NULL, sfx_syfail); // he he he + switch (msg) { case KICK_MSG_GO_AWAY: diff --git a/src/discord.c b/src/discord.c index 7e20cde65..31b1f9d94 100644 --- a/src/discord.c +++ b/src/discord.c @@ -268,6 +268,9 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) { discordRequestList = newRequest; } + + // Made it to the end, request was valid, so play the request sound :) + S_StartSound(NULL, sfx_requst); } /*-------------------------------------------------- @@ -375,16 +378,6 @@ void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum) discordInfo.everyoneCanInvite = (boolean)READUINT8(*p); DRPC_UpdatePresence(); - - if (DRPC_InvitesAreAllowed() == false) - { - // Flush the request list, if it still exists - while (discordRequestList != NULL) - { - Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE); - DRPC_RemoveRequest(discordRequestList); - } - } } #ifdef HAVE_CURL @@ -514,6 +507,8 @@ void DRPC_UpdatePresence(void) char charimg[4+SKINNAMESIZE+1]; char charname[11+SKINNAMESIZE+1]; + boolean joinSecretSet = false; + DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); @@ -564,6 +559,8 @@ void DRPC_UpdatePresence(void) char *xorjoin = DRPC_XORIPString(join); discordPresence.joinSecret = xorjoin; free(xorjoin); + + joinSecretSet = true; } } } @@ -724,6 +721,16 @@ void DRPC_UpdatePresence(void) discordPresence.smallImageText = charname; // Character name } + if (joinSecretSet == false) + { + // Not able to join? Flush the request list, if it exists. + while (discordRequestList != NULL) + { + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE); + DRPC_RemoveRequest(discordRequestList); + } + } + Discord_UpdatePresence(&discordPresence); } diff --git a/src/m_menu.c b/src/m_menu.c index 04ed17fab..5e4121f8c 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11334,18 +11334,23 @@ static boolean confirmAccept = false; static void M_HandleDiscordRequests(INT32 choice) { + if (confirmDelay > 0) + return; + switch (choice) { - case KEY_ESCAPE: - Discord_Respond(discordRequestList->userID, DISCORD_REPLY_NO); - confirmAccept = false; - confirmDelay = confirmLength; - break; - case KEY_ENTER: Discord_Respond(discordRequestList->userID, DISCORD_REPLY_YES); confirmAccept = true; confirmDelay = confirmLength; + S_StartSound(NULL, sfx_s3k63); + break; + + case KEY_ESCAPE: + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_NO); + confirmAccept = false; + confirmDelay = confirmLength; + S_StartSound(NULL, sfx_s3kb2); break; } } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a740ef84d..cbd1e96c6 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -81,6 +81,10 @@ #include "ogl_sdl.h" #endif +#ifdef HAVE_DISCORDRPC +#include "../discord.h" +#endif + // maximum number of windowed modes (see windowedModes[][]) #define MAXWINMODES (18) @@ -1387,6 +1391,11 @@ void I_FinishUpdate(void) if (cv_showping.value && netgame && consoleplayer != serverplayer) SCR_DisplayLocalPing(); +#ifdef HAVE_DISCORDRPC + if (discordRequestList != NULL) + ST_AskToJoinEnvelope(); +#endif + if (rendermode == render_soft && screens[0]) { SDL_Rect rect; diff --git a/src/sounds.c b/src/sounds.c index 61fddb76f..40ef0f7f2 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -816,6 +816,8 @@ sfxinfo_t S_sfx[NUMSFX] = {"mkuma", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Trigger Happy Havoc Monokuma {"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping + {"requst", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request + {"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification diff --git a/src/sounds.h b/src/sounds.h index dfd0bbdcd..2a7169190 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -891,6 +891,8 @@ typedef enum sfx_mkuma, sfx_toada, sfx_bsnipe, + sfx_requst, + sfx_syfail, sfx_itfree, sfx_dbgsal, diff --git a/src/st_stuff.c b/src/st_stuff.c index 252457f98..d570e3182 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -129,6 +129,11 @@ static patch_t *gotbflag; static patch_t *hud_tv1; static patch_t *hud_tv2; +#ifdef HAVE_DISCORDRPC +// Discord Rich Presence +static patch_t *envelope; +#endif + // SRB2kart hudinfo_t hudinfo[NUMHUDITEMS] = @@ -349,6 +354,11 @@ void ST_LoadGraphics(void) // Midnight Channel: hud_tv1 = W_CachePatchName("HUD_TV1", PU_HUDGFX); hud_tv2 = W_CachePatchName("HUD_TV2", PU_HUDGFX); + +#ifdef HAVE_DISCORDRPC + // Discord Rich Presence + envelope = W_CachePatchName("K_REQUES", PU_HUDGFX); +#endif } // made separate so that skins code can reload custom face graphics @@ -2080,6 +2090,18 @@ static void ST_MayonakaStatic(void) V_DrawFixedPatch(320< Date: Sat, 22 Aug 2020 16:58:59 +0800 Subject: [PATCH 091/193] Compile m_menu.c without HAVE_DISCORDRPC --- src/m_menu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/m_menu.c b/src/m_menu.c index 5e4121f8c..0bec965e4 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2130,7 +2130,9 @@ menu_t OP_OpenGLColorDef = menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_DataOptionsDef, 30, 30); menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); +#ifdef HAVE_DISCORDRPC menu_t OP_DiscordOptionsDef = DEFAULTMENUSTYLE(NULL, OP_DiscordOptionsMenu, &OP_DataOptionsDef, 30, 30); +#endif menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30); // ========================================================================== From 38ec458903a74e575ca8f5543d05a00b90e889f9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 05:03:08 -0400 Subject: [PATCH 092/193] Always have XD_DISCORD defined, so that people compiling without Discord support can connect to servers that do. --- src/d_netcmd.c | 32 +++++++++++++++++++++++++++++--- src/d_netcmd.h | 8 +++----- src/discord.c | 36 ++++++------------------------------ src/discord.h | 10 ---------- 4 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8778a2420..fd856a86b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -81,6 +81,7 @@ static void Got_RandomSeed(UINT8 **cp, INT32 playernum); static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum); static void Got_Teamchange(UINT8 **cp, INT32 playernum); static void Got_Clearscores(UINT8 **cp, INT32 playernum); +static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); static void PointLimit_OnChange(void); static void TimeLimit_OnChange(void); @@ -713,9 +714,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_dummyconsvar); -#ifdef HAVE_DISCORDRPC - RegisterNetXCmd(XD_DISCORD, DRPC_RecieveDiscordInfo); -#endif + RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo); } // ========================================================================= @@ -5696,3 +5695,30 @@ static void KartEliminateLast_OnChange(void) if (G_RaceGametype() && cv_karteliminatelast.value) P_CheckRacers(); } + +void Got_DiscordInfo(UINT8 **p, INT32 playernum) +{ + if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) + { + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal Discord info command received from %s\n"), player_names[playernum]); + if (server) + { + XBOXSTATIC UINT8 buf[2]; + + buf[0] = (UINT8)playernum; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + return; + } + + // Don't do anything with the information if we don't have Discord RP support +#ifdef HAVE_DISCORDRPC + discordInfo.maxPlayers = READUINT8(*p); + discordInfo.joinsAllowed = (boolean)READUINT8(*p); + discordInfo.everyoneCanInvite = (boolean)READUINT8(*p); + + DRPC_UpdatePresence(); +#endif +} diff --git a/src/d_netcmd.h b/src/d_netcmd.h index d6e7e08db..1e1588083 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -178,12 +178,10 @@ typedef enum XD_MODIFYVOTE, // 23 XD_PICKVOTE, // 24 XD_REMOVEPLAYER,// 25 + XD_DISCORD, // 26 #ifdef HAVE_BLUA - XD_LUACMD, // 26 - XD_LUAVAR, // 27 -#endif -#ifdef HAVE_DISCORDRPC - XD_DISCORD, // 28 + XD_LUACMD, // 27 + XD_LUAVAR, // 28 #endif MAXNETXCMD } netxcmd_t; diff --git a/src/discord.c b/src/discord.c index 31b1f9d94..91c06ba14 100644 --- a/src/discord.c +++ b/src/discord.c @@ -351,35 +351,6 @@ void DRPC_SendDiscordInfo(void) SendNetXCmd(XD_DISCORD, &buf, 3); } -/*-------------------------------------------------- - void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum) - - See header file for description. ---------------------------------------------------*/ -void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum) -{ - if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) - { - // protect against hacked/buggy client - CONS_Alert(CONS_WARNING, M_GetText("Illegal Discord info command received from %s\n"), player_names[playernum]); - if (server) - { - XBOXSTATIC UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - return; - } - - discordInfo.maxPlayers = READUINT8(*p); - discordInfo.joinsAllowed = (boolean)READUINT8(*p); - discordInfo.everyoneCanInvite = (boolean)READUINT8(*p); - - DRPC_UpdatePresence(); -} - #ifdef HAVE_CURL /*-------------------------------------------------- static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) @@ -547,7 +518,7 @@ void DRPC_UpdatePresence(void) discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = cv_maxplayers.value; // Max players (TODO: another variable should hold this, so that maxplayers doesn't have to be a netvar) + discordPresence.partyMax = discordInfo.maxPlayers; // Max players if (DRPC_InvitesAreAllowed() == true) { @@ -566,6 +537,11 @@ void DRPC_UpdatePresence(void) } else { + // Reset discord info if you're not in a place that uses it! + // Important for if you join a server that compiled without HAVE_DISCORDRPC, + // so that you don't ever end up using bad information from another server. + memset(&discordInfo, 0, sizeof(discordInfo)); + // Offline info if (Playing()) discordPresence.state = "Offline"; diff --git a/src/discord.h b/src/discord.h index 6bd081a98..ec47ec91a 100644 --- a/src/discord.h +++ b/src/discord.h @@ -76,16 +76,6 @@ void DRPC_Init(void); void DRPC_SendDiscordInfo(void); -/*-------------------------------------------------- - void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum); - - Recieves the server's information needed for - the rich presence state. ---------------------------------------------------*/ - -void DRPC_RecieveDiscordInfo(UINT8 **p, INT32 playernum); - - /*-------------------------------------------------- void DRPC_UpdatePresence(void); From 9feee5348dd73755da1d6fc5187530aeecf7677e Mon Sep 17 00:00:00 2001 From: lachwright Date: Sat, 22 Aug 2020 17:35:31 +0800 Subject: [PATCH 093/193] Correct misplaced MT_ARIDTOAD listing --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 96d82fff4..909747548 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7764,7 +7764,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_TWINKLECARTAMBIENCE", "MT_EXPLODINGBARREL", "MT_MERRYHORSE", - "MT_ARIDTOAD", "MT_BLUEFRUIT", "MT_ORANGEFRUIT", "MT_REDFRUIT", @@ -7775,6 +7774,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BOOSTPROMPT", "MT_BOOSTOFF", "MT_BOOSTON", + "MT_ARIDTOAD", "MT_LIZARDMAN", "MT_LIONMAN", From 26295bb793db2d01e72db588b75414d0520fb011 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 07:16:52 -0400 Subject: [PATCH 094/193] Ask to join is a menu option, rather than trying to replace your menu temporarily. --- src/m_menu.c | 77 +++++++++++++++++++++++++++++++++++++------------- src/st_stuff.c | 4 +++ 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 0bec965e4..e2e2be241 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -194,7 +194,7 @@ static void M_RoomMenu(INT32 choice); menu_t MessageDef; #ifdef HAVE_DISCORDRPC -menu_t DiscordRequestsDef; +menu_t MISC_DiscordRequestsDef; static void M_HandleDiscordRequests(INT32 choice); static void M_DrawDiscordRequests(void); #endif @@ -599,6 +599,10 @@ static menuitem_t MPauseMenu[] = {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, {IT_STRING | IT_CALL, NULL, "Switch Map..." , M_MapChange, 24}, +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_SUBMENU, NULL, "Ask To Join Requests...", &MISC_DiscordRequestsDef, 24}, +#endif + {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus, 40}, {IT_CALL | IT_STRING, NULL, "P1 Setup...", M_SetupMultiPlayer, 48}, // splitscreen {IT_CALL | IT_STRING, NULL, "P2 Setup...", M_SetupMultiPlayer2, 56}, // splitscreen @@ -622,6 +626,9 @@ typedef enum mpause_addons = 0, mpause_scramble, mpause_switchmap, +#ifdef HAVE_DISCORDRPC + mpause_discordrequests, +#endif mpause_continue, mpause_psetupsplit, @@ -673,7 +680,7 @@ typedef enum } spause_e; #ifdef HAVE_DISCORDRPC -static menuitem_t DiscordRequestsMenu[] = +static menuitem_t MISC_DiscordRequestsMenu[] = { {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleDiscordRequests, 0}, }; @@ -1690,13 +1697,11 @@ menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); #ifdef HAVE_DISCORDRPC -static void M_HandleDiscordRequests(INT32 choice); -static void M_DrawDiscordRequests(void); -menu_t DiscordRequestsDef = { +menu_t MISC_DiscordRequestsDef = { NULL, - sizeof (DiscordRequestsMenu)/sizeof (menuitem_t), - NULL, - DiscordRequestsMenu, + sizeof (MISC_DiscordRequestsMenu)/sizeof (menuitem_t), + &MPauseDef, + MISC_DiscordRequestsMenu, M_DrawDiscordRequests, 0, 0, 0, @@ -3252,12 +3257,18 @@ void M_StartControlPanel(void) MPauseMenu[mpause_psetup].status = IT_DISABLED; MISC_ChangeTeamMenu[0].status = IT_DISABLED; MISC_ChangeSpectateMenu[0].status = IT_DISABLED; + // Reset these in case splitscreen messes things up + MPauseMenu[mpause_addons].alphaKey = 8; + MPauseMenu[mpause_scramble].alphaKey = 8; + MPauseMenu[mpause_switchmap].alphaKey = 24; + MPauseMenu[mpause_switchteam].alphaKey = 48; MPauseMenu[mpause_switchspectate].alphaKey = 48; MPauseMenu[mpause_options].alphaKey = 64; MPauseMenu[mpause_title].alphaKey = 80; MPauseMenu[mpause_quit].alphaKey = 88; + Dummymenuplayer_OnChange(); if ((server || IsPlayerAdmin(consoleplayer))) @@ -3329,21 +3340,25 @@ void M_StartControlPanel(void) MPauseMenu[mpause_spectate].status = IT_GRAYEDOUT; } +#ifdef HAVE_DISCORDRPC + { + UINT8 i; + + for (i = 0; i < mpause_discordrequests; i++) + MPauseMenu[i].alphaKey -= 8; + + MPauseMenu[mpause_discordrequests].alphaKey = MPauseMenu[i].alphaKey; + MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; + + if (discordRequestList != NULL) + MPauseMenu[mpause_discordrequests].status = IT_STRING | IT_SUBMENU; + } +#endif + currentMenu = &MPauseDef; itemOn = mpause_continue; } -#ifdef HAVE_DISCORDRPC - // Kind of hi-jacking this... - if (discordRequestList != NULL) - { - // Gotta take care of these requests first - DiscordRequestsDef.prevMenu = currentMenu; - currentMenu = &DiscordRequestsDef; - itemOn = 0; - } -#endif - CON_ToggleOff(); // move away console } @@ -4167,6 +4182,25 @@ static void M_DrawPauseMenu(void) } #endif +#ifdef HAVE_DISCORDRPC + // kind of hackily baked in here + if (currentMenu == &MPauseDef && discordRequestList != NULL) + { + const tic_t freq = TICRATE/2; + + if ((leveltime % freq) >= freq/2) + { + V_DrawFixedPatch(204 * FRACUNIT, + (currentMenu->y + MPauseMenu[mpause_discordrequests].alphaKey - 1) * FRACUNIT, + FRACUNIT, + 0, + W_CachePatchName("K_REQUE2", PU_CACHE), + NULL + ); + } + } +#endif + M_DrawGenericMenu(); } @@ -11470,9 +11504,14 @@ static void M_DrawDiscordRequests(void) if (discordRequestList == NULL) { // No other requests + MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; if (currentMenu->prevMenu) + { M_SetupNextMenu(currentMenu->prevMenu); + if (currentMenu == &MPauseDef) + itemOn = mpause_continue; + } else M_ClearMenus(true); diff --git a/src/st_stuff.c b/src/st_stuff.c index d570e3182..9d7269982 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2095,10 +2095,14 @@ void ST_AskToJoinEnvelope(void) { const tic_t freq = TICRATE/2; + if (menuactive) + return; + if ((leveltime % freq) < freq/2) return; V_DrawFixedPatch(296*FRACUNIT, 2*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTORIGHT, envelope, NULL); + // maybe draw number of requests with V_DrawPingNum ? } #endif From 7228cc80a1332885b8b125e40f2d70a79262e3d4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 07:25:47 -0400 Subject: [PATCH 095/193] Remove timer variable Doesn't feel necessary to me anymore, especially with how the menu works --- src/discord.c | 3 --- src/discord.h | 1 - 2 files changed, 4 deletions(-) diff --git a/src/discord.c b/src/discord.c index 91c06ba14..1b2ef6253 100644 --- a/src/discord.c +++ b/src/discord.c @@ -231,9 +231,6 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) newRequest = Z_Calloc(sizeof(discordRequest_t), PU_STATIC, NULL); - // Discord requests expire after 30 seconds - newRequest->timer = (30*TICRATE)-1; - newRequest->username = Z_Calloc(344, PU_STATIC, NULL); snprintf(newRequest->username, 344, "%s", requestUser->username); diff --git a/src/discord.h b/src/discord.h index ec47ec91a..97c5557b6 100644 --- a/src/discord.h +++ b/src/discord.h @@ -29,7 +29,6 @@ extern struct discordInfo_s { } discordInfo; typedef struct discordRequest_s { - tic_t timer; // Tics left on the request before it expires. char *username; // Discord user name. char *discriminator; // Discord discriminator (The little hashtag thing after the username). Separated for a "hide discriminators" cvar. char *userID; // The ID of the Discord user, gets used with Discord_Respond() From d302ad6e382bf5c1d4de8fab937f8d33accb9fb0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Aug 2020 09:46:25 -0400 Subject: [PATCH 096/193] Fix XOR not working --- src/discord.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/discord.c b/src/discord.c index 1b2ef6253..068c2404d 100644 --- a/src/discord.c +++ b/src/discord.c @@ -26,7 +26,6 @@ #include "r_things.h" // skins #include "mserv.h" // ms_RoomId #include "z_zone.h" -#include "m_random.h" // P_GetInitSeed #include "byteptr.h" #include "discord.h" @@ -69,16 +68,29 @@ static char self_ip[IP_SIZE]; --------------------------------------------------*/ static char *DRPC_XORIPString(const char *input) { - const UINT32 is = (P_GetInitSeed() % UINT8_MAX); - const UINT8 xor[IP_SIZE] = {is, is+106, is-64, is+251, is-207, is+16, is-28, is+78, is-4, is+118, is-46, is+76, is-153, is+45, is-91, is+100}; - char *output = malloc(sizeof(char) * IP_SIZE); + const UINT8 xor[IP_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + char *output = malloc(sizeof(char) * (IP_SIZE+1)); UINT8 i; for (i = 0; i < IP_SIZE; i++) { - output[i] = input[i] ^ xor[i]; + char xorinput; + + if (!input[i]) + break; + + xorinput = input[i] ^ xor[i]; + + if (xorinput < 32 || xorinput > 126) + { + xorinput = input[i]; + } + + output[i] = xorinput; } + output[i] = '\0'; + return output; } From 28882fec24eb1e8dc840add7220444f3669adc26 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sat, 22 Aug 2020 22:04:45 +0300 Subject: [PATCH 097/193] Fix replay recording memory leak --- src/g_game.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bf0557d02..068bd9076 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6326,8 +6326,8 @@ void G_RecordDemo(const char *name) maxsize = 1024*1024*2; if (M_CheckParm("-maxdemo") && M_IsNextParm()) maxsize = atoi(M_GetNextParm()) * 1024; -// if (demobuffer) -// free(demobuffer); + if (demobuffer) + free(demobuffer); demo_p = NULL; demobuffer = malloc(maxsize); demoend = demobuffer + maxsize; @@ -7153,6 +7153,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7181,6 +7182,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7193,6 +7195,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7212,6 +7215,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7272,6 +7276,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7342,6 +7347,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7355,6 +7361,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7379,6 +7386,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7422,6 +7430,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7477,6 +7486,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -7491,6 +7501,7 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); + demobuffer = NULL; demo.playback = false; demo.title = false; return; @@ -8140,6 +8151,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void) saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file. } free(demobuffer); + demobuffer = NULL; metalrecording = false; if (saved) I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap)); @@ -8320,6 +8332,7 @@ void G_SaveDemo(void) if (FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer)) // finally output the file. demo.savemode = DSM_SAVED; free(demobuffer); + demobuffer = NULL; demo.recording = false; if (modeattacking != ATTACKING_RECORD) From 37c5c1ca8aff471ed889afcdf846de49dd1c1d10 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sun, 23 Aug 2020 00:10:50 +0300 Subject: [PATCH 098/193] Fix timedemo in OpenGL mode --- src/hardware/hw_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c7ff5a4af..9b7b31e49 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -4733,6 +4733,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) } R_SetupFrame(player, false); // This can stay false because it is only used to set viewsky in r_main.c, which isn't used here + framecount++; // for timedemo HWR_RenderFrame(viewnumber, player, false); } From 7194aa9ad62947bcd3403d4897e3f6b38959ddc5 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Aug 2020 16:26:15 -0700 Subject: [PATCH 099/193] Skip XD_DISCORD payload in non Discord builds --- src/d_netcmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5d802ae3..5efe30889 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5720,5 +5720,7 @@ void Got_DiscordInfo(UINT8 **p, INT32 playernum) discordInfo.everyoneCanInvite = (boolean)READUINT8(*p); DRPC_UpdatePresence(); +#else + (*p) += 3; #endif } From 688adb205535a4d70e6a3b835ee57dbdc60bd791 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Aug 2020 17:16:49 -0700 Subject: [PATCH 100/193] Make join requests options on pause menu selectable even if you're already in the menu --- src/discord.c | 1 + src/m_menu.c | 18 +++++++++++++++--- src/m_menu.h | 2 ++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/discord.c b/src/discord.c index 068c2404d..e69b3abf4 100644 --- a/src/discord.c +++ b/src/discord.c @@ -276,6 +276,7 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) else { discordRequestList = newRequest; + M_RefreshPauseMenu(); } // Made it to the end, request was valid, so play the request sound :) diff --git a/src/m_menu.c b/src/m_menu.c index 2c8f66fe9..1979e30a8 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3348,10 +3348,8 @@ void M_StartControlPanel(void) MPauseMenu[i].alphaKey -= 8; MPauseMenu[mpause_discordrequests].alphaKey = MPauseMenu[i].alphaKey; - MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; - if (discordRequestList != NULL) - MPauseMenu[mpause_discordrequests].status = IT_STRING | IT_SUBMENU; + M_RefreshPauseMenu(); } #endif @@ -6370,6 +6368,20 @@ static void M_SelectableClearMenus(INT32 choice) M_ClearMenus(true); } +void M_RefreshPauseMenu(void) +{ +#ifdef HAVE_DISCORDRPC + if (discordRequestList != NULL) + { + MPauseMenu[mpause_discordrequests].status = IT_STRING | IT_SUBMENU; + } + else + { + MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; + } +#endif +} + // ====== // CHEATS // ====== diff --git a/src/m_menu.h b/src/m_menu.h index 1ad20c777..4fc92bd55 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -267,6 +267,8 @@ void Addons_option_Onchange(void); void M_ReplayHut(INT32 choice); void M_SetPlaybackMenuPointer(void); +void M_RefreshPauseMenu(void); + INT32 HU_GetHighlightColor(void); // These defines make it a little easier to make menus From 481f8c2caab542c21c9e1fd396028a75c9a58267 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Aug 2020 18:38:20 -0700 Subject: [PATCH 101/193] Avoid underflow hacks completely with asktime --- src/d_clisrv.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c847f0a65..829c37d29 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2270,10 +2270,10 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) } // Ask the info to the server (askinfo packet) - if ((I_GetTime() - NEWTICRATE) >= *asksent) + if (I_GetTime() >= *asksent) { SendAskInfo(servernode); - *asksent = I_GetTime(); + *asksent = I_GetTime() + NEWTICRATE; } #else (void)viams; @@ -2314,12 +2314,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic case CL_ASKFULLFILELIST: if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved cl_mode = CL_CHECKFILES; - else if (fileneedednum != cl_lastcheckedfilecount || (I_GetTime() - NEWTICRATE) >= *asksent) + else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent) { if (CL_AskFileList(fileneedednum)) { cl_lastcheckedfilecount = fileneedednum; - *asksent = I_GetTime(); + *asksent = I_GetTime() + NEWTICRATE; } } break; @@ -2387,7 +2387,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic case CL_LOADFILES: if (CL_LoadServerFiles()) { - *asksent = I_GetTime() - (NEWTICRATE*3); //This ensure the first join ask is right away + *asksent = 0; //This ensure the first join ask is right away firstconnectattempttime = I_GetTime(); cl_mode = CL_ASKJOIN; } @@ -2414,14 +2414,14 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic // but since the network layer doesn't provide ordered packets... CL_PrepareDownloadSaveGame(tmpsave); #endif - if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent && CL_SendJoin()) + if (I_GetTime() >= *asksent && CL_SendJoin()) { - *asksent = I_GetTime(); + *asksent = I_GetTime() + NEWTICRATE*3; cl_mode = CL_WAITJOINRESPONSE; } break; case CL_WAITJOINRESPONSE: - if (( I_GetTime() - NEWTICRATE*3 ) >= *asksent) + if (I_GetTime() >= *asksent) { cl_mode = CL_ASKJOIN; } @@ -2555,9 +2555,9 @@ static void CL_ConnectToServer(void) ClearAdminPlayers(); pnumnodes = 1; - oldtic = I_GetTime() - 1; + oldtic = 0; #ifndef NONET - asksent = I_GetTime() - NEWTICRATE*3; + asksent = 0; firstconnectattempttime = I_GetTime(); i = SL_SearchServer(servernode); From 36c5abf611ed2f46ffa3cee462f2ce816f0f55a6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Aug 2020 04:44:05 -0400 Subject: [PATCH 102/193] Fix version string --- src/doomdef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index e7abf0906..1965b3d06 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -155,8 +155,8 @@ extern char logfilename[1024]; #else #define VERSION 1 // Game version #define SUBVERSION 2 // more precise version number -#define VERSIONSTRING "v1.2 (HTTP MS)" -#define VERSIONSTRINGW L"v1.2 (HTTP MS)" +#define VERSIONSTRING "v1.2" +#define VERSIONSTRINGW L"v1.2" // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // And change CMakeLists.txt, for CMake users! // AND appveyor.yml, for the build bots! From a92ec01f434cb27742e88dac785319375d11675d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Aug 2020 05:47:55 -0400 Subject: [PATCH 103/193] Update win 32 bit libraries, implement win 64 bit libraries --- .../win32-dynamic/bin/discord-rpc.dll | Bin 307720 -> 307720 bytes .../win32-dynamic/include/discord_register.h | 20 ++-- .../win32-dynamic/lib/discord-rpc.lib | Bin 3656 -> 3656 bytes .../win64-dynamic/bin/discord-rpc.dll | Bin 0 -> 399880 bytes .../win64-dynamic/include/discord_register.h | 26 ++++++ .../win64-dynamic/include/discord_rpc.h | 87 ++++++++++++++++++ .../win64-dynamic/lib/discord-rpc.lib | Bin 0 -> 3622 bytes src/win32/Makefile.cfg | 5 + 8 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 libs/discord-rpc/win64-dynamic/bin/discord-rpc.dll create mode 100644 libs/discord-rpc/win64-dynamic/include/discord_register.h create mode 100644 libs/discord-rpc/win64-dynamic/include/discord_rpc.h create mode 100644 libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib diff --git a/libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll b/libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll index 4254cfa278673e877a605f31c5b462c8e355d3cb..88c7d0cefe4c4007a97e4469e07f64f619147000 100644 GIT binary patch delta 119299 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 WPrq9E=H46A*8DKC%sG7}Mg3p$_w_sg delta 119274 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 L73cCgiu!*52lGu= diff --git a/libs/discord-rpc/win32-dynamic/include/discord_register.h b/libs/discord-rpc/win32-dynamic/include/discord_register.h index 4c16b68a7..16fb42f32 100644 --- a/libs/discord-rpc/win32-dynamic/include/discord_register.h +++ b/libs/discord-rpc/win32-dynamic/include/discord_register.h @@ -1,17 +1,17 @@ #pragma once #if defined(DISCORD_DYNAMIC_LIB) -# if defined(_WIN32) -# if defined(DISCORD_BUILDING_SDK) -# define DISCORD_EXPORT __declspec(dllexport) -# else -# define DISCORD_EXPORT __declspec(dllimport) -# endif -# else -# define DISCORD_EXPORT __attribute__((visibility("default"))) -# endif +#if defined(_WIN32) +#if defined(DISCORD_BUILDING_SDK) +#define DISCORD_EXPORT __declspec(dllexport) #else -# define DISCORD_EXPORT +#define DISCORD_EXPORT __declspec(dllimport) +#endif +#else +#define DISCORD_EXPORT __attribute__((visibility("default"))) +#endif +#else +#define DISCORD_EXPORT #endif #ifdef __cplusplus diff --git a/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib b/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib index 367920b36768f4de42c5b1bb697133d6f1e495d8..d8b6689f3ca2cdf5fb142b0b7b9752cdb46fc903 100644 GIT binary patch delta 375 zcmX>hb3$f<6o-kiv9YD0rOD(1R-%ZN|b=YiVg#)G0qeDXpbh+S$x yyI2`GCU0OCpL~r6i*0(mC_2R^@j)$Hz>DI1kObKE8bA{{fF^!mmYi(CCjbE2hb3$f<6o-+qrGcS=g~j9oRM!YV#~s4isUr$p_d4 zSV2;IC-Za4PyWLG0>zxkzc>USI{7#?7?GvKCx>zggQX_ta5xROb{S`d8^+%|AQkS`{0;ue9(oZ@yvmJy$<&jYo~jR!@K_~eB=5WCcX ycCj*WOy0mOKKU9C7TffAQFMw;;)7bYfEUI2APKPRHGn2^08RYBEIHYNPXGX{=xx>j diff --git a/libs/discord-rpc/win64-dynamic/bin/discord-rpc.dll b/libs/discord-rpc/win64-dynamic/bin/discord-rpc.dll new file mode 100644 index 0000000000000000000000000000000000000000..8493c5490041059c525b28f351bb0a0658ed8e74 GIT binary patch literal 399880 zcmdSC4SW>U)jvL)&60(X%<{4l1!RFmqY(_G)x?1AkX@LC4Mq_`K|p~Bii*iDFY*@L z1hWonsSj3LZA+_GYHh_ws{!9O1oJ`=0{9ZdYJ8t{eF<1ez}EeJ&%HCVn-F~I=V|-@ z=cDY*eL45sbI&>V+;h*pQ+(}a!7K?Mrq};Kr?6w=ruD`{x;QCu{z0L2qVUeROaI0gX1`@yj^X$EvElpZcj4G&?ET6m3l>vbr|VgXM-Ucnv{Vp^=$lZ?=7t9D+rDcQ-vXT?f%z_^lT)$ z@Vli;W~xv_1Am&oG5$p>{m6UkBNQT(sqe%jNq-JOm_Md$;r0IO1;Kg;A%_BM@cU_3 zz%DdDh8Gjuk0Y-FG|WfwQcg=eu|Jm}M8+)B3Q`~SzW(Nva;`BqmM&RXE(o8296|;D z>4V?49?JQbqLd&|F<~?Q#TW^no>DF{rmVbd0kRm4L2Fj-vs22s#*{5udK(H7&58B` z*h2W?lyZdq|1bXJ;Ah!8i6(M5W9!EjLA~%U_Uy#C%fa2*(fFSQVQ=D(%Bzpvi83LJ zqr<|=)Fra_Cd1o)9Pr-V$l!hWp$6~CQ{m+`slR?bMUZ#Q(Eb2>(`|qq%CH0>24Txa zyzL|glXhH;AMei5>+kc8{`(cPHp@z5aoA;*YonLSd9_~g$z8H}mmJ^a4Y@kK!Kf+l zj+kv((P0+E^DL1&Ekc9k9Kgy45b9ms-n87fEms!>LRCA@YP-giB8l2%oMHdT!s*W9=W@eg(SHBZMB>S9Z_D zcr23}T-&~l$K!HnmQ@Zdw#lJoS+a6cR*uTy(ebY^NTJ0oa#(IvFNMHWMf_<>qk8|( z1fkyT%r?o2+gT!q+|JzRiHyo2IqY-JQZEY{MVF#z^&DrutRO=U`J66PoG++bc@Zn1 zc~NAu9JXBXcWTY;lw~DNZoo(pL%E%cePK!q+oOY!pzKqd)(S$fGgb7oRviI4&8igu zD%JpM;QUf?SS3|N!K=_78ZA+KClG(<9<8MRUCLW5S2g+5cUn3RQgJInHHXWwy<$+??fA${k+igL!p(UDH9`67f90 z(^d0Rz!%S3Z*e7yTxpf7G(QY%m&%T>;^F#M2G$?nO!G` z<~VJ{@wuRV1Il4cgdH^U;C6NA!$dQ;(}i*H50aHZatM+D-0O%BWPr$>S0^`^sZ!TO zU1zyCY=X?iX%2uXhh@dk&~9Hrqy&d%6NyXq_bD;!RWxiN9IT z9`>uZgA0*DKp8HiOY>#DwTe|%!Ecw9-s&w$P>w28C%8MGv=%Z6k3NjqqrHGfLYGk= z7{5B?;Eu(BfIc4f&nB+P)%inKo~InS@cEVa@eh#|g}p25ZK{6{%?LcdDjs-ER)Rc_ zsE;o7lM91XD`17TA(8{WQjG*QRjR9gPYKQHDhvHi&E9E48Q|@h9i59TPT`6I!QUIy zv_?xIeRX>i?{}=DcR8WYYBYU@5nP-FeddrA%db~sn$FuzV?1A0PN-pO>&2(Hym5}2 zKBk{I3=OA=&)O;vij~hZ{D#TFIw(Y@xti5uXhYL|58lmmp9{M0Vs+9T0CRKWLU}{2 zu;|sHvhs@D5tVCCo+T^0!1b5O<`y}0736)%E@98F3gw(eXuxd7r={Y0(w*pc74%tE zBw!88sp={~Cw+$u=+bGkR5yD2$A5y^nc_Vg+3EoZA>)n;juml?1sR*87c-&op^JHY z)yMxppty2^V!9cMsXUu2CpO+QuAV7fC0#8MyS;@HO?=ULBQ+7F6J4^j7##3Y8l_4M_y+G!;7otsnfKhcs ztn36Zm`Qb?)8xO!xs8#!S-mWSv<6gw1>DOh6B6Kv^Eh`f1;ufw~AND^%F%zli}1ERe%X0PJ)B902;c28foJvhu3> zI|AepIC{lN=*a|$l>(#@;&am3rL}6GZ6^O2a(I=k)LnIBW*|dW)}u;1mYVl@UYwe^ z3Qb(ZnjppieCc%X!2~`O0dPkW@PTspb^>0S8Az9v*hq=LM;Z!R#-~i2}L92z5K>JWm zFck9SaK^b*u@D9%!x;jWdX_w)0{tPZ%^KEqD8UTKSX#a1??4-tt<_ewWs;Or!*;+*r#c*UaaN*mzl-%X1iEx-%n}p zr^!p+vbq30Y*~kupKNaRU#?i{mRkh362VkzsllTho@(7Gpp^RfwI~`gqyB{ZQ6B=? z2xJoinaR`&n@>KefO4_ z`3K4hxDHBx{cckFh2V|A2Qq{gb1_r)nQ~!~Em_k~b#OI*??X`d*^GM4T)S5wg*}00 zt{G59GnhIGKjGX8O;$6%3BC0H4rOxMNI%Wudi^2iW@N~rXPghP-xr))=~uDr2!K4K zsyZz~NI|3wa0St|fFSwNacI zfvWek;2FK}r~;t7LyOw+-YGX!*-%uV;$aUURAsTpDwYzea^TGudNezc&l+m*2r3K% zK(OL}8ya*`i4boWZ`JHApH-W&6`XO=o9Ntmd4f<0YTN@Rh7LMk<%+@~hl{tWFM!V# z&sI&zZS#(NRnx)q4J9-zNeK-g?OR$^o*C#10|tuXZ!xo1c^hEg{6yD31a{Hsz`|t1 zOb7)eR&ItMBtVOm6?n�`RwI2GVHGCv2*k#L802p?M@T6%w!P6`xH_$BOR~w-^_z zPGV%_;E7&=D6qI*!y=b;?p=7hdhFaX!XlT{;B-C___SHYKM6pb8W2oF&FX^$BWaG( z>9dkR;lY4~esUVOp z0@;=1I!$aEO=4-#X;TRxX5k4?j14OE8w{l}5?j^!a%zqB5YvywV!4ZkBz?=uZ?t@~ z1qxNRd%*$$^647#SdC$9L*SzxsOqUE^kqUnoe`BLLqE9VAHeV< za@U~ex4e=9YeB4fMxeGZy^KerXyJ9r2YE2{?N_s@fi%vNmop|Hv%s^N;FkctSlJ7; zl3C~YX<6rO$_d*a{fJZO0)s;0Fo=mZe|#@}-uNHT=jR;MIISzcha!aeb_<`f2C~%s z>KqI$pR&Zt`Mkn;AERwdTe*7MkxoYx(F-y^#S+_j$!%I9nR`B^UCB^Bun55u)-nsYId(Q%=3~kk zx|EVXz~Fq)8}rrNL*Q_8D@M|PJB$SXQeyWTW#vs8>waHisTQ8sq;x=~6#z@>!6VMs@#-n6I#&3;mku(5fSWw_{imMf|D0(0nVKqGTmZs#aCub(fX3 zHPoFe&E zLmftcMyO)bz-lPL*Vo62rT=?r0>BkARZ6`1&o)?fZE3zG5pLB@w|dPj z#Y$=xP$%^HPr9ho8cylFa9a%Z%Q;vF-PJafs=eTv=nHTXUOdKq^ zvZb6(T6#HZESO_U@ia!JB@X^D1;EJ5_Koa>W56LRlC?O!(>wANA6~QNP>O;jF`uhx zAqQ1mkRnz_QIVF>%XW}CC5N7HRv<-uZmDyOFZ75L11FS@jKYz+&^cOG&Mw6QNtDEw zc1txd%LI2$BxZIi4dQdtGfPJv0PZgpW$d7woW@v+lI{lIkEJ@839(e7DR)1Ng;F?o zv6MW72~q*oN4_o>?(yfU`M)Ji?LSuzPj|?nGb9DYr@Os zk(HHtvYMCq6pk-VKe@ERmhUG<-|gZ9GH-4jO2*#nYEK`gxp|@w?f}ecx#|qqu&^R_ z8dfFNyYk3AY>orP7-USG{kS#KM-EFiZXv_uB#($OmSg} z=)~OVA5Lwt&X-onnE`l{uv;;57xg@!nuh;NX-IlYUOeFWwCkO3{&wnm=UqxmUhmuk ze}lH(Ikd!B?=<2uvEJD;h4_c9cOFB12vO??$?Ki7ml+O-j+H-bz2h=GXrLfBflTD1 zF5xB!SZbD97mLTC{ac8^vej2q_C~&J5S)9{)EfAogU@dRR|@`K>Mm?k?GL0_rfj7`acAT<=|vjf^yP`xN1+E_E> zSkA@n5b(@ZCzLW6efXBmNR*NrSSeHz8W;$AN}$Hqe{stbR}sxK{)`_rKT1L{^yhen z13F{uV)~tvF`WHgQwuLIIcb~Wl(l5=w2VjJhC^GtrV$wz)XF)na>39k1X1)2ok+i- zp+y8a^G3A2dm{mO~>f& z2)gA7ouv_>CwXK3=2Z^4L2>|9NQl>_=HG?+P zre2U#emhUyum1K^;(Ay^naq7mw)yT&tcKx`#^%}*Yto3w%5Ldu?pbu{4%x&8Q)ngB z)=`hr&bFGOnDyH}vVZ zQ)j8_Qatp;-pqLw8oK>m(i2!VPJ!*{fn7PAY}Eebj)Hsc4H+J$@O(jt^?~%@`Mr4n z{@E-+2w9Gi2O*qsVIz30zm(S=N}5=x2~&=|TXj+gH`KL(wDbDe;isf;*-jo*di*3adu?2bBxo>Rl z80rq2D^>#puzrH345)1GtUd0{&c*|zgdy(pG<@IRDMiG`u-A2nyG=t6Ra+LWcm6?@*Z9*AV*wtOTW(4d)M>{@7$l01OCWB!@+C3K$$<&Yd8c}<1tFSo7dQB)X3&FeovX#={2%>jW8w7LJiSo#$+=Wl|huBi}Z`T|B+lZteI$0e%kQn0Q?3oYoNj#&q zx(HhP0|!m5ybWUcdJg{AD-a59b|UeTvE+N+`|Qf-a7kb?bEO6gnC)P|J7 zwbA|+P3_fIWtTVZvU(NSTC4VTUe>pw+C&LRW=XvgNfqy3M&K*8O9cljuNZ3%GX}Vt=raZlxV=WWol3m`{aSW0pfj z*4S`b6!K?Ej`f4l>A*HO7Lz9VfGV3CeM(W5T>EjVoKtS|n%m{VeX>||)R$8P#gI;R z7;Fmo`BKl2LngUUvM#sCp?)#!C!L%UPEC_TtI;|jgi|l2NDmiYfV{CL)y0w98zl*O z#2_!G?jkgY8<~gmA|VNun3PWw;(ST831UAOXoxGZp@%z#a|FS}v;n5e(#_aK9Pd~m z%Hm5AS$ud`xdroJmwOg>VQJ<%_nWT(KV<7$6i80CYyT7ZVXk zBb9X1NW|3$c3A3k%ry4&<0)Q61slM^drJ;RK=2dZLYHfKCi8ZB!?HvD2#x|-DbC-S zO3jkDA>_#?p3Wd~#$s5qmWHm#gqHBWtWyYULDCd%Ck6se7c7a~ScE3%1;qjZpUI9ak?F1)*R?u2|KHO62uT_|t)LyuKTzKMu!7024Th&sr-^ zTo9N*=!MKoU6LJ-p1V#D&qaN9>3xn3tT=I<|3aB~J~TPMVR9}cULZ;Y;2F}oquzu? zCZkTJ3ln-QTaLr?RS(}&M=RVzsj>+JgOTzn#Wpz@#bD0JSFavSIC3KD<~Ur1H^WuP z+=K}xVe9F5imaqm@yYONt{DmN>#u<=Nar%gJmoHh*Bn)HXA;q6gg)i%1ZeSQZDVKvSh?3 zfq$)BH4&C?Q9SoG7+YC5Vw7pBoUhYMyb&qpw16#*Y;@kuPF=f%vokpVtH&3tzY>*x`O9CPiAp2J|8)J?-&v{IqwIRxgr39(BmBvm)1Z$q2HRaA zK}^liGU!Dmmg0{CBMnm@VfriT$n%;y?_0W=I=2yNzQQ^*elsw>QEMD3xe)@8t29jw zr&jpFsS-yTD9?&D;@>Of=Ycc*orx~EIfV0o{;2PVa7Jt|+*;TcIOe7B!&LaT_Dav1 zJe9}&Gk`9jPs7OAKYkq}Q?Pzwsd(X@GCKr1gHR3Kh-c#g|gF=7MT&sd#JT zEAit9Tw~-es}-{w8ML)KCFMxPC1b=YmHN*IlDCsOzF-}Nkr5py=R!ANR+ya6z+=k> zJ1xfpw@S*c{Oe?|=!y80i1=)uiW9kOJl>bMb9tChaiU=TMa6~nE5;@|-qe+JaqwIp z36nLtEQ#(~M1=y#+E>W8M-)J^K?IH$`b_#Bz0cR6O2HHT);hsNJ$E$T3@W{{+^mn% z+T8?bk>5JrBUYV569Mlw^~m9-{wR@v8C}!-ixl=_;NTh8M74ALY2!x+`bbY9;G2mQ z!%f`u9LPkR^JEe=8D4Is6-7LiP5vv0+aA-({Wei7^7u&V%c03JcnNr6=DSV|#odAJ|StaG5T8@Zd zXygQiv4;)X)eb!_KZBso#(i%D!goRCAs7~G@2@TmJR~F|7iD=S0ZL?&*%$qi7N!{JG;N@xZ}9|ThbI;#Xy4{0xV>ERf2=5?8^ATP9ksjaul5{s}ys`^e(9Nh3)q}3kDFMtxVbNh3TPex^o+Ti_Lqa_epC6Ih0|NKrFeHFc`2q~HcWN;lvHQwW^_t`Lnj^1iQ00DHy(+7?hBzhfd5u5XqAc7drlTkarB)Pj?JqQEi-2($P=YOWL8Q@Glovz<}7noOt0K zoAKgP>bNb$!+DOXR?_ATMOFcsa`V=zz; zPC_7{Ow5%-6G6Ty_?<$(F0Bwb5fySQOw3{_N&bZ3sx07&BaMdx{n-IJwfPp_C3MRn zhcC|}kHrP5^k53yxe@;egY4ADbk?tsc{?U9!<)e4=rz{@>lKfm@SzuIhr-IvfTK6w z$d2v=uxWM0Ld82@injouYP$q67$7tL$T`6r+q^#Nsw0pKH*|U}IhS#60ml)*Wkc}l zcXG!|&Y@Cl4B_lTu+zm(F39mZc6fnKF0jL*VawhKs_VdsehL4Iaweu=a?5fwixOxm zQ|BW#YN3Yal7a-92zdMzUO5T0vzg4Vq7NyY}a;-@zb^i zPT*j2z={rYuNkP8D~GRz8@ity{GgLgE%%j`DOTCsEEo1!K1wc_2N@@KtSnBRX;rKK z5s$-OChu`VTIf6_)d^3`qOd?zWqBba89b!(e}aFyb+nMSw~}3r-JH~{V+n?LF*kTk zTQxQ`a!IN3GbGvEVtvSI4Uv>R;k2>eXPjB|t5^a>79KWP>1;t&7^x8BV5eEEdbrElYz8T$5^Nu@t0fP?tLh)_ zW3lfzdyugLNlEeSIGRxiBkdA*K^zN|V7yo0^M*-Q*q*322}kV^k;G`gwx5X>t6#yw z>&m=%=%s~>R?|SEdzRvHXD4>U+~LV4HNQ8OMH?3*4F{sU!3ZGyf(pw@rs}(x2<`8U zMvwyE15y7*jD@Uh;zOCYSG^y4pB6gA6|mqCta3BfZ4_AB&09|Ak*3d-aS~}e48occ zOu*`=*wiB~QY=JK;?$!=!j*CR;*d!gfrIQ{~4O9}${%4%k1n1yS zVOixJC$1QPQrh|%1MCJzk8)$n2$f`-C$^y^6;n_QY~prT`%`Jm+<0*XRg+jb6a-}t zbtr-d|J-0FlD86>p~xGG=0l^R%l3>24cIO@r_LLm@({M=_!)%aoLxR;5AB>J&WDJ> zS5XIRE|}jEZ7|ll^T!6>jgoIo!_Ecli2gW~D~b887v?){u3I~fV>J$*R6b6K#ir4vIN$j<<9EYu;(~dzudC}y*#}?x4$3C> zqmt=+3nq&0IKCU5Nz(_r>VWNvk;#YcNum+{!4|m>9s%f-{gj-qJ_LhXeu<_Wez-R7 zzZ7y`_P>P8eyZe9qX!`M(A<|Jj(1;DgfUYjIPfTGhm|aJSP2f-dZI3(+NJ~0Pm=*{ z{VqVGk~GqpzOdI;Lkg=@s**ATwqjU$zLEQ4X})mA@1G*vo^e)yjKyH}ONox2{IULV zva*K{vz&vIfwd2L%}vEQwF!!mjYHhXj&%|N6%&t z2Iaa^(;(z@tgg13q81d^=R$5TEOo+~)c4CJrM-qYoz3>jr!b&AP+=3us(=P*Qr^Kq z7|7-!^%<;&65E(CvE|{Qzw)}=n~dQ`b<6EAGHLq5$&JKrCo8kmnIF11v;tsgpWB!j zIaai_+YKL5rr(7RMZl}h@4+YaU&g1aJ*C+vFtfXwdzJ5k9^Izx%XM=PN{e*>tL>`z z%HBQNQ=I_|*-kF9r5e!Via`JFwp33RO?P{b<97;rAH8!bdc!pI$clsARQu&wr5$gU z20w9>rgxTxR`qN^G5ebMMK>~!Hf*4|3I9QN)Y3$TKz;ST+i3VRePM6|pS(gZ(S)XM z$|e05aQT!;nq59A$u6HZAL}u0muIR6Mhn8Y2qrp!=Db84qZy8jw}>D{ipD>5Uj)wE zT%t`~Zi<1~1zT@WX@e;jV93CLuJe}yW)OEqfWKGut~ZUQS)ij8t9@elOMKyhPPYG3 zb0a4EVz~OH4i(<9+LI~Du3B}Nxh0%>uQ}q*iNH)p1F5-E^~5-u*^f=8xV)gHDzb6_ z>1)^r1NnjL;HRclN9leX9fhNdG!(kXBZ8UvdU48pzFvfl?{c)u3d9PV&xjl;PpcQ! zBC>|98aNtb}~DPrHYlE6d6UwRZ38?#9CfZpK<{l1PA4~i=t)P zqw7WxW&6>>!dE!OTAsDdBt3rt$X$cQnShP5I?Rl6d?G7NHKRV%poe;CyeWDaUXqc= zGs8#W=qyY^$H&oeB%_m0=%B2Q&E_A4jdCzLkC0I~F4H+KF526s%N}6UxJV~#_$&ej zN^)*IjBG0F|FmNugghatsE(*A6 zk68#GlQz|a?XST5!P|XzIJFD6o?Hk|ihnAi9awW0SFBEI4pBmdvbB7KySgBDCYsxa z=Jt0*rB`(y-Wuu?JgrHbFx!fbR>fEKudkw~(Mb6nemmZbH^5>4TN+?qMh=ImfKO@h zlq%A1~+aIL!BB!RrW^&;+5cyBZ!+yijrMMk11`kGF7F z08tf2C&%lZ9L+i@8kL8-tMg+6(Z7214?#y)pxaki%h?qT02wbz5EAw{@U~arP&_B( zFC3)i_DK|4kF6wj0YfKU#RSU2s6|AsGLcdCqKZ2V${K?;+=G@$)I@j3DZ_|<&@fIk zTF==BYm_c>V5~C)Ez7~BPDnIo4sEp3>P^rIsHt36A?F~P)7biOslpr@cDT>N#OS2` zmS0aPnj~!4O%g(fySnW~l4$e&kgMhJwtOy4+q+5=-KMn+ty)I@RF@_iR|z3l*dzJ3 zOB2ZlH+V>{E*ruE79m*AbHOVAp@KDz3DzNu2%^@$SFqUf4)=3Bt@Fc1-251fn*u%< z@OM9F25(@oQ30-@%^JF$BZ)c0$`GY%3~~o!kQrSYUF50Gjp2$G`&dp7Hb*h6eck3LWYZR+dEiLVOpa_<_4%9# zn3zV%$^aj{*S^Au*78iK-UiYAibOgb9&MVrQpv24YOqU91Lc1N9D<qE{8!Z(Om)vmD|q(77GkzB4p zTe@n{hnxcD7ts2>3v~_J$Tg^h(_zYA9aKE*sUSV);wA;wdb(9oqFW^_Mt_-Rm8{gP zk~J_o$POW+gv^;@7$BZ)czysj$u{DNkY_9P(q(8-mwEz`ESur$1dqDZ&sg9r1rjq` z#iQ9Ovq%APTcsdjtAItcVRw?P0tF-`j1`#tr!!X8V9Yh0n)(;66N8nHb&W8&qzut2 zj>G<+_gmA3zU_LyFJUbc?U&iL>FkF!4Qu*eBKhGb^`>7QgigbBLVLIs((ltvr~HI{ zh#}T2j*<1I3uxAq5(Z*o{JR;5^O=FT1_E^-nIPOiq$e^EpTe(ZAg*J5XJud@QUPut zVvCW#!anqglVSXVKDvEK#lGJ@bal56;RC`gUxyLhWsEbfx1oMd(EB~cmA|?Z5$n3W zd3L>ULCj8F{%)I*_!&|Q!1hXmpKpLzahZBx02|k{*;&o6Q4afAV&!L4;Ro~Cj~;AI zrwRr>2f0soET&tgx)_JL36y*~&pU{LT?X+amQqfab9jPd!pw42(&_V=oR z^;OAwNA95LEA=8Ko2TlX(`cRXSe0s8=d|n5F8U@0A0;#v?ZaQY^~b0i zZTj@$<^(>dU(ui{)3W}{lzp4{oNN6q8vQhsrS<;5tj~inc#esWbbZ}VbCe|!Uzy|} z@0f)SF~K1}Dcg8+;w{&BI|FZsn#HcvhafFVizPXi~=>Gjvm~mkC=r01U_~v zGVr9#1*SMb@SmC>(m6f@41AtKkoJFTg7_(GdoD%?6!-@)V^ic;gyO$%iqur(@W=EK zc@oDFGz#>zKKxJ2lov1*=7KRnh+{e-qW@k(XsnR^2b~aqU<`R$TVaxJD_q=@tuTVw z3h;Nq4rl(Zgpm$cL|1>;8dFz8VKkQnNa7LyL^4$-kcU1jA=1eH%SUze1_5ELb2U>Y zLbBbNQ-_k+Kc}b-wvsu9;iU!id`vcE*DI%Z&L+7G@wWxn$+&zbxXuT`KS|W)YXLTS za1<;+VT6Ig7J|;&*@@NMW=Jo9b;UUa6^8yo$9Vx&_&%I3MIpij``y6AKa01V3P9Uw zZOqy`%V>ESY)<8BnJ1ScU`Dwb_dL={qLiX%dgy!Kdee1mb2IRQ(* zU+gsSf>g2xk{gcNu-!pm5v-u2vkG0`!j zLi2_vW;-?qQdXkik0_&WFTta)KFOgUy->& zulrx{^)}N$QlGi6_utaj$2RMIy^giEpDxta<)^!U-0#+Blb`nF+ICQP2W^AD?gzMA z_p?POF6}07?|T^OAL~JVWg<~0*@IdrbB1S(yJ7cs3|_L^?+5bx*Mt9C{7z>!b$%Cd z-_?JL-*bMY^T0J&1%ltN{LO!b-;coI_V4ER=fZklZ(*(dcliA%tyeGuK~G_Nhr&8# zVn21O@b$~O^ZQh$@-;<$D!+G$KXK5RC)yO4Nf!xffe0QZ#n==;9D;_yIJgdtoe`X8 z?~QnJYziQ>fp$fABnKcMG!NSYv>%HdQMNsRb6yS>(_AERixCv2PU0$wC?sYXtu_D~KounuQ!%fCKriFZNLaJlEnd#0eSS zb21zIBz@@2dNtw;Xs?z&1J;j%8~#R2OYsh{zWySSh$mmWiG4dOXzD{)4aNtOuURP| z$Or-iy9xnLV&&gyO%odLWOz>4xFH_r@fA4T*axSE0KyyJxG>0LU6g^}^U{GZtmDZw$P?^030;`i547l)BU)M3#(h|`~XLvF5)~cz1n*Tro zYI4BRNGbRM?$JfFNd;mV97Q*eQo+Rt;+=G!OIF5nMrVXcj5Z?-s2bdDt;2YShkkM& zoz^_)Rt|4CVLKO}&6ry=tSWYe)bT;>rPHR^%-H9-e4q;&FBzpa}1MZ4rZeK;D{b20eFx4u9Lw9xls3 zbeUAEW=KmSgo-<-QNyLpzy(ogTH1Rab3{uX-ycOi<|e?d_{9TVG2HNtYoaK0M{>hA zTQ{17vUHHsKs@ZyF-77q#JHIdpibYwK&UDenwUbR(esIBx?Mp_O!bB*T4vYPgbF{N z!H9Myz6&(0&q=qrHF&a5*_ogpuSN7Ss-?twRqR3>U4KRa)WbcBQf(AHujM^&)q9!L zbDbI=psPID9Zp5pamtpVW%8Jh7%iLzTdl(rEjTi(ccB+04o1Jyq;Qd*Xhpwzce|f! z#?|b~=~{Wi2|kABLi%x+-oyW6##JPpjD2RGGGvm{!r7;ju}^O_c3yQad>BRwb}lUI z%Q4Q@Q~H!qf%Dlb=N-xlPcmV@nGp-xUFvA-POp~lpx66J^cn(swRp@2y&F0it0b^N z3geV62qBR%N*^MThP8n(rR7vlGHF!HuAam=$h^+;@FtzZHA`M7M9}dOOgVmjsf**{m8R9?_eHKqmrP3ExPlD`BMrn)@PiIgt>JT4&)Zs~))ElEt zcQ=o&#F7vfpvWCZeRN}cB-q)fyth;l$#qxvimf6kDDM~UOX0H|M=)4yPLdcwv`BKw zhlD5QR`-HVh7yGGBW%E6(B<_hv23oYk;B0tU*}UWC@-0UlHg1x_e3Cr-@F|k(_u

z;f*=va9emfp6T-eOKT9>j=L)0T80~sR9P<0(p&xkK8KBk*X*6h$IVD6F`p{jB=&>X z1tG)^@UalLk6Q_#p(6UaS3IyZZl8$vU}s9eEQkr2&)3nhq%3dMWQYL7Th+?ZOUb2Q(k*9Ai|^f02;t) zpbGrVs~Z=Iy~Akcn$BXwa2Nz3jmO9gM$mg7oD&Y>(~)!W{Ykh_kJ8xU6Gd_VC*;GM zMQ1un@^B3p$g-3$ia-T`0uH>*ircRTGQhh8i_}?iVMEy*8dI{37vqZvC<3Ct!EXGE z+s{DJV5f<`HV2SKn~2MLgvBW#F$opM8ec6>u!gHThfXvnRn_kEM{f`^7O*lSkUoJ~ z)fBL5kg%-Ldh-Px3yl&7x(L0Jyd!ulp?62z9z;8Ynzdk!vm}s0_b{;qk&9tzNC2Pm zFvmG?%?QG3pK!M0HUUJb+t@~rjR<8C$N9oj#0>Kl4?>hC&ulQ(H5d> zLF(S6_<)&DX%67zQJ4zhOw%v#G2_Bd_@%JND!|vGW`n_e%6?>Rq%0ayVvl92Xk=8e zI?Qq@ZdW@nbKvM6b#fVMZAE6O8#vF}DIkZyqM~{L&oo=RXpraAfaU{J%3?>jFx=p} z>|Ursi;RX^K(8PuAl4ZHHsnL4@U#^5@O5-n1W|VOd1y%e6;O^Av382v(yR3Y-1C;`UdNjHshCcL1)S-e|OF zp%$~zq8z5ij_T1!^U7Gy+9KDnGqLTVo zwxU|Y8!FMLXlH&Sq9bUh(U2rJ`!Yd9r}i`Av9{6Aa%-At7mT`}3&43Dc?(W=2$@yW7ej31h<)Aisc zV0<08k3i3l+Yh1~HY%7HL9GRG@{!KOlAIy()i@7pM85JVf!&Yj!%+^$bXfNEjhO?V z;>(cAQMb~(W2TT+;Z;-|@Q%xL9B;i_oQ(5AO)J-s&IR}15of*jh5gQzYAr^DUNJGg z>Z6M+peNX^sL+i~CiU*h6i6k`ItH8KSdq)MI5IRuegGP9g*`B&G&E!#`JD+Y7WnkR zMM`@94xrF2T|#6k6qC6$gagRDs4=~f{{nLxDFek)VbOF;rE?ied5ea}0Y;%w!(ozI zO)u|={u3+@yyEuHZpRFGkW|g+`~m*+sNgcHC4e=k1QRTcl7)l=MFxr}M1Gq|F-^crwi*#0s~L>q}tU@Z(Yc~gmT zk+qvDhbx^CYr$qk+`m6aZd`hfzk9-cm@ zbaQDi|H1%<5*Iy0V4FrwfzPQu7gOlI@Fvy}Cwkbjo;8kh-)K~wNHbt~3wx1KumTvV zVSIHfrfI#~XR*$lPnF=Mf;`|Cm5J0Cn;R}ndK_i~d#k|EE zqpzX)dy!L}uJ0A&_IqwM3mvV4BONWlw$zw~9igR3OTXnp^CRZMH*GCeaA_(ob3}j8 zJqLPmIPYLylRD;3Pzdl0I7BWs5>P{QXAJze{GOk_&Qh_w)mkcv>mqRGt#U#RYl1Je z^_OxQJKoFLb0BKoQyU!`uf@t3OFg$a;>&50cG9z_aMzjzx0cygvgRW;)BDzncFU(&8vT5(RgC#T4YbImK$0_kXceJ?%* zNa5!M*Uy#867aJd2!+$AHBZiR=v)su3Ec%24q@q8YgKzY za5n?ur!BkJ^HL+IR3S>`B}#$Ic5X$Wle!wDh@DLb2xTgU3ok576UEfjLW(E}vJadS z^yo||U#NI`#(L(DOR=6|>32;`PG4oDPsITWDi2y_k=z+~XG8WZH^o!+6;CcXj!%j^ z+i?@$Q8H$K61NWq6scg6>>q}U#~?@uC~Q`*g&y~Yr&;3=z>_zlKxmRI#V7fqy&eEnZ${~j6OdC!e zXUDe2?Njt>XK|cxf(<>YMp51)+^#W%`XVWld^F@2N*%rEcyk~(-iUts&^`Y^FTR!- zNRxsXU4L2)DWli`c7!3;H*QyN;uL|^%UBx9)p9S6E+F3Ql7dEFenF8Jcz#54F}1Ay={PY-4AgI*a| z#QB)c<1$5-&543*e103&pD121Sq@m(H$S?G%UbTk| zA*c8ZDkzG*saYu6tX?>VisJlelKjc>HqwN&C}Q=DxxgU@hvRtB&JqUyM0moU0E>eH>`M&8xKx9x30<3ni*f>LgGe_7ToJrnjt6h_2_dbLrV94v z9gMz*Y`)h6qz}jKqn5z3FT(9+GARsb;RMX!5}zqBglL)QDH96MXx7vCAsY82RfD>x z>XynCOt(yjZi#-Anygz~kja;zc?{iB40Z5PFri!QkmguEN-fq(;oF!^ethlE@-vi* zwRX`XZzomU1ohBGkK`0FzDQV$d-v`_iVLQ%eg`K1%HkxTsCh+TCLS%O#s3~z`4o54 z=Rrz+N*f71NcXCGKgx}@}X_4HjQx+GchOF6AL6`I)`fY8dOpES z5@z^un$^ES_JB-Rsf;#|L~8m$yR%Dc8=IQVYU_ELuyyN8^fE9M>S1n;{)8;zF1BU~ zUDoEp4xim`71S2cjoTtO;p5MuEjRCY!P5@dE&f~NFuunT{C42l1u}gXw^+1IZfDHK z%HtXUOrl@WUADREUr-l=em>$7Y|v8vUJd**3|uAGz?MIVO}L4svf#NwTp{q}xDPgD z2cM@GA#lH}JV-7Y7?onxDp)ntrTH{bET*gBTo|U3dC?1jEc(UnYr@5}hQsnt9hzvu z3ldxufwV=)8gIbW(~Rzi>pj~+^AJ`!IfFIGT61C4eY;=2l*ME4Q8R8(FioGIU8~HP}gyP3&3|* za0M8?f-Y^Nt921hI~C|nw5nNzL)bE$LC3_?fNzQbGI$(q;A%Tv&nNkw=|aof8A#mD zG~JriC)uht;^QR5)Lipxwz~uK0tIV^GYU>uZ!^^kTf8hE1k@oU<_@}tFKp>tfBL@o zIE)z#20~~pH{;3Z58XMbG$i+m&gWwdW_-mVaaFNm+5EN68l7-(a&K(|Zh8e@fD3#f zkLs@Ww0iPQ_%s{T7vfMo`KhKr2drwZf^Y>~iEF`r)aFnC(z&)#%=Xaz&7KJO2Emy# zFbvJ=Qpnh5FaVg34ZA+%7}!?z6BtJ5!Zy}lMi8aAlZN}a0~zo!R}+f{@!2W1U!afA z6&Sq33?A;f#J$2g7d=4W8j7+QV+R-5P<1tD)xpNjfd(UNIkFB!2AGiP_%0K^U`n>W zdK`+h$8iZ;7O?W*E0bn1TzO%NaJ&3jvl9O_9 zzvykKWod;c@TDW=WNa+m8R}IUM}{&O>D|nw*VNv(;mZ}D4KFyM$WOt(zm1NbMsF8d+rK^rQj8D zQtyofm$t)~yI1{`{re&N_Z{|cEB#Az0N;a|PxsL9iyjrrBS#GjZI%}^EJ-_<89ILQWz)N}hJcW?{#3U7b~B!#4AuL|SPk3N7qu*PZVrt^SK9*S zYbV$rH@9CP<0@WT*(g~VOBWGfHS~sB>EtZPCq#T8oe17Q28zLaU@h<&?>xKLsvZmf zWM#u_$#BLW?|`G5&V~~&Xlaj9T7{mb?8asFm>U-Z;4(1oN(U2E2Lh{YUM0$Iucy5` zZ(&5dvkpY@=0tc1W+SdQ#iz8eFW({;z9MdXk}|{NaSrQq|9V_+`K@@@?~x=Itja3y zBP(yJDoh-F2M5;wV0ri|2nh8Y`l1gTW-r?=#%Ih2+29^%&m_V2Fz*r571on7a$Gp z>jRAq0@5z3PQVqMfcG#0uFfil_4_8(eFW@f(2(rTE-jG^frghD$qCAdgdM0x3^sDa zcMwje86zM!DX}R|ZEph$A_%it{VhEh^n!1ewluPm`G$Y>z~Gxh)HCo+A@K~9+Md7# z2vN+J3nZI(=gZW`5W;pDKM*17v2}@=5iNP3b7C4Z{hHAN%#=y|M+|j8d91&Wp``C% zsJOo`XQl>K9R3|NA&-HK)R%47FYwR+fA_)+Oel=*CFsS z3493zK9_xaIoYm;>VdIWc(QCIrX4wF7gXDdGvyF2_3An{^{F-?Hi^_Sc&Ll6^5%5P z;l2Pv)fo@L85&WW&;EXzu?2>DJH(jjW5sfOu~pb+3XBOhBDTfyHr{CJ30dC36IKxA zQ!)F(E~bs+kYxuF)ogZ7+VW>A07D?*Ps`0aA}NiS$Q`hv)LKloa>Hb90%K7ZnrEvX zmY;{}N+HSfKuK?eExpklEO}N@7cEZOycIpn$*0@o8G}$FZueeI zwnrl-)N_ddY+sC$@+|MFG{M>(EEzlcmX~g{qJo3xA`+N#`xv>~SZ)f>eVFI|gKZj9 z-FsQ?hcg)DDxUj=k-LWF*7MvOd2YLrTgq~`@mw#@{nW@UVYzqn+#H_!wvl@Y%U#NI zGkESBMy`|PPUE>>e!_axV&rDB++3b}famTpayv_j8vS|h(>!;#k^4T&{rgp{zxVOn zdLwr)%YB3AuHd;5BllUBTf=j&;ki4F+^sD4S3I|n=RRZP-pz8?@Z6z1_b*1SpXJWw zx$%!#f43XCb6Kv7=f1~t|JTTsS?&m)TgP(~?T%x)sXX^lo||ZQ2+RF=I-|y2Ja?M` zxfjcAyVgDls{b6?@P8;#r-SZ*(#`xwuyFmnIEa@8vsH9|bM%*fr$a`*7u zn|ba+BlixL`vlJ|rreO_R-|!%DO>B24%K{;L(`E(ORc=2!>KHk<^*=R%_%z`nr)@m zD_Wq}i++%b(~wlt%xh{6S~6_8ZcT1)q*3oFqh2#j9=d|gP1HO6I$*+<8>~2xb5zco za1+4LX-!@#kWO3Cc2)PaDY|xYJ~7-$bZsST{wNk+sP;2dWA-Jerj4j!0b*V^ z+N-1Lc|*rjdjqKA)vO}-abiO+As)&8e`VhX9aVr zzLu$o&#L)!d&VU4M!QkknC~EJA*nF1VlFzBUpS)j=J}! ztBjv&OHvc7&;*wukzva83S*g(fX|mAOoqAx*T=sH8m}C3z_C4U5m5_YLkSO^20$-cX)TL>kH#7ZpKpXGC6m1VD zP)Y_&4|_j*@ogq7Uv5E=2<>HI1_WFvRH!#{XJDUz4OSic)xS27@{ zrEAkbMwYh_0%DLw|27v^z5E=n`X;YGR=`? zDO+(z9X`A?FfY^639w-^Ag7vik*s@}qz(|~b#b+&b zs9e>=VykdezS{CM-ql}WBVLUTFsLw4-F^#u=?$8VM)laIck3ER>ocwj{|pVel*0%h z7Vr84Mo%5gam+zzF?JU-vI+E5;HVAA9Z0)?(k^Cc2z!m)h`6KM-%J&}(6q{~9WFo# zwq2IfxZUJE)ac9Gh0WnM+90Nn597C;J`36|_OrY?3)AE&dHph8LAqY1mOb~&Xu>a% zmmGGk>*{tOwm?uTIU5omW+9rprubTX8>PTvDT>DftK+r;J;JmMs5-wI#2?;bfc(CIBa;LlQ~^u-C%vOe%t znrTb1%!=@u?AW^)e4JJyb{iPH!zrw#r#@irUic+HBa2YgZQi>Nt92Njyn~=(dgYNo zntYeNekS7pC?-Cx6%%4e5$-22ra>5eEAsZ78Wbq zif?ie`%!u#d{x~C*X?Gu4&srcT;_`D%Y`^Ns~k`vA)3+3YE!>G6yEt3&eV6}Q_RwF zAr&7e_79MtjEWbOq;|CW%m?HJjlPiwybJ24CuJ`5*fqNb2pwa4Qc(&!Q1(HNec_^1bt$^2c<@!z;s)X(kI1vM|_AV%kt&4 z6pIrNE%GTtM!{qiiVF`ND<4!UZFpa3FAhH_wBzHkh&Z6+z?VJ+wb3$oY`5(g`9e?q zOKPt$!C) z#pR|`G-_qh>U_WV+?h!N*zftC4<9pkIrlx=d*1Wzr*{uDijOmUUefcXjveUtVGyTB z`yA~#m!%3BI+*1d*)qVLxtDtL9M56{qRE3_TrG;%EkSk}?S18IeGyQsGtbxMQFprN zZ}Ki#RIl&BHG-l3;5<4Z`v~X!A7mlyi{2tesT*X6xz>u0E1^aJZL)*f(`TfI`!o|% zR6u7nc~NULfw5lNY*l`0z0_k>zGfvR@BJDJDskfb@{=4nL;818MrSP!YW=*S<7JKm zU2EJw&>nWKox>G(=Bj@YS)=Mza zD;M%4OS=i8spgcPXqo&u7FYA=QI1e21?(m%e-N)7D;}(oJ!Pjncu_wn8eSt)J93+W ziFhmR$6J@iXU-@PhYK<$dy-L#XVfi@k)%z%-wehL-IG;exGc-}33KNBcej z3KGSuIHwiV(f9}?2*+5VVHfcs`|LCFy%r(Uo)X$VP&(F@w|>iQb}!GKK^AQLPc zP`Qiv0(WJ5sv+H-@Vv?sd-8tT(sqE6s)|oOOPZM-rgF9ydn&z3oLDR+Z{;%Bw%I<5 z)>m!|wr#S*TnP`dHfQzASwge)$uIsb8TXcqcS}%Micgs~b`D>4Q3J5=lzDrU{t*X; zhxmds>?gQUJ7Co-z=GB!cMO5rPXF!SfX>-h(U#ofx7TDJ%ef)t%*j_YcOsv3^DcLD z4d3T(rURK}=ZU9Xu`_b8f&;7IfQ3xWr{o%y8mdE8E1Zrm1WAM9w<5IaNPHBs_RTlB zM26QoLHrUO*K7Ew9(%6tTvtX7b|}eZgUZ>MvNTEHz>|F_u^S%yo2*N80Ae*KtOx&; z{zcxqAY-#nkNY$0Qud5U!qRghK1Vr!qpsdbOyV@#ZPguI$*i*c3J2Zfh~>o zM?CEzO~-{fGM)9;D5U9kvSXqpeKjRQmOr0i$hq(6alqsa4r*_0=L{*%z@RR+CZZzH z+2Ab?;IeSwjpzyCa$Hsxi$DJ+JCX&=`NXN$Q-m49^f>2kHdhQdbF+8Be>5N8TVA61 z%R^+Fx5xl~xTJ?aViiGU2nM}}$~F*4M<#t!ztJ-33;NQLN!i;hM>e^TgUng!zMo}< zm7fDr_=U`SiKXHFUw)o_e}{fgZyh&)4Abe4UuVOQ5KiuiY;p=S9mVHx3=b*=LjI%g+g(hY$Xjh3a20)`I_*{~%$$ z->hvO!RPsWc_&|n!1Cjly_M(d^Wm}OYm`Jd0O#%3dHa-XFNsTc(~!3V2NsuBuGdYI z7S$i1QHiT;-!prLYluVm=1jgZe9yc!rumjGPYEp*a@gfLmMh4)el@oF5-CyA906GV z7OUf*erp64-Am!BLWQM6Ck>6A10C!^1=8*9@@(fqjL7>!wm;$7%3buBc(=dH^LH+V zwN7~c#+59oD%m+R9u4A4OR$^gZPdkhh_8*0`+hx}i(?1A)Ji^B8f2@GQ__H;c0Mh+ zMC^YQ=$JaU>e)CxKJHri0B_K~%5BC|TiD<2VQxW437GzQ6*#MYE}Iy!-#cXUS3NKx z=^`a>Sb~!*?8-RrB$Fsnd8-Ra1~P&oh$Y~Z(Ihrf5I-XElz0Pk;jdhuF1lEQUfwM) z5wIJq#B!+y9zLlb8c&ID4FWVS2g_GUv*){n2XwxG7B}J^*MLPu;zGYBb5499FrnBI zQ3*V@TL~WbeUH)LNENB{lk;gcwFQY^;u5la)V^I~+fVT{MqoAYSlz-f;ixR5B}1F# z)rjHa>nG)Fv4{Ph3;3<`C& zp2%E1#cbwTH?{+CK>E^1VbT_>;v_f_3e*T5(?ItPD!!la`0~uxdiH=aF)pNQY zbYG2fzSH?E-4#y{ zS^S8uQM;t-JGXi82Hs_P-F( zV5Qt)SUFp+w^ZuuWWC?HFP3*9Kc5xqaLr1tD?^1=Wry`ND8WDApT>Jn06nfn@ z>5%1uAZac8COrq|NEs#o%*pZ|D305vQAhIJI6o<@908GAkhVA5--nk(6BJ+TwE}N6 zy>kdN0#Y9iYq|rg?l4&4|2J5W3jJ>|%=uQAA&Z6v-c~GO%j(lEqfl@ftn4!UT;z2b zrSh}ilh0uBZ)vnpjVxk3>^Twx1Bvq3Q}5*};fklz(OLF)FGHDgz_s`!WF|KC%QQp2 zmJZ)7^PU(gpPZdC?RrF8NMnT`pk(=Yf^v}RGOFG}MiG%(gt__PG_C(STbLTS9b}#> z@o#L#m3t4Io{&m=w`3Pbj<`<-S{_`)cE=2|R_qs?ci+#vn{A&a@3M4L6_ulDwtFArye0GW!i60q=F3K?60~Z zt@in+>?9-;r*i?w$vj`OV@ zv1#~QZ?#)HqBW|ikn4D-H~#LY+@Hk_Q%aFj^#EWj%MK8I=&?VQoIo-_2-dFnCin0d zOre&Q26bO(RC?TZzbbeoZ4um&F;KPc~r@1S)*#}`Al}oZ2>`7YrOv(u7 zkkkq=Pdk|m`o1r1xZ|4%%A;U)vo3#o1uc~yYU^`iQmpDB zzSeG#ONWhGD{rX4_d6EBMYBw7DnS1;i--t-FRbe3;%H;?mVC8tWvyRQU|nJ_7t$rq zEwv6bsV#M_lNT2xFXQVT`=s~K&75h8n_`cBoow=ntB?hI)T~1L_c-t>4M&nhXou@s zN`zYXp(vhKn3%!PN+6jWUp~T0`0%MLQ`7te7C0D>A$hjMR-%h))FCGf8R9dvujpjd zYR)40M;Yx)7QMRKA%T8-(r5WX=CrPsm{GUNXGTP#KpA1*DD)vN+g6TNLDTSGh0_?R z^TtR&Mzio_dc>VW!?B-l0GDNf2D-<#kCW5^3D2(QsnUT3E+i*_3w`O~YaRO8mkzu8 zZD?D`A0?}Ms+VY`ai>i6D$mdPn(bhynW*um-c1bYs*wy+d0jPL`ANM@0+Q&p@~1}r z5Mx13083rLkoDM?q6U>E9LJqUc`g|Usqj!pDA+6GHE!%wfEF<(3#8BiGUnse@geEh zNL7QDdF=XlhYN3GZSA4^mD{isn8}OXW^yWLuUXRkQ;b9d?f=K$o%V;gOTBF*$VAZK z4iTJ`+HRDuk4}|8UzI<@jKKQnd8w0~&wfAfvk?P6JEwiE5f9Q%uhA9M4K>=fe!RME z&DxJw8}Yn!%t!t1{X55Qi?4ABETgT%;W-?-&DoObQxVFUOso%jlX8Nt6x$Ue&9Q20 z#K3lWMU)&!lt_-0*e++|6$+iokli}LVzM0wPV6PLG$C%_+)9QxqU+hO5?7Wv7@FlV z*^Kn+932}bE13MnbVqO}#}kRlBIDD~(nOnjT04Fel7R6;BYF8zfi=;u($92<85kLV z&YjnE-11#ZhNgQe*QY#AP)*XW)7uzo=xJSn&KOi7r@_feoZ7w0!RrQwL3T*Jj|_oD z%J8~=L7z^MJ0*TwZl0Fj66dOMUF$^#ecQ>YpBf7NWALU`#3qQCz^D3f7eAv?X5Wl{ zk8!N{A_w&vpk`dibFeczJ0J5BIw3w{u!|0n`a8=w52okdf66Nn5*dA;=&HKy8fk;t zA-P1P8}e|oolhsA(dT4tKN8(F;Tw6;5bhuHtAphie)!G z*=&TOWs5~9HpuhcVIy9QsMo`ZE4|^wjfG)CroxFue&cKUz;k_I>uqI`mmpaDP}`C~pPqSsAc5RzypA48&k0ac!wZ8fGTG%Dw}TQXGiy>u;)W zDUOacR2QQ&rPOeB>3%00P8W9bYj06MB~MJ{rbG{Vkt++VNB|RTpE*xLJ(FTt#_MJRKb}3Qv+VjVuwvJz4EPwoYVaXko z^qhPJ+R((%%J$Y>M%NUxazD>1#$ zzG@gf!4oPm1-Wes>j1S!>#pb-Z5ajV$!1$ms7)M~1cl#|RhIOu(%pxK+**~I;FL^9 zPa9Bjv;CY?@&}ocU3FE01>{(=TpTG!(AEh8lDq-nf>aA}b4|wr_OJX{@4j8uSmK{{ zSm||^YaPw1(CL>rSyOx^MI^=rQ?$?h`p*ovFNr#i)4X9$5Xh9#69+=)3KrV$YsM{G z5Y7knk*D67S)H-zH^gP9;p6#;)3&l<;f(zL)4;%Vw53UgVx+I!fjF{5KZ~a3SGmAlX5XKj2k~z%M zLt~;t5538T3+;=fsKev*PJ?&q-_vrkM`m38+K$XhJ{UY_Zx&WfnZU}Am07CE8*H;z z)y$OW;Nz|MH&BU4c#D%CqZKYvv@n{o?*ZY1t$U**!s=B?OmZAKbfm3884*iHq(NG( zM+AbC*<*9wfNIsMC;_j@QnV;$FHwX3gN2pJ-ea+OH^~3A61y29` zW^#UL-1|D8W!pb!^2G1a*|+V{Q?&l!j@vEed2SK}7q;i-lnSi$v~mfnc&+FmlAdE_(4lD+sWHlP1Fuk59qX_YUm@)VpU!bUjqc{U zA#;r{y~mVmz3Drf(4-v{PZo`gI}Cc2{nHOLgRbd@GAeYGIh$k0Op6$2QXyD@Es%_L zuko3Kc@7UAQQ;~=6@&GGj;KGJY+z^8EXmAZln}P|Fv4rEypzziVfHUzF)}_>S)A1} zS{RQClfy_1ID;rS8ziZPDS}zZ_lQ-O>*qkoB>;hUY-aewMIg&vA|Eq4uaIWt253hT*aEDT>Ow}Q) zynntlf>^5BNp%F51jja7;zJJTEAoTJJ|@tSh-9ewX@jV_9^$_X?QLWO<{v zID#*Zu660{4Oo3`Atst+?R1#G=op7tGsz0uP_du{CZ3zzD&P?Y_${iFm zg|0L)Pl6|U6nt6mVCB(m#!Pl|R*$?cOud=KQfDX7IHKXT8Iu4<(F`Q^>K=RD?##pw z#P5N7h?D1%I-GA|1E@yhOdFqyP=h$es(1RzNDGnCpucrEXiQOh&t|P=^Jfn1WAg~z z7ahV2;mB5W%AplGR4W54OCxjo`yhE6gC&;tj6{Ov{xj;#v}M#1TRHAzErRJW&i$_{^Ta~23;X1 zaZ2Ln56iQ}<4@_I=i22*c`kbpHlY&PCA2W2-m2V3GIY3+zKAd_7`jf074yHJxyLQI z-m-h(>Q43n@fKe*dS8U@-@-h zNSz>;r>(Ij=ZVTMX%sHF0AFlt?0V}o)cA&vj4=z2vYrxE8UKj&-rZ-{ZU;HacT3t` zzYOQuT|-~QsV`NM`_i7B#=Zgfi4J2_SDpw#9ClHN0%M;{!U;6J!Y}Ny=J^dHIE;x(@^wR9qM@NRS<+Aq=t4G%a z#@Ewt)NfN01^W#0oMY&3fd|(P4ZAa+2L^2c)O<3$4nH_D^1w!p^)ZXI_;vugTvnWqO5UH?A_~U~?pQ zmX)0Me|SIqO6I*QBu@Q^He%pe2;pOcAU}+!J*?;bIL71Tysi+ID%N(L#A+r&C-eS+ zsk!&(lO?T=GSj3?S4bQhd2$J38#<2C=j)Ve$@(|#AH4(0JPIipVP6g{;@w%4IZNB2 zHrvO(FY=p|dy#VXN}+EsFZXjkspK&K6;K^J+#<2V>3nI2^(8&HFFWxvj-G?>%xGEj zn1_UuYwz6!D3s+3IA*$`N$=mI2>zVX_(bSU-rt{npA2p1(SH0384y`RC)+FG3|D8M z$2|D>rHY*4xd1Xx;SU&|4`g_x@DmiqB{6oA3cW{bqE5PQm_xaKs^aX4S}J<&S~!zbvS0#Sb@C>?(O0H-jC^%c4)P4-Xw_D zXC+!oACSRdnN6jW_twcE47K9Kn0HOl>LvRG8iOK4bV-Z!7|3gXUgwmG{H`ezWth6A zRLM{0lt~n9@#{sJyt+7?yo`^;rut3zNf0KiM%iWP2N;QP;v^jfW_L3}%s`1l4KMED zZL;XJ7Dv)4vmf~7{?vO{$i1p9KLF! zUCHzY+q$&*7o5vz-1g-B2h|JasvycI%j+Z!b!csJ-YQmAkilbBtaVMz6c+AuEb zbtOuC+W8w9u4Mg>>`zG?&V5fzQzGu)qS>Ol5O2#9*if}1G7?C~3^}sq4+1m>&o&(T zq9I-GKzi7I*nzanfyBK)GSn;fb~w)7&0uzAyU;Y8Iwo~U^sK8Z%(){U5>}HeI>v$0 zZGX#w5^$h!uc0IjJjp)y{h-~5f1Wn*_{9|!0j|qv!!HRhCHrL7DB2k|;&bGHieRYn zZR+y1p37KFRg?CRNpF>voFzzKXx|I-2hziH;KTSI%N1o0=TC!`Hrc~TJ1z#7t8!bU zrnzrG-}ILIHBm!sUi#c>L7eDZZ7e)iGz(X)5{s6dFA_3{m}vIup@S$crKA;yJI;$9 zA6$jDGIjK$`SuP`xkjyxrXnN z>H6U|9)_?Z`jL|i)X8P8E|E{fA6IK}ja5r#_WoI*FkQaZU&xi3QU=Sj9G8ha`?_-- zmQqgK(9BrTZ}#Hc&tjR2pEJ5#_SW{;bu?_ZeXvU-=!PbZpu6qM-vq7fVIS~RZ9!os z=5y^#p|uw#dTn!^wHX#Nd`mVg{lL3jWSG(p1SmY|*N`FRA6XBXp}0xmxa# zTF`H$$DNz0r(XXaQ4e##$%^o->}-f%j&nx3sGKiA*<;d0iIJ|n$?rh039#r{zU$7{ zG75-8G#(k9X+}OqHXiw-GUg;LEHJZ70_gv2m8ErffcpU45Se*;o8Wc)}erAmHeBV5W<1p%nL5WK@@;!PWEkYQd4`@7v%n^Edal?EGD~OXfqS z+5XpBT9+((_Ll6su)IqbwS5V!uR5@f$-z2a!#aG7gQ{1CI62ZjL;7?AeR9y=Ci;f_ zKl)x+XigmfCpT@;`$6S4iEB(3ji7oNf*6d#tdFxp;A<6fCp_dYo#GfQrMxVT@98&M z&9Alb4MNHrvqSSN-l<1I?YY=F=)O5W+x@Ap>F%p9DSwtzeoU_X2~yq}%3rS0Lph(* zvYiKC?9{L>`Vv@sgis`lx^K;v-z4Srx{&W1(nY^x6d0i4Y$P(tvKl_Z|MW;5`GEs$ zQ4ZKL0j6;*r-$%sS>Qwayt8Dxa4$XuZ2fc`*g8`vo`bDZM3Tyc<)^;F`9svls|a># zON9Jjy68AsRoQ+$CsJhGbrEf$uv!}{gPu`?unwQ2=P&kt?AVRa%=FHF4(RcJ&*thv2k`N&U8>G$Qb+c^E64jU}Gqioj;w7M5x*0X8Nk^4v7fipfd zHcPB7>2K#C63XEVGS{!?aO0obbvx7JZfAgnW__J6<@YvyuVFvIBXM@h0^jG&4Z`k8fFuh1KRtWF9BKSfSiy6c}}*yXG{BJq|)Pl&&Q5E!HQ4b zeHs`T_1Md=p{HVNu$1RI1{r_atc?`zgb#(c+i9N!QeZa$h)L`O+vj{VC-c#8IaB9;r`NgfDj-HLQJ~B7 z2mUhkkf9p|G~~7N#PL5R5ThtHEQ9Wd7vh@{6vPjT@y0LM&x9M<0jw1NU+wFu!|ENo zQH3DwJSktsMWcLY-zbu?L&E(}OSSM6Bma0R+cYi}`N(7lFa%?IQa^zZ^aDilAk2*? zp6|$uO04azr42lhjp8MSp6+~aD4>aVuXN7T%Pi3Mhw3>#Z?cGe(X(W-peMn~ZSVo| z9ee||Tk38NPSVm&=mgHP7oNHpdgt=B0!?#3*^I`NgmXbfHh?dn60oz{zK zOBjK??V$ZNCN?t!6YbMr!|CpSe+-x#HuWK<1^2xvP4|m)ryoRs9ROF%(>m?_xMF9v zxuDP%QHW>r|LHxK>LhWs?4VR8_+{7jS9YTP1OgV7-9u%%MeLwG_V8zNP_ve%!TUIy zJpc&-aS+y?uba_h=RFRB{-#l8rKry@2)a#t2R7T6=sHDz51wXX$1*%Y4i=bi-;)9K z!D9fr!U1&uKtM+g1_VE&ah}faw9k7q6Y9Lu#S%U}YhGLc`e^f_v+KOffNEMSrSNcs zIq3IC(s15?!_WV3LFX~1{3=eshhnjt`hXHYMvtv&V=-7;08E%n%M;2JgX>x?foYA&7A zxpm$JqP3i!Ni!?e&Xv6T52m2nA2mI3ava6 zKabzXe$S~{XoNSMC(!N?Xn^wAHwq}n3h(S=qL)=`D%XqK9k`dke-}>J(jIzQk07B- z5yvMwD4Ei8{(gwhqSf8tAW~&Ial1EhdjalMg(A}6E0?$(!+S@CJsXa0H6ClT`M;6> z*_{^6=l_LP;@PDXdZZ6)Zoy}%c$QljyZ}Yv$jk324%m;%0#omQLy_(6F zSQC}s1q8U3tPgrfLaMit{Ufl&7hlR7oKtk}oMlok8%yGHckfTR^vzoDo7Mi2Js^>E5fdhR0^Qs|8GD~I1K1W>$F0Gi&9>Jh~@2g znN4zSJ9`h59(pBwu^jzfCe|BxQor7K?IKy_@XOp{m&u+N>BxktyZ5Wak!03SWB+Lp zhlqEZC~U9hE*ST*z9(@dhIf4NSe=PAQR}^4sD45p_VI@7R&T%<=E{%K z;==U_O3e1A*yuaq-5=oF)RoyP9+L6{s;C0SPX@w(nHqVOR7dDk1J%t3m;);VoQhgCF)RB)_jlXbwHbKGB}_!F=mnZA(*CG9d)ZsBg6DL$ldYY(3jaRs|DR!B5bvCB``p(!g78r9UcBE@I*YDwc$78mMsJ+8h&_eGqzdUB1gFmh3$Z*=73LKQvP zYwdqmR!>6~_RgKMyNaR-u;A*6Vvaw72c0wum*SiuRBDa0Xeh=d&+9)~2DC3v3O*mB1E@(k# z_0xq6g8jM*ie-=QkQ+S=LD@cKEObJ9Jq82# z#-!(&qnStn>xhV+KXMQFGZ>l*X4rFv#O(2&^%ub@WhLgrOv$CXWb8fGzDIya^wrZW z7IMgCnmSt=ii;mStPGA5Ws^nhv;RQVvQ5kmzT60hhayvev#@byMU2N zuMJ#X(u6V{6M~2^ISp&?`wz*!n1Pk^GtGGlNqRlccM64|A0))s>OyeADCt?iBQ>)) zVU#G(HC!hQ|5LJIL1E3j_1{c3xDo|;>doMKq}_voUM z-@sR+Klw4AK(7S_kPM2gB++Y}f3*=XS$dV2(P-ZbjQHfwd~BVX$Xe>${oMHOJ{6=F zUP--UR(p2yD5O5|tsn+JPmLw&9FyFTHR;evgA#1J(_Bq!;dYq#aX{iq zoy<(?7Qpn3WEyJ3qk~z%ePBRS^`z=ycfNID40UG1UVr?6IVhAl$Uqp21zs2j2|9Ak zVEErj7zJkH257`+Bi`l5tBgHn@^#eeyX$lr_@noy)3l?OC($dJIkWoKl9r1xt8-D@ zQWH_L8Y4(ardlK0ah;k|SV(q5S9>Thr^6dNDy-Zc)0Vpn(Y|jC%-|55)LZ&BdtOID zZNe-pcD)jqRnqj3c%=mKNUcm(vk-RP-Z4u879~5mFJg!WrZx zg^=$C+1@OPfDbx8ubOPOJPwhAl=EzJ&WO|CxIrYG&jXJOOgUGtr6TKA_q*Jdst+|3 zggBI+tAu!dXjMNgANyLx1Hw{2)~%CcnKG`W;_dU$tzy`I(SJP1) z7eT72p!(Sz{Q6d02!y}}U(Hsb_RX94M#(oORJ;67Qq)xJBDH>{frQlb64%ytV%MwHHKi>>O<^3?q&vd#HEs$wy$-BU;Ik;fv?SSf z-)$t^j~R&xeA=ZxFx13WtK(hB0syO*cw469V4W;zx73uX_@wUq*r~KDdJbGpw_jFZ z#=pNOooCP500)}hm?{cZE$%j=qjK-;;cp;$uM6DbZF*GtZ=}20=u{|h(0A9dLfYdp zyw&l?_!~P4+Kp53cPeEdWpGRt*W>BZrHnwg$t0N0ENJIp$4&b%Mpf@GrfBAc{vCn^ z6{dQyR3=^h3-689T(G}}&Pjs;mkEM=Y^fEicp6a!oHLGlW=n;kj0pyzYAvTnlC7ok zQr57)-}!cv@npXJFT9%Z3?Gu-$RV?c*RY+B;np7e4YS$$Iv<5dX}T+VdnkDY!PK9I z)LJvJt2xYmE)ZuVCnCWgTaUN0>I$oZ4rREjYupA$d?5>)fwfI22WCKi z{{j0vW4c2)o8Hhld+~JT1m4zP>mf=L&eHlFE-kfO#wAPxb^C5M)r<*d^^CHXYlwg; zG#*EVTjRIXRK1)QS}N2yMY$6pwL9GL_RyO6Vt-j)%h^t_in&UrOKr3_xCC&d6nV@n z@EX--L21mDuA6{$F2Bz0>Prn17q>*+1YBmrbkGcZ?7Qn*AZn;}uWxw+7t*|qx}`^+ z%;f*C$<$qr=*iTV|7$Y$kiuyB+tG_;__SBI{-Rz@8tP2wSX1@jAg6P9*k>QbRnU4Z>l${7a#-Qucxw(0q=<@cQ*l8g-YwAjDS_za)+f>E~NxJl#yOX$GZ(Rn_y;f z2%TS|zAr>nttnp%lOaEWzGk<2H8rICB~k#Cna+F0$W%$8$mI~Vj`9uVogDP(G0Qil zhMVOtndRF;Y6GbWCaC2Cu6(Ug-ci2BNCYd4G)r(U0`ZkhtTgn7T(sBid zf0_MUSoLk*mJ5NN(^|S_R;Zd{g!EBeL$E=T5w8H^mKq(XDUB6$4Z#@Aqq1~esgCLo zdz;qiWn5R%^!vKx#IGb4l(80L!>bpR`d0YZN8)#u4e`Z2&Q-ny>TI?n2!QH+^@e&? z59MxP!rd%X8x9}I=93voK|P#H^f<~ulQJQr{B!M0uL;a5XlfT)B*ae$!PTblx+wl$ zmCA0#r43e3ZB~8u4@-nvLE<~jz!xE3O;@P=fEnm)K02gcRGWPi?vL5sskMFJIZj?Wvm zbTJGFnS`o`#sbdu#I#Y&!RgM;&~zkFmU8pAAgpHjWh~gb8Y~y5=54mGTPu3n&UDnB z?o9ngnwk-F5SoJGg(_xWg!{D-*@4golzJ~2tX;B9wjbI3v>;W3kM16t9F`53PpTq{ z!$Z~Dde;{b)?7tM&6pTcTSMyBvQS`WNYzy^EiZ-2dyVonX5xE#Lo)*Fm=rry-VrK) zCsh8=P+&KOUO*#NzT3<=OB*;ghE*N1a)Fi+X2K?OU$Va8XsBZ>6B&(nUsI zu&p^59Ty~H%h62u2kx@Od+J{kZWzWwp0NW%GWAhp&{2E zOWp14w@LTSJjs(Fb<`_Fb~e_#HicbAg#bBT;wFh0BHCid*%&KOC zsJJKcrQ}dEe{NaoDE>}Jx%tZspsN%HiQ_C^-zFpbSn02%?lv->5@Ds;l1-NDGUTZa z2d0_tOhOdRnd44wDrYoh5-ZZ{dN&C+;>na_Z?C>e>$%Y>+M;|xBUAha>^v+*tDR1W{}YYxy-+&M6>1~tv@iuu(%XozWSIr=wbH^v7Q{kS7KNe8UGcw_ z?xxXr+>-%zHxcC#)!>h)`DGCVCNf;$BNsc$T=g?uDmB7rGyJPs^-V~HC&bs|MZC*o z6srvNS>o=}c3{cv#86;Hi7$>5AH0_PJJO7Xkjm3?Q1zU`Qr~i6=WEqXh2D@#Ro~)< zck$A=M}Oav0=2da1bY}-Sn0k<)(K6O{<80gZjJ^iFibP z>{l0;1vb>H7C*uUF^Bv!YBsg+u2}u%eo4Uk!g;-L4H$ zgabF=*Xr`7Yp0Q3lkN&+ntRtwJ!g-V$~J1T0=oHylufKny33gpv&tE6Uu&y0d0lKc z%&2eW3Mu6bafAg1;bTzQ@WSzSw==*p=68J`JeTFv7(a+}uwt0e9t74hioTj1Qw1Ni79%po1DQGv3ICioL@k|z})H!RF|)2NMLRAS=AT#YA#5f5%@9U#cTSL34Z z!@1!ykQ$;sLrue$@nTqQgIwxP-Aku(9T*=de>1&~ek~W(zPfBet#UtPsDd9L^8KJT zQSeZD%7o4-{(K1Ms;m7>TNUec;h{8+nL*cZM7}Q!k5(xs&ztI>dX3)98jY1{+oi{B z>q@ypZ9%`9K0zqAskR$>^$Cx!Izp;oMVQ&GtzJC7G)*=~6CzD<0%;J*hWZ z_xYA5xDKo3@&dE%Fa)Fwatfij6p8^WWBMjaoYjeDwdpHkAFZA`VL>5JO9Q5FLbQVW ziWbtG*MeajCb3Z*09dqn(wGS}Mwa<}lLbG|NPFpQPFQ-ZRI2se)eO$b4o%b=vKD~7 zR3wsC?S%9+cS?SPk{m~*j(ApP%;ZI9UyV;remYG1Yp|vE-nCj5YhpK$HO;LW9rKBP zsLD*%a3sNRc4Gz7Tkng?C_%YSZtbkcg7TaTAbNT|c7i4@WfHP>gm36q{dRw#cCd(b8`?v+aA54O49jzw{Pf6WcxIIj-`T%@SjT4~%r*Z}+^$kSB|FkZROg44Ns34{KQwF5hbH|CiPA zsY|kwcGwqjcn!kPOfSd4CkrkXnIP<1FN%MRh}cv}GE#<%rc>WculPF20uDBY%gGh~ z=}^9HC}|$yOi$R=E-J$gu_3rEWmSnQpV*rR;^5Q!b2#`umjiL|4gIPg2d~~f2nV0V zn3Az&J>~!b2fyW92@Wnm%v7GsdC=v#hra`HP>)aX+TQYU+P2rb=f7fld9imcYm;9M z7Pnr{Sp4tP?XqrIcSQhkFD4USQ6X3Mc3jL;1-K{;k-N|FZNZ-P=|1|P7o5;Q#~v(t z0skR-ff~^gY7UOLPW;_)E;fW{g|Yhf(NYuE25BdpEP((`EAy?uKA6W5iQGWr)^LOF zv7MHPS_LeAO|c;k*2wV?Z)CNN z_VV?ROQhMV`e5{G_{02Z6OH^xX-R4(0zG1W`P5Q*hek2b-7?uo2Gfa}VqN;=dQ|uk zg+P5R+1gy2w91|h1;F-=r%4yGb=(ZIKpk*Yi!i6xGRDG%HKsca9GZN073v2%Q@~B= zS6BxN8Nz;L88{<*t=>-ti-{HLC&F1E$C~P1(IRrHgQLxhxU5?UDcAJR$ZRt{e0}H&(SbDT@ zxi6ALmlLmY$L1RGg;jYAW*bWv8hNpqVf1uaS$wK1QcvGk-nIY6K_qG-6o@YH0+daxRcki4wjF&kqc zHq3+7mlw3KYU>tSYT}NiOHRp;oo1=&Y?;~y@Ld^a*LNS5-ifB(PBJ!$DWzCA=N2?| z3u%Ko>hVkQ<9So`18UW<->RC_Pp{uDc_V;p5qbc~~$F^P*0 z#k2}0I0%dDwwr!~3x(QmR)hH~x&SB5Xb84`;%ohbbQJa#Vqb{OQr%eC@s8JUy>BHa z;rsUxE9*`*<3B9q+UDaT(6SrH)YeER;W+vgp}Yv znq06yYtjCc#Nbw3JG?R+wF{%?QPe4wv3c*H1kbEQTp$m6^_+a1h)XSGt}YOHf7(K( z%Vn?qKOCT(w#a@r&Uvk0J;1AMX(gZPr}yd)4n=gq^I}-lLDA>p4dmI@Hw;opnxD5| zyhhj`94mX&b3DY0><98gE`+1)-8<;I=l9OzJNbbt^4%?@?rBM5Z{LJ8gQY#m8l*zF{0}gHjjF$FC%i>zBCku*=JnX~`@2)@`hecNa#(K;u<~L)DJ;23BGu(IdOylZKi$JV zd~S#en*JPk-%LzCol8#hKKBfMb!&a0jiJ`J6C;awu*x$+9y|=EcM?T9P%|iwGWd4_!_V2vj)s;2v;~h3~p#VG0i*0?K zpyJ5uO&2LmoQe7vSnUZ`LuzHl|3u4KhwQbXw%wXf?-g-#v|X^1scO4hTl`poepB5i2wIs|N_JvV zg)Zec)Qa6!LY_^>{!7|pb)W2pIfyhN}Yuh6gi4$``<9@u}&nJBZOB zmxHcX-LKh-RZ08gM?#?6Z6*7(vmi| z02K2Z?JiQ#$?rvOp29`R_N!m4Vc69Hqz+AeXkE-BR)rQy7?M^PJAiOUe2cBlE-rv7 z9teEVe3^2j){thSlI~Gv((Qx%`=yd!z|gu57!*4z1Q?jL91#1}5!rXgnC1I{ObPks zRkX0K19ByKHL_z7YI{N4&U#qt(*6S&@FWenZh0Ze5Hj%ne@oOsb;@3>fi!Un8Yq=CJ`DRS& z!qUS2ad%4 zlr*Wq5>{h2S-Qhsl9e}@(X?Ksts@n10>KjN2!-^ti~jPQuo zRrZg6nonmp!T%cSWns8>GpkFV^NK3KIc!ZWe?Y!gcNE4Q7El~I7UrqPWtr$LT?e5c z#P!&}ypP2RBLMfS-8vR#ZOhuTW4?OT6kUO6;g@Qd_49OZC;gVa8jdTVjdv}ha9U=j zhoXX$r=o~R=tiWDa?E@3)O0RUaYVmj#Fvm2dVFQOv1-aGhzkp+CnoO36E+;sJ(nDG zH&>)4#-BStt$8imLxCx!3%07|n*IB}%`?~cWyxsv&kU?-eq2jVsfWdJ44d$J#w5qe zi`84=D1?_Epf^Ldg15%ap!xC+vc?|~D9UE)9 zHcwUV4O0{D*D}EkQxhfKAx^CY#dWZQfyR+Eq^e+xNla4aVdi$ifZI;PpD*A2SUgQ& zMYn7Jog}pxfvwGVm?~ER-7~8fmc?$Map*7L31|C5)!!~@K?Nyc4?|U?xRecWOdwM1 zZ61|vmnztS30W&uYD$`3u6LR@O_cy`QCaL*x-t!u`m}_Mo^HByh02>&q}TA{yK5ms zNS(T2XRvI;_z)%M;YzdvW+TwsJf&VeD#H^_PDHjK!E1{bn(*hVE;?12+0nnY`h<_ zH=~UHlj;_7W{HL=?0PX2Xew!*J1sf!ulV0FA~kBUzZA{?S84)_{oZE3GD<}f2^e0~ z{s&qL1^ZF*7Q{2(lR7ruiI3C8B~5=11-c}2=4SM}sy5;NleUMT_g0x;7rk$cIo=F3 zmHFBmeyLPgqAuK^bJJ9zgOd6TgWvD3o_Xn)H^Zjx@Vh#^k=MBE` z^Sdke`Nl7NijW-BH$M0Py;ep6`vqdIZ$%6CClc1-rb51)74x`5;j}P0JB`i68z#tw zbzrJzWQAvhd%eE3=x?e=g|LRop;B87R<&9FsH_;nwb=}O*m4Z4M0T~ieWq(i$n~y3 zwItH#w>mrCfgyYzc-^=111?Ai!=gziYrWx&@P{95>mLyuFrQ>1-9rnBCqf%d7!s8E zS5!u%S|*TawBfgui_@p)`#;^6PmEpA9?;4pM)G1^ee9CDQq zT9u(P$$}v~mtGm=%cL)Xjuy^6UK37+=VPI=5(UIK&A!dgDlh|~f~MaZ>V=R*K}CpV zGJItRh=BS6JfS7TGO^+3`^w-U!(QT?f;4M1QBaZ`{awsI4nME}Ho|P{WH?%4k`l*E z&O)I*T#S!uwKSxBwI0*zneG-t{Qkp!>cjtU);1EgN!PdF59ZE{!PQ&nzb8#@tB-th z);Pgcmv7*@k55O3VZl3a1)~8!gQfH>p9;1~f*dj1a@JG5+Fq~D4r8EFZL;BH6g#Ht zC$i-j0sL9kVhS>BcW<({z70+orf~lf;|D zW6+j}!o7@Rk5*~!Ry{08Xq0a<)ha!40UTjF*cp(wjXN2b6xm&J^V5k>5B$INUm2CE=QLzn0 z>52hDI}4fRmOE6wN+w1p1{WUSTY|#zPUiQ6{1{w;__T{3yg0Q4DZS42i+Z$An2(mF z=-JEIh{SQVK^#{(DL~yA#C6pQ`O0dy_{tWSV!avQ#5zDnYIsqQif%{ry6i4T8_Nzz zFb0QVpz!Zqhn5tL=^1HOj#g_waEE?yJI6dZ5AOe-gk&X)j>{EU?i9H!SEN;n@K5rF zQcsYaq55!wY|c%wsI-SMHH@UU~2P`@DJTY=BcZm}N6t$}7 z&Lb^#az=&i{$V=ZRX2gLr+V)50J_?7RmkrglBdt*nlaB#k}N}Z@uvw0(!@+|`j$Xg zwb^I3a0QKwo`l05#xG^}bJ3aPimXI}k_Aqcj8Y$k7ndVXTUOSM>d0xrY~Y6#TL zuo2LySIFvJXxGy|$*=dsKIHLFLZa7MP|&nRj8eydCwi+AXD9o=P+)>mpz^bwaFm8@ z(*|mLXk*sb=n%Oe{vzI#xEWFNhZsMp?_L<3@N3F zjl*n(%GJ+kv8IO3*X7-2`FhjUE%60g&GIdJ4{xOmXzQ=sRgXVJ_0rW3Sy(hVR-a5tc|`W_CjFI4`j99^aJ3@83H)8PsfL5-dfqy4tGeQodfr7 z3B&1zETN4gcWz>y-r$Wk2la3fep>`<$(-5W@_h(3t3w=_MN-V@u|kzvkxU|reDY67 z41LIFey)~FH>&lDjp3(s%NYKLA6W4t9#aPQoy5(I#m!Z6k`~skM5rSBHW8}IUMh<> zH{@;;^SFITK$!Utt>!1xwmH;>Rt2oyWS{T>`C*sui}=jGLDeGx>-T3s8RJU|B;7sZ zHR=2GB6XXYxCMf?WL{ouN+fa1Jhb2g6_dA$ViNe>_Sb(cy2TYssZ=!OT5p+oHN|-) zv^>=Ij=teEgZ5_k~Ar6ZgkA zQptx)pOGPEMhSr1iR!U2I+C0yV@dU{Y}nUEF`JN0wO-9FtK3$7NlA-^uPMeyhpRQK z4R6a?5jB%o!h-hN_@oW_F_>s`gFjFa8&%uo-XL_8qvy5vC>)OAZQ6=~@sg6Jrz-b> zQ@++(I0Xn+bu(78^SP$!^J~=@Wx~x6@g**Uu9wa7SEJuBt1m8SLA`%*K?omaGf-R7 zJRL?G4tX~m_6ufpgMQsmpyrjj=J{2yz-6ncrLL)dRa1a!YF+ap0LMd&EP#Q^ur+GF ztG1-+38^>!?E;()?xx9f(X6iVw%i#?Hsni)DVFGVf&_-dt_|#Op3~*-hODyY2o*5% z3(fqCAt=x5k&KRpSR2(7;+*u=xl%teTpy**jD0s(Vx?4ED@B$KZcy(nEKV~qraL53 zX^ppOdHfp%@Uxqu$JD{=BUnHU@58%Ag%a-jwBzkGjBguR(^*WDNzXC!lD`+{CSoha z8DB!#OoTuDzMKJ=*=Q!NAl~K*#F#6*Ehod43QHjMk=6hY9TY2H;gVgLU&ox{-1ATX?hEE9eF~V6adqEno zHuLGVaXUYHynGMfv-n1^F10~RYspq(NgNgQ^vD1Az{$nT`c5!bVzl9t?I zYuS)+e1)ea6yg}+@-5$l8gHToC!P^uBsAf>VaUB1=6g;z3_hpch z*ejz~g~_OpTtuYB$Aq>74mJntlM_EB!a{XYlJp0v0Crtq`DXWA?>h^|_LKBF$1A?K zX}if;^P|EFmRcQSNy^_bA2}l8n<5SW-bl_wu$GE^t?L*}DnkR2LR(O<4XB0}h-+Jp zlUvtzq=A{3cJ;%T7Nk-`T-$m5(t>Sf;I#!49TCcsFM3)|;A77tY*&#)ke1Cs3F}G+ z>GW)L64>b1dhz~kl1QN_mQ*^p5MF2p0YY?eVy1%pm(;kuG zMrRVHNzm#T8LlG-lSW8r| z%XgV-p5IgpOH$t&6t#3IDqTP1Zy(gIOc<{2__rtKwOq*A#<5ZCfM(#^6I#j;l2WIe zfp0kDb!u04=`j61ql% zaWjzaX-|VwgnwZoMX`MdsXOxell?TuA75F?lOKO^% zt7&|;rY;?lu*se;RcZE5s`{bk34bWeRA3Dunbd*f+2PfcbXx&kj||?Of*^9RUFKxDha&mKmFn_>1PQb`WB(m~im+8E+{gyq5q6M$2y!igY!*=79CeWHeE zdSkN!eX$}d5Uhx~aj^oyDV5M~A_VG6_Sl|u^{kSXiyV9qUw-&v5)VZ(J3c$ya^ zT1up~i&d60c0{)6V&c~ay9AkU`?E!YJewp&ox$SKP4Gz|SOdsZF;|Y7I?^~5W_e{N zLG3{P_|;X^Cs=dyuVroN3{_){M>qnr-N|2T&wpF0JRr3{&cIR_6{*fsh~7==o~`Sq zK{ztAS%<1-2cwM0hIDAv?4X(7Pz-lb!BZpZ<*tM~NUB*{7Oxr_8(}7jL-{ky%>0?9 zyewTS=9c_)usB`gd@?j0EJ@cmAEbk&B8v|8-STYHg7UQp)8}w0D$A_@;Hmmmzwg$s zAE76_hX#nWolfMW?E_mKX!J-+>Z$Mi3VxIN}TS%DtS)>*T&Pd@J{E8{TP^5^IY;}*e#Ti&ks1?2}5GvrN3M0wE0oORoeEzPG{iN9Dp9rV#6=Oam9$w%_K|D!wQBg1LgY~Ij_InK{K z=jSGVk~bbq{AIp#A9a2fIzNk@pQZfBkUKxCork}2ejads9&~;l=7*AxIX~^r!`=Ka zo_+kJi(cU!H^L33i@Kc~yok7Y*17TPn}_6vVP}>dYW~U{80zW@lb8HgR%sV^WXUZ~ zULs_`eoDU_C0d%?PA)2m&M{)X4^(FqLHr+m>T!&!UuKzJEVSi?d3nD>*=Dak66Jxdx%rP zd&fEz%(M40(`fGQk?*D96|B25L27X^mS3k2?@o~cwF7jP9-Nmh`uvf@d*C<{*uVIT z?nGSIk$JX4W1-@-2PYf}4JEwfjc6pBt1?>2!}G?CN5gx>ym3qK)Oq8+`>woq2{nfo zz%HqCtN10W73!+w$lgs~@_o?x{)R98-oALM%-J+pZvgielThf0yuI)i?40Dc`YTmo zRrq_5;TLIU4664&t*sqv@B9{@!`}RxEvk@w(aa5H4=wfATq!lzpYjd({>S}q7Lbr- zU(~GJRXw+1Nr~88N9jrm#@o%5f`v}r>u&T(H_nJ8TYkmKOlV}H$0LGp-iKc{yt|{BjKb(E zI5v)W&{v%y_yFvY6Hflew+%=5RxR+S#tp6u7RWhG&40x5dbqBv+*^HX!xHko!NZat zX3|hOq=SYI%YMA4IoD9~nmS@q;MRu4Te72g>m%kh z(kTqDPwC=z(@}bJ`lTggf|+T8e!z-wV8SHevTgmcD)VM`?#-#r8)rlNK2o>rZnEpy zeYjf7jcnBR_Rr-;-6Nc@{XW8o+{iX-zm>UJDmO1XH#6qj8!}h(?DovnZ2O7KRfGMY zzDmyU+V3vVZT1|kyUAP$$0mH5_;Od}Vjv{{;Aam=wQI*Q=TE(YVYSeR-KorG`N^JKju=Imfz&DE3(sL|rm>eqX)skxA>LqYT$8V&Vt{UAS-g3p7sqWubuMyryY|t_AS*i^v_b{4QQ`h*QM($c^6E|3lrI zz(-k~4d2-Y21uMBM57{(5;PjB(a@SWE;D2TcOZc%sIhg4#VT#BMVMhxmc#+V&Fvu8 z)}^(Tr<-#F$rsdx~T z!peB=`L$`X+-Q^@$95zIB0c13@ydV}No20r^MYc_`v!DU?0K#QPX`mp+NRD(gywy+ z+An=Q+wCiUP;Jsz;;rWtN=B?UUx6e1tmVYyr`FDr8PV4yrDl6cj2s|-)I4d(O01@w zCGAxu^90f*mk_>^_9d3R&?^VIf}Gf?d~%#u%U#HrbM=*Qycgqo>Q!qrD$%`^gErF zQ4E)k7J#kg$u9NiJ2DNE5V+b5{oJK)ZwDQI=SP#^^=TrfvGl#?AHss| zew%%YB;;2QG%*$NqVEh!DFg2ZZp0kg^SUWgsb-lXYH;Y`WH`yRuIdx9=Q6OZ-CGROR^M$p?4lx>@Zo&dGe5d~ju z`!#(bGhN!C#oCd0KreZ$X-`(H>8&jE6d(B(29@d5`s>r3(%JgHD*kWy_<>7al5VYh zS9j~o|6#XSY9kJd(NyD3@W?Ke1MRZV?42*iwsolqjAfbU`t|*@{+NfSS0l4?!P*%d z<2;^6kAUe{_osxKa*@n{Bw9C%wC5Lkk-k@uTBMJnEE4jt;c?uU3He}f*#QD zDL<3(9gz9Ui{@Qm$7_-l&$|Cvz&A zDuZi|w(l8D$ik_}Ss~+=Nmw~zTVPwHipV_dV@Kr!iy4>p?aAOd?fL%J`E%R>9!)>i z2M*k??>hYJBi*ly6MfH`AiaX^RR^wROw8dsllEzz)kM(vDYWMNXj=-A4pqX-yz}(KCFvla~TxRvOjJ#2b$;`$i_X72MeblP<1V)fSjcMi+^9=NiMAWXRT4+^5`M*k65!wvUprq1)b9Ls zR@Q#c8|RBThJO0h*4Y?N_Wl^z*YoakboO`Hd3st3+T0Qyo`JL?|IuFpMZa9yca1FBXb3izh;}X!_AMcVFuv&HXzu9=j{;LCshk%o>A+Gx2RPv zn2%aF^sNQNuB1Ix&tpOSDr_z)Rlr=7iiiFvbbFG}d)&uRUh;AKrOFn~14=q>PJJ=y zxE*1Qok@Om7IP81$}U0kcF5@QHP80=VuPU|Q-7U-N&73iA%)mq0ZlLa;j6~jab z3y@b2q2cVeVmUxvt0zfmEa(6!Ul({v#Lp=-Ej|F_iUmpdsVLCtw|q4e)TJ%w6dC7a z&0g<@{E#PdM=Wywk&F&&O-v@j&W# zJkCSQ8P+*xc!>O|!n9eEEqq@Bxm&-im14s>ojX=il5)EOJ z$1W9s4S^Lx&K+iw!@lbF8vJ+$)C4+fd}G^jYkmqVSf{hBjfQkir(M1~>)8}ZPS2Ap zSGR?Icb>$pOcCCQ7}bt!<6=cpv4PHcny~MxIv2kur`D@Re?6IuUvY|E(f*Dn6+4&5 z_e=^>n(S1yEwD5AVmYW5V{NCjD-S~;3s)N}>J?$q$;+|x23qmUNSv;+qv~)xco@8^ zowzD$EhV}DHS>TBfXO?6@K7pHP%n&Z#xd-$6={_ELib%K=(t~j0nlQsX(tffurt>5 zj}()#N8L;jgNZU^Yjw-bZ0aXIF}rK-F%m6?7m`499>F<v^rDkqqbUL0pR zEz<;x!AZ;364qL{Zj?vrSn$mXNjm<8&Cshm&@O8BHr2C%6d2j zv$$7(T}P{!c;?GW~SX5f>&85ddcx!yYG`1qJ) zZ9MOey1>h`JyxJ&=16C4Y|m#QvbqoqFk^q*B3hojTHQIdFwEi$!R1H4ljw+H;`iJB zdJd5Wc+{f7ABN$nmO~zJfqh?)l~?$(BjF_m;J2LB_cP_Pnwk^SlX|1(qBW&h-o)Ev z(ftof;@*9AtxpN-CR()%YvJB%owMbvb~w3l;NHekS&=iTY+RW|x41)7Eb$Kd))hj* z{tZQquD9aLjoy3?%PNd3M>N-;w6AW&hmrG0zEjA)kw+p#okT1x%V^r(jL_1{O4T|0 z1rKkbHKTYx%e1zqw>3O{$NFha9%ZyvEFEn=Qe#_}dG23j82KfQ4BasDEj-eUJWsNe zb1oAWe>TWWBt{AGJ8PuOAdqFDO#L2H1?u5y>Q8dATt#P>y76*ABz8#_-A#*zhptOa zmBUlbhlBc#zz!Napul&X@V&cgSC1TV+M>4}+>7=4MRHN(9c!(AlI~>judbrAHo3OW z_t?f-0kGGtspvLpLBac3V$x(!-DH95*~bwO^PnorD@ml-0&UTc1n00>gbws`to#rJ zlU=Rhr|;gP3l;mEg4><~_S*@kc14o2L?nrp1S=mGnIsBWbgB!2GB>RUc)(Oy&h$Cz z4E;!(hCv%?1gded=Iz4{l3}Q24|@cy#lcWaT zC#nhQ{a|96f{9#bJbf1)4%>8UW*Cg&QFHWZvnavh8HkG#%98jY8JvoE#rd7sV~#a09i*n*vCX*Eq?p zq`u1zn2xekyh!5mwPW65-PO<$`wH!r+;RTP)BeK_gzw$yA*Yx;MOp;&;Y=JFfxE6a z6URQyj$9wFJ|r5G)_?F-#B!BqR?bbxMylw#cui-;aGZ=SyM9~Cn*s&SL>!X-UNPLR z2bE3aqxI~zPWzfpbuF9{Zm2qeDQA}Nq|vZ5%UiLVpg>Pu?yaT5(MPbxf(g8w1lkpQ z;}npm$H`yn`^EN|lbrg;CB(FAsT}CXhQo=+&oZiOB*-of@aod73Zh5mx?+ckUW0E&znrS8-X`WBzfz6sRN#sMSRNc~8BQm3Z}!*~`{ym^%VpNX1^_Wl5q~a5fo4 zgl%0wTSPH3k0}0z~6C6I;QgS73(oE&mZ$&V-2b@{Uge_uR?N{29c|2{t$k zKGFC!^Yn=D>ArK})2W{BZlVsoiGZ2Wps#%qkoXq$Q9{o?dcO4aVACP|rw`2cY6nJq zF|ngp$gGdRjcf!1b>*W#bk=J(TZ$$Jg>Y-|eDVRBxOWzNXL(nqNUUibSV@pPQg?zWp58qEq)D+-()(_J2t5HtF=+9s8*MF z5lX8cKY9%dq^}OpWfJ_HD({)VU-grfyXaVbD~d6{jOOJlWi+s3Sh%Fs>!iBldG}Fq zW$8y*`A=Y@i4_-3Dn8Sa@g}BsF8(_`p zezcj8f<-SzZnA_T-fzTK^nWmjdYCZm@N9P(|B12m5DrloIP0V~Yec(mvG^0Z!E#&G z*=Mk7JL%*J>gm>GvaGcYxFU03e@+q>_rlUPkc$ciQaVwpAYnKyPLq3btRQbkKsdOl@yGoNX>&MzHq>%ABE zoQ!)<;`gFFGv^=n%GPLBvUK+jN)JEPn)~|>&_nU#%&XX)_!XZ47*^wirq|mO6lb(L+tf9B?;80xT8tm0<}iphklEg>E;g?lLw%}}xyb-c>`C3N^s+M`n!5n_(<`vXW8Xe7Ol&KP* z5IF@{_*~aU*3vG5ORb0wpCpUEad!q73aCei#jSGHW0|d3E?e<y z!qq=dd?&xhdAJXA&*%QcPqOIQ6?~MA!iSDq?FUb(M(Gz%G(M+mXR}R_+&nk`o)7cT zkJ>WGwN4g-V{3!@`S)|OHpB|4tZhHRrO(K92BOx;%S`)5W_r9cv<)2;1L3Q~NE6n( zkos+Yn_0-j9-_K4U|t}mSfBIb7Zt#+z~IP?_>5CrQr7;yA#Q+F?fyMF1rwODqKKLI~^*#-nTWD(?0&Jlm+1ip95_SVMp@1+TCp3PCO!3k;ag$3gfbM2X4 zJ0MF^ZsZ%Nbdc4T=$AB~@AA&6equ%V(0PUP2EOa;Q0EGPHiTExVRMuYz}~5@pr2?H zF$N=e567S?wylYXazh8yBld^vLg}O>+OD_DUZa!GYG*(p(&_@;$%%z(*I4P~EYQ)B zyZD0BA~ufIEY!*o?c_@RA<>vlsC=E%B0{mia2JZ`Q}>l_W8bchvAPP7r*)mOSY)Cu6=R8=~eh*&t_Gcs(LSUyub)5#$v^m+WiKeTrcMMmJWnntXGysY6tFVVdsEd z^H9k7EL?SCa6!2>^)-^HLmMWRa%PZoMOY30TpHjO?HrFtexL^N2##?Q3y&9v@sjtaNEcW*nqB=xp3VdPzPQ9{gT0!Arv#>80m@TcdzxQ&Dvst)J@|zB1 zmu-gU)0BP38`T4bE)CmJCu2xkC%QwbbM`PKtj%Hw^(|Tn%+)#H;tUTNy~en|q$v4Q zk~{30?;~g}wBnb4--<8Snu2^#F3)DW&x8+E2V`QLEqvbO;|%Q}0(OINw?x)IH+DdN zGYn{HturOBn43HBf~Rk@^#>6{y42{@_uM`$o-RAsng1~TA{p-Y7Os{>S0k%=YS+9i zfI6fGm@`)C1?-wSM~O$od#h1AW~tA{=z zMA<~3f%-&L-SLTJEO<%1udkPj2r0Mcn^%@x@8CMr{;xa^&(izz&BJ7o3EvhFT_Vdb zX^@EhK{=pr>+F*3+FVYBAx!CAo|(PDSo$$L(`3P7!rvNA{!WDbLN$X2a=e!8T5LSa zrAqQjAeh~=#Bgvx)(KK~97Sd0177m8MU6*xv6l$fAik=(S*L*QU;)JF^CG!=wIy2p z{d`%{B~6r)Nj{tNk6XX!v0A@h^(>OvDf)YYoP2Ta|8cn(-=*vK-_KB`2hUG%dmnZ* z_FU$9^B4WIF3g(&75_U|=qmI7n70~`%TlinqfX0uLCn2zc;7%@(38}51Wh+#%sFO6 zb6SROLN)819<1rkC%wu>6pdBZnhZswm25}RxXFqg%$xDX7Zi=K`T2NMlE2;QPgtht z83%BSo^hl6SUS6)8XS~)374dtgN9f!KPSU;S_lZCA3yopQ%Hy_Nn()!KdiM`mI=gn z<$ai}&clm@r#f%QVS%QiQ~RP12}7O6NJ%83V@$00Z;Sx4Ia%}y3nFVH95I%c#+8zr zKN=F})bryLrmGt-k!jy)n16OAhkx4?zY zv~_5w^id0Bjlvu^2?sI_=ELw-ef7BeLJb7Td>FliP&%web4w+$!`YTI7;%8z(xtAh zbpZ{0?InsN?2H`+Z)s-iCG~1P!dFLX11nms4aggR_e zNzFjAi?IC2Nx<91Gpp9wsE%P;3~SP-tv>QhvFs!9t44GFi3%@FCN;Q+opv1X+GC}> z4wKsrp7A%ri$wkv#_8UQU+LO|sTC`fCd3Qu9(~oz3&~fzt7y{bWVPg?^9>N`ft6VO z_I)API9iE~3IC!{to@V_R1EG>;slFX+o5BeVJ&E&8^(+n)~w@H+h-_s{Y6;Bse>Aa zUT08buam5iKH9Sr`T$WnT!NYqP`aJYLL)gBir;1Sp+JC2lo}&OZAeq z$WM>udt$X*MBlHAuNK);reQEj@(O6Do2SBDXZz8IG+Qa67 zL0ODZSEBSQAi_&d3E8Aw%_3xrU`2SefgPC2gaTd4fu6L7ZLCEt!tKbtIqb6RE(T2%!ze}Y`>?22B#ib0}E zZuKnY3H0DhD8Mnd!wX0bK_O|Puk)*$XhIil^<1qBN){N;SEQA|I?ER(v9RY9?(B;$ z;;)l3a4!ft_Xyh5hkiDr8(A9~)QQ`jBM&1(oKCW)^*#u!jlQ=ZvwHFrImxO^O@s~{ z$zZ-0rK}886iY@|sK=)1zaTn4=));k`C}}5Z%6fnMNUfj7#=H>E_6C88|a97(5xl$ zbsTC>_S4hplje!kssE4AIZq)#h9C;c(xY?vokMU3!p# zw&=e`O7|D=mAiyXV)-OS>7r&xs%~$k+g^t-$I_nUmMJ!a)ap03s5!=~z#SwsOn3rA zMKnFKT)%KhY9Xg4$7hgGvPYvRj>~*8nnUn@ln0OawWj&d#qI^x~Oj<@-xNrFJv8I(b-Sd2eU`GoyFhQ?5?W~RP#gtCL z=ysf4nh-h%De%lR<}vqR;9xZ9#3Ec{dMbAc*51$f2FUmT+KSZbo+Yb0wM15TT@-^# zm%7&rf#}!jNKH{Q$f*JvYD|aEpGynzqPjPPQI!##H$fa&d+9!)1e1x#%mQYo1k(W_ zy?TALg}wIeG#oZrxJ2l6!Mppw=f2io$m+vH%|RJ;Js<-kmMm)I0-n2^PcUg@v(&5> z1L4|&w zC5wKE)a)Ma9cy(Ft!-iLwfUB{lJ zZ5HvMSw~$xM0LJ&)OEaBY{PPPp+PF|z-Njp&hWIyiFBk`z-<4;(h_dtbfYJ^e2&0N z{Bl3*(xX8~cP#A{A)DDsj{F*H>cX0Q##N}spAdy=#@w7jF?Vj-_jqJ_h-16buczfB z7M2JBriE_M8{L`j(3zBR;fu3!R6m-!_!DBUK&35SzC|l&+I2gzPQs$FHxjm-zZx+z zJ?% z4aLVCB2Uv3jfFaab2E=v|76i^x5*A`v*K422C5?qzG=m82XMD0i!SAbzGyThb9C`jDPHGHAW8XzQb7WCHPBS_JF-@(8?m@02I$XzG&cw5_~#Qj zhLyEMTxyUl;2K@npDu0u|KXgT&fv_PHuas;&)HvklJZQ6|LL4=rrsCl^huDWI+oKKkAu6AsNb;%! z$f+ybLn=9^|9NFbD~v16_zd%bJ~(E7R#4T^Dn3280%MH zr_LkX=yo=Mi1aR| z2i}h0k0GoIV`A$HhvHjAc5OH2Xd74L;_@v*MniknQ|FwYC@~Z;?s*;+WCj!Z29t`L z%3k$XLWg^!lWwmv*arMx51;XpXjt;w)pR5&VGjP-yednW+z4#SQt^s_W~%f5%e-W6 zI@L?fGKLPXrE?_S25V&a1ol#+EJRaEKnzOiuXxedBraKV3m=zJHeU4nODXo@qcMhl zPWVHr$aYiY=jkHbq=>sOwRJh@=qQxu=wq{F1l%>EgIu&{rY52J`+X+iRhHW@)y}37 zTLW98%GqSpbD9aSj4!mRiSekn(HO2+Bepi7 zk|V#uPylK;f)`%c@A+pxanQ(WMpIC*cR4<(RfL&nUQqbv7U3mWze19QF{-=eHrh=F zoX|3pSJUi^!)WwDoVJ{TU(%YKlcEq?jm0l=qO7!aA{~M8$Dz1Oy(W&#D4`){&xvZg zmw=v0uTybgz1#udP$}RL6i`p+H4!J2bjlZ-<5zXAIoL){qI3#Qq9e5qX5qc+>fXM< z-gK(J$mi(`Dn)$rxsi^vN=J~vNt~lY=sJ&1F1xSf=p*JaHg{-tozosY%lUy=i;b0c z4(1>d$B3h3euk+yWUsiIXCQzm~5%Y)w&_E>DMTy8b3DC zt$@X9dem1C7U9iK=vDo2jJ5g+;m*<^&i?APlZC-X00=FU&`(A*6LZMzc!KHhPf`Bp zy`XK&e$lug3;*CwAc^rdq-9T!T!{~HG4otQo_yPa#7eg*IcEGMc+B|(NWZ$8GoI9i|oHbXr412sh)<4cv4a`PAQoxo+y7bS zaK*d^N5`EoK3VZHChNT2q9rPGJ*B?ZolFkg>9jtgySQLPO3qT>)<6=4DsCmG%X7OJ zA4Ay4>6hv89FMZcw;1BckW78wT`{@4YmW@Geg95G=vKMLUExP-ayTAFsha!?V7AYXa5DfJZ^!}pED=L2%%73@O0Q|fk|iAZE{-vA{T+Snc^c9<^+q_fg5HPuiDJlLi_iK4}7~4KTnTV zPm_aNd~s$Dbf5YXxAUlxCT14(t&>BY3lrDy_8%VJ7EmE4)Y2yD(h}oVuOA>EZJ&9( zK0MO>&~`c=<7>B z@*(Gd#7%ccE=NX%;XDl&q>I)0U(3!S;p4PFix5=w1O@e?%4l~3YiElS+3-H~+oK-VON@nK^A)!Zciu92QJkDbU$h8m=MH*i z%!ki#6`6dx_GNLRjn9lPK5s44*lqSB*~mU9#xpn*`(qu2{P3 zx{smtOqk8^nTyRO?%z4MU;-y7ox9*1oup_ShFKHAKsfXXn(n2m8yN!bS~%E+R7eY+M<`sUahHUOS_N))dJgS zKP$YK{WC#M_RoYhv!4}i&3?8`|6R{t%YL?#pZKKymc4wNT#fU}*@3Oa@QBtI8hhCmRA15_H~pFufr> ztPuI|GEcudbVM4lXmBW=k6SqQZC_gsZrS1()Mnf~ZecxFSmKXxgj9|h`}Cl6*XBmrE`D1Ccu&bBN{|2crSD>?ztWhLI6N;J@II1Td(5YKyBRf`Y&Yfh>x?~UvD4K zy~Ka^&H2>-I#ELDoKJ_p(+O%Oi9B!KyLdD1Y1J+F=7jRn4RwM3bRYe{#tY&yeNY`Z zl**UXqx-y74bDipogxrNm zPw=BTTZjZx*ho1bYT=2H*VTeVqwhN{ZTunw)V!olgcWmQf>uqrc>^i;jND6fa+)Z( zJRh=-9hB~0FG5NZ#n%jY6a_@dLF(aMlZ$X%rx_In$GoeEHx&<^xsBX|B*9pILJP+3SRuF9)uK zi`c5l>4Oy*u9H!F#o3Gg`4)p;C`XW#%44nddKpuN9JQQ+xlBIFFRg$`>x-rJxKJ^*N?+F(s>jSlvHCR^YkGJmHmV2YDWTV^ zEc;r2>$P*-h3uo_Ti1V--(m z&EIYXx}y2$x+A%keWIlCu6J^U3kvz#gC*kGKBV69))E_m`uOY9>F(V~j2Ll*WPO1~ z07%LPty*ZSFzRNI*oCLbfFZHV3VwN;P}mDL&EQxI&pq6%hTF;tFKUkr3UfB2)25Co zaDmRyflPSS`y+#Y!WJkKT7fz?RMl_6DYaz1Br0<%d#}r?s*2v8Y_sI_G|s6CY@7F< zUff8nHFdw(9Mm-<quG|0CNV*h6-aPfQ^y22~X#PdmwPh`i1(hy`q8rnJU zUENT04aUQajn&I-bw9%paA&>Wa)zx-IM6|EIHfPK@dff4JUWMyj9%>ibh249V?9Lv zF}gg+uEj(GOXX_i<8;uj4oZ^mw&+wkT@FfH$%x4la4m##ahtPMGFMpJIJG(`uAU=T z3*@NNk7L3?);!ZW89G%X6Qx~jZx14>E)#o5Z*xQ(1=V>6Ws$0bmhJkJNKZ6+GVw(^ zwy4&gK(1!@Y(i*n1ATQ`=qFn^A{LP#x+l+*S7Ph1tG9_XSgG!2kC{MdM)7X!nZHpw zYXOL|OP$U>DEksfT z&~yW0UdC$;bS$TkeMCc|$XN6?7gj)*h;Z!IO&mLpvs+#KIl(@12r0&xNsUU0+SP=c zl`^4Tl?%Q?(8Fubd@^}94BUD9^cM{y`1ecc=2Dukjo-IYzT%)G!hwx|AxDDZF`GdF zOpL4**cKg1zI0>N%GJs8bTlsqg$Pwb{hp#HXe8}pQ}jd*bAWyj%;nM(P6gt=0VVfw zwPKH(Mz;vB=FD? ziWet1`J2Rpd8mboU3{2FiJPi%WBx$9F-Oi7Dfq!hQik}du5QnpH#iiV3&H6Zs=8|W!n~d&0c*)N zvO-7}gGU58NKS`}wyIF+tbU@h&01F#D&cf$=G2cw)6yxA7*aWcK4_TH-SG&(?&4O> zi(1ZH80};PN1^BBT?Sq~L$jmc)r4SegNsu5Qi6ztuOPHe|MunQQAqJ+5Q}iSABqz3 zGw$6}3&toVu1UPyUkEh{6l;d0UnAEWf z(T)75ty@@HD{xg~#E-St6ek)na;#8sqCQSy!JO9w3%|mj$ZRWcZDYg|>fEJ)U>9?=CG&eetoe7ceQX5awaljYzUTY68VjK8OtAC``#2=)|65Zr*(_|M@ zE}e`VgK21STld$g0SLLS3FVnY2mDqF6vAlag_2kV+FHltIXU#*cb8}#t*PU^wNnWn z>}1>fQ?`NaA;~#AqfTDN-Z(^oIxLp)4pf4!OG%s&MD4MOlV#+#`^h|$D@oSh zg{P}5q#LJTQ!LHuOomSzfv9ibw+~ny3UA_jHnTH?Y8I#kc?vi_FB0%!8`t)J6cQsBfIO#-r z_ohT&Wt5#tSz_eVPbQXYul`oO8w?efW{P#wnS6wdq1!&dWDAdu+0e+AVzA}I|JloE zFD?F`dl~H$9t)}tEA4t2jXuoFs6l%f-N{!%@v*&LMiF>ZJdBF3NO{AQi@(d)(;h}2 zTrQG5u15Q~z+Xn*)>ya>se>Cdr9I5EsDol(^nw}uZ@gggw=%WHv*-azWq83fimI}xcX*u-#~NFRgQJxF%Qr}V++M&_lJWWcpv6qqa+;|n{w z!_1B4B*ZBg-nd0*Rc)~QW$-g|qvh}n8#h~l#TCAo9U-|HW)d5=0vlo-cO1EZ*Km!~ z?o7@L#ph<1y=+g;b8-V~7nWI5(ZK1fK=8I8X%aF?Qm@uU3cI=R`^9FN8!D-$?&Zo;te(h0@kMIDjLYh!y3k-vzY$Y&vV;PL!zb%CuWB_i`- zwdQZD4Qz~HilYP=3qd+mf(OOPgkF}EiZF#tg*-KIDkwbk0-&6$<8@?EXUm{m@2U*H zagn#D!4{H|*PdhIm;v&OrhX3*7jFd|l2GG^oev|8(73p@MjK$B8m0{}{U4NlS5!{f zlmTWG*GDtJgyE<$z}$=hCQ>p5_P9~KeyQ_Do@026-wUic*<9TzNW+?1>xD(cF~eUs^_AKYU3INk+l6M{TtC&) zG_!t0mk`YQTR;&yi7zhn zti4zjcOQc9pVYKQm`yeIzqyM&EYw-#vMyZ(^hO}WLv8X*lY9GV_8ix!T{ma~NK>K> zOWT~xnHoPLP{^_e5ufCRA-4xwHd2m=O7F^0;+~Ur>o9XEeX5`hG}Ch2Pu!DS;dZy3 z`950C5@U-9EmJyVa92w$lSyW>*!6{3vFrU=9ib9B+RS|^*Nt4upqo@#sopkAgiu>{ zO#j4wp-0^jld;Kvv0rGx(~A9Kr`+!McwU!T!?CN-DLCQVI0-a?k%ZgysEL0ND5x$* zsn=xL3~4Mo4;P-_d`^EN@cH5y*`-b@R$+)1ibR`9B zebjEtR5MYAfwP1PHpqG!rIqmihppvC9npJ)we+8;*Ya+i?=xd9H7^|Z{N^rq6#+q{ zwZS;g6t+~o`YPhPM?LZt>dJ-^+IG0k-uLFAT|T`GdG=5lde#ij7QgQOviNnvq5Es?51OqNzHL|hDaEDqkKMM!fj@Kud1@S2VKMi3&nm@QdEJ?I>shfWz^Q0{2 zNsU3Oexh*_hG9L&Sc$VEm*aIv2PYKSzYQ0>^rU&cHtf3fi^sLyFY7TvE>eG zpcxZT;}IsGHPJMUvmL;@;)EoDbhI-7?lV>Am~*HkDdFckBBuO zGBj&eiTcyS$)xM(XE{q{HiQe0c(R$Fe%UQ%Vjx6Eu=_O#sDp?PR_xpF*}2nY7KS(l z?>50KR9q-apwl#Y)$m`Lo2r>XA=3DeD|t}u5@{=NU2(J^ppSSbzJfshSyJe;1cg|_ z0an#24Ngt&R16X`6^uy3YDI@t;~|iv7QiSxSf8%S0&u<`p=~-vns|G|szdj?)WaY0 zFs``*nUtU)2up>eMdP#3moad0U~I0$R_uz7-0yk+&SDG#A zQjh&v3yNb<<1)}<-|t-z;| z0{2XgcIio(b9-ZaT=F->{GpT5p5r>2X+k67*_+I(TX+h4v)@mgq5|RRd)7gF>rhJ>*FF zt1<6#cv5vE_O?hK8*?)mNK-QtEoDd~#jHcpV^qldGODWy^wp!%4*#5Y0zieAJs&d3 z2~VS8;eT^mXR=}XI_rwu&#~m#7+7DNB#a{KFm^ArY$vL@y7;Z6zL}225&g;nbo(n7 z?T#K3ThNfTU}&v#G1w?NM#u3@ELOAWka?Ei=!S>Hnqw@zu{Jv6T6(+GFZUQrZ=eOw43nE&X*67Emd>Kw+grXSCQeY z*n(+Uk?E)%r&YX|$j7WHc_lE7XjU)!L88!)VpwZDIelg^r;ql`|_;L18sa%SLzlQ*#oUrJwPM>Azbh2QR6>{!j$c?aJBJ^baZY< z?h7w~GdPdjt*Ne~H-_Ftj_Jq}iI4NuQ@m#MMDm>YRoTCVi!b`@A(3ggck(69%)SPA z{Fd+@MnI3XF(o)VAIe_fXVZ9V;uEPgj)#&@A8Mjkx>l@hDue+9#9V7QjyG}DLqwX; z1yR3_#T3 z%t3KeZDv=41F}!2@tuI>7FOLX=(A zDEStAAdr~S+zE5S&@-IZXeHEOho;yq%q|nyUM^*$C{!IhveuHTE1Py_GD`r$R&HssK5C(Kk&3G0<>&WzkG_h zbe`EsVZI5AzK1K+uGM~##RV}pDN7JDWD%ZttNI`*u(2fx7nM$D$eU+6U`iue> z;l4oPP&I)5!lg-vE$J|dyh=;XG=wN_o-mUWFB_ef6UJ@x5(8G`3;KcZ`_ z1ShwNJ|ctHVbN(?ra-;^6ZzD4LE}A6bz|(nfaq7WRKVuE;HzxJ35|Mx#tsgMzGn94 zqypKWQZ({@q6-~);NQ_&`Z2jN+7>&I7agLRxGiuy!s@)fQ&@(U2}Y$BHwwL~6C&eN zG7eJ>Bt>w~?*2X~RI^pOHR;Qi?(gu-X*HnxNOm+|?9N+N8BgU(BRAn>{^6``t)8;D zZEhJI*Z$F1SM;JJboS~>b$>nsjon`Ajeu6V)T_T0;E^o@1Wl89p3Ynmz~bPLBe95= zB#OWTp_V<&Zd-VeJ|HtRCE^V&Zjbzn+*V|`5*ex#Wxd!@lHy#lP5m5B4oCTv-tLR@ zPI~&JVZTiTm8*{|k!l*^cvx zFT8`By!h=o@oJAR{Bk(ZIeS=O+w9`nsqOf11;qdqA8IkruM>*zTUzc;X zcBCxU_L+9+fif0^oE*uVkQ?!Y>>Tv?bjG_FZxFtUGXr@|&Fkpvx<=x)!}taGi&s{c zGGg`f9$;BKL5UBL0Tt%>1``o8zR)+=D%|TU9OK{X8ysYUILXQQo@zg<$%XYuSY&DT z%jv=89MA=z?;?g(=@?c?pd!cvtf^%tSAG-An4Ek4;bh?zj-c1I^0Z!eyVl`n=yeaP z|HRSidrkfJ|4{$dqt)-s)!+a2;q}q;tdZ&Je(Zxo+9nFW?F{yzEg78SE3}xb@iJL5 zPwmXt7(dEo<^=@W9{Xg#Y*=9|N}HU#rOb%64q_mrTvQ{PFrR%MJ1Aas&x1zE>Q@Kp zhSoV}_^?ffIbYh#oKGhWD3PcV7GoB27usq;;r61EmGsoI`w`c)von`2vYrM1)K_{* zJ#TQWSCvqX`oO?jLrV4mew5$pN!Qs3E<5Eme2ma7JI> z{)y>psj8>1A?I((Ln-#wNRY@I@_jS7p!&0^aGX21pc+PnuwC*@ZE{1R1r4-g<-rvN zHbq8OHFzWbfaRTelFUn^`lhKGaI0eD%VBx2?BWHNwL(0uryto(%%w9k;^rfPgoZEX1@s|PvA zSJN3<)~+$BI)DZm)ZoHuc5IF}D>6uMAlbVGQnlNwI+m}ef%U)7(Eu>^$0=Z~%chOh zUb?QZ>h%`yV{j|uX=uEri8mILJC`E3|yD}7H*>Gblxuzim=7Z0Nhy(n4q`*Z1~>=vATFN8CYGNN>vLy^?={X-#6gO#lp!o0p4E-eEZ#TOFH5+VYL zerb6DPQ=r?o@J`QkSRJg+^K5o;-PGNTpg_I;ylRJ`Yp&`>n0K$I z-<=`vdhMGbh6*jT`2OTd#}?j0t-y9h_7~23(5z+eSSTyyq1g?^&X1)mY}VJMH1eiB zuF{bIa;bJ*_jsPa@PhBkjW_zvZojQxG|yMsz7BcgM&CK@aVy6+uKh;eEgX^jFFa|9 z>Rl6*35|zvcGz7<_6`0WsI!a1q5z5q{k3vx+}rpRl1jABwzJsfb@l^8`LXXF#^1e` z?<`(C;OCmH!c{3(Vf%q{em1m>=5Kb`ZYks!g45#VHzl*k&S~(SIbAug_|BZOGxp{s zVc(g-PT9F+I_HyRYw77)>8X{wi_4u{?%wOWc4yV(p^;;8cPXs12i4gVy!Paw+!a$L zw&xOyv|d(jXSgafG%|#ZOrZhnaL&OCQL46>nF&`-D2#f1rP~vOX|`Gj_YnCEZEtht z7Q*!M@?b)qS5ctDeRp85@9Iy&zS0i)e~RZ1F@A?dGQ)xJ_#O<(D}lJdPF-&JjND2znsZUD_E1$}2mWP9%Noyl2TY7YXl>QCdySH98% zq9XTQxla$Tf{t|3kBxlvL#{PksNL6O^XY3D@3n{IYvp>(G$oq^nMZtSS_B3-?6}@; zDdq|adR88>?jT|4g!(LV(54Z|@y*AI}2`D)8v3{gPN5=>7bBSL%G_*?K z5?F9@0{T;t(*ycLk>O5D312B2LwL=3vuJz#XZ(AYeZ=3$S@Li}n!DShKOqlXC@H?+Kt7VXI zU5+hKEdX4Ta?EZKj2d$8{)haOaTc_1+}colXjV=&_!09M!=Vsi_kQSN)uSEw1UYV7b>_Wqt`_I4*-?&%8fpsA%{hCp1l0IOoZ_y%} zV{b3s81mI@z~?kNE4h`m#iB4&3Pf(|^YSXELl$SM_?CI9=AAnfPUMDj(Rjs5&gIVy z=X$gFQ=DbMLA{Zd)`Q7*Rto6+Fgvt&jeh|M7g(cq0#!w#= z8KgnL#?N%(;F#L)U(`{+eaiHqryw?8IN4pOhvl4}e28aKFSEJ}f!(=BkP4VQKlhnY zT;)C~*aafc6!?5*e&FCtuk(Cahx2)Ej}sF_ggRwLQ!JT1^BXAdW?lgWz6|+Ja95b@ zAz|B*=4{=RZJJst3+JqmbqhPqN*0Ob+wL2;DHPB1iUjA2&4#~qVlo}wH_PRda!Gq+ zRI0O|lzurKAQ$@{=>q67Y(FXA5e(UhP#}m>_Vbd9WM5y~8T%xA_8|L7**x7ivt&6N z^xBvM5ps7_&Cob*1^|9uojt>A&tT`wD8`U0HC$9@R+7^nQrPZRil19&-|Aw!TZ^5T zY!>hbNG2ya29$QyMmQ~SaSl($d$}0z4OiV-82P3Z*dDn?1o-tV^P)BbTzt=fdzW*M zd{|uqCu*JnXS5a*lrDqo@s)0&$xg0EZ47iqk5O}o)ZxhndD7`@m)yx+>L%TIq-wM6=N|b0-}_NJe}34OyM3vCC52~D80`uEp?z&NqKL|bFyVa)eOJDSzx=#1u=W7{ zUFsElmK5wJ^DY=p(5}XCAoA_pZ}XL|1qr-$vq1uX;dgc}&G6Xp66i&bsN3YsA`_7S zkGX;TD8k3SXW8Ed%Y};3Zv|EHK2bN?w@kAieFSK->_@dG)qZrRx!c2!{ixD^(Md=F z$*V^ATw(ljw$oPi?1yrOfP_E9FGtqgs{WuKh=dTo9LadAdQd-T(+{xLZdHzcuwFla z_1da#(+@iJ1GuQI>f1bEcQDOMmw5SJ%d_%Qq=M-2$)XWw2qW65W)A~7KHF(8mzTr` z!qTB1fK};IS0{wy67B4g-}6!x;veMfw3i58bC$R9H|*RiohG$oGIw3Yi~Bzcbk01+ zULvpTp9$}hD8ySJl)IL9AKFXg35OQQ4=0zxzVGh|JDuQxQ%s$(z2tAabC$@!iEhYk zr`Y}v6W@>zgmT;DPHLFaTG>(78S*_w$xZfhsTwY8562rgyn2&7HVsw0$VuUYVr_?L zFSLJeqM)o@w@u5mX)h6UfGMk3&ASd=PvxUBSCMm9S9%%pt(;{e>~y0jC66P$80YCY z_sVR@-2Dz?w?v9yJQ+Jp4aSk0zAa~hS=(FuM>$*N2mJ(w#+Ft+Dg|c^aW>;wQEHD` zN^T+jXvTZ4RlG@Xsy@BWaNWz$KPA1k0lL%dmSK@+X{tUl_?-W1^EI1$b@W8Rsb zp!i@|?g0|A|C=x|k=Wf8f+qQ$v0+quZS_u_m#DVz_F~a0qS2m(5_nca;tV`Exf& z4LWx{S7RF80$etvF7<~l#wGw`egf);piqIRC13&PVlQIhBHz`iYb!hK{=U*g_=X_BVJ_ z&!kPML(JzO-hq`9obV)_;Y`)2-*M4k3NaS&u(=^QXyYWCs@2Kxrb0Q2 zwKn!{b`-S};S%!wIiR(zYVp4WTu5XmK#_booc(8wt^uT9u#kF+rwKev7e;4}%yT*= zyqvSzPVOYmb#a36N7`$?1O#!gLj{#RxrB-!)G?;}#NAquxWEU7H>RPHb?8zH^b5>QY#9No1{rQ6!wr%-HC|Mf z7K>{MrRTwkv6Sd*eW1nLFT)l7)Y-ACHIiEpaz2pb9NPc8Uv2I!{&3Z|ytA(5aHYyg z@lkJ$cOr__-X~rVvlSWX=P6*LSaOqFHr0tU#>Syk8!Q2m4`WTM2$CqcVM{JQo}cm~7Mm_r_Nff#fUN3QB%rCTYPLT@ zWN^_P;&_Taqf)KFBY_9kx0r>iR1!h?LT&t7HtT~W&5S`B5}UvZ=lj9b;yAO(fGL|r zPj(|t9~^YYs3a{l&~jiy@Ky}2oD3Py+gFESn|+9luMhHZG29s0tYZHgBm0vJ7xB1H z8yY9rQ`%*-a;O;{8M#;Qz7CnF>9PT3?=`rN6Gz3voZxK$U+4BwZ8(Q8|7KlqKj8eK zu+xQ~AU-(s;I*SNgYkIQi=Hbl#QS4SapC^W582S+DEyB*;(6Vk$s%EmG z<`=e!=7-HtUMUnZKb$@9(1qb_o#aNYE2Ec4?|34TOKt8;;q2Q+HP1UV1~+syIA5aC zxfYKnc6l`o)s+%}` zBX&y4A1>`Jm9asL^la2{&Sk(7NImybKZ->wJ{qJx1Ep(t--V&v@V?Q}fOt@Zv&WZ& zI`a1Mk*sjGEvS(3BlDz`iB^-YJyaUYtIeI_EvE{n`%MU{58gY(86I`}0GmmT#2qxC zylvo`f8!u}A)&1MiX}{L2w`%K>8#BSwYEH$CRU+Mge~&?ut`{`{%e}__qV!Jr0F}7 zEyG5r?S2?90?{{q5jTJrQgH!$4Ew!DvgZ@Ga@j(?Bu@ySH8q3oRVVA9TkH~) z-Fi%1L#BmW2pPgxM@;i!W11iouX(|(Sjdy{6eSb;komYll6MOq>uhnwcJOAOU<{mv z(z;aE|DZYz!7l^FPS&-V9F4?`?vXkvJ6))_Vz;wYh?ja|Fcm?D{T|7+d|`H`SOo=g z(s~wyN{Jau<@*)4FftB<>c~bQN?H?$s@4n@hGZ=Iel53zoTo(Cspoiijh?_JU1|+% zF8rPIk{~Cjn4n(KR3E2Fgf`W>U52&=MOh(y6ES~B@@cT#FjEZrA6ix8{m1|U73zC9 zmIyux8azMPAQM6IvA5GmK7wZXch&9c`e z+{J_5Bwhp`yL#3w0%>6s*K0>k8*y;dBD8%pT-C+Qyadu|%TCh=xw0 z>>Xr34v6zTrgFRS5UnKNOo_AkC=CAX2T^(Qg$52qruhaw&d-Hm=k2ib8it9r;ia6c zB1h|mYXbkAF`$P0fQ#P-dxQhO7Ao(Hy}}KrY6?QF%bn#9RW0z(9?0Jk-;&3qoU^Fp z5eh=4X?S!^V5i&2a;X=X==H@GaYa}vxJsLxVi#`8^)3FGI(#g8GC}OgaA0Sof4J&~ zlG11aK43C^-r%y1goi`2AJ6W{+a#h+C^@zysWZXJnHR^_JMY(t#2^8oB|2q@C|s6M z(9z;<*m<^%8&{ChkI)1rw}SLHjSjmq6Cb*aO*^{IxuK*X%+-a4l`EHR@Z@gU1}5*d z-Upo_2TthuT04JzDAtjT?g!PL;ESg~!3n21F-?12PGEU-TJZ;;T~Sw8+_2^EOq^E_HyJdKRA3 zH}*tN)bl`bJr(lA8~S6mURX&!%>0b*z5%W)WH0cCoj>X2f}!i~$1}Ps0E9vpmof$m zHd9yPR4?|S2Ibjy2+{p7C89WpH0Vx8N1Kj(g&~u3`|n8ZQl|oSW3BL)@%-JcXc|h^ zxa*b3lfslV!hw4K-|~S2I)doIl9e>hj&4e`j5zLT{rDGo2krKo$vq@->iM7W52KFD z;2%=D>BDiRFc8bSHNx1X`G-vA!I^SM4y?Arhbqym047-Fa$ic08I{yX1dbsD#~{o? zwt59mVc{4?9>y{BOU4BI$g6?L*jrpC!-r@Jz!1_=Dafr4I{ktyU^odBWKOqB8t&C$ z*ut>-7X648V|#K*4X9_uioJaZ?%vN)0)>!pQj0)An&(WR6YwT)e{RKR zdkx$}rn=NKBAFP>zmV?02gEwGzuQ2mtb<=DzE!=9`@5-DD%BRHtFfzazJsgXtP&_N zb@c6FgM?g&u`#Hz_7$NosspBrwqy?DgkQ`aQx^z!~W4()y?zvvpOCX0!phlg!czw<*$KV*RI4}Y9F%B3fSlU z!f;DwAy@-0CQk_PNKy$kHR@DCaN11b#>jcibD6|Z8$#2GD@TRiD+x1yTh;9_%OMS_ z##dU{lUiqe{o=Qxe~^i~2T2(&B7+?_qVUz%NtpEuNn4Z#9Nb>#h@N2#>=kSa*WAlB z&$Mzv(y|owWyCDoyy2YYD|6V7#O~&39;`d?g66ft%hno?^5X92Ct>7-e-Oec3()dz zGoS5Mg=tT9kMk>}oC|^7?=+WLfpJdk%S-Bd{Z*h*0m8gCQNoRBp4SFo9cMUidnXOR zO_z?@5a#i^WQ#mVjxqOmJn=?X8&y;Qist#Eg`LawgxhjfLX=nB6|Rj6_+ zG^Hz?r7O%!SGYr0h@>i1x)shzS2$Hyn2@e;tFBO;s_<2}!l8btG2CA!V>mHgp;1>j zHdWy~x5DOhg}ZcxZeME9m*@(w%e@Tx9Jj)Q=?cf`3aio;&ejzkPgNM@R`_nZ!bQ45 zYq~;_uCOpw;Y_!}1?dXw&yq1*k*@F=IaiWJ6H^sVcPk7?SNNT-Ffv`?O4u>VXM^!4cq59tb5r7EOmX=u8_7G0q{U15o?P@1ZcT8B4v;ERBF zR98qAqz3&hU17i6XRh=gb%h5@8e}8cp8Ke!(R!=rK`y}`NZFP^c^JgAs zH+LY@951@|*3?eDVK+N9S@f&k@_mlKt2O1%cFPCVXj4AgTfQl?{S!?2L(@(B6MwK|6)r2mnpqcJz`3C4|2y^-=xV_FKRP&Uh$1#Bh;0o`+4HJ53#Vt5%pg+XtlPz<*esnWj#osUvUI z)lx_3VavNq`7XNGctI8p#hlCPg=7{*n0|I#{poBoII;P>e*H2*eG}-Jp<&(nzFO{O z&$iNx(6B=iauP3ilD9PQ*+@Yp$E7Q+*OiV*Rl38iwDEs2_b%{JRoCKw0trL}Co0jP zR7VqST4+rTw!x^KArm+w6Npt5MQbcR@YVx5 zNi^~ji=elPkJcSWYqTw|TIcs&YoB=}1lxN*_x|tSk7UkspS{;!d+qgJ@=~IB3r~9s z{51|);LXn{j2+^j-k3v$r0L7BzpJ{>PT4rAO^wYHDOHq}SI;!ter>yRGUlEhU zA$d}EBVUGFL%`-Qa>#(&U(Vf@q*XuRbFg&cDcMio-1{oM$&QcwH>U}nd2mk_*9>?TY%ehP^m}#?eW*2DU>lUMn2%O1l$XAwFKt!j4xSUNk}9>y zZjn_`CAH>~#YuK90`Q1%OVl@AocK1{&u=@TPy2(18v&6!os5ZUKz?dlW;A~COGgiv zwY3|6l=)vwr#rU~NfbZBwN=g1k6%6mXwWd#id}|Y)bri%+wVHbYf9ZIrO&9LTFU=X z#q(pa)5q7AI`Iz+N$8g>?zGQ#)oaaxK-=|aAO+xUuA*?BW)#2NlEL+hECI>fOKh+N z*eY7|n4=NGmO1gS7f5vW2J?9}I$_=FF{3dsfodE}!ZOcZr1>9LhbB%MAKgEQE*Ti^ z5*Wt495*z}BzQJKzlcG};vY^I9Q&x|C3kvNT}gCwdNrn`@5;q^M-gcq=f9V#c4ogS z>4ZsY=nQ>bx$rjufF#8$o`2Nv0c;6{J z`G!7eVq5ke@G1ImzX4}8BX;2_g_cxnO;l>m(8Bz=`3Nfz_vbYUij5V49f+cFJ*P?_ z`iHJh)5){&?#Mt?j98xq(~NzIz9zr)^G2Zf8V(wsUb!w-aNW_)>>elGX|v6N;_nva zK_u-|?2a8L8Ks{qivPW6{yGT=JJQZDOQy#((x0Hqs@4ANk|l&TPP{N!mp$go+*YrS z97sS6y6ml65fbz4P1H9Udkc7L@pZ5K0TI?p@~YC{)3PLWJ)TNuBgud_ec=id{h~y% zxVh=Of2~d*LC8p^{ijbBa+o5Ou}3^|JXoi+FP6@_yVnNKD6~0j)YJ?a?=e&L1AA>( zy_euWlbPSl`^qV7VG=T&!l^?6wo;y0Rk|!vI0V{G?k%U$4* z_pY(`mfL$W)O6YH-o3Nzy}Ru_zuvpfyH_Xo5>vodra8Ca*gYiPTNM2g0V|ZdI*Xz7 z>UiSEocK9t^xER%%3(*`&gGeb}Wx^hiA~& z$^5I!BcB}xPevdg3pBw-$oXm00$|0DS z8nx<1K9Mdfl&*H<0VW^6&h~rl?VsP?dpol2y|HFsdk@jwe>Nm22-TZBOPk*V$@8 z5WHmK@6QyjvsxWRlEsV1fQ&F0%^qtWGIju_U&5FUVF43!@s$&NYd%G+S;1**YSvYgm7 zFLJ7Rm!0uPy#Ae_5O|9{JBm z$rB!Ia*pue-lg=Upny%bYXJVmv*XgEdE4uFU1ZCyHk1Kz`k_*efq#u9tckeB@SMJ%m1vB z1_F;#zs~TnIsST;NZ#reCQ}QU=`od0XQnnQ*k<`QMAm=SMy<55{i%P_#=dNWf&w9z zS?z^ZTHX(L?jWIE(ec6fo}r@1_xdayh5+=KYxHA3=+KdXkA%>i_b^MS$D9Y=OPsIN zia9{Dd|}z>F^4Qjr-=fx+@NWKM!_eMrp%=UIc*g_zLPU(VIpz>h4T=F7 z*%-Uh@g2eq6DSfb2lpU?!4~Srlr$0nYW@z=#VdsRsAG~Rx5m{AjQQ9WJD}!yGvZPq znQ^5AUeOEhd0DIF8H*CJY4|G=CfX87tg6-Z{SikifCoTqNM1)A$wSRt=f~08q%%q^ zTBz`%Pl-i>1jL)J57iLTwMpLU!D$ zFyZ6wOuqo~)h7fCn32TI*0V>Z3^??dj?5jDXwdqCg!&sql8Y;)D};qFl*W?Z(_i!S zEDY%OmXo}vk6YH$-pn|h8yls;C+5hUkxyPKEvYX(ZX^UOmW13+As+EJkKDYSB;$ce z`xV^;;lhJ9H1USf(V~Xrlp*G}cIo zE-6IR9s!I=Hn^NKgj$LG9Jw`2?lCAzQZ9zwwS5uaLnm#Me!DhgqhIgOd)xy)MfesS zlqte@Sja8DlC#j_)zJfN4Za}ex}p;K+TE2hrQl>!#yn^1)DZDZn(72;suR~+zo!7m zOiy~t-knvyfr21 zf_M2KYiJvS@EvqV-m_#w^v^q4VvQwzN1b%1@>Md4!cNRbSoa!uX|Cid$SRhZTFDV2)Woe94pwU7TpruI)Wquq9GRaI zx9*ji_%cuJyVQi>l7*oXT#;l0tY#;9o8831pw@n7%XehWG_<(i7s3gP1gGwpkO&)G zg-NPi_>^pGs-JA&6?mxdSh)vQTKYt6Ew0=#vocdU7xwyO3OFT~i> zvUT9qTWaq5A=?=XQd6%++-?xG=7oy&HJkJRL4;!o!w>jHPOA#K!Lj_}9Tuz-@k#wq zkmt-dPvN7(`E2f^&!{Ms`DkpJ1wbu3j9q}$cInTNrC;%gxAbwIWZ`Z#2kC{ReR6a9 zOfCYx_%G!`f$PPe&{lTmeDil+3+=4Lr1#Qq zi|?#6jGCNby?N$Hi5i4b+AxPuy$qs{$74r8+>&Ra&e>pIhxp>}4F>LKhTt9q_xW`T zJ-@*J=MM|?JlV=^wP1%_i4v#BEYZ6^)ULQo<)(1Fx>?Fr>4m@uECA2%>J^MzE$QO9=M_1X0_c79~!Dv#xyz!=httz zcUrc}ikk$0G`5&0HRE$~IlU9Jfjb(z+n&N00)}2A`;%p`Qllj$o6r5B5*T^&PnsoI z4In6D18t89H4xx8h1wpwQ;q?rvQUHQf!r}Q>G+x>*%o0!SpT!pR$ome)6+^K?ucZ4 zI-D$86sddRx?;)~Tyw0uCcgcXco#)VuihO>28L4RgL?F=blJ?p+%rdk33Y$JW-Lmp z&NqjQdqY^o$tCeh2zD||jscQlz_kpJztx}JQSG3_#u=m$@Lm$B&SiTf9p6^^C#fh{ zbEO$;q!~69MZ~JYQb^@AfBfRgf_*qcU-2RzCq^_fd$K=QO}gbV!dJQ`R|B7duE`VZ zPp$nqNq!PU_9Uk@Y2vjTgDZtgVV=;C_$L}TgL&^`6<#p_Xd&~VKVNI7M{*|}QZ})!Nz3#L0437@_ z27Zw{qUY3nF!9Q!1;=X<^I?tdNy8fP_+);dv*ps)@8sS^$iiVdP8stno91uIl&pT> z=X9qW&|-ox%Q>NNrW7;!71XC$pcfd#zR`Of4+{Mdx|y*D2<m@a9(bgn zz~;~Q{S2V8Va%1RNFdfhj--8zB&x#*Mj_I2q=xY($Ik32m(@*~gCSBB?vk2yba$xm z%FCSA(@Ug8bL^KJ%29z?%#M#jL_(7rvQ25!?E7J7RvyQ&d2 zSK0S_Ss{!9QEjEF#lSasCHibbmG`^Te$Jm)#GLA^bs+^ghz@Vh=(dXUMTHNZwAO&eUEpR(Vdg7mbCjeoxVA z_=-X`0=jV-e+_BHeYZ*=fa|K9@XF>rb+_xZUK4U%JFS~!S~mr4#R)HMr_@XHrj?3E z-n4r6bXtqL-S~3a)me3Bcj@R^TO%3$^SD4iRA>WQ%^khcFW(XbFw&fK$SYlSyL`96 z^GZQ!yX9wflyD>xZ*O_we0ad>PE;8kP_UA%e$IcN>=kS(BnBYxrrTB;5WSbItHZ94 z#jcA2ohU5OXYRoQ^N>p=fcFsI0XN}azGM&Gb!OLTEje;MITagr6m^qxkeCge?QK1| za&tMR*Nk_CJgN&*pN_N^QR0jeA=gH@4kPqwq+)}u2|AS>GF$#LU%<~^>?h!#ASKSS z1-glH$t%#k>aPf_edndq$ti`2q3-@}eNkO^?pufeCiNh?&GsK*@rjQsjJ+w`&aC`eV&M{V zg)~j4SgEDy{q>y;Prvt^`<>)Dw24Wi$KdhEOzetNz=8Pd=_v1{nBO|{V@By2{0~7j zcGz(+_p`^Ylvi$DEsWGw5oLC66-3Em8Md&b3@=X=<@v(JCxSZLZWsfn&ixmh#1M^t zNe{(kssxR_RExD8Rvfi_NAjFqBB8nao27h53YCt5)QPB-aRb?){Y1x4qdn%SyQDXH zABU|b9s65-4*4Ygq$`nnr+5jyR}|mlzp;q4ENe91{?66g8SR1*=}vd`W^#huxFgH}xK>H8&hHd(Z4gOX z>Jd1D04cw{;^V4Cj*?nYg6}Zk=C*k<7c#D7aZv6i$T(8msz*~utZvP0pJ01bra94S zci2|hVYyh&PomY&-?mod0{$MWSDN|jh&9vgTH8986U>}myiiVVlpMGpe8LC@%Yhqu zfEI}Brz(LAD}&Zq|Ho}MhwcOWdcVlN-|$b~zkFZsKZ{m|??3QQ-v83R-d}9rpE2nB zdA3>dbADNdh~#*7DIXjut06Je^c8!x&}2pS9)>Oo+!&aj<}a+RWk6Q)(@@;4S1bqp z_-kvU#mej1Xcx-7u(EYu!xIgppqYa3)UbiF!bORO z9!~jI0}6TB7g_|BOJH{+$p*S))e}OTi8?CK?_fdoE&c^(!G%u?=TYEW@&nF6{auyttO4d$ti*EA$ zyxSMA$$PeGtQ#mFw-Gka@jd30lMO97n%fcgiU3~^yFZpsn0J{6(rQLWblohN&;P_@ z-zpyq&pvHzcy<`pAXq+>oFz9<8zorfR6wRCbQ6sjOa#YL5QidRP-_!dWQ<4&5}Nxv9HO=rcL8rZJk zHJwP>0S_N`dia3xL-6769zF~=pMC+@Catw-ucXH3Qw~CGGeW_u!(H{NTL>p+sR0S5 zm|eh02K&^Fafeg!5q2dA=z6nmp{x?myWxEec?9#i&B>}PW3yRr{@p$TYd4tOul7)c z9LIg1PlH$=nR&bbn%9QhYyElL&7_~mx`MlDA9cyjVFke_nYn_6yNj;=oIC0pa$)g{ z(SsUX!qX#_a9bS@7nz=fw4|8%gdonsm0}1faMwhuPzp~zW9S<4wT^VxND-im&u}+K zNs#k0+KfIE#N^UT>S9qI+vj{sgggWN+O-YUkgpllYa7U6?5qM6h2&{cWMjR^xdDx1|a|r-^eJFXO+rQ|NR;E+6j6Mm+(&jraJ@Cv4adk zPPtk6}oJDrtK4kAZ)3#&(?p zzAKi;h4cp+iES@PeCBz*Pb19b?X%S=WOY-s3fn-=thIKLY-xq?#W>B#Bi6`e=Dn_L z`(#n5t2m}ctPZp24VD2L>Di^`y%Tkz#P(aO+hQS09I6{-b;<8&>5)6#F<+l3b1&>J zRQ~)tzDu^|Q=$26)2i;3-E&s&vXKP^o;@E_muEh{K^kD(Y5AS%*yl{GoMq!e6>FQR za)ZA)H$)jn`kafZ3mT43r_1WL7Gm^PBA>h#KeF{^w~$mwxP>dUsK@-<-3r%$!gZON zKod7qC(mMib(=9iOCz(ye5RZ-F7aK%V#R^>H(rw|uAaAVowzTQ%_p zk~OLjFk`D}8HK8yK=>KmMvz$^-Nts@zBP5M*~kG*AwvCnvzQgpezPD6P2k|M-h2}X zGx)`?`4%?-Yl(6#HsSU5(+|LFrH+DKd}+>YPd&Z_g) zL7rYyWGW}VO4cp!eH6XKNnT2`>r5*REPPtZ9C7^~xgR~;ajyqGHkhf(9rut1DgG;c zlVU`C^AqjdG!SWX=7wpWCH-1wYPCP>OcA~Cm~>@EavZ;97rNJ5m?etxXYwQtxhCqlo4BmZNUMNBW~}m3o&B-AUqqrkvYH3m z8ttvmT>!y+N?LTEkxqu)^8*CP(53ZeOs-4VKj;!ruB~xXv zt)U?GYv~cj9`oSYVA6@fwMCVMsq+VE{fRrW1LnJXwN9J`JwLix?)0UOh`6!76r!z; zmXvoQ?QsJivS3?LrC3eKwL@CCS=AI2uce@PAWT7V!mNYm1<#c|IUhG)MZPezz;wr5 zDh!ah{xvz4h-fokRbGfI8LqSUcpiLF^VH+ZG&bTc61gHwSUf1FMv{bFi1LaJP_ppscFhhr496!HHPNqHJmH3 zqi9f9GF@0SsrxQ=kLDMcao(2#o_HsjE^zVPfFVqaXcV?hHB zXyMr}5%*srjHz@1ozT=oWF2z3y{3}8DB}ymi2{ZdOayR~SRGDxHYNk5k-GKspT>=9 zAtMcrTN|0(p&ISvNO3zLBiB0et{v3h{ z4zhT~daE$tvxx)Q@=K^s9*%D)bY`>dR3Ifx!JF))jW`?>CZ?eN=`nZPNV8ki)ys@< z@d$DejGnE{AS;w~R`18{VuWx{I|J_&Ru=ec_tb@}bRU6>j1hjLJb}lHMMLpbx>a2Z z+4q=*Fneuhv%5@W33D7c9{;&MVy&~k(p^H_lY7jmyRsBcc5!dLcsymsg5j5-4%J9d z*W+5WsjrElbB?B;Xv1s?8RKV;`CyNrqN(MQD08L<+U^kX+DSN&mPPk*=<#l8cq8b{ zrkF@CKHU&zz%} z)XW2@FZCJ8hfxDP3gQF=@!Md|2M%G`E2`yp7)y~c^_cOzBhO%6Bt$0lIeSZibD-Q> zDN7{mzO1*U*N4+oK{U4bXWiN*D^t+$ASFNthI||u9&RT0%-YS@H#ne`*@tO zs@8}{E;yFEEamH~)-IB5w-C1|JzyOzl3jKUI-JC9GMTNxX3#1}hpV%=ygPcj$Gobi zmNK_6v>qq>;AY{JVqZwX#0%k604?}#7C_dX0%L!~%gR!1TdVq=0vWc)v*y!&%aeZd z8gk@`CPJY7W2RO-P?UTs@$5Z?`ab&(O0H7FA*oZM4=I%R?e(D@#7 z9SwWyMCs3EP$LZDbH%{pC7{gU97H|mj|N`M3l!ilp(c>sHm=)UBx3=z$1ni(hxtT8 z#?&81fqyoFv)gJPioFkQ4>;}$C1;ZnMZY9!bc9^ZH3_VRveZf^Us@`i)VMn_<8LuP zonZ%rd-Re4)G%=+ZEC(CNnxu3A?bPqfAjpE0})q!l_{g3sHFZ5+<_np zvg#0wOWkGF9Fe}R#aujB$&KF8)udpo)*cGE>C%?~asgJvgXLvY!z(L!zo2z;b84E% zT<8;+TA2jJrH^m*CP8%t?ZpSZJFMWu)mIH?8Zo)(b>!apq8P{@~)4WE*2>blc-CJR?4j%|=FN&uE$p>VZ=eENAkViXaj>%gJ*X;~bGZgs0eqavkTGMk>C0+rTjl#tVD^ z9%P5>C!Kp~dvrdeLe7AEfQ*X*322fGfe$KxN^#_?r2fNNhEJJ?gX%W;mORe!GqRoZ zuHeuA=!m{`C<}+$0DDbRW73zzzXT*-O91xX)j1|#1*n-KPq?>ao-jVU>@p3+^3cDWf)3aF4&r7Jt)4czD2fg z)&twO?%u7_*t`!2yAoE1{};C%_j~#^eN1-n6LLHFdh=s;1joIlav{(&9aXZ83o@3< z+ZXY+n6jC?)M0r%LnGG-T-Obpdtf(ZJQ>Ig{8wdcqFd1Q=7|hhc^0~1_086SKqb1l zk>or`$46m!ZHY&I&8gUCe)I)es@v*YypyJa;us$CbzTdXwxBkc#^di|MweYPPs8^q z;c72?`|V)`t5AFuoVe-ga<^ly+t>nz)ioK#6vJM(Tl#%w zwYL@;SGbePMZeln)1AmWnN*%=Sb_DL1<l*G2bueUD{P-mgX_6-wwMmP=umV{@6W9FC+!&A>Nn+xFaDs&mSRK>I`TB}xD|?xiej zt9O_{H$&~~MeksR{O3gHfLJeThbWTt1{22NFBpGD`d1KTQE3N76wmA6CiF+a_`Ui} zPF2z*#?hIwB{#FUH2<6ghh}*en|U~rX!ctf!|F@OCfjiQJLd6AWo5H^`m3+wZJDJr z>b~tc*5wY4_hMZRhK&nby>*6lIhX?N9_#W;gr45Xj4Esya8b*sPA2Sr6S!T+)UG$9 z`JAVh*^luLP=fdtQzFsR%Me9FOY6-L?f0}=km5m=F{s|V%e+sB4i0R-#kcX8u9I8U zydQ<|y`tyeoeu4eczwz5!fmbDE(puouM51)G#w|ZnAj>(rPqq;Wr8Er`G!7mn7dfU zn~`2#F+u1?YDVV4{do|_(G)3oRRqcZ>fyPs%Eb5kDxW++`+UkVG_T<&(aR0;}@L4 zCIlyb!AucOoUY&;BAE-if>Pp#pSk=cIx8IQ3bx20p@h-nNTRoCS7`qZemi#!$s81A z4oWfyrE-w&AdaF*bsu3w`>y9y_d$|DUjRRkZu5hmi7#Az&KC|-h52ccDld&=O!4)M zPM8W&(d_St8=RAb2(38rpEOL)SMu3E3FaWhaSe0fI=n?0Amc zsZE=9M@PE#XQVqe?T+n9)U%mf#)`MvqFPpz#4!v1Z+%$<__E?C0G0H7S+nWYQBHUn zbuK6c#XE^L4hW3Y%hW&tustO$70tUL6zg)i=PAMCzY?B|;xJYGblp5&PWVr`W&Vtp zT1x8aObsryms&t@sj!~rcctKbVsfeHwVI^}go>+dmZ#gzxaqMKq{|K#faH8u{dC50 zGqI{j4NyyJ4i)!(bE z@%`-;TM=g0g9Q6n0Zm@;O2nS?I}%+I38T!T?~9&3jUqKR9Dck3U0z|G6`X8c#G z0f=OZamZG`l#VRJ=Hlrq25Ma#VzM$37IclSur1-Z5CpTtyNeB}vm}0$jF9o-etPd1 zx99Y*MO!7ukWL%Vo$!=K0ENK zDcPs6W27XME%W}Da(AxN?IBe2+Qjpl7K|m4n#jAXnUP;G!+8p?yyWF2b>}zD|5GM- zq3`Ovjo@ugyp7;VWQS%?2FN-dA_#yX=%ByZyHXytdgp<%fM562XV@!c$j|h7L!L|m zwT+H@xCC9CfH1X@>*gx`qQ-5Ti}V;_`CNNYZcgVw*jCWMk!t|yaSNmiZ0iD~LE9ap zP1trv+2%@3kv%9kf4?f*ZXbuXT>)!wus*JnM_k)hrykSCmG+?AJjj99$6v@b?W1ca z;yODK(`{@?*d0Giyyh8vQU@Q^Hj-Nf5OegnvDBPz56aD%9B9Mq%v283Yc%kLxkI!* zz;VA>VkU4o8+h`0eNO{V5}COR2rYw**w`H#EYjxqgoBA_&cJ(ux6sJ z_ft$HT1<_&)Nps|gy1;GkK#Y*>kL#5?Tn3XbW4!UcXuQGB%NwA9v_NjQu%@$56?in zN(FxcLblHDZR4vR-;gZ6o$Q)Q`U}ylsgJmLhfnaVHk8z+nu{cHz!87BV2?X;qf^%t zJ2>pN+4D8nI7A39qHwq?1jWo3U1ZXg+vdW5$nn%>bEqt2cdD|mvwDkV;EJ=#@>_pE z1PRX{uur!G;s&pj>DIMvEv0#~!oM14b6UjRp8`NB~8dDt_no`Rd;UNJV|MVT0z-(114ku2u^&&AkWxWYDx23`yj zix6=K#Mo?LFe0Y~)TYip`0YT}o^JEU%Y0?|wTy)jyxw<=)Bhh~zxsQFzJ}3+@Ey&Ang3z8;0kv}x7)ZKFKdF?r?DfPSt228M`)KriN<$ox+C~Jx~n;U zF;ugSldTOE{~1ZlFrk_w=wNrS1{dG+wlhL5Zmij8oqB^co9TA2;$;pJ;{NuE`Q<5g zGUj@qV=!=wv!F=VWGRv;r!)G`Ty7_vt&miT`OAF6YrAHei3c?8Jv)xwEXqzyt3CJFec9up`=`-j(oz#TH$ zV}1l**eL9^wPwJwQ5EhKzmsrRfH$Wm&OOENE)@};q#q?rkU6%G{bwh+oF$*a(&aks zo2fG*A*(O^T{c|R8LKeGDA659xMKN6&##j*hN!S)BNHw_>}Hsv14*Z_&tX^d$4;>F zTlpNjhMHwaVi0;(29hyW3Xul5PN?4Yx}yB8X+4{ zttmirkdYfdfGuefyY}EbEUG2+0K45QtKBIR>WpvkuNh~uvCw^veRq0ww8%B7Kf;7a z@Y)8mKR|`&s=`yusq2iMgR4S)S)RJqeChfXh#{?(o8pE%8bEf8NLW4w;eZjF8k zUOV*%cX~NIm9Mj2SSrc~6WB79&xbV6&8l+hUXN`Kj=RFIyD8I&p}9(m zCHc)0(?rzwEp7y&|3oW}oFb@|ACC^=c|wbf8JS#tXSD8o#`$?J{k^M0r@F^HL@EQq z@w+*7TOZ+XqE%eP1euP#kLan^$Ck;dq}WX+g*mfqU_L7fLwGOR{*0f7`ZP+n0uoud zM>#_E(b&0FGDsZW;l#Hi&YoN154#RATQHod3jFBn%YQ9sC?0b7W=#ZhhB*_jDUFVF z+#d<{3NyY{@NuRm+`}#9<&Cs_g?!v^tH#77hHlj|-jtlpz3$(s-Q~M=p^oMu8HqdX z3|C?h&%Er0iy=vKsI@x3B0u_YanephXCt=8joE)9*3f86Px8mNSvL--mvI7z^Ozgyzp}N-JdXIwKeM)Hkjwv}e zfhid`fkVj{Tq{%b8)a{yvkEgv-*-+B{92p3XRo!ug!nydunr6l7Y4-KO$C<%UX(844h|ua(XXDsupRZ1eLRYPU}OnpR=_iimjT_Fu|k^+`A; z+L|CMe7I1FA}4VpWC*ne&pgI6%RFUoK)@nY#>@L1pXU_dwyAy@gI zp}9~*4~JUC%+lLgml$Agb!L{vh@HSFkykr`t$`VA-=*Qe>K(y+7Mu4l6$NYwq^W)> zfrLIUd6nNCHnhIH&Rjhmq zb@mE*D>=jHn=GxTdCQ%#9PJtrASHJ1m-vt6HKmK*5^Sw0<;1D`3n_}|ZNq)4HEj5W z)f!@kPd2O!`x@5DX5?=oqPn&z@*xeN`;@K@Fg8gCGHo4Zqj!6nMUb^j`Vw@5JfNY;ignc$p&@DfghB=-*p zm*umsfN;5&AjKyx&{6p$OEoUrDQ*huWn{h`e~te z@rPiB@F&U)Dyzf>{epS%6~QxCD31Di!q|oRde)QtZdAtotZ`o>}vJRVM&ru1guEMv_sZTlLHND3eWCQ zCkbNvLb4)tS#sT}>-H_Vg|GC_jn)F}(q-TKm1pNTv$VgF&4afiJXxm#^TxA z$WhN@dmA}|e}$WBJ2GqJDC4Pp8#!`zE?Cs$Z5@IrZo*ReaEW>LOEL$d;v$|y3|{a4 zDaX%v^ChAItnKp>7j~!cBj(>h2Zkzq366bubLZbo=HHB8;x9liUCu`{^J?`_+IGmj z=@$S|P{JBsu*H}qaDk5dgmiIG7C@G2tsv=rm&n$d#{yiZgE==-iXk88UKK5R4kwjI z8Dj21!-jGG*pM5S<$&sNYhPDqRL53Z_;}|O6Ov-yC#@V28XJJ2mM9 z%y-YR=PcwF+4082oBajx=1Trm@o#)VSD+e{D`2A~%I(%Bj#(6@{(b4dW@)io?1A1Q zfdiy7NzON(a{v$$UnMr=nRBJ()fbAALLY`eiXR|~#%6Y%Qq+vz&hvIQhkU;ngv^Ft zd`=BhRD%^0y=ch&snB(tokQ*T!IEzJ9pA0B^tR@MnjL{QxIw*gELZS#*?W%~&ddKh zd(=sUzTolmD<(m+AhItV;V#vEs#R7c=Lfj0vi-165$2JauYRk+b`@T~cnA~50L7f@ zTk=i1Ukk~OjSI)qz8glSpPQ73K3N@3ccsGK3#&%wzd#BG#6Yj{m_O5jLkDzVxLWqW zk&e)azVzB)-CupTh~09@2fkbXLeHGpKoRSR@76!cFwS_S+Cnh5$9Ho5e-wV9N2)Ws zyh1SgE!Rh2P+jMZrTsgE*E)57^WB1>8WJ$IS>0F6 zb!_yI(?gVP7k@57^JE*X>fIo~h%e(y!u&kL87}I+#gFP`K}VY+N2lI$MVcbVazzVy z>VFhs*)%zpE2_vbKjj1XB3;{$z}b7ukmk%ehe3-V>lG~h$1GX;cUNwtn|TFGC6Z3v zVl%v8*1jlQ4C2#k36umMp(lVfVjY`M(m68R*;@=e)5-dxu)0#g z;~hsj#8yd?YHnc39b%>t4eAc#2G<~KN8<%c#!Wgtt;8KYv|{uah)PLf^iU@`iyVMP zGQzOm1eABnv#Lp~vQYoLJQnmTAgqw1f%u53cZv&{(4?n-AT+6U4iguS`?+-UE)MbF ziX`k2&%DJm%e+tJBwF2*RW^~21#~r`8)+3G6{e6_j_Bvnj#UZ2em^ZJb+mSu{gC+e z`PTYci28f(=@WD}?J=hlgcx$`CxpfY{F12m)Wn3&zI6Qr)i89`*D^8mQNnrV(6|3xtm4hdKIulkt$Y7d@bIE>wqx`FVWzYO zcFA2TG?IL?v_@kza4g%Sq}>n&fV=b#>3q@9zWs{OR(=C@s)u#kl_(hJbXhEdSXRUF zujAaQgq8g>oV$W&2EjS_G{TJH&0AH3ZT?S5AX0=38`zHTVX|qfIfYGLME0NrkvJ+t zyU$8io2Gbkw|(hFFlbxmy^Z=_xA`tQ<*EVf@7Xl$Z>ZdZLNu!Wlzg|D+Of06cG z^i#ZA+i^r&#e*60h%)M#VV2sMGRH8;$R62}yxl=`qhjF_;n6p=GpMYCq4TGa{H2Uzw6_ z7MgK%ju(0aKW6p*4O6_OpSoVGty^qBwmykps17UU<0iqXuDm6X$NR_CwVmgLzQhhd z!gxb!Unb%L59Ck%0gp$B0p*iNm{DDYlON?F<;?r7sXX6H@Wb!8;?DJvdnrd*X3(cQ z0xUk+^lN@6PS+tKd9QBU&r9BPb4S{fJHznfkvqAO7n{#v_sIlJ z$A2zUD(tZQFb@*o`kC-tiWOxa@H|)GN$kk?=F;S&a!6W|2iLM1XM8V!}yN zBW-}_2x1n3<8{&7P3FSeXewFO`42oh)Z#p4wv~;fpE;eH`m8m#zuIV?>a#Ph`6 zoGxqR8J6A|=6gtEVOO+JxR=a7jP4O=mo~-3UO9=I1p-1m%2MZWXS>|FR~NS4$rBc+ z$l!`k-P^=Jq{|+|lUnfyqrK&S-iC9m5*O4JZEJ5XU~$U-QZCW>&TKG)BBbiUxT@UsPBx>bU1Bq;%3j&FXC!zMVL5gPx--IDd*JQfv8W{kX+$_r1 zIl^VgzR`648eP2CzGE?GiOf&3?9n|LRQjY@#A7w#oo4Ut@$S`{?Q&0K@GPEr48pux z_p1C6{O(Kzh@>Oh`rBMQM>ofN#dFk4;s;%EbkRD%_uh;5O6EKE6UpMC@5*>2haBqg zQe$3|Psl<|7MB`s!8Ij|C+pFaQlYb!B5LjiA8?lTB##3T8G4*u)4cZXpK#6hYPfP* zC7;5!V7ea85b=eAz!32-O_k5)6Y+uljA#2vi}aNCR%m;LCi>Q=-kuPEs7iCMpE|HP zd8OC)ZjOfZ+k;lgi{>(Yt6SJk2Ob}Ci6`-9vyNr8jA5(e>YzM}yL*)k#EjDhwrT^i zbCQ^2ip^j3l2f5u;>jzRm1ObyKMEvZ%Zo>D$rS^BG?e9pjxw4bpbfRvgIaUkQ3-)F01K!-vV?ML{)>&srV|y(&yI(79QmDJH znt!C_Nzav6=N?_I;+4SoNO)ky36A4OddKfO)R|ohjVwOt{$WV)q|srJ4y0&2R|hN9 zzEQ|}vg|+pl374coaf=%`&HBj_{}g#>q_2@AJfaR>{L~`d`sr5R{~Y-qU>Lu&mQz> znqKcQXZE`f4~NC#D+9@$@$l?1D#9#gkiAmVR+65juXzOe71Fh=avF=P3} zU}??s_Z3Pt({3L5RPbfdN)|$@SaTpqaQjdvF+%nqzJbT9H1$HEFzu&RMTg%vG)f6C zs(0o3h;mH!Hfin?3^VcFBPEw{cl=G5xaUjMx1cKa#wy_k&686k*a<|{Y(?EhEZFMM zEPe#?N_hWn`-bhIyD|To45JsgX$lLAYfagNCqjxKZo6?(t8(IRe-b_0iT{11Bp)Ea zFoIp~<;x3|RWnPkC@gqfx+5Il#q>x$kq9Y5~_IfewC@`GRL`jJ5gU1l(YN@{?csdx%@2VIl(~td>2bYdb6!7SfodZ)BIh* zlCN{TN@Q5Go35`a;GV2Ilj6(@nt<`J#{>E<;LlINVl(4UEky`lsbX-VzTCVtQ@)bv zll^x5g0b@D_yr?r^R!ai6|Wy_gXP3t<@MvLyngu2?0>Z%>Lrr-EPDp9koBBL&zL($ z<}P{Xb{)-T1A(M2qH4If!fLtqKK)HDUEw`(5&&G*1rd7p8J5w2Z+NW$)>*4P!c~oZVd^~5NM8Qz|0avPZ2~>A%Wgu6dr{z zE{BO{7Q*Kow`gTW(W>Ck-9ZW%{D~(%A9imN4l0x0acIBnj-_69$7}e^^SWOJ+fkQf zb~@?bZFkUX@>ECM_WNiD>J@;X$WR(FHTBlRw+`52JaB>ZJ!TosZpPi68+{KE62nPM zlTdYJBF|~0tySDe60MTcIiWCi60HJXyN}9#*}IA6_SvVgnLa&OZ2Kg+z_eIl??dl! z#mSvNB^H`N{9mxQ&n*wMBjt#yX1jbmVU^QegkUXGfo#3`iEfVwiTF^_Aw(_RaY2@B zWdzlUGz|#W2e-0Tf$~PTx6$3MREqRO#M7Azr)aLm}$s~ zS9TYqH>B6ehi21aEwihUv@UK@n=#n^}+$Obc@76C%{s4ksgV+mRlRnuTPxsE4;s@_AMN ze>2B*;uH0d(hB1L7dftfe<$L_NNh6y-5l40!NQ$Lx^Iw)eT-&Xai*t&i7#n$P2&&( z>NvA^l00hftlw4UI$ZU{Z}XQ-)xzt`3!?jj6!l~07p<+w=$K6d?&oz$11>=US7Wbq|~n^MmVFDj@IO0Ae#0T@uhvJg;t)P3e zP$ryZ&$5{%{IdS%z^a17;`bW+dZ6}cAz|Wjd8;&s3JGjC5#4o-&~(u_r!F>c!9dF? zs=b!Jg-bsVT^*7-(_0w_q9NpFEbO(y@THYuxmLy_MJZu~wZ>3DgxEx9ds2aT>ODFIx$%8TPzX(y_5MP^3MC1Feky;zuI{C^AWr(;`8Pd zP&`zs%q4@R;ho!@!x@fK9Z>*B+l`35i6<5Gy*^|9iaHusm zohEern~)OXpW#mx>zAdNE&I)gdtoKplkK|0uM8)`&1L-d8-dkg{{Uo__%Dzd&+!+{ zcM-HbRY8fU@30Ju{Z_v>MEGG@WnHb$(i&q+cPY1EkAz{D|GHW?+5@5HWuT_RmmTI3E&#^Pu0 z8OHNWf4ULh170<8P57zs%O~P8SY80QdRv6@9>5kQuxPN>h&6peOWLP;K zROG$~`5^1e4bEKS2BZ9Ivho<~cblNKpVd+AOl&HRO*E&|2s%43P!x87S-tHS>7;#K zBI1}uzj>;bFC@!ueLExmJ&Arn^(THaW0BrF=TDh?%jKS|an_}siC~M+*bvuwiE zX2@*X*M*n4)H_ZpzVVLtNbeip((|0mfdPsx6wlfw!)H0Jak}C%dijsTHhToLRVMM? zOBQe5D)ZsN?uh5|VJ8S>>XBvgVJCArK({zo{YIpswr}xk@-^`&wmEwq3V8_Zc{dP; zumCWM6kjU{ew*I6J|;Y(lP{{}7ST=(i4BXiUR#p7#;P2X#Z#`QFRZ}o6x8@;8IjO$ zia5v={6MWWH!t+AzRnZgvG^w2Q&!cfOffx?Kp7I_XWO`9{5W(L45DllhI@8FbE)yqtvsTb!;q)~)!ce0-(mBW0Gol#hdOIYE>B(B0g9UA4U z;?Kpt8v)kEbu*fPWL<+pW)F7;#5qEOtg~+VH0^s#-8Z}(g3T8v8k)oeUX9}+JD8+* zQOl#p2f=@yMLvG*oFtkNCmY}52NYKYJ@Pg=VUE zZ;n}H@7?U(n`2&-dztwa`IcFQeuze*2}8YZcjJlF7JUaDtqitrT=>?|g_PwRBQ;*c z%~!`y>}PCvINt0pNS%-wa#KrY$e?}3F*t+vKT&g%g&m6lw3o%j*TLi9Ud%$1&S)kN zZPvCuR|}=fgho$AB_vRjUS5#5lHUyonDZu*l$2y6V>71!oXii3L9a_Quzu%S57tayNs3H3LR#w78btXQ zi(;hp@2Xc6vs-7W0K4n!8Ti!gk{p^c3%aNNmK!1A4Kq|ums?)$q<|=rvTLl=DmxRW zF()7o3yGy>QzBldpAf0e6W@S+MuVVs2KDhnQnYSx+EBb1)sEZS6)2Go<8&_$N8gwE zb?4E&kDT~=Saj*EUq(@=pMb^)aoJ*Sy6V$(>fgvQNoV94r9*!>MMf%qOtPvoxAgVZ zatm899n+iVadjPem*%qVT;wk>sChxG*P=d;hXqF=zhc8&XAVHadvGWLYiw&^`teM$pDB%vkm{I~GP#G4lQ)K{_X0mH zqEvg<>AhW~1Ua}ulo83YsV`+P`gx+MxNz5N8I10pl1BsbY_Fb76u?`T{DHQXiPFxW z=saX;<>HfNvsA#TE8x*e-`y6VDZtkavss@32fWAd^&`6o#4d5IzwX0WvD?);8DEmk z=5`!`2z)<+i(uoKBvSw$Q%OeAoi<;`J0;Y5?F0dQk7*dfz<_wOKzvTdLKLu(E(~y$ zq&v}d@XB#I;`?6BjCczI#z5R{ieE6E=Zn9!iPy-~AxI`Ol_(?!Q`OpFHCn|9(&esT zEq}eKE0vzaPZG>xWEno7IJy7Of^!IrbRk_9 zvH2v6mx%>ccv4AMBC8oEsw6ss z#3bStLV``jC;Ubp8IA{y^0*|u;Nl8hxuB`qp2;(Ye`RCJCC;6<(^g{bAx{{)F&5 z>0=H8Ou|Q>1W@%tpSQb&S!r&&&Z?t+!OY>DZGr?P_EgQhW^h8NX6(8hykY7>z%2yICmNN%zskh>&9$N4^v zOJp{p+u>EmR2;qdox4Bo3Hlx_I>=e|JAoblA~oIN^y-N3K}th==v`=6cLjzQVT54jvZb)=QysjuV4rPG~-i~`Zuj7{Vt8QtMdU{~f zsD*8!_bKd>bbcT$R~2JCx@C9Ci4b9w(c5TnVo|Id*M4b`bUQLIpRu*fY8jq8xIp2N*(YE6hYuJr0V8Yd*oh1fcLr3JnNhkxuV zbxJ?>9XP#|@yv?P8-3B$rTo3|ns691)E7~^Qzn6C-P34w+{Ps@(Pqbuxi;A^Hp*J6 zHCXKTYBOCwI(2V+_Gm9OpcyDAA;i-fM#T$96=+nuclrSpg*VXrE>f+6WHlnJWR5`q zhDc1uivOB+?qHwG=^X;r3e7T?si`sVu4O8jgp*{yU<-i+BlQ4&1S3!%Qc~qB<(AF~ z(~&AO{w{SF9wBcCCzaY{BP=XKjr~S>FAt6_}zPK_MjU`}L_qRTG=AHETvUpcn`^^`lST-^U=!MeN_ zJ<)3n_HuKm?Lu+F+hA_7O_ga=#HtFnU_JyTX#YRFFx^yVrvEDE5i%|aVT}-!eI3A$ z0zgKvD~}i53gbqzk_=8l-!0<#O&O|9Mdo3O%qt2(=V`j6SsU3j^&%(b!)tDNCKk9K zxEQc%764O=fQjO!f_)F*JkLl7+qN+&8w{$P446(u(=`A_CxqPfp*Av+@q%b@0LH)B zCJw^=z((Vlk=jJ;sSJ45_6JYDET_8OwI=QiuOydLqEZ~g18h(9D?vPR4&PvL>}y>O z+u>|Mv4%bYM`~J(Nyg#;O>8C#~ba-2ItPo+gD=HFt6@D%~UeJvS zKdGZ+t1?HG15w!}mEvomJ+7CSMSroxz~fZ*!ecE7i$EpYK8^R7Qf+uIJZ^-0{_o&% zHV0WX6seiTo>?`$!c>tAWVN`v{6sBdv4$OMMnuPkB=g$Z;3kp)yb@gaCeqkCGf53a zvC185Mn@|;Q$twE3*Ro}F`l6g2f$xvu0=5MXoHj(9Y6tD(1JDu2a9d_GEZlxhge)FN!Azk{uVja1e-ln;0AVn2&sZob4Kmr>82SRV5`$z>>rNMtP;AKTY8LaHDTuUkYvC3Q$>@u$%P zon+fe9x~@kOb`46gnD{O>QUW28ecJ6|E7yB=sU2e((*zaMk-4~?K>QIc%W^?T?!Lc z(XSVB6iHnEphV^@`J#*~5&N*KVW$AP_Rc%_8#~0zC6j%q{k)~%=%|Lpuf^dkMj%;Y}Q2=?+>0p_J+Gi{i zlkjA|(}2V8(E_N?MJTFS^Sno!e+*dUkt3xmJl^2G8>;wI^sDU+C`vX&+{Sw&F1>YU z{E($f8PU)KAIev%;c2QuZ8+weE^%ppk|qT_V2fOf4E?i) zZH?uY9^6jPBktu7hM{Gwi?hnz)#Db4a2dg18Q*o`wZrkjk#pqra!r=$3oRKDo#r+^ zCQdoS!;3n$%HV4Xf)$;TivO`-O-ZogdH!-toc?1rrD(%5HaWl(UUGmgvyzY>VE5>2 zz6R8@zQ($Lv~Lcw`pxj~kk+Ris@a%!YF;$W$1=1<&&tZrRUDdi9(JkxI$w+47xZ@A z;;)E9u8@Ysm9jud?HXz$At=r+VgZ<`%-S{ziI`oxbf=Iy?=Q1JOy#*k9Axu^IEdz9 zU&LX}+d>?sBF8*ZDI%-VhWf#11GyfAbjDW+Z77IdW=el=iNm)f4_}Ttz&M23*Xd5t zV=iQqWtvY)9U9!RwlGH?0{H2AJg8*Y&I)D>bEH?YZ-V9qC9l%LLm(t?N9xb zg&am$01g>0d`o^ohfwa$7Z>&x>0H<1N_j#kMWA+-l8Q3Z@Ne3ck&?+a7dE*zR2ih+ zmvNLv%;0S|a(_#si=zUupQ6MfDPVm2Xu#bBAw032Qa4;-&DA!#FR}_6-RDB=In|Yg zb}gZm?9t_PV6k0JAJ7YF699~ep3wjOO=h33AJ3;{HU5^@8{AKWI2%W&wTEQ#Ulbz1 zi*D%t7F4+UdA=&dfCqy5^~V@IfWJbb-9)oL?EFBmmiXM8q44 zM64R@9&-S*U>7GK=0h^^$QS}Ef^gq42fZxVWn^b4gSBXq;q|_*3smOhG1!<3!Orw^ zGChlb$_>dxQYUF`K}wRfbd0-6mZ2`mNJ5Vz6Tbtub|x6RzbqUt8M5zttIe}i(9lvY zw(6QJqcIh_jAUkW%ji6b3(4@$HuaMD3}mw#hT88}+k7K@$o-yI0QtV&!ik^}?~h|b z?YF3by^)Z-wmBV%IV;V6x~!ukq_YLR!}#kYZEX|aO?jK)W`97HDmHfzd4>6(8gsHv znTcxb@xH|`aiY5027C$|34z>ef7vi#e|cr#{&JG;FE}g6_Hq;-SWcUE`{gr!abcL{ z?H-G{BjEyVi0TZ2$&|YnVCrNt;o$A?See$%%{)V=xicfA20?FN5u5E(#}_S%ka^tv zW|Po}KG`gn)F4G1-yy~z1V$;>`0d*KWNs2A4W`le@prkwQ+_^h(yXXSQ z&gF{Iq5mDxNTmA9rGro3n{c)W*WSEUgxp(nHraA@=q9e{o0+xaXm;U~Im82~wjWp(ajZhHpI?dE}VJ9eLQ z+m)Hy8%f0`WMGlbZFZot>}8r|f)~Q+F^x#NOsove9Bn7|jDf?_iH>Bl%WaV9CA-{< z8*|9}BTSP3)uCO)jYf|rI+GKW@iYBMu9R5&ecdHG`?0-jR_vz1GpHF)0 z^UeJA`QF~^lX-5@^|?+5nwe<{&mOowzx{#E;Vw;^u-E#O-Tr@PeQtnkX6En-#Yf+g zcAZ1kCk(Xmhq^vHb)Sg&+iiWyhISaFwL-(GkS>#X66g&x`Gorbk4B5qGD!OHB;9{|IjS z5YW<$2;lZT^JlM5`d;j8?;8_x-&jX8qoa=3OrZ0FA+R?6+n`QTuCK8&uZ_MJv;AC` zV(;8yMx?Nk2qV~EYw8K{eqCheqGs=>s~sG!ADI0VC!zw^-3(UY`V^mJ$y&Is9UYzQ z!S$37+(YMwC>REeJ_?Iqp=<=o8*)%SKG*x_1H!gYzf}Qwva~e_Ab;s~BG9f)$wuS> z;@zGsAf&0nXxOn$jko&{`;7Tob{cp)Hk}(N8O#H`X?L9%`lkG;z66l-C6WyN6KDc= z@mkzslD!zYH)MP_a=uO3Tx9CZ@iEdeDZ{o~osOyQl`#Grwp+iZ=yjNjkustU#$90} zqCwhVSA!8Y6LDpiM-Lmeiw1BP+a1D8A4)*8bN7nEgpx+zfhbMUmZrv=hiQb-;#aYB zISn+hU)!qk=A;+sYjOHq;8Dt?$&v221ab2BtKTNhdF(3x@&s8e+q_^^}6za(EZw_wo=3ojMnIEsp&O!sgLU=96&Oo{&1N%GCr0miO8Dti!%{zqsc|aIsCLZo) zCKl7U>T7?R1;gGm;pvMr`PI}&^pRAVmL)2ygCsuj)#W5O@GW*Z4U)k!dT|hql#R{Z zIRuLBi(cutQu9N$C3Dd`iXi7kBy)ylM{tAv0#>FoHDpQ%f)&%Le373iVWVbB_%bzo zn^I%Ewy}|C+uqN%J?eLbsr~G2-}l+wWjUc!%Q&0}FE$80lC)>g__IrDUQ-PQ?hdG1 zKWcDK4>e%0sw9=}oxz=Z;(O9jP>>oMHkTQ$?y=@zy0f9~h57%9s$X2+XGZFtiyow{ zAUCZy7cp=a#mNzzW4W4Xa#Qm9!b!;@pEL^>Oo97;t&YgFn!3+@HFYnR6i-{qSO!aY zk)s2iZ;@X#mGHA$nYRXaVF$esJ1wS&-U!Av)?Sa<@)YZVE?+~sr|2=LV#?hY#b6__bjS#*KJ%-r4_o-jk3CW%}v09winYc(>+F$*| z*PFj3q`Mi-D*Y3Op`2X-+E#*|R7P{8wku%sFj}4!?i#h1o5~1Z7#-`n7oX)g#^e#b+ zkgx|K5EL|G)M&&;gPJ%|XOkJ&0~3uR8mtxC)QHt8k{K?F5S*kk*^Z^x)ARLmYRQ^qIv8CPTUF+YE<^`vM!EZ2Q&6{E z5k_#&*_6Aejg}@Ci)oVKyu21>cBNH31|!H+l@+ezdBEX~l#>?dsTC3mez(jUW4hCN zvMe`mqq;C+8wqFBNR&ixlflEG$$XD!vyL(1b78z=o3-m?#v$Q$GfEjPX6A>pJ5qCz z3?$r#|69J`z9r!vR6+O>JC^Ri|P_RaZ?EuHZct50Of?xs2w$FapH0fhoIw#Ar3mCNcpHKxYmSF#7ad*U2+Ik;Y9_xNn42;OW)O|KEl>P0fnrC&aM&yY z2=!Pwce}|12J7;o*XS~d>X4#umRXFRfKS_Uy=se0CtLcg%21tNX^zzbyW?XeN2SUU zJz2+1qF%Klb|`;EDdr&6j_5e6y&CSsO24^1?o(-}@2!;SANSs@3~r-*y}I2mt|REt&J zLP??Cd_!waLU;>r^?mDBVhN8V`w9`MF_NK@kU4~Ln|9AMc6wi%OG`{7u*8{)<0kT0xAKakqjIZN1um=Y z3vP#fwG)1eAyRPUosV6#lR2?hEsPa2}Qs9Yp`L$Zdy;7YfujbNP ztAKzlh~~n~T5G3U?Q}N=&5wh1r)l@P$s02NYW);>YpCT?m*MOS)O|V66*+#c+KeGUjV;ifkvE{U|a}b}&AIF2w!cw}oN4`AC z7q$U}y4^0L>~xdoc~HS6cuc;d`rZ@h7L=fIi^tPLu<@ihcs&=*ClOYB}0vpe_~ z>oO#UY^_s8*b@uB=EA8;aCc|-+5t28Y#Pg`trJrnUBO!$MHCUdnqxMnZFbh(UBT^y znh;iiPI0(E=Xv-;iP=N)4wel5MFQ2~_?H<5w$$>zV1J&n`J?xYL7Gcx`X6UODAQXj{i%*24@yh-%B>$$vek5!S}nB^Jt>x50vXHSrboAGonPl& zhKgZSqr-uZ0b?{}glGelh&@JD8!1zdh_1fi4;u@%!8W|Z$YMg+hF|Elo;??gZapQx zB8S~=O-FhFW;kfQt^%cRz|~goI*L-ete>j%KUBZ}rhdPneogtc@hS#j0sHdx5k~OR zb#;tRJIG4$5s|L$*E&uB?yWCX3CRDlVo-JMm$nPN*h;S>a#hVhy7U#61cyP(M>3&R?W>|N{^va6gAkZ1NUA0(<4TSzIGz!e94 zD{ulSk_$q^+Skt{WQApoYUu%^Wxz;Gj2nsK4MxixS8TP%<%pba%{caF(2d7sdHE>l zCZpDNg_WiU|DZ6@ZaKzhyRcq9W(sR(VC~b7_20+Na=_*BM_bihb;#6Yxpq`^Gf3$H zL=nvnewvWo*ga~w3=2@#1{rB*e|8>Zs0<@9=dUsgeg&omlA>z%YXBXJ&YqG-4{}Jh zX@PW)5!*g7ZojC3z3V~Nu~>1deLl8zX^tOy9WZ-Rbq?0h49nRn9i(Ycj20q)=riqYd2&DtHUiTi$k&PuCVhX>yeRYqs1sT&l=PD?wWwnvOS!r zXt%zrzU~XP6dKNbRz%)pyD*G$j;ku9RTaC~y;E>|yKDPXPW{om^mev` z_3AXCWRjyS6l?~RV&7ZyK4Al~)lTT4C&D|Wlfv3gRt zWiDv@3OHTGMd23Lm_`t?6DL=SEFIyu*!PBq%tP>W@c>v?cBh;3M5-{T`Cs zTaKL#Gfj2iw=+zW3HAX)t+g|7TixS{aJp%%l#{|3`R+F2<5Kn3m-t9|d679Ga{*tUiPXv0nT;*8 zq4%G*&ShEZu{9GtTIYi@%5@iO9Y0aSJuZ~sddZK_Nuj3Xy~J6iQkZ#N8BH2zw7g=y zin5sF9+6VaJ#14_=MzA5%5QCF;|bN>S(#}Og^str zFD2q;v}HDsU=N)1XtLf_P~keT?}UMaFQt3TLoDrbb9-u>6{Cy|)}8Xp*#c6Rt^G5) zX(D@9M~CdRE>)DRt>zLq`F9HOhEHByQ5oi?UwZ00RmiC&kcc1^YX)x^j+Yw<>ATh7 ztY{MBVTCTtIfh^zHikuh4A-g^LJFHJ$Sa3OX70ULKS^833d)EtEw=_$pt;g|Q37UD zxnW*WDXg`{*55duWz3Un!*y$$BU2G`KSYJH0IZ*@^u|&H)Fq9?D-7|9;0#w7mIY}j zi%2#UF(~dPNou8Nx~kkak~_B8PZGP`2zjJrN91H%A?n|eUn>Jj z)iYyjAge1dcg&LRD<)&YQE6==A#gp$Ziz-qtj(v$a6z;`OduRzLYHKWTMX{`C@{9W z@>mSCSFC|YVQY?nJj|vf8o6W6dX+Hf-a7 z%3doVUlv#6Q|n9{KO%388|s{7#GAb0cjQP&Vhcx9ImBU!>!z#$aIzYI#rS3;-))`A zuyYG}YC84!t7EW!Iu0+_%$ z%B`D6#l=?Vv8hkBnn%Ui2Zz}bGqRIkhr!TDWA@WMt7251Y&qUhadtVMdUECH8%40A z6p;I^yXcQ?kuLVNDRc_|Q^~^xtlFjZ!if$G4_hFFhF2sDnJMe#N?E9XIypD;FA-$q z=|8uw`95A#U_C~XT}QN6r@g|Z8rhCT{tprRclKf6aKFOX98+Ogeq=>nVddRQO#+}B zXkN`Lzo#`PZJrK&u5Ua88d2ZGx@p&cM;9G+&Hm_28(1S}1?mc-ULaPBjxpA)SuE%B z$mBrXxX5v)cFI$}9m~c9I&P@I43C`L_jZ4)?E$cEkxT1XM_k22?&+B z?y_7BhB}wXdV;2yBuRZ+w7WKw_rM&EBOQV&GOPGj#>Bbo#=5J@mw(wXXIIxXdm_hK zZxpEdS)n(wlUcGOq~yw0%w>L{M7{<`{2TGgAvoqfO2Y>w6Dc|_3VQ-xeE1$8*dY`O z6QEB&pW^bMTn0(0v|{X(E_mdti)ginfhJUCGrd2 z_>)Vo&(xKH2r*gsZVI~gxAt>~N;oBdX2V9Z(hGQg_xG*V0v2JhJ z*=;0dd&5;Is(EH23^;-en_ICN~e|cLZVI9w_u+!7Q`^!Mw;6f-VWV zBIERqf{3f5FydC)4`wf(<$y1mDa^pX!*Cb-CChmtsbbybtXk!7MB?6`rs6pFkHqZ+ zjmVhG_uYIKbez3lYJ?uk(b^)85U#YiqV}YWk;iwFYt5g1H+j|$hLFi2 zaBG5Do-X~}C32f)1OEHMPEIylz*HRI{uiNeLW!ng$nB7COJuB^P_|6eeFMwh<)`fj zA=6bs$Ks)u@OrOd7GvCk4*+!)6KqTovIt{3^#&4i)AOV~3D>=|eb?5y z))dOavj%V8d+AN|Rp`udatBVr)gDSry*uQ5wdNV1eGL*HP!6-x=m|Lolo2srit6&# zOh=QkPIs+6KHxifb!ou&Mb}zQkJYB5PCf2T%*xZ_`ROg8SkFY?3(I~lA0p>+6Fh^` z&ccDC>{@R)gB}?azl?}sOE}nrM1MXjdRJ`M*mNuoQ_ouls25NhwV!0k@`T*ir9KryI)#F=zdi=~|+bv>d@awx%JgOXjzy(^g7iK_VbkEiN*GZT1{^>qoG z^J^7P8#ib85kon4DU_dxt4P+ggbEe!2aF4Xgm^|$Z2b=krPdWH^F0;ql9GXgNhS&g z6pa?TG_EOQ=BETw;K=aN^pG@;3J!_iatNe7FqQXNho?w!wk)B|0oG>;P$w>F;I>hf zBjo_F{LIagMeON0|0mcGSWik`UvqQYSnCm0jL{5Z0{H@(B|nvjcr&dW@dG3DfU-l7 zfw@a%UfGPlmlY~=b2HAMeTT*D^7EKlz7qW)Rc2kQKB4NW)_>ekK(eUUnprLz!rAg` z3ZIF70Qd?Je!dsDJ1{E!3uUS^ zYW048lpoP2?0BhwO#TH%;+GXoXqgLlUg3ZD$4_OHhCA>QNp2`@HUIz>z`VPK+jC=*_(J=*DzrpJTpL*^{M zeCNy)1X#iq%^(Hg;VqTBRX9E^Xb)i_OGLS`J-9*WF8Fdj;makYx1t>S@$e_2V=bd0 zDiUf9gS$tT3Z8JlxoQpX=bkunA;DxNEVjO=Ubw1=Wno>ZUgoiM&@kF=V!!gV;cQxr z(?#J0^P|-7rL1y=#I3dHVG7V>mWf&vf<)B6)>iGL4u*-^s`iFr!=7c+5A+m9Cbn+q z%Dd1tCVG|u^Elm4T=cz0Hk6*EGkTK9$TjQggrjiI4%Fcg+h}GotdwecT z#pE^jXBLk)w0=Vy;3ihP_H4&K(?0+VJ`r8?YF}GQe+iW@pG&XXv=Hnhh!DKP+;hH@ zFYAWeNuYi0#(aJ^pSrNIz>%lLu#JZV?hTu-v1r*`)fwf354nnOC}bY))x7yScAVZJ zpjWI%JVPQL95ue^@+V3rUGPb|!MtbP!Tx*)yGiE-<~ zuEzAV2I78KZ}wgC7i1#IU%>d}n|86q6l<4SoT=~8xgO%y8=jH+GgbFWd&p;J>D5jG zA=u?SHaAla^%@kEh1XHvX~Pxc?3^l5V13g#=EF56?1U=m@#+V9wa5gmvL9c;LlUnd zwee%aRR~T6s0(dVBkc@*DC{e_jWJz5Ik^QO7@0r54QgkERDR}pj2Dd<1tw?c?Zj`u zZg!|w2>#A;PyDW$I$*Dz3pl3W3mk-z({OQ1AFT>||XjQbgiuRUujByAc({HIa*X}CX zU3GN3t7v!C5$z_sU$R%x{;DI||3mFp!^v9CrV9Q-&VK3JkodWlJ{;)L#F;V^AY;xq zsvqyc&1_>;Z)VUVOSCicQYB0&@XKI$wKIxT8Pd){V_h;czDpUn#qyy^KKSkNZ6eYy zk<}7ePWEbI$|c6Dd5gzHe!=InOYW^Jj zdHE~nZ;0;(PlWH_yVK4UeAlt=9%94Jet?9Y>gm^8H(+GBKYsb4*7)V0tiPOHgZID2 zFaH>S4VNF32%aCtFF!0n1s}YB5WoB*(tpY~ul%*fn>_2A#$>;%cZp}81P|Igm+bwn zHjlr3l?TKkdSMYZqdehE2aMS6w2|K4UXQ)dLEJ#*SyL2IB%W|0Q^TUZ}+kzuu;~qyb|82^m*p zO(Sm~{+tYTldsY?8BG@~z&731KwPX*#yxzf zQ^DXMujnG9k#->88=0mokLL_S+WNU}uv!}Z?Y}{baN7vH`NRTPg;7hWhINZ4&Cv_!Bnvs92D6>pod#x5ykIAnmcGN*lIR%T6`4) zA>m*wMxI)U2}ZTZQmD(7>2%f8#Nkvpr8 zl8gBp%`Y-Jcy6v!2k3>hja45j1|7y7Z(A>H|0I1N=xWtE2U$b9x!jQBceQYEYJD|Y zOynu%r-qZel@GhZIOx6W7o_9GBQLQZ@)BEp7zty?H|=0CPVxC%tcc%`b1RbS^q$V{ zm3OX*th>{p_`j7#?4mEkDL8T>3QSiLvOYIn-Fh-^GqFI$Q|Z!+h&IEAy}IIhgq*u5 z$ntoFWSQ;9|A2YrVJ|ZEfY}J$@th)N`NGKxnl)YexpOFV45`z7$@9SIc*y-NCChE& zy1B*(RsA(7_Lm&JodafIr`@x=Bqp#^de*ioU@ma!u{X*i0Z3zDuNHA!qq7^M&|Vi; zjVvpd5Om;B+a&UMXVnFIz0t7*izQeb@t`dkfJ52OPxJyw!s;s~ee^`sH>0PfCgUHN zKI!q<1_mQLiyzKwbZ*ttd(zv@3BHfCyB5=I)%SnQKpS;-wu$dib6?I?jsanryk65C zf>V+xxnag9a1h=ex=efccl+C?6|wHzH9a6WaE8t%Ef)n`V$|$0p50;iUer1eGXSg4 z$nxHjyoi^O?;Wk<2^pv;NBE&eDQ7X|AoYMJ=i9DzB>0$&@zZGjZvTeQyOfUSZUiN8 zv(u%gVJ9RK5(Wd^#WZL5(o<$|nZxka(Fw3;l#AGOsl!f{`Xfnn^EDd&tFu_)oE%F~ z=?mT6q3RS*uD#h8DbWiqNS8i%wk+9_)tMY$Fk;(W((KLgBsnH` zvB68K`i*DdM3sD0C5eaU-fa(cPB@l3;R`Mmj**fFEBu~4L(~3>^;}lKS&P27y@XLe z>Oop*6)QQp8bQ?3`mu>)WJJq6#T8F?pC!YaW2lXk$zdT;^3vTD$$3qR+(wup*GQ3@ z6oUoa*v%YLWRN12iwKX5=e&PdW@v@lGOs_X%>D0=F7q!{pKYg-{bmN> zWm@Z_H1W+<`ay^)V3bQ0ClYBDsj`(h3&+KN>pPTyKxraZ1I(0K*|xHJ ztb*X!QnL;W#F1R0w>Rc`qc09G3x9#w&5Q^K9|#_zm$%v z4H)#4aQ{t`;H`C^&Bvh1-^J|F~1W!?e#bkdDg#p&p5JxSuykJSBT3n%Mei)Gd zy>nY`E-RjGez^=*YD#X@~7zU`6ILJ7Qc zAu2Gc_8}wSxK~`JR}F9pdm0B<1JP8?EBsD?R?#|TQwo^NU|Bw+qd=P}W1E!mg@0bg z`EXV0t|?w!hUN$yXW^){%w^wnq0m$83B&$vnMKnvkc5d8^)$-P1W~4A*#XuJ`nI-R zjm`zC)w3o#qu~RyC+0X|(ludL+pPAd@ zqBbV84}-eIRjQtF)gJ1ZO+DVD>nYCF^LXmJfPLy)yLJiN4N?`oIpGF-j_^!C)k{GZ z0&;q67hW@AhO-e41E*$bVpOv4hWXXTruAP^D6-0 z%Z3)*7BJtGi@ojQnlr^!{Y%AZofg0xyS1TZ6uB+TA}QJATKs4k0`B0}Vi484YFKMT-Ucg4&}4K`q$-o4nM_Pz(MT zS^~AGxNa8^gaDCD+M_+@m30$)-p)VpO#2&o2Lg2y^VsaQK!2cXKzy3XU4a4UpkQ4? z@#+bomifh@mU-T}CJqN#F@|$51@qV<2Z=(XPL@qW$ox>~&Zg8@THy|Bfe&a^x>uCK ze39nrH?j!C!hY9?o_<@qU`tMHJscT5FtCld z(%-K-GG6xj%(ga)1YE3?#RtPJV&4lAV^Zb12*_hL4$53QjNCi~g&a-)RLEt{{nt~$tz*Syl> z!f||b0yk@64`8ciS|_oSrcM`tuSEEalE3I+veb5S2_F`OoS1II6%v;I;_pVNE;gPs z>YAAeqoo;GF<783pFI;V39N{&n)5a3dv)p@^+4wT5KW438$#*QwKNkhyx)?$6NxyO zfWT7`*Q4RGL6+hf8CAtZ>G8SL?RM(ATX_eT8iIoRca&n22V&v7t5Rgv_q+@%#t~zurB61 zadS4WByVDF+$uSr^Fc|K-C#+M9A=L-StW~VX32QI(2oKlkkcT#H}U3z8Q4y1p5%mj z`#JSz41lTy8)pbO=@W#~!%UC0i+d#E?mmVc#F%8Lzu8pRz-e#qQm$#y(? zzC=z5EbOy%@mytk&?1~F;O#d99#@t;+7)VPno41TcyYeX$5CXiF6=V64FaF}M`ib+ zGQ7qW=XVr2zl%mBX@J9q714?Wpqj%imzRZwM{O>gI4|KEFC%`D!i%p&s*w?IncFh4 zDCFF0_+DPkv9vqr>s@BaU0f6Ngpr2fI>1@hn`tdpWU0Sdzvkev%X&b5kNh{nFI00u zPvI3@Sy3AR|M|tMs@Y?Gi58~!Sf&gWI09S`F?^h^F2=Kh5~~1S6+EiEb?@w>zD7(I zMbP+VdvmO`%Ku7+bz9||fHnVYexcemrS3AR+Z)>6BVwwQCzEEwNa7Bvv@_e|1{zG1 z{;a-0=y9ai-{)_~=KF-x@(0{NFfQZM?qBga-HV^JWzS;hXu}bPsT?C@aoOdV)7FzJ zHH~F>wItt}DXc$T`r3(kj&#Y5#Ik~4Q?Ml&k-Wl_5H3T7H@V1OjEv(FW0S;kO~+d7 zmPHCjf~8ueg)XpmhG;OJ0)^J2b+(hnz#v!tX^wDbPXsfleyviHAjVDp=~}+4jgExv zVIc-y$;VL-a|6PcE3H-ewYEAhVOTw(O#0z8$tq7K8P9S#_iRQk4Yn+KST~lJa`z_1&?nnx#KXSswwmng9;d#kp8kc$a@w;St$wd{-=Ip! zfrN6U3s8)}fb=s1JcE}QV|xGx)o&8f^(#>?=}De#^jI_CWW)yZLOs^Z*nnSOH<;!? z?d%U$_2}!YG(y5w?UsRr-uXJ^DI8|G&-K$m7p@r_L zcIOzH>-Mzj+AZ*IcM!jW5(p*<&KTgFMc-%aRnLXQHP85CCahIHO_KBOwO>nKu%B;} zL(sOPExr@qyfp{=93|QBkkS_P)H5IvvtpxATzX<*X3A0?MOb*OoOv% z=n|sas$>wqB(m|zSwMBPOY1y<1;44;JlzY}sYO9>%O=p7gE5$SX1}ed_UxdM?l%tZ zF;0O9%H^Uwn0U|;l+x^;OzsS#&fsm3yN$1rWGl(N_9%k;@zKi=%;TgpD3bekLXWp+ z{)INDKehkez|MCnSkZCOFh48UcSPPkKKf*1qBvigA!7GadP1GOk>}uY7$<CJRcxu5{8)hAgcV(w9>ZURlYVRnYM8!i2t1y{!1e?7mzVNIAJta@m^ z_vGzP-i*3wZxD`t&Q5mGQ@lX$T1jA1rQ9kh=i4baN=mnE!O<{hio|)^e+#`jW9XOi zXsl+g?TyO!B}z8k>!q+n>3H@Ts3&3Fa)(VQeil{vh<&Kuyq_2Sk-(r5_T;!%oI?!(RI$+D_yPY|8gf%N=RPcjv-_ZR zTYyzm$y#A&HA_A`Pg+wv*4jY~jeOI7chZLHbm=xM7mXOKso3GND5``dTls9wXEco1 zsgDunsWw?jj#KR!fH8MWbFQIi&1&np|{4(RBrM2tNRX%L9p7+^vM?fks@jzQ+XN}UIyZcMIfx$OW%AfK1goPL@kr7V_MBOJeey{xGv==U^BUGZQ$aT@~h z>%WJE@O@|vT>lgP9^&PZaQtT*$<3o%J;Y@&W4JO9|JfcQtoNajjTT7B1Ce?7w9JcO zIFgQfARaouCQDl3Ki~G~8J(}JJk#*Kw*1u8ZE||OK@8TXbat;Qpt{piFrx)g!rr=p z_X^lV38~Lod1kKeKrR022$|55x&W(l~g);BFuTTkbZ^`rsOw z!D9M`u7$h?_qga{-J2R_Rp5FV3@i^1euWJ)(_BL zH{w5(u((f*|5lPCs_P(L`O-UbIRV&)BQEi=FVf&V9)K=R+s#|j$xA7j>cl<#6>x1n* zUu;ian70nC_T%_+7kS0ZDXAYgWql&ULk4Rr*P){ zN4L&@MC)2(s(G!kkLs3}ex`jpv?o*(wI_C2D9RCY#`&Y(MTBje`IvGfs_kS>u zzd80m$}J919iMf=rJUY?bK!CZx;}s@z|u4eOk-kCviLv`*AVh%#8!G7uu;rOy7t7= z*3GAYgt=FKeA;O`B`_BI%TMlfr}|=J$$aDr9JQfI8 z`slaA@!x)r#60d(4`DH|>#_Zzu^Q&+zc>*8?L&OBCd@0G_vu;4=SYppV9UhJ%7qQ&qvx5Mb?JJ0(;kEZ-P?K zqhqbr*C`5dzxKpfE26%wjKWvxg|}JQ1<}5KSZ2=5>+-B0OO`I@`Uzcox|{^j7TJF! zosyX!#1UDYcq6t-^om&)FGY*2v#z8lxtYbh1Ks;8EBwmwxcRx*>5f2RHp4!zOgNi< z6k(sXPNy+C#;P$KLX&rBel~A|Hc7L$yvuc2&N0cDn@oVXQ?UE+uqz@W~Le@KL?E~ni{ z#!nF#G?Kl(q0DwbWw-JGtwcjN=eB6nI+r%uv_VF;3>3$g2#iC{9cTTSHI^agrUN#q zB5N#c52nK-l-;l7&>oWure8?MmXayg4dtdWj0KgkJ3g;Abxy3M){zwIPvt^mZ2rs? zRKiHo_`I2&1FMQ+^Qv73U*d(yxQd?Emq#k=Pl=pZUlo~HKO-`xzB<}K8c*sXNtwEU z-#kj6S6!Z};x|8aYOG}@BUjB|J-TClNBoZ3zJ@CuDX7GqmN?nw&^M3vm9b{#=}<z%`v?0VSx{5{r;w0 zUN?CKA~eA?b+?s8-bk-g0I?|p5Fax707N)(-gGX?O5A#KysatTc2*MSRx-$C`E_lT z+C8G+q&?O!3y^57+SVEy7O3F8Y8ya-v6$-|1r=r=P$93?qrgH+60q<{O#&7vCP)Q- zVh18O2{2qIKcm0`-4O)|r*lqCPRqP}1g1=Sd1;N^F>4fDoXH-LR9I@%WM|$XyZD{`GBsKqTH)Pw2|j0`0N+vw#;iH&d-?(ao>Zy-}~f zmN{~#8!A&(@jEJ0rx7yDm`bDeZ9ACRyX_bD?}zdLw`&C4w8z-i=gn%R@0}M3#K{yM z1#&n*I|}5K&c`U^V}0$?90c`>$=vqT-rTuajwUZzi|H_xHe_1YHD4bgchJVkG)+jJ zDUhg!`7P;#wHzcRau0VAJ#^AJ7T|GK*+^{*s4d5^&how(9MSTxo~ zoyAQNEJ;;m|4Q`-ryNziy+YAnV37)PMweuw6>rRSzVAeJ+OqFMQ#^Fx*LCT7{cNs1 z$@6HPivh;%`dNx`hI}CTF$=B!(&*{xV}t!sI4xGqNU2Lzsmas%8e3KGh`w1LvW-0J z!&=7!B(A?w>-e5LmqeD;e?idlfk8WS$plxXl10kNzWXx`|4#$sfBlzlOri&X~LpmmHY-jViVF{yCmwh^Uz9_ z2af=HJ-u_JZY5A6fmQ<2xzrc9B(1C~zrH3qx&8LMbE6>aS^0dFJyYsS)z_b|KF!R1 z8k_xe@>$LG|B-p5wHc@Tr^=)z=ke7Ze(f=RR&1C#*iB`zVJ6}CD!?o}NMQ34eqjBr zjtze$a?|FU?YFwva76IIipb>Hu$q&P_&|%*oV;g;)tn64VKpbu5ti|7<=&ZzXfntk zp`9BN{g5>OL!gG`L}7>DiQnrK;T?Dl+n za$F?HP0lR7o^OA(=SRNg=BJ`HW;K(de8utFWB+W5vi4M>jv~yW+`V4f6S~eS)|Ib1 zT>G#6g4WSb(__QB?Fm0N+z_dW4F|M)pHk6tw0j>{f%%a|vEk;(t!z4x6Jx_))H(z! z$A(+9P6;59t)?f*ZxOco7|WzW%ELFTSsNR^F>*s}_?F1$W5c&bYGcDoBPYa$+ag+Q zI2ySkHoP)=eWK}msYYuBtk={6uC){~SEld5uI-!UHFXXbvYS0UmRs-G9x2o|Ckx~A zsuf$5y*}l&t{X?9vRJ6vyIDpQW~X(S0NMbx%%12|18hO&tNjkYL+cdFN^?G(^H$CT zO*QHDfr=~S@+FrPf3}1QR7K*yXh!OIz2n75p@geaNEkTczWs5w&9en?k$Lpt@Y?Jz#N zD+XO(bWL7Q5>l9LGppK~stz95QXDPC$$w?Q>@yN-r}|B~bpW^BMxwzR;9!=xsZx$M z<>AEt2!0QnPvPItws+e-o_@|#Bj=Pxmtl^kckGUGoI?G%?>DHVKW+VjvSdHmY3*YQ zVs|wi$%b`M8pZgbVEzhKI?Ec9D%(Nw_bjZ zhI!w~91USp%94Mgndt&16SpJ-i8T)hP<~$P=pz!XoesgI0+a0fz~nCV225^Nf%%c^ z(4qPVAbB~f&j!i=VP*bbfMkSq{C^FSJ6XI(fu#8F1SETyr3^^koCC>4S&(EZt83B% zm@^ef1`@ZH%S4=;1IsaqIWy#Yb?O8KnH#GZkJP(a$SmR@EW%lntL5j-(l@=97W%2D zwW_aG@9a4inDSNsT{VRWT}hfz5c@gdl&%(#ZS_4JDQfjSbqtVY*PuNr)o8GtbR^j7 zUC&!v z44<46`>H48#Eupr$90r)ZsOTy9HE$#fo0BrSzQ++rptMUL!e%cN6KMgV%Hev3e1EL zM9$=%(fY2-!LLQum2ZnH*Q;>JA3_J$_erZ9{) z$XV=Ra-08UIQH)EWJ1cKjy6tn^%6UA}4( zwAWdB#cMcpLTCL@th*$x*QSn(dL@1im=KylFs$5kj-TUZ_ZN79`GauSO5p7reLB`7 zE8t`!_M}I4-l!XOS1}#BJHtA5{}lQ--oSbDZb<;=93Ck*(>7pRYabXgIR*1AmNO^< zML%xDk#{eSx*PGVH70C+T}lhdrG8(xOBsk3g%kfKwc{4a{5Sja+hO!uVuu_Na;W-4 zdo4<2ruRzeeqqp>PVEV(Q!VhKMAe!NwX*(17fnf4luW4+9iIyq(`&(@IVjVf~GpE;#6_A^K`IRkX#fYrY{WK;}AifHFOSuDp~-zmUr)jr5kTd>=)ET11X>{tWG>J#DVO2E9#0#}v>Gi4r{+pVuUyd96bB+*q5# z^Q$Y$6zgUbgy>HQ#zD9}TmRn$nOXlF zWC~&YWr5){AhRWfwhm`sM$sOp|5w4yl%wFLXk!~7)EaM?bp-IJI}x~GVO~=Xf=2o6 z6Z}#x$P4_E!JiYkn!rfh(G0N9J94$N(4!t3f5-wKS+4)47I3>MZNTRLp5!jHdnB~vcz_;X?IO%d`8h6J~gQ4iGTIW!lKQF-AO zZr&@MrmhZb^UQUfe7Xf)d#!!1LiZq zM8n6iL+-WLN6JtD42vjnzWmIQpNnE#zRYEgDTVucCK=nD*w;AN>NIj{n@lqRO$%4p3Qu2c$g#546|EhT=^;4idocH;1 z$nEYP{Ei7}`?tnaEj>HSu|L^B0@2EK{e{*DxcA+Voo1;@?jy4L_Yfn^b9d`qR z6pT-0rds29qh~53!x^CkGwbS*k+%@|)%jzw-+zrP+~{O$=Y_ek_rQK&?AKF|d0G8t zp>|~XofWy5<#&NBjpOBKocvU>{355}7Hj(BY89QJ{CA}$$|}+%!5CRZ?i9=OWh0i# zi=3%`aSM{l7Zb{tqn)@@;xt&0DmCX+@))N*Hm9mq)5W-|W zLihe=^oP=rpeBTDP{r9+DaLz7?s6=;*IZSL&%qH|cWJgkx6z#!soJFlG3=&6pHQz5;$cyk;ravjRB06?5mz4?VzQfFGed8$bwo}V(hR!~NMA^^ zJjL}?{7xsvb^KR$y1Q3D8^~;J)zS<4BgOMFMy!n{8UDbjNL(ts>|-iZAcX9mW0atK z=Mv2|aI9`KYDAxT4g2Xh*{x>UUYQZwB8F4vmc;UW>*6^I;?Gg{)*%*W$T;1}xlgeq z44Yx0@>{b&KgiebXo^0VIz2Y8GMzdZ)ZBzEA>%vW2q!|7cyL%EoeH^+~(;Zi3P;^AmdqrrcHz z%6^PTvODkh8%WZE&H0+EGg1M%FGql^Y+yX4W@H1WsV|04#EHksJt_h!d(@@Y_TC?P>)rH`2-3BVe^8ZiGt8+XivYNT&EHh=v|XX zIQyK^GI|sfl~7@lbt=e81PZPqXR0WpN`bAPU2gYzs$8VlvD!1E5P8Q`$(V#n#->ZC z&DuxrvxViSS!mnCzE8By<)9rsb}=o*WGmtf#!r(WVq*TDvp9fJ>-fI3KPSW$>|K^+ zhL)q17it*-W8mz1WPaOMDW#3Pb5e7a(ni3j(qVxi+rtf6Us#(pG%qm~)5u=yu}8&+ zR-uuH0zE@14a zVaz6_={)l)oQb+0h~+!-BWG-yEK~LP7DZhbDg*1M&d@h9b&k!Y4u>D(kbLVSz+#i| z(XCx;tSsBwyLTqX2((t=1T1+8+`M#KvOlOZa z;@=Zp3+rrlSTymXUaU{7ygI2LX4QJ3At9SJre2`CGF$G3G5H! zKJkhZZ}TJ!SGx2UlqHI}>LfCHi#e+NYPIe~QF^j98{PB_0T^TKFx_jXR(#R0fmqkG zo-!(l7m3~tSVplnDzRh#s>mkcqhJ7x_M+h8bm_@VN#?S-M*JkC)F4L~LbR-CGpli) zkk(>F{xv@GT9$deTIJknJxJvlJtDuLG{Z(|wrQ#4q{^mv9@NjT6#QLAD}WgrHo}}o zIAxJ+92~#ItU(msIEpN52x-6Gr{IoOKmqJam0Dk6n+0wwg+hOWjuXa$s$}g1Oytd8 zB{WEveoFkceWGPm`G-+Ur`O}5&l z`o`}uY}WU|H4@Y4)i>^8gS5JckZD^gTcsf%SkK@AhA>@P!UTlP--@hXc3xPUPa_aX zmwrM+rO)S!a!;*Nq~csk0HN{C^Ac! zW3*_00o!PSl`D6iFBsxBX0}?+M{X)=*A1Jcb64kMO21P#caR}mQ+f^6LxF|x?7^+w zXyc*6%)nv6u7(T{mIXwyHYtNTk|03Z2W|M}igE;01563Gi49m6$V!ZDtrh%qtnZW6 z_oFh&@j#`ov{7+2+c|wJR4zJlsCcr=(G4ojvu;IYCtVt}7y-PKNHjG!-spTp+y0%W?NUR-XNvrF=u)B3) z0AlthSzBxvN0AoHUpac?_%VIsOc6o~Vy89AI5r@wQH~J|(KouKnK;uZsS^dm7~ULw zTcRI^_(t2G37dZ0DRf5$La||Yv;?;j9*F;slX(wO^NK>$+OuM3`c1NbKt9>M&=lFHCn7eZS>-GjOw>U-Tyc4B_za zXp-7e?soSI$eDSj2f8E#(r#EOXrNoiOnZ}`WcWP*ErSGbdt{dUP*O9ilZ=`kRP{09 z&aClt>DvG-=f1~*`Bbqm*-CiZ#+!BXSRqhQKSoqA0fU_Tq^pUN+jv;6aS|^5p&fOv zMC~sjic_g1pVa3*hblQGGr0f$20KPJEyRMr=)WTu4lYCdF1P2LcNY4fPlr=AV)}DCqIjB}kXQz+d z37Lg{KK@c@f72Iw*%APEM+P@Ruiexnq#sO@>kZR&>SDf`8)T|_-u8&Ifbr?l>)({I z=@%kwE*+u-#+FCAH1_9FILO}p#9M7GBt@dM;IcfnDDXdt7=x1plOwlM@#)h0rBI?h zQqC!FNe%MZCUx$RaZ|$$E-3jSZq|AQr(>>@m%Q7iUjP9;(UhNDDMXcfnI!qgXE^Zy zgX@LYF-@`+c0;JUhB9uP*=Y*H@q979@}K)|yKj0_^LM=Ke{{NE`}Q_;fm0WR(uJ^E zB84aj7vjoKd!mMDgr!>-soouT)OSAmxiWbwr9>a{w|`n5Dd4v#`kzurQQw@j0~SPj zCpx|DK8LL&((WsdxZ8b2QMXjMU&U``pvADKFciO78W&2tDijMC<48%C?r!&88=;Kp z(Q`VsM5jodW83e@n{F3URPzim^r3IBmGWZxN96^5h1b#n+6$Dv)8&Mx6ChF4gQBRf zz9@15V}YF^1XHG?W353%DNL2>BUWmohCzCr?oHk(7~L)#Ra+@LDHpy;%kjU@nU`h@ z4Eq26xiW(Hf=H8R$891YmC{|4ojIJ40Rogn+l0@2;p;X}M5WR3F1Cf5{P$*SNCA}pgxR?BI; zESeNAvwnKbdq8z&lPj~HKh@!Q z9CjzGi55MMg9J6;mnK{==5Vh_@!TDmE!{eRzNjAiG>;1_f>_MmqgP53!r_84`w z(Z9w^exb@#rNv8s#)~y|R1sb<R-K^09SUxH`<92eqAunb$?{Itn=5Gs}kcGG`3y zzps_?R6D#I^F8BhoGh)VJ&iQL{@py;_G{w(w6CfS>Ay2!?YC$0Q0Xd46 zP;)O6k1fQ0Zb?7B{vynGk?G1FnC|x1&cxzaI_T{0Li02VhMk+I4mZ{S-o5R1MQ#q8 z!+P!ckuMstr^vQVubp|eWZR~cA5pO?Noq{&>;86yn+lSoqtO`D1BgKkS;w#obV|>B;2RUQTI6Y$supCEF{IevKbyi6LDN9R~w^M z58MOp>a68c1E2<57*(QWaYzgU`>kbCW@0U*6POKDAXQ(NsV6gdHf_y`4<=2?#c8*k z*nci-9V*wRt25t@x4)T3eM^);+d3>+)a<<_67H8U{!~6Ce61b+BVj2q8Rk9t8Ve%3 zR*yk<3;|P$?hx9a)7@C>TzY%Nl-uxijJ%Iz^NM1)%6lLOq1hh)Gd`fe4ScOK&c#t> zkATfRr$Tr8tvJ~JonN5Ttb$4rUZ@bh=I6bk8mqAevgN5z%|3A}_w3HFZ>QF|UD8(l zQAx#&p2JRD!WrgaLz^?Kvxg2EPQtL>eTSEw6eOZwdGmeB`W9FS&+uHk@Gx3qpx9_7 zf>4*f(WB~qQNZrjT$+TPM(enov89ggRRLhwc5nakbMh{PPlywdL8N)G)=zsmQXF1KU6j~BTx4!UIaEd)x8 z+iT0NHR`G&oTQx;5t7DNLDZ&8r~Xla(s@+IbzY-tVGP7@YnG?6W)JD*c>HXCqwo2s zJLKCMd7#mJJ7j(&SEQNyQr|b^@+emLZfgU|Sv3C7H>(+9dID9$6s&jA=%+(9Lu%~L zk(gyw&vnj_ z45eq#c6B7k!o{-#_+xC0dm1^z@JCB!KY{!f27xC-gZtSOi4YRGk<}rb+u#u7vXvo` z7l73p%V7pXQCr^>{!whJAG2osWddaEDz~$%VCZ z5lTvkiAt-GqB4=$^H1pwH|2Uz^NLy`S+Lz{nM{zNU6WNR&q5$ao~N@7$}#mxCWEAwP>JknV)O3l5mReUUfGhc>AlUD}BW0D`PZ;z4@9}V0ciV9;*)n6N-lO8$ z?6_e&ou%RyNnGsIH{~VMkE8cbIg7Jvv#Q1YCfiBXe%uul8a?lUz_=!1&Osr5&cz7p zM>3Qgzy{JoT8~E#XUuAq97e`k2hhI}s28sTh;e+MM%fs1Rzd)rpOLj?eTmZNF*Ts4 zuOh%n&VXHRkM)bkGe)qfNBL~F#!eY&H|IM$-+xKIXfEG_Cyb;yiZ)PvL&o)yQ3Z-e z`DA;c`8T8IeR{Too_!)D?(uX7FKh5{ zNGar57V`j6npH%Nb|15IBjcyqma&I~QMaG-6|D=QleYm5(5eps!xr~fc!U!(6*X2H z^1W*NB$hhC1?7f`7`r&EUl#^z;IOp+8A&}U3WE4!$EIh(; ziK~>KYVqa;`(4tBc94;{M_tWl{RA32r6w>bTzhkzWvM0cgNk-pPKa)@_YSQeg;`?P za}ejy9lO4q9yJbT4&umx6vi2Xy8aVk>c_KrqOnYE%b=Nf@K4q*YHYvL>o9}mT99+Q zLONMmXG8}AC^17!q?*5Z%wJ@->8rZK6KnCZpxQvyHHgyB6$H_WsK!M72qOCh%l&z@ zyN4rVhGO$3dO@VA@n!=;@_eS(Jrrx1_(>MFSYJ6=dKOP984`ZBGaS|p{4goX z^~p)9ps^G*L24bUm0GRfl?rxWrC7<}PI2nO}EE za^KxT?&KPN)O7|q|1{(W1KHKIOTw@HFVf(vsN_uETD7*K@85sNLR8kKwZpN}fHGwIDX+!x-y6Uiv`J?xb)#vp$A~)=C^;=Dy-${Eq#N^I3AK zD)nr4KQYTE$=G~KD0S`V`z2bn_i<<8o+6pL$&|YIv-8D>l>C7NGJ;!qsxD^kk08H* zRxP(Lv{Rk^jUuxCh_)TxQhEV-@(D~kpMZPY;FZs8I|Q&y zt=Y9@%>D7-Yv+ebx=UbBQp_nbn<#frO?~(bFuQGq%tY<3D z15c#pbN|h@!_6g&rGp!C6hxkop|K@Z9DXS_vran`C0lI=U1_J znse@}Y%h9CL`KAqa)X8U?B!X{bsGEE9`OmVgEE?vzZP!nM*1QD+T~LiJ#>+vB}%r_ z8ePQD;yW(@()WilDTi^6hMsyYzX;v3uJiQ7<*uyzJnL-OD5 zMEhoUM}PEtA83~&#-a!uwJE1sOQl6wokC-iDSx{qXVSEL}j34`s|u6Rwn+vLtkXmuzb0t<5IB*ioH8?g%=!LHYX;?U{k z=xNq@syZ0~YniC!ipqfSan<&ohkX3U;CE4CDni`yX>54<3j6fXIB4DeJ2|0LiH-LH zD;)o7t=@Gf*M|r50*PDL=bW+IJ$ZiZ(d}y{3iYm=&$I3PY9T!(c?xyWeiRErYGLD~dU19U?D!Mz)%yJH=^1j<;M4gjc(CcJC}5UJMh#(;N^$6{AKcF6 zsA;XUoqigv6XDN8SgxVcy)TD^ZIyLhg1B z1QVpn zrTcb8X6U}%ZI!{X`*rTkD2}>(JC@4`xl-KF^Xr4o{i8=Hb(fJ=BioUVl=^FHBVMav zq`B?r=jsRgJke?GcMjthq)O{JtRz9f!PtGupHwo%yZM3+vvUO@_|0Fj@>Kx>`sB?M zGnK!lt|gcsIbDj8nat%s3Z7)Kx2G=ur^y7MQ)j9&^AEn7P2M<|b2)+~3eyFYa$ldv z?8{m$Vc;dMgztb4`)2mUo8?&^+y0JAOyAYJ_N(>qQMm3*mc(@CCZi*=NV$=PVzrk< zp@A+&ce176m`egm)oR6GVAwo8X#Pz+jg|ji_&U1w*wl&;G;DqfJ+VUVu`;o*mFp`B z@3g9}Qk2-A;p6M(yIkUm8-v3$;FUfF%oD5<)#_|6@;vbtN{n2hzMi7LLQCWt&P|IY zu(?`ld)$kq$>t#{s2;KLJ z7L(hbTYWEScm0TR^o)w1GAjOoQSo_FQWJtlRTA>y zTuFa{WVY*Gc1O0}XI0#6(k{Cp8}}q}oBRNl#9p3_eL!Ld(HoOkUpDqzD*tH`ce)+d zc2aT;ksFU^W@S`7YR-C7P7Vs97kEaZ;Ykdkrz{_rX!s8mU{Us@j?27}dW91|DY5Q> zCj=m~h;9|urTM8gpiAFq8$=4e!>5o2zQY2kJ943oqVV+A^C{7-Y@LDDy75>$e8RY9 z7RiOLip*jlJsSQ})3nE?9EId4xG?2bEfDP319sGZuK@RV0Q}Y^8)bDVuo5o__V;Cj2_5hv&xDb44upz)!^ieys%87C8v9<5-D|Acy+C!YRAG?KDthYAdRBrLA>k zY-KA!2N@vz?JMcDzvI2=N#XeEfm$?}wT=epB4H;~X7{pliO|g#;ITh=K<#C>TIZ`0 zWH0j{*p{a#jdbdZg!3b3sA7b!9VtR_FGltPdpo=OUr5MEuT7n;%5~}oU(MxU&&cg* zcqLVNNI$*v;&}NTp?gl911_ahU>OKJ| zV*emNhQ&|wKCUy~V?6=`Kvd7ltu+m50}_R^%ZOBjGj!tCCQ%Qy*JGpjU3&s&3JjlFp=b@e8WvJQX8A)F&YnisW=v9_sChg0=_afdlcqQ|sp z_FX^^sBfR9%K4TLgN|=w+a~bhMafo(BdDdXZ!EP%l&o=Jo6n>ZuguZC<>`$=q| z%8k7B?fDF8<09cg!J~qouEqtfI=X?3B1|jmbruD&(G?sf{INUN=10ey!6##bZ}L6* z>sY@t9(hMqpA~W z0M9SnYA(1z@x)Z^mHZ+zw797+otlcg;M+)pMGeERdSX4#tDPI+@cW`Y`2F@z(pu#s z6@v_dcm?GlJTg3j?#dq5Jg^b^6)#?8_2lM%G!Ik!!@?x2cu6q{;W2s)bDa<)Lum)x z;l;#{W;7}u>M`>FX`jlki6p(q`Y#9*QAC=4RPJN&P&4)ar=4F3tYpb3=Xln2nQ~s{ z>*!Ye{c^6g%dz_rmwpJbcOpA0obNnT3Zlm$TDbRWzy(dW0&Dm=+D%JW9#unDXB{?X z<36T%pUqrsELZYmCBJ}Q=HTi?L>HOwFdT!zgdE-|YbG8PCe z!pu}(qfb5M;FEbeOBW94Ci*2@Pg<=UYW|Ew+4rmn-~|+hTY<4qG=lb_zP0Aztttx) z!|ZrvIh%}`KdGwd%QNk^Wyg(FzGO!ztW*(YN6;81m~Jh^t@D=5B0pv zHlsZ+x0>^NUUc(^JufrNSv@Zc%`3WJVzn~lOM2dw+Wfh^0|Ivj1b`Y4yv=qJ&M=AG z%Y++DVud<{M97rIhZ&JPf(9g*v4;dWfa`4YmueIKmrC#yeEuOq_fwhB_N%SclP4Yr zBQ7&Mg95Q9t#Ui9U?XpW1A4Dmi??eOTtit=fj-OpE7VR#g<+IYFo|?r%zUU4*P5cA zE)#V9mh1!_*VlAW{%hc~fS403~JvIiLa~ zE818z7MfqYnfqYD8SwHD^S%wjmd?V%PleL|5$11MWf^#&)k*(< zl)B3(D9(0%EuZQ9t-U(Ga+7pEM|FOCw)0PukKVYY^Vk<`G9Ok2rSsdbRGnX}8d@uz zUuzDwI{!IUiIRUmu|S}|H#D0&)=NKoWJc+t$6pbcKTIta%r7)Q6+W*59B!fMvsPcv z;*p^`87i_lkp^^IRvQJ^QdUjHQ3^7ew|b4HbEAyr0?E-c6>pJ`(OgCMQqRh0R;q$B zn*P>k{-(N+nTkshdXjyd8ckZOn+KsHy_rwXlRx4mBd;v@gp8)(j+m=y1~VY0O<2+u znIdlxP>SBO=sBg5S;;D}K(5O8B|kHkZwksHJ`C?!Nvp(V!szdf33jCBoBik~dHRP- zFeG$riXJ?B-WPG-C((lg7xY;MQ_qK2L31g*hBg+*%FUbPLwuBjG`@$Odc4Zbid;ao*eUct-s-z~Zm)W_UXiVb`Nnoo zWX4*r?|;sIcU#|2kWY5=;gn_nA^aMiwft**m!s2&W!nDf#)HS^j*|eQMPGzu&Lx0o zQ_$F=mu*JyyaW{#D}?9`g3h56Idz-Vji!=FCUtVVC4^|u_*LhovHi%fQ*2*%X?4%@ zA^BVWsWe{dwBcJ(LV|9{h7aAhBD;kmSEP%Q(6bUbbV|JHMKTd7417LvA`IlIASb|e zrj;~|B=I#f(Mrt*8r_gCdd#X1H=H{eU~t*jvPXn}Awfn}&r+v}qDtmPrhpB^db?B# zl67WxbkTE|5`gjHbhaRj+g`y%&sr{d@%e#zL<^r+cp09!0q-4L6z!Gi4?LnkDp5s? zo*-jt84r)}0A!a-p^QX-W3cS-^aoy5nJqCJWZuc6ie~W;Vm_8_?b(F197#nO2+idg zaQ!Dgrp;Owkh-Y&wI+l=t8fIG|6eiCWf%uGT1eyg`esReqpbQuR(+LHA6A~SoKIHY z@BUeR4JFhk=RN%k^GyHNa|tzPXX=A$TAS_IESUj8WOVYOm!aU**{_Bflxn~lQq6@{ zHUBNu45JzZsv*9V)FbD?3H5A{InMN^zpAGP zuX5-D`;}CHGFQB4993i^rv5L9=6zHL;8A;|XGATzkGdq7>zbw1*?Wuk@a?H@Aw`!9 zmZ5XETAkbdHl4db)pqFOZbFy9BA zvNo(f=HYfMU9^+kC*5>(ceA&sH+v839cy)lW!J;~C0?)|vJ2}TntMf_;ziGIqpiWx z);!u0P+S!<>WZHtMeKaI89vUV@wO7ehd6eS#*}9-jPg=S79oduoFM77JWIM;7sH^5 z(%bWecwcW$=8Fij*HXWfpf(ZUqn1WOEeUdM5qmi%QYK)w?5+^j7A9+^E>gAE**#4QwX;Q>**RdVvGqQ#voQq4}OtVW;vHak&D6Ub9QkloUN8k zb98UU{lVTox?^NQ7x&Fb8lW@n^ZV6KFRxYZ{#vG%FE%y`-EDc#IAmXrrdm3czZ_j$ zVnr^8H-=%{A~n6-=n#OR|1}?}4qA?PkSb2L**T-MrES<+;h!TX(udl;sdJ)-9Fa45 z%uS7x1if|Y1m;dw#W@>U6%@)NR&JkG?@&p`)&Q%BXxgFGU#M~^9WqgGVQaNN50 zvRj{{Vj7G_OsZPJd0&&U(Xx}Mri~Pexq|#wc;ITi>|97ZGS&C2UR{q380wVpiZc3@ za~TNN+IDlruhVIU4Lw;?&Ry4}>-RDb$oQZOxYHXAg?!VNjvOWeIIZqo*)lz%z2rLH z#an2gupxkgYrs^!oFGOMGtI=YTx7o+%*o&o=6#U2AyT*;=8Uc3#(^z?K{!7#SFr5V zrLxs_mikcG5*SDe2$QkhEo;n%!lMN@1S@@M2#hrdacewW&I@M>-43|PRw`IdG1%1k z6t6VR?^Q~>TAhy(X-jW(vj?=g3)D;Hn8e=Pym!7KRTLrOT!>TS2HAhI_vFGaXF+uG zD6lS)O>VN!v)J&Ymxmiui?N+>qy8#40=fGQ48?rPPSFvJ6=Ly{!S7h$65s?BfZnYv zA6O48pG|}thN5OYPiyBL#K`=sHFskEr%%XVf}A;%|KxZO#xU4{s|DQJOl*u(#C&pq zzx@2j>9PTwII%p>qIPHZI*-|U3#oYqXom}#^tROe*{`g{ULLA=Q6L{FQuadH(mvI( zzRDd>@);&TTdJI*l$${v)d=#*#G~{FUEv3x`#o##IoIb&E;`Ey=Etwjg~LrXA)^H? zP$3tTx5pFE14EtLnPBl8$=Ddsi<@4>~3ykaWd~cP5+OUeQKwcwj0o*GK0{n>U z{e1i-P!ClMFt5feg0s+CWD>tZ7uFNw>JFKd>|##QV@|%vZ#S+T3V@g^VfD<0<*5LP zqTQB=1xSHm&X|FSga1nwA8!rdy=>7>%SEJAO(eO?L>+gK02|MYtwL;VFrVSBYThfA%chA91-De$ ztP%ADfy#!~}p^x0CX0|${;Z!A5!$OFb5L3^P%R_OiA7T_( zn$gz*re#|IF4F`mNf*8FM>V~}qO2F2R{tNeK&dh(<|x~lyp^|f(J%fWkEt$J*zAe616g4vVjbce0zrEP4M?Y*Q4%%e=1sSU` z2i6|^ITfIVzRb-phNz>1C5^$Z?Uf_8gvMj>9Q=QOKxslnCSj}p~d*j}w|YgEh*Oq-=o`&6IQSVv{Yn!=8jj(!6G z;3&4c`^Im=H;Pa7kg)}@DiCjF7(yYIP!F40toyV&38BEofyC;MTRdL$Xj2Z65hI@@ zAK@*!d$gJoYuJ?|c811n4{lE3E{+-5K|%aGG5D$w4)ips1a3g4g3p5`oAk|x$*R?3 zi;7;+;28uA4wi6rNiabK#}36uUdH!cGZg86+zd4_Lt7&s(87#V|8G{G{4zU5Yfd&c z8F2U5T6oEJ?GxdXoG_*Tk7J80kTJ>7O4c&HYG_(!XxaIV^YpRfF@Uts&;K-^dNA<3~Q**=*((Okv_|q za6DOX%U1u@>h+t$McG$`RA?XeQ6dwfGVU+y+7jJkkFLovniL-p%iVnQ#)AV=EUp|T)d}^LV zZ}se7FvQIJ80j%2!dNiXwAa8?;*Sp3P1dNohZI@BrMHbb3CJ!wK;DqCN|7q~lHtpX zs%RXW$JQ3>*|B_c#iPt11Exw*D9gWWI!Bng-3^@B`wozbnxr zZ?y=A85vTjhXRIZxD5)prj%P~x!gi=W$&9M`%Br1VN4ghtuPp?87kWfZfRX{EfXCy zI*jKP3rMWS#PZZQj4@eTe5T#}dYT9(RCIP|2Jb{ailP`ksNjnApJ-1u>L7e@6P^_@ zoqU_l>W+=(`BM~`;cDrNLk(%>DVp&ctNiQ)b<>B*WbLkvdK{TK0SJhGMal&7K2Bb> zy@v8|QpS_W$WDSU)Sxico3ovj7g&Or4^ziO+7->w#(w(aoNo)xF~8o|L%5VL=S~1e z;&|3pHz^JE1)h=KSoE~-XPA<_s`^j2UQz%H!%K@^JW+e337Xuot$rsE0UwMEB8bMT z;*?rW;|SONHePhc8nY6!eez>h8tE%rJ${{?#XVxl6nlfX#I}gxUi-13zX0DgZ;0Ji zZh;QS>y90|11m6CLhym{H54CwF2H}wT#FPDoKr7ky&j*Q6F_~`(c%c`#-+Kn<)z{4 z{KlnDLT}mc?Ca~=-ZIrb(MObmMI)Atu1{U*>oOCEUE-O4v}2R)>KqoV-+_OSgZ;@M zOI2k6U1u^lZfoi!gWopQ8GW_5mL_u|gK-fH`5(X1?h_wH@av!{A$4^{>N3fhYc%9{ zy%6muIuPo7S;^u~GPe>E(JNXU3&o%-u5SnB<7vu#{=Y;y=yGRzHOng zvld)i*fjQm5|Njs?&nLR8qlQAAk6>&+iiLMD(aO%FnC$dlgTsCSC$UnJK^bZz&v z-t9CFb?xGYFam~B?WA+?ORmq^7>ZwN*B$G1<1Mq8%0hEmm2Rk1e#>nNknkh81#$Ld z04a-ihFk}`0?XE{-MMEberGP zq5TS~^y@_dk5$#(lj_jy&qn_1YM6?@S_@Yk*CI_R+`v5wMB%oZJ7FKCreyQ-xd*Qx z{>t2f6n=?kkPDjHCm@$sguQ;k*0(Ou#736;%3t70Hqv<*KBt}sb69B|T~cXm)|SHb zX>{V4Kjhg|bD9q#b6LL1Sc%i6b!KuF&{(`u%HG(Fy7>SM5y&(#m|o*+vD-RtyjPDG z&B7%p8~b{G^w`W@aEPZ^kNb4gkN6`PimOZ|iSO9}-d|m%)+?ZPU1&YB)m#@@kE40K z#CklJ8qs;4c}D!o^oZ3VbfxgYn$xuz69itmxH>qRcsP~6Zxh#j)f*W{48B?j#z-fl z(jIHXtwCL*j}7GKLWNm{gDcjS%O$sY3zB^yZStT*zOjYNUXm@G<$ZR`J$lJeXIC0Y zi^U;k{oKrln$>8uXK&rV{hMIr$cEgH_@&b906qj2`iY3*u8> z!HMF0SK$0kYyy_DbzfCv9C~NDk+aOnFh{9mOwi!8Uk}-N37eN7!TtuW!zi*01Pdi= zXbO{4XLO%Ex-lo3@@aL5GHg+NZrAN&7R5brmHcyRG&NkSE69FZE8mXT!*~L%Q}k#h zO|Z^{b|gg>07oUaZ;s-P(qgGBLZxAD&W4ak9wrBak&MEMLHr|rW z#K#S$fn+2#H=FI>7{%{#)gtz-|Mu_;U?OI@9S|$G0U@yf=+^$X^Z?=E!J&y1nU}(7 z%ld!(3nwUsmJXs`f#($e&M1gLN`nTYtL=J4L17azfUK`5Aqw1_8Rnl@t<3mVbbVy; z{szEUx-ncsL|r@G~hfOR94jD$*J z4zfrke3PHl9=y@@u(j8LEHB8YcI_~xAb#m*30>Bh=d7}1F86{MqjQ+l=p61_O8S(! z`*rijaP5#`j{)~XXao)dpbsI$I^4)bGJav|9B>_195?vnGfDDn$L^`348;VI46Zfr zKS##Et+F{c^)@ETr&C^f=@rQ!Y%xntr0#+bxSpT}Ifs~p3F_*HgMvbioLrVTP7AIc zD%%DB1i9u73Yld1u~{2uHF;JyZ+lTG9*#0fP|L~qxBIU2Z|hn*@f*H&I~9#)^hmSq z3MKLn+l77riDlX?IQj-P#tM#EKWXd7CWEkO&a1JymMBotF%}j2&HgdB4)rdzdmCOV zuB9EsDq7u7)q^uUHhQR!R%a#Wh6hIv<>CAMkQV-N+?NvR&JR<5GrCh0CQR;%5#`%t z1gk)sx>u;@}R6RHNM zTAkTWr7jU?>t)fTlc*fYeqpzGVIPEe2p2%<)L7Pc5hG0vy@%mW@V z0S!ppa+}rP2R3tC80%gj*sC-c{eCVP zawFH8n^EU9yoG#~YXu+O=IbQ2dJ7-qUGkjI`MnEwgLjEwo!#e z`Qv#b^)yRW=T;{!L2eSJj&%=$UozLN$7U2Z=(!n$x5j);3EhP;Jg3dLa&Tu8SnpXX zS8V#Sod|pkU#?Ms>0Zg50GOkbJ!$Cf^lDmy);&|mZDatg;&jEh3cuPTIj%ZXHcyvs z=($Un46f+66E#jbem@OD0otYkG|5sBACMWh?jlH)#Z*vvR*tRHSMClk7h4kXE|+62 zznVUYX4>ym&pM*P!G)m3VLpXJlmh{>n!iA#FPvFO`rg>4$KO;|j{~AY^gGv>uO1Tj z`cnrR9lp-SK9Ct{)RbFoB6-N#>s#chnC$1P!=Nq^E-<&NY-7om+D38GhKnBnWsvXS zjcb|`YKwvYF{vp>p1Lx5tkdCabIV;I7LccAohHrJYb1~J_KJ|4L6KZRr)!n&=~nNO zjbR-VQphrC2RZzsObfxPVy;3`dwhP3 zxADrkMLy4(8s$BP-2p@XF)?J#oHOE^F@q|*9ly;MJxL{s8Dd7*8J4%VzdgqSZ%#f1 zQH61_9=k}ds37utpdAlq9pURlts*{iMs;go##xtm8}7c!eEnKk!U#oBVilcV%m3Cx zluzZO|JyFwlfyyzesQwKp)O+L3Qy{#knw$Z80G*l}6u6 zijVWAtb`-R>?fTw1Hc1oXBSAgtVIN2QljUN~!24+KM8QPQ` zs8WWE-RM_%%O{-~=~B{iq(Qo4KXSzR?)Vh+7>;MtKxcIKp}8`?&>nIe6*qp5JtLIB zcJYHa3|?-fu_`Dm+&1&Cpc!F~Y-R+NCC4s{=a=z?TS29<(tI@YaWx-9#{5Emd|_^= zVt#Jq9KGV=$P}Nk4d3Oahq`5~eaz=nD~RJ7!`JJvOLI9mz`vIk>c-^&e^|(VPcDFn zlFMjKiZDA=@~s{d_+a9gBjo8=fKugv>G89_1ub4_w3=t5t}n}a2t{?(z*^JG?;CQv zO#PW2chI&TqltLF$!{SUN0TAdaN-oa@O!4<#KgxXF>^O_XMHj*{0f*3 zqR45wa56lb!bMDhS{9P;W2n>QVydf6o?AxT(1{BQs0dd8PdCS>+2`fJjxBjxAt_j4 zzGYr|(TOxL9U6BS3I5IBQPN%!X3Ax$R4q9rTum4KiPK+n%H9HEhj?L`O;N zd447o2)Jk{M9yu0cPS_Q7LgeKPf_ww%LeFpC{c%RMt?4YBKq`4$@pzSU3v?5EF-#iyhI;AZ7R1W2*_6x4-&vSy*% zs!B8at6eM#QrSC+rJH2DlhsK2_$Qpivf(1iY;B#5og;MMUNbf z3~qJ(Z+G%@B;$1M)aa3~BZCyfvDFpJd^M`_S*aG6kZWGIt_1CimQwfm+>Mht>$&M& z&x-l{HJLtEEPQ&l+ktC^sLf*l-7j}mqBd6__#-!iQo$G{$CfxpH{i|_ zrs^m5$QOG2r!uE2WDHB36%BR~Ws3`rFEZrJ?V>5x?QKlx1tfl`$H8=azPJ{W4Zd4# zU+IbK$x#`UR6kc4q8ySX0O;?JM!hBfDDn~#!UQ;rHvxx<*|6!bDzDN&%V z!kuD*I*#;s{ai*b_+PFW%>icYmSQiQ8=9q*;;B3)0K#Xi=(NNGbA{MaR}8sOn{^XT zC}Ktb(~aXw)eB-ezTk8ULfXs?m9&Q9arqu9L2&Y@^aoMeu+ALfY1Q^C^eyq{?n@PP z=H!G6Yx}u*?YFOCu|O%+w?+LbMF)}9!_JDdr7n?Wc*QWt@F(#Tg0|JL1-aA{pW;sC z!}%Y!U;xz+qT+-~R5(u0)_EYjxSAQz=z(cglmnSMOBd@du#}~X#b_*_Wumn_l#|Eu z1nl)LAy7on7#QSu{w2MH9DY~nZC93x$7r>Yd>*e;d7WRzo>)GC5u5%MvIm8l(&`{m zSt)^1rNt*Slu;|&kr6G@3T+C%=^6ay&EPjZgWtRv{HAB{o3{tQ>2rgQMnQ3E_ItIxHv{!=FU%L z5P?#J1=3*)_1FdVpm5lo3=?L}X>0=2OkCy#)Me@ZsDTDbA?ahPw3O(q6+4}rNUYPf z2aIKp)UpCued2|&Qv9Q#WfeHcGU|v=b&0N@*z`d^#{#DLYqlNTLRu{cl0jAwTdd#` znofR;Bnxk_PbKP*LK#Xw&2ZgGOZ7_Y05@gP<#GvEG3fqB#+3S=G8-^o{Ixoxo|H7% z@1?ru{V?jBJ7JJ0driC?;5wPHzAqbIl)S6~PyHpmiM?!y0ezJX%E)GtKznycd+~zDMoXfrR1#wa=ka6u zR||u(U=+_Gqw%lyoS2<%VK~ zx<}i&u9U6fb9@HPSsFd+n)h-1fuT8^v7T1#e&J1PFK;B?&;u4~{90fR5@fZei~cv0 zxKkxgAIcrvI}9j(EVjgha6 z>CVN;XIQ7ak!_>U{kKD1aWhNyzn_@ z|1~(eKzpP&nAq#Abikk4>?=Pn?DdwAPx&Glp1CDK(JoDS-IPs#g*%*u_aw{EO!IIA+&jVmT-Cq2e8vj|*&z#|r(a#FP z1ALxzgrQ$pPUmIl7oH&x4E@3(^1#q9bny@>VY{mdf}5Pbf)mj;JwA!Bp?>WkF&=Rc zoFfzPnXI7S9aN_`TSE(?##piK)dK8Dr_HvfK7CX{WF4{q^3 z|AYIea*oO6FC?KW`egkVe4HbJRm4#YQ`7q6H(n>tmMVq;} z-F46=D^2C+N}VpcjS^5Vu&5!wJ*|;H^ zCx^yqrfZeV2ob#Ay$9maVHWXjaXQhVhO1LV6PBt=<|={^2&Toy<@%h>(Zk0g1?JwB z*%GPS>sK$V@)RY0@3PgfW!KP^zhkv6+ z=wzX#-GWAlRSG&zonro^yCL&sboFFIMpeitYdxR9Hsmg#_OI*b12bnP?h58`%W#dQ zOCjT<0K(+N*L|WNVMOy$A7LdChlMp(=`b%dob4c~(g`{~8o&$G1Zf^v7sH(8nBO?a zl_56OS?T#oi{1k^52joEOJGvF+J57*U1kh!uZ~qV`Wpf(mxRurEWZ3sTjD?e;@aIu z8hD3*YUkT-pK@>A5NIcof(d6?TdJ+F(NJ~$U_E@cs{057u(CmJHKO`sPU8X!y{Ogo zrCCv$OSi}kl@}%!Nq=RdS)9%Cfh2O#kRWGRcc^O#M4XseOyES!=y|iz^ zQS;rx0)?coM4iGUgbsu_xuw`mRWnT-H?jW~2~60F3%Sjf1hzX`fr!82?zVY9s=a$< z|HuV#UxBh7^yBRgiw>j~nR?$TZ5(7q6`4r<>K+ z9m_)bg%cmh%VuCMX*#7$3DfQR4=uA$>WaO53C-Dp5*X3Udr|+p?f#Gn5d&5-L{NUE?|itLt>p`FAPQKS&Nz`!pvvRE?_V{Zc@1 zI^SZP^IQ}jRJ|53{N^I-MkV9pMg?lW4UsLDWxdYq+lzr$QbrT6CiVk?3!D zj@3-mUe-j#v^sjIesmSti6h$V6FVZLIvre4j3>jzV!(AN@UB^^Q5Sxr;C0`j?8X+uvma2OK}L06L_Kd0o zPt@E_0G5z(04_PF;KYl5#nEG7_vwcJD==)Rv^=`o9u7d~4#O;)o3{GU=`Ss}qbxG4 zyJGXx*Hl|6Er2Tn<4p>9nGYNxtu-B#G+I+5%r#aXa}U2(f6UP+1<1JseN$!8UeT4k zLNUm_PVvu06QD=DB`zsBe&#?sA4UOHF)jX@0{~HRa+`Cg-XEJ_-lduuLO0B1KUK{n z<7BEtNE3Hm{!3YPA6-&u?2|(+$cYw;eg7bsvwg~vS?5*6r=};_7vMRwYZIy;f1J|k zKMz?daYRAcrfF8vdYC^V>N?IxaIp=OHS5fQ2Y+JWGzc zom!pXjALtSkJz;b*7K-3#OX8X+w|y7E>W`Wlp7MUrk2+KCGi8(-~ zcQA>4H>V$rc}^=4U~e;@L{$m*ST>_7H)-8$UG4v)4kdxbdF8d?tF+hG*6p+c*`J1T zL}c*sHN?1;r(QK~G*i{be|Yc+AD%uwt%@ss>c13xICkH(K!C#lfdFTGNVo#&qSNR# zE2Uq$sMzX1z@d`?yoe7BaG3FzFx}+1l}EPtM2Vs}!u<9t-AgrJJqjU^B(2Xo!xB`@CGk^FOj4md$XcNL<=jh5?ExymyO9jXh;K15E zf}weCGjCca8;XNoqC`YRxkzwEp}LbSOLLdkUfxj+WF!LDWlA!^TTLO<%Sd#@w3kei}^h`VX&X?-%sU!cPeBX17m5!{+e`h7EE`DymaFj{O2=uTjhb-%weDO*}l)XYs; z6XD@VD-~m$bt!hjjeeqbA)!yS1e$EAJf0f;#+sHug1KZNl)`5ex)mZ4nQ~wgmWe{p zGCpzP#j;=q%sbF6W3>-1540EBER@zJ$kSY_U5;a!C!Kjxd<7t9*&yjLJsaT0&Ar-Z zd|tNY4KPw;>`Eu2D2&bE$|gSqrf)0^!@Ua>zJXp96Hz7e%J7O=Z5^XnWy#5)vl+p& z_MFTI=W!qM9F3Xu9MY*>>OvJJlV~T=+?4}p3^{I8Q_*04;j_-}=iFy=wGLi}lGiH~hcT`StRPzoSH?R8^7dNS&b`+~|Q*Wo}OD zRO|hy^==nl7$4kU%g^GEu!FKCK5{K~kH&S6B=u7jq2*B?)!p{!-Ci415$P*-L)wg) z!4w8cMP$c}>3~zZJsY`Qpm5BL+45ryi0L!L7n($;-b)$7=k|?jjJD;#?^s99pO^=<>Art&j7)L6ZFM-0^ZbU$%9ONMacjRM|mC~ z&jmnj@woJp5E}xJzJ_l`tdBs_H$&736wXirIe1IDV2^J5c@zYT;y&ER+ZPNqn^5d- zwF$CJ<(Yq1FJpMI&|5~w$b8{K(8P8;T9~iN6KJxR{wch07-$*B3>sR8%zGqL2b;0n zSlhLE@#=eP)9G<-xhRj-7arf4@P^&y_sE<&wZ&zVL=h51cfVnVqz3qQ zDI&kUVO}h6iZA6Ax=oGdyS-v658ykU%OmWu>RGaY1jTbS4_v^f_DeFuS=W&!AeOPX zT}B9kbKoVD*N)>~&EsDMYj$Uj`^tUG6kfp}juU)aF{4B97NO<0NOg$d7MjYMV+Q_Q zvPq5ZpvXcHW5wLmmb{aHdhRozr5*vy;)BwxQ4Jfy-F8EEcbEa#IUHJD>hmud`aEZsTUdgaJJL*nJuE1e&Qds|Ht(Cu!q&I3w&`UO&w_9mLB^$3dTCY-#d8PF%V>f>%+2eo1gOBYs z3y^mqRCWE;A;6s0Az`63;b9JvF%_6YRM7$E0QrsH3v7|jT>)(2a!L-D`6a81+bg%Q zR<{yX|3%5szRAQ)t|2FP8j6mj#oXO=1PNTB;?UqVH;>kbeMoj_%sqvb9@0aq?<){|!XM zWp{_)9_+n3og{#lx|e76J-a@$@7bBM-g~dm8*?%%G(WRKB@3a6WQlU9SxYpNmRY0d zgsL@aDlafXR7B!_fp#0C2mNVYTSsD;Zzeq8=GvhKJeH9yc!8LqUNk7N^5>&kmyT8cnGRiJWMx zF(-bl>cM*>S3luOMeo9;=U{+;B#lrBHBI2R)_hp9aVyM$`~)v%+L5RuLt*D?FWalx zY^PLvav}pn%q3zTwO>EvllF4|k_4plH|?0OaPGF5rz=ACAA&1Lwj)esR#U#nJj%Mv^yi{Xe@awm z@*!~gnuDk>wcmUI`QSrRzl>ejKFIM!WT;xo5ZM1fmu7_*5V7D2S*}?b6jGx2vtrPp zdscW*r6`=zXnzn?>twGCS2P?-C!YIOD`o+9mrZz_}Ax!))ss|!`K_;>R|0E(2p8NxP z6>arOIKWS3oSW|Iu3DsFs>+_K)P+JBKa@fV?=%RkWpi_zdAN}+Qe-~)JZ4;?UX88& zPUgvIPTjH`<`waU?@+XrnjgI*$jq+e+^*m9Xl4j(Ss%3oL+v9QdqXxuBsN3SOQg+USR4 z6vO!h(u<7fLb>%yk3X|o&Q_J@?SGV~K;;pwFq;E-jJid6B(of3&H#Z|CQi1oqHUswjxjlV z{-E-jhxxUNr!Hp%=X(maKgbBqLK4KTUe_SdYEoh+nB}lOh3RRDo*)_hg%eUE&BroT z{kgj;^J7^z(hmz2SoENm)dmmJZT7iFRuy+HB*@l{^2+fdB{`X#II1+&ygd<+aO5NQ zm=LQsdi7XDSE|~E>%J}!ciAc!D!KZc8)sLttYb560g-yi z*%##)+x>v7e(BCYRkRK;%ddNe ziEeeA2-QPB7pN-5{BEc=3sjZw+M0g?E73!i0SQ7ghcFl>=6nmJ2;;`U@bDWOfsy#F zFgAeCs!5w5AK?3)&U`3!A6EACj>Q!+oe-#3qXU5$9_5OGdi?U9bPwrr_*CZVW$VqD z49#FK80-ZDvxWHg3$yaE8=Ec8Mh42MM|#!&zvh7m*z&3DeIjEoGZ*=MP>ux@J`T!u zonc-lhh1H9BOsH6D(duxl%LRLYP*P^Q1Hy#Ay?eqMUmF_mnAgS2q zw<`LN%6t2)K$K8uv1)L2mGvztpkIxg;3~A$vo^s$T_A@^@)2nOI%hBJ^@d>pR=Wao z7YGxyk@C$kQeZ4uQtQQL`1PQ1CF(&wdyBm^HO#+cPykzO7nA%mU6V%u=#JREw2cFb<9> zLX$mTT+U-C_Dpf9`hBTbG@U}RSBp^>vvDod>c3!ge&Z)j)LVSfG&g_adZn%EhZ;q> z=v!#~i>``y#X7c=Ir~&rS##2U73t1gM&`0Dz_hEYeoO3Hr-X5% z#$*emyYE#&cL>c1P#z$Z*|{wIOnk^GhX1Ed7JTAEcfheu&%<}-K3u4L4syf$hT|aF zBRjL*65*DgN>%q*;ZE~41R=VyK6Zx_LmM}wcl~P28>DR?(s-JmCe7nhtuHPkJUj=C zL~zb#KYqS|muQ3H?ZY3x)gBPnR$CWcA*0gc&L;pAsBKpWnIpC?@%*1<`fJ#I70$fK zKwo-|&i^Q(QS9zyY6#AyeI@vf*c6=8WQ6W_R)ijN);N6W#;hpk?=hzw=aNmVGGnX1WJm0gVo}re3!Y%ND1E-EUxn zAYY6vTicDzTlf0&Hf1V7^pP`v2(=$2qv3zRxz)_up7|zI4@uA4YzhaAW7gz=;u_v& z(tSqrl=!5Q04|Jau~fEWxzBTGK_BDx*PYU*PYIkZ&0f(fJD%Pe2-RHk~_P z8Q=v_@{qGSxYb0jWovsVuhF-44-P-)oeI~*9iMj8@ZV3D=x@p;JaQDAILv}w2T&E^ z*Ryi|sk*uwgx<;Uk{5VkmNw_KmC~xHhCqC2fGv4coSL!qD zRL*s$&1mwoMp$L2`Q0xg@OheQ&|6OsMlIc>3L*$qiqUH_4Iw)9Bx)`XSj|igku1&l zrJZKg&O=ORW&8%Gf@D1ZexEV*Yai;-j!lR}_%~#%fm4iAB)6(~GvmqB?Z*XDNG>kv zwZK6ZLV9D6z2h?)Lym))>19@97ef>K=%umR?-@{20Ouc3N%(%JXTZDz{@4I8OL%zC zc#Q$+)p*YRsWam8+fR>l1jV4u+9@G?)+mgEF9pFcI_ZLOMB3x!H-zg1!8AQcO5{B% z5>HmpiH80J1Y2OY?IM#lTt`0x>19vx+CedW4rmAap3g9mxvhi>^6TaQWsy$hOdpbAM4saqA{5G)IE-s z5ou6c9ue{O5nBRfTe^1m9NS_uYXw0_P*5DS)=vPD+PDtJ>Wg`b&&`Sb-OYpNZLR)N zsssVxm*4Qm>niHvl0&OMSCSpAeoVYR@W*e+@ry(!XzU7bU_|$h_9aZ0(d8>SU~JI0 z?)8=I0wcEgjU&FUZMdOs6ui?^au5@P9KUCmS9`2Ada%Z)J(l1n()TZRZ(Ta#@GV&* zK|up`tF?NJJ8be>$FJ}8dGfEP#jj222pDe%u$H_c;Mp|qG7DwL^)wq9az0dFmvV5_ zV3pd(xF%i)6}QDVZkr(74gPT;+<@av@TqU>ZeQN!*fsTa0neLZmoc+8^%pcynfUzZ zE%7>=vIvQMNuPWevH7(?ht%2`{hflEwR-)r*Jc0_pk3QqFQDVa)>?lJKODBU3MShOd@YU~Xx|2Rlhrhct&+?&3w?R0PJ zet%w*KSpM#AHQRhACo=>vFBM?K4Xm_A1rAEBLPps#6`a71vdc?0pg4={H(t$;2sq) zX5iNYyV&0acFBubpo@=4tZuCi_xt~9v5NZKNfv7dkjO4pwGd^o_JXUi!E?-~J=`SA z4ZXHBFz9C_dKQph*3%S&5ve6)Bog~iLxfhgiQtEhb=FANS#W6db?(sPYLf&;bOIBC z#5p?I2ghXaiO*cdEq2*{p02J~xYY!;CfBaD?X&xPktY1{oSz&6iW!6%!(V|^0gN+`pkqvja zCemLK!gv)-<(%IrFu;rbEp?5N+1b_eHaqPLE-kXtF8g+p!jRZTFe}5z^%+WWs&;5Yis_Exj-lKIX2d1zqquDHE<0|?HVO~ZTCSH5=K?w$A`t__hfiY?|MT(gN&Ew@@iI-xef==A zEr6-Z%?=wzy*2Eu|IVzaQvpa9Rwm9CW8=wLMk>Oaux#J+K=5q=b6y9s8}n1E-S<&AjCn3o>?QgUGzlPQpF z2UcY}1A;3VRWols<8aq5U-VE-xLPJ=IFuqP9noj%rKpGn20GeeacK?DlNDTejKTLY zi3n4*KjUC=(-fmlWfdZm45%eci9MoH0G|}Ytl#bPG>74G(euHrrpV+Qw6xY66Ovvs9~>u)00S z?4K?NX~4W>XwPzpg2~LAu!G3#>0jaiFS8u{#x2E@{KzQ0FWSXFhAh88$%pLfUtjSx ziwj3GCqZ2Pe;;w>frvm{*U-%W72-060B3bjDOB(PpzKCl`Ory%-jf^HvkJhg)aHe$ z`Kmnh;4FNVpMbDP>Bd$YBJT;d8Xvv8wx)*QoVAJsfq7dCS0MF7rWMeWbsM9>8>t>* z2i)`v>ClaJX#J@()#!@dT`NfGq6|{1tix?}4@P>msAmbD0Fl{wve!C_sfHf$U;Z|azx29slw+uhPG2(!1$;)7MfXSp&*FOeLgKfmR=~R zGlPPv{IS3P6BGpPE`&i33YrHBQU!WYkQ^Q>$;9Q`*R?Z?f;OOK31t%x_QpZOvp5KR zl-G@e1OxR#KcFPXCdqId4pNN@4)U~GD5$D83JNI{wBetlpk~8DJiSp+rrP6Ckmwkw z3)b-{sKuhEBL86K74m61rDx{*fQZc9KkJ_P)8<$+-;H_v7Un^-D}#B8r*~tXuwWk9 zuCPJXPS0ST@ciCbCwv1NB0Pts6TYbzrumo4MmEBkY9kCkVIwH=dXQ={`u$oiw(q6x zkmbsQv~n}8sDqMw91O5pVJyU*`lT`6LG!ZN0-g<#UDCj}R09Rs2GW7r)VS#HggD{u ztcVh6L9N`f)fRKnA!lU#Q9<&g*P4zLP{8#7mH}G*LljK6`dR|E^Mdgk?Fz-B+$Q@) zb*WCB#mDu^O6u;|#>hL;u~}K{I^=llnpWgXYVrx4{GD`%{#ZwSwyRYjhtwd*Drb6Q zs=u>Ty5wsJuyBMkfoW1~p|d`{u>WFM8|ix&;XdnUSH+5GJ2W7uzSIm>@pC~TL_maOCP z)QUC6N)xk7^O^68+K3Y9^-BHeNmCb|H1+0_riLU{WbLqfEZMNqH$af0%!v*ofl%7L z*IIfI_3<8IM&c%RN^@$GoJxb>KF=dc2zn!Lo242UiTEf@P{(1glL&&~Kpv^dHv>|B zED%k-uHtC+689<*eYc#qz#LB2jC^+r^4)Vh1qbgyzI(bQ>HhdXz99`aoK4zNe{OlS z-J2R@?tCy?+#E_rky%8Q{<7#F37f;xa}s$`aH#Gncx12LhHHZVxq1U-ZB;_hArRzJ=L!(w!rKccZCGN2j8?3}~D{;M*C^(FqC061xNz90OM8BfC z2M+zn71^V}@USX69mCXu+pLsYD+L*G!S}3`JFJu{QXaEXW?3n-Nr_u2mr06h?S%D( zbTID3M6^Rx%t}5-1dJ1}PjM#HhKI5)Sun`G^UVHI9#x6>4Rqz0IPnr$016V_$}5aP zt5c_^wSaL5qtqH+V@I*k-Z9=X(W9fa>E%2ke@w?CF5_+9=b92HMvoht$jPduDS0bp zZNz6DF{jI`VR(5pqt~lvU7iJ;a+d)h@4w@f5G-P^xn*7GuX{<2E)D@fdKLE|yOE8$hHuO28Myt%{(v%#uQb{@^lauiWo+fY%kr_53Ie%GsX&%az}Q%6 ztUS3+!4IZ#uFetnbbZUx$C2%pSk&>5R@7qce;(r1fBsYdACw55WRw5PZ7O> zFgN;mEu+As6s-%4b?~zlV8$$~a@t3n-Al}U`*nzf?;fhu_ye|#ia)22QRx=>_H)vC zgE=sm1A{q;f4|$+TX!sPOBI$|(m%B|`}6^*cDK#!(~w1KNi^|opv48AQM-Q#TKv&* zwq_f?kN(cdY|R7`FYE+jCmNeQPYW4koW)jSslsk!Gp3onB-Un+N?8;ON18Wex>VCs z@I_gdqC3OdL|h=Y{_OqkbIsDL?4`;z=N%;V;_BuLbJV+16xTBY&9U7TN?oa|Q9lrQ zD=GqSD{Jw?^hQ`q>7r#`=BCYDd_Ttx>Nt{zD#C5d9g>nYw*Z4ZmoQ>IaY$(Z1&#c+ z+FJ&eqlJGW8d;b85u1>6Fv*pygFwEO?6GT)AF zc_w0vTb8L;sRW7Cg;32f31%RQzdT-y`~J^+$g^+3SZ%alrVEH{Kx&5H z2?a2yqW5*jSGuQb{@xG*LDnhW2~N|II}XDsg0?hQTRPa$kncdDAnvsXMSXj}UUC$R z$_2j-!MUJWrI<#CL`|9uzz0xRzgdBBw7!<_2}Uo?LLW2BgzHkQfus`;!^edYVibqMRM*| zzsURKH~y1!Nc;2AkmC^Mb0W*dSIbH)M)j_*2(*cxn`5~P&cMwb!^dNy8DqwS`1;Zs zhS3W4uphfGH(vd?ke5_K-~6fg#?=1&RnYMzOD|ot!YAwQo^urN-VS*_3XiKa)`dzo zD0t&p#iuRJALwgv4s3wdyUK!~NkjC2s?tSwS``ouTAIh=FFckCDoap7@WP$_hvn5g zQoiE2@?Vl z^+2*(e&s^aRpVx@NefoztQMxJ7Q(d9HE)Dki(entgEKPUl|mNO|61l7N%x}D*#|X+ zm&cgV%VO4y;(C;!Y0*3;J!I@nzK4w<2{nb6#bz<@J-H~VML*?CB0T84<5Wmzh5vr5OpS$-kw|8xW{)Z&%07bI{E*j~{;inN9e5;ArOU46D}2L9O+ z9&DbCf_PR-=+>*{M#TAp{(>MX@P$OsTa7~z^1dlWT|^s7z0Yp3o*F|X7|mfFjVbz} zAd!1vODu5~s#q+zc|21I?LSG4>7AIb^sO<2Cj#s~Li0r&{sw~m?TkF2Dj0BS!!^SY zD_uP!S;H*Rp4k8;rHJ<@a_|Dz$lV!lKYz59DEillx9k!cjltnrl;4Y#_RE`p-R{2 zp2G|Jv&=jFxx<`F>sT+rb+}zGd53H#Qwf#pk?8S7aiE5t81S25j>7_}Y&GoQM-pgS zs5?s1L8s}ze3!lsGY9nabvk|RR!{}XLfsU6>t;?V%HpDBFXqKkR>Ueg{goIsV zZoG}tvWk&L`kH^doaf8HwCxKjm^W&}j{J4h&%nYHzyRjAu2RncF;c-fi&5|@N<9RcIu^+{E8#kj@&_v=9qGGF zEJ*>Yy4{MAsP0p0H_A$sxZSixQ}1Zlhk6-hIiSn@*K_{SOs@8)k;R6ym@K)?(zN!c zYl?H5wLbxg<&CW2G2Lm^+BvTMG+ILt!*g6FPxJeKp=%S{_~$I5re@%G5+65nw*J=Mxt^KC$K?PsbWfJPvK-ljsK;6knf*gNl@2Nd6qEG+rFhpycg0%Z}EhG*o~%lvwYjeaKHFYhOXP#o33l|4=g9PTR5Lp zJ9m-lGD2@KL^lGJIb{J5OhH6p_-ceG!|YXlaX2qOQGF=Nf$}=i`fv33&3;R{(e7TpNNXg2i`2NdR3`I(A=G!R6&()!Ly zCr%i*(|$Z%U{iTx=MCc>YMP`9 zmz!vr$`bi0UQB7ClRePD z=*}b|jHY8+Zx71Kfk1R(526*a5c}Hd6vID{Ou=g ze1}zjVy-IF{SDs(>X|%+MGs=-McxnTJ{>H)@OIv*i^<^h5PqxSuME)*q&NmJ?&DdFz5|?eN4DUs*L@B|nD- zt9~eQ8oemElx(kJ-bDK%C<)T@uy{i&>R)$W>27p&ZU=pLSqjLIYLs>2^0SaoRU#Epk|K6LKHR(^@` z&1zn?`Lj<*VQs@^Lx&yBh@^Dr?@tAMjzrA|@VuIFX*^0t+Gb}wH?1qHXO z?iWschToNYR{ybQ4WIAGtl?Fus<-&G+%;F_t!i3zsC%9tqT!;2Dt(r#R(?}en^qmU zX$|S)Ycm~t+-mvrRR^k8)8^)tdy>zMzgl%N`P{_+kguuhH?P`x{T_a|sOPQM?_agE z>cdq#XMF^aM^&pDW;K)6PQHQ`M2Gy2-ypwxuXm~Uz1NTWle{0PTFD=vscP=kQ-SI( z`J9?nv`v0D*KE09RdQ$1Me=fJLer|vhbP`7BRI@r^m2Vr>mzawBPm$grPkwTd^VRr z?DwiS6B9GLJ|*|FR_;}jd)D!}xu3c2)w%{oX%#q{Mw~*r?$d8MOE6Y zt@z@UjIJF+N|s7pOSQSzpPWuCWZY`&wG|)ioQfa7pRA?^PQJBv8LnHRsb-wD+%( zG5D6-awVN8#SF!_Ld+fm^-c0-_q{G6j6i*hH7vATeQ(%iQ|-8o;bO~{F+7Z4FAFIj zx3m$hz{b?2a(#I#QDV(&S>dG@{&A=dn%?k8-(s`zUX?X^&>I;Dq=u&Q3h1VT#kRs6 zCQc1-XK)~IgGAkq()8TLFgzYTrZegI=xI#q}m`5 zz0tjIki44YvUGewp*15Gj4imP`de9kPnOQ+T2vc+_3tth8yV$ZnXZj=Zf~AE4!%C?s$%`KG zMg~#K&{RITGgI=}Fu9G*5~iqF+aa&+X2)4!a%F#tlYxFqSrp4hP|mVegOHOAT(0c9 zd;Agkhz6-#65$wGxg`3bxFpKDBMKOh8J0()F_drhE@NSy+#?YynUm#2k3?WCC{!!@ zljWHx1WCHk+=&F{IB!Jh@gvVh;y(W3iLyDl&0bi-gD&j)8SF4USDXYgv{Jp*yB$=F zsc{t!az0lm< zR5YC#ZaOI72q<`kVHjX!V8)pN!RvS{6iq5CDpE2lD=R83G&8)UlxC!sRF-6FLt0|q z@RGjIv(~d_HY5Dsi;m_S$RjU8KeLG%iW^fzdb4m)$@8 z#x)OSHn={^)(W74Gngg#Ee~8rg0ad|3NmvN;f8G#d!4z=4E=AgFv87%#|JAMgLoOi zHBK0o(Sz`wZCsItkJ(|w8Mw&Qju(du9`2Qj*b{_}nD_%;sBjapkLf z*+-Dvl7)UPJ1lz(eie>atXfoa&3t+Zd}6a5j*BnCLujkfq$T(S{*s08r*O8i!_qD2 zhr6OSVFU}>!IW)1dKqHc!3uuxl9A1%4o~52F<6wn)#cWs_+wl!EbuF}hSBY`2D1-* zjjdK|i0&K#pH*w)%EDXWtz504!_Y&oBcebEFCvO69D_?N?6vBy?$B?v%kAJ{#-~Qu zGEp-2Z7cTeJSbruJO$0K4BH-uDqYp(r{#FY#!E#LwCDq_{q5L&3#inqZCS(LeL#cj zKdj9vM4Q!Mn}t#1s&gyW2xV{4a9(Q9gU^zDEH!*^p)gWbYCgQwa&qBaB+6S|p6kat zN+q-@cyIl9NE^D%Z0Jsx9b+M9@H$>iyRn9NspXRVmJ;j^Fu=n@Evlf)Z7|1hBYAnV%e&we65Ci0TwjAtU%a`8ty^GW^Ak0#jOF{l!+r3sOc(`1V37k} zS~A85Wt;DQ&=mdfeaiR-Zg}iW8+@h(wlwJ5Q!ck|=?DM`SYI94A`wmz@F5=<_>4c@gaz-4 z5L%akC(aMLLcs6LBaJ8rb7l5m`a#z{z>3Zcj7AIKUdKV#pg(Wky4>0aUO7{!#*&p6 z*4A1oEF9dZLZOA@k|>NJFqeYmnMnAsD14t1Kc50+5xFm{6!RRi;yinEDXw!Uq{vla z8{yl|Mfg%YY`cK?;I<8Z0gtx}6VNV~s zp1aD`phEa_$8ZP13EJ!fiEP}*y&AG}5L@$6NUkHl!6^-1TI7gy{R}_x^+jse=jd7J zvuJqq_^=wsK-c?%$A$ra2p(9|HgmniJmHm=z_2aj_cPE^m?*=0VcEx5fx#PS_{yu2 zKOBrL35v$EzzpO0RndKq{(j>YTfAqT#%ukp@gN%@2rgs8b~QHg@fA_+ts0o0!*j5{ zwS%v-u?^Y=mVgeLL>21>Q~>-t;4tzfML-K!9##WC!}-r@xc-4P!}pfLp08DL)vi}y zF`)>=?KL>yVh!W6nCoU878Z^bI>=&E=P`x0Fdxre17Z9Rj#@qx`?3bC;qShH?|3gdixYm@*71r9T7?!i#WMH|u2Z4Gwi>Yo1`>Ra z#Ig$J!ESTFYnfns$I-7Ld#k!s7lH=zVUXPe9em-1*3pV>PSEb)3zs4tp&G;Q^Ic_I z-NAPZd}9W`r8OSDF%tsc(i#Kbn89ypjfZc{guu77#=tjb@LO8#?!#|s0k^_4f*tER zbZplVinXIJY$}9ZCK18#feC(U*$k<`%o0v5Z^N&`XiXz0U`h4;>q5*%USJ=@Tm75G|c==*TP{2Nyh^i?z*GXNWCwD6=LJWT_Sa7P3ePV5bD zz|+9vVsQTJg{H2mz*J$Y#|}1L^Li=d^-{>|W!39dpfi29tA8tgerDI(T|WB^KbzkE zmq>W`d;hOO%5l_(?~E>~=@11EZ!g0KIp6{0D!A)exGNHT;2q1pY*1>o1VWBm)zXKp zZo_!i0Y93(yo(vUp~3j?cI|=(MX=wndq)nqIPVDpm}o^6hC=)5gkxZj3vob$Zm{1~f|GB!ZiZ9-myU7#+!(^nohqEN_jc`(qr}gmr9z9J7~ph}w0rQO+K76{4CcjkH~hj`^Eue14EEMOS$zYM_f0zP7i=OFm>7Yu6tXO7_q z#i29cw~oA~-;g4AQHGF_IJUId8C7@)UT3B046guAguAH~evvII;29amgN3?wQI8+8 zwS^DNLR*AAtM|aJU{LUncznO@1iXh!#DypyPNBR!>tJYvH+tjS z2h)a6@WU^Rv-aW#_e-M+zl1x3+K?r`cEC?KEICmJ&uinW+BzhB2@jmYDrxhy4h~pH zKUxv;@J^TqE!pKO_XNAncGwO(hIg@TwM#~QTGmWz-T_|P1cS`SyQ1Oc91wLR#0wAj zB^=uMtIyrAj-QQBh5y~bS_wnMm$!I2yV62BI3Tg3Ah-SiYtNS5!lx?a;cr78{xavW z4xfDm{&?(;Z1L5yLs77t5{VW;(6&MwmcWlzK_fj>2oEAZyz^G&!&MN@qYrz%mhAC^ zcW~@z_V})7IH89)hf59@+>{Qvyt6~j?_f4qSOL+EFf`NH+eaC8M#CKlP8}8Sz`gI8 z6I7L+^=5YRl2x#F)e3(ogWApT9ufE*jK9nIZ!P{F8OWNmJA}|#L;k=^QSahMEIj^( zQ=_6bDBaC#%VlwZej@5G^@LAG9uNAM2*@?aFQ&2ZMflr~WF%+yo&-7LgGR(# z{mnwYYK0Qx2=b)I_#Sww0McS{Rl;wK3!O-RgG|1W|7PJz_-|HDMf`V}55&I|ep7g+ zmkJ!tkN6@hoXStszzIa_I`|=^3a8KMaLvTaI(RGi;5$t`y3rZh<#0efmo;NE7S}$e z6p;rLgn&;)4A3=Y!Jvr-ZGN^NL>4Ke_vcY}dhYD3h}N)7)V2U2?69I-x(0lAG+(MC! ze24{pCu7oC{5OkzKK?FTQHB0LWVnAr_Izre`&G`hwW}J)lWXTU;CZ!KGQMbM5k?u) zz^e?sj;48HU9=OoRo7M-pa0K?LOS(0$;z< zR6!T=ansN$mW8fAi3mi+S3uEwN<_)bUWN7KO1nL=ZP+GY%3F-`(T&KhOb%`K(ME($ zA#CRs(pjYTt1`$oolzF;#8hgpzc2TyL9cMsTD2kre@DR=0z7utffp;w4-Se9x`p8H zk8Rhr47&SmBM}aJxH_@QiBT-t$+9+8rYY+8bD4=tb|%;EKPMkNsGDhAO1A+b;CgN~ z7ESGAl`IGT_t4cxY zD*KwwNC_wS8A%7ekbO;USD5R%3uRld4=b@m*Fp&3Zq>=4T>JE2GGn%(7i&9{-`Z~ta;~V2)-zoeZg`MvoGxU~{qZ5--X*VN=I~M0Qb1SH zF$ly50{6(TsO=Yv>j(TyKqLQs+$$LZvC%UQQukB=tC_;+4A+hQ?ksRqYbS9tDWI~p zJ93)E+|xjoT)S%uOPA?Vx)&1VUN|(=o=sq08evLhN+YHuyJopfxy??UE8Z=nBCUP! zU3n}TxLL;aezXJJ=}NphLlEPzvJ0MF-$7Y>b9?UsthT z_(fAb__aUq4~!+NS}j=x#mXl)-chiT)bYo+4Ul<+k(9n5F9R}6|Uus<7`h0 zgXkXr>992Bu2b0m77+~J5u^fe`@6pRQf8%l$~p?-T9T)f7WlXV;pZ)_QXWu)`B)VJ zDdCD-;j&U@0!6Kb=sz+#x&ewaYRg>Rvc>!ZHx?i~AyfLl8FOCILhBd`eJ_QgrJkLy9$DSX>0 zhB5&2WNaw4yBdUJ&$@EKTMYy-p-vP*RyUA1Kf_$Z6GvO2zrnYsK-fr{5jZht2~KDb zntgAW3xXR6AVGJK58ssMjlI5{2WjOM1g-&C8Uc9!$z2#*;pQdvt|M@V(<6ZCEZNi` z0Vq6vW#nkLDEI-mLUQ28WIWp(@mh(GthI_$w>(2AoGBBb9{pV>Hi%)vZ3D@YU3=ZK z20fHrwguvS3wIE9&j{kvxn}Ma@riUdo@|&dAGppC$f>qeSK(!>qF?2tmJjBWSHMJ()t=+>xw+AYHw-= zU*R^tDz4w#Z-p;m)=mSy3-9r-YX8=Qt)$wbolyF#+DAMBzy0wk3fXOjQQr)PjQxju zbyIP2`Je99jSvd5`x4ln{@tl&!PPrB9bqz|gHTB*Vp>CdJ>h$V+XyQOs|j6%*9d)W zJiSK<0|_S)W)M0F3kXXH%Lw-p{z!O{u$`U9-@w&4lpETUN9u3E^1`#yb=CBUBN_6Q&X760RUzO9&6p zuz0Eo>j-^vxqoj$72za82jLTh&l7GW{Fv|%;c3EpLZ8_@em}xT2!jcu2r~$m5|$9Y zN4Sgd5aDUUdcxLuR8EBb2!|4e66y%k2y+RaAY4PZmavR)J7E=J4dLH}HwoM3b31wv zK1Qe|Od-rAe1fo;@NL3vga-+KByF z(#7NR*7xSUVETvUpyjsoC!QZ5yZ#E15Ir(1W2dj1dGo-W#x0sg}Iz>gx}3i818u>6R+ z0sZ~qWUe3|QO@Y^ZJ*XlkAg_jsg_REU-J%c-^N^rlx@j27{MbfXj!79Tk&GJbN>*zu7uAh8<_HlqXXK@0ylT|`)XNPGl^bNi!yQf$P8 zi4k$}Aj`2E@wKgzDU~ZC&(>$L^z^x40F#0(CcD9A$}(l^9TxP;JH_qy9l-l74_&7w z4C5$O5PCtK8p{>i#{noG;jlhxWaWkBBl>IVLmoYBmsa5Z6j114e4P~@J^bYlT;ZjM z`WZ4k7GEQJXiuG&o^Q|FYU-1$NsvndBB$N&9D5BAC~t=_|wh(2lOjcL=0&W_{qh0<0G0cy?Juh56dGKb$V#v z?|myGdLBJ}t!B{0h(A`A4xg-gbad}BpMrC%l1AT`-D=C0@@Gevd^x(;Z*NqNZZWU= zkC&@%j$RzS&!!I@Jmxn={x?@UWsJGsln_>a-`X)ByxO(j;e&_9JTSK8;**QpXkVYU z`~0JZQQF_Tbsu(Zx=pJut=W@z=?!gurQhZ6KRl+5n*Yt@z2`bcuALTE_etjH$i0K( z&%WMzUgWgW%^&q$`az_<^3~Eor%pw#9`TjWf*JRZeJORp7x&yhVQj@eKj$9OFB_|z z|6J!W+qR8WW&dn8Z>}4A@XO8xPyX6_T*!@XGi$p|8TZyG-HAcEqH*^|pPZsf-7_vB z`IYn0^RJG}DsP$n>al@Q;TLwDJXvdq>QZ*$ff*-@qwX)UzMk{IH&IXP)1EZhT0~#E zs;+G@J~-M|zxua5DcR9ut`EQG(|^`ScbilB?W3O^jb2wgVzgsZ`qC*P?o42>DJ z@8|EgJvKY0-SFpDnyzh%c{A+iD~o!ZhXNS4O*nUBh3@`WhCbWn zmkM1fo3$eOsjtt@BCvsp1MCMcKCbSvTVIfvHK=JdRK?cb+HdrZ#CAgKOEaT#{cuz3Vq_<)&Kq8 z%J)=p?dB)^d0&7%Zv3=&UDNB|j5B;xosx6;=eP&0`=;Nuty6rTsNq`*GseU}w(iOG z?}g5fpTB!epLZiajGuP#YPR38)A9CMLH_U9_TZvQ_PYBqcl?|?e{&j?h&7-C*54LcZ_oE7n4Sf zD4)E&|doO)VN+b*WsTR6IOn(>(ZmDs)TdX2irGCw@l0& zA{F)24^R9eRJv%|YDxU`QD^Xp!>=Xw3o70FV`X*XkPvNN$Lse@-svB3=8tJ%lh51g zU+)u?H~HPwxg7?Eyf^tkx3^87xlT-0mv-KBcKv-*##eT`^k!l7l)wj+SGBVjPg&4= z-mv?-d@^Niz|>YLKc1bk_(o#be4k!ZUweGunvxZhrmk%J_|~w1l~Z3hy(eyL$x z6d${$wYWSrdgE`sf|mB5ri?uJTxpv);w9YEMYsNeCqI-w8)Xez) zr{sW%tsFBBblRHtcI&rhEKK;l>qPDG8Sg)@du+(6dy}SaKbgPSs!g(;neO-6$^}V% zKN@lF)O{Z%olm+e^Z3udC;j=!{)mL{9!S0zyRLB3>#@ln^nLQNO)*bG@9-ay(Cf40 z<}XZMnmgq0PTvr|I+Nt3Ml3`LzD~X{!f>jo7Pi7xdAU!`Q0xum#DRVlB#{us6~u$AGP^`|0Y7mhIO zT=stEv#YI!!1R|#=Y6!na8nbSIH~3b!@7(|uM8RBo4R+`1xMJh@YKAIl(C&7@>3_( zY)_l~{QIdx+YPO`^4V{xVXMQcbKdnccDBdI9IlQrp51rRcj=fVM%UFrU5wqzjrzO= zQ+7P`mvPkHtLwi%+$*i!!4E$^P>_(edQsBJTQgUsT`l|cuj47Z(t=-^`Ns>_FQ+{b z-s;r+Z3EJejD7h1r!MN#mp^cJ%7?R_Pe0T9&GQf5b0GcR***z}3z}sdUGvodpEE-; z+K>2W*jL8P3~l($l(i4Nnla_O&#o^^|1RT$vG2}F-`Upme$1v#HRT%9l$4`K{eE|t z&X4%I{c*+HrtiZ`GYaEtOzkFBhQ4R&JacvCoWtFgM$WXio~%`zS~$~Z!?1v$b05uY zyJ}f*voo%lQFU)Tv9r%ZnaWB27mH`dWqz6&IB4O-g3PyDFZ}11nL9H3rCMg}Z+;r*hw9h=OyuRq+=Kq)(%`P-~^yH@P_pl9ps)wY1a55E0)_KWE^j>nFj zmA!D$^%s6Gc|H5M{@VPWEq}@$Fn{}BYfkuDa!WG%jkrI;vSvx^Ej}r8ETcO0o)s~_ z%yOV>*4NLMoU|;QR_WZnyPNf!;q$8|cGp?M3QEGyD3)5|-`?Hjkw>>$Yu5z5Y_XrS zcKlh|*=OU!vnF*888q;j#95h>pZ;gt$4|}r?dY6ef&zEXI<~4?*IhTS%!)g|?q%D} z0NatvVXwCNJjJ&B;+o}OX1`#2XVA4XbH{#dyE|!~eN{|z``5WstCS~(+84#!T7LGD z*B+kSKtTjeszre+`jPV@m+H6eqVR|$ZKPB(zBkq+xUhvr^07;%*}5$=bZnl^ND|2 z{E_qh&8T-5ger1Zs!9_djEm157*qMzsr2Q!pX%q#Jihbu+%EH1uJ8ZDh1@A60}FG% z@t%zc`|D_O!LX2WcX2&c3?Kw`b*_fqCT@;vXIH zR(js+%C|o%c=naN^ciEVL*^aIYj%0sfzbQgH#+pq$@8wof0Mb~r)XYqzukM^X}f3MzKizqN$s!Bt3O|qwn;Z|{^Kun zugHAHFu(1no@K3P7tfDc`RKIDH3#RHU3qc%KRsG3D9MouMxGqDpvAM&+v9%DT5w{j zX6*R4*Dv_dR@LLT_@fJoR<}HPf4}w%xBaj%oe+m>*@41he>Z%;9Ol{ z%2uS=_~bcMpQJdd)$9kIX)_pxR51qL!3OBIZPR>96m@nXwaa* zL4yYc4H`0N=%8VPf(H!`R0a+T3=A9`7!)`paA@GLz~I2)gO!5^4GtVUcyQ3*A%lkw z9yU05@bDmI(4e5epus^wK|_Lu1`P`e4jMj0Ib_g~z#)T&1PvK7WayA#LxP74AF3QW zXlUTj!9#7N*bh>1F1TsNM;>2=!Wr{3?f3ilmQ{k z@NXJejwP23zc5c(@J|nnpVu0OH(0W=^x3Ik)9v}41fk5(N3ag!zy>4mES5J<{w!8w zHfB4{Vbo`NG7k!#5D`)ygcXjb55<-04i}MU^aSn2%C#5N42&U^<_KvZ{Ijtd6!m9< z{0yWzz_`VDUeK?VNQ$i!SM_N)?rTr;^{wzVe0}T2a}6>4dCmurypr^F#5-!iNZyp)esCjEK{;s#=;z|`ZhDd1=kpTBBI0?3!j59nuOk26 z|Dx|#%i}L0{d&Tsgl57Fx-U`IgX>ok))3Ya`aHnp3PKH`FXW{w9C(h$Sq$c_Hyn5_ zN0(~MH5rTw=uFTL^=4D50!|w_SukRAZ3)&^74{rMhQeyI7+?t% zxI&2smQ77&eTvzraM>VX*#Mf?gif7tRxk0da;_=T(2GhjMdYPy57Q zPT>B4=&&qAd#IM_VOiAxm#?oEQ+*xzxEWh-u6Ucbhl$yl*_PSa3S*wZ$i^Ft7@Y4P zygt13i^yNIj)xcP)^6O${YweA6Z)Lt@tG-{NPmQaB?nJnH!fG4<^CeQafx9;SPGOLgYg1KN8v`B z?IC8UZy6tLGa4oEA7gY((wlSmm<2pe&LguSerSnvjA1yo8}#Nlm|#GP5qT^sl#hq` z1q^D29ETBqGXF3_e%>8%+Sn-P4t^YUl9DB=F`88~uUEf#Y; z%&)M0f^oNdf`5YETD5uV`h6^~mo+MHw0 z(D6>003~G%%}Gl$+W5&D!eL=YnNnQ`E5duQWicg-|jGGVMXX{MuW+246$Pn^ni!rJnh7ioyywe zB)!dq!v_nWpwBdhH6lTOXqb(OShzT|(P+I*8WzWd@W+#GTx>!VjAtYWG0e&_*^J~F zk!OJVG36Sicw2r`A{H{%Xg2EYw})%o4v3!8^KP#R78^4qE++*>UQfdl{@(3~)TJ;> z7EJezaje@z)j&N$uJpX-fJcQz>#c67B$d+m;yuo#z;!z`ehA8j2TAc~ONzj(g#-!0`wiZ^MD-#HR>j0a5<~4Eh-MBH<-M)J6F) zI53{nyLkAg;rF9(z>Brm`PB-)d%&TC1JlKG4<3EtKz}^Lq8qf?l3@{7uK|r)^0wCHR1c+tW84zuF1Q5$b3pfkV9}w*d2DAf?2gLde1;ha} z954qk3=pp~#{kX-907=B8wt1oa44V?a1`KTz{dff1RM@n02l?h0&pld9raV!IL8sb=X=yb$0IF&MpD~RV2pGmxc zcslV?;+e!Nh+`d~t0sWE|8LFX61?Z-BRPRyIJU8D0S zIrar~!Njp&q0(IV6`JmzxZc4& zjLx6rD>;=k#Fr6IB;KBQ2Jt6|JBdF@yoflCm*_SU@6D+suCwkYF0QwF5w9S9cwHKE z)x?(*uOnVaTvGD9b7ce0H&B&#_u9HoRB^C*-gs4_jJ19Ixu!7ig$w#+h=y0v1TJVS-Mp`fMGO zT|X+!CcDFP8G-AY+~1PpP*~Cwsg^8ueaGev+$Kz3LY@Y*JcU@&VY_Cyzu3dW^lX+) zW46Kyw-s18vI8$b^(pS$;2t1mAM+=ZA!~3on~ufMKyEXpL6%u5c=_9LhY)uLn*~ab zuAbj5hK6Sf;?KG^sF$|27$?f4ZvZum=*2?0A3)b zYzJf{$0}NeSYNOkjjYHZB+n?7pUJM!E8K+ya|XU&OCi>vvyG0~FlU8!GdmlD2oAN# zDo1edEW-Ll3cE5=*sXd4tlELa875eOfK0Fj4N4#US#2WXDuukQv-B*m-odtzH57rq zyjf!W5VON#U`4{`!W5nvpnwL-TMthd^x@JPSNFt92(NGS$8{IadJVNIkG(RvXPt(% z6Ik7m6(tM7`mfBFb30^}w{7Cg6mGAhLElEXe4ubwx59wS{btc_JpK*3nb-P|Cnk?R zUl&Tj(gy>enxRSJq7lz9TT!yxu^&T=HHqXTcVSa&a&s?(akPT6har!ZkeyvyLex;| zIc6A<_^4#lXVd)>cF%=%BIvi!Eo{_jsIu_(3xt8WDBMxuQL<$;c3%b(LJ@P=Wnl@J zCu_%El8v`J-RUs@R;t&~u##|3$Z`mi;jm?r=`!3tUTFjX=HkG`y4Vo3{lXC9_o9?SKWvn<~KON zlP>!7yf0W-&B0w{;4N;yvo0+ z{r}2&38ka>ne)Vx91r})QG_d}a83#*>=5@6$|#)h7vVG)czW6z9-olkIL3JouW*&5 zzmeotwiIfYG#7b1({A!`VjbDN-Z;i)$+3o{rI}zs4K7Y_9gi)7-tOP{dLgaLvUQ_$ zT(8yf6RX80Tl*s=sNaev?9pE0c6#^6$|n4^mz&UU!e4u(3H>Jgwe?NtH{q|n+Jt@+ z{@QDe>tp}Kz94tlFI=*Ig8c^dus`*FtYLo&2Ce{qbQlixuzy=+dN_U*G@^(4%3+@P zFkICg>eb(&o?_tb_F+B-gnQC$3EbL*-X`!D4)rP;(ZhUrKTjYj8&7v6_zQ>SqH9F2 zvG_JNqKEltImilYCA zCfw7L`)?q9F-{ur#sb)HjWc8zQ*+GXMg`2wa8d{o?zHc3f9da|b5shTzHneUa*5|J zmRr?ePx*-Qz8m~~gFN+zYX?@~8bEZSygBpyMgKa`Q;?U456fH3tNe$wVr#!TK%pn% zLp%%q!eKqv16PuVTu-g-VTPADh=xqaT_ zM{py0Xiu_ioIy*ijp$)JE|=-yT4@dN0zh=~biM7<1~+bB9{3A~<>zcf5AE9|)58!I zP3Q#;_r!<4w7?aB=>C&^3na{CC&+*7)j$*zk;wzwhb*6my zrt#<2Hl}i%Hj9T#zRB~enZfI+-3?zOluf^>@)OZBxa6yC`!YD$gS9(pg2d7uQQ4h;w1NaMv^;ag-6ZMB!b0dR$v9_p9N^w7SFM)W9{8!((priT`Z{Cc-9AJy&Y zqJ28>7Y^-9ZbT35DUs>nJh~Mg_vSY`VEsvtyHjvoF$~t~ zjd6SvHhIN4VAl zY-(Ka(Rk9wwj%8G2Y=!4SFlVE?G$?RWO~^4ihwsZ|2`no!**NUNPHNtBE*v}>iGj# z0HTwpn<~@8bn}|fJL0A1YC_LH)RQj87Ytkhh)$la!%MHA5k1uR9%m}=P_MoTJ^wI| zeON9^;0i!=!amMCf6+ftriU^3wc+hc@fUALQRg^@ALm#~Jdx1nSMIMo!LjZX$0C8h zagKYxJWol$e1&f`giFj=+AquP;`^^+;g*?$w)ycjF77`u%Z?kX%tpKc6a_c&Xg?kJ z^VKzXD&cVJ!~q-JECwzJallTj6xi9wLcu;4CciyCxBqSN!L7O+<81*VVJjWpCy2oN zav=4L6VdD~3ACTmZ6MDjTMwjlzlI`M?~8#U3OvzJjGc!7fADLVdA1Zu5xR$(|Bz=? zoseU+<-3!VEhSPsuzuO7)a^OhWHiHoJkbhIm^GFb#lv^-r@-EOeD1)ri#68bu@qHf zEfZlUzSx`UIS+4ub}1aT=Q3G473~?Z+Dosd5j_!)Kn&*_?&%+xk{GYN*UL0AJq!om zx^^N4ui*3i@FzK*e1hYFo4mabxXN++GVbqp7Y`@ShyRAf6dsb2H24P-g@MP~md5{o zc>IaiIeN!;>>Br%C?0RQ;K3BWi0liecTvQoEi?YH8gyH2!yaL|~g>)U@fZ$+&Bq0e7) zWo|4AD@l$k+G;v9phIcjgdMtW5sN2XF%{3cxqn~GvITp-o}$_E;oDcf{QXF~Bxm1& zxm^#$Jb!IL*(>47fqUD(kzbh<8@ZuJN5gw(XOvGpS z+fipPMi##|I=cJX(Tg*GtQ=(2hiVF+S+jOfQuNYgM?aaC_R93p+kPG0vGx7&K`TGm zvi06J#oG1V_vS|)`?C$U6RZj=v|+o_o{vg}~Gf-2Vv49rtqX^x?RvBS*jX97TWg zjqsEso>vvX6@ci_4&iXkQ2_^*u?7wu9LaE4;c&uH1VTWH_8~l)_N~M-3bjWK08x6OIZvB*?M`4ksKHa7eJ3Oaq4#jtV#=xKPl*;e?|C z4&1EjgabE(I&rZMHorM>0~{=@I&pCoHiJ2FLl`WqI&pFJ9zZy7ldKaCwqT1J3UQO- zy>N7f12^d5ras&lg`1&pHMct)xX}S|`qC{D}VU|Qc z@t)^6WqKH{srWX^^hA6O5(R?c>l)ER|IVW^6n9t!3g8OjB3-OHolFm_CfQ3bquqZu zZu{QN^~E}qGJv;tf5IMvIoJAg%j$q~UBbZ?2u?rZ{pL>k)lslHE61V~ooxI%V$@DNp z5pX5b^5_wD1BS1X#V6VY`nzQDVSU#%5+CaI)_UwiJtc7ObWzW{-DZHE0{qdT9P^7^ zwL}&lcGapoj86w&B2s`pIuReDce)i#=()gOIF{cg^rFT#)Jp`epn#ZOIkm6&vAi9K zd4V?;k=}p1jw<4DucOkw1F?>3g&8`U)6{rT;AL-9dgV>%o%G6oT_bu_9B#mJ@f+tU zCoI7lF%G=R=RS!O@xN9e8e(kpI4Z;zK=m6u+{J^$UfDB6KQ7lr?y8Yi6{xIK3|PF7QW{695L zRz1P@g=Dnnb`@E9{nis!5EfXt|5@T<+!t6(<#dwl^}b(IL*f3X?$;DheBSqKs>n`Z z*PY(48A)^Pb zUjD4|qcGJ*JG>oEztQ360Qj%}J%91@1dZ8;Z5-Xd*;h8TCCgtWq0cnVwS?Z+yF6BS zZh>6^2f(vxc6j&2Y`7ZE2IOzWFv71i@JW&InP-%nr9tqQA3TPC3vZY5Aw~ndMM8uQ zgkPd3;xls%pQjdfVOd~1Y6`J!vUb*qw{P$IuV}(wb8qANAqLzPugEo7%=}@70N4br zh>eerWUp%KDeMY`c9qKSDI^*6c83rslX-b-2(^TXgc*c+gaw4fgc}IU2+Ij839AV+ z+VFHv60akyC%i!@>A7A@LLb6*guaA+guMy<2?r1=34;iO2}crY2*U}rgi(Y#!g#_& z!fAxbgsFrXgl0l3p@T4wa2}zPa4BH{;Yz|H!Zn1&ge8Qfgk^*kgq4KVgmr{c3NKeb z!eGKg!aTxa!g9hILdn3x`x9yjt%OB{rGy&@%Lpq7PZHJZyFD;B-9dS5GEz1z~kPj{t~&QBtxpvV3v}Sta>}# z`t$hP9jU;DM^cg_170QU_DD)fgI*`+x$OBO;RErSZA6lijQSK4j~UW&*x&*Eq$GIq z0d6(WwhZ9#0uHk&J)54a5K=T0x9|(Q;OPszVIhqI50y%iMvj!Ep747Vc=Uv0>U6Hj zpX=cFGy_1dA9(lgFG&FbaNut^9)Tl3)eqh?BT0SWfT*OA5C^DBD!>7N{&2!t05}7V zd2l=ef#$)Fejx7;M*tiH;phX03IdIU2p)mo^Wc~PM{;hO16LUPLRfUkxhY;83#qS^ zj88~d(uA6n49}s!<4^b)h{>Qx&(YgbnJgI#Ej!(#my7iYr2_srcoUZ-$-Fvvap4;^ zyfl(ijcIy(^2A_)H)ipm?EQE-HfC!w*0tW&ba;wi9vt2R0qtGiV+{obofz3ss0a!&cQELGrnfb4@~~>1%}SEB|jq9TnYaP(HVV)Va1iz$C&@k zz@HJNnMW9ELLI*_|J9w2GyTN(8J5QOsbT&q8^apg4u;MPcm0a~6}#gZmY!bAP?O~1 z{>nioP%h2LXIN8P!BBCu&2KDz{}_f9HP13kZvF$qn$-`SWbqd28EWb`FqEo)XQ&A1 zcZ$icW-`=h-ep+Y{4a(zp^u$r>0;W#{d6IJp7Wuhs><}{#XId8s=Uh8KWEO??JK6Lj=etl`G zH%{v*o=jEj#Mf4=PRLZ<*!upg%Heud>cM7h4u5A-bw5uY0UYeqsoizG`F{iUs4?J;Y z?)`60S0%qt_C&@^l`8dI*&jKQMfGy=^~Igq3|1Z5aejB$nPk<{X&;?!{eF@vBk7}2 zr_aZzUZ2(dde{r2RnI5HXpZ+Vt7fG9*!Jx;8r9j~{&_wsAX^o*`mNgUE~cn8f&&{=r4;yKi`qAQs3aCr zk}Or|p0~R7n`BT$`Dib_dm>&n=gZ1zr&j8y9F(fh`!@enRhFeHE-NXU`iWlE>-g4p zx>_PtDLq17yzr4xH8Jk;WL0LOs?(-l{}`Z+RgKIaa&GvwY?axP9w^!Ztp2yy@oC+C z(;nTgs=GD4GRQeny>7z!HV;Rps6)p5@OUU7*LA9Yneha?X?D5#$lh%XPsdLLw?&jJHQTz-Ub za~9`Sgd*MwiZ?HU%Qq9&kLNr{$5FX}qln)*hjS5+Kp_{rlHwQs(})Lo`9o8an!_Q= z>pt_&uI8PS)B-y)w@59JHRFk2s0H%$czcGsDdhjU8zt%5&Cti5_t5B-#l z7;A~Y3W#=YfCJNc6Ask-1P-+8ARH(^3J1#lpfIp}O0E2QU#vIQ5LeFP{zZf}gkn8X zVdL^-!cxK-LZzL;6V?!l^-Zy!sdR8X4PgNxn8w_{txLiiquG049k9NM@9h<WO=|{-!wli)u((X%gKMXJjTBco2MJ>U|F6eqf%R8A#BByE@y)gJ6qN21{w;p~ zK4Jre$2Sc-U_BP!K8&lv4cB!8DaL<`596@0)#7xwMOb#iqJIyc4=Lkna~kALo+n;v z{}!IFRNKG`5mui1zn6C&D+S&LFa};<|7Jg?C(b?rV7mj(!MK0Y23By3QXx098?mYJ z{9srE#DOL0fR*w9hz-jH>p2Tb2H!1h0LI%^!!rM< zit@)jeCQg+i|_7}my5*C3pi#sgQGFTu^t_s7sS{uN4)$r8)M)NMDdoWu*h)K#q$Cl z9pMPETKV$>u+fhuBk}lBVE&4vXrm*;k_u`}7g4Uyx9qt0-?IAIgFkorFb%8wtw@%Lyw8D+#Lzs|jleU4(Un^@P$~vX{`0P)VpE6!GhbClgu;orJ}N z8wo22j}RUsbP-DPc>4Z?8p61oY$+O^hmX%ON(r!*AD@u}?*cYSae9XYvQ&LOJlg~B z#YIF1dUhnBo6Ucbe@2dt34lU)w?4#^Ve`rl|{3Op-hgZ^s+%$LSO`eSp>9NqCZd1EeBG?s)np zmKOT(^jY;Y1#X8YJhunWcDzAwyZAhT-=F94ayxO(h_e!5FU}@}57*=NH}nqyE79o) z@pm-*8wh2JUq#55_CZaqt%bKgjeyj%;4jWRg~RVi1i;rS;>g2F z{n$xZL|95V6Us9HN=ZlhbJ&i8FsKjiGNE1qq;c#z3Vpad9@?qg)v5K)*{YWY)TtfI zx*RT>T&J#lIma*J$vSn1LA&a237?Jt6`qy6` zy7bE0ztwlQIQho1Lw~Egd@`u*&DIyxx4Nc&=ri(ydjDG^hP^rKg8It3Q$atyc|qMP zs8iG%zg$rFd+bWjwp}l(1D5NPePS-Et48kJvi^yS>c0Jke)r?&7u9bLcqRA4s~6Ra z$Gtqel%YDbDMWAskJ@d?PB@;l6u+15oHT{Usm_&HF0rp%4PM} zk1tGHTY6bNu)A^OhsQ6g9Z6?hIS*Y?f0-US=}giU^^?uc*gL;^MV%7!&Cm-cuBh)l z81>@vKK1H9p9z2Yhm3mlPo@1rPi?AK51rlR#rMwFt51FLb=k0CSJk?hU)C+2e^p&n z_uAs8_gqyQPfeej*6x~mdLPw?Ehb)5fBsMY#LTtV)bAdi^5QzzHTCr_jMQa!BeDfBq&#`p`CDuQ*iYlysKwqqXz+|j{Kqb~c%CY_dOR@gZAL}2m z2J0U=)<2*V>mM)~>mL#8AFvMVAFvebA5g;j2Pnh(2UKGHBVzpnYOwwR6Jw7u>Jw9SpR@Jtbafy)<4Rz{sDcl{sCG213Iz(0h6)*0hL()fHheEfI6&yKr7Zi z%CY_di?IF?vHk%ySpUee{sCG2Bggs&bYlGjTCx67j`a_y!}! z`UkAS`Ug~C{UgWv2h?EwBggs&l(7B*lvw|OHCX?ErC9&SvHk&dSpR@dPyI{M)nAV7 zl+Kw|{ZvO^?Y~K@+VMokqG7P!Z5#B(gPkszRQHYW^TTKh)57QD zRJ}7Y!!vqksHO%kU3bE!R#pA9M>8(PPxVym@$gEt1l5dta!NK<$E#Al+5FOnjwz}R z{pPh%hJ^{i!E&t;n@sanjhPu$h7z3Lm)^4=Za3RUeG z5E^*y#$?qOAt$E?eU+j5b@aC1cU0?DGq?TIYu#OwRf{(67}_pzysE7uq+s}{sj80a zy7Vb|F-7%wWwRgpEKh^-`uWG!6GK!n$^`RY{Zdp#k zUGv=YFBHG{(#s{UtX)_7>iX9Z^usecJ9*kKEH0= z@9*)zgAXZs_IkK?pGW%oKiaSVfPlvadcJEn;_;EARBDYr1vZeSrDvFCW}36IE!J5! zyCWxec3%FRsne#y1q=Uo=l_3q`v14fpMPF(j5c!YxTxru@wy4Iaq$x; zB_vLs!ppyD`Tr5+FL{lAILe9f&%Y7b?W5rTzzEoEk^5h>J9J_MLtNa<^RIccJNqZX z%ky(*`IU#j-#(3u|DBp){7Sc^Gd#-bD{)_6_$_{+)*WV;)V%fYEVmvlU&-^E|7z9@ z_67_R>!2BMy&3>l{j^*HSDSMfp8|iQfH~MI0adaj@y#Q$n z#Kc`5yH54+IV`+~>tU5&uAj-SbH_jm-0{03qhbAly9`)i4}kABitFAiung0WCpq3x z2mqK1n!NJmvg)4aOQdqt{ zc3D|IJUpG{+r#m0fjl4l&Xz}>=@nlVyBpw@YyUs9(gv)zo#8onZ8cePI&Hg z5R1Hh+gUD0gQeWb`#~5h6I>m`Zxf;Yac+BfI}3G}x4hlR!-;Z>V0Xarehu0Y;ckC? z-A3!~PrRH$+;;PF3URkPUQV8R2kZqxa}5piTcL-uh@FTI00W6hVMvudM-~t+}(b7`qA!wEz%eD#LLCg z-gx?9?)5O9zQ`i0g;A>y_(A$?W0h zAF=Mq^+j(J{cW6kU6jYKlZ``|`!E<2xa0AM$nuZ!iSB;E<)L2Tbzbrb?)Jg$5u+xS z2gVmiqpILqSvx>^jC(xd`Z{<1dB4DMl%D~yU7#!el;0;3 zyp;Gc;$_5fpAotW;s-dDs)&D1yoPwCkW+cF8^+*QNAj0R?z5hkM<($i((g%JL2`cg z3*HY&<=c$pN|Li1=-{UzKA+?|; zw<2Claxp%alN|Q}pxa3D^_)uO#GfW!NnDJ_)xJC4b^uhzAqj zNnA_(2=PSX8;EBRf0uY3@#Dk`h(Albn0O8Gjl@4CUQYZV@k-*q60auy6LA;uUx?Qe z|ADyA>pcH^iI-A;e3-Z&$&JLTN#2jRKgrF+%SrwS@nDi?i11WDxaSa^mgG^KO7#@} zG2)3Nw-c|S@OKl>Ao&{NdBk5QUO@ak;>E3+7tID`FP@$ z6rVrwV3N-y?j-p`#I+>X6R)TE?;)N@a&doEqVOF^oMg$uO?ne`pbz|6OSYANBV)p zT_n#ZUPkfvAzn}NbmALH-kZ43o4kJ0i2D(Lig*p__b2X8@+{)P#Fr4al6~-KCv#eo zKS4a17U}J|uUqcaZ!QlIIbhO}v134)J2*6N&qg{hf$!B>D5i%ZbMmuO$8g zaRr6Hmv}YFR}l{;dxsKtk$eGhe+u83csbMJd!UXUO@a!;>E;siEkucLR?4bbt7I* z@~I*`$$Js6BzY?FYT`SHyNK^5UQfJ?xX)X>Jj;pu5#LYTpZGrFiIjeK;=v@JM!fhL z9^Y@owIts~Jel}s#Pf*1LA;3gi$b5$yPx<*lD|Z}ocMI&3aXF6#4AZYhj;k) zzJj=x6!(5E zE>DFuLAl(>_K=|umz&r(zCC;wI1MW5p0P98kDNqu~(4?s7(30w7k=ZgKZt0PI7{qS)Ntc~GnaXR%#?xL$#~)YECzL9FlSVRe{0zP^&;wwH6vm0WIc+t1~m z{BUk^+sFA#cRh2S?G?Y(-7dKtSL@~J;XXn+_q21aKgZn;`FabsE}@U>G5GX=oExAN z8gg%W!|?c&gj}ERr9>Z|9{!cf(`EK!`Wfza!}YUZm%m&;+g*QLj&~#Ea;vO7F+RQ* z6g7mtXt$i>s<=FSj=NoRd9ExzT$eY>INmQpE$-4-`!u=B6W_1I-?Aj+xQ`L<`UsBe zr`Vh1+|z#edaI{BbB=%I`u~fyHxG>IdjH4oM3%`WlYJ2~Atd%e5F+=EJ+>MV1TBLg z){Y25wL-OmB2-c9P&D>bX+u@V9;zBj?Tn?w+DUA&|DNY8$;mYNyuW|^9&TRub+nbLf;D=N#4=L5W0UhyrjP9 ze%Gjy`-AQ)mFA!C`|uF@9_gr({L}rhQ6>G>Xz+|Z6!$-x?3dtQDs3tnv0k_k~K^59Rom>!W{B zAMKa+2Xr5=G(RRtiT4XnPxqAum(&;K_?HUbSK&Pd=lqW6oZpeDkaPUh56?foNliZm zMD5XizS8{AeH^@tcs#b(((=%F{LABUmv4N@^%zgegP%m>@pS)yTfp^ve4=m*e4j>) z$9)?y9&426i0>zq@^KK0pTgq)`2B<5x`um>G=}bP{P%fC!^guFC{Le;i_cHGpU1u9 zehltn^4ucF{a*YQHl824@5x67bf31gzUe-#$nl%q@biRJ=>B(U`@v5MVm$8;=)N&t zRVov}cYR>I|G+o+v3>FTh3+HLzheD};bM6w!j-HKW#sn@-G49558W@o{)F~0rJS}{ z&Ut#uQ%dqfIetQo3gzQV`UlF>N}iu|zrWN!OD3t<3Ey{cV4Sa$^Kma<_jY9Q%l6Q5 ztP|sW-P@UQzOL=UIA6zBGtSqQT^Z-=$Zm}Dbys)BFED=|jPrFnPsaJWSY^ifx|J8> ze4Rk?Fdon1 zt1!;z(R?1p=dFC6Wn}t%UX;Q(UuWaoT6lcs<7R8L!Xy1;!gPZe_d~;{}X2XWYg(p9dE+ z-h##RbyAsezAnYr>G(RJp2hQZxz>#Hd3syM`8>28<9y!B=XHD?sUwTmvw3SL#`(Ox zGvj=o-i7fbrvCxsM#j4{p2E0+aTDXc8Bb@NuRCQh-jBs+GS2(oEXJp?_+^Z%SO;Kc z+=1~N#w#&yVce1HPi6fD54=byK8XI#VLJs8(A?#Z~0 z@ya|s<6ev#7}qdvWZau^{<$C@#`))he0e<6_hUSRaeuDQcmU&O#)BBoVLX^|3*#Y- z=QCb~aVz7YjN2Hm%DD6^t*>f~YZwn>T+6tYaUJ8~jO!V%&bWc`8jKqmk6_%ycumGL z7_Y^67UPkOn;Eaocn;%r7`HH9m+|~Lv^@0~w=!Oz@nXgsGOnIW^&2xD#CSC0QH(cb zJcjXRjK?$Hobe>aV;E0iyanUwjJISwlkr%_moY9gp3S(zcrN35#`73&&G-ez+cI9j zcss_WuV{JOGp=F01LIo8J2I|gyc6Sk#yc}^V7v?CM#j4`Zesic#xof2&UhB%2FA^d zCorDFcyGonjQ3$YpYeW-TNxk9xQ+2qj7#%q`A0LZVSGB{TEnehO|a~Ka{+`@QO#xF2lkMRP=V;Ps` z)AGxVYZ!0KxR&uzjK?sp@}T97XWWtTB*tABPhs4Z@pQ)B7|&$fgYjjIhcKSacr4=< z#z!%p&p5oeOT@~!BjYy4UHAZQ0WGg9;~K`@7}qlH!MKj`5XSY4$1-kUT*U@>M#dc( zH!<$Qcn0IHjAt?K#<-br55{vC4`JNGcr4==7+0|YV*%r?j2AQR#<)6*mfwT%AjU%& zk77KQaXsUzP+GqR#$6dVGVaE>iSZD|GZ>F$d>P{^Ewz`;xGUqijJq+O$I~-@fv1n4 z=?i#z#*2A+#?=dHd1Dz5Vq6tP(?>Dx%6JUpZj8rs{d!bCiR&|-!u1(X=lVLTpUL$Z zU&i$r&*u8kR6m#NGoHuw8Na~wW2k-sk7vA?$G4*K>P58tZj1*puF})^C?3yv4Cn1= zd_3pzlqYfCmGTtM4V0%d?l_h5OvXbPU&c6Jk>xAud_|DXKkmz)nh_49ol^wnUz*h?S zip+SnL&8^F`N{%cLBRC^6ux4J>jx-&g$vhnQ1H8wQ25FmU$MjWR}@?)K*9A$6x_i; z!T0-7_=+9w1EJu$A_`xb#dT1WF|2-Zoe>4s3sG=g1qIhBQ1}WwUx~(bRvJ$2;<^e7 zu1})i`|T)v$A+&E;(9F#UAM$|T(80Fi|?nG3f&LtS@PZ`-G6CQ^8Pa2S1EmOn(nK# zEqO1M?xVD`dk?#v@E$0x19mKV4-?x>>07T@Pq>cLp(LKJBX=r!uavF}$Ctc6P1k)p zu~v)M7uQ)kmAv;#*DXINNl(`~`P;+zeQ1~;LrFYchip^gpROw=l)P6<*A+`Yzd+Zm z<4g3hJ>q+*U09W3yTs3ju-g{u9oNzLThe%aaUCqN)6qSTEsv;W}oUlJ{uoI%QAcJy@)t_9fSYu0wY%d5@N^Q+F+?Kf3SGsU$yiove4s zd$!VT_e8n|r5RFxz_C$IG+X-nkJ38iudKJm1Noq5d$#&hi_aC%tf`_6=(s)}lcrhxYwy z7M~f{yYsrc&j;(qGz%t-eKQx7e!xvi5AIt{scDRIro z-v;%)vSrJM$2{6OJsTdd?{vbBpI_IWx4^i#4*HkEJ?8upxPJbQ-ka8RbZ)k@%SYCM zS2El-zq9Et&d<)z2pLsl2;5&WT^lbs*5~tcZ93l`RDILMI8CcxQ=j~^FwMWO#dBlT zhodjn(foA>tZBZe)_+jXzj}fp%IoJRMZSG}{n}jl^rIGke;eGlI5cLF^ta!SU+$T-ValL)Kl_Xv z`hxRqUin2ZZL)p+E z*gWpD|6y2y6BzJ1(!`cH>WI3yMJNV&MK=EP6ujr#yvQ1a_LpZ6>B+SX^W_wEd7 z*zVfeKhGRF|LGWILuj|3|F~vabt$fOz|S9?$RD@N_xAa$miq2Dl`Stnp46?@ogHr2 z$~297ZE-!5xfv2-3Oe|ffBtv=QTLrk4$Bgj`FKW{JpWeO)YA!whAZq4= zC4Wtd?Nn`h{jiSOm~Bf=pL#Xt@*mee_|lTnYwbrPcFu0)6EUv+ryBUinb642fn)n@ zAN+9h?CSqseZB6d+yCAOEc$g=lZ?z=&E*Djf;CY3Vw7ZN~A~)AfJ2S6qF}faSAeKDiJ)`Bdt}qv>n* z{4@E3N{erku0xX6-aGr@=#M|Wz2ff0*q3WR9z3Z>^xUHbed}IJy4LE?r1u@F`%mqY z-FL#>T0MtP3LNtDod*Nm-rgF~qM%7rvs3o0J&m5X^$H8W+_v9^CC{pCSr@FNI6?&` zzx#B5L&xJ?avnXqap{A#3&vk|y}j~r_u-yv{5So*d-wCoOJc?^JlN^M$)$A%ext;A zJ^yi`)~|W`>FaGyAGlUs_V06j$DD!xJexH4#NF@uCp4@S*ZpoAH?S*du0Bn0ZU51Y zHoLYri2m`rkH7mv89OCv*NaI9y5#4b8E!4k-ab6`__iN==Z$t>wdxz`Kz#qWIr}?5 zZFVuCTfyaVN5Vs2x=wH3dE|x7=cjhPp94EX)xC11wXv<fH6W>M(WL;Isu^;lY=; z|MXphoqmSR^R~3O-zIv=(oNT|tKR+fNg+MPro>+xL(}( z8{?KWynD`dNRQO3!Pz@H^sDptsVfe3-d*i*YF>nI78j^W`1~gkf_DG|jvzi@g z6O}iA^7e>pmGL4aw?42u^W3-f+FlH~o3XAz-1AX!lRM35yXo%a&UfBcUvB!_`tP47 zoX)SG_v>gW`PPqDKG-l;oxOH-`vqxviSys|IhuCz?4jH?V~1SqbF%;LruW|YjvmzG zaLDDo-|SxXLE&1%-IrI!O&j7;cTK}%|=~m2lzsGf7eP(*S)i2hk z9ea6b>D|zgF}DY9T{-uO%d3Z(=e8c`C!adG;|r4#w(74j^$R?LJ`MbM@rVI8>djA% zZ?X0pZ>v{yQmepSi=W3w{@SP3)CQO4PB3R>#QOepxqepspJP6AydCi^n}cCVK^)*x zR)TSmqpPHH^psRiK9b5gNOEwlCONp&lpNIcrAq3iQYBYes^k_gIl3iCj_w~xjvgZ< zCyxn|ljjV{sdAR&fjz))4{ze_9Mw~6u^^91DihPFIAGh zjhB?MWEnh1pE*e_-&Ci9LH(zi70Iu9)chc+k1-quZ?d* zuz+H_cr{m)-s6*05Y|7Jgit?TC7hht?0Bg;sq#Oy{6G1f{BA)>nsk9T{bpQL^G(@& z8wqs`zU}@}xN=Q-@HOS%N@6@l>3KXw@;y&N5dZ#b2(m8#(K43Ur-@4S>Gx=#vy0C! z;mhG$Y z{vBeL_alNCs@(O+tuZq;AZqq>*@&qB+>iXuh-KLog88q$%f@ia=8>Ba^Ec@>BSJn1 zO3U|dL7p}5v#p4lbFH@_n#VbAM>HiLC782)7Wv&0OH>58{?I5&XifQ*pg~vVM+~=SrxVoub&jBUN8?=>o-Z#TsGa_Rpl(vf-572R z|Bj$O;~mAteSgAm+ef&|DH;X(B;g(4PeLcOv&-c-EK< zg68cP2txhr#c=b$OoFET>jZP&H2Vd^^KZ{1sGEJ8p#FZ#eHd<1FC?hBeTSg=(^kJ? zc*ePf1f_>}2$~PX?#J+)-dP0ATW%AySYm!7`b)kdsH<-!Xg(NhA@;ksC20J0G{Fpy zB?P6o-w0~D7Z9|y@jih1=Af1YGx7%$%o;hDpyuw61a%#*P@d^{5cREF>k-V~l}OOG zW-7sqvFi!uXipN%`soQlxQ>S~opyd3iuof5W<1X#XjJ7AwA!u_)E#j;Ov>?DJ%ah6 zJt;R&rs!%WC?y>wsQc<6LH$ObBbZLNF@~Vws{sVFl4cUryKg3FTK^~I5zh$b&j~q- z=?oVXg83dp31&pip;&hZL972+8h^K#plNMY^1C|LHf;#%PYxq!j+;wRw{!FjHF)avY?EH}S&o`zK zG`w6#(DeEU!JNAV1Z}^%pFn>ZpEV$8tlfj4dH;BVCV2@#TXrr%?Ym0^b@E$^Bf@^i z^!jP737Rs75VVc{f?!U&?dh!7Xt28H6aN#d5Y#!=C8(L*lAzwHGeK*5UxKzy%U55!8Z~sN(J#G=S)GZ>YX{$O-u3LhJV&5==x}*jKrS5SA^W(Y_G)MF& zXsR@lpy5(FLH(v#1dXO81kDXM5QP4Lpk>KHf;sYOg0^ed37RH7CTLW@C1_sgeg^Yn z@C_konioY-BQ+rMiPDpJpPc|Kl@)M&Ee^wFxT-nrCk!Xx;NO#hXV6 z<~W`uXb8JW(9+mOP^-KnXlmy{#$P#YLI_%0L=n`~X-2Tj_zdz*WPx^P^?hzTF@s~^-}KK?)T#Sb#4B=kSWRACfD8Q>!_9w|Ml1sxYS8@=yZ15q|V#oD(jlt zl!=w)L(iQ%o*#T8&Tn70u#+lh`L}ANSx%NHzB(?=tBzwg{RuZyp|ZQt6}m)Zu%b?(2|_5HA_ z^1p9h#8`JekIS@~jwarF68GbYmaeX;j`H^(Mc!Rtsw&^zGj7uA>tV9d+3ow^BGod@ zM}XXaz(~#W=FW1L!BY!;NBYPu2WOnxDZ9$o7W>~jt@D!`nHOj@qpHfSi}t#&oE0F~ z-M4XoO8q`=^zfs9#QjuTuD9jzzIqOI<#t1_ewrWnGH!MAN_~uaf7w`N%Hp}FJ><_e zO^qD7El}PxaLti!ncnh@d}rff??Aa&-KbRq*M@S-8+C6T@e7ofcOU=u`(J9wpU<@J zUhwp7+&Avsj;&}|L-zHU9A7a0W!%`Lqw0MptI5kcC%nApA1>!MYSdRB*idd7lU#G| zpH<|HM{}!Y4-AuUU#$JITjvOQu}Ad_+GRen>n+Ex65SO$yNI<^|#%x zEjRn5(b$cfgXLb%sjj_K8_0=Ur@UzrT1!q?H|Wp0!)nXA$iv=wZG+@yzxQ+hC0Q%4 zS4GbBh<20zp1fdpulXVJ@>K&?hS$FrH}dT?&pSgD*>6aXi;JHhE(5 zlxym$RNwp{5 zzRl$2Uh6)b71luBu%|_*)Nks^H&N%eGB6@ z_Pt+uh^D6OeZH|*dV^Z>pT(Mxi}ItmFaGHAS?8fOxho6B!9#tohs7$pztbmQ3I9{1yD`)ed;PF!2JTdU^s^tgN04Fz@N9pM4Cqd9ft zVg0YB&8?=B7bpJm@MA6flIzLtsdIPP;%eU<1s+({mR5@Wn~^J-X~_ z(wL6Xa@b!1t2346@~k?OJvDur$j?=Swog7CE8l#6sJG@j5BarvoEelk`z{Pk-)Cn-k0G16G;;KI7{^}0XRx;~|j ze0=PTyzUc&WLt{YKXKi|m!4oH)$@9`dogZ`MII& zt4ZEFHnW8s^3{&~l%B8S#?_uzWyDuaAs=I^4_*8&ZmFa=f0ool4qD^ma5tzKyN{a5 zosH_58(kDRJaO^u-=bT|?z+~MZ9l}wL#q5evrSt?ZkE~4C*GLY$SAF)m$Iwo%j+(isZ*6&QWP^d- zE_=#{n>yDi+)x-7-Kt)utxX4cK*#6buXtQXzHRLM`Lr&z&XE}b@rO6)}+R5R6bi03cQ&)N4xv7KWKCLbLZr=Cu!ymNr)ro%&(e`g7 z$472{a_95f^5T2%pC)%zWUqb&Q}^!gAm?uU;O&T&vGSJn13%v|vaWpkk=NuyC)>)_ z?)t-BEw$xU7bZ;UGF~tH1XvF{wyPp9jz4v9=n=jAN7IX~{f;$}KZqUGYtj1DxVBeX zS8{!&m7iX`ndf~I?@|it)-M+vGx#-`?pZq&tFW>8PrNzg`y2*W>dVK%2V^`T@2ICv_G^L9=;Q5V{~EWx4z3p`x0x}}H~d(2`O~yZ-Q?wwa^3G+k62Uu zCa#gX(Bb<|;qv7P)2DXX_JQnncgQ>4h!5n3k1y_bzR_A<6k0f=Zc2T5>iWx>g)S}S z=cxzkN59m|6|K)`tzq!*y<&5Dqnc)UulT%DZ4O=gUeOj$4=bMjUfF%wyj3&gy)v#% z=hZ{o0e7p>?s?7kiaKnn?SRXB#bJK?;$!#TDVB$-A6-tqQy!@HPRZE*P8t5QZA+tt z@08nyr5i%h-YIGSsE)Vn^G+Eba%J+umhY5mL;$TS`2S9s^JVd^zyEowoTzHPqB`+b zxjcQ(MC10i%A9G{Y7{Mct3=n!P5f!nTV-eJgp92pzE!H8Z4Lk5Dk1&fd{-~>t&(OL zyI{A+Tjl6)?LHh^^hR-CQE7JX3vZOWzpIvv+5bjacw>OmkL%wkZRWi0S8MJYCG^-h z`2R-vvXkfO#Yt}zTk#>)0tLd&4O;B3^+vH;rdM*Q{6?8O@5{%ZKYOjba9%vS?$y`I zCvxV{w})RV`Fjuez1;d*sqsFjZnbY-D|4G%iW~juYsKmD{#!SPzE-+dDcCjrgV)L_ z=L?LSHIY(i|cD8YRB4rUlzSm24pRsR&e>1QpI#6#B}79^7g^3 zhE;dGQuYrSY5aHDE2ZxZU5`66UMXWj@;9o+yiz*PNpWf2=amw&zBT-RrIu0Ds*MWE< z#fo!~F+=5Ctmvny-a0*fp;YbOuSZSzzVxR~pSB*D|3aCnYOv_X{V$Y553TCz+g>Ph zTl_R?^NJTr-um?EUFW<|JdZB-R!@GR{IDtd_Lbo;lra-j2T$~Vp>zoRDD8Cn7s|YM zt&iBEUnu*ob(j_v@j`K(x^hgq?+e8=-TgzW!wV(QSQKC^dafL6-geZB8_$(NdDClc zJpG*g&fjw-F62A6PCqNGnr%Vq>nf_b}8Ln!!X54e-W%V8- z9}ar1eAhYli|z@}mE>^uzIkn)E7vS@a(g#2Oc)^pWN(-lk z-&_BAs&uV)Wq;cXPnEGdYR<|%@l?67VB=N!*QZMV-xAD!?|7c~? zExviGWW=kgJ7qpqKJnY$@y3*=$}nT!=u=~!D$Dd7Me5$;@ICyAr zho?%n$6IufEuSj0!p_wkR{yEeqwj!PyR}ahKO&HRXywy0TtMG9M^U|gqPXnX&}PBo zCyK+S+xzO>e4-qkuzBIdb5E4JTRvI;&G9Enui7;(8h?GF_{IL<8~X$N4n*FRn1~He zlp)iC>Vk?DRBAu!pzr)k zp>k_)(eSB16e>$T2^qBK`$A>4w>4O`x=>lU?yHQRiwl*@b+bOtnNz6v1Wor2nqH{P z{JTo5jrs^-HxKiibu4;!R5++kC`*QB^j-)K{epYPasd6i8`bXB!bzOgAchfer$ z%U3pKrlyTLd?xVwxwY4PVpER3=;&5!yiIxSGkN9ikv8ScrouDb2HTV`C#0Gm!|!G| zMxTGNw1-VOk{ocdQzx6Eacm9$L9gSCpO41al#l^oyBF$g%Dvi?-Xura6t$znbiLN5 zG@sI=Sws-%6R{~7@I};)Y8$R&fN+$g>P}yiH)QsGXFug70ajg5mah z$qMbir5BFK#KsXnCXF)Tlv>cc|&(|pfaq$+1GHAF&e|I)w zql$#<{it60RT|!o;o^&B^(=OJ9u?^EJRu&Q`nEr!CmbuGaIO5Eq*m`Sw~#OL_y7Mt z)~{)K>@DY3qVMrq7~02^O2@z~S5hx(hBkl7O^!4@O{XhSN3LBB*YY1I7akP|UJJbt zZxGHg3EJSo_to?eK}^)JOm7yQMa?a9*B&w8d?U{?WcDT!)02q5NPkUa&9f;yEe55O1!)zXZ0p zZP7p4FHca{D#Y1@bJRD0JuafXJUcz^6LrmyMql741;J5~V0n^+coUr4`^L0ri`y3c zqkU1h4u;tvjRoSD+3AS-=>LKcZ-evl{G%;yTlA0iMd3Oare6!?zX#=qqaq>Y7vfED zUY>un#chlJ(Y`2LhlH(z@aL#RTebF!46-D%W0rDDi-%--QQITM| z4MKbhod3r+=%6ibTlA0iMd3Oa76s+bh4?Hx9ee$JA>JySa~oV=^ow>y5&h;sUUd%~ zB{dus2|nZFh4>UWuc+*318rhmp@{iFKca~F$b>Yx5T9kIBkE)7d?CIV&i_|lFg@DF zGN6e0L4Tr%`ALH75&sbSNjNGJETd6~Plt2+Yl3Of7Pl?>NBg319SqBYG%kz)6xNXrt+82fEU|24e zAL7k+I-)*aQ>z&N7{(RA%gY0|SzfzDKca~F(0u148NhB792E)XBSnbM{2%#1o8{#L z{fHvwBL>o3fcP9c9Z?_iVH4st|Nh_gfi}y_2l^33%!d)u7{P7~92E)brwj3!!a1fv z8(hTpfObU@{bpeKAwI`WN7N_f7veQGN2#KF0@I<*a?3B+Ks_-ZIaq$M8v{p0f;Q8I z_$)XtuT7#YZo5o92x-v=*THiuq_IMLuAL6XaY6lJAzocrHm_)di|7~aiX!?ATJIz! zfgPQlEm0rKZ4%-$;oROg>Z2`gTlA0iMd3OamIP@oKzxp!j;N3RZ9=@dsC@rui`y3c zqkU1h4u&nm@`F7c92E(+Ka&ujh5qrdv&++?&GOnG`VmFUM?R#nLVT{Bj;N3MC>G*_ zp8VhWK%3>|1O131=0m-~NlF2`@I#NHN||%aM}`n@hVzQr1SUkA<>dqYh$8000BLLx zpJ%6IudjYuY9mNEM?JK`MQjgfR}|524wfJ6=A$}R0m)j43EpA)%kM>33 zIwWkPlT-ll7CRkLAM0OwR%$~F=N09F+bpmC(T^x%KH?!CCa@bX_z`vS9BpO_@n$$L zuO84Aw=LEK+82fEU|0sEu|a&EosOuF{?*S*ZD`@VJpX8m+ZO$!eNng$hFP%uU@u;< zFY4ksDZdbJuE0Oq;33Iv8fd@G?2Ed1PRcLDn=9~-wzzH4KiU_C>tI+Gq_IJK zo}G@UPs;zQ)P@$$%PT+H;G?2Ed1PRcLDn=9~-wzzH4KiU_C z>tL916O}4bIEEo@k5P7W0Yr%M;Yqy(x{;3+JeRp@MP;+39iHXfH#E z%M#8}Cuno|^_XI($IF0e43NeO@ws+77{>+cr&x&ByoGU!z_}jU;$^_RABrn=ix_z`5q#e{~Bgu$wESMf*`(%CG;J za%`eL=G7p?+56^YL0u!*&IDbfppW6A;P}ie#9QFJqB3DRw25_qBGwoB5k<^LHl)$O z{#AjUj;N3Mh+=zO7#@%M71S-pqs{X2fqq00^HBh4mO;G9PDj*7n>j*!9-P|*&UMii zw=LEK+80H;wfyTFB$bAX`e;)p#M%2cRA4VfNMmm=Lx|H0=cvcafajQ3%olw25@{I` z^`fBc*gkXrhaTn?^JV=XdYPc7Q#na$I4TmnF7ZOV5zg()f@#qf&#QP{(7q^K2g59o zCI{j(?Q}$a^q(ihTj5;oP}U~5#chlJ(Y`2}ZKYh))0JZr)1l3HAkxNWgKXkU~h(BmTNr3*fIJR;i665{hP`j@6ZJJY(6@tbkf6`)aK!Q? z3GpU4UnX#_hqkzFu{>yB6a(mS5%nx~dRzx>UJ&B!eRH3vp9!`#YPin@eQt*%8UKI| zmv}fg!I39~@i;`ZSzh}@KceJSQ2u3hdOQu<%n{-$%)<|*e#H8+32C`aM63_BYiSz$ za*6e&1KWDg4HEQ4Tijlf5T7EPV?5g6BIXb6iX!?oL7H5M&$81I_3=983-LBM4|0QX zju0m5qs{WJ6Z#Rw0(v|zW%3KQ%M;T6g}93Hz-^)p^r43|CLtZSE$T(>g!wP%EGtJ( z)W0Ca7YOI5gEqK`IRAgWw=oU+;4^Qre$lTeVmW!fGVF9vUo2-n*v~i$DZEOPtLcCf7?HCUG zaSqoZ47PXE+!0fqq00^N|H1h$k86!1F8%@$U3OoYx0iWF=_aMec^ka2lc9o0}*C2I#>W~Os zBRG|e5SD>ur(vY582XBBEe=vn zecJ!FXh^O00K_A@@ z>N7N*#)+mgK^sH8hkz)KP(tLRNJ%B>bH)!ceu;5kR_eNds6UwTDU82h+>7PwLqL=? z#=X59B%~t1m5Nd==w68K>oU!?dLQMplj-RzICsIR>zD5kEVr zpG?rrV)c?vdN!yxK=gZu@pp^|hdN81sjfB`t5bd@lY{ZY{m@P!|KVZIQrKZ{$tO`G zIl*<%MoQ8?II=^Wq;QBY!<`%?r!;5D2w!?W0gmEGs#At@9r!Zd@o<#r2t3$RB{>d; zdk*RK3~ceFSdpl^VDUw||T$Ekh> zJ_A6$0>tzyIYOI5u|hsEdzo+PODr z=U8?Fq=Bj~IH zzM1Lggi`(bP_HO$Sp9kjsU@$S?lxDei^a+0Xsl$QZ8VRiGq8ND1Vs6Pah02+~jG8oQgq@&1Z-mQ1}RX$c(0 zYBUdJIMb2(NK!30xQ@uZgW#FX>K*SxlbY7C^bftCJQ$if(|qj!MEM&K>oXXB5CX4> zD$r31Oo6^H!9k~T25jt5l1F2A_#=2fLZuG?v0UE4&QfqPw6$nYY<6-erez&ILHWg3pPELwp%d+69bZW1(+`gX@UgySkfH9iFe%pwIDHRB}HX zu#IS3X&bSzGLa0}{mEeEIKav^0rG<7QdM)4s-*==)sh3iwx3k3$j|0u)fiOp17*I6 zzLIaWkL0`4%jRi?Hcsna(zbwp8V-FpEsMy#>v>4^QkD3c_tvH^BzX~&3iCF$8^WW(KXgjQYjBhqw(DUQGXmD%GZFHkKm>* zQbcr!%2ySj63c3Xysl$;&4Rq-GyPE$X!@Cx{_pfb{xrRX>Ge;i>C&dKbj@9*TFF&Z z)xozv_=fS1$&uBwo~6b7=~z9qVwlD1x&AXgFG77`U|OVwzy+hdf{`?6HWp zf7#B^bsRUa2MhDj6qnJ1Y2bM!ytCc;0(TUeO+s z*CKaQuol)=2K!lQ&C$FXtFmV`m`0oE0ox`DN6P5EiD8ynbR7(8joiN&b{4|)fLsnk z+lR+VnXuO^jO(m4Q=jf@X-F6#<^j{9oCZYO7Xi`NBNnc{L;IUxHwVeBG3}$V94ddc z$Gn-KhZBDEqXj!C^%zF1qv3r3QKkc;|HX`7 zW%1bdE5kDyM+dy^2S-BR4bM5xoz5mFqoV~D3&d%QZ_!}$&*KmYENq$$6`9uVXkQRSeLu)kM%;yvjGb2Ddy zI4`p3XgflmSROn~@GQ~;axs7{yk01+An#Z|od8jKFy51KvEI=(@ehrc^hG|@zBjoR za2=9e;959K{*Aq@JpYvr!rT&$QLNs*`M4DHa9qmu>Kuai+TfVM^jt%nrI2VZDI_(( z>Syvba=mS!)90`xtp+_rZ&<-_QzCqW5lV$RmpJJiTrU z08utDuBz%GRZXMoDpg@!r7HA0RTl-@0c0a-34cI&aO=u7o1}$G^^&VdHKRkMfEwOlrF3SG_g)OxS`KNj9p^B6 z`xqMT((tW-D0>0%S`-!#Dn;C*@!s{_rTWp0q;OS~6kvmXGhbt=Y{F*^>pNL~$q$D> zQV;wA5s;jRH2u*>gi22U(f;R;DgPD_`CG=LY*gQVK9>phQC9wsddp#YqglQm0iw9G z`tz=$ma4$GI1t9g?q|rT#fbHNf#v@!)C<q1h`dUBeYyF__@oTIx(K#~s zfc6W|Ruk#d)drJg84zVMvKgwp#a1=6G6V|Le}YaF#V9S|iC5YJAtaGD=C$Pewud4953e&8i`V)q6h z$^*u|BRr%C7%yrQD?>Z+lr#w*HaDv}-`PUjMJ~(FUv~L<%KXO4)Lt~&BDxJd!f#si=p z0~-6-G}g-bFnTu9bt89j|6u!8$IyBWVr?;sVOJQHVtp?pqf=OS1tj@}zZf1&);Y>P z8+BmIPetQ<0it}#_!7nsF;4H92p8%1og5VlM+Mvc{Op#yNf~9pc&}TUIAEnRm3|8hOW-}Sk(UsbX14QWx zh-V{MxT>*_)Hu^Yssrna5irKr!rHDD)^@cpZ__RcwS`!N@&hb>CVD+QaSU1JzEB0a zNr7q3Qed)^6qpEWZm^z!V*!;P-Y2kL)VS;#2x+%){qR1(c7SXCc)hYA?}99Mx@ZL8qN=mTKh1M|d^_NEGRmBMaTu?1TL^ZhNpCLg1>L8F7V zgzHIA?rI4kwjgUjzMqB6kPNC2S1Dvh*(bj{>ifezkp|Wk}?%HnIK_(qr8{VC~`s zAl9zSJvtW(1jNxsBp_ae=73IsodIE`NcsrS1<(Wt(`e~)Kv%#efLvCx{`dqS$~8bd zdjyF6`%6I7cJBiHI$$s$`iKTZAHx9A$9O>W@fHw$G=|Mjn5Ijge?ZG`Kd!Ms*-)pOIlgb6`!gB=onL?9sEa{44N%9N~YDv>#ddw>xt(rO!ztTp`og~Tk z>0IW@7;68{SXw@(k15A^lrDf6V`N;M*PtI&mb5*YayoA1l%!8TsovCMDUIGV4MfGDa|%E|K@ z*23{#OMx_pzoOw@^Qc}eK(s^0AuePN;Z5d9Zc^n0Hhd35G+@Hn=d`R_^0g1k_kE#>&F%LL)%3nbB zYGl!L%>nVO2jk-biJh9zKdFKxZ=09ZGvD2UV^0?yU3W-gcIU#iMBS6DO|>m^UyqV;Xg*8h%F5=?&@`I*ISPnU0ElNzn7ww) zo=W2^X<&^;18oh)GHlEZ_dbkmaGi?IwLRe;^1yuqHjeXz_E~m3T+H$j^nliVQ$UpD zDKsDZ0a0!-?)M4Jj~)=~gRQ$5=yMOA_X%a6dvq?~1^Z>RUn;M?YFjxVFO(O0%`JT&)2~dCKV{HF`&>-WnP|3lL=?_=v)<6C`k1J+*Y5-}`25xVa{u%?WAmdFJt-oOeovdBh1cGYPUTgp7jSL<~A$vw4n zzH4E&S_-yMzXNcTbXG69fGEu^)NU6*)Y}S(_t_~Hev^f(ykLI<%7E8e<>!F?uP&IZ zAqK!a*I&|{DLc=#wfpbbFGeZ5jy(tR#MiMkzf=D`08w-&DMuZYrGOYCj{DF@a4^0{ z0&BO6=-!qdbW~aoNt@yz1(#F*Dqm;GH{Ai=@qn>+D!jYU$W3Yly7qe!WS#-@wg8y7 zX<**w0nazwXA|3psXaVTF0<=F#&VzoV_&`(y$s^9PIt0090bg|K=n@pqU2nn^?wQw zr7E{qa(ydH?u&%$%WSG2mAwztH>lkOfGA!!sofwz%tJ-bw*qGG0n6huE7d;e2Zp%smrftq!i4IREs<_6lq2I0x`dAn(Lk`25(&{PnY| z*PMscZtNrKZ!I96Z3aYt!S&(2q39sVyNIl5m+U8Hg1uDGL!DuQP2FRfE*%iX?O$ro z7Z7#nb^7l*SPsiyBf%D?-*ua&y9*0?F;*o z3C?8RrtM7Aq(YkbVj4dV5M?ss<*i@EL%NwPo{qmu$3Z4$bB*0~voil)uc>`4AfD9* zB-hOo=JD(3`oglVG+$f5#$otXc8m{zcTrHr10omeF{PWM)DGS!z;L=2?g{fhIvyu$ z;Tjmr!~TM15naW={)(>eYhaxO*7#uz?Y|S&_sRSi#`Q1`^+{m!V=~V#JBGA^KlJr8 ztLtNc7I;Qr*lR$P7Vs_vmhW>w3|HYgOSDQ7^S!J)e3w);8t+{V)@=rtnZ+5LVLb=u z$QIDcu21!18dCeM0nx75KNo;rXm#cf-#3DBiEpZhmA@}U)(qes*swGWj9a{5-Jr4* zM#r26F<%-3?3;zNeBu2B?^1a0EWO_`Pcfi_{%^55h>xQB>j6#lTCjp|Q0%HAr!oumbfprUb_W7jJYon^>4)0loKz|f0Ra5y$C94212BYhX zK2mV1-TESF3mQq2DzpV$`+{dZzMDzcRp_`b4C;^75AJ)wTo2cLJYYTn>xHRw%_pY^ ztp}gS^uDXk@GFIu=PV$K4G_!ILQliVeVqv73pSpldq}jehP|DVJ){fFep6<*Jwr=d zYUe2+N~d_p?qIu7jN;x&zeI82idKF;=_@vaHfUL+vK z(f;SZV{`*c`-tT)w-411Ph$B4EKTnX^JNWL&*ksKr?Y%z51@LD2GVq~fFxbG8s5K$ z{bAUT3x+n}xyaRuc{Ve@W0~L20I{CuFph{ac7YESKc42Y5ii0QThqW)ii zsBZ(rl{>c%RG+p(HveaR1ZivVzF#nm3xm&)R*0+W<&G8dSUK~1QG21issCDlSkB;D zu>KAE#(_m;=llF!4zhla>yB(*%h$sy8t>Sczr>!@{&ql=pBWdQ2SGjQ^&bp%G?TTT z<*YqKamU+Js-w(MB?}wzpxF6mTVDE?6?BBoDGanXq9U@r0_5(y& z&3JjwV;iK+PNw-N0z`SuczOCkuzwIboazq)MERKU|KtmFv3zd-()&0ug{J!i5JfeD za$bUvl;iskpA`I`)B630_N z!vImJ{nB@Li<#~iWR$y!31aE(>s^~feSE>vEo5kfy$HM>a{*Dd0pk66oQ0S7 zT#0A4)`Ja9i_ewBiPX-QfGCRr(atIsj_>o6^<`Mc=l2qSC+EZ@cn<-NeJoGpond$; zl{fXFDNL1<&nJa<^g-mu%-dwZ2#Uu*Et{@`rtC* zxNiw#szA8U15H*PS zFpkI0l&+=VyO4PQq1HcpV6L*)K^h4*5RuOST)=FqTwouu4SoU#>I&Xlt)5WL7HSR2 z53&T9{EdDkcK-SWZ0vK8!U`)9Tn2n8@TR~~RFR%iWLlUM345NA(VRpD@f{DlF!^0rvE&E@lY+6#Ve=Tcvf0Sn+j#Op@zzwbDZU4PnsT;X1T zeZj_bOzj$6NeVvbZ}NjWk@>>#a&BMI_@$nHHuaP>-(x9TVIekZm`GjvPzeuQ^= zVT?@PQ6e=A;R2Gr1U`G=4{P53JBeR|E4hE@XG{Wjmfk-+4{NVL9H7OO%Ts@kepgYyF|kb9#F*OUIUn-2MKgn4EgKr+8xLGp!pBS!?jvjE@6aZr-T zd##|y{m=NvL7D>x>Ty)L!nN7yUiSu2I-vK!2A>G;qU#)q&@l;{FuL}#g5;}974;CgD=cW5WxbC5FNILz-u-j_oj z;XMnJt26nW6}gYG-kbQsIt3ip|3CK51uUwv?e}XKP&|~FCsfSIEGbWel9G8C1Pu#q zQdBAn5C$O;reRPlGc;2=Q<+g&jT{t}Q)NYEZ&+0H=37)&yk$jYMP)^0MP;>r_gc>= z;9&K=*WUZv`)jW2&+ndRp7VJv)&c)QQ+NFuYpdR-i@o(wM?E}CJ^Z6xJv7lbdv}O0 zlfJ$V{pwA>KiBRvXZF9nm%U@!H*@{}H>l6Itgj*TdmrVwkl!yxMfj1Ae$Nl_^_kn| zSo0)5W8LXt(t40*<9ZY0dZr=XemqWH|LP-+7f&^7uOn|7zVZ=3KkkLReoQ_6I9?5~ z`||tRnP%;rk(_5yb*p1Yu5)%{4eUdoI*C3-ziRV;E>j@aR^9Gz9e6tXuQc}0rjs@9t#o!t46w?zx6v2S*_o3&Gr z`nuDLy$;9R?BNE|mj~Y7uf^!I{Wuoq{5Ww9A+8~>scWS4F!<#>$&tO2t6!5b=aV1e z=|?>J^|aEK;{vR``*M9N=WD(D32z;3Tf$gJDc(LO*WOHL90zsPg4Qyo8UkzeI~v-o zRqv;pwYM)fYpcEW>SGMe;eR11$lC`@1LS(MZmjQt+JJF=*t5}oQoj$iU#kb6){V9L zY_5m0s_UWTOrp9UhzV*S*JAXK>&+a(F@kYTioVWz=gom5yJ~~nhqOWA-?3h_Xx@8t zoLs1M^gaXQ|BK!_=ys)9n~!#T>p=d0Ded4gt^@Pc#~s;b?H6~O@&$4Y6l2ww-!s-Q z2ckE;^(Fg9+D@)z9z1S9OTVT*4ZUlR?oCqj&Dth(wzpr(y3M|3K!bHTvTC(Iv8`36%yxo*DCyc3PHvkk_x1EjyjGkyn- zrXQPrX@C5njxTEIbEb<<)Gj&{rCoR+TANijQ;Uo{SDO$X!#prclNngQKXu;>-m6La zMVu47#JE4L?RV3Oe%guSx?4?hJqYW4Fl#)=(TU{otaf=Ee{3EZI$G~P=x?w8gY$Mz zAMI}gxj*>?Q@+-v>1bKyMBOR$x6nh^YL*JpJnlUfOYtB<--mK#uCFX(v=7Pjpv$g{5Q(@bF*Y7zjQvFrtN?8y6e2=jc>do40 z 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 literal 0 HcmV?d00001 diff --git a/libs/discord-rpc/win64-dynamic/include/discord_register.h b/libs/discord-rpc/win64-dynamic/include/discord_register.h new file mode 100644 index 000000000..16fb42f32 --- /dev/null +++ b/libs/discord-rpc/win64-dynamic/include/discord_register.h @@ -0,0 +1,26 @@ +#pragma once + +#if defined(DISCORD_DYNAMIC_LIB) +#if defined(_WIN32) +#if defined(DISCORD_BUILDING_SDK) +#define DISCORD_EXPORT __declspec(dllexport) +#else +#define DISCORD_EXPORT __declspec(dllimport) +#endif +#else +#define DISCORD_EXPORT __attribute__((visibility("default"))) +#endif +#else +#define DISCORD_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command); +DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId); + +#ifdef __cplusplus +} +#endif diff --git a/libs/discord-rpc/win64-dynamic/include/discord_rpc.h b/libs/discord-rpc/win64-dynamic/include/discord_rpc.h new file mode 100644 index 000000000..3e1441e05 --- /dev/null +++ b/libs/discord-rpc/win64-dynamic/include/discord_rpc.h @@ -0,0 +1,87 @@ +#pragma once +#include + +// clang-format off + +#if defined(DISCORD_DYNAMIC_LIB) +# if defined(_WIN32) +# if defined(DISCORD_BUILDING_SDK) +# define DISCORD_EXPORT __declspec(dllexport) +# else +# define DISCORD_EXPORT __declspec(dllimport) +# endif +# else +# define DISCORD_EXPORT __attribute__((visibility("default"))) +# endif +#else +# define DISCORD_EXPORT +#endif + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DiscordRichPresence { + const char* state; /* max 128 bytes */ + const char* details; /* max 128 bytes */ + int64_t startTimestamp; + int64_t endTimestamp; + const char* largeImageKey; /* max 32 bytes */ + const char* largeImageText; /* max 128 bytes */ + const char* smallImageKey; /* max 32 bytes */ + const char* smallImageText; /* max 128 bytes */ + const char* partyId; /* max 128 bytes */ + int partySize; + int partyMax; + const char* matchSecret; /* max 128 bytes */ + const char* joinSecret; /* max 128 bytes */ + const char* spectateSecret; /* max 128 bytes */ + int8_t instance; +} DiscordRichPresence; + +typedef struct DiscordUser { + const char* userId; + const char* username; + const char* discriminator; + const char* avatar; +} DiscordUser; + +typedef struct DiscordEventHandlers { + void (*ready)(const DiscordUser* request); + void (*disconnected)(int errorCode, const char* message); + void (*errored)(int errorCode, const char* message); + void (*joinGame)(const char* joinSecret); + void (*spectateGame)(const char* spectateSecret); + void (*joinRequest)(const DiscordUser* request); +} DiscordEventHandlers; + +#define DISCORD_REPLY_NO 0 +#define DISCORD_REPLY_YES 1 +#define DISCORD_REPLY_IGNORE 2 + +DISCORD_EXPORT void Discord_Initialize(const char* applicationId, + DiscordEventHandlers* handlers, + int autoRegister, + const char* optionalSteamId); +DISCORD_EXPORT void Discord_Shutdown(void); + +/* checks for incoming messages, dispatches callbacks */ +DISCORD_EXPORT void Discord_RunCallbacks(void); + +/* If you disable the lib starting its own io thread, you'll need to call this from your own */ +#ifdef DISCORD_DISABLE_IO_THREAD +DISCORD_EXPORT void Discord_UpdateConnection(void); +#endif + +DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); +DISCORD_EXPORT void Discord_ClearPresence(void); + +DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); + +DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib b/libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib new file mode 100644 index 0000000000000000000000000000000000000000..fcd009d82f6353ab287f3b5fa1b8d0f8416e82bf GIT binary patch literal 3622 zcmcImPfrs;6n||g#Vsfi^=48cikb+u?N*zpDN-U0P@1+}G+|jfD{S2U$+pDA7`$=v z&Nm?O!Z$GCzz5Ky7ZMMC0qzFhba%Ep(``#ZCV4wE@4eaI%$whvnaLNbS>Apn%|`h0 zR8GX>@l-6D;Pp|vE2olnYeNEn+W^5BK*#_XIRKD$Eg3xqXh{xhl(9ogq!F~EgikCP zOQIe{I<+KBP)`&kyOLiml$6XualVkvmsSf3(HrHONuHSXGAK%JZF#xV3BI_zET7Wi zT5d_n%$4RKWBozNR`sT?8u}*5ZmlcKq5_i+;tXrdHml6>+KTKW+PlnW#)1Gw2;cx&wu=lsMr97= za10rW{gW_265FTPCfG&-!w3vP7)Ie53_}nk2!Rj$9@>DzA@6v*m?Z|l8QHnwwyU*U zPT$QbrtDx|mkkm)bm5HRGlX9@Z*wi4H?S3V;Yxe!#bKW0bq9qX;Dm-(3jy*nI!V>@ z{2=WUXoeWZY~PbInsQD`vKN5k&wt-d_$b?~GDEb@ovo>&-Gv2gd}STGPQ&=?86D3G zbqY^etK7osC(0kYAHX^d(IeXrPgARmV--L==0~+@h#dK`zJpw+<%Y1RX_y$tI?@A(kp6E{E zBzlr_&A@gT5Id%igH>5Y>lAO{Vj0CqfRK zg>u{*lZMpq!gTJpOCu&@-iVJ8G2nsNzB)x{q2E8gU63P)Jj5Yem$LBx!T0l-_e$?0 yUeJx_zV1cP|GJ~YE>!0wad|}c1c>n2GL7pDxe$d%%9Y~M*KWqn7vY)Mf%^}SVUt+^ literal 0 HcmV?d00001 diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index a01be4bd5..1b9e742b5 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -147,7 +147,12 @@ endif #MINGW64 endif ifdef HAVE_DISCORDRPC +ifdef MINGW64 + CPPFLAGS+=-I../libs/discord-rpc/win64-dynamic/include + LDFLAGS+=-L../libs/discord-rpc/win64-dynamic/lib +else CPPFLAGS+=-I../libs/discord-rpc/win32-dynamic/include LDFLAGS+=-L../libs/discord-rpc/win32-dynamic/lib +endif LIBS+=-ldiscord-rpc endif From a235062557c5d88eb8e373a2a57ebf61c00784e2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Aug 2020 05:58:09 -0400 Subject: [PATCH 104/193] Move the .dll's to libs\dll-binaries --- .../bin => dll-binaries/i686}/discord-rpc.dll | Bin .../bin => dll-binaries/x86_64}/discord-rpc.dll | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename libs/{discord-rpc/win32-dynamic/bin => dll-binaries/i686}/discord-rpc.dll (100%) rename libs/{discord-rpc/win64-dynamic/bin => dll-binaries/x86_64}/discord-rpc.dll (100%) diff --git a/libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll b/libs/dll-binaries/i686/discord-rpc.dll similarity index 100% rename from libs/discord-rpc/win32-dynamic/bin/discord-rpc.dll rename to libs/dll-binaries/i686/discord-rpc.dll diff --git a/libs/discord-rpc/win64-dynamic/bin/discord-rpc.dll b/libs/dll-binaries/x86_64/discord-rpc.dll similarity index 100% rename from libs/discord-rpc/win64-dynamic/bin/discord-rpc.dll rename to libs/dll-binaries/x86_64/discord-rpc.dll From 74bad0e34cbaebc0f6e91d79f4185c555545ebbe Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Aug 2020 06:45:35 -0400 Subject: [PATCH 105/193] Attempt to improve discord-rpc checking in CMake Haven't ever gotten CMake to compile SRB2, so I'm unable to really check if this works --- cmake/Modules/FindDiscordRPC.cmake | 23 ++++++++++++ libs/DLL-README.txt | 20 ++--------- libs/discord-rpc.props | 16 +++++++++ src/CMakeLists.txt | 36 +++++++++++++------ .../! SRB2KART INSTALL INSTRUCTIONS !.txt | 11 ------ 5 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 cmake/Modules/FindDiscordRPC.cmake create mode 100644 libs/discord-rpc.props delete mode 100644 windows-installer/staging/! SRB2KART INSTALL INSTRUCTIONS !.txt diff --git a/cmake/Modules/FindDiscordRPC.cmake b/cmake/Modules/FindDiscordRPC.cmake new file mode 100644 index 000000000..b43221abd --- /dev/null +++ b/cmake/Modules/FindDiscordRPC.cmake @@ -0,0 +1,23 @@ +include(LibFindMacros) + +libfind_pkg_check_modules(DISCORDRPC_PKGCONF DISCORDRPC) + +find_path(DISCORDRPC_INCLUDE_DIR + NAMES discord-rpc.h + PATHS + ${DISCORDRPC_PKGCONF_INCLUDE_DIRS} + "/usr/include/discord-rpc" + "/usr/local/include/discord-rpc" +) + +find_library(DISCORDRPC_LIBRARY + NAMES discord-rpc + PATHS + ${DISCORDRPC_PKGCONF_LIBRARY_DIRS} + "/usr/lib" + "/usr/local/lib" +) + +set(DISCORDRPC_PROCESS_INCLUDES DISCORDRPC_INCLUDE_DIR) +set(DISCORDRPC_PROCESS_LIBS DISCORDRPC_LIBRARY) +libfind_process(DISCORDRPC) \ No newline at end of file diff --git a/libs/DLL-README.txt b/libs/DLL-README.txt index 06fae1278..bbb6d3cd4 100644 --- a/libs/DLL-README.txt +++ b/libs/DLL-README.txt @@ -1,6 +1,6 @@ # SRB2Kart - Which DLLs do I need to bundle? -Updated 12/4/2018 (v2.1.21) +Updated 8/23/2020 (v1.3) Here are the required DLLs, per build. For each architecture, copy all the binaries from these folders: @@ -14,6 +14,7 @@ and don't forget to build r_opengl.dll for srb2dd. * libs\dll-binaries\i686\exchndl.dll * libs\dll-binaries\i686\libgme.dll +* libs\dll-binaries\i686\discord-rpc.dll * libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll) * libs\SDL2\i686-w64-mingw32\bin\SDL2.dll * libs\SDL2_mixer\i686-w64-mingw32\bin\*.dll (get everything) @@ -22,22 +23,7 @@ and don't forget to build r_opengl.dll for srb2dd. * libs\dll-binaries\x86_64\exchndl.dll * libs\dll-binaries\x86_64\libgme.dll +* libs\dll-binaries\x86_64\discord-rpc.dll * libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll) * libs\SDL2\x86_64-w64-mingw32\bin\SDL2.dll * libs\SDL2_mixer\x86_64-w64-mingw32\bin\*.dll (get everything) - -## srb2kartdd, 32-bit - -* libs\dll-binaries\i686\exchndl.dll -* libs\dll-binaries\i686\fmodex.dll -* libs\dll-binaries\i686\libgme.dll -* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll) -* r_opengl.dll (build this from make) - -## srb2kartdd, 64-bit - -* libs\dll-binaries\x86_64\exchndl.dll -* libs\dll-binaries\x86_64\fmodex.dll -* libs\dll-binaries\x86_64\libgme.dll -* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll) -* r_opengl.dll (build this from make) diff --git a/libs/discord-rpc.props b/libs/discord-rpc.props new file mode 100644 index 000000000..83c7a03f3 --- /dev/null +++ b/libs/discord-rpc.props @@ -0,0 +1,16 @@ + + + + + $(SolutionDir)libs\discord-rpc\win32-dynamic\lib;$(LibraryPath) + $(SolutionDir)libs\discord-rpc\win32-dynamic\lib;$(IncludePath) + $(SolutionDir)libs\discord-rpc\win64-dynamic\lib;$(LibraryPath) + $(SolutionDir)libs\discord-rpc\win64-dynamic\lib;$(IncludePath) + + + + discord-rpc.dll.a;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d29597474..7cb9d1191 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -332,7 +332,7 @@ if(${SRB2_CONFIG_HAVE_GME}) if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) set(GME_FOUND ON) set(GME_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/gme/include) - if(${SRB2_SYSTEM_BITS} EQUAL 64) + if(${SRB2_SYSTEM_BITS} EQUAL 64) set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win64 -lgme") else() # 32-bit set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win32 -lgme") @@ -348,17 +348,31 @@ if(${SRB2_CONFIG_HAVE_GME}) endif() endif() -# TO-DO: make this check for the dependency like the others.. if(${SRB2_CONFIG_HAVE_DISCORDRPC}) - add_definitions(-DHAVE_DISCORDRPC) - set(SRB2_DISCORDRPC_SOURCES - discord.c - discord.h - ) - - prepend_sources(SRB2_DISCORDRPC_SOURCES) - - source_group("Discord RPC" FILES ${SRB2_DISCORDRPC_SOURCES}) + if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) + set(DISCORDRPC_FOUND ON) + if(${SRB2_SYSTEM_BITS} EQUAL 64) + set(DISCORDRPC_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/discord-rpc/win64-dynamic/include) + set(DISCORDRPC_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/discord-rpc/win64-dynamic/lib -ldiscord-rpc") + else() # 32-bit + set(DISCORDRPC_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/discord-rpc/win32-dynamic/include) + set(DISCORDRPC_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/discord-rpc/win32-dynamic/lib -ldiscord-rpc") + endif() + else() + find_package(DISCORDRPC) + endif() + if(${DISCORDRPC_FOUND}) + set(SRB2_HAVE_DISCORDRPC ON) + add_definitions(-DHAVE_DISCORDRPC) + set(SRB2_DISCORDRPC_SOURCES + discord.c + discord.h + ) + prepend_sources(SRB2_DISCORDRPC_SOURCES) + source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES}) + else() + message(WARNING "You have specified that Discord Rich Presence is available but it was not found.") + endif() endif() if(${SRB2_CONFIG_HAVE_ZLIB}) diff --git a/windows-installer/staging/! SRB2KART INSTALL INSTRUCTIONS !.txt b/windows-installer/staging/! SRB2KART INSTALL INSTRUCTIONS !.txt deleted file mode 100644 index 91d055be6..000000000 --- a/windows-installer/staging/! SRB2KART INSTALL INSTRUCTIONS !.txt +++ /dev/null @@ -1,11 +0,0 @@ -SRB2Kart Install Instructions - -1. Move every file from the "new-install" folder to this main install folder. - -2. DELETE "staging.bat" and "staging.txt"! These can mess up your installation if run by accident! - -3. Optionally, create a folder in your user profile named "SRB2Kart". This is where your game data and addons may live. For example, - - C:\Users\[User]\SRB2Kart - -4. Run the game! Double-click srb2kart.exe -- or see if you have Start Menu icons under "SRB2Kart". \ No newline at end of file From 5127696fcfa7424bba24f462470b6655aa116875 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Aug 2020 07:15:33 -0400 Subject: [PATCH 106/193] Finish DEVELOP mode functionality --- src/discord.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/discord.c b/src/discord.c index e69b3abf4..236c801bf 100644 --- a/src/discord.c +++ b/src/discord.c @@ -473,6 +473,21 @@ static const char *DRPC_GetServerIP(void) return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites } +/*-------------------------------------------------- + void DRPC_EmptyRequests(void) + + Empties the request list. Any existing requests + will get an ignore reply. +--------------------------------------------------*/ +static void DRPC_EmptyRequests(void) +{ + while (discordRequestList != NULL) + { + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE); + DRPC_RemoveRequest(discordRequestList); + } +} + /*-------------------------------------------------- void DRPC_UpdatePresence(void) @@ -497,21 +512,21 @@ void DRPC_UpdatePresence(void) { // User doesn't want to show their game information, so update with empty presence. // This just shows that they're playing SRB2Kart. (If that's too much, then they should disable game activity :V) + DRPC_EmptyRequests(); Discord_UpdatePresence(&discordPresence); return; } -/* #ifdef DEVELOP // This way, we can use the invite feature in-dev, but not have snoopers seeing any potential secrets! :P discordPresence.largeImageKey = "miscdevelop"; discordPresence.largeImageText = "No peeking!"; discordPresence.state = "Testing the game"; + DRPC_EmptyRequests(); Discord_UpdatePresence(&discordPresence); return; #endif // DEVELOP -*/ // Server info if (netgame) @@ -710,11 +725,7 @@ void DRPC_UpdatePresence(void) if (joinSecretSet == false) { // Not able to join? Flush the request list, if it exists. - while (discordRequestList != NULL) - { - Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE); - DRPC_RemoveRequest(discordRequestList); - } + DRPC_EmptyRequests(); } Discord_UpdatePresence(&discordPresence); From f20f0b2f19d8a01f886252ce899a7762cdb88895 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 23 Aug 2020 16:15:33 -0400 Subject: [PATCH 107/193] Better CMake support No idea if it compiles on Mac, it does on Linux though, at least for me. --- cmake/Modules/FindDiscordRPC.cmake | 8 ++++---- src/CMakeLists.txt | 13 ++++++------- src/sdl/CMakeLists.txt | 11 +++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cmake/Modules/FindDiscordRPC.cmake b/cmake/Modules/FindDiscordRPC.cmake index b43221abd..e71762767 100644 --- a/cmake/Modules/FindDiscordRPC.cmake +++ b/cmake/Modules/FindDiscordRPC.cmake @@ -3,11 +3,11 @@ include(LibFindMacros) libfind_pkg_check_modules(DISCORDRPC_PKGCONF DISCORDRPC) find_path(DISCORDRPC_INCLUDE_DIR - NAMES discord-rpc.h + NAMES discord_rpc.h PATHS ${DISCORDRPC_PKGCONF_INCLUDE_DIRS} - "/usr/include/discord-rpc" - "/usr/local/include/discord-rpc" + "/usr/include" + "/usr/local/include" ) find_library(DISCORDRPC_LIBRARY @@ -20,4 +20,4 @@ find_library(DISCORDRPC_LIBRARY set(DISCORDRPC_PROCESS_INCLUDES DISCORDRPC_INCLUDE_DIR) set(DISCORDRPC_PROCESS_LIBS DISCORDRPC_LIBRARY) -libfind_process(DISCORDRPC) \ No newline at end of file +libfind_process(DISCORDRPC) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7cb9d1191..507eaab2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -237,7 +237,7 @@ set(SRB2_CONFIG_STATIC_OPENGL OFF CACHE BOOL ### use internal libraries? if(${CMAKE_SYSTEM} MATCHES "Windows") ###set on Windows only set(SRB2_CONFIG_USE_INTERNAL_LIBRARIES OFF CACHE BOOL - "Use SRB2's internal copies of required dependencies (SDL2, PNG, zlib, GME).") + "Use SRB2Kart's internal copies of required dependencies (SDL2, PNG, zlib, GME).") endif() if(${SRB2_CONFIG_HAVE_BLUA}) @@ -359,17 +359,16 @@ if(${SRB2_CONFIG_HAVE_DISCORDRPC}) set(DISCORDRPC_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/discord-rpc/win32-dynamic/lib -ldiscord-rpc") endif() else() - find_package(DISCORDRPC) + find_package(DiscordRPC) endif() if(${DISCORDRPC_FOUND}) set(SRB2_HAVE_DISCORDRPC ON) add_definitions(-DHAVE_DISCORDRPC) - set(SRB2_DISCORDRPC_SOURCES - discord.c - discord.h - ) + set(SRB2_DISCORDRPC_SOURCES discord.c) + set(SRB2_DISCORDRPC_HEADERS discord.h) prepend_sources(SRB2_DISCORDRPC_SOURCES) - source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES}) + prepend_sources(SRB2_DISCORDRPC_HEADERS) + source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS}) else() message(WARNING "You have specified that Discord Rich Presence is available but it was not found.") endif() diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index 33c83b8b3..f8d8f08db 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -70,6 +70,8 @@ if(${SDL2_FOUND}) set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS} + ${SRB2_DISCORDRPC_SOURCES} + ${SRB2_DISCORDRPC_HEADERS} ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS} ${SRB2_CORE_RENDER_SOURCES} @@ -86,9 +88,11 @@ if(${SDL2_FOUND}) ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS}) source_group("Renderer" FILES ${SRB2_CORE_RENDER_SOURCES}) source_group("Game" FILES ${SRB2_CORE_GAME_SOURCES}) + source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS}) source_group("Assembly" FILES ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES}) source_group("LUA" FILES ${SRB2_LUA_SOURCES} ${SRB2_LUA_HEADERS}) source_group("LUA\\Interpreter" FILES ${SRB2_BLUA_SOURCES} ${SRB2_BLUA_HEADERS}) + if(${SRB2_CONFIG_HWRENDER}) set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} @@ -153,6 +157,7 @@ if(${SDL2_FOUND}) ${ZLIB_LIBRARIES} ${OPENGL_LIBRARIES} ${CURL_LIBRARIES} + ${DISCORDRPC_LIBRARIES} ) set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") else() @@ -164,6 +169,7 @@ if(${SDL2_FOUND}) ${ZLIB_LIBRARIES} ${OPENGL_LIBRARIES} ${CURL_LIBRARIES} + ${DISCORDRPC_LIBRARIES} ) if(${CMAKE_SYSTEM} MATCHES Linux) @@ -244,6 +250,7 @@ if(${SDL2_FOUND}) ${ZLIB_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} + ${DISCORDRPC_INCLUDE_DIRS} ) if(${SRB2_HAVE_MIXER}) @@ -328,6 +335,10 @@ if(${SDL2_FOUND}) getwinlib(libgme "libgme.dll") endif() + if(${SRB2_CONFIG_HAVE_DISCORDRPC}) + getwinlib(libgme "discord-rpc.dll") + endif() + install(PROGRAMS ${win_extra_dll_list} DESTINATION . From 5d5385f7a1fa1eb1aba4084d47732c9abf029d69 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 23 Aug 2020 17:45:31 -0400 Subject: [PATCH 108/193] It's discord-rpc, not libgme --- src/sdl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index f8d8f08db..04670bdc8 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -336,7 +336,7 @@ if(${SDL2_FOUND}) endif() if(${SRB2_CONFIG_HAVE_DISCORDRPC}) - getwinlib(libgme "discord-rpc.dll") + getwinlib(discord-rpc "discord-rpc.dll") endif() install(PROGRAMS From 858f345802f1d19a18d2a24ab25a18ccadd0ea1f Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 00:56:53 -0400 Subject: [PATCH 109/193] Haha NONET... --- src/d_clisrv.c | 7 ++++++- src/d_netfil.c | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c847f0a65..21b8e86c9 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2038,6 +2038,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET +#ifndef NONET static void M_ConfirmConnect(event_t *ev) { if (ev->type == ev_keydown) @@ -2072,6 +2073,7 @@ static void M_ConfirmConnect(event_t *ev) } } } +#endif static boolean CL_FinishedFileList(void) { @@ -2157,10 +2159,12 @@ static boolean CL_FinishedFileList(void) if (!curl_failedwebdownload) #endif { +#ifndef NONET downloadcompletednum = 0; downloadcompletedsize = 0; totalfilesrequestednum = 0; totalfilesrequestedsize = 0; +#endif for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) @@ -2276,7 +2280,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) *asksent = I_GetTime(); } #else - (void)viams; (void)asksent; // No netgames, so we skip this state. cl_mode = CL_ASKJOIN; @@ -2993,8 +2996,10 @@ void CL_Reset(void) fileneedednum = 0; memset(fileneeded, 0, sizeof(fileneeded)); +#ifndef NONET totalfilesrequestednum = 0; totalfilesrequestedsize = 0; +#endif firstconnectattempttime = 0; serverisfull = false; connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack diff --git a/src/d_netfil.c b/src/d_netfil.c index 621ce2953..c9bfb4ea7 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -874,8 +874,10 @@ void Got_Filetxpak(void) file->status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), filename); +#ifndef NONET downloadcompletednum++; downloadcompletedsize += file->totalsize; +#endif } } else From d1d1b3aadfdfafbab433d4d50543fc448e48485f Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 00:58:14 -0400 Subject: [PATCH 110/193] Screw you C90 --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 4c044fed2..e05067194 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -58,7 +58,7 @@ static void MasterServer_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {2, "MIN"}, {60, "MAX"}, - {0} + {0, NULL} }; consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; From 7005d8303c7ddbe4897abd76743006f6a7825553 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 01:00:28 -0400 Subject: [PATCH 111/193] Turns out compiling with the internal version of curl was broken on CMake the entire time :upside_down_face: --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9dd7f500a..18c3a34f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -398,7 +398,7 @@ endif() if(${SRB2_CONFIG_HAVE_CURL}) if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) set(CURL_FOUND ON) - set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl) + set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl/include) if(${SRB2_SYSTEM_BITS} EQUAL 64) set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib64 -lcurl") else() # 32-bit From 0021128446993eadf59ef79ea09069c8ade10bfb Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 01:06:10 -0400 Subject: [PATCH 112/193] This is just stupid --- src/d_clisrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 21b8e86c9..581b6d2d8 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2164,7 +2164,6 @@ static boolean CL_FinishedFileList(void) downloadcompletedsize = 0; totalfilesrequestednum = 0; totalfilesrequestedsize = 0; -#endif for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) @@ -2194,9 +2193,11 @@ static boolean CL_FinishedFileList(void) "Press ACCEL to continue or BRAKE to cancel.\n\n" ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + Z_Free(downloadsize); cl_mode = CL_CONFIRMCONNECT; } +#endif #ifdef HAVE_CURL else { From 6c0c0d710f66174778cda4a399711d49ae439927 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 01:13:59 -0400 Subject: [PATCH 113/193] Remind me why NONET exists? --- src/d_clisrv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 581b6d2d8..4364051d1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2038,9 +2038,9 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET -#ifndef NONET static void M_ConfirmConnect(event_t *ev) { +#ifndef NONET if (ev->type == ev_keydown) { if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[gc_accelerate][0] || ev->data1 == gamecontrol[gc_accelerate][1]) @@ -2072,8 +2072,8 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } } -} #endif +} static boolean CL_FinishedFileList(void) { @@ -2164,12 +2164,15 @@ static boolean CL_FinishedFileList(void) downloadcompletedsize = 0; totalfilesrequestednum = 0; totalfilesrequestedsize = 0; +#endif for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) { +#ifndef NONET totalfilesrequestednum++; totalfilesrequestedsize += fileneeded[i].totalsize; +#endif } if (totalfilesrequestedsize>>20 >= 100) @@ -2193,11 +2196,9 @@ static boolean CL_FinishedFileList(void) "Press ACCEL to continue or BRAKE to cancel.\n\n" ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - Z_Free(downloadsize); cl_mode = CL_CONFIRMCONNECT; } -#endif #ifdef HAVE_CURL else { From 94ca85b2297ddf35f756efc820c5baecbe58793d Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 01:18:38 -0400 Subject: [PATCH 114/193] Hopefully the last time... --- src/d_clisrv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4364051d1..0842a65c3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2072,6 +2072,8 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } } +#else + (void)ev; #endif } @@ -2175,10 +2177,12 @@ static boolean CL_FinishedFileList(void) #endif } +#ifndef NONET if (totalfilesrequestedsize>>20 >= 100) downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); else downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); +#endif if (serverisfull) M_StartMessage(va(M_GetText( From 23b1f8fc5e73b7de4662db8e7bacfdabe0a8523e Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Aug 2020 01:25:34 -0400 Subject: [PATCH 115/193] Initialize this variable --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0842a65c3..fd6fbda32 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2080,7 +2080,7 @@ static void M_ConfirmConnect(event_t *ev) static boolean CL_FinishedFileList(void) { INT32 i; - char *downloadsize; + char *downloadsize = NULL; //CONS_Printf(M_GetText("Checking files...\n")); i = CL_CheckFiles(); if (i == 4) // still checking ... From d52a35e5b6c1e65be94820fa42a8b8e8f0b8d091 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:51:01 -0700 Subject: [PATCH 116/193] Fix NOGME compiling --- src/sdl/mixer_sound.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 86fc8efb8..c5650192d 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -110,6 +110,7 @@ static void var_cleanup(void) internal_volume = 100; } +#if defined (HAVE_LIBGME) && defined (HAVE_ZLIB) static const char* get_zlib_error(int zErr) { switch (zErr) @@ -130,6 +131,7 @@ static const char* get_zlib_error(int zErr) return "unknown error"; } } +#endif /// ------------------------ /// Audio System From 594600e5ae3b47fec3e96216b459f2df509c8f62 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:52:55 -0700 Subject: [PATCH 117/193] Fix NOPNG compiling --- src/w_wad.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 77d0d6d2e..d4da027be 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -11,6 +11,24 @@ /// \file w_wad.c /// \brief Handles WAD file header, directory, lump I/O +#ifdef HAVE_ZLIB +#ifndef _MSC_VER +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#endif + +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE +#endif + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif + +#include "zlib.h" +#endif + #ifdef __GNUC__ #include #endif @@ -66,24 +84,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define O_BINARY 0 #endif -#ifdef HAVE_ZLIB -#ifndef _MSC_VER -#ifndef _LARGEFILE64_SOURCE -#define _LARGEFILE64_SOURCE -#endif -#endif - -#ifndef _LFS64_LARGEFILE -#define _LFS64_LARGEFILE -#endif - -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 0 -#endif - -#include "zlib.h" -#endif - typedef struct { From 6a14d9c1d492328ba172ed7329bb9fcf57047213 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:55:42 -0700 Subject: [PATCH 118/193] Fix NOMIXER compiling --- src/sdl/sdl_sound.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index 4bb1b5676..929ac79f5 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -1434,6 +1434,8 @@ static void I_ResumeGME(void) boolean I_LoadSong(char *data, size_t len) { + (void)data; + (void)len; return false; } @@ -1495,6 +1497,7 @@ boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms (void)target_volume; (void)source_volume; (void)ms; + (void)callback; return false; } @@ -1502,6 +1505,7 @@ boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)) { (void)target_volume; (void)ms; + (void)callback; return false; } From a79316b3759f10de051edc5b6a632e1fce3632de Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:58:39 -0700 Subject: [PATCH 119/193] Kill NOHS --- src/Makefile | 24 ------------------------ src/doomdef.h | 6 ------ src/sdl/Makefile.cfg | 22 ---------------------- 3 files changed, 52 deletions(-) diff --git a/src/Makefile b/src/Makefile index dd678cb26..b417a38e4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,7 +63,6 @@ # Compile with extra warnings, add 'WARNINGMODE=1' # Compile without NASM's tmap.nas, add 'NOASM=1' # Compile without 3D hardware support, add 'NOHW=1' -# Compile without 3D sound support, add 'NOHS=1' # Compile with GDBstubs, add 'RDB=1' # Compile without PNG, add 'NOPNG=1' # Compile without zlib, add 'NOZLIB=1' @@ -136,7 +135,6 @@ NOPNG=1 NOZLIB=1 NONET=1 NOHW=1 -NOHS=1 NOASM=1 NOIPX=1 EXENAME?=srb2dummy @@ -167,7 +165,6 @@ endif ifdef PANDORA NONX86=1 NOHW=1 -NOHS=1 endif ifdef WII @@ -217,7 +214,6 @@ NOPNG=1 NOZLIB=1 NONET=1 #NOHW=1 -NOHS=1 NOASM=1 NOIPX=1 NONX86=1 @@ -287,13 +283,6 @@ endif $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o endif -ifdef NOHS - OPTS+=-DNOHS -else - OPTS+=-DHW3SOUND - OBJS+=$(OBJDIR)/hw3sound.o -endif - OPTS += -DCOMPVERSION ifndef NONX86 @@ -942,19 +931,6 @@ $(OBJDIR)/r_minigl.o: hardware/r_minigl/r_minigl.c hardware/r_opengl/r_opengl.h $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ endif -ifndef NOHS -$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_ds3d.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_ds3d/s_ds3d.c - -$(OBJDIR)/s_fmod.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_fmod/s_fmod.c - -$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_openal/s_openal.c -endif endif endif diff --git a/src/doomdef.h b/src/doomdef.h index 1965b3d06..be4557b1c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -30,7 +30,6 @@ #ifdef HAVE_MIXER //#if !defined(DC) && !defined(_WIN32_WCE) && !defined(_XBOX) && !defined(GP2X) #define SOUND SOUND_MIXER - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -47,7 +46,6 @@ // Use FMOD? #ifdef HAVE_FMOD #define SOUND SOUND_FMOD - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -64,10 +62,6 @@ #if !defined (HWRENDER) && !defined (NOHW) #define HWRENDER #endif -// judgecutor: 3D sound support -#if !defined(HW3SOUND) && !defined (NOHS) -#define HW3SOUND -#endif #endif #if defined (_WIN32) || defined (_WIN32_WCE) diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index b0c591ce2..1744d6917 100644 --- a/src/sdl/Makefile.cfg +++ b/src/sdl/Makefile.cfg @@ -53,28 +53,6 @@ ifndef NOHW OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/ogl_sdl.o endif -ifndef NOHS -ifdef OPENAL - OBJS+=$(OBJDIR)/s_openal.o - OPTS+=-DSTATIC3DS - STATICHS=1 -else -ifdef FMOD - OBJS+=$(OBJDIR)/s_fmod.o - OPTS+=-DSTATIC3DS - STATICHS=1 -else -ifdef MINGW -ifdef DS3D - OBJS+=$(OBJDIR)/s_ds3d.o - OPTS+=-DSTATIC3DS - STATICHS=1 -endif -endif -endif -endif -endif - ifdef NOMIXER i_sound_o=$(OBJDIR)/sdl_sound.o else From 3f8edd38a5b74903185f3f49955b22f867105d27 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 18:06:00 -0700 Subject: [PATCH 120/193] Fix NOHW compiling --- src/p_setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index f9e23a5ea..301ba384e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1401,8 +1401,6 @@ static void P_LoadRawSideDefs2(void *data) { UINT16 i; INT32 num; - size_t j; - RGBA_t color; for (i = 0; i < numsides; i++) { @@ -1469,6 +1467,8 @@ static void P_LoadRawSideDefs2(void *data) || (msd->bottomtexture[0] == '#' && msd->bottomtexture[1] && msd->bottomtexture[2] && msd->bottomtexture[3] && msd->bottomtexture[4] && msd->bottomtexture[5] && msd->bottomtexture[6])) { char *col; + RGBA_t color; + size_t j; sec->midmap = R_CreateColormap(msd->toptexture, msd->midtexture, msd->bottomtexture); From 11f98adad4fa3cd7d45d40b890dcd7213c4ad759 Mon Sep 17 00:00:00 2001 From: ThatAwesomeGuy173 Date: Tue, 25 Aug 2020 23:12:31 -0600 Subject: [PATCH 121/193] Replace 'ignoring skin' debug print with a proper console warning --- src/r_things.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_things.c b/src/r_things.c index 25c4edb3d..9cb35e191 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2746,7 +2746,7 @@ void R_AddSkins(UINT16 wadnum) if (numskins >= MAXSKINS) { - CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS); + CONS_Alert(CONS_WARNING, M_GetText("Unable to add skin, too many characters are loaded (%d maximum)\n"), MAXSKINS); continue; // so we know how many skins couldn't be added } buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); From 45af41ef0cf05b3c4efd0196e16f466fa7aa416c Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Aug 2020 20:20:12 -0700 Subject: [PATCH 122/193] Check DEFAULTDIR for srb2.srb after cwd --- src/sdl/i_system.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index c630e5d8b..10fa99cb9 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -3660,6 +3660,18 @@ static const char *locateWad(void) #endif +#ifdef DEFAULTDIR + I_OutputMsg(",HOME/.srb2kart"); + // examine user jart directory + if ((envstr = I_GetEnv("HOME")) != NULL) + { + sprintf(returnWadPath, "%s" PATHSEP DEFAULTDIR, envstr); + if (isWadPathOk(returnWadPath)) + return returnWadPath; + } +#endif + + #ifdef CMAKECONFIG #ifndef NDEBUG I_OutputMsg(","CMAKE_ASSETS_DIR); From e4a522353fd77176e17e439e81869a80f9d07a57 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Aug 2020 23:37:52 -0700 Subject: [PATCH 123/193] Say DEFAULTDIR not .srb2kart --- src/sdl/i_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 10fa99cb9..3c3f1cc50 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -3661,7 +3661,7 @@ static const char *locateWad(void) #ifdef DEFAULTDIR - I_OutputMsg(",HOME/.srb2kart"); + I_OutputMsg(",HOME/" DEFAULTDIR); // examine user jart directory if ((envstr = I_GetEnv("HOME")) != NULL) { From 99e3adcdca254cbe11a1ec2d1424273184d0a0f6 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 26 Apr 2019 14:14:25 -0400 Subject: [PATCH 124/193] Flush random map pool after all but 3 are played --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 7303fdbaa..c9e18210d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3647,7 +3647,7 @@ tryagain: void G_AddMapToBuffer(INT16 map) { - INT16 bufx, refreshnum = (TOLMaps(G_TOLFlag(gametype)) / 2) + 1; + INT16 bufx, refreshnum = max(0, TOLMaps(G_TOLFlag(gametype))-3); // Add the map to the buffer. for (bufx = NUMMAPS-1; bufx > 0; bufx--) From 9be59cc560fc43cd87f2cc28bf893763ed076873 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 27 Aug 2020 14:58:46 -0400 Subject: [PATCH 125/193] Expose demo.playback to Lua --- src/dehacked.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index a117866a5..72da6a9cd 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9762,6 +9762,9 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"exitcountdown")) { lua_pushinteger(L, exitcountdown); // This name is pretty dumb. Hence why we'll prefer more descriptive names at least in Lua... return 1; + } else if (fastcmp(word,"replayplayback")) { + lua_pushboolean(L, demo.playback); + return 1; } return 0; } From dcad2cd1fa8ec0660c74f05a803b49922d502beb Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 3 Jul 2019 17:11:38 -0700 Subject: [PATCH 126/193] Convert from caret colors when sending SERVERINFO --- src/d_clisrv.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b3b2cc034..13a9297b4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1306,6 +1306,70 @@ static boolean CL_SendJoin(void) return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak)); } +static void +CopyCaretColors (char *p, const char *s, int n) +{ + char *t; + int m; + int c; + if (!n) + return; + while (( t = strchr(s, '^') )) + { + m = ( t - s ); + + if (m >= n) + { + memcpy(p, s, n); + return; + } + else + memcpy(p, s, m); + + p += m; + n -= m; + s += m; + + if (!n) + return; + + if (s[1]) + { + c = toupper(s[1]); + if (isdigit(c)) + c = 0x80 + ( c - '0' ); + else if (c >= 'A' && c <= 'F') + c = 0x80 + ( c - 'A' ); + else + c = 0; + + if (c) + { + *p++ = c; + n--; + + if (!n) + return; + } + else + { + if (n < 2) + break; + + memcpy(p, s, 2); + + p += 2; + n -= 2; + } + + s += 2; + } + else + break; + } + strncpy(p, s, n); +} + static void SV_SendServerInfo(INT32 node, tic_t servertime) { UINT8 *p; @@ -1334,8 +1398,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) (dedicated ? SV_DEDICATED : 0) ); - - strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, + CopyCaretColors(netbuffer->u.serverinfo.servername, cv_servername.string, MAXSERVERNAME); strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); From 7a19d9a9df59ccae7e8bb4a675f7bb501e1ff8fa Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 3 Jul 2019 17:41:11 -0700 Subject: [PATCH 127/193] Convert caret color codes to SRB2 color codes --- src/d_clisrv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 13a9297b4..a876e9925 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3951,6 +3951,7 @@ static void HandleTimeout(SINT8 node) */ static void HandleServerInfo(SINT8 node) { + char servername[MAXSERVERNAME]; // compute ping in ms const tic_t ticnow = I_GetTime(); const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); @@ -3959,6 +3960,8 @@ static void HandleServerInfo(SINT8 node) netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; netbuffer->u.serverinfo.application [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; + memcpy(servername, netbuffer->u.serverinfo.servername, MAXSERVERNAME); + CopyCaretColors(netbuffer->u.serverinfo.servername, servername, MAXSERVERNAME); netbuffer->u.serverinfo.gametype = (UINT8)((netbuffer->u.serverinfo.gametype == VANILLA_GT_MATCH) ? GT_MATCH : GT_RACE); SL_InsertServer(&netbuffer->u.serverinfo, node); From 61d6ee098259313088f459895afbf52021d646df Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 27 Aug 2020 18:50:36 -0400 Subject: [PATCH 128/193] Misc changes to how the program runs - Call DRPC_Init at the end of D_SRB2Main instead of in the middle - Call Discord_RunCallbacks in D_SRB2Loop instead of G_Ticker - Fix errorcount nums in I_Error, an old leftover from when Discord_Shutdown was not handled by I_AddExitFunction --- src/d_main.c | 12 ++++++++---- src/g_game.c | 4 ---- src/sdl/i_system.c | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 9d5d29a29..4b7db8079 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -731,6 +731,10 @@ void D_SRB2Loop(void) #ifdef HAVE_BLUA LUA_Step(); #endif + +#ifdef HAVE_DISCORDRPC + Discord_RunCallbacks(); +#endif } } @@ -1427,10 +1431,6 @@ void D_SRB2Main(void) CONS_Printf("ST_Init(): Init status bar.\n"); ST_Init(); -#ifdef HAVE_DISCORDRPC - DRPC_Init(); -#endif - if (M_CheckParm("-room")) { if (!M_IsNextParm()) @@ -1617,6 +1617,10 @@ void D_SRB2Main(void) if (!P_SetupLevel(false)) I_Quit(); // fail so reset game stuff } + +#ifdef HAVE_DISCORDRPC + DRPC_Init(); +#endif } const char *D_Home(void) diff --git a/src/g_game.c b/src/g_game.c index 495131e81..dcc1adff3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2503,10 +2503,6 @@ void G_Ticker(boolean run) if (spectatedelay4) spectatedelay4--; } - -#ifdef HAVE_DISCORDRPC - Discord_RunCallbacks(); -#endif } // diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 419204cd1..3a1dd124c 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -3251,11 +3251,11 @@ void I_Error(const char *error, ...) I_ShutdownGraphics(); if (errorcount == 6) I_ShutdownInput(); - if (errorcount == 8) + if (errorcount == 7) I_ShutdownSystem(); - if (errorcount == 9) + if (errorcount == 8) SDL_Quit(); - if (errorcount == 10) + if (errorcount == 9) { M_SaveConfig(NULL); G_SaveGameData(false); From 5e9e3e163d5b2e4f493d2053d525c79bf48c2eb1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 27 Aug 2020 19:14:45 -0400 Subject: [PATCH 129/193] Attach initial join discord info to servercfg, so that we don't have a pause from needing to also send XD_DISCORD XD_DISCORD is now reserved for when the options are changed mid-game --- src/d_clisrv.c | 19 +++++++++++++++---- src/d_clisrv.h | 5 +++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0447d16be..88a4754de 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1606,6 +1606,15 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor; } + netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value; + +#ifdef HAVE_DISCORDRPC + netbuffer->u.servercfg.discordinvites = (boolean)cv_discordinvites.value; +#else + netbuffer->u.servercfg.discordinvites = false; +#endif + memcpy(netbuffer->u.servercfg.server_context, server_context, 8); op = p = netbuffer->u.servercfg.varlengthinputs; @@ -3888,10 +3897,6 @@ static boolean SV_AddWaitingPlayers(void) } } -#ifdef HAVE_DISCORDRPC - DRPC_SendDiscordInfo(); -#endif - return newplayer; } @@ -4316,6 +4321,12 @@ static void HandlePacketFromAwayNode(SINT8 node) for (j = 0; j < MAXPLAYERS; j++) adminplayers[j] = netbuffer->u.servercfg.adminplayers[j]; memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + +#ifdef HAVE_DISCORDRPC + discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer; + discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer; + discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites; +#endif } nodeingame[(UINT8)servernode] = true; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a872b02e8..ef988ac8f 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -334,6 +334,11 @@ typedef struct char server_context[8]; // Unique context id, generated at server startup. + // Discord info (always defined for net compatibility) + UINT8 maxplayer; + boolean allownewplayer; + boolean discordinvites; + UINT8 varlengthinputs[0]; // Playernames and netvars } ATTRPACK serverconfig_pak; From ed88e4dd7ca67cb502b57202fd34c5869c48d042 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 27 Aug 2020 19:36:37 -0400 Subject: [PATCH 130/193] This should be set for server too!! --- src/d_clisrv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 88a4754de..e952d4492 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4321,13 +4321,13 @@ static void HandlePacketFromAwayNode(SINT8 node) for (j = 0; j < MAXPLAYERS; j++) adminplayers[j] = netbuffer->u.servercfg.adminplayers[j]; memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } #ifdef HAVE_DISCORDRPC - discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer; - discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer; - discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites; + discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer; + discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer; + discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites; #endif - } nodeingame[(UINT8)servernode] = true; serverplayer = netbuffer->u.servercfg.serverplayer; From 18a7f1b3f69d1dc28936ff729485d9568a52972f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 27 Aug 2020 20:10:25 -0400 Subject: [PATCH 131/193] Add join/leave notification sounds --- src/d_clisrv.c | 7 ++++++- src/sounds.c | 2 ++ src/sounds.h | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e952d4492..0b38ee083 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3363,7 +3363,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) #endif } - if (msg != KICK_MSG_PLAYER_QUIT) + if (msg == KICK_MSG_PLAYER_QUIT) + S_StartSound(NULL, sfx_leave); // intended leave + else S_StartSound(NULL, sfx_syfail); // he he he switch (msg) @@ -3780,6 +3782,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) if (netgame) { + if (node != mynode) + S_StartSound(NULL, sfx_join); + if (server && cv_showjoinaddress.value) { const char *address; diff --git a/src/sounds.c b/src/sounds.c index 40ef0f7f2..9285a2a87 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -816,6 +816,8 @@ sfxinfo_t S_sfx[NUMSFX] = {"mkuma", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Trigger Happy Havoc Monokuma {"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping + {"join", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server + {"leave", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server {"requst", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request {"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: diff --git a/src/sounds.h b/src/sounds.h index 2a7169190..4091081b5 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -891,6 +891,8 @@ typedef enum sfx_mkuma, sfx_toada, sfx_bsnipe, + sfx_join, + sfx_leave, sfx_requst, sfx_syfail, sfx_itfree, From df78298a11d098c7cfd487f5846abf07d7357e74 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 29 Aug 2020 17:21:54 -0700 Subject: [PATCH 132/193] C90? (cherry picked from commit 1f38db46298181d117432e12e88b326a92b4b785) --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 4c044fed2..ba0854049 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -58,7 +58,7 @@ static void MasterServer_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {2, "MIN"}, {60, "MAX"}, - {0} + {0,NULL} }; consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; From 754347987d2128f94111f03a202b4b2977bd5112 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 29 Aug 2020 17:51:59 -0700 Subject: [PATCH 133/193] Fix stupid NONET (cherry picked from commit 679bfa21edec6b652def93082da5586e6c755678) --- src/d_clisrv.c | 1 - src/doomdef.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b3b2cc034..b6b8d63bf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2106,7 +2106,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) *asksent = I_GetTime(); } #else - (void)viams; (void)asksent; // No netgames, so we skip this state. cl_mode = CL_ASKJOIN; diff --git a/src/doomdef.h b/src/doomdef.h index be4557b1c..e9dddf5d3 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -673,7 +673,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Hardware renderer: OpenGL #define GL_SHADERS -#ifdef HAVE_CURL +#if defined (HAVE_CURL) && ! defined (NONET) #define MASTERSERVER #else #undef UPDATE_ALERT From b8f1842855b53c659ea129eb9c2455c8f4c7a677 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 21:42:04 -0400 Subject: [PATCH 134/193] Use ENTER and ESC A and B are v2 terminology --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 1979e30a8..2fddb6745 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11453,7 +11453,7 @@ static void M_DrawDiscordRequests(void) boolean removeRequest = false; const char *wantText = "...would like to join!"; - const char *controlText = "\x82" "A" "\x80" " - Accept " "\x82" "B" "\x80" " - Decline"; + const char *controlText = "\x82" "ENTER" "\x80" " - Accept " "\x82" "ESC" "\x80" " - Decline"; INT32 x = 100; INT32 y = 133; From fbd1280e175fc01ade52fd1ab05f72893dc71252 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 30 Aug 2020 19:02:31 -0700 Subject: [PATCH 135/193] Update patch.kart asset hash --- src/config.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index a7a0be53b..b529121e1 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -37,7 +37,7 @@ * Last updated 2015 / 05 / 03 - SRB2 v2.1.15 - srb2.srb * Last updated 2018 / 12 / 23 - SRB2 v2.1.22 - patch.dta * Last updated 2019 / 01 / 18 - Kart v1.0.2 - Main assets - * Last updated 2020 / 05 / 09 - Kart v1.2 - patch.kart + * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart */ // Base SRB2 hashes @@ -52,7 +52,7 @@ #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" #define ASSET_HASH_MAPS_KART "1335cd064656aedca359cfbb5233ac4a" #ifdef USE_PATCH_KART -#define ASSET_HASH_PATCH_KART "2a556446ab428492110e7544841e1f42" +#define ASSET_HASH_PATCH_KART "f7b68076f8abc54e1a78963d97f69ab2" #endif #endif From b66b0bf648d268be1cedd3de662fa72cb430a669 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 30 Aug 2020 19:06:31 -0700 Subject: [PATCH 136/193] Update versions to 1.3 --- CMakeLists.txt | 2 +- appveyor.yml | 4 ++-- src/doomdef.h | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51f98707d..1e80b549e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. # Version change is fine. project(SRB2 - VERSION 1.2 + VERSION 1.3 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) diff --git a/appveyor.yml b/appveyor.yml index 4ad24ee05..7e87c271c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.2.{branch}-{build} +version: 1.3.{branch}-{build} os: MinGW environment: @@ -29,7 +29,7 @@ environment: ############################## DPL_ENABLED: 0 DPL_TAG_ENABLED: 0 - DPL_INSTALLER_NAME: srb2kart-v12 + DPL_INSTALLER_NAME: srb2kart-v13 # Asset handling is barebones vs. Travis Deployer. We operate on 7z only. # Include the README files and the OpenGL batch in the main and patch archives. # The x86/x64 archives contain the DLL binaries. diff --git a/src/doomdef.h b/src/doomdef.h index e9dddf5d3..bff809923 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -148,9 +148,9 @@ extern char logfilename[1024]; // we use comprevision and compbranch instead. #else #define VERSION 1 // Game version -#define SUBVERSION 2 // more precise version number -#define VERSIONSTRING "v1.2" -#define VERSIONSTRINGW L"v1.2" +#define SUBVERSION 3 // more precise version number +#define VERSIONSTRING "v1.3" +#define VERSIONSTRINGW L"v1.3" // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // And change CMakeLists.txt, for CMake users! // AND appveyor.yml, for the build bots! @@ -223,7 +223,7 @@ extern char logfilename[1024]; // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1". -#define MODVERSION 6 +#define MODVERSION 7 // Filter consvars by version // To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically. From 7c1f73ed7136d0867236e5aa3fef897549f14a35 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 30 Aug 2020 19:11:30 -0700 Subject: [PATCH 137/193] NO_DISCORDRPC to disable rich presence on Windows --- src/win32/Makefile.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index 1b9e742b5..935b27222 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -30,7 +30,9 @@ ifndef MINGW64 #miniupnc is broken with MINGW64 endif endif +ifndef NO_DISCORDRPC HAVE_DISCORDRPC=1 +endif OPTS=-DSTDC_HEADERS From 2d7c835c4d27ca0dfeda4098006e099cb8b3cff5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 23:20:06 -0400 Subject: [PATCH 138/193] Dedicated servers can change discordinvites without needing RPC support --- src/d_clisrv.c | 24 ++++++++++++++---------- src/d_netcmd.c | 6 +++++- src/d_netcmd.h | 2 ++ src/discord.c | 26 -------------------------- src/discord.h | 11 ----------- 5 files changed, 21 insertions(+), 48 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 51f084195..dcd85156c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1671,12 +1671,7 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value; - -#ifdef HAVE_DISCORDRPC netbuffer->u.servercfg.discordinvites = (boolean)cv_discordinvites.value; -#else - netbuffer->u.servercfg.discordinvites = false; -#endif memcpy(netbuffer->u.servercfg.server_context, server_context, 8); op = p = netbuffer->u.servercfg.varlengthinputs; @@ -3587,11 +3582,20 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum); static void Joinable_OnChange(void) { -#ifdef HAVE_DISCORDRPC - DRPC_SendDiscordInfo(); -#else - return; -#endif + UINT8 buf[3]; + UINT8 *p = buf; + UINT8 maxplayer; + + if (!server) + return; + + maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + + WRITEUINT8(p, maxplayer); + WRITEUINT8(p, cv_allownewplayer.value); + WRITEUINT8(p, cv_discordinvites.value); + + SendNetXCmd(XD_DISCORD, &buf, 3); } // called one time at init diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5efe30889..8959486e3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -467,6 +467,10 @@ consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, consvar_t cv_sleep = {"cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL}; +// Here for dedicated servers +static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; +consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, DRPC_SendDiscordInfo, 0, NULL, NULL, 0, 0, NULL}; + INT16 gametype = GT_RACE; // SRB2kart boolean forceresetplayers = false; boolean deferencoremode = false; @@ -1012,8 +1016,8 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_discordrp); CV_RegisterVar(&cv_discordstreamer); CV_RegisterVar(&cv_discordasks); - CV_RegisterVar(&cv_discordinvites); #endif + CV_RegisterVar(&cv_discordinvites); } /** Checks if a name (as received from another player) is okay. diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 1e1588083..2c0ed142c 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -151,6 +151,8 @@ extern consvar_t cv_skipmapcheck; extern consvar_t cv_sleep; +extern consvar_t cv_discordinvites; + typedef enum { XD_NAMEANDCOLOR = 1, diff --git a/src/discord.c b/src/discord.c index 236c801bf..eaf595c4d 100644 --- a/src/discord.c +++ b/src/discord.c @@ -41,9 +41,6 @@ consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_Upd consvar_t cv_discordstreamer = {"discordstreamer", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_discordasks = {"discordasks", "Yes", CV_SAVE|CV_CALL, CV_YesNo, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; -consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, DRPC_SendDiscordInfo, 0, NULL, NULL, 0, 0, NULL}; - struct discordInfo_s discordInfo; discordRequest_t *discordRequestList = NULL; @@ -338,29 +335,6 @@ void DRPC_Init(void) DRPC_UpdatePresence(); } -/*-------------------------------------------------- - void DRPC_SendDiscordInfo(void) - - See header file for description. ---------------------------------------------------*/ -void DRPC_SendDiscordInfo(void) -{ - UINT8 buf[3]; - UINT8 *p = buf; - UINT8 maxplayer; - - if (!server) - return; - - maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); - - WRITEUINT8(p, maxplayer); - WRITEUINT8(p, cv_allownewplayer.value); - WRITEUINT8(p, cv_discordinvites.value); - - SendNetXCmd(XD_DISCORD, &buf, 3); -} - #ifdef HAVE_CURL /*-------------------------------------------------- static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) diff --git a/src/discord.h b/src/discord.h index 97c5557b6..a6bb1134a 100644 --- a/src/discord.h +++ b/src/discord.h @@ -20,7 +20,6 @@ extern consvar_t cv_discordrp; extern consvar_t cv_discordstreamer; extern consvar_t cv_discordasks; -extern consvar_t cv_discordinvites; extern struct discordInfo_s { UINT8 maxPlayers; @@ -65,16 +64,6 @@ void DRPC_RemoveRequest(discordRequest_t *removeRequest); void DRPC_Init(void); -/*-------------------------------------------------- - void DRPC_SendDiscordInfo(void); - - Sends the server's information needed for - the rich presence state. ---------------------------------------------------*/ - -void DRPC_SendDiscordInfo(void); - - /*-------------------------------------------------- void DRPC_UpdatePresence(void); From 40b0a2479ed0ff677be281ecb1be273dc056b91a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 23:26:00 -0400 Subject: [PATCH 139/193] Wrong file --- src/d_clisrv.c | 4 ++++ src/d_clisrv.h | 2 ++ src/d_netcmd.c | 4 ---- src/d_netcmd.h | 2 -- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index dcd85156c..704460d8f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3564,6 +3564,10 @@ consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOf static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_CALL, maxplayers_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL}; +// Here for dedicated servers +static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; +consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, Joinable_Onchange, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index ef988ac8f..d750fb6cf 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -559,6 +559,8 @@ extern consvar_t #endif cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed; +extern consvar_t cv_discordinvites; + // Used in d_net, the only dependence tic_t ExpandTics(INT32 low, tic_t basetic); void D_ClientServerInit(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8959486e3..ea265e43a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -467,10 +467,6 @@ consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, consvar_t cv_sleep = {"cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL}; -// Here for dedicated servers -static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; -consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, DRPC_SendDiscordInfo, 0, NULL, NULL, 0, 0, NULL}; - INT16 gametype = GT_RACE; // SRB2kart boolean forceresetplayers = false; boolean deferencoremode = false; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 2c0ed142c..1e1588083 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -151,8 +151,6 @@ extern consvar_t cv_skipmapcheck; extern consvar_t cv_sleep; -extern consvar_t cv_discordinvites; - typedef enum { XD_NAMEANDCOLOR = 1, From 1a0e134d10f5a28ef242e8e989543c371ac3ea9c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 23:31:54 -0400 Subject: [PATCH 140/193] Bruh, lowercase --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 704460d8f..dd0c0e507 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3566,7 +3566,7 @@ consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_CALL, maxplayers_cons_t // Here for dedicated servers static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}}; -consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, Joinable_Onchange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; From f11481fbe2e0b4084e9a3458d5e15f6a399e35f7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 23:35:41 -0400 Subject: [PATCH 141/193] Move to D_RegisterServerCommands --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ea265e43a..e524fb2c8 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -714,6 +714,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_dummyconsvar); + CV_RegisterVar(&cv_discordinvites); RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo); } @@ -1013,7 +1014,6 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_discordstreamer); CV_RegisterVar(&cv_discordasks); #endif - CV_RegisterVar(&cv_discordinvites); } /** Checks if a name (as received from another player) is okay. From 2a93c2260d4fd7cc29fd2615e0aeab2c92e4cf7f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 30 Aug 2020 23:41:27 -0400 Subject: [PATCH 142/193] Expand IP_SIZE --- src/discord.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord.c b/src/discord.c index eaf595c4d..d28a6771a 100644 --- a/src/discord.c +++ b/src/discord.c @@ -35,7 +35,7 @@ #define DISCORD_APPID "503531144395096085" // length of IP strings -#define IP_SIZE 16 +#define IP_SIZE 21 consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_discordstreamer = {"discordstreamer", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; From 528ed46661516a1976e1bb9b5af2761bc38e0df8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 31 Aug 2020 00:06:47 -0400 Subject: [PATCH 143/193] Remove default dirs on Windows Cause more issues than fixes. Still around for Linux. --- src/sdl/i_system.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index cd2d92964..4cdac36b9 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -147,11 +147,6 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #define DEFAULTSEARCHPATH1 "/usr/local/games" #define DEFAULTSEARCHPATH2 "/usr/games" #define DEFAULTSEARCHPATH3 "/usr/local" -#elif defined (_WIN32) -#define DEFAULTWADLOCATION1 "c:\\games\\srb2kart" -#define DEFAULTWADLOCATION2 "\\games\\srb2kart" -#define DEFAULTSEARCHPATH1 "c:\\games" -#define DEFAULTSEARCHPATH2 "\\games" #endif /** \brief WAD file to look for From 7561c6926675cffbcdba5d253b572f885f2d7d2a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 31 Aug 2020 00:25:28 -0400 Subject: [PATCH 144/193] Increase cipher length --- src/discord.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord.c b/src/discord.c index d28a6771a..4ee5d0a32 100644 --- a/src/discord.c +++ b/src/discord.c @@ -65,7 +65,7 @@ static char self_ip[IP_SIZE]; --------------------------------------------------*/ static char *DRPC_XORIPString(const char *input) { - const UINT8 xor[IP_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + const UINT8 xor[IP_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; char *output = malloc(sizeof(char) * (IP_SIZE+1)); UINT8 i; From 9ad0a0660cc5c4636ae3f97c8f4ca6eea0d80786 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 01:23:56 -0700 Subject: [PATCH 145/193] Just SetCurrentDirectory to the exe's folder on windoze --- src/d_main.c | 12 +--------- src/i_system.h | 9 ------- src/sdl/i_main.c | 18 ++++++++++++++ src/sdl/i_system.c | 60 ---------------------------------------------- 4 files changed, 19 insertions(+), 80 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 4b7db8079..b241acf37 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -895,17 +895,7 @@ static void IdentifyVersion(void) } // Load the IWAD - if (AddIWAD()) - { - I_SaveCurrentWadDirectory(); - } - else - { - if (!( I_UseSavedWadDirectory() && AddIWAD() )) - { - I_Error("SRB2.SRB not found! Expected in %s\n", srb2waddir); - } - } + AddIWAD(); // will be overwritten in case of -cdrom or unix/win home snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir); diff --git a/src/i_system.h b/src/i_system.h index 2f4b1b5c8..3e589c69a 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -318,15 +318,6 @@ const CPUInfoFlags *I_CPUInfo(void); */ const char *I_LocateWad(void); -/** \brief Save current wad directory to appdata -*/ -void I_SaveCurrentWadDirectory(void); - -/** \brief Change directory to last known directory saved in appdata - \return whether the directory could be saved -*/ -boolean I_UseSavedWadDirectory(void); - /** \brief First Joystick's events */ void I_GetJoystickEvents(void); diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c index 41874e9f7..a2ce653ec 100644 --- a/src/sdl/i_main.c +++ b/src/sdl/i_main.c @@ -97,6 +97,20 @@ static inline VOID MakeCodeWritable(VOID) #endif +#ifdef _WIN32 +static void +ChDirToExe (void) +{ + CHAR path[MAX_PATH]; + if (GetModuleFileNameA(NULL, path, MAX_PATH) > 0) + { + strrchr(path, '\\')[0] = '\0'; + SetCurrentDirectoryA(path); + } +} +#endif + + /** \brief The main function \param argc number of arg @@ -126,6 +140,10 @@ int main(int argc, char **argv) #endif #endif +#ifdef _WIN32 + ChDirToExe(); +#endif + logdir = D_Home(); #ifdef LOGMESSAGES diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 4cdac36b9..51f708d0f 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -34,7 +34,6 @@ #ifdef _WIN32 #define RPC_NO_WINDOWS_H #include -#include #include "../doomtype.h" typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD); @@ -3766,65 +3765,6 @@ static const char *locateWad(void) return NULL; } -#ifdef _WIN32 -static FILE * openAppDataFile(const char *filename, const char *mode) -{ - FILE * file = NULL; - char kdir[MAX_PATH]; - - if (SHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, - NULL, 0, "SRB2Kart", kdir) == S_OK) - { - strcat(kdir, "\\"); - strcat(kdir, filename); - file = fopen(kdir, mode); - } - - return file; -} -#endif - -void I_SaveCurrentWadDirectory(void) -{ -#ifdef _WIN32 - char path[MAX_PATH]; - FILE * file = openAppDataFile("lastwaddir", "w"); - if (file != NULL) - { - if (strcmp(srb2path, ".") == 0) - { - GetCurrentDirectoryA(sizeof path, path); - fputs(path, file); - } - else - { - fputs(srb2path, file); - } - fclose(file); - } -#endif -} - -boolean I_UseSavedWadDirectory(void) -{ - boolean ok = false; -#ifdef _WIN32 - FILE * file = openAppDataFile("lastwaddir", "r"); - if (file != NULL) - { - if (fgets(srb2path, sizeof srb2path, file) != NULL) - { - I_OutputMsg( - "Going to the last known directory with srb2.srb: %s\n", - srb2path); - ok = SetCurrentDirectoryA(srb2path); - } - fclose(file); - } -#endif - return ok; -} - const char *I_LocateWad(void) { const char *waddir; From 33c11f1b0b9e4ff73dfca5b411f7cad647c3145b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 19:29:09 -0700 Subject: [PATCH 146/193] Save chosen renderer to file, use on startup if no -software or -opengl parameter --- src/sdl/i_video.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index cbd1e96c6..37eedf147 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -20,6 +20,7 @@ /// \brief SRB2 graphics stuff for SDL #include +#include #include @@ -96,7 +97,7 @@ static INT32 numVidModes = -1; */ static char vidModeName[33][32]; // allow 33 different modes -rendermode_t rendermode=render_soft; +rendermode_t rendermode = render_none; boolean highcolor = false; @@ -1799,6 +1800,13 @@ static void Impl_VideoSetupBuffer(void) } } +static FILE * +OpenRendererFile (const char * mode) +{ + char * path = va(pandf,srb2home,"renderer.txt"); + return fopen(path, mode); +} + void I_StartupGraphics(void) { if (dedicated) @@ -1846,6 +1854,62 @@ void I_StartupGraphics(void) rendermode = render_opengl; #endif + if (rendermode == render_none) + { +#ifdef HWRENDER + char line[16]; + char * word; + FILE * file = OpenRendererFile("r"); + if (file != NULL) + { + if (fgets(line, sizeof line, file) != NULL) + { + word = strtok(line, "\n"); + + if (strcasecmp(word, "software") == 0) + { + rendermode = render_soft; + } + else if (strcasecmp(word, "opengl") == 0) + { + rendermode = render_opengl; + } + + if (rendermode != render_none) + { + CONS_Printf("Using last known renderer: %s\n", line); + } + } + fclose(file); + } +#endif + if (rendermode == render_none) + { + rendermode = render_soft; + CONS_Printf("Using default software renderer.\n"); + } + } + else + { + FILE * file = OpenRendererFile("w"); + if (file != NULL) + { + if (rendermode == render_soft) + { + fputs("software\n", file); + } + else if (rendermode == render_opengl) + { + fputs("opengl\n", file); + } + fclose(file); + } + else + { + CONS_Printf("Could not save renderer to file: %s\n", strerror(errno)); + } + } + usesdl2soft = M_CheckParm("-softblit"); borderlesswindow = M_CheckParm("-borderless"); From 6466291ac607d4cc6668d81780ac695e2452725f Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 19:34:43 -0700 Subject: [PATCH 147/193] Oops :v --- src/d_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index b241acf37..6c209e329 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -895,7 +895,10 @@ static void IdentifyVersion(void) } // Load the IWAD - AddIWAD(); + if (! AddIWAD()) + { + I_Error("SRB2.SRB not found! Expected in %s\n", srb2waddir); + } // will be overwritten in case of -cdrom or unix/win home snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir); From 2948885660c88f103dce4995bf4470264cfaffd9 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 20:08:22 -0700 Subject: [PATCH 148/193] Don't let players change their name more than five times per minute --- src/d_clisrv.c | 4 ++++ src/d_clisrv.h | 4 ++++ src/d_netcmd.c | 37 +++++++++++++++++++++++++++++++------ src/g_game.c | 7 +++++++ src/g_game.h | 1 + 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index dd0c0e507..8b3e1abca 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3024,6 +3024,8 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason) // Reset the name sprintf(player_names[playernum], "Player %d", playernum+1); + player_name_changes[playernum] = 0; + if (IsPlayerAdmin(playernum)) { RemoveAdminPlayer(playernum); // don't stay admin after you're gone @@ -3692,6 +3694,8 @@ void SV_ResetServer(void) adminplayers[i] = -1; // Populate the entire adminplayers array with -1. } + memset(player_name_changes, 0, sizeof player_name_changes); + mynode = 0; cl_packetmissed = false; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index d750fb6cf..4bb795c9d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -536,6 +536,10 @@ typedef enum } kickreason_t; +/* the max number of name changes in some time period */ +#define MAXNAMECHANGES (5) +#define NAMECHANGERATE (60*TICRATE) + extern boolean server; extern boolean serverrunning; #define client (!server) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e524fb2c8..4a90de333 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1218,6 +1218,8 @@ static void SetPlayerName(INT32 playernum, char *newname) if (netgame) HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false); + player_name_changes[playernum]++; + strcpy(player_names[playernum], newname); demo_extradata[playernum] |= DXD_NAME; } @@ -1399,7 +1401,12 @@ static void SendNameAndColor(void) snacpending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) + if (player_name_changes[consoleplayer] >= MAXNAMECHANGES) + { + CV_StealthSet(&cv_playername, player_names[consoleplayer]); + HU_AddChatText("\x85*You must wait to change your name again", false); + } + else if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) CV_StealthSet(&cv_playername, player_names[consoleplayer]); else // Cleanup name if changing it CleanupPlayerName(consoleplayer, cv_playername.zstring); @@ -1523,7 +1530,12 @@ static void SendNameAndColor2(void) snac2pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[1]))) + if (player_name_changes[displayplayers[1]] >= MAXNAMECHANGES) + { + CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]); + HU_AddChatText("\x85*You must wait to change your name again", false); + } + else if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[1]))) CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]); else // Cleanup name if changing it CleanupPlayerName(displayplayers[1], cv_playername2.zstring); @@ -1638,7 +1650,12 @@ static void SendNameAndColor3(void) snac3pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[2]))) + if (player_name_changes[displayplayers[2]] >= MAXNAMECHANGES) + { + CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]); + HU_AddChatText("\x85*You must wait to change your name again", false); + } + else if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[2]))) CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]); else // Cleanup name if changing it CleanupPlayerName(displayplayers[2], cv_playername3.zstring); @@ -1761,7 +1778,12 @@ static void SendNameAndColor4(void) snac4pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[3]))) + if (player_name_changes[displayplayers[3]] >= MAXNAMECHANGES) + { + CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]); + HU_AddChatText("\x85*You must wait to change your name again", false); + } + else if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[3]))) CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]); else // Cleanup name if changing it CleanupPlayerName(displayplayers[3], cv_playername4.zstring); @@ -1816,8 +1838,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) skin = READUINT8(*cp); // set name - if (strcasecmp(player_names[playernum], name) != 0) - SetPlayerName(playernum, name); + if (player_name_changes[playernum] < MAXNAMECHANGES) + { + if (strcasecmp(player_names[playernum], name) != 0) + SetPlayerName(playernum, name); + } // set color p->skincolor = color % MAXSKINCOLORS; diff --git a/src/g_game.c b/src/g_game.c index 43a270b77..86a949fd4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -548,6 +548,8 @@ char player_names[MAXPLAYERS][MAXPLAYERNAME+1] = "Player 16" }; // SRB2kart - removed Players 17 through 32 +int player_name_changes[MAXPLAYERS]; + INT16 rw_maximums[NUM_WEAPONS] = { 800, // MAX_INFINITY @@ -2502,6 +2504,11 @@ void G_Ticker(boolean run) spectatedelay3--; if (spectatedelay4) spectatedelay4--; + + if (gametic % NAMECHANGERATE == 0) + { + memset(player_name_changes, 0, sizeof player_name_changes); + } } } diff --git a/src/g_game.h b/src/g_game.h index 0edc13c74..293041e3a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -27,6 +27,7 @@ extern char customversionstring[32]; extern player_t *seenplayer; #endif extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern int player_name_changes[MAXPLAYERS]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; From f76ae44daa2a78fbb13809accbefb134351e85b7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 2 Sep 2020 02:53:06 -0400 Subject: [PATCH 149/193] Redo Battle item balance - Far more inclined to give you Orbinaut, Jawz, Invincibility, and Grow. Far less inclined to give you Bananas or Sneakers. - Item odds no longer scale with bumper count differences. --- src/k_kart.c | 76 +++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5cef91db1..e6591a267 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -668,29 +668,29 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS][10] = static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = { - //P-Odds 0 1 2 3 4 5 - /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker - /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker - /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility - /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana - /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut - /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz - /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine - /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog - /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb - /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink - /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro - /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring - /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 - /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 - /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 - /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3 - /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4 - /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 + //P-Odds 0 1 + /*Sneaker*/ { 2, 1 }, // Sneaker + /*Rocket Sneaker*/ { 0, 0 }, // Rocket Sneaker + /*Invincibility*/ { 2, 1 }, // Invincibility + /*Banana*/ { 1, 0 }, // Banana + /*Eggman Monitor*/ { 1, 0 }, // Eggman Monitor + /*Orbinaut*/ { 8, 0 }, // Orbinaut + /*Jawz*/ { 8, 1 }, // Jawz + /*Mine*/ { 4, 1 }, // Mine + /*Ballhog*/ { 2, 1 }, // Ballhog + /*Self-Propelled Bomb*/ { 0, 0 }, // Self-Propelled Bomb + /*Grow*/ { 2, 1 }, // Grow + /*Shrink*/ { 0, 0 }, // Shrink + /*Thunder Shield*/ { 0, 0 }, // Thunder Shield + /*Hyudoro*/ { 2, 0 }, // Hyudoro + /*Pogo Spring*/ { 2, 0 }, // Pogo Spring + /*Kitchen Sink*/ { 0, 0 }, // Kitchen Sink + /*Sneaker x3*/ { 0, 1 }, // Sneaker x3 + /*Banana x3*/ { 1, 0 }, // Banana x3 + /*Banana x10*/ { 0, 1 }, // Banana x10 + /*Orbinaut x3*/ { 2, 0 }, // Orbinaut x3 + /*Orbinaut x4*/ { 1, 1 }, // Orbinaut x4 + /*Jawz x2*/ { 2, 1 } // Jawz x2 }; /** \brief Item Roulette for Kart @@ -928,7 +928,7 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 INT32 j; boolean available = false; - if (G_BattleGametype() && i > 5) + if (G_BattleGametype() && i > 1) { oddsvalid[i] = false; break; @@ -962,24 +962,20 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 if (G_BattleGametype()) // Battle Mode { - if (oddsvalid[0]) SETUPDISTTABLE(0,1); - if (oddsvalid[1]) SETUPDISTTABLE(1,1); - if (oddsvalid[2]) SETUPDISTTABLE(2,1); - if (oddsvalid[3]) SETUPDISTTABLE(3,1); - if (oddsvalid[4]) SETUPDISTTABLE(4,1); - - if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items - useodds = 5; + if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[1] == true) + { + // 1 is the extreme odds of player-controlled "Karma" items + useodds = 1; + } else { - SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc - if (K_IsPlayerWanted(player)) - wantedpos++; - if (wantedpos > 4) // Don't run off into karma items - wantedpos = 4; - if (wantedpos < 0) // Don't go below somehow - wantedpos = 0; - useodds = disttable[(wantedpos * distlen) / 5]; + useodds = 0; + + if (oddsvalid[0] == false && oddsvalid[1] == true) + { + // try to use karma odds as a fallback + useodds = 1; + } } } else From 4ce347c6f219481dccf03cb7e1da142c0382027f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 8 Sep 2020 22:40:42 -0700 Subject: [PATCH 150/193] Apply volume to sounds with origin too Previously sounds with an origin would always start at max volume. This is because the distance calculation adjusts the volume, and that volume needs to be updated every tic as distance changes. Storing the original volume works. --- src/s_sound.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 2c96d389d..225863211 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -162,6 +162,9 @@ typedef struct // origin of sound const void *origin; + // initial volume of sound, which is applied after distance and direction + INT32 volume; + // handle of the sound being played INT32 handle; @@ -432,6 +435,7 @@ void S_StopSoundByNum(sfxenum_t sfxnum) void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) { + const INT32 initial_volume = volume; INT32 sep, pitch, priority, cnum; sfxinfo_t *sfx; const boolean reverse = (stereoreverse.value ^ encoremode); @@ -789,6 +793,7 @@ dontplay4: // Assigns the handle to one of the channels in the // mix/output buffer. + channels[cnum].volume = initial_volume; channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum); } @@ -1054,7 +1059,7 @@ void S_UpdateSounds(void) if (I_SoundIsPlaying(c->handle)) { // initialize parameters - volume = 255; // 8 bits internal volume precision + volume = c->volume; // 8 bits internal volume precision pitch = NORM_PITCH; sep = NORM_SEP; @@ -1361,15 +1366,12 @@ INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *v } // volume calculation - if (approx_dist < S_CLOSE_DIST) - { - // SfxVolume is now hardware volume - *vol = 255; // not snd_SfxVolume - } - else + /* not sure if it should be > (no =), but this matches the old behavior */ + if (approx_dist >= S_CLOSE_DIST) { // distance effect - *vol = (15 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; + INT32 n = (15 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)); + *vol = FixedMul(*vol * FRACUNIT / 255, n) / S_ATTENUATOR; } if (splitscreen) From 7b1f97439783d3e8c91d9f48db5e70feb7736c40 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 05:56:40 -0700 Subject: [PATCH 151/193] Basically, update master server code to v2 Removes rooms, replaced with cv_advertise and "Advertise" on the host menu. According to the new API, SRB2APPLICATION is sent instead of MODID. A contact field was added, but there is no means of accessing it or setting it. As a slight change, the server list will be populated even on an outdated version of the game. (The new API was designed with this in mind.) The update alert is still presented first of course. --- src/d_clisrv.c | 109 ++-------- src/d_clisrv.h | 2 +- src/d_main.c | 12 -- src/discord.c | 15 +- src/http-mserv.c | 252 +++++------------------ src/m_menu.c | 516 ++++++++++++++++++----------------------------- src/m_menu.h | 4 - src/mserv.c | 35 +--- src/mserv.h | 28 +-- 9 files changed, 277 insertions(+), 696 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index dd0c0e507..8a2613a31 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1973,57 +1973,30 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) M_SortServerList(); } -#if defined (MASTERSERVER) && defined (HAVE_THREADS) -struct Fetch_servers_ctx +void CL_UpdateServerList (void) { - int room; - int id; -}; + SL_ClearServerList(0); -static void -Fetch_servers_thread (struct Fetch_servers_ctx *ctx) -{ - msg_server_t *server_list; - - server_list = GetShortServersList(ctx->room, ctx->id); - - if (server_list) + if (!netgame && I_NetOpenSocket) { - I_lock_mutex(&ms_QueryId_mutex); + if (I_NetOpenSocket()) { - if (ctx->id != ms_QueryId) - { - free(server_list); - server_list = NULL; - } - } - I_unlock_mutex(ms_QueryId_mutex); - - if (server_list) - { - I_lock_mutex(&m_menu_mutex); - { - if (m_waiting_mode == M_WAITING_SERVERS) - m_waiting_mode = M_NOT_WAITING; - } - I_unlock_mutex(m_menu_mutex); - - I_lock_mutex(&ms_ServerList_mutex); - { - ms_ServerList = server_list; - } - I_unlock_mutex(ms_ServerList_mutex); + netgame = true; + multiplayer = true; } } - free(ctx); + // search for local servers + if (netgame) + SendAskInfo(BROADCASTADDR); } -#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ void CL_QueryServerList (msg_server_t *server_list) { INT32 i; + CL_UpdateServerList(); + for (i = 0; server_list[i].header.buffer[0]; i++) { // Make sure MS version matches our own, to @@ -2052,62 +2025,6 @@ void CL_QueryServerList (msg_server_t *server_list) } } } - -void CL_UpdateServerList(boolean internetsearch, INT32 room) -{ - (void)internetsearch; - (void)room; - - SL_ClearServerList(0); - - if (!netgame && I_NetOpenSocket) - { - if (I_NetOpenSocket()) - { - netgame = true; - multiplayer = true; - } - } - - // search for local servers - if (netgame) - SendAskInfo(BROADCASTADDR); - -#ifdef MASTERSERVER - if (internetsearch) - { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; - - ctx = malloc(sizeof *ctx); - - /* This called from M_Refresh so I don't use a mutex */ - m_waiting_mode = M_WAITING_SERVERS; - - I_lock_mutex(&ms_QueryId_mutex); - { - ctx->id = ms_QueryId; - } - I_unlock_mutex(ms_QueryId_mutex); - - ctx->room = room; - - I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx); -#else - msg_server_t *server_list; - - server_list = GetShortServersList(room, 0); - - if (server_list) - { - CL_QueryServerList(server_list); - free(server_list); - } -#endif - } -#endif/*MASTERSERVER*/ -} - #endif // ifndef NONET static void M_ConfirmConnect(event_t *ev) @@ -3755,7 +3672,7 @@ void D_QuitNetGame(void) if (nodeingame[i]) HSendPacket(i, true, 0, 0); #ifdef MASTERSERVER - if (serverrunning && ms_RoomId > 0) + if (serverrunning && cv_advertise.value) UnregisterServer(); #endif } @@ -4017,7 +3934,7 @@ boolean SV_SpawnServer(void) { I_NetOpenSocket(); #ifdef MASTERSERVER - if (ms_RoomId > 0) + if (cv_advertise.value) RegisterServer(); #endif } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index d750fb6cf..c2ccf6f92 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -587,7 +587,7 @@ void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_RemovePlayer(INT32 playernum, INT32 reason); void CL_QueryServerList(msg_server_t *list); -void CL_UpdateServerList(boolean internetsearch, INT32 room); +void CL_UpdateServerList(void); // Is there a game running boolean Playing(void); diff --git a/src/d_main.c b/src/d_main.c index 6c209e329..0404dc446 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -69,7 +69,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "m_cheat.h" #include "y_inter.h" #include "p_local.h" // chasecam -#include "mserv.h" // ms_RoomId #include "m_misc.h" // screenshot functionality #include "dehacked.h" // Dehacked list test #include "m_cond.h" // condition initialization @@ -1424,17 +1423,6 @@ void D_SRB2Main(void) CONS_Printf("ST_Init(): Init status bar.\n"); ST_Init(); - if (M_CheckParm("-room")) - { - if (!M_IsNextParm()) - I_Error("usage: -room \nCheck the Master Server's webpage for room ID numbers.\n"); - ms_RoomId = atoi(M_GetNextParm()); - -#ifdef UPDATE_ALERT - GetMODVersion_Console(); -#endif - } - // Set up splitscreen players before joining! if (!dedicated && (M_CheckParm("-splitscreen") && M_IsNextParm())) { diff --git a/src/discord.c b/src/discord.c index 4ee5d0a32..99cc0a3bd 100644 --- a/src/discord.c +++ b/src/discord.c @@ -24,7 +24,7 @@ #include "p_tick.h" #include "m_menu.h" // gametype_cons_t #include "r_things.h" // skins -#include "mserv.h" // ms_RoomId +#include "mserv.h" // cv_advertise #include "z_zone.h" #include "byteptr.h" @@ -505,14 +505,13 @@ void DRPC_UpdatePresence(void) // Server info if (netgame) { - switch (ms_RoomId) + if (cv_advertise.value) { - case -1: discordPresence.state = "Private"; break; // Private server - case 33: discordPresence.state = "Standard"; break; - case 28: discordPresence.state = "Casual"; break; - case 38: discordPresence.state = "Custom Gametypes"; break; - case 31: discordPresence.state = "OLDC"; break; - default: discordPresence.state = "Unknown Room"; break; // HOW + discordPresence.state = "Public"; + } + else + { + discordPresence.state = "Private"; } discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! diff --git a/src/http-mserv.c b/src/http-mserv.c index 13c7b43d3..eb22a414c 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -11,7 +11,7 @@ /* Documentation available here. - + */ #ifdef HAVE_CURL @@ -141,7 +141,7 @@ HMS_connect (const char *format, ...) if (cv_masterserver_token.string[0]) { quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0); - token_length = ( sizeof "?token="-1 )+ strlen(quack_token); + token_length = ( sizeof "&token="-1 )+ strlen(quack_token); } else { @@ -156,7 +156,9 @@ HMS_connect (const char *format, ...) seek = strlen(hms_api) + 1;/* + '/' */ va_start (ap, format); - url = malloc(seek + vsnprintf(0, 0, format, ap) + token_length + 1); + url = malloc(seek + vsnprintf(0, 0, format, ap) + + sizeof "?v=2" - 1 + + token_length + 1); va_end (ap); sprintf(url, "%s/", hms_api); @@ -169,6 +171,9 @@ HMS_connect (const char *format, ...) seek += vsprintf(&url[seek], format, ap); va_end (ap); + strcpy(&url[seek], "?v=2"); + seek += sizeof "?v=2" - 1; + if (quack_token) sprintf(&url[seek], "?token=%s", quack_token); @@ -258,117 +263,6 @@ HMS_end (struct HMS_buffer *buffer) free(buffer); } -int -HMS_fetch_rooms (int joining, int query_id) -{ - struct HMS_buffer *hms; - int ok; - - int doing_shit; - - char *id; - char *title; - char *room_motd; - - int id_no; - - char *p; - char *end; - - int i; - - (void)query_id; - - hms = HMS_connect("rooms"); - - if (! hms) - return 0; - - if (HMS_do(hms)) - { - doing_shit = 1; - - p = hms->buffer; - - for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );) - { - *end = '\0'; - - id = strtok(p, "\n"); - title = strtok(0, "\n"); - room_motd = strtok(0, ""); - - if (id && title && room_motd) - { - id_no = atoi(id); - - /* - Don't show the 'All' room if hosting. And it's a hack like this - because I'm way too lazy to add another feature to the MS. - */ - if (joining || id_no != 0) - { -#ifdef HAVE_THREADS - I_lock_mutex(&ms_QueryId_mutex); - { - if (query_id != ms_QueryId) - doing_shit = 0; - } - I_unlock_mutex(ms_QueryId_mutex); - - if (! doing_shit) - break; -#endif - - room_list[i].header.buffer[0] = 1; - - room_list[i].id = id_no; - strlcpy(room_list[i].name, title, sizeof room_list[i].name); - strlcpy(room_list[i].motd, room_motd, sizeof room_list[i].motd); - - i++; - } - - p = ( end + 3 );/* skip the three linefeeds */ - } - else - break; - } - - if (doing_shit) - room_list[i].header.buffer[0] = 0; - - ok = 1; - - if (doing_shit) - { -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif - { - for (i = 0; room_list[i].header.buffer[0]; i++) - { - if(*room_list[i].name != '\0') - { - MP_RoomMenu[i+1].text = room_list[i].name; - roomIds[i] = room_list[i].id; - MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; - } - } - } -#ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); -#endif - } - } - else - ok = 0; - - HMS_end(hms); - - return ok; -} - int HMS_register (void) { @@ -377,29 +271,26 @@ HMS_register (void) char post[256]; - char *title; + char *contact; - hms = HMS_connect("rooms/%d/register", ms_RoomId); + hms = HMS_connect( + "games/%s/%d/servers/register", SRB2APPLICATION, MODVERSION); if (! hms) return 0; - title = curl_easy_escape(hms->curl, cv_servername.string, 0); + contact = curl_easy_escape(hms->curl, cv_server_contact.string, 0); snprintf(post, sizeof post, "port=%d&" - "title=%s&" - "version=%d.%d", + "contact=%s", current_port, - title, - - VERSION, - SUBVERSION + contact ); - curl_free(title); + curl_free(contact); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); @@ -473,19 +364,13 @@ HMS_list_servers (void) { struct HMS_buffer *hms; - char *p; - - hms = HMS_connect("servers"); + hms = HMS_connect("games/%s/%d/servers", SRB2APPLICATION, MODVERSION); if (! hms) return; if (HMS_do(hms)) { - p = &hms->buffer[strlen(hms->buffer)]; - while (*--p == '\n') - ; - CONS_Printf("%s\n", hms->buffer); } @@ -493,35 +378,24 @@ HMS_list_servers (void) } msg_server_t * -HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) +HMS_fetch_servers (msg_server_t *list, int query_id) { struct HMS_buffer *hms; int doing_shit; - char local_version[9]; - - char *room; - char *address; char *port; - char *title; - char *version; + char *contact; char *end; - char *section_end; char *p; int i; (void)query_id; - if (room_number > 0) - { - hms = HMS_connect("rooms/%d/servers", room_number); - } - else - hms = HMS_connect("servers"); + hms = HMS_connect("games/%s/%d/servers", SRB2APPLICATION, MODVERSION); if (! hms) return NULL; @@ -530,81 +404,51 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) { doing_shit = 1; - snprintf(local_version, sizeof local_version, - "%d.%d", - VERSION, - SUBVERSION - ); - p = hms->buffer; i = 0; - do + while (i < MAXSERVERLIST && ( end = strchr(p, '\n') )) { - section_end = strstr(p, "\n\n"); + *end = '\0'; - room = strtok(p, "\n"); + address = strtok(p, " "); + port = strtok(0, " "); + contact = strtok(0, ""); - p = strtok(0, ""); - - if (! p) - break; - - while (i < MAXSERVERLIST && ( end = strchr(p, '\n') )) + if (address && port) { - *end = '\0'; - - address = strtok(p, " "); - port = strtok(0, " "); - title = strtok(0, " "); - version = strtok(0, ""); - - if (address && port && title && version) - { #ifdef HAVE_THREADS - I_lock_mutex(&ms_QueryId_mutex); - { - if (query_id != ms_QueryId) - doing_shit = 0; - } - I_unlock_mutex(ms_QueryId_mutex); + I_lock_mutex(&ms_QueryId_mutex); + { + if (query_id != ms_QueryId) + doing_shit = 0; + } + I_unlock_mutex(ms_QueryId_mutex); - if (! doing_shit) - break; + if (! doing_shit) + break; #endif - if (strcmp(version, local_version) == 0) - { - strlcpy(list[i].ip, address, sizeof list[i].ip); - strlcpy(list[i].port, port, sizeof list[i].port); - strlcpy(list[i].name, title, sizeof list[i].name); - strlcpy(list[i].version, version, sizeof list[i].version); + strlcpy(list[i].ip, address, sizeof list[i].ip); + strlcpy(list[i].port, port, sizeof list[i].port); - list[i].room = atoi(room); - - list[i].header.buffer[0] = 1; - - i++; - } - - if (end == section_end)/* end of list for this room */ - break; - else - p = ( end + 1 );/* skip server delimiter */ - } - else + if (contact) { - section_end = 0;/* malformed so quit the parsing */ - break; + strlcpy(list[i].contact, contact, sizeof list[i].contact); } + + list[i].header.buffer[0] = 1; + + i++; + + p = ( end + 1 );/* skip server delimiter */ } - - if (! doing_shit) + else + { + /* malformed so quit the parsing */ break; - - p = ( section_end + 2 ); + } } - while (section_end) ; if (doing_shit) list[i].header.buffer[0] = 0; @@ -626,7 +470,7 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) char *version; char *version_name; - hms = HMS_connect("versions/%d", MODID); + hms = HMS_connect("games/%s/version", SRB2APPLICATION); if (! hms) return 0; diff --git a/src/m_menu.c b/src/m_menu.c index 2fddb6745..79b8d53da 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -182,7 +182,6 @@ static void M_StopMessage(INT32 choice); #ifndef NONET static void M_HandleServerPage(INT32 choice); -static void M_RoomMenu(INT32 choice); #endif // Prototyping is fun, innit? @@ -190,6 +189,9 @@ static void M_RoomMenu(INT32 choice); // NEEDED FUNCTION PROTOTYPES GO HERE // ========================================================================== +void M_SetWaitingMode(int mode); +int M_GetWaitingMode(void); + // the haxor message menu menu_t MessageDef; @@ -263,7 +265,6 @@ static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); -static void M_ChooseRoom(INT32 choice); #endif static void M_StartOfflineServerMenu(INT32 choice); static void M_StartServer(INT32 choice); @@ -372,7 +373,6 @@ static void M_OGL_DrawColorMenu(void); static void M_DrawMPMainMenu(void); #ifndef NONET static void M_DrawConnectMenu(void); -static void M_DrawRoomMenu(void); #endif static void M_DrawJoystick(void); static void M_DrawSetupMultiPlayerMenu(void); @@ -1017,7 +1017,7 @@ static menuitem_t MP_MainMenu[] = static menuitem_t MP_ServerMenu[] = { {IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 10}, - {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20}, + {IT_STRING|IT_CVAR, NULL, "Advertise", &cv_advertise, 20}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30}, {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68}, @@ -1047,54 +1047,30 @@ static menuitem_t MP_PlayerSetupMenu[] = #ifndef NONET static menuitem_t MP_ConnectMenu[] = { - {IT_STRING | IT_CALL, NULL, "Room...", M_RoomMenu, 4}, - {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 12}, - {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 20}, - {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 28}, + {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 4}, + {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 12}, + {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 20}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156-4}, - {IT_STRING | IT_SPACE, NULL, "", M_Connect, 168-4}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 36}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156}, }; enum { - mp_connect_room, mp_connect_sort, mp_connect_page, mp_connect_refresh, FIRSTSERVERLINE }; - -menuitem_t MP_RoomMenu[] = -{ - {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 27}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 36}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 45}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 54}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 63}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 72}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 81}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 90}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 99}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 108}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 117}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 126}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 135}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 144}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 153}, - {IT_DISABLED, NULL, "", M_ChooseRoom, 162}, -}; #endif // ------------------------------------ @@ -1992,17 +1968,6 @@ menu_t MP_ConnectDef = 0, M_CancelConnect }; -menu_t MP_RoomDef = -{ - "M_MULTI", - sizeof (MP_RoomMenu)/sizeof (menuitem_t), - &MP_ConnectDef, - MP_RoomMenu, - M_DrawRoomMenu, - 27, 32, - 0, - NULL -}; #endif menu_t MP_PlayerSetupDef = { @@ -3392,30 +3357,6 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; -#if defined (MASTERSERVER) && defined (HAVE_THREADS) - if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef) - { - I_lock_mutex(&ms_QueryId_mutex); - { - ms_QueryId++; - } - I_unlock_mutex(ms_QueryId_mutex); - } - - if (currentMenu == &MP_ConnectDef) - { - I_lock_mutex(&ms_ServerList_mutex); - { - if (ms_ServerList) - { - free(ms_ServerList); - ms_ServerList = NULL; - } - } - I_unlock_mutex(ms_ServerList_mutex); - } -#endif/*HAVE_THREADS*/ - if (currentMenu->quitroutine) { // If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH @@ -8406,7 +8347,118 @@ static void M_EndGame(INT32 choice) // Connect Menu //=========================================================================== -#define SERVERHEADERHEIGHT 44 +void +M_SetWaitingMode (int mode) +{ +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + m_waiting_mode = mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif +} + +int +M_GetWaitingMode (void) +{ + int mode; + +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + mode = m_waiting_mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + + return mode; +} + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS +static void +Spawn_masterserver_thread (const char *name, void (*thread)(int*)) +{ + int *id = malloc(sizeof *id); + + I_lock_mutex(&ms_QueryId_mutex); + { + *id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_spawn_thread(name, (I_thread_fn)thread, id); +} + +static int +Same_instance (int id) +{ + int okay; + + I_lock_mutex(&ms_QueryId_mutex); + { + okay = ( id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + + return okay; +} +#endif/*HAVE_THREADS*/ + +static void +Fetch_servers_thread (int *id) +{ + msg_server_t * server_list; + + (void)id; + + M_SetWaitingMode(M_WAITING_SERVERS); + +#ifdef HAVE_THREADS + server_list = GetShortServersList(*id); +#else + server_list = GetShortServersList(0); +#endif + + if (server_list) + { +#ifdef HAVE_THREADS + if (Same_instance(*id)) +#endif + { + M_SetWaitingMode(M_NOT_WAITING); + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); +#else + CL_QueryServerList(server_list); + free(server_list); +#endif + } +#ifdef HAVE_THREADS + else + { + free(server_list); + } +#endif + } + +#ifdef HAVE_THREADS + free(id); +#endif +} +#endif/*MASTERSERVER*/ + +#define SERVERHEADERHEIGHT 36 #define SERVERLINEHEIGHT 12 #define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT) @@ -8478,77 +8530,18 @@ static void M_Refresh(INT32 choice) if (rendermode == render_soft) I_FinishUpdate(); // page flip or blit buffer - // note: this is the one case where 0 is a valid room number - // because it corresponds to "All" - CL_UpdateServerList(!(ms_RoomId < 0), ms_RoomId); - // first page of servers serverlistpage = 0; -} -static INT32 menuRoomIndex = 0; - -static void M_DrawRoomMenu(void) -{ - static int frame = -12; - int dot_frame; - char text[4]; - - const char *rmotd; - const char *waiting_message; - - int dots; - - if (m_waiting_mode) - { - dot_frame = frame / 4; - dots = dot_frame + 3; - - strcpy(text, " "); - - if (dots > 0) - { - if (dot_frame < 0) - dot_frame = 0; - - strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame)); - } - - if (++frame == 12) - frame = -12; - - currentMenu->menuitems[0].text = text; - } - - // use generic drawer for cursor, items and title - M_DrawGenericMenu(); - - V_DrawString(currentMenu->x - 16, currentMenu->y, highlightflags, M_GetText("Select a room")); - - if (m_waiting_mode == M_NOT_WAITING) - { - M_DrawTextBox(144, 24, 20, 20); - - if (itemOn == 0) - rmotd = M_GetText("Don't connect to the Master Server."); - else - rmotd = room_list[itemOn-1].motd; - - rmotd = V_WordWrap(0, 20*8, 0, rmotd); - V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); - } - - if (m_waiting_mode) - { - // Display a little "please wait" message. - M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); - if (m_waiting_mode == M_WAITING_VERSION) - waiting_message = "Checking for updates..."; - else - waiting_message = "Fetching room info..."; - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, waiting_message); - V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); - } +#ifdef MASTERSERVER +#ifdef HAVE_THREADS + Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread); +#else/*HAVE_THREADS*/ + Fetch_servers_thread(NULL); +#endif/*HAVE_THREADS*/ +#else/*MASTERSERVER*/ + CL_UpdateServerList(); +#endif/*MASTERSERVER*/ } static void M_DrawConnectMenu(void) @@ -8557,6 +8550,7 @@ static void M_DrawConnectMenu(void) const char *gt = "Unknown"; const char *spd = ""; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; + int waiting; for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) MP_ConnectMenu[i].status = IT_STRING | IT_SPACE; @@ -8564,20 +8558,12 @@ static void M_DrawConnectMenu(void) if (!numPages) numPages = 1; - // Room name - if (ms_RoomId < 0) - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, - highlightflags, (itemOn == mp_connect_room) ? "" : ""); - else - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, - highlightflags, room_list[menuRoomIndex].name); -#undef mp_server_room - } -#endif } static void M_MapChange(INT32 choice) @@ -9164,7 +9041,6 @@ static void M_StartServerMenu(INT32 choice) (void)choice; levellistmode = LLM_CREATESERVER; M_PrepareLevelSelect(); - ms_RoomId = -1; M_SetupNextMenu(&MP_ServerDef); } diff --git a/src/m_menu.h b/src/m_menu.h index 4fc92bd55..28fdd44e6 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -79,7 +79,6 @@ typedef enum M_NOT_WAITING, M_WAITING_VERSION, - M_WAITING_ROOMS, M_WAITING_SERVERS, } M_waiting_mode_t; @@ -175,9 +174,6 @@ typedef struct menuitem_s extern menuitem_t PlayerMenu[MAXSKINS]; -extern menuitem_t MP_RoomMenu[]; -extern UINT32 roomIds[NUM_LIST_ROOMS]; - typedef struct menu_s { const char *menutitlepic; diff --git a/src/mserv.c b/src/mserv.c index 344cbc30a..1e71a0ac3 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -67,10 +67,11 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; -INT16 ms_RoomId = -1; +consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; #if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; @@ -82,10 +83,6 @@ I_mutex ms_ServerList_mutex; UINT16 current_port = 0; -// Room list is an external variable now. -// Avoiding having to get info ten thousand times... -msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test - /** Adds variables and commands relating to the master server. * * \sa cv_masterserver, cv_servername, @@ -99,7 +96,9 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_timeout); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_token); + CV_RegisterVar(&cv_advertise); CV_RegisterVar(&cv_servername); + CV_RegisterVar(&cv_server_contact); #ifdef MASTERSERVER COM_AddCommand("listserv", Command_Listserv_f); #endif @@ -120,14 +119,14 @@ static void WarnGUI (void) } #define NUM_LIST_SERVER MAXSERVERLIST -msg_server_t *GetShortServersList(INT32 room, int id) +msg_server_t *GetShortServersList(int id) { msg_server_t *server_list; // +1 for easy test server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list); - if (HMS_fetch_servers(server_list, room, id)) + if (HMS_fetch_servers(server_list, id)) return server_list; else { @@ -137,17 +136,6 @@ msg_server_t *GetShortServersList(INT32 room, int id) } } -INT32 GetRoomsList(boolean hosting, int id) -{ - if (HMS_fetch_rooms( ! hosting, id)) - return 1; - else - { - WarnGUI(); - return -1; - } -} - #ifdef UPDATE_ALERT char *GetMODVersion(int id) { @@ -181,15 +169,6 @@ char *GetMODVersion(int id) return NULL; } } - -// Console only version of the above (used before game init) -void GetMODVersion_Console(void) -{ - char buffer[16]; - - if (HMS_compare_mod_version(buffer, sizeof buffer) > 0) - I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, buffer); -} #endif #ifndef NONET @@ -460,7 +439,7 @@ void UnregisterServer(void) static boolean Online (void) { - return ( serverrunning && ms_RoomId > 0 ); + return ( serverrunning && cv_advertise.value ); } static inline void SendPingToMasterServer(void) diff --git a/src/mserv.h b/src/mserv.h index 9269c408a..02aaf3675 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -16,9 +16,6 @@ #include "i_threads.h" -// lowered from 32 due to menu changes -#define NUM_LIST_ROOMS 16 - #if defined(_MSC_VER) #pragma pack(1) #endif @@ -35,19 +32,10 @@ typedef struct msg_header_t header; char ip[16]; char port[8]; - char name[32]; - INT32 room; + char contact[32]; char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31) } ATTRPACK msg_server_t; -typedef struct -{ - msg_header_t header; - INT32 id; - char name[32]; - char motd[255]; -} ATTRPACK msg_rooms_t; - typedef struct { msg_header_t header; @@ -65,15 +53,13 @@ typedef struct // ================================ GLOBALS =============================== extern consvar_t cv_masterserver, cv_servername; +extern consvar_t cv_server_contact; extern consvar_t cv_masterserver_update_rate; extern consvar_t cv_masterserver_timeout; extern consvar_t cv_masterserver_debug; extern consvar_t cv_masterserver_token; -// < 0 to not connect (usually -1) (offline mode) -// == 0 to show all rooms, not a valid hosting room -// anything else is whatever room the MS assigns to that number (online mode) -extern INT16 ms_RoomId; +extern consvar_t cv_advertise; #ifdef HAVE_THREADS extern int ms_QueryId; @@ -88,24 +74,20 @@ void UnregisterServer(void); void MasterClient_Ticker(void); -msg_server_t *GetShortServersList(INT32 room, int id); -INT32 GetRoomsList(boolean hosting, int id); +msg_server_t *GetShortServersList(int id); #ifdef UPDATE_ALERT char *GetMODVersion(int id); -void GetMODVersion_Console(void); #endif -extern msg_rooms_t room_list[NUM_LIST_ROOMS+1]; void AddMServCommands(void); /* HTTP */ void HMS_set_api (char *api); -int HMS_fetch_rooms (int joining, int id); int HMS_register (void); int HMS_unlist (void); int HMS_update (void); void HMS_list_servers (void); -msg_server_t * HMS_fetch_servers (msg_server_t *list, int room, int id); +msg_server_t * HMS_fetch_servers (msg_server_t *list, int id); int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); #endif From 3fc0c5259935ca775837a4b586df42d7797be1c5 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 06:49:41 -0700 Subject: [PATCH 152/193] Register/unregister server by changing cv_advertise --- src/mserv.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/mserv.c b/src/mserv.c index 1e71a0ac3..266ed3686 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -59,6 +59,8 @@ static void Update_parameters (void); static void MasterServer_OnChange(void); +static void Advertise_OnChange(void); + static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {2, "MIN"}, {60, "MAX"}, @@ -71,7 +73,7 @@ consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; #if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; @@ -263,6 +265,9 @@ Finish_unlist (void) Lock_state(); { registered = MSRegistered; + + if (MSId == MSRegisteredId) + MSId++; } Unlock_state(); @@ -284,13 +289,6 @@ Finish_unlist (void) #endif } - Lock_state(); - { - if (MSId == MSRegisteredId) - MSId++; - } - Unlock_state(); - #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); #endif @@ -540,3 +538,30 @@ static void MasterServer_OnChange(void) RegisterServer(); #endif/*MASTERSERVER*/ } + +static void +Advertise_OnChange(void) +{ + int different; + + if (cv_advertise.value) + { + if (serverrunning) + { + Lock_state(); + { + different = ( MSId != MSRegisteredId ); + } + Unlock_state(); + + if (different) + { + RegisterServer(); + } + } + } + else + { + UnregisterServer(); + } +} From d3b31d1d7607df461891ac6d245acfbd21a48e95 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 06:50:31 -0700 Subject: [PATCH 153/193] When changing masterserver_update_rate, only update if elapsed time within the new rate --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 266ed3686..a14fdbd5c 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -71,7 +71,7 @@ consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, MasterClient_Ticker, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; From ef4229a42f46225f9601376bc69bf55842e85db4 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 07:08:45 -0700 Subject: [PATCH 154/193] 7b1f97439 lies btw, you can set the server_contact cvar 4head From 4e9d006c37f57359cd4adc241c8d24ae79ae37d6 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 09:00:18 -0700 Subject: [PATCH 155/193] int -> INT32 --- src/g_game.c | 2 +- src/g_game.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 86a949fd4..d9d7b7f12 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -548,7 +548,7 @@ char player_names[MAXPLAYERS][MAXPLAYERNAME+1] = "Player 16" }; // SRB2kart - removed Players 17 through 32 -int player_name_changes[MAXPLAYERS]; +INT32 player_name_changes[MAXPLAYERS]; INT16 rw_maximums[NUM_WEAPONS] = { diff --git a/src/g_game.h b/src/g_game.h index 293041e3a..493be5d49 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -26,8 +26,8 @@ extern char customversionstring[32]; #ifdef SEENAMES extern player_t *seenplayer; #endif -extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; -extern int player_name_changes[MAXPLAYERS]; +extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern INT32 player_name_changes[MAXPLAYERS]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; From 87037dc7ae9603b9cf29fcc022ca9d7cf88b3b21 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 11 Sep 2020 12:14:08 -0400 Subject: [PATCH 156/193] 6 -> 2, void the now unused parameter --- src/k_kart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index e6591a267..e6afebc78 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -666,7 +666,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS][10] = /*Jawz x2*/ { 0, 0, 0, 1, 2, 0, 0, 0, 0, 0 } // Jawz x2 }; -static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = +static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][2] = { //P-Odds 0 1 /*Sneaker*/ { 2, 1 }, // Sneaker @@ -923,6 +923,9 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 UINT8 distlen = 0; boolean oddsvalid[10]; + // Unused now, oops :V + (void)bestbumper; + for (i = 0; i < 10; i++) { INT32 j; From 0146decd8b837b4f633049a439a710c8f2928657 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 17:33:46 -0700 Subject: [PATCH 157/193] Strip trailing slashes from masterserver address --- src/http-mserv.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index eb22a414c..34fc2c7fa 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -499,6 +499,19 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) return ok; } +static char * +Strip_trailing_slashes (char *api) +{ + char * p = &api[strlen(api)]; + + while (*--p == '/') + ; + + p[1] = '\0'; + + return api; +} + void HMS_set_api (char *api) { @@ -507,7 +520,7 @@ HMS_set_api (char *api) #endif { free(hms_api); - hms_api = api; + hms_api = Strip_trailing_slashes(api); } #ifdef HAVE_THREADS I_unlock_mutex(hms_api_mutex); From c0801c44130c0a147f2c9da359fd0b60dbc70a13 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 17:39:01 -0700 Subject: [PATCH 158/193] Set masterserver to ms.kartkrew.org --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index a14fdbd5c..7abdf49d1 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -67,7 +67,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {0, NULL} }; -consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver = {"masterserver", "https://ms.kartkrew.org/ms/api", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; From 99115d9e76d5e6c1d6eb3c6c37ff6eba3f71037f Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 11 Sep 2020 20:12:31 -0700 Subject: [PATCH 159/193] Update update alert message --- src/doomdef.h | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index bff809923..79ab2e13a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -181,32 +181,13 @@ extern char logfilename[1024]; // Please change to apply to your modification (we don't want everyone asking where your mod is on SRB2.org!). #define UPDATE_ALERT_STRING \ "A new update is available for SRB2Kart.\n"\ -"Please visit mb.srb2.org to download it.\n"\ +"Please visit kartkrew.org to download it.\n"\ "\n"\ "You are using version: %s\n"\ "The newest version is: %s\n"\ "\n"\ -"This update is required for online\n"\ -"play using the Master Server.\n"\ -"You will not be able to connect to\n"\ -"the Master Server until you update to\n"\ -"the newest version of the game.\n"\ -"\n"\ "(Press a key)\n" -// The string used in the I_Error alert upon trying to host through command line parameters. -// Generally less filled with newlines, since Windows gives you lots more room to work with. -#define UPDATE_ALERT_STRING_CONSOLE \ -"A new update is available for SRB2Kart.\n"\ -"Please visit mb.srb2.org to download it.\n"\ -"\n"\ -"You are using version: %s\n"\ -"The newest version is: %s\n"\ -"\n"\ -"This update is required for online play using the Master Server.\n"\ -"You will not be able to connect to the Master Server\n"\ -"until you update to the newest version of the game.\n" - // For future use, the codebase is the version of SRB2 that the modification is based on, // and should not be changed unless you have merged changes between versions of SRB2 // (such as 2.0.4 to 2.0.5, etc) into your working copy. From 1003ed52439e719436b6e27d2a921ea77320bbf4 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 12 Sep 2020 10:32:14 -0700 Subject: [PATCH 160/193] Fix master server token --- src/http-mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index 34fc2c7fa..c149eb9b2 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -175,7 +175,7 @@ HMS_connect (const char *format, ...) seek += sizeof "?v=2" - 1; if (quack_token) - sprintf(&url[seek], "?token=%s", quack_token); + sprintf(&url[seek], "&token=%s", quack_token); CONS_Printf("HMS: connecting '%s'...\n", url); From d437bac34fb17e3fd9fcf94cd1ec9ff425f89cee Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 12 Sep 2020 18:36:25 -0700 Subject: [PATCH 161/193] Make cv_advertise NETVAR --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 7abdf49d1..9233c7f06 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -73,7 +73,7 @@ consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, MasterClient_Ticker, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; #if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; From 8bbd6e1b19b8aa37cf1c5d3592d848efd64fc8a1 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 12 Sep 2020 18:54:44 -0700 Subject: [PATCH 162/193] Make cv_advertise no by default, do not save --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 9233c7f06..7adec7fff 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -73,7 +73,7 @@ consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, MasterClient_Ticker, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_advertise = {"advertise", "Yes", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_advertise = {"advertise", "No", CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; #if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; From 8f3e2dd2dd42a18bb9d064f5961efb72d2e88497 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:49:12 -0700 Subject: [PATCH 163/193] Detect the compiler version and set the correct GCC flag If the version is not supported by the Makefile, the flag for the latest version supported is set instead. (cherry picked from commit 9963d38ce22ee50327103774613b596df529518a) --- src/Makefile.cfg | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index a3baeedda..7a0455aa8 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -1,3 +1,4 @@ +# vim: ft=make # # Makefile.cfg for SRB2 # @@ -7,6 +8,42 @@ # and other things # +# See the following variable don't start with 'GCC'. This is +# to avoid a false positive with the version detection... + +SUPPORTED_GCC_VERSIONS:=\ + 91\ + 81 82 83\ + 71 72\ + 61 62 63 64\ + 51 52 53 54\ + 40 41 42 43 44 45 46 47 48 49 + +LATEST_GCC_VERSION=9.1 + +# Automatically set version flag, but not if one was manually set +ifeq (,$(filter GCC%,$(.VARIABLES))) + ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC + version:=$(shell $(CC) -dumpversion) + + # Turn version into words of major, minor + v:=$(subst ., ,$(version)) + # concat. major minor + v:=$(word 1,$(v))$(word 2,$(v)) + + # If this version is not in the list, default to the latest supported + ifeq (,$(filter $(v),$(SUPPORTED_GCC_VERSIONS))) + $(info\ + Your compiler version, GCC $(version) is not supported by the Makefile.\ + The Makefile will assume GCC $(LATEST_GCC_VERSION).) + GCC$(subst .,,$(LATEST_GCC_VERSION))=1 + else + $(info Detected GCC $(version) (GCC$(v))) + GCC$(v)=1 + endif + endif +endif + ifdef GCC91 GCC83=1 endif From c9463d90b6854df545dd9997510a4874b21e1ddc Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:52:19 -0700 Subject: [PATCH 164/193] Makefile: Move the PREFIX stuff up so version detection can take advantage of (cherry picked from commit 2a059632a1e6b93c7de44c0b36d68a5f88372fc0) --- src/Makefile.cfg | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 7a0455aa8..f7bae027b 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -21,6 +21,30 @@ SUPPORTED_GCC_VERSIONS:=\ LATEST_GCC_VERSION=9.1 +# gcc or g++ +ifdef PREFIX + CC=$(PREFIX)-gcc + CXX=$(PREFIX)-g++ + OBJCOPY=$(PREFIX)-objcopy + OBJDUMP=$(PREFIX)-objdump + STRIP=$(PREFIX)-strip + WINDRES=$(PREFIX)-windres +else + OBJCOPY=objcopy + OBJDUMP=objdump + STRIP=strip + WINDRES=windres +endif + +# because Apple screws with us on this +# need to get bintools from homebrew +ifdef MACOSX + CC=clang + CXX=clang + OBJCOPY=gobjcopy + OBJDUMP=gobjdump +endif + # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC @@ -500,30 +524,6 @@ ifdef ARCHNAME BIN:=$(BIN)/$(ARCHNAME) endif -# gcc or g++ -ifdef PREFIX - CC=$(PREFIX)-gcc - CXX=$(PREFIX)-g++ - OBJCOPY=$(PREFIX)-objcopy - OBJDUMP=$(PREFIX)-objdump - STRIP=$(PREFIX)-strip - WINDRES=$(PREFIX)-windres -else - OBJCOPY=objcopy - OBJDUMP=objdump - STRIP=strip - WINDRES=windres -endif - -# because Apple screws with us on this -# need to get bintools from homebrew -ifdef MACOSX - CC=clang - CXX=clang - OBJCOPY=gobjcopy - OBJDUMP=gobjdump -endif - OBJDUMP_OPTS?=--wide --source --line-numbers LD=$(CC) From 599add9ad46acf65268cdf6f9e56a778217b4e77 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:58:11 -0700 Subject: [PATCH 165/193] Forgot a comma (cherry picked from commit 6cddbf7afb4c1c4b220a168da8d9c14d2a96786c) --- src/Makefile.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index f7bae027b..363bf56cf 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -58,7 +58,7 @@ ifeq (,$(filter GCC%,$(.VARIABLES))) # If this version is not in the list, default to the latest supported ifeq (,$(filter $(v),$(SUPPORTED_GCC_VERSIONS))) $(info\ - Your compiler version, GCC $(version) is not supported by the Makefile.\ + Your compiler version, GCC $(version), is not supported by the Makefile.\ The Makefile will assume GCC $(LATEST_GCC_VERSION).) GCC$(subst .,,$(LATEST_GCC_VERSION))=1 else From ccefaa77e9868e252c04409bbf275607833455f7 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Jul 2020 12:45:35 -0700 Subject: [PATCH 166/193] It's not always GCC, but it probably is gcc (I hope) (cherry picked from commit 38ce80317de8a342727efa182d1fa4c22cd64382) --- src/Makefile.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 363bf56cf..a1d9d21ee 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -47,7 +47,7 @@ endif # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) - ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC + ifneq (,$(findstring gcc,$(shell $(CC) --version))) # if it's GCC version:=$(shell $(CC) -dumpversion) # Turn version into words of major, minor From 6e102db9c546648573aca697c6b5da6e1067912a Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 24 Jul 2020 02:32:10 -0700 Subject: [PATCH 167/193] Add missing GCC version flags to the Makefile (cherry picked from commit f939cf973b7e609e5358c299bbb7a449b61d7ce1) --- src/Makefile.cfg | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index a1d9d21ee..dbec2944c 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -12,11 +12,12 @@ # to avoid a false positive with the version detection... SUPPORTED_GCC_VERSIONS:=\ - 91\ - 81 82 83\ - 71 72\ + 101 102\ + 91 92 93\ + 81 82 83 84\ + 71 72 73 74 75\ 61 62 63 64\ - 51 52 53 54\ + 51 52 53 54 55\ 40 41 42 43 44 45 46 47 48 49 LATEST_GCC_VERSION=9.1 @@ -68,7 +69,27 @@ ifeq (,$(filter GCC%,$(.VARIABLES))) endif endif +ifdef GCC102 +GCC101=1 +endif + +ifdef GCC101 +GCC93=1 +endif + +ifdef GCC93 +GCC92=1 +endif + +ifdef GCC92 +GCC91=1 +endif + ifdef GCC91 +GCC84=1 +endif + +ifdef GCC84 GCC83=1 endif @@ -81,6 +102,18 @@ GCC81=1 endif ifdef GCC81 +GCC75=1 +endif + +ifdef GCC75 +GCC74=1 +endif + +ifdef GCC74 +GCC73=1 +endif + +ifdef GCC73 GCC72=1 endif @@ -105,6 +138,10 @@ GCC61=1 endif ifdef GCC61 +GCC55=1 +endif + +ifdef GCC55 GCC54=1 endif From ec8cab9a3a944eb1c592e260d88aa767c2c36e5b Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 24 Jul 2020 02:33:39 -0700 Subject: [PATCH 168/193] Update LATEST_GCC_VERSION too (cherry picked from commit 4931d82393778f376186199401e7e6b5df14aa3f) --- src/Makefile.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index dbec2944c..c3abdea2b 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -20,7 +20,7 @@ SUPPORTED_GCC_VERSIONS:=\ 51 52 53 54 55\ 40 41 42 43 44 45 46 47 48 49 -LATEST_GCC_VERSION=9.1 +LATEST_GCC_VERSION=10.2 # gcc or g++ ifdef PREFIX From ce894b783784db1b1280ece048db5f78bc7f0155 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 15 Aug 2020 21:29:02 -0700 Subject: [PATCH 169/193] Is it GCC is it gcc, is it???? (cherry picked from commit 246e71a463c88c3bbc5334f9d07caca2e1577f79) --- src/Makefile.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index c3abdea2b..dcbbe027e 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -48,7 +48,9 @@ endif # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) - ifneq (,$(findstring gcc,$(shell $(CC) --version))) # if it's GCC + version:=$(shell $(CC) --version) + # check if this is in fact GCC + ifneq (,$(or $(findstring gcc,$(version)),$(findstring GCC,$(version)))) version:=$(shell $(CC) -dumpversion) # Turn version into words of major, minor From e415b4b4755ffa1f79126f91b985cc6a7528bc78 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 18 Aug 2020 16:45:00 -0700 Subject: [PATCH 170/193] Makefile: Make WARNINGMODE the default, optionally disable with RELAXWARNINGS (cherry picked from commit 9495e6354fed1028c6fc502cc9d946cfdd0b7896) --- src/Makefile | 3 ++- src/Makefile.cfg | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index b417a38e4..171225dc7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -60,7 +60,8 @@ # Compile with GCC 4.6x version, add 'GCC46=1' # Compile a profile version, add 'PROFILEMODE=1' # Compile a debug version, add 'DEBUGMODE=1' -# Compile with extra warnings, add 'WARNINGMODE=1' +# Compile with less warnings, add 'RELAXWARNINGS=1' +# Generate compiler errors for most compiler warnings, add 'ERRORMODE=1' # Compile without NASM's tmap.nas, add 'NOASM=1' # Compile without 3D hardware support, add 'NOHW=1' # Compile with GDBstubs, add 'RDB=1' diff --git a/src/Makefile.cfg b/src/Makefile.cfg index dcbbe027e..e45265cab 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -214,10 +214,7 @@ WFLAGS=-Wall ifndef GCC295 #WFLAGS+=-Wno-packed endif -ifdef ERRORMODE -WARNINGMODE=1 -endif -ifdef WARNINGMODE +ifndef RELAXWARNINGS WFLAGS+=-W #WFLAGS+=-Wno-sign-compare ifndef GCC295 From 538c27f2faad49af1473c78b7e650834f6d6e019 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 18 Aug 2020 18:01:59 -0700 Subject: [PATCH 171/193] Makefile: automatically detect system to compile for, if no system was specified This should work for mingw and linux so far. (cherry picked from commit f92026f98b168f4556d50d3dde403ab6bfd97ff3) --- src/Makefile | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/Makefile b/src/Makefile index 171225dc7..b342ec7a3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -81,6 +81,57 @@ # ############################################################################# +ALL_SYSTEMS=\ + PANDORA\ + LINUX64\ + MINGW64\ + HAIKU\ + DUMMY\ + DJGPPDOS\ + MINGW\ + UNIX\ + LINUX\ + SOLARIS\ + FREEBSD\ + MACOSX\ + SDL\ + +# check for user specified system +ifeq (,$(filter $(ALL_SYSTEMS),$(.VARIABLES))) +ifeq ($(OS),Windows_NT) # all windows are Windows_NT... + + $(info Detected a Windows system, compiling for 32-bit MinGW SDL2...) + + # go for a 32-bit sdl mingw exe by default + MINGW=1 + SDL=1 + +else # if you on the *nix + + system:=$(shell uname -s) + + ifeq ($(system),Linux) + new_system=LINUX + else + + $(error \ + Could not automatically detect your system,\ + try specifying a system manually) + + endif + + ifeq ($(shell getconf LONG_BIT),64) + system+=64-bit + new_system:=$(new_system)64 + endif + + $(info Detected $(system) ($(new_system))...) + $(new_system)=1 + +endif +endif + + # SRB2 data files D_DIR?=../bin/Resources D_FILES=$(D_DIR)/srb2.srb \ From e4622cfeb3eea71135cc1a204cde87d0a9fa4ed1 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Aug 2020 20:19:50 -0700 Subject: [PATCH 172/193] Makefile: add WINDOWSHELL=1 for the Windows detect (cherry picked from commit 6f51c68c7248d616ad16f66b72cfcc36b7c952f5) --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index b342ec7a3..23b391fab 100644 --- a/src/Makefile +++ b/src/Makefile @@ -105,6 +105,7 @@ ifeq ($(OS),Windows_NT) # all windows are Windows_NT... # go for a 32-bit sdl mingw exe by default MINGW=1 SDL=1 + WINDOWSHELL=1 else # if you on the *nix From 9f775a169d34bffe3f22fc4b500a9914b977a5f4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 14 Sep 2020 12:47:55 -0400 Subject: [PATCH 173/193] Rename the "small" variable to "isSmall" Reported by MK, apparently "small" is considered a data type for some systems. After a little bit of research, apparently it can happen when including ... whatever, just rename the variable :p --- src/m_menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 79b8d53da..480c887b6 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11300,12 +11300,12 @@ static const char *M_GetDiscordName(discordRequest_t *r) } // (this goes in k_hud.c when merged into v2) -static void M_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean small) +static void M_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean isSmall) { patch_t *stickerEnd; INT32 height; - - if (small == true) + + if (isSmall == true) { stickerEnd = W_CachePatchName("K_STIKE2", PU_CACHE); height = 6; From 9d3a49e4ae7b867571a3cdbb5008c3a8dde5a616 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 14 Sep 2020 16:34:17 -0400 Subject: [PATCH 174/193] Move master server presence updates to Advertise_OnChange This will probably call DRPC_UpdatePresence slightly less? --- src/mserv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/mserv.c b/src/mserv.c index 7adec7fff..ab615711d 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -251,10 +251,6 @@ Finish_update (void) if (! done) Finish_update(); -#ifdef HAVE_DISCORDRPC - else - DRPC_UpdatePresence(); -#endif } static void @@ -288,10 +284,6 @@ Finish_unlist (void) I_wake_all_cond(&MSCond); #endif } - -#ifdef HAVE_DISCORDRPC - DRPC_UpdatePresence(); -#endif } #ifdef HAVE_THREADS @@ -564,4 +556,8 @@ Advertise_OnChange(void) { UnregisterServer(); } + +#ifdef HAVE_DISCORDRPC + DRPC_UpdatePresence(); +#endif } From 11e2fb4110a44a943302d99027620b276e67e2ae Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 14 Sep 2020 22:54:07 -0400 Subject: [PATCH 175/193] Make "divide by zero" or "modulo by zero" errors show the file and line of where it happened Shouldn't break scripts as it stops execution as soon it happens, like it already does. --- src/blua/lvm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index b654613f4..46a015c1e 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -322,8 +322,8 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; - case TM_DIV: if (nc == 0) { lua_pushliteral(L, "divide by zero error"); lua_error(L); } else setnvalue(ra, luai_numdiv(nb, nc)); break; - case TM_MOD: if (nc == 0) { lua_pushliteral(L, "modulo by zero error"); lua_error(L); } else setnvalue(ra, luai_nummod(nb, nc)); break; + case TM_DIV: if (nc == 0) { luaG_runerror(L, "divide by zero error"); } else setnvalue(ra, luai_numdiv(nb, nc)); break; + case TM_MOD: if (nc == 0) { luaG_runerror(L, "modulo by zero error"); } else setnvalue(ra, luai_nummod(nb, nc)); break; case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; case TM_AND: setnvalue(ra, luai_numand(nb, nc)); break; @@ -492,8 +492,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { if (ttisnumber(rb) && ttisnumber(rc)) { lua_Number nb = nvalue(rb), nc = nvalue(rc); if (nc == 0) { - lua_pushliteral(L, "divide by zero error"); - lua_error(L); + luaG_runerror(L, "divide by zero error"); } else setnvalue(ra, luai_numdiv(nb, nc)); @@ -508,8 +507,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { if (ttisnumber(rb) && ttisnumber(rc)) { lua_Number nb = nvalue(rb), nc = nvalue(rc); if (nc == 0) { - lua_pushliteral(L, "modulo by zero error"); - lua_error(L); + luaG_runerror(L, "modulo by zero error"); } else setnvalue(ra, luai_nummod(nb, nc)); From 2d5b48e08c432438ebcf3d96570a4d1ee990f1ae Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Sep 2020 19:56:08 -0700 Subject: [PATCH 176/193] Disable DRPC if dedicated --- src/d_main.c | 10 ++++++++-- src/discord.c | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 0404dc446..2c02565dc 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -732,7 +732,10 @@ void D_SRB2Loop(void) #endif #ifdef HAVE_DISCORDRPC - Discord_RunCallbacks(); + if (! dedicated) + { + Discord_RunCallbacks(); + } #endif } } @@ -1600,7 +1603,10 @@ void D_SRB2Main(void) } #ifdef HAVE_DISCORDRPC - DRPC_Init(); + if (! dedicated) + { + DRPC_Init(); + } #endif } diff --git a/src/discord.c b/src/discord.c index 99cc0a3bd..b3798e291 100644 --- a/src/discord.c +++ b/src/discord.c @@ -482,6 +482,11 @@ void DRPC_UpdatePresence(void) DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); + if (dedicated) + { + return; + } + if (!cv_discordrp.value) { // User doesn't want to show their game information, so update with empty presence. From e656a7d92d56bd05c8079369f12c38ac3063a95d Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 17 Sep 2020 15:59:02 -0400 Subject: [PATCH 177/193] Link with exchndl, instead of dynamically loading it. It caused problems with crash reports not being made if the current working directory gets changed. Not even recommended anymore per the drmingw readme https://github.com/jrfonseca/drmingw#exchndl --- libs/drmingw/include/exchndl.h | 39 ++++++++++++++++++++++++++++ libs/drmingw/lib/win32/libexchndl.a | Bin 0 -> 3210 bytes libs/drmingw/lib/win32/libmgwhelp.a | Bin 0 -> 85370 bytes src/sdl/i_main.c | 6 ++++- src/win32/Makefile.cfg | 6 +++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 libs/drmingw/include/exchndl.h create mode 100644 libs/drmingw/lib/win32/libexchndl.a create mode 100644 libs/drmingw/lib/win32/libmgwhelp.a diff --git a/libs/drmingw/include/exchndl.h b/libs/drmingw/include/exchndl.h new file mode 100644 index 000000000..37dafb88e --- /dev/null +++ b/libs/drmingw/include/exchndl.h @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2015 Jose Fonseca + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#pragma once + + +#include + + +// Set the unhandled exception handler. +// Must be called when exchndll.dll is statically loaded (as opposed to loaded +// dynamically via LoadLibrary) +EXTERN_C VOID APIENTRY +ExcHndlInit(void); + + +// Override the report file name. +// +// Default is prog_name.RPT, in the same directory as the main executable. +// +// You can also pass "-" for stderr. +EXTERN_C BOOL APIENTRY +ExcHndlSetLogFileNameA(const char *szLogFileName); diff --git a/libs/drmingw/lib/win32/libexchndl.a b/libs/drmingw/lib/win32/libexchndl.a new file mode 100644 index 0000000000000000000000000000000000000000..cd462a0df1b7e8f95d34ba2c6fa184161dd1604c GIT binary patch literal 3210 zcmdT`-HOvd6h3LYcDJlQpcmqWL{RWTYWkO5p)M6s6p9Gmi7?GJX&bjqOPgx($_pQ0 zA8FYq@Y1JoJ?G4%owiB4E$HHb%$%J0GL!k{n=|vE>I|Emquf(--U^j+sc4sq&kCk# zag$z}X0c?e4;ncDxCUTY0O_CT{*uq$Il$hz5HE!o4EvwGW;hb=u;&Nuui|*@cU>X< zu751>@g~hq;C6*0{J`mXAVkM=+>O5m@oLh1gB^YF!g{~`+V6Vru)7zvGWO{A24Y3K z`tohyhc#;fu7cq9ympxHPw7hHsbE>QjTG4hB#^C5RiZfIc9U@j5pExy{TN)- zu9iFRcDs;o;VP+y-xmOFqvf1L0TtJIqHfa*eG^RQD3yfK_(>;WG;V0VX@kmnQ~WV6 zJJ_@W!<^8{rww9d*`nSFlY-}1ZoO-Aq|eR<`aCF9mh@>Ox3~B=Ggqz8V#L-RbV#4H zcgSe%;`GUl3x$T8>U{yiuY`+>Q;b$F%O&mHEc0`nz00V#!zm5nXsiTig`IJ5A|tFH zxB`VUyHerAFL63EJwwQUUeY{+BlS{EyAGhrr7Th~MrZ3}zH$Cnrw8wYk%d-}n_+F9 zKAfFrbtB6^{{yu~hZ`wRUmc3<;`0RCKPf#@W&XW9Js#L&OZ? zanpT3{Z$e3EgolMGuu5ZYYL-6Sea)RkLd06bQZ�K}icgdv5{n4~DYAK)Xorgz=b z`KDph%h?_DvyO8)D1|axGzxyDonLC7Y1w^F*F0DH2e}XEdomhb>iRyv15iIQa-y;9UNzT|v@lU%3t_iFX2`j<-osExZy>25r3N$Ej6 wZmRSfIKP_0psOri=@2QMu7`;HpVfU^&2*;j$I;|@W(_POb+5W2Z=37?0OSYSbpQYW literal 0 HcmV?d00001 diff --git a/libs/drmingw/lib/win32/libmgwhelp.a b/libs/drmingw/lib/win32/libmgwhelp.a new file mode 100644 index 0000000000000000000000000000000000000000..330cb52fe52c82ba557712e6cdd55f154f6e041a GIT binary patch literal 85370 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 KQFfWV`+otx_Un8A literal 0 HcmV?d00001 diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c index a2ce653ec..e6644ec51 100644 --- a/src/sdl/i_main.c +++ b/src/sdl/i_main.c @@ -58,6 +58,10 @@ char logfilename[1024]; #endif #endif +#if defined (_WIN32) +#include "exchndl.h" +#endif + #if defined (_WIN32) #include "../win32/win_dbg.h" typedef BOOL (WINAPI *p_IsDebuggerPresent)(VOID); @@ -170,7 +174,7 @@ int main(int argc, char **argv) ) #endif { - LoadLibraryA("exchndl.dll"); + ExcHndlInit(); } } #ifndef __MINGW32__ diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index 935b27222..e9e18280b 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -60,6 +60,12 @@ else endif endif +ifndef MINGW64 + CPPFLAGS+=-I../libs/drmingw/include + LDFLAGS+=-L../libs/drmingw/lib/win32 + LIBS+=-lmgwhelp -lexchndl +endif + # name of the exefile ifdef SDL EXENAME?=srb2kart.exe From 3b7681718c99d3f8939cc2271f5b7f235f80d929 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 17 Sep 2020 18:58:44 -0400 Subject: [PATCH 178/193] Fix engine sounds' dampening to a sane value, now that the bug preventing it from working properly was fixed. You can hear engines in 16P again, but it's not as obnoxiously loud as it was before. Additionally, I commented this function better. --- src/k_kart.c | 117 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 38 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e6afebc78..35de052a4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4401,14 +4401,22 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) { const INT32 numsnds = 13; + + const fixed_t closedist = 160*FRACUNIT; + const fixed_t fardist = 1536*FRACUNIT; + + const UINT8 dampenval = 64; // 255 * 64 = close enough to FRACUNIT/4 + INT32 class, s, w; // engine class number + UINT8 volume = 255; - fixed_t volumedampen = 0; + fixed_t volumedampen = FRACUNIT; + INT32 targetsnd = 0; INT32 i; - s = (player->kartspeed-1)/3; - w = (player->kartweight-1)/3; + s = (player->kartspeed - 1) / 3; + w = (player->kartweight - 1) / 3; #define LOCKSTAT(stat) \ if (stat < 0) { stat = 0; } \ @@ -4417,81 +4425,114 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) LOCKSTAT(w); #undef LOCKSTAT - class = s+(3*w); + class = s + (3*w); - // Silence the engines if (leveltime < 8 || player->spectator || player->exiting) { - player->kartstuff[k_enginesnd] = 0; // Reset sound number + // Silence the engines, and reset sound number while we're at it. + player->kartstuff[k_enginesnd] = 0; return; } #if 0 if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! #else - if (leveltime % 8) // .25 seconds of wait time between engine sounds + if (leveltime % 8) #endif + { + // .25 seconds of wait time between each engine sound playback return; + } - if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->kartstuff[k_respawn] == 1)) // Startup boosts + if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->kartstuff[k_respawn] == 1)) + { + // Startup boosts only want to check for BT_ACCELERATE being pressed. targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); + } else - targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; + { + // Average out the value of forwardmove and the speed that you're moving at. + targetsnd = (((6 * cmd->forwardmove) / 25) + ((player->speed / mapobjectscale) / 5)) / 2; + } - if (targetsnd < 0) - targetsnd = 0; - if (targetsnd > 12) - targetsnd = 12; + if (targetsnd < 0) { targetsnd = 0; } + if (targetsnd > 12) { targetsnd = 12; } - if (player->kartstuff[k_enginesnd] < targetsnd) - player->kartstuff[k_enginesnd]++; - if (player->kartstuff[k_enginesnd] > targetsnd) - player->kartstuff[k_enginesnd]--; + if (player->kartstuff[k_enginesnd] < targetsnd) { player->kartstuff[k_enginesnd]++; } + if (player->kartstuff[k_enginesnd] > targetsnd) { player->kartstuff[k_enginesnd]--; } - if (player->kartstuff[k_enginesnd] < 0) - player->kartstuff[k_enginesnd] = 0; - if (player->kartstuff[k_enginesnd] > 12) - player->kartstuff[k_enginesnd] = 12; + if (player->kartstuff[k_enginesnd] < 0) { player->kartstuff[k_enginesnd] = 0; } + if (player->kartstuff[k_enginesnd] > 12) { player->kartstuff[k_enginesnd] = 12; } + + // This code calculates how many players (and thus, how many engine sounds) are within ear shot, + // and rebalances the volume of your engine sound based on how far away they are. + + // This results in multiple things: + // - When on your own, you will hear your own engine sound extremely clearly. + // - When you were alone but someone is gaining on you, yours will go quiet, and you can hear theirs more clearly. + // - When around tons of people, engine sounds will try to rebalance to not be as obnoxious. for (i = 0; i < MAXPLAYERS; i++) { UINT8 thisvol = 0; fixed_t dist; - if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (P_IsDisplayPlayer(&players[i])) + if (!playeringame[i] || !players[i].mo) { - volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. + // This player doesn't exist. continue; } - dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x, - player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2; + if (players[i].spectator || players[i].exiting) + { + // This player isn't playing an engine sound. + continue; + } + + if (player == &players[i] || P_IsDisplayPlayer(&players[i])) + { + // Don't dampen yourself! + continue; + } + + dist = P_AproxDistance( + P_AproxDistance( + player->mo->x - players[i].mo->x, + player->mo->y - players[i].mo->y), + player->mo->z - players[i].mo->z) / 2; dist = FixedDiv(dist, mapobjectscale); - if (dist > 1536< fardist) + { + // ENEMY OUT OF RANGE ! continue; - else if (dist < 160<>FRACBITS)) / (((1536<>(FRACBITS+4)); + { + thisvol = (15 * ((closedist - dist) / FRACUNIT)) / ((fardist - closedist) >> (FRACBITS+4)); + } - if (thisvol == 0) - continue; - - volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT + volumedampen += (thisvol * dampenval); } if (volumedampen > FRACUNIT) - volume = FixedDiv(volume<>FRACBITS; + { + volume = FixedDiv(volume * FRACUNIT, volumedampen) / FRACUNIT; + } - if (volume <= 0) // Might as well + if (volume <= 0) + { + // Don't need to play the sound at all. return; + } - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class*numsnds), volume); + S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class * numsnds), volume); } static void K_UpdateInvincibilitySounds(player_t *player) From d03cbdbba013670c085a31a35a55826511e0e51c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 18 Sep 2020 01:10:59 -0400 Subject: [PATCH 179/193] Make it a bit louder --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 35de052a4..c4bade950 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4405,7 +4405,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) const fixed_t closedist = 160*FRACUNIT; const fixed_t fardist = 1536*FRACUNIT; - const UINT8 dampenval = 64; // 255 * 64 = close enough to FRACUNIT/4 + const UINT8 dampenval = 48; // 255 * 48 = close enough to FRACUNIT/6 INT32 class, s, w; // engine class number From 9f8c9b6026d8cdb3ecf068f26c19ef3f6f847aa7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 18 Sep 2020 13:11:49 -0700 Subject: [PATCH 180/193] Kill some nonsense --- src/d_clisrv.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index e94fd7e83..b88e7936b 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -35,13 +35,7 @@ applications may follow different packet versions. // SOME numpty changed all the gametype constants and it fell out of sync with vanilla and now we have to pretend to be vanilla when talking to the master server... #define VANILLA_GT_RACE 2 -// Woah, what do these numbers mean? 200 refers to SRB2 2.0, 246 refers to -// SRB2Riders. Both use the old 2.0 gametype numbers. -#if VERSION == 200 || VERSION == 246 -#define VANILLA_GT_MATCH 1 -#else #define VANILLA_GT_MATCH 3 -#endif // Networking and tick handling related. #define BACKUPTICS 32 From 7533109dfc2894651c113c6a419ab62ff9f8c669 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Sep 2020 14:24:50 -0700 Subject: [PATCH 181/193] Fix few poor conflict resolution --- src/d_main.c | 2 +- src/k_kart.c | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 565962893..e26f60dc1 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -851,7 +851,7 @@ static inline void D_CleanFile(char **filearray) static boolean AddIWAD(void) { - char * path = va(pandf,srb2path,"srb2.srb"); + char * path = va(pandf,srb2path,"main.kart"); if (FIL_ReadFileOK(path)) { diff --git a/src/k_kart.c b/src/k_kart.c index d10bb25d1..bfc03c772 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -619,7 +619,6 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) { UINT8 i; - UINT8 n = 0; UINT8 useodds = 0; UINT8 disttable[14]; UINT8 distlen = 0; @@ -1346,18 +1345,13 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) */ static void K_UpdateOffroad(player_t *player) { - fixed_t offroad; - UINT8 offroadstrength = K_CheckOffroadCollide(player->mo); + fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); // If you are in offroad, a timer starts. if (offroadstrength) { - if (/*K_CheckOffroadCollide(player->mo) &&*/ player->kartstuff[k_offroad] == 0) // With the way offroad is detected now that first check is no longer necessary. -Lat' - player->kartstuff[k_offroad] = (TICRATE/2); - - if (player->kartstuff[k_offroad] > 0) - { - offroad = (offroadstrength << FRACBITS) / (TICRATE/2); + if (player->kartstuff[k_offroad] < offroadstrength) + player->kartstuff[k_offroad] += offroadstrength / TICRATE; if (player->kartstuff[k_offroad] > offroadstrength) player->kartstuff[k_offroad] = offroadstrength; From 05a31da7c36ac40fe7a8d1429e66f8332d698e3f Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Sep 2020 14:54:45 -0700 Subject: [PATCH 182/193] k_enginesnd -> khud_enginesnd --- src/k_kart.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 28b223871..89c3df399 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5312,7 +5312,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (leveltime < 8 || player->spectator) { // Silence the engines, and reset sound number while we're at it. - player->kartstuff[k_enginesnd] = 0; + player->kartstuff[khud_enginesnd] = 0; return; } @@ -5345,11 +5345,11 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (targetsnd < 0) { targetsnd = 0; } if (targetsnd > 12) { targetsnd = 12; } - if (player->kartstuff[k_enginesnd] < targetsnd) { player->kartstuff[k_enginesnd]++; } - if (player->kartstuff[k_enginesnd] > targetsnd) { player->kartstuff[k_enginesnd]--; } + if (player->kartstuff[khud_enginesnd] < targetsnd) { player->kartstuff[khud_enginesnd]++; } + if (player->kartstuff[khud_enginesnd] > targetsnd) { player->kartstuff[khud_enginesnd]--; } - if (player->kartstuff[k_enginesnd] < 0) { player->kartstuff[k_enginesnd] = 0; } - if (player->kartstuff[k_enginesnd] > 12) { player->kartstuff[k_enginesnd] = 12; } + if (player->kartstuff[khud_enginesnd] < 0) { player->kartstuff[khud_enginesnd] = 0; } + if (player->kartstuff[khud_enginesnd] > 12) { player->kartstuff[khud_enginesnd] = 12; } // This code calculates how many players (and thus, how many engine sounds) are within ear shot, // and rebalances the volume of your engine sound based on how far away they are. @@ -5419,7 +5419,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) return; } - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class * numsnds), volume); + S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[khud_enginesnd]) + (class * numsnds), volume); } static void K_UpdateInvincibilitySounds(player_t *player) From f9aae8c2e859a1aac53daf48b34f9994dd3c71b9 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 21 Sep 2020 19:34:51 -0700 Subject: [PATCH 183/193] And use karthud not kartstuff for khud_enginesnd :v --- src/k_kart.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 89c3df399..86c05e143 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5312,7 +5312,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (leveltime < 8 || player->spectator) { // Silence the engines, and reset sound number while we're at it. - player->kartstuff[khud_enginesnd] = 0; + player->karthud[khud_enginesnd] = 0; return; } @@ -5345,11 +5345,11 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (targetsnd < 0) { targetsnd = 0; } if (targetsnd > 12) { targetsnd = 12; } - if (player->kartstuff[khud_enginesnd] < targetsnd) { player->kartstuff[khud_enginesnd]++; } - if (player->kartstuff[khud_enginesnd] > targetsnd) { player->kartstuff[khud_enginesnd]--; } + if (player->karthud[khud_enginesnd] < targetsnd) { player->karthud[khud_enginesnd]++; } + if (player->karthud[khud_enginesnd] > targetsnd) { player->karthud[khud_enginesnd]--; } - if (player->kartstuff[khud_enginesnd] < 0) { player->kartstuff[khud_enginesnd] = 0; } - if (player->kartstuff[khud_enginesnd] > 12) { player->kartstuff[khud_enginesnd] = 12; } + if (player->karthud[khud_enginesnd] < 0) { player->karthud[khud_enginesnd] = 0; } + if (player->karthud[khud_enginesnd] > 12) { player->karthud[khud_enginesnd] = 12; } // This code calculates how many players (and thus, how many engine sounds) are within ear shot, // and rebalances the volume of your engine sound based on how far away they are. @@ -5419,7 +5419,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) return; } - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[khud_enginesnd]) + (class * numsnds), volume); + S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class * numsnds), volume); } static void K_UpdateInvincibilitySounds(player_t *player) From ce7161e81d53774b226d071d3af3b979413f8dc9 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Sep 2020 23:41:18 -0700 Subject: [PATCH 184/193] Check out this massive slopes Line specials 777 - slope floor by anchors, 778 - slope ceiling by anchors, 779 - slope floor and ceiling by anchors. Thing types 777 - floor anchors, 778 - ceiling anchors. Summary. These line specials slope the front or back sector's floor/ceiling by 'anchor' things. Anchors are floor or ceiling specific and snap to the nearest vertex. Using this method it is possible to create seamless slopes, without even tagging. Details. Normal slope flags apply. To slope the backside sector, set No Climb on the line. An anchor each is required to be near three of a sector's vertices in order to slope it. More may be present but the order is undefined. --- src/doomdata.h | 7 + src/p_slopes.c | 14 ++ src/p_spec.h | 8 ++ src/slope_anchors.c | 341 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 370 insertions(+) create mode 100644 src/slope_anchors.c diff --git a/src/doomdata.h b/src/doomdata.h index 767e47383..f42b13889 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -215,4 +215,11 @@ typedef struct #define NUMMAPS 1035 +/* slope thing types */ +enum +{ + FLOOR_SLOPE_THING = 777, + CEILING_SLOPE_THING = 778, +}; + #endif // __DOOMDATA__ diff --git a/src/p_slopes.c b/src/p_slopes.c index d90b583c5..2102dd7b2 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -24,6 +24,9 @@ #include "w_wad.h" #include "k_kart.h" // K_PlayerEBrake +static void P_BuildSlopeAnchorList (void); +static void P_SetupAnchoredSlopes (void); + static pslope_t *slopelist = NULL; static UINT16 slopecount = 0; @@ -731,6 +734,14 @@ void P_ResetDynamicSlopes(void) { } } + // jart + + /// Build list of slope anchors--faster searching. + P_BuildSlopeAnchorList(); + + /// Setup anchor based slopes. + P_SetupAnchoredSlopes(); + /// Copies slopes from tagged sectors via line specials. /// \note Doesn't actually copy, but instead they share the same pointers. for (i = 0; i < numlines; i++) @@ -920,5 +931,8 @@ void P_ButteredSlope(mobj_t *mo) P_Thrust(mo, mo->standingslope->xydirection, thrust); } +// jart +#include "slope_anchors.c" + // EOF #endif // #ifdef ESLOPE diff --git a/src/p_spec.h b/src/p_spec.h index 6c918ef11..6c85c6d1a 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -461,4 +461,12 @@ void P_CalcHeight(player_t *player); sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo); +/* line specials */ +enum +{ + LT_SLOPE_ANCHORS_FLOOR = 777, + LT_SLOPE_ANCHORS_CEILING = 778, + LT_SLOPE_ANCHORS = 779, +}; + #endif diff --git a/src/slope_anchors.c b/src/slope_anchors.c new file mode 100644 index 000000000..270707577 --- /dev/null +++ b/src/slope_anchors.c @@ -0,0 +1,341 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \brief Charyb's vertex slope anchors. +/// This file is self contained to avoid a Big Large merge conflict. + +/* +FIXME +FIXME +FIXME +FIXME WHEN 2.2 MERGE IS OVER, REFACTOR A LOT OF THE CODE IN P_SLOPES.C AND +FIXME MAKE THIS NOT REDUNDANT. +FIXME +FIXME +FIXME +*/ + +struct anchor_list +{ + mapthing_t ** anchors; + const vertex_t ** points; + size_t count; +}; + +struct anchor_list floor_anchors; +struct anchor_list ceiling_anchors; + +static void * new_list (size_t n) { + return Z_Malloc(n, PU_LEVEL, NULL); +} + +static void make_new_anchor_list (struct anchor_list * list) { + list->anchors = new_list(list->count * sizeof *list->anchors); + list->points = new_list(list->count * sizeof *list->points); +} + +static void allocate_anchors (void) { + size_t i; + + floor_anchors.count = 0; + ceiling_anchors.count = 0; + + for (i = 0; i < nummapthings; ++i) + { + switch (mapthings[i].type) + { + case FLOOR_SLOPE_THING: + floor_anchors.count++; + break; + + case CEILING_SLOPE_THING: + ceiling_anchors.count++; + break; + } + } + + make_new_anchor_list(&floor_anchors); + make_new_anchor_list(&ceiling_anchors); +} + +static const vertex_t * +nearest_point +( + mapthing_t * a, + const subsector_t * sub +){ + const fixed_t x = a->x << FRACBITS; + const fixed_t y = a->y << FRACBITS; + + const UINT16 lastline = sub->firstline + sub->numlines; + + vertex_t * nearest = NULL;/* shut compiler up, but should never be NULL */ + fixed_t nearest_distance = INT32_MAX; + + vertex_t * v = NULL; + + fixed_t distance; + + UINT16 i; + + for (i = sub->firstline; i < lastline; ++i) + { + if (segs[i].v1 != v) + { + v = segs[i].v1; + } + else + { + v = segs[i].v2; + } + + distance = abs(P_AproxDistance(( x - v->x ), ( y - v->y ))); + + if (distance < nearest_distance) + { + nearest = v; + nearest_distance = distance; + } + } + + return nearest; +} + +static INT16 +anchor_height +( + const mapthing_t * a, + const sector_t * s +){ + if (a->extrainfo) + { + return a->options; + } + else + { + INT16 z = ( a->options >> ZSHIFT ); + + if (a->options & MTF_OBJECTFLIP) + { + return ( s->ceilingheight >> FRACBITS ) - z; + } + else + { + return ( s->floorheight >> FRACBITS ) + z; + } + } +} + +static void +set_anchor +( + struct anchor_list * list, + mapthing_t * a +){ + const subsector_t * sub = R_PointInSubsector + ( + a->x << FRACBITS, + a->y << FRACBITS + ); + + const vertex_t * v; + + a->z = anchor_height(a, sub->sector); + + v = nearest_point(a, sub); + + a->x = ( v->x >> FRACBITS ); + a->y = ( v->y >> FRACBITS ); + + list->anchors[list->count] = a; + list->points [list->count] = v; + + list->count++; +} + +static void build_anchors (void) { + size_t i; + + floor_anchors.count = 0; + ceiling_anchors.count = 0; + + for (i = 0; i < nummapthings; ++i) + { + switch (mapthings[i].type) + { + case FLOOR_SLOPE_THING: + set_anchor(&floor_anchors, &mapthings[i]); + break; + + case CEILING_SLOPE_THING: + set_anchor(&ceiling_anchors, &mapthings[i]); + break; + } + } +} + +static mapthing_t ** +find_closest_anchors +( + const sector_t * sector, + const struct anchor_list * list +){ + mapthing_t ** anchors; + + size_t i; + size_t a; + + vertex_t * v = NULL; + + int next_anchor = 0; + + if (list->count < 3) + { + I_Error("At least three slope anchors are required to make a slope."); + } + + anchors = Z_Malloc(3 * sizeof *anchors, PU_LEVEL, NULL); + + for (i = 0; i < sector->linecount; ++i) + { + if (sector->lines[i]->v1 != v) + { + v = sector->lines[i]->v1; + } + else + { + v = sector->lines[i]->v2; + } + + for (a = 0; a < list->count; ++a) + { + if (list->points[a] == v) + { + anchors[next_anchor] = list->anchors[a]; + + if (++next_anchor == 3) + { + return anchors; + } + } + } + } + + I_Error( + "(Sector #%s)" + " Slope requires anchors near 3 of its vertices (%d found)", + + sizeu1 (sector - sectors), + next_anchor + ); +} + +static pslope_t * +new_vertex_slope +( + mapthing_t ** anchors, + const INT16 flags +){ + pslope_t * slope = Z_Calloc(sizeof (pslope_t), PU_LEVEL, NULL); + + slope->flags = SL_VERTEXSLOPE; + + if (flags & ML_NOSONIC) + { + slope->flags |= SL_NOPHYSICS; + } + + if (flags & ML_NOTAILS) + { + slope->flags |= SL_NODYNAMIC; + } + + slope->vertices = anchors; + + P_ReconfigureVertexSlope(slope); + slope->refpos = 5; + + // Add to the slope list + slope->next = slopelist; + slopelist = slope; + + slopecount++; + slope->id = slopecount; + + return slope; +} + +static void +make_anchored_slope +( + const line_t * line, + const int plane +){ + enum + { + FLOOR = 0x1, + CEILING = 0x2, + }; + + const INT16 flags = line->flags; + + const int side = ( flags & ML_NOCLIMB ) != 0; + + sector_t * sector; + mapthing_t ** anchors; + + if (side == 0 || flags & ML_TWOSIDED) + { + sector = sides[line->sidenum[side]].sector; + + if (plane & FLOOR) + { + anchors = find_closest_anchors(sector, &floor_anchors); + sector->f_slope = new_vertex_slope(anchors, flags); + } + + if (plane & CEILING) + { + anchors = find_closest_anchors(sector, &ceiling_anchors); + sector->c_slope = new_vertex_slope(anchors, flags); + } + + sector->hasslope = true; + } +} + +static void P_BuildSlopeAnchorList (void) { + allocate_anchors(); + build_anchors(); +} + +static void P_SetupAnchoredSlopes (void) { + enum + { + FLOOR = 0x1, + CEILING = 0x2, + }; + + size_t i; + + for (i = 0; i < numlines; ++i) + { + if (lines[i].special == LT_SLOPE_ANCHORS_FLOOR) + { + make_anchored_slope(&lines[i], FLOOR); + } + else if (lines[i].special == LT_SLOPE_ANCHORS_CEILING) + { + make_anchored_slope(&lines[i], CEILING); + } + else if (lines[i].special == LT_SLOPE_ANCHORS) + { + make_anchored_slope(&lines[i], FLOOR|CEILING); + } + } +} From 0999fa7096770b77376503b29f3616bcde4e7d0b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 21 Sep 2020 19:09:48 -0700 Subject: [PATCH 185/193] Fix vertex iteration and Not Dynamic flag --- src/slope_anchors.c | 117 +++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 44 deletions(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index 270707577..8c8d04e76 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -63,6 +63,28 @@ static void allocate_anchors (void) { make_new_anchor_list(&ceiling_anchors); } +static void +compare_vertex_distance +( + const vertex_t ** nearest, + fixed_t * nearest_distance, + const fixed_t origin_x, + const fixed_t origin_y, + const vertex_t * v +){ + const fixed_t distance = abs(P_AproxDistance + ( + origin_x - v->x, + origin_y - v->y + )); + + if (distance < (*nearest_distance)) + { + (*nearest) = v; + (*nearest_distance) = distance; + } +} + static const vertex_t * nearest_point ( @@ -74,33 +96,15 @@ nearest_point const UINT16 lastline = sub->firstline + sub->numlines; - vertex_t * nearest = NULL;/* shut compiler up, but should never be NULL */ - fixed_t nearest_distance = INT32_MAX; - - vertex_t * v = NULL; - - fixed_t distance; + const vertex_t * nearest = NULL;/* shut compiler up, should never be NULL */ + fixed_t nearest_distance = INT32_MAX; UINT16 i; for (i = sub->firstline; i < lastline; ++i) { - if (segs[i].v1 != v) - { - v = segs[i].v1; - } - else - { - v = segs[i].v2; - } - - distance = abs(P_AproxDistance(( x - v->x ), ( y - v->y ))); - - if (distance < nearest_distance) - { - nearest = v; - nearest_distance = distance; - } + compare_vertex_distance(&nearest, &nearest_distance, x, y, segs[i].v1); + compare_vertex_distance(&nearest, &nearest_distance, x, y, segs[i].v2); } return nearest; @@ -179,20 +183,57 @@ static void build_anchors (void) { } } +static int +get_anchor +( + const vertex_t * points[3], + mapthing_t ** anchors, + int * last_anchor, + const struct anchor_list * list, + const vertex_t * v +){ + size_t i; + + if ( + v != points[0] && + v != points[1] && + v != points[2] + ){ + for (i = 0; i < list->count; ++i) + { + if (list->points[i] == v) + { + points [*last_anchor] = v; + anchors[*last_anchor] = list->anchors[i]; + + if (++(*last_anchor) == 3) + { + return 1; + } + else + { + return 0; + } + } + } + } + + return 0; +} + static mapthing_t ** find_closest_anchors ( const sector_t * sector, const struct anchor_list * list ){ + const vertex_t * points[3] = {0}; + mapthing_t ** anchors; size_t i; - size_t a; - vertex_t * v = NULL; - - int next_anchor = 0; + int last = 0; if (list->count < 3) { @@ -203,26 +244,14 @@ find_closest_anchors for (i = 0; i < sector->linecount; ++i) { - if (sector->lines[i]->v1 != v) + if (get_anchor(points, anchors, &last, list, sector->lines[i]->v1) != 0) { - v = sector->lines[i]->v1; - } - else - { - v = sector->lines[i]->v2; + return anchors; } - for (a = 0; a < list->count; ++a) + if (get_anchor(points, anchors, &last, list, sector->lines[i]->v2) != 0) { - if (list->points[a] == v) - { - anchors[next_anchor] = list->anchors[a]; - - if (++next_anchor == 3) - { - return anchors; - } - } + return anchors; } } @@ -231,7 +260,7 @@ find_closest_anchors " Slope requires anchors near 3 of its vertices (%d found)", sizeu1 (sector - sectors), - next_anchor + last ); } @@ -250,7 +279,7 @@ new_vertex_slope slope->flags |= SL_NOPHYSICS; } - if (flags & ML_NOTAILS) + if (( flags & ML_NOTAILS ) == 0) { slope->flags |= SL_NODYNAMIC; } From 0cddfdee747ed8702fef8090528e5f330164f14b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 21 Sep 2020 19:11:13 -0700 Subject: [PATCH 186/193] Ignore slope anchors in P_SpawnMapThing --- src/p_mobj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index d58772f5c..c309b6a4f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12072,6 +12072,8 @@ void P_SpawnMapThing(mapthing_t *mthing) else if (mthing->type == 750) // Slope vertex point (formerly chaos spawn) return; + else if (mthing->type == 777 || mthing->type == 778) // Slope anchors + return; else if (mthing->type == 300 // Ring || mthing->type == 308 || mthing->type == 309 // Team Rings From ff7813ef3822f1dafd1b8adfae1dccd87747b276 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 22 Sep 2020 01:30:59 -0700 Subject: [PATCH 187/193] Use sector lines when snapping anchor to point Sometimes segs skip a point! --- src/slope_anchors.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index 8c8d04e76..da2fad2e8 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -89,22 +89,20 @@ static const vertex_t * nearest_point ( mapthing_t * a, - const subsector_t * sub + const sector_t * sector ){ const fixed_t x = a->x << FRACBITS; const fixed_t y = a->y << FRACBITS; - const UINT16 lastline = sub->firstline + sub->numlines; - const vertex_t * nearest = NULL;/* shut compiler up, should never be NULL */ fixed_t nearest_distance = INT32_MAX; - UINT16 i; + size_t i; - for (i = sub->firstline; i < lastline; ++i) + for (i = 0; i < sector->linecount; ++i) { - compare_vertex_distance(&nearest, &nearest_distance, x, y, segs[i].v1); - compare_vertex_distance(&nearest, &nearest_distance, x, y, segs[i].v2); + compare_vertex_distance(&nearest, &nearest_distance, x, y, sector->lines[i]->v1); + compare_vertex_distance(&nearest, &nearest_distance, x, y, sector->lines[i]->v2); } return nearest; @@ -151,7 +149,7 @@ set_anchor a->z = anchor_height(a, sub->sector); - v = nearest_point(a, sub); + v = nearest_point(a, sub->sector); a->x = ( v->x >> FRACBITS ); a->y = ( v->y >> FRACBITS ); From b35e2ac9b3bb4f12e1f172929416d7bc5294ebc8 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 23 Sep 2020 23:17:05 -0700 Subject: [PATCH 188/193] Use the closest of three slope anchors to their respective vertices Previously it just used the first three anchors that had snapped to a vertex in the sector. btw fuck DOOM --- src/slope_anchors.c | 118 ++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 42 deletions(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index da2fad2e8..0102b68bf 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -24,6 +24,7 @@ struct anchor_list { mapthing_t ** anchors; const vertex_t ** points; + fixed_t * closeness; size_t count; }; @@ -35,8 +36,9 @@ static void * new_list (size_t n) { } static void make_new_anchor_list (struct anchor_list * list) { - list->anchors = new_list(list->count * sizeof *list->anchors); - list->points = new_list(list->count * sizeof *list->points); + list->anchors = new_list(list->count * sizeof *list->anchors); + list->points = new_list(list->count * sizeof *list->points); + list->closeness = new_list(list->count * sizeof *list->closeness); } static void allocate_anchors (void) { @@ -88,24 +90,26 @@ compare_vertex_distance static const vertex_t * nearest_point ( + fixed_t * closeness, mapthing_t * a, const sector_t * sector ){ const fixed_t x = a->x << FRACBITS; const fixed_t y = a->y << FRACBITS; - const vertex_t * nearest = NULL;/* shut compiler up, should never be NULL */ - fixed_t nearest_distance = INT32_MAX; + const vertex_t * v = NULL;/* shut compiler up, should never be NULL */ size_t i; + (*closeness) = INT32_MAX; + for (i = 0; i < sector->linecount; ++i) { - compare_vertex_distance(&nearest, &nearest_distance, x, y, sector->lines[i]->v1); - compare_vertex_distance(&nearest, &nearest_distance, x, y, sector->lines[i]->v2); + compare_vertex_distance(&v, closeness, x, y, sector->lines[i]->v1); + compare_vertex_distance(&v, closeness, x, y, sector->lines[i]->v2); } - return nearest; + return v; } static INT16 @@ -147,15 +151,18 @@ set_anchor const vertex_t * v; + fixed_t closeness; + a->z = anchor_height(a, sub->sector); - v = nearest_point(a, sub->sector); + v = nearest_point(&closeness, a, sub->sector); a->x = ( v->x >> FRACBITS ); a->y = ( v->y >> FRACBITS ); - list->anchors[list->count] = a; - list->points [list->count] = v; + list->anchors [list->count] = a; + list->points [list->count] = v; + list->closeness[list->count] = closeness; list->count++; } @@ -181,42 +188,70 @@ static void build_anchors (void) { } } -static int +static void get_anchor ( - const vertex_t * points[3], mapthing_t ** anchors, - int * last_anchor, + fixed_t distances[3], const struct anchor_list * list, const vertex_t * v ){ size_t i; - if ( - v != points[0] && - v != points[1] && - v != points[2] - ){ - for (i = 0; i < list->count; ++i) - { - if (list->points[i] == v) - { - points [*last_anchor] = v; - anchors[*last_anchor] = list->anchors[i]; + int k; - if (++(*last_anchor) == 3) + for (i = 0; i < list->count; ++i) + { + if (list->points[i] == v) + { + for (k = 0; k < 3; ++k) + { + if (list->closeness[i] < distances[k]) { - return 1; + if (k == 0) + { + distances[2] = distances[1]; + distances[1] = distances[0]; + + anchors [2] = anchors [1]; + anchors [1] = anchors [0]; + } + else if (k == 1) + { + distances[2] = distances[1]; + anchors [2] = anchors [1]; + } + + distances[k] = list->closeness[i]; + + anchors[k] = list->anchors[i]; + + break; } - else + else if (list->anchors[i] == anchors[k]) { - return 0; + break; } } } } +} - return 0; +static void +get_sector_anchors +( + mapthing_t ** anchors, + fixed_t distances[3], + const struct anchor_list * list, + const sector_t * sector +){ + size_t i; + + for (i = 0; i < sector->linecount; ++i) + { + get_anchor(anchors, distances, list, sector->lines[i]->v1); + get_anchor(anchors, distances, list, sector->lines[i]->v2); + } } static mapthing_t ** @@ -225,12 +260,10 @@ find_closest_anchors const sector_t * sector, const struct anchor_list * list ){ - const vertex_t * points[3] = {0}; + fixed_t distances[3] = { INT32_MAX, INT32_MAX, INT32_MAX }; mapthing_t ** anchors; - size_t i; - int last = 0; if (list->count < 3) @@ -240,19 +273,20 @@ find_closest_anchors anchors = Z_Malloc(3 * sizeof *anchors, PU_LEVEL, NULL); - for (i = 0; i < sector->linecount; ++i) - { - if (get_anchor(points, anchors, &last, list, sector->lines[i]->v1) != 0) - { - return anchors; - } + get_sector_anchors(anchors, distances, list, sector); - if (get_anchor(points, anchors, &last, list, sector->lines[i]->v2) != 0) - { - return anchors; - } + if (distances[2] < INT32_MAX) + { + return anchors; } + if (distances[1] < INT32_MAX) + last = 2; + else if (distances[0] < INT32_MAX) + last = 1; + else + last = 0; + I_Error( "(Sector #%s)" " Slope requires anchors near 3 of its vertices (%d found)", From 388d56a5f8a5eb9983700a3b9820aa3a015d2e94 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Sep 2020 01:56:03 -0700 Subject: [PATCH 189/193] Search FOF target sectors for slope anchors Requires spawning fake floors before slopes, and therefore before things, basically before everything. Hopefully nothing breaks! --- src/p_setup.c | 9 +++++--- src/p_spec.c | 36 +++++++++++++++++--------------- src/p_spec.h | 1 + src/slope_anchors.c | 50 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index c2af9b3b2..51bdbce89 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3206,19 +3206,22 @@ boolean P_SetupLevel(boolean skipprecip) // anything that P_ResetDynamicSlopes/P_LoadThings needs to know P_InitSpecials(); + // set up world state + // jart: needs to be done here so anchored slopes know the attached list + P_SpawnSpecials(fromnetsave); + P_ResetDynamicSlopes(); P_LoadThings(); + P_RaiseThings(); + P_SpawnSecretItems(loademblems); for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) if (!playerstarts[numcoopstarts]) break; - // set up world state - P_SpawnSpecials(fromnetsave); - K_AdjustWaypointsParameters(); if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) diff --git a/src/p_spec.c b/src/p_spec.c index 4c86714eb..47e476478 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -5358,6 +5358,26 @@ P_RaiseTaggedThingsToFakeFloor ( } } +void +P_RaiseThings (void) +{ + size_t i; + + for (i = 0; i < numlines; ++i) + { + switch (lines[i].special) + { + case 80: // Raise tagged things by type to this FOF + P_RaiseTaggedThingsToFakeFloor( + ( sides[lines[i].sidenum[0]].textureoffset >> FRACBITS ), + lines[i].tag, + lines[i].frontsector + ); + break; + } + } +} + // // SPECIAL SPAWNING // @@ -6822,22 +6842,6 @@ void P_SpawnSpecials(INT32 fromnetsave) } } - /* some things have to be done after FOF spawn */ - - for (i = 0; i < numlines; ++i) - { - switch (lines[i].special) - { - case 80: // Raise tagged things by type to this FOF - P_RaiseTaggedThingsToFakeFloor( - ( sides[lines[i].sidenum[0]].textureoffset >> FRACBITS ), - lines[i].tag, - lines[i].frontsector - ); - break; - } - } - // Allocate each list for (i = 0; i < numsectors; i++) if(secthinkers[i].thinkers) diff --git a/src/p_spec.h b/src/p_spec.h index 6c85c6d1a..ea497fb19 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -38,6 +38,7 @@ void P_SetupLevelFlatAnims(void); // at map load void P_InitSpecials(void); void P_SpawnSpecials(INT32 fromnetsave); +void P_RaiseThings(void); // every tic void P_UpdateSpecials(void); diff --git a/src/slope_anchors.c b/src/slope_anchors.c index 0102b68bf..ff1504a37 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -266,6 +266,8 @@ find_closest_anchors int last = 0; + size_t i; + if (list->count < 3) { I_Error("At least three slope anchors are required to make a slope."); @@ -273,7 +275,18 @@ find_closest_anchors anchors = Z_Malloc(3 * sizeof *anchors, PU_LEVEL, NULL); - get_sector_anchors(anchors, distances, list, sector); + if (sector->numattached > 0) + { + for (i = 0; i < sector->numattached; ++i) + { + get_sector_anchors + (anchors, distances, list, §ors[sector->attached[i]]); + } + } + else + { + get_sector_anchors(anchors, distances, list, sector); + } if (distances[2] < INT32_MAX) { @@ -287,13 +300,36 @@ find_closest_anchors else last = 0; - I_Error( - "(Sector #%s)" - " Slope requires anchors near 3 of its vertices (%d found)", + if (sector->numattached > 0) + { + CONS_Printf("\nSearched for anchors in sectors...\n\n"); - sizeu1 (sector - sectors), - last - ); + for (i = 0; i < sector->numattached; ++i) + { + CONS_Printf("#%s\n", sizeu1 (sector->attached[i])); + } + + I_Error( + "(Control Sector #%s)" + " Slope requires anchors near 3 of its target sectors' vertices" + " (%d found)" + + "\n\nCheck the log to see which sectors were searched.", + + sizeu1 (sector - sectors), + last + ); + } + else + { + I_Error( + "(Sector #%s)" + " Slope requires anchors near 3 of its vertices (%d found)", + + sizeu1 (sector - sectors), + last + ); + } } static pslope_t * From 1403c49f501c75a6f20878b560d83f7871baf815 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Sep 2020 03:18:31 -0700 Subject: [PATCH 190/193] Use Parameter on slope anchors as a tag This makes FOF sloping over other slopes possible! Had to remove the absolute height function though. --- src/slope_anchors.c | 58 +++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index ff1504a37..add9a33b7 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -118,11 +118,6 @@ anchor_height const mapthing_t * a, const sector_t * s ){ - if (a->extrainfo) - { - return a->options; - } - else { INT16 z = ( a->options >> ZSHIFT ); @@ -194,6 +189,7 @@ get_anchor mapthing_t ** anchors, fixed_t distances[3], const struct anchor_list * list, + const INT16 tag, const vertex_t * v ){ size_t i; @@ -202,7 +198,7 @@ get_anchor for (i = 0; i < list->count; ++i) { - if (list->points[i] == v) + if (list->points[i] == v && list->anchors[i]->extrainfo == tag) { for (k = 0; k < 3; ++k) { @@ -243,14 +239,15 @@ get_sector_anchors mapthing_t ** anchors, fixed_t distances[3], const struct anchor_list * list, + const INT16 tag, const sector_t * sector ){ size_t i; for (i = 0; i < sector->linecount; ++i) { - get_anchor(anchors, distances, list, sector->lines[i]->v1); - get_anchor(anchors, distances, list, sector->lines[i]->v2); + get_anchor(anchors, distances, list, tag, sector->lines[i]->v1); + get_anchor(anchors, distances, list, tag, sector->lines[i]->v2); } } @@ -258,7 +255,8 @@ static mapthing_t ** find_closest_anchors ( const sector_t * sector, - const struct anchor_list * list + const struct anchor_list * list, + const INT16 tag ){ fixed_t distances[3] = { INT32_MAX, INT32_MAX, INT32_MAX }; @@ -280,12 +278,12 @@ find_closest_anchors for (i = 0; i < sector->numattached; ++i) { get_sector_anchors - (anchors, distances, list, §ors[sector->attached[i]]); + (anchors, distances, list, tag, §ors[sector->attached[i]]); } } else { - get_sector_anchors(anchors, distances, list, sector); + get_sector_anchors(anchors, distances, list, tag, sector); } if (distances[2] < INT32_MAX) @@ -311,12 +309,13 @@ find_closest_anchors I_Error( "(Control Sector #%s)" - " Slope requires anchors near 3 of its target sectors' vertices" - " (%d found)" + " Slope requires anchors (with Parameter %d)" + " near 3 of its target sectors' vertices (%d found)" "\n\nCheck the log to see which sectors were searched.", sizeu1 (sector - sectors), + tag, last ); } @@ -324,9 +323,11 @@ find_closest_anchors { I_Error( "(Sector #%s)" - " Slope requires anchors near 3 of its vertices (%d found)", + " Slope requires anchors (with Parameter %d)" + " near 3 of its vertices (%d found)", sizeu1 (sector - sectors), + tag, last ); } @@ -367,6 +368,24 @@ new_vertex_slope return slope; } +static void +slope_sector +( + pslope_t ** slope, + sector_t * sector, + const INT16 flags, + const struct anchor_list * list, + const INT16 tag +){ + mapthing_t ** anchors = find_closest_anchors(sector, list, tag); + + if (anchors != NULL) + { + (*slope) = new_vertex_slope(anchors, flags); + sector->hasslope = true; + } +} + static void make_anchored_slope ( @@ -384,7 +403,6 @@ make_anchored_slope const int side = ( flags & ML_NOCLIMB ) != 0; sector_t * sector; - mapthing_t ** anchors; if (side == 0 || flags & ML_TWOSIDED) { @@ -392,17 +410,15 @@ make_anchored_slope if (plane & FLOOR) { - anchors = find_closest_anchors(sector, &floor_anchors); - sector->f_slope = new_vertex_slope(anchors, flags); + slope_sector + (§or->f_slope, sector, flags, &floor_anchors, line->tag); } if (plane & CEILING) { - anchors = find_closest_anchors(sector, &ceiling_anchors); - sector->c_slope = new_vertex_slope(anchors, flags); + slope_sector + (§or->c_slope, sector, flags, &ceiling_anchors, line->tag); } - - sector->hasslope = true; } } From efbec2c2527dc279d12294f623e86c69d8c312ed Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Sep 2020 12:19:19 -0700 Subject: [PATCH 191/193] Line special 777/778: No Knuckles to mirror slope on opposite plane --- src/slope_anchors.c | 47 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index add9a33b7..acabd0caa 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -368,10 +368,35 @@ new_vertex_slope return slope; } +static mapthing_t ** +flip_slope +( + mapthing_t ** origin, + const sector_t * sector +){ + mapthing_t * copy = Z_Malloc(3 * sizeof (mapthing_t), PU_LEVEL, NULL); + mapthing_t ** anchors = Z_Malloc(3 * sizeof (mapthing_t *), PU_LEVEL, NULL); + + size_t i; + + for (i = 0; i < 3; ++i) + { + memcpy(©[i], origin[i], sizeof copy[i]); + + copy[i].options ^= MTF_OBJECTFLIP; + copy[i].z = anchor_height(©[i], sector); + + anchors[i] = ©[i]; + } + + return anchors; +} + static void slope_sector ( pslope_t ** slope, + pslope_t ** alt, sector_t * sector, const INT16 flags, const struct anchor_list * list, @@ -382,6 +407,13 @@ slope_sector if (anchors != NULL) { (*slope) = new_vertex_slope(anchors, flags); + + /* No Knuckles - invert slope to opposite side */ + if (flags & ML_NOKNUX) + { + (*alt) = new_vertex_slope(flip_slope(anchors, sector), flags); + } + sector->hasslope = true; } } @@ -398,26 +430,31 @@ make_anchored_slope CEILING = 0x2, }; - const INT16 flags = line->flags; + INT16 flags = line->flags; const int side = ( flags & ML_NOCLIMB ) != 0; - sector_t * sector; + sector_t * s; if (side == 0 || flags & ML_TWOSIDED) { - sector = sides[line->sidenum[side]].sector; + s = sides[line->sidenum[side]].sector; + + if (plane == FLOOR|CEILING) + { + flags &= ~ML_NOKNUX; + } if (plane & FLOOR) { slope_sector - (§or->f_slope, sector, flags, &floor_anchors, line->tag); + (&s->f_slope, &s->c_slope, s, flags, &floor_anchors, line->tag); } if (plane & CEILING) { slope_sector - (§or->c_slope, sector, flags, &ceiling_anchors, line->tag); + (&s->c_slope, &s->f_slope, s, flags, &ceiling_anchors, line->tag); } } } From 3d6ed19d55da2bdc3246fccb46698cbdd52517af Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Sep 2020 12:20:32 -0700 Subject: [PATCH 192/193] oops --- src/slope_anchors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slope_anchors.c b/src/slope_anchors.c index acabd0caa..d66c9d9dd 100644 --- a/src/slope_anchors.c +++ b/src/slope_anchors.c @@ -440,7 +440,7 @@ make_anchored_slope { s = sides[line->sidenum[side]].sector; - if (plane == FLOOR|CEILING) + if (plane == (FLOOR|CEILING)) { flags &= ~ML_NOKNUX; } From 0d573b514e592cf4e12723760db85d0d1ebb770c Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Sep 2020 15:10:07 -0700 Subject: [PATCH 193/193] Stupid polyobjects require things to be spawned >:( --- src/p_setup.c | 2 ++ src/p_spec.c | 7 +++++++ src/p_spec.h | 1 + 3 files changed, 10 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index 51bdbce89..6b92a405f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3216,6 +3216,8 @@ boolean P_SetupLevel(boolean skipprecip) P_RaiseThings(); + P_SpawnSpecialsThatRequireObjects(); + P_SpawnSecretItems(loademblems); for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) diff --git a/src/p_spec.c b/src/p_spec.c index 47e476478..84b4a7dd1 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -6848,6 +6848,13 @@ void P_SpawnSpecials(INT32 fromnetsave) Z_Free(secthinkers[i].thinkers); Z_Free(secthinkers); +} + +/** Fuck polyobjects + */ +void P_SpawnSpecialsThatRequireObjects(void) +{ + size_t i; // haleyjd 02/20/06: spawn polyobjects Polyobj_InitLevel(); diff --git a/src/p_spec.h b/src/p_spec.h index ea497fb19..24bbfadd9 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -39,6 +39,7 @@ void P_SetupLevelFlatAnims(void); void P_InitSpecials(void); void P_SpawnSpecials(INT32 fromnetsave); void P_RaiseThings(void); +void P_SpawnSpecialsThatRequireObjects(void); // every tic void P_UpdateSpecials(void);