diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ecee2b19..67a3b66b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,7 +43,7 @@ jobs: - v1-SRB2-APT - run: name: Install SDK - command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client + command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client - save_cache: key: v1-SRB2-APT @@ -71,4 +71,4 @@ jobs: - save_cache: key: v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} paths: - - /root/.ccache \ No newline at end of file + - /root/.ccache diff --git a/appveyor.yml b/appveyor.yml index 820c77e8b..2acc2f712 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.2.6.{branch}-{build} +version: 2.2.8.{branch}-{build} os: MinGW environment: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b9bb5767..f9778a40b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRB2_CORE_SOURCES m_fixed.c m_menu.c m_misc.c + m_perfstats.c m_queue.c m_random.c md5.c @@ -97,12 +98,12 @@ set(SRB2_CORE_HEADERS m_fixed.h m_menu.h m_misc.h + m_perfstats.h m_queue.h m_random.h m_swap.h md5.h mserv.h - http-mserv.h p5prof.h s_sound.h screen.h @@ -128,7 +129,8 @@ set(SRB2_CORE_RENDER_SOURCES r_sky.c r_splats.c r_things.c - r_patch.c + r_textures.c + r_picformats.c r_portal.c r_bsp.h @@ -144,7 +146,8 @@ set(SRB2_CORE_RENDER_SOURCES r_splats.h r_state.h r_things.h - r_patch.h + r_textures.h + r_picformats.h r_portal.h ) @@ -232,6 +235,8 @@ set(SRB2_CONFIG_HAVE_OPENMPT ON CACHE BOOL "Enable OpenMPT support.") set(SRB2_CONFIG_HAVE_CURL ON CACHE BOOL "Enable curl support, used for downloading files via HTTP.") +set(SRB2_CONFIG_HAVE_THREADS ON CACHE BOOL + "Enable multithreading support.") if(${CMAKE_SYSTEM} MATCHES Windows) set(SRB2_CONFIG_HAVE_MIXERX ON CACHE BOOL "Enable SDL Mixer X support.") @@ -264,6 +269,7 @@ set(SRB2_LUA_SOURCES lua_mathlib.c lua_mobjlib.c lua_playerlib.c + lua_polyobjlib.c lua_script.c lua_skinlib.c lua_thinkerlib.c @@ -471,6 +477,12 @@ if(${SRB2_CONFIG_HAVE_CURL}) endif() endif() +if(${SRB2_CONFIG_HAVE_THREADS}) + set(SRB2_HAVE_THREADS ON) + set(SRB2_CORE_HEADERS ${SRB2_CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/i_threads.h) + add_definitions(-DHAVE_THREADS) +endif() + if(${SRB2_CONFIG_HWRENDER}) add_definitions(-DHWRENDER) set(SRB2_HWRENDER_SOURCES diff --git a/src/Makefile b/src/Makefile index 8efab2803..62a5e92f2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -56,10 +56,10 @@ # 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 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' @@ -80,6 +80,58 @@ # ############################################################################# +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 + WINDOWSHELL=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.pk3 \ @@ -119,7 +171,6 @@ NOPNG=1 NOZLIB=1 NONET=1 NOHW=1 -NOHS=1 NOASM=1 NOIPX=1 EXENAME?=srb2dummy @@ -141,7 +192,6 @@ endif ifdef PANDORA NONX86=1 NOHW=1 -NOHS=1 endif ifndef NOOPENMPT @@ -223,13 +273,6 @@ else $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o endif -ifdef NOHS - OPTS+=-DNOHS -else - OPTS+=-DHW3SOUND - OBJS+=$(OBJDIR)/hw3sound.o -endif - OPTS += -DCOMPVERSION ifndef NONX86 @@ -443,6 +486,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/m_fixed.o \ $(OBJDIR)/m_menu.o \ $(OBJDIR)/m_misc.o \ + $(OBJDIR)/m_perfstats.o \ $(OBJDIR)/m_random.o \ $(OBJDIR)/m_queue.o \ $(OBJDIR)/info.o \ @@ -474,7 +518,8 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_sky.o \ $(OBJDIR)/r_splats.o \ $(OBJDIR)/r_things.o \ - $(OBJDIR)/r_patch.o \ + $(OBJDIR)/r_textures.o \ + $(OBJDIR)/r_picformats.o \ $(OBJDIR)/r_portal.o \ $(OBJDIR)/screen.o \ $(OBJDIR)/taglist.o \ @@ -563,8 +608,9 @@ ifndef VALGRIND ifndef NOOBJDUMP @echo Dumping debugging info $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(EXENAME) > $(BIN)/$(DBGNAME).txt +ifdef WINDOWSHELL -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt -ifndef WINDOWSHELL +else -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt endif endif @@ -584,8 +630,9 @@ endif reobjdump: @echo Redumping debugging info $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(DBGNAME) > $(BIN)/$(DBGNAME).txt +ifdef WINDOWSHELL -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt -ifndef WINDOWSHELL +else -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt endif @@ -751,19 +798,6 @@ $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.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/Makefile.cfg b/src/Makefile.cfg index 019b8c8d4..db7230bb4 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 @@ -208,10 +210,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 diff --git a/src/android/i_cdmus.c b/src/android/i_cdmus.c index 426bc5dc9..12063745b 100644 --- a/src/android/i_cdmus.c +++ b/src/android/i_cdmus.c @@ -8,8 +8,8 @@ UINT8 cdaudio_started = 0; -consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = CVAR_INIT ("cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL); +consvar_t cdUpdate = CVAR_INIT ("cd_update","1",CV_SAVE, NULL, NULL); void I_InitCD(void){} diff --git a/src/android/i_video.c b/src/android/i_video.c index 1909cd71a..18f92955a 100644 --- a/src/android/i_video.c +++ b/src/android/i_video.c @@ -16,7 +16,7 @@ boolean allow_fullscreen = false; -consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL); void I_StartupGraphics(void){} void I_ShutdownGraphics(void){} diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index 12ea064b4..eae95ba3a 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -47,5 +47,6 @@ OBJS:=$(OBJS) \ $(OBJDIR)/lua_skinlib.o \ $(OBJDIR)/lua_thinkerlib.o \ $(OBJDIR)/lua_maplib.o \ + $(OBJDIR)/lua_polyobjlib.o \ $(OBJDIR)/lua_blockmaplib.o \ $(OBJDIR)/lua_hudlib.o diff --git a/src/blua/lstrlib.c b/src/blua/lstrlib.c index 297504e95..af933d25a 100644 --- a/src/blua/lstrlib.c +++ b/src/blua/lstrlib.c @@ -19,6 +19,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "../m_fixed.h" /* macro to `unsign' a character */ #define uchar(c) ((unsigned char)(c)) @@ -790,7 +791,7 @@ static int str_format (lua_State *L) { case 'e': case 'E': case 'f': case 'g': case 'G': { lua_Number n = luaL_checknumber(L, arg); - sprintf(buff, form, (double)n); + sprintf(buff, form, (double)n / FRACUNIT); break; } case 'q': { 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)); diff --git a/src/command.c b/src/command.c index 0a46839f3..58434ef89 100644 --- a/src/command.c +++ b/src/command.c @@ -79,7 +79,7 @@ CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}}; // First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20) // Also set CV_HIDEN during runtime, after config is loaded static boolean execversion_enabled = false; -consvar_t cv_execversion = {"execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_execversion = CVAR_INIT ("execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion); // for default joyaxis detection static boolean joyaxis_default = false; @@ -165,6 +165,8 @@ void COM_BufAddTextEx(const char *ptext, int flags) */ void COM_BufInsertTextEx(const char *ptext, int flags) { + const INT32 old_wait = com_wait; + char *temp = NULL; size_t templen; @@ -176,10 +178,14 @@ void COM_BufInsertTextEx(const char *ptext, int flags) VS_Clear(&com_text); } + com_wait = 0; + // add the entire text of the file (or alias) COM_BufAddTextEx(ptext, flags); COM_BufExecute(); // do it right away + com_wait += old_wait; + // add the copied off data if (templen) { @@ -560,7 +566,7 @@ static boolean COM_Exists(const char *com_name) * \param partial The partial name of the command (potentially). * \param skips Number of commands to skip. * \return The complete command name, or NULL. - * \sa CV_CompleteVar + * \sa CV_CompleteAlias, CV_CompleteVar */ const char *COM_CompleteCommand(const char *partial, INT32 skips) { @@ -581,6 +587,32 @@ const char *COM_CompleteCommand(const char *partial, INT32 skips) return NULL; } +/** Completes the name of an alias. + * + * \param partial The partial name of the alias (potentially). + * \param skips Number of aliases to skip. + * \return The complete alias name, or NULL. + * \sa CV_CompleteCommand, CV_CompleteVar + */ +const char *COM_CompleteAlias(const char *partial, INT32 skips) +{ + cmdalias_t *a; + size_t len; + + len = strlen(partial); + + if (!len) + return NULL; + + // check functions + for (a = com_alias; a; a = a->next) + if (!strncmp(partial, a->name, len)) + if (!skips--) + return a->name; + + return NULL; +} + /** Parses a single line of text into arguments and tries to execute it. * The text can come from the command buffer, a remote client, or stdin. * @@ -875,6 +907,9 @@ static void COM_Help_f(void) CONS_Printf(" Current value: %s\n", cvar->string); else CONS_Printf(" Current value: %d\n", cvar->value); + + if (cvar->revert.v.string != NULL && strcmp(cvar->revert.v.string, cvar->string) != 0) + CONS_Printf(" Value before netgame: %s\n", cvar->revert.v.string); } else { @@ -1201,7 +1236,7 @@ static consvar_t *CV_FindNetVar(UINT16 netid) { consvar_t *cvar; - if (netid >= consvar_number_of_netids) + if (netid > consvar_number_of_netids) return NULL; for (cvar = consvar_vars; cvar; cvar = cvar->next) @@ -1262,12 +1297,12 @@ void CV_RegisterVar(consvar_t *variable) // check net variables if (variable->flags & CV_NETVAR) { - variable->netid = consvar_number_of_netids++; - /* in case of overflow... */ - if (variable->netid > consvar_number_of_netids) + if (consvar_number_of_netids == UINT16_MAX) I_Error("Way too many netvars"); + variable->netid = ++consvar_number_of_netids; + #ifdef OLD22DEMOCOMPAT CV_RegisterOldDemoVar(variable); #endif @@ -1280,6 +1315,7 @@ void CV_RegisterVar(consvar_t *variable) consvar_vars = variable; } variable->string = variable->zstring = NULL; + memset(&variable->revert, 0, sizeof variable->revert); variable->changed = 0; // new variable has not been modified by the user #ifdef PARANOIA @@ -1321,7 +1357,7 @@ static const char *CV_StringValue(const char *var_name) * \param partial The partial name of the variable (potentially). * \param skips Number of variables to skip. * \return The complete variable name, or NULL. - * \sa COM_CompleteCommand + * \sa COM_CompleteCommand, CV_CompleteAlias */ const char *CV_CompleteVar(char *partial, INT32 skips) { @@ -1392,6 +1428,18 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) for (i = MAXVAL+1; var->PossibleValue[i].strvalue; i++) if (v == var->PossibleValue[i].value || !stricmp(var->PossibleValue[i].strvalue, valstr)) { + if (client && execversion_enabled) + { + if (var->revert.allocated) + { + Z_Free(var->revert.v.string); + } + + var->revert.v.const_munge = var->PossibleValue[i].strvalue; + + return; + } + var->value = var->PossibleValue[i].value; var->string = var->PossibleValue[i].strvalue; goto finish; @@ -1452,12 +1500,36 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) // ...or not. goto badinput; found: + if (client && execversion_enabled) + { + if (var->revert.allocated) + { + Z_Free(var->revert.v.string); + } + + var->revert.v.const_munge = var->PossibleValue[i].strvalue; + + return; + } + var->value = var->PossibleValue[i].value; var->string = var->PossibleValue[i].strvalue; goto finish; } } + if (client && execversion_enabled) + { + if (var->revert.allocated) + { + Z_Free(var->revert.v.string); + } + + var->revert.v.string = Z_StrDup(valstr); + + return; + } + // free the old value string Z_Free(var->zstring); @@ -1676,8 +1748,19 @@ static void CV_LoadVars(UINT8 **p, serverloading = true; for (cvar = consvar_vars; cvar; cvar = cvar->next) + { if (cvar->flags & CV_NETVAR) + { + if (client && cvar->revert.v.string == NULL) + { + cvar->revert.v.const_munge = cvar->string; + cvar->revert.allocated = ( cvar->zstring != NULL ); + cvar->zstring = NULL;/* don't free this */ + } + Setvalue(cvar, cvar->defaultvalue, true); + } + } count = READUINT16(*p); while (count--) @@ -1691,6 +1774,26 @@ static void CV_LoadVars(UINT8 **p, serverloading = false; } +void CV_RevertNetVars(void) +{ + consvar_t * cvar; + + for (cvar = consvar_vars; cvar; cvar = cvar->next) + { + if (cvar->revert.v.string != NULL) + { + Setvalue(cvar, cvar->revert.v.string, false); + + if (cvar->revert.allocated) + { + Z_Free(cvar->revert.v.string); + } + + cvar->revert.v.string = NULL; + } + } +} + void CV_LoadNetVars(UINT8 **p) { CV_LoadVars(p, ReadNetVar); @@ -1764,6 +1867,14 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth) // send the value of the variable UINT8 buf[128]; UINT8 *p = buf; + + // Loading from a config in a netgame? Set revert value. + if (client && execversion_enabled) + { + Setvalue(var, value, true); + return; + } + if (!(server || (addedtogame && IsPlayerAdmin(consoleplayer)))) { CONS_Printf(M_GetText("Only the server or admin can change: %s %s\n"), var->name, var->string); @@ -2297,18 +2408,43 @@ void CV_SaveVariables(FILE *f) { char stringtowrite[MAXTEXTCMD+1]; - // Silly hack for Min/Max vars - if (!strcmp(cvar->string, "MAX") || !strcmp(cvar->string, "MIN")) + const char * string; + + if (cvar->revert.v.string != NULL) { - if (cvar->flags & CV_FLOAT) - sprintf(stringtowrite, "%f", FIXED_TO_FLOAT(cvar->value)); - else - sprintf(stringtowrite, "%d", cvar->value); + string = cvar->revert.v.string; } else - strcpy(stringtowrite, cvar->string); + { + string = cvar->string; + } - fprintf(f, "%s \"%s\"\n", cvar->name, stringtowrite); + // Silly hack for Min/Max vars +#define MINVAL 0 +#define MAXVAL 1 + if ( + cvar->PossibleValue != NULL && + cvar->PossibleValue[0].strvalue && + stricmp(cvar->PossibleValue[0].strvalue, "MIN") == 0 + ){ // bounded cvar + int which = stricmp(string, "MAX") == 0; + + if (which || stricmp(string, "MIN") == 0) + { + INT32 value = cvar->PossibleValue[which].value; + + if (cvar->flags & CV_FLOAT) + sprintf(stringtowrite, "%f", FIXED_TO_FLOAT(value)); + else + sprintf(stringtowrite, "%d", value); + + string = stringtowrite; + } + } +#undef MINVAL +#undef MAXVAL + + fprintf(f, "%s \"%s\"\n", cvar->name, string); } } @@ -2370,15 +2506,6 @@ skipwhite: } } - // parse single characters - if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'') - { - com_token[len] = c; - len++; - com_token[len] = 0; - return data + 1; - } - // parse a regular word do { @@ -2398,8 +2525,6 @@ skipwhite: len++; c = *data; } - if (c == '{' || c == '}' || c == ')'|| c == '(' || c == '\'') - break; } while (c > 32); com_token[len] = 0; diff --git a/src/command.h b/src/command.h index b39153a65..ea5593395 100644 --- a/src/command.h +++ b/src/command.h @@ -49,6 +49,8 @@ size_t COM_FirstOption(void); // match existing command or NULL const char *COM_CompleteCommand(const char *partial, INT32 skips); +const char *COM_CompleteAlias(const char *partial, INT32 skips); + // insert at queu (at end of other command) #define COM_BufAddText(s) COM_BufAddTextEx(s, 0) void COM_BufAddTextEx(const char *btext, int flags); @@ -138,12 +140,26 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL const char *string; // value in string char *zstring; // Either NULL or same as string. // If non-NULL, must be Z_Free'd later. + struct + { + char allocated; // whether to Z_Free + union + { + char * string; + const char * const_munge; + } v; + } revert; // value of netvar before joining netgame + UINT16 netid; // used internaly : netid for send end receive // used only with CV_NETVAR char changed; // has variable been changed by the user? 0 = no, 1 = yes struct consvar_s *next; } consvar_t; +/* name, defaultvalue, flags, PossibleValue, func */ +#define CVAR_INIT( ... ) \ +{ __VA_ARGS__, 0, NULL, NULL, {0}, 0U, (char)0, NULL } + #ifdef OLD22DEMOCOMPAT typedef struct old_demo_var old_demo_var_t; @@ -202,6 +218,9 @@ void CV_SaveVars(UINT8 **p, boolean in_demo); #define CV_SaveNetVars(p) CV_SaveVars(p, false) void CV_LoadNetVars(UINT8 **p); +// then revert after leaving a netgame +void CV_RevertNetVars(void); + #define CV_SaveDemoVars(p) CV_SaveVars(p, true) void CV_LoadDemoVars(UINT8 **p); diff --git a/src/config.h.in b/src/config.h.in index 595bea7b3..a6f43a7d7 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -32,12 +32,14 @@ * Last updated 2020 / 05 / 11 - v2.2.4 - patch.pk3 * Last updated 2020 / 07 / 07 - v2.2.5 - player.dta & patch.pk3 * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 + * Last updated 2020 / 09 / 27 - v2.2.7 - patch.pk3 + * Last updated 2020 / 10 / 02 - v2.2.8 - patch.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" #define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "ecf00060f03c76b3e49c6ae3925b627f" +#define ASSET_HASH_PATCH_PK3 "466cdf60075262b3f5baa5e07f0999e8" #endif #endif diff --git a/src/console.c b/src/console.c index 0235b9bd4..3eee67bb8 100644 --- a/src/console.c +++ b/src/console.c @@ -124,22 +124,22 @@ static void CONS_backcolor_Change(void); static char con_buffer[CON_BUFFERSIZE]; // how many seconds the hud messages lasts on the screen -static consvar_t cons_msgtimeout = {"con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cons_msgtimeout = CVAR_INIT ("con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL); // number of lines displayed on the HUD -static consvar_t cons_hudlines = {"con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change); // number of lines console move per frame // (con_speed needs a limit, apparently) static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}}; -static consvar_t cons_speed = {"con_speed", "8", CV_SAVE, speed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cons_speed = CVAR_INIT ("con_speed", "8", CV_SAVE, speed_cons_t, NULL); // percentage of screen height to use for console -static consvar_t cons_height = {"con_height", "50", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cons_height = CVAR_INIT ("con_height", "50", CV_SAVE, CV_Unsigned, NULL); static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}}; // whether to use console background picture, or translucent mode -static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cons_backpic = CVAR_INIT ("con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL); static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"}, {3, "Brown"}, {4, "Pink"}, {5, "Raspberry"}, @@ -151,7 +151,7 @@ static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, { {0, NULL}}; -consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cons_backcolor = CVAR_INIT ("con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change); static void CON_Print(char *msg); @@ -870,9 +870,14 @@ boolean CON_Responder(event_t *ev) // sequential completions a la 4dos static char completion[80]; - static INT32 comskips, varskips; - const char *cmd = ""; + static INT32 skips; + + static INT32 com_skips; + static INT32 var_skips; + static INT32 alias_skips; + + const char *cmd = NULL; INT32 key; if (chat_on) @@ -1007,7 +1012,6 @@ boolean CON_Responder(event_t *ev) if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' ')) return true; strcpy(completion, inputlines[inputline]); - comskips = varskips = 0; } len = strlen(completion); @@ -1023,6 +1027,14 @@ boolean CON_Responder(event_t *ev) CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len); if (i == 0) CONS_Printf(" (none)\n"); + //and finally aliases + CONS_Printf("Aliases:\n"); + for (i = 0, cmd = COM_CompleteAlias(completion, i); cmd; cmd = COM_CompleteAlias(completion, ++i)) + CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len); + if (i == 0) CONS_Printf(" (none)\n"); + + completion[0] = 0; + return true; } // --- @@ -1091,43 +1103,64 @@ boolean CON_Responder(event_t *ev) if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' ')) return true; strcpy(completion, inputlines[inputline]); - comskips = varskips = 0; + skips = 0; + com_skips = 0; + var_skips = 0; + alias_skips = 0; } else { if (shiftdown) { - if (comskips < 0) - { - if (--varskips < 0) - comskips = -comskips - 2; - } - else if (comskips > 0) comskips--; + if (skips > 0) + skips--; } else { - if (comskips < 0) varskips++; - else comskips++; + skips++; } } - if (comskips >= 0) + if (skips <= com_skips) { - cmd = COM_CompleteCommand(completion, comskips); - if (!cmd) // dirty: make sure if comskips is zero, to have a neg value - comskips = -comskips - 1; + cmd = COM_CompleteCommand(completion, skips); + + if (cmd && skips == com_skips) + { + com_skips ++; + var_skips ++; + alias_skips++; + } + } + + if (!cmd && skips <= var_skips) + { + cmd = CV_CompleteVar(completion, skips - com_skips); + + if (cmd && skips == var_skips) + { + var_skips ++; + alias_skips++; + } + } + + if (!cmd && skips <= alias_skips) + { + cmd = COM_CompleteAlias(completion, skips - var_skips); + + if (cmd && skips == alias_skips) + { + alias_skips++; + } } - if (comskips < 0) - cmd = CV_CompleteVar(completion, varskips); if (cmd) + { CON_InputSetString(va("%s ", cmd)); + } else { - if (comskips > 0) - comskips--; - else if (varskips > 0) - varskips--; + skips--; } return true; @@ -1677,8 +1710,12 @@ static void CON_DrawBackpic(void) lumpnum_t piclump; int x, w, h; - // Get the lumpnum for CONSBACK, or fallback into MISSING. - piclump = W_CheckNumForName("CONSBACK"); + // Get the lumpnum for CONSBACK, STARTUP (Only during game startup) or fallback into MISSING. + if (con_startup) + piclump = W_CheckNumForName("STARTUP"); + else + piclump = W_CheckNumForName("CONSBACK"); + if (piclump == LUMPERROR) piclump = W_GetNumForName("MISSING"); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7ccb4ef92..e314d419f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -44,6 +44,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "md5.h" +#include "m_perfstats.h" #ifndef NONET // cl loading screen @@ -157,10 +158,10 @@ ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; -consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; -consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { @@ -1269,19 +1270,25 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir) return 12 + 5 - dir; } -static void Snake_FindFreeSlot(UINT8 *x, UINT8 *y, UINT8 headx, UINT8 heady) +static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) { + UINT8 x, y; UINT16 i; do { - *x = M_RandomKey(SNAKE_NUM_BLOCKS_X); - *y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + x = M_RandomKey(SNAKE_NUM_BLOCKS_X); + y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); for (i = 0; i < snake->snakelength; i++) - if (*x == snake->snakex[i] && *y == snake->snakey[i]) + if (x == snake->snakex[i] && y == snake->snakey[i]) break; - } while (i < snake->snakelength || (*x == headx && *y == heady)); + } while (i < snake->snakelength || (x == headx && y == heady) + || (x == snake->applex && y == snake->appley) + || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); + + *freex = x; + *freey = y; } static void Snake_Handle(void) @@ -1412,7 +1419,7 @@ static void Snake_Handle(void) // Check collision with apple if (x == snake->applex && y == snake->appley) { - if (snake->snakelength + 1 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) { snake->snakelength++; snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; @@ -2113,7 +2120,7 @@ static void SV_SendSaveGame(INT32 node) #ifdef DUMPCONSISTENCY #define TMPSAVENAME "badmath.sav" -static consvar_t cv_dumpconsistency = {"dumpconsistency", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); static void SV_SavedGame(void) { @@ -2306,7 +2313,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; @@ -2351,7 +2358,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) { @@ -2388,9 +2395,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); @@ -2407,9 +2413,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 */ @@ -2436,6 +2445,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) } #endif } +#endif/*MASTERSERVER*/ } #endif // ifndef NONET @@ -2510,11 +2520,11 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2 before connecting.\n\n" + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2 will automatically add\n" + "everything you need when you join.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; @@ -2563,7 +2573,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; @@ -3159,6 +3168,8 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t 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 @@ -3198,6 +3209,7 @@ void CL_Reset(void) doomcom->numslots = 1; SV_StopServer(); SV_ResetServer(); + CV_RevertNetVars(); // make sure we don't leave any fileneeded gunk over from a failed join fileneedednum = 0; @@ -3665,29 +3677,29 @@ 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_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); -consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; -consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done +consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "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 = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_joindelay = {"joindelay", "10", CV_SAVE, joindelay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_rejointimeout = {"rejointimeout", "Off", CV_SAVE|CV_FLOAT, rejointimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "Off", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; -consvar_t cv_resynchattempts = {"resynchattempts", "10", 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 }; +consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); +consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); // max file size to send to a player (in kilobytes) static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = {"maxsend", "4096", CV_SAVE, maxsend_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_noticedownload = {"noticedownload", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); +consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); // Speed of file downloading (in packets per tic) static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); static void Got_AddPlayer(UINT8 **p, INT32 playernum); @@ -3775,6 +3787,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; @@ -3840,8 +3854,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]) { @@ -4105,8 +4121,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 @@ -5427,11 +5445,15 @@ void TryRunTics(tic_t realtics) { DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + ps_tictime = I_GetTimeMicros(); + G_Ticker((gametic % NEWTICRATERATIO) == 0); ExtraDataTicker(); gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); + ps_tictime = I_GetTimeMicros() - ps_tictime; + // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) break; @@ -5568,7 +5590,9 @@ void NetUpdate(void) // 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) { @@ -5587,8 +5611,13 @@ void NetUpdate(void) firstticstosend = gametic; for (i = 0; i < MAXNETNODES; i++) if (nodeingame[i] && nettics[i] < firstticstosend) + { firstticstosend = nettics[i]; + if (maketic + 1 >= nettics[i] + BACKUPTICS) + Net_ConnectionTimeout(i); + } + // Don't erase tics not acknowledged counts = realtics; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index ce8d87adb..adc8a7cc9 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -14,6 +14,7 @@ #define __D_CLISRV__ #include "d_ticcmd.h" +#include "d_net.h" #include "d_netcmd.h" #include "d_net.h" #include "tables.h" @@ -484,7 +485,7 @@ typedef struct #pragma pack() #endif -#define MAXSERVERLIST 64 // Depends only on the display +#define MAXSERVERLIST (MAXNETNODES-1) typedef struct { SINT8 node; @@ -526,6 +527,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_main.c b/src/d_main.c index 25209de68..ce1331fe3 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -67,6 +67,7 @@ #include "keys.h" #include "filesrch.h" // refreshdirmenu, mainwadstally #include "g_input.h" // tutorial mode control scheming +#include "m_perfstats.h" #ifdef CMAKECONFIG #include "config.h" @@ -99,6 +100,7 @@ UINT8 window_notinfocus = false; // DEMO LOOP // static char *startupwadfiles[MAX_WADFILES]; +static char *startuppwads[MAX_WADFILES]; boolean devparm = false; // started game with -devparm @@ -434,7 +436,7 @@ static void D_Display(void) if (!automapactive && !dedicated && cv_renderview.value) { - rs_rendercalltime = I_GetTimeMicros(); + ps_rendercalltime = I_GetTimeMicros(); if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) { topleft = screens[0] + viewwindowy*vid.width + viewwindowx; @@ -481,7 +483,7 @@ static void D_Display(void) if (postimgtype2) V_DoPostProcessor(1, postimgtype2, postimgparam2); } - rs_rendercalltime = I_GetTimeMicros() - rs_rendercalltime; + ps_rendercalltime = I_GetTimeMicros() - ps_rendercalltime; } if (lastdraw) @@ -495,6 +497,8 @@ static void D_Display(void) lastdraw = false; } + ps_uitime = I_GetTimeMicros(); + if (gamestate == GS_LEVEL) { ST_Drawer(); @@ -504,6 +508,10 @@ static void D_Display(void) else F_TitleScreenDrawer(); } + else + { + ps_uitime = I_GetTimeMicros(); + } } // change gamma if needed @@ -544,6 +552,8 @@ static void D_Display(void) CON_Drawer(); + ps_uitime = I_GetTimeMicros() - ps_uitime; + // // wipe update // @@ -623,80 +633,14 @@ static void D_Display(void) V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s); } - if (cv_renderstats.value) + if (cv_perfstats.value) { - char s[50]; - int frametime = I_GetTimeMicros() - rs_prevframetime; - int divisor = 1; - rs_prevframetime = I_GetTimeMicros(); - - if (rs_rendercalltime > 10000) divisor = 1000; - - snprintf(s, sizeof s - 1, "ft %d", frametime / divisor); - V_DrawThinString(30, 10, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "rtot %d", rs_rendercalltime / divisor); - V_DrawThinString(30, 20, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "bsp %d", rs_bsptime / divisor); - V_DrawThinString(30, 30, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "nbsp %d", rs_numbspcalls); - V_DrawThinString(80, 10, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "nspr %d", rs_numsprites); - V_DrawThinString(80, 20, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "nnod %d", rs_numdrawnodes); - V_DrawThinString(80, 30, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "npob %d", rs_numpolyobjects); - V_DrawThinString(80, 40, V_MONOSPACE | V_BLUEMAP, s); - if (rendermode == render_opengl) // OpenGL specific stats - { - snprintf(s, sizeof s - 1, "nsrt %d", rs_hw_nodesorttime / divisor); - V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "ndrw %d", rs_hw_nodedrawtime / divisor); - V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "ssrt %d", rs_hw_spritesorttime / divisor); - V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "sdrw %d", rs_hw_spritedrawtime / divisor); - V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); - V_DrawThinString(30, 80, V_MONOSPACE | V_YELLOWMAP, s); - if (cv_glbatching.value) - { - snprintf(s, sizeof s - 1, "bsrt %d", rs_hw_batchsorttime / divisor); - V_DrawThinString(80, 55, V_MONOSPACE | V_REDMAP, s); - snprintf(s, sizeof s - 1, "bdrw %d", rs_hw_batchdrawtime / divisor); - V_DrawThinString(80, 65, V_MONOSPACE | V_REDMAP, s); - - snprintf(s, sizeof s - 1, "npol %d", rs_hw_numpolys); - V_DrawThinString(130, 10, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "ndc %d", rs_hw_numcalls); - V_DrawThinString(130, 20, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "nshd %d", rs_hw_numshaders); - V_DrawThinString(130, 30, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "nvrt %d", rs_hw_numverts); - V_DrawThinString(130, 40, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "ntex %d", rs_hw_numtextures); - V_DrawThinString(185, 10, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "npf %d", rs_hw_numpolyflags); - V_DrawThinString(185, 20, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "ncol %d", rs_hw_numcolors); - V_DrawThinString(185, 30, V_MONOSPACE | V_PURPLEMAP, s); - } - } - else // software specific stats - { - snprintf(s, sizeof s - 1, "prtl %d", rs_sw_portaltime / divisor); - V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "plns %d", rs_sw_planetime / divisor); - V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "mskd %d", rs_sw_maskedtime / divisor); - V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); - V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s); - } + M_DrawPerfStats(); } - rs_swaptime = I_GetTimeMicros(); + ps_swaptime = I_GetTimeMicros(); I_FinishUpdate(); // page flip or blit buffer - rs_swaptime = I_GetTimeMicros() - rs_swaptime; + ps_swaptime = I_GetTimeMicros() - ps_swaptime; } needpatchflush = false; @@ -729,6 +673,7 @@ tic_t rendergametic; void D_SRB2Loop(void) { tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; + static lumpnum_t gstartuplumpnum; if (dedicated) server = true; @@ -768,7 +713,12 @@ void D_SRB2Loop(void) */ /* Smells like a hack... Don't fade Sonic's ass into the title screen. */ if (gamestate != GS_TITLESCREEN) - V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_PATCH)); + { + gstartuplumpnum = W_CheckNumForName("STARTUP"); + if (gstartuplumpnum == LUMPERROR) + gstartuplumpnum = W_GetNumForName("MISSING"); + V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(gstartuplumpnum, PU_PATCH)); + } for (;;) { @@ -953,12 +903,12 @@ void D_StartTitle(void) // // D_AddFile // -static void D_AddFile(const char *file) +static void D_AddFile(char **list, const char *file) { size_t pnumwadfiles; char *newfile; - for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++) + for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) ; newfile = malloc(strlen(file) + 1); @@ -968,16 +918,16 @@ static void D_AddFile(const char *file) } strcpy(newfile, file); - startupwadfiles[pnumwadfiles] = newfile; + list[pnumwadfiles] = newfile; } -static inline void D_CleanFile(void) +static inline void D_CleanFile(char **list) { size_t pnumwadfiles; - for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++) + for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) { - free(startupwadfiles[pnumwadfiles]); - startupwadfiles[pnumwadfiles] = NULL; + free(list[pnumwadfiles]); + list[pnumwadfiles] = NULL; } } @@ -1062,7 +1012,7 @@ static void IdentifyVersion(void) // Load the IWAD if (srb2wad != NULL && FIL_ReadFileOK(srb2wad)) - D_AddFile(srb2wad); + D_AddFile(startupwadfiles, srb2wad); else I_Error("srb2.pk3 not found! Expected in %s, ss file: %s\n", srb2waddir, srb2wad); @@ -1073,14 +1023,14 @@ static void IdentifyVersion(void) // checking in D_SRB2Main // Add the maps - D_AddFile(va(pandf,srb2waddir,"zones.pk3")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir,"zones.pk3")); // Add the players - D_AddFile(va(pandf,srb2waddir, "player.dta")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir, "player.dta")); #ifdef USE_PATCH_DTA // Add our crappy patches to fix our bugs - D_AddFile(va(pandf,srb2waddir,"patch.pk3")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir,"patch.pk3")); #endif #if !defined (HAVE_SDL) || defined (HAVE_MIXER) @@ -1090,7 +1040,7 @@ static void IdentifyVersion(void) const char *musicpath = va(pandf,srb2waddir,str);\ int ms = W_VerifyNMUSlumps(musicpath); \ if (ms == 1) \ - D_AddFile(musicpath); \ + D_AddFile(startupwadfiles, musicpath); \ else if (ms == 0) \ I_Error("File "str" has been modified with non-music/sound lumps"); \ } @@ -1280,7 +1230,7 @@ void D_SRB2Main(void) { if (!W_VerifyNMUSlumps(s)) G_SetGameModified(true); - D_AddFile(s); + D_AddFile(startuppwads, s); } } } @@ -1321,8 +1271,8 @@ void D_SRB2Main(void) // load wad, including the main wad file CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n"); - W_InitMultipleFiles(startupwadfiles, mainwads); - D_CleanFile(); + W_InitMultipleFiles(startupwadfiles); + D_CleanFile(startupwadfiles); #ifndef DEVELOP // md5s last updated 22/02/20 (ddmmyy) @@ -1358,8 +1308,6 @@ void D_SRB2Main(void) // setup loading screen SCR_Startup(); - // we need the font of the console - CONS_Printf("HU_Init(): Setting up heads up display.\n"); HU_Init(); CON_Init(); @@ -1371,6 +1319,13 @@ void D_SRB2Main(void) I_RegisterSysCommands(); + CONS_Printf("W_InitMultipleFiles(): Adding extra PWADs.\n"); + W_InitMultipleFiles(startuppwads); + D_CleanFile(startuppwads); + + CONS_Printf("HU_LoadGraphics()...\n"); + HU_LoadGraphics(); + //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" @@ -1559,6 +1514,12 @@ void D_SRB2Main(void) ultimatemode = true; } + if (M_CheckParm("-splitscreen")) + { + autostart = true; + splitscreen = true; + } + // rei/miru: bootmap (Idea: starts the game on a predefined map) if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm())) { diff --git a/src/d_net.h b/src/d_net.h index ed4f66284..ea6b5d4d9 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,7 +19,9 @@ #define __D_NET__ // Max computers in a game -#define MAXNETNODES (MAXPLAYERS+4) +// 127 is probably as high as this can go, because +// SINT8 is used for nodes sometimes >:( +#define MAXNETNODES 127 #define BROADCASTADDR MAXNETNODES #define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer //#define NETSPLITSCREEN // Kart's splitscreen netgame feature diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 68b8ecfc1..87abd596a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -197,186 +197,183 @@ static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"}, static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}}; static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}}; -consvar_t cv_showinputjoy = {"showinputjoy", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", 0, CV_OnOff, NULL); #ifdef NETGAME_DEVMODE -static consvar_t cv_fishcake = {"fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_fishcake = CVAR_INIT ("fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange); #endif -static consvar_t cv_dummyconsvar = {"dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, - DummyConsvar_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, DummyConsvar_OnChange); -consvar_t cv_restrictskinchange = {"restrictskinchange", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allowteamchange = {"allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); +consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL); -consvar_t cv_startinglives = {"startinglives", "3", CV_NETVAR|CV_CHEAT, startingliveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, startingliveslimit_cons_t, NULL); static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_respawntime = {"respawndelay", "3", CV_NETVAR|CV_CHEAT, respawntime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, respawntime_cons_t, NULL); -consvar_t cv_competitionboxes = {"competitionboxes", "Mystery", CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL); #ifdef SEENAMES static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}}; -consvar_t cv_seenames = {"seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allowseenames = {"allowseenames", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0); +consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL); #endif // names -consvar_t cv_playername = {"name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_playername2 = {"name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange); +consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange); // player colors UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE; -consvar_t cv_playercolor = {"color", "Blue", CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_playercolor2 = {"color2", "Orange", CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange); +consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange); // player's skin, saved for commodity, when using a favorite skins wad.. -consvar_t cv_skin = {"skin", DEFAULTSKIN, CV_CALL|CV_NOINIT, NULL, Skin_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_skin2 = {"skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT, NULL, Skin2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT, NULL, Skin_OnChange); +consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT, NULL, Skin2_OnChange); // saved versions of the above six -consvar_t cv_defaultplayercolor = {"defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_defaultplayercolor2 = {"defaultcolor2", "Orange", CV_SAVE, Color_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_defaultskin = {"defaultskin", DEFAULTSKIN, CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_defaultskin2 = {"defaultskin2", DEFAULTSKIN2, CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL); +consvar_t cv_defaultplayercolor2 = CVAR_INIT ("defaultcolor2", "Orange", CV_SAVE, Color_cons_t, NULL); +consvar_t cv_defaultskin = CVAR_INIT ("defaultskin", DEFAULTSKIN, CV_SAVE, NULL, NULL); +consvar_t cv_defaultskin2 = CVAR_INIT ("defaultskin2", DEFAULTSKIN2, CV_SAVE, NULL, NULL); -consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); INT32 cv_debug; -consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); +consvar_t cv_usemouse2 = CVAR_INIT ("use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2); -consvar_t cv_usejoystick = {"use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, - I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_usejoystick2 = {"use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, - I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_usejoystick = CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick); +consvar_t cv_usejoystick2 = CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2); #if (defined (LJOYSTICK) || defined (HAVE_SDL)) #ifdef LJOYSTICK -consvar_t cv_joyport = {"padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_joyport2 = {"padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: for later +consvar_t cv_joyport = CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); +consvar_t cv_joyport2 = CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); //Alam: for later #endif -consvar_t cv_joyscale = {"padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_joyscale2 = {"padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale); +consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2); #else -consvar_t cv_joyscale = {"padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save -consvar_t cv_joyscale2 = {"padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save +consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save +consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save #endif #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) -consvar_t cv_mouse2port = {"mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mouse2opt = {"mouse2opt", "0", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL); +consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL); #else -consvar_t cv_mouse2port = {"mouse2port", "COM2", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "COM2", CV_SAVE, mouse2port_cons_t, NULL); #endif -consvar_t cv_matchboxes = {"matchboxes", "Normal", CV_NETVAR|CV_CHEAT, matchboxes_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_specialrings = {"specialrings", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_powerstones = {"powerstones", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_matchboxes = CVAR_INIT ("matchboxes", "Normal", CV_SAVE|CV_NETVAR|CV_CHEAT, matchboxes_cons_t, NULL); +consvar_t cv_specialrings = CVAR_INIT ("specialrings", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_powerstones = CVAR_INIT ("powerstones", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_recycler = {"tv_recycler", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_teleporters = {"tv_teleporter", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_superring = {"tv_superring", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_supersneakers = {"tv_supersneaker", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_invincibility = {"tv_invincibility", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_jumpshield = {"tv_jumpshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_watershield = {"tv_watershield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ringshield = {"tv_ringshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_forceshield = {"tv_forceshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_bombshield = {"tv_bombshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_1up = {"tv_1up", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_eggmanbox = {"tv_eggman", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_recycler = CVAR_INIT ("tv_recycler", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_teleporters = CVAR_INIT ("tv_teleporter", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_superring = CVAR_INIT ("tv_superring", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_supersneakers = CVAR_INIT ("tv_supersneaker", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_invincibility = CVAR_INIT ("tv_invincibility", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_jumpshield = CVAR_INIT ("tv_jumpshield", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_watershield = CVAR_INIT ("tv_watershield", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_ringshield = CVAR_INIT ("tv_ringshield", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_forceshield = CVAR_INIT ("tv_forceshield", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_bombshield = CVAR_INIT ("tv_bombshield", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_1up = CVAR_INIT ("tv_1up", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); +consvar_t cv_eggmanbox = CVAR_INIT ("tv_eggman", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL); -consvar_t cv_ringslinger = {"ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT, CV_YesNo, - Ringslinger_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gravity = {"gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_ringslinger = CVAR_INIT ("ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT, CV_YesNo, Ringslinger_OnChange); +consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange); -consvar_t cv_soundtest = {"soundtest", "0", CV_CALL, NULL, SoundTest_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange); static CV_PossibleValue_t minitimelimit_cons_t[] = {{15, "MIN"}, {9999, "MAX"}, {0, NULL}}; -consvar_t cv_countdowntime = {"countdowntime", "60", CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL); -consvar_t cv_touchtag = {"touchtag", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_hidetime = {"hidetime", "30", CV_NETVAR|CV_CALL, minitimelimit_cons_t, Hidetime_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_hidetime = CVAR_INIT ("hidetime", "30", CV_SAVE|CV_NETVAR|CV_CALL, minitimelimit_cons_t, Hidetime_OnChange); -consvar_t cv_autobalance = {"autobalance", "Off", CV_NETVAR|CV_CALL, CV_OnOff, AutoBalance_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_teamscramble = {"teamscramble", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_scrambleonchange = {"scrambleonchange", "Off", CV_NETVAR, teamscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, AutoBalance_OnChange); +consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange); +consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR, teamscramble_cons_t, NULL); -consvar_t cv_friendlyfire = {"friendlyfire", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_friendlyfire = CVAR_INIT ("friendlyfire", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange); // Scoring type options -consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL); -consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL); static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}}; -consvar_t cv_timetic = {"timerres", "Classic", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_timetic = CVAR_INIT ("timerres", "Classic", CV_SAVE, timetic_cons_t, NULL); static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}}; -consvar_t cv_powerupdisplay = {"powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL); static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {0, NULL}}; -consvar_t cv_pointlimit = {"pointlimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, - PointLimit_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange); static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}}; -consvar_t cv_timelimit = {"timelimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, - TimeLimit_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange); static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}}; -consvar_t cv_numlaps = {"numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, - NumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange); static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}}; -consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange); // Point and time limits for every gametype INT32 pointlimits[NUMGAMETYPES]; INT32 timelimits[NUMGAMETYPES]; // log elemental hazards -- not a netvar, is local to current player -consvar_t cv_hazardlog = {"hazardlog", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_hazardlog = CVAR_INIT ("hazardlog", "Yes", 0, CV_YesNo, NULL); -consvar_t cv_forceskin = {"forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_downloading = {"downloading", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allowexitlevel = {"allowexitlevel", "No", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange); +consvar_t cv_downloading = CVAR_INIT ("downloading", "On", 0, CV_OnOff, NULL); +consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL); -consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_killingdead = CVAR_INIT ("killingdead", "Off", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics +consvar_t cv_netstat = CVAR_INIT ("netstat", "Off", 0, CV_OnOff, NULL); // show bandwidth statistics static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; -consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange); static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; -consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_jointimeout = CVAR_INIT ("jointimeout", "350", CV_CALL|CV_SAVE|CV_NETVAR, jointimeout_cons_t, JoinTimeout_OnChange); +consvar_t cv_maxping = CVAR_INIT ("maxping", "0", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; -consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_pingtimeout = CVAR_INIT ("pingtimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL); // show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping) static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}}; -consvar_t cv_showping = {"showping", "Warning", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showping = CVAR_INIT ("showping", "Warning", CV_SAVE, showping_cons_t, NULL); // Intermission time Tails 04-19-2002 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}}; -consvar_t cv_inttime = {"inttime", "10", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR, inttime_cons_t, NULL); static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}}; -consvar_t cv_coopstarposts = {"coopstarposts", "Per-player", CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_coopstarposts = CVAR_INIT ("coopstarposts", "Per-player", CV_SAVE|CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange); static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}}; -consvar_t cv_cooplives = {"cooplives", "Avoid Game Over", CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange); static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; -consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR, advancemap_cons_t, NULL); static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; -consvar_t cv_playersforexit = {"playersforexit", "All", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR, playersforexit_cons_t, NULL); -consvar_t cv_exitmove = {"exitmove", "On", CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange); -consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", 0, CV_YesNo, NULL); -consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR, pause_cons_t, NULL); +consvar_t cv_mute = CVAR_INIT ("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}; +consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL); + +static CV_PossibleValue_t perfstats_cons_t[] = { + {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; +consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); char timedemo_name[256]; boolean timedemo_csv; @@ -865,15 +862,14 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_fullscreen); CV_RegisterVar(&cv_renderview); CV_RegisterVar(&cv_renderer); -#ifdef HWRENDER - CV_RegisterVar(&cv_newrenderer); -#endif CV_RegisterVar(&cv_scr_depth); CV_RegisterVar(&cv_scr_width); CV_RegisterVar(&cv_scr_height); CV_RegisterVar(&cv_soundtest); + CV_RegisterVar(&cv_perfstats); + // ingame object placing COM_AddCommand("objectplace", Command_ObjectPlace_f); COM_AddCommand("writethings", Command_Writethings_f); @@ -1126,6 +1122,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); } } @@ -1302,7 +1300,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); @@ -1463,8 +1466,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 % numskincolors; @@ -3241,97 +3247,130 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum) */ static void Command_Addfile(void) { - const char *fn, *p; - char buf[256]; - char *buf_p = buf; - INT32 i; - int musiconly; // W_VerifyNMUSlumps isn't boolean + size_t argc = COM_Argc(); // amount of arguments total + size_t curarg; // current argument index - if (COM_Argc() != 2) + const char *addedfiles[argc]; // list of filenames already processed + size_t numfilesadded = 0; // the amount of filenames processed + + if (argc < 2) { - CONS_Printf(M_GetText("addfile : load wad file\n")); - return; - } - else - fn = COM_Argv(1); - - // Disallow non-printing characters and semicolons. - for (i = 0; fn[i] != '\0'; i++) - if (!isprint(fn[i]) || fn[i] == ';') - return; - - musiconly = W_VerifyNMUSlumps(fn); - - if (!musiconly) - { - // ... But only so long as they contain nothing more then music and sprites. - if (netgame && !(server || IsPlayerAdmin(consoleplayer))) - { - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - return; - } - G_SetGameModified(multiplayer); - } - - // Add file on your client directly if it is trivial, or you aren't in a netgame. - if (!(netgame || multiplayer) || musiconly) - { - P_AddWadFile(fn); + CONS_Printf(M_GetText("addfile [filename2...] [...]: Load add-ons\n")); return; } - p = fn+strlen(fn); - while(--p >= fn) - if (*p == '\\' || *p == '/' || *p == ':') - break; - ++p; - - // check total packet size and no of files currently loaded - // See W_LoadWadFile in w_wad.c - if ((numwadfiles >= MAX_WADFILES) - || ((packetsizetally + nameonlylength(fn) + 22) > MAXFILENEEDED*sizeof(UINT8))) + // start at one to skip command name + for (curarg = 1; curarg < argc; curarg++) { - CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn); - return; - } + const char *fn, *p; + char buf[256]; + char *buf_p = buf; + INT32 i; + size_t ii; + int musiconly; // W_VerifyNMUSlumps isn't boolean + boolean fileadded = false; - WRITESTRINGN(buf_p,p,240); + fn = COM_Argv(curarg); - // calculate and check md5 - { - UINT8 md5sum[16]; -#ifdef NOMD5 - memset(md5sum,0,16); -#else - FILE *fhandle; - - if ((fhandle = W_OpenWadFile(&fn, true)) != NULL) + // For the amount of filenames previously processed... + for (ii = 0; ii < numfilesadded; ii++) { - tic_t t = I_GetTime(); - CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",fn); - md5_stream(fhandle, md5sum); - CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f second\n", fn, (float)(I_GetTime() - t)/TICRATE); - fclose(fhandle); - } - else // file not found - return; - - for (i = 0; i < numwadfiles; i++) - { - if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) + // If this is one of them, don't try to add it. + if (!strcmp(fn, addedfiles[ii])) { - CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), fn); - return; + fileadded = true; + break; } } -#endif - WRITEMEM(buf_p, md5sum, 16); - } - if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file - SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf); - else - SendNetXCmd(XD_ADDFILE, buf, buf_p - buf); + // If we've added this one, skip to the next one. + if (fileadded) + { + CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn); + continue; + } + + // Disallow non-printing characters and semicolons. + for (i = 0; fn[i] != '\0'; i++) + if (!isprint(fn[i]) || fn[i] == ';') + return; + + musiconly = W_VerifyNMUSlumps(fn); + + if (!musiconly) + { + // ... But only so long as they contain nothing more then music and sprites. + if (netgame && !(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + continue; + } + G_SetGameModified(multiplayer); + } + + // Add file on your client directly if it is trivial, or you aren't in a netgame. + if (!(netgame || multiplayer) || musiconly) + { + P_AddWadFile(fn); + addedfiles[numfilesadded++] = fn; + continue; + } + + p = fn+strlen(fn); + while(--p >= fn) + if (*p == '\\' || *p == '/' || *p == ':') + break; + ++p; + + // check total packet size and no of files currently loaded + // See W_LoadWadFile in w_wad.c + if ((numwadfiles >= MAX_WADFILES) + || ((packetsizetally + nameonlylength(fn) + 22) > MAXFILENEEDED*sizeof(UINT8))) + { + CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn); + return; + } + + WRITESTRINGN(buf_p,p,240); + + // calculate and check md5 + { + UINT8 md5sum[16]; +#ifdef NOMD5 + memset(md5sum,0,16); +#else + FILE *fhandle; + + if ((fhandle = W_OpenWadFile(&fn, true)) != NULL) + { + tic_t t = I_GetTime(); + CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",fn); + md5_stream(fhandle, md5sum); + CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f second\n", fn, (float)(I_GetTime() - t)/TICRATE); + fclose(fhandle); + } + else // file not found + continue; + + for (i = 0; i < numwadfiles; i++) + { + if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), fn); + continue; + } + } +#endif + WRITEMEM(buf_p, md5sum, 16); + } + + addedfiles[numfilesadded++] = fn; + + if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file + SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf); + else + SendNetXCmd(XD_ADDFILE, buf, buf_p - buf); + } } static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 897c28968..841f71acd 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -114,6 +114,8 @@ extern consvar_t cv_skipmapcheck; extern consvar_t cv_sleep; +extern consvar_t cv_perfstats; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; diff --git a/src/d_player.h b/src/d_player.h index bd33ebae5..eb0372832 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -51,6 +51,7 @@ typedef enum SF_NONIGHTSSUPER = 1<<15, // Disable super colors for NiGHTS (if you have SF_SUPER) SF_NOSUPERSPRITES = 1<<16, // Don't use super sprites while super SF_NOSUPERJUMPBOOST = 1<<17, // Disable the jump boost given while super (i.e. Knuckles) + SF_CANBUSTWALLS = 1<<18, // Can naturally bust walls on contact? (i.e. Knuckles) // free up to and including 1<<31 } skinflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index 4c7ffaa96..ca013a25d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -29,8 +29,9 @@ #include "p_local.h" // for var1 and var2, and some constants #include "p_setup.h" #include "r_data.h" +#include "r_textures.h" #include "r_draw.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" // R_Char2Frame #include "r_sky.h" #include "fastcmp.h" @@ -9516,6 +9517,7 @@ struct { {"RING_DIST",RING_DIST}, {"PUSHACCEL",PUSHACCEL}, {"MODID",MODID}, // I don't know, I just thought it would be cool for a wad to potentially know what mod it was loaded into. + {"MODVERSION",MODVERSION}, // or what version of the mod this is. {"CODEBASE",CODEBASE}, // or what release of SRB2 this is. {"NEWTICRATE",NEWTICRATE}, // TICRATE*NEWTICRATERATIO {"NEWTICRATERATIO",NEWTICRATERATIO}, @@ -9699,6 +9701,7 @@ struct { {"SF_NONIGHTSSUPER",SF_NONIGHTSSUPER}, {"SF_NOSUPERSPRITES",SF_NOSUPERSPRITES}, {"SF_NOSUPERJUMPBOOST",SF_NOSUPERJUMPBOOST}, + {"SF_CANBUSTWALLS",SF_CANBUSTWALLS}, // Dashmode constants {"DASHMODE_THRESHOLD",DASHMODE_THRESHOLD}, @@ -9906,6 +9909,25 @@ struct { {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. + // PolyObject flags + {"POF_CLIPLINES",POF_CLIPLINES}, ///< Test against lines for collision + {"POF_CLIPPLANES",POF_CLIPPLANES}, ///< Test against tops and bottoms for collision + {"POF_SOLID",POF_SOLID}, ///< Clips things. + {"POF_TESTHEIGHT",POF_TESTHEIGHT}, ///< Test line collision with heights + {"POF_RENDERSIDES",POF_RENDERSIDES}, ///< Renders the sides. + {"POF_RENDERTOP",POF_RENDERTOP}, ///< Renders the top. + {"POF_RENDERBOTTOM",POF_RENDERBOTTOM}, ///< Renders the bottom. + {"POF_RENDERPLANES",POF_RENDERPLANES}, ///< Renders top and bottom. + {"POF_RENDERALL",POF_RENDERALL}, ///< Renders everything. + {"POF_INVERT",POF_INVERT}, ///< Inverts collision (like a cage). + {"POF_INVERTPLANES",POF_INVERTPLANES}, ///< Render inside planes. + {"POF_INVERTPLANESONLY",POF_INVERTPLANESONLY}, ///< Only render inside planes. + {"POF_PUSHABLESTOP",POF_PUSHABLESTOP}, ///< Pushables will stop movement. + {"POF_LDEXEC",POF_LDEXEC}, ///< This PO triggers a linedef executor. + {"POF_ONESIDE",POF_ONESIDE}, ///< Only use the first side of the linedef. + {"POF_NOSPECIALS",POF_NOSPECIALS}, ///< Don't apply sector specials. + {"POF_SPLAT",POF_SPLAT}, ///< Use splat flat renderer (treat cyan pixels as invisible). + #ifdef HAVE_LUA_SEGS // Node flags {"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf. @@ -10844,7 +10866,6 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(p, "USEDOWN")) // Remove case when 2.3 nears release... { - LUA_Deprecated(L, "PF_USEDOWN", "PF_SPINDOWN"); lua_pushinteger(L, (lua_Integer)PF_SPINDOWN); return 1; } @@ -11116,7 +11137,6 @@ static inline int lib_getenum(lua_State *L) if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release... { - LUA_Deprecated(L, "BT_USE", "BT_SPIN"); lua_pushinteger(L, (lua_Integer)BT_SPIN); return 1; } diff --git a/src/doomdef.h b/src/doomdef.h index 31dd2bcda..b9ee1ce5f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -29,7 +29,6 @@ // Use Mixer interface? #ifdef HAVE_MIXER #define SOUND SOUND_MIXER - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -45,7 +44,6 @@ // Use FMOD? #ifdef HAVE_FMOD #define SOUND SOUND_FMOD - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -62,10 +60,6 @@ #if !defined (HWRENDER) && !defined (NOHW) #define HWRENDER #endif -// judgecutor: 3D sound support -#if !defined(HW3SOUND) && !defined (NOHS) -#define HW3SOUND -#endif #endif #ifdef _WIN32 @@ -135,8 +129,15 @@ extern char logfilename[1024]; #define VERSIONSTRING "Development EXE" // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. +// VERSIONSTRING_RC is for the resource-definition script used by windows builds +#else +#ifdef BETAVERSION +#define VERSIONSTRING "v"SRB2VERSION" "BETAVERSION +#define VERSIONSTRING_RC SRB2VERSION " " BETAVERSION "\0" #else #define VERSIONSTRING "v"SRB2VERSION +#define VERSIONSTRING_RC SRB2VERSION "\0" +#endif // Hey! If you change this, add 1 to the MODVERSION below! // Otherwise we can't force updates! #endif @@ -159,7 +160,9 @@ extern char logfilename[1024]; // the other options the same. // Comment out this line to completely disable update alerts (recommended for testing, but not for release) +#ifndef BETAVERSION #define UPDATE_ALERT +#endif // The string used in the alert that pops up in the event of an update being available. // Please change to apply to your modification (we don't want everyone asking where your mod is on SRB2.org!). @@ -644,4 +647,10 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Maintain compatibility with older 2.2 demos #define OLD22DEMOCOMPAT +#if defined (HAVE_CURL) && ! defined (NONET) +#define MASTERSERVER +#else +#undef UPDATE_ALERT +#endif + #endif // __DOOMDEF__ diff --git a/src/doomtype.h b/src/doomtype.h index 0aa3e23e0..4e13ba96d 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -371,4 +371,12 @@ typedef UINT32 tic_t; #define WSTRING2(s) L ## s #define WSTRING(s) WSTRING2 (s) +/* +A hack by Monster Iestyn: Return a pointer to a field of +a struct from a pointer to another field in the struct. +Needed for some lua shenanigans. +*/ +#define FIELDFROM( type, field, have, want ) \ + (void *)((intptr_t)(field) - offsetof (type, have) + offsetof (type, want)) + #endif //__DOOMTYPE__ diff --git a/src/dummy/i_cdmus.c b/src/dummy/i_cdmus.c index fc35eb9cf..94b8fa30e 100644 --- a/src/dummy/i_cdmus.c +++ b/src/dummy/i_cdmus.c @@ -8,8 +8,8 @@ UINT8 cdaudio_started = 0; -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = CVAR_INIT ("cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL); +consvar_t cdUpdate = CVAR_INIT ("cd_update","1",CV_SAVE, NULL, NULL); void I_InitCD(void){} diff --git a/src/dummy/i_video.c b/src/dummy/i_video.c index 56ead3672..2b0478220 100644 --- a/src/dummy/i_video.c +++ b/src/dummy/i_video.c @@ -8,7 +8,7 @@ boolean highcolor = false; boolean allow_fullscreen = false; -consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL); void I_StartupGraphics(void){} void I_ShutdownGraphics(void){} diff --git a/src/f_finale.c b/src/f_finale.c index d83eeb5cf..d7f81b9df 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -225,6 +225,9 @@ static INT32 cutscene_textspeed = 0; static UINT8 cutscene_boostspeed = 0; static tic_t cutscene_lasttextwrite = 0; +// STJR Intro +char stjrintro[9] = "STJRI000"; + // // This alters the text string cutscene_disptext. // Use the typical string drawing functions to display it. @@ -312,7 +315,7 @@ const char *introtext[NUMINTROSCENES]; static tic_t introscenetime[NUMINTROSCENES] = { - 7*TICRATE + (TICRATE/2), // STJr Presents + 5*TICRATE, // STJr Presents 11*TICRATE + (TICRATE/2), // Two months had passed since... 15*TICRATE + (TICRATE/2), // As it was about to drain the rings... 14*TICRATE, // What Sonic, Tails, and Knuckles... @@ -527,6 +530,7 @@ static void F_IntroDrawScene(void) switch (intro_scenenum) { case 0: + bgxoffs = 28; break; case 1: background = W_CachePatchName("INTRO1", PU_PATCH); @@ -617,97 +621,34 @@ static void F_IntroDrawScene(void) } else if (intro_scenenum == 0) // STJr presents { - // "Waaaaaaah" intro - if (finalecount-TICRATE/2 < 4*TICRATE+23) { - // aspect is FRACUNIT/2 for 4:3 (source) resolutions, smaller for 16:10 (SRB2) resolutions - fixed_t aspect = (FRACUNIT + (FRACUNIT*4/3 - FRACUNIT*vid.width/vid.height)/2)>>1; - fixed_t x,y; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 2); - if (finalecount < 30) { // Cry! - if (finalecount < 4) - S_StopMusic(); - if (finalecount == 4) - S_ChangeMusicInternal("_stjr", false); - x = (BASEVIDWIDTH< 6) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH2", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - if (finalecount > 10) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH3", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - if (finalecount > 14) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH4", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - } - else if (finalecount-30 < 20) { // Big eggy - background = W_CachePatchName("FEEDIN", PU_PATCH); - x = (BASEVIDWIDTH< 1 && intro_curtime < (INT32)introscenetime[intro_scenenum]) + { + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + if (intro_curtime < TICRATE-5) // Make the text shine! + sprintf(stjrintro, "STJRI%03u", intro_curtime-1); + else if (intro_curtime >= TICRATE-6 && intro_curtime < 2*TICRATE-20) // Pause on black screen for just a second + return; + else if (intro_curtime == 2*TICRATE-19) { - { - // Draw tiny eggy - fixed_t scale = FixedMul(FRACUNIT/3, aspect); - background = W_CachePatchName("FEEDIN", PU_PATCH); - x = (BASEVIDWIDTH< 4*TICRATE) { // Door is being raised! - int ftime = (finalecount-TICRATE/2-4*TICRATE); - y -= FixedDiv((ftime*ftime)< SRB2 Community Contributors". + "Ben \"Cue\" Woodford", + "Lachlan \"Lach\" Wright", + "Marco \"mazmazz\" Zafra", "", "\1Art", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: + "\"Arrietty\"", "Ryan \"Blaze Hedgehog\" Bloom", + "Graeme P. \"SuperPhanto\" Caldwell", // for the new brak render "\"ChrispyPixels\"", "Paul \"Boinciel\" Clempson", "Sally \"TehRealSalt\" Cochenour", @@ -1188,6 +1126,8 @@ static const char *credits[] = { "Desmond \"Blade\" DesJardins", "Sherman \"CoatRack\" DesJardins", "\"DirkTheHusky\"", + "Jesse \"Jeck Jims\" Emerick", + "\"Fighter_Builder\"", // for the CEZ3 button debris "Buddy \"KinkaJoy\" Fischer", "Vivian \"toaster\" Grannell", "James \"SwitchKaze\" Hale", @@ -1200,6 +1140,7 @@ static const char *credits[] = { "Andrew \"Senku Niola\" Moran", "\"MotorRoach\"", "Phillip \"TelosTurntable\" Robinson", + "\"Scizor300\"", "Wessel \"sphere\" Smit", "David \"Instant Sonic\" Spencer Jr.", "\"SSNTails\"", @@ -1213,9 +1154,9 @@ static const char *credits[] = { "Malcolm \"RedXVI\" Brown", "Dave \"DemonTomatoDave\" Bulmer", "Paul \"Boinciel\" Clempson", + "\"Cyan Helkaraxe\"", "Shane \"CobaltBW\" Ellis", "James \"SeventhSentinel\" Hall", - "Cyan Helkaraxe", "Kepa \"Nev3r\" Iceta", "Iestyn \"Monster Iestyn\" Jealous", "Jarel \"Arrow\" Jones", @@ -1242,8 +1183,9 @@ static const char *credits[] = { "James \"SeventhSentinel\" Hall", "Kepa \"Nev3r\" Iceta", "Thomas \"Shadow Hog\" Igoe", - "Alexander \"DrTapeworm\" Moench-Ford", "\"Kaito Sinclaire\"", + "Alexander \"DrTapeworm\" Moench-Ford", + "\"Revan\"", "Anna \"QueenDelta\" Sandlin", "Wessel \"sphere\" Smit", "\"Spazzo\"", @@ -1266,7 +1208,7 @@ static const char *credits[] = { "\1Testing", "Discord Community Testers", "Hank \"FuriousFox\" Brannock", - "Cody \"SRB2 Playah\" Koester", + "Cody \"Playah\" Koester", "Skye \"OmegaVelocity\" Meredith", "Stephen \"HEDGESMFG\" Moellering", "Rosalie \"ST218\" Molina", @@ -1283,13 +1225,6 @@ static const char *credits[] = { "Pascal \"CodeImp\" vd Heiden", // Doom Builder developer "Randi Heit ()", // For their MSPaint sprite that we nicked "Simon \"sirjuddington\" Judd", // SLADE developer - // Acknowledged here are the following: - // Minor merge request authors, see guideline above - // - Golden - Expanded thin font - // Creators of small quantities of sprite/texture assets - // - Arietty - New Green Hill-styled textures - // - Scizor300 - the only other contributor to the 2.0 SRB2 Asset Pack - // - Revan/Icefox - the new Nimbus Ruins skybox "SRB2 Community Contributors", "", "\1Produced By", diff --git a/src/filesrch.c b/src/filesrch.c index 13d73b6f4..cb53d07be 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -312,18 +312,18 @@ static CV_PossibleValue_t addons_cons_t[] = {{0, "Default"}, #endif {3, "CUSTOM"}, {0, NULL}}; -consvar_t cv_addons_option = {"addons_option", "Default", CV_SAVE|CV_CALL, addons_cons_t, Addons_option_Onchange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_addons_folder = {"addons_folder", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_addons_option = CVAR_INIT ("addons_option", "Default", CV_SAVE|CV_CALL, addons_cons_t, Addons_option_Onchange); +consvar_t cv_addons_folder = CVAR_INIT ("addons_folder", "", CV_SAVE, NULL, NULL); static CV_PossibleValue_t addons_md5_cons_t[] = {{0, "Name"}, {1, "Contents"}, {0, NULL}}; -consvar_t cv_addons_md5 = {"addons_md5", "Name", CV_SAVE, addons_md5_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_addons_md5 = CVAR_INIT ("addons_md5", "Name", CV_SAVE, addons_md5_cons_t, NULL); -consvar_t cv_addons_showall = {"addons_showall", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_addons_showall = CVAR_INIT ("addons_showall", "No", CV_SAVE, CV_YesNo, NULL); -consvar_t cv_addons_search_case = {"addons_search_case", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_addons_search_case = CVAR_INIT ("addons_search_case", "No", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t addons_search_type_cons_t[] = {{0, "Start"}, {1, "Anywhere"}, {0, NULL}}; -consvar_t cv_addons_search_type = {"addons_search_type", "Anywhere", CV_SAVE, addons_search_type_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_addons_search_type = CVAR_INIT ("addons_search_type", "Anywhere", CV_SAVE, addons_search_type_cons_t, NULL); char menupath[1024]; size_t menupathindex[menudepth]; diff --git a/src/g_demo.c b/src/g_demo.c index 57a955cb1..0f72ad109 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -94,7 +94,7 @@ demoghost *ghosts = NULL; // DEMO RECORDING // -#define DEMOVERSION 0x000d +#define DEMOVERSION 0x000e #define DEMOHEADER "\xF0" "SRB2Replay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! @@ -345,32 +345,29 @@ void G_WriteGhostTic(mobj_t *ghost) else { // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((ghost->x-oldghost.x)>>8); - INT16 momy = (INT16)((ghost->y-oldghost.y)>>8); + fixed_t momx = ghost->x-oldghost.x; + fixed_t momy = ghost->y-oldghost.y; if (momx != oldghost.momx || momy != oldghost.momy) { oldghost.momx = momx; oldghost.momy = momy; ziptic |= GZT_MOMXY; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); + WRITEFIXED(demo_p,momx); + WRITEFIXED(demo_p,momy); } - momx = (INT16)((ghost->z-oldghost.z)>>8); + momx = ghost->z-oldghost.z; if (momx != oldghost.momz) { oldghost.momz = momx; ziptic |= GZT_MOMZ; - WRITEINT16(demo_p,momx); + WRITEFIXED(demo_p,momx); } // This SHOULD set oldghost.x/y/z to match ghost->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldghost.x += oldghost.momx<<8; - oldghost.y += oldghost.momy<<8; - oldghost.z += oldghost.momz<<8; + oldghost.x += oldghost.momx; + oldghost.y += oldghost.momy; + oldghost.z += oldghost.momz; } #undef MAXMOM @@ -464,7 +461,7 @@ void G_WriteGhostTic(mobj_t *ghost) if (ghost->player && ghost->player->followmobj && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do { - INT16 temp; + fixed_t temp; UINT8 *followtic_p = demo_p++; UINT8 followtic = 0; @@ -492,12 +489,12 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEFIXED(demo_p,ghost->player->followmobj->scale); } - temp = (INT16)((ghost->player->followmobj->x-ghost->x)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((ghost->player->followmobj->y-ghost->y)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((ghost->player->followmobj->z-ghost->z)>>8); - WRITEINT16(demo_p,temp); + temp = ghost->player->followmobj->x-ghost->x; + WRITEFIXED(demo_p,temp); + temp = ghost->player->followmobj->y-ghost->y; + WRITEFIXED(demo_p,temp); + temp = ghost->player->followmobj->z-ghost->z; + WRITEFIXED(demo_p,temp); if (followtic & FZT_SKIN) WRITEUINT8(demo_p,ghost->player->followmobj->sprite2); WRITEUINT16(demo_p,ghost->player->followmobj->sprite); @@ -547,11 +544,11 @@ void G_ConsGhostTic(void) { if (ziptic & GZT_MOMXY) { - oldghost.momx = READINT16(demo_p)<<8; - oldghost.momy = READINT16(demo_p)<<8; + oldghost.momx = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p); + oldghost.momy = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p); } if (ziptic & GZT_MOMZ) - oldghost.momz = READINT16(demo_p)<<8; + oldghost.momz = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p); oldghost.x += oldghost.momx; oldghost.y += oldghost.momy; oldghost.z += oldghost.momz; @@ -627,9 +624,8 @@ void G_ConsGhostTic(void) } if (followtic & FZT_SCALE) demo_p += sizeof(fixed_t); - demo_p += sizeof(INT16); - demo_p += sizeof(INT16); - demo_p += sizeof(INT16); + // momx, momy and momz + demo_p += (demoversion < 0x000e) ? sizeof(INT16) * 3 : sizeof(fixed_t) * 3; if (followtic & FZT_SKIN) demo_p++; demo_p += sizeof(UINT16); @@ -697,11 +693,11 @@ void G_GhostTicker(void) { if (ziptic & GZT_MOMXY) { - g->oldmo.momx = READINT16(g->p)<<8; - g->oldmo.momy = READINT16(g->p)<<8; + g->oldmo.momx = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); + g->oldmo.momy = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); } if (ziptic & GZT_MOMZ) - g->oldmo.momz = READINT16(g->p)<<8; + g->oldmo.momz = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); g->oldmo.x += g->oldmo.momx; g->oldmo.y += g->oldmo.momy; g->oldmo.z += g->oldmo.momz; @@ -905,11 +901,11 @@ void G_GhostTicker(void) P_SetScale(follow, follow->destscale); P_UnsetThingPosition(follow); - temp = READINT16(g->p)<<8; + temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); follow->x = g->mo->x + temp; - temp = READINT16(g->p)<<8; + temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); follow->y = g->mo->y + temp; - temp = READINT16(g->p)<<8; + temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); follow->z = g->mo->z + temp; P_SetThingPosition(follow); if (followtic & FZT_SKIN) @@ -1010,11 +1006,11 @@ void G_ReadMetalTic(mobj_t *metal) { if (ziptic & GZT_MOMXY) { - oldmetal.momx = READINT16(metal_p)<<8; - oldmetal.momy = READINT16(metal_p)<<8; + oldmetal.momx = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); + oldmetal.momy = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); } if (ziptic & GZT_MOMZ) - oldmetal.momz = READINT16(metal_p)<<8; + oldmetal.momz = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); oldmetal.x += oldmetal.momx; oldmetal.y += oldmetal.momy; oldmetal.z += oldmetal.momz; @@ -1149,11 +1145,11 @@ void G_ReadMetalTic(mobj_t *metal) P_SetScale(follow, follow->destscale); P_UnsetThingPosition(follow); - temp = READINT16(metal_p)<<8; + temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); follow->x = metal->x + temp; - temp = READINT16(metal_p)<<8; + temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); follow->y = metal->y + temp; - temp = READINT16(metal_p)<<8; + temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p); follow->z = metal->z + temp; P_SetThingPosition(follow); if (followtic & FZT_SKIN) @@ -1213,32 +1209,30 @@ void G_WriteMetalTic(mobj_t *metal) else { // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((metal->x-oldmetal.x)>>8); - INT16 momy = (INT16)((metal->y-oldmetal.y)>>8); + // Store movement as a fixed value + fixed_t momx = metal->x-oldmetal.x; + fixed_t momy = metal->y-oldmetal.y; if (momx != oldmetal.momx || momy != oldmetal.momy) { oldmetal.momx = momx; oldmetal.momy = momy; ziptic |= GZT_MOMXY; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); + WRITEFIXED(demo_p,momx); + WRITEFIXED(demo_p,momy); } - momx = (INT16)((metal->z-oldmetal.z)>>8); + momx = metal->z-oldmetal.z; if (momx != oldmetal.momz) { oldmetal.momz = momx; ziptic |= GZT_MOMZ; - WRITEINT16(demo_p,momx); + WRITEFIXED(demo_p,momx); } // This SHOULD set oldmetal.x/y/z to match metal->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldmetal.x += oldmetal.momx<<8; - oldmetal.y += oldmetal.momy<<8; - oldmetal.z += oldmetal.momz<<8; + oldmetal.x += oldmetal.momx; + oldmetal.y += oldmetal.momy; + oldmetal.z += oldmetal.momz; } #undef MAXMOM @@ -1307,7 +1301,7 @@ void G_WriteMetalTic(mobj_t *metal) if (metal->player && metal->player->followmobj && !(metal->player->followmobj->sprite == SPR_NULL || (metal->player->followmobj->flags2 & MF2_DONTDRAW))) { - INT16 temp; + fixed_t temp; UINT8 *followtic_p = demo_p++; UINT8 followtic = 0; @@ -1335,12 +1329,12 @@ void G_WriteMetalTic(mobj_t *metal) WRITEFIXED(demo_p,metal->player->followmobj->scale); } - temp = (INT16)((metal->player->followmobj->x-metal->x)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((metal->player->followmobj->y-metal->y)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((metal->player->followmobj->z-metal->z)>>8); - WRITEINT16(demo_p,temp); + temp = metal->player->followmobj->x-metal->x; + WRITEFIXED(demo_p,temp); + temp = metal->player->followmobj->y-metal->y; + WRITEFIXED(demo_p,temp); + temp = metal->player->followmobj->z-metal->z; + WRITEFIXED(demo_p,temp); if (followtic & FZT_SKIN) WRITEUINT8(demo_p,metal->player->followmobj->sprite2); WRITEUINT16(demo_p,metal->player->followmobj->sprite); @@ -1818,6 +1812,7 @@ void G_DoPlayDemo(char *defdemoname) demoversion = READUINT16(demo_p); switch(demoversion) { + case 0x000d: case DEMOVERSION: // latest always supported cnamelen = MAXCOLORNAME; break; @@ -1933,9 +1928,8 @@ void G_DoPlayDemo(char *defdemoname) if (use_old_demo_vars) CV_LoadOldDemoVars(&demo_p); else -#else - CV_LoadDemoVars(&demo_p); #endif + CV_LoadDemoVars(&demo_p); // Sigh ... it's an empty demo. if (*demo_p == DEMOMARKER) @@ -2073,6 +2067,7 @@ void G_AddGhost(char *defdemoname) ghostversion = READUINT16(p); switch(ghostversion) { + case 0x000d: case DEMOVERSION: // latest always supported cnamelen = MAXCOLORNAME; break; @@ -2168,7 +2163,7 @@ void G_AddGhost(char *defdemoname) count = READUINT16(p); while (count--) { - p += 2; + SKIPSTRING(p); SKIPSTRING(p); p++; } @@ -2324,6 +2319,7 @@ void G_DoPlayMetal(void) switch(metalversion) { case DEMOVERSION: // latest always supported + case 0x000d: // There are checks wheter the momentum is from older demo versions or not case 0x000c: // all that changed between then and now was longer color name break; // too old, cannot support. diff --git a/src/g_game.c b/src/g_game.c index b969eb4a4..1ea7cd080 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -297,100 +297,100 @@ static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. // it automatically becomes compact with 20+ players, but if you like it, I guess you can turn that on! -consvar_t cv_compactscoreboard= {"compactscoreboard", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_compactscoreboard= CVAR_INIT ("compactscoreboard", "Off", CV_SAVE, CV_OnOff, NULL); // chat timer thingy static CV_PossibleValue_t chattime_cons_t[] = {{5, "MIN"}, {999, "MAX"}, {0, NULL}}; -consvar_t cv_chattime = {"chattime", "8", CV_SAVE, chattime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chattime = CVAR_INIT ("chattime", "8", CV_SAVE, chattime_cons_t, NULL); // chatwidth static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_chatwidth = {"chatwidth", "150", CV_SAVE, chatwidth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chatwidth = CVAR_INIT ("chatwidth", "150", CV_SAVE, chatwidth_cons_t, NULL); // chatheight static CV_PossibleValue_t chatheight_cons_t[] = {{6, "MIN"}, {22, "MAX"}, {0, NULL}}; -consvar_t cv_chatheight= {"chatheight", "8", CV_SAVE, chatheight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chatheight= CVAR_INIT ("chatheight", "8", CV_SAVE, chatheight_cons_t, NULL); // chat notifications (do you want to hear beeps? I'd understand if you didn't.) -consvar_t cv_chatnotifications= {"chatnotifications", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chatnotifications= CVAR_INIT ("chatnotifications", "On", CV_SAVE, CV_OnOff, NULL); // chat spam protection (why would you want to disable that???) -consvar_t cv_chatspamprotection= {"chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chatspamprotection= CVAR_INIT ("chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL); // minichat text background -consvar_t cv_chatbacktint = {"chatbacktint", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff, NULL); // old shit console chat. (mostly exists for stuff like terminal, not because I cared if anyone liked the old chat.) static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}}; -consvar_t cv_consolechat = {"chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL); // Pause game upon window losing focus -consvar_t cv_pauseifunfocused = {"pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL); -consvar_t cv_crosshair = {"crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_crosshair2 = {"crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_invertmouse = {"invertmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_alwaysfreelook = {"alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_invertmouse2 = {"invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook = {"chasemlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook2 = {"chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove = {"mousemove", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove2 = {"mousemove2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_crosshair = CVAR_INIT ("crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL); +consvar_t cv_crosshair2 = CVAR_INIT ("crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL); +consvar_t cv_invertmouse = CVAR_INIT ("invertmouse", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_alwaysfreelook = CVAR_INIT ("alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_invertmouse2 = CVAR_INIT ("invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_alwaysfreelook2 = CVAR_INIT ("alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_chasefreelook = CVAR_INIT ("chasemlook", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_chasefreelook2 = CVAR_INIT ("chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_mousemove = CVAR_INIT ("mousemove", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_mousemove2 = CVAR_INIT ("mousemove2", "Off", CV_SAVE, CV_OnOff, NULL); // previously "analog", "analog2", "useranalog", and "useranalog2", invalidating 2.1-era copies of config.cfg // changed because it'd be nice to see people try out our actually good controls with gamepads now autobrake exists consvar_t cv_analog[2] = { - {"sessionanalog", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"sessionanalog2", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog2_OnChange, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("sessionanalog", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog_OnChange), + CVAR_INIT ("sessionanalog2", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog2_OnChange), }; consvar_t cv_useranalog[2] = { - {"configanalog", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"configanalog2", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("configanalog", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog_OnChange), + CVAR_INIT ("configanalog2", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog2_OnChange), }; // deez New User eXperiences static CV_PossibleValue_t directionchar_cons_t[] = {{0, "Camera"}, {1, "Movement"}, {2, "Simple Locked"}, {0, NULL}}; consvar_t cv_directionchar[2] = { - {"directionchar", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"directionchar2", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar2_OnChange, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("directionchar", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar_OnChange), + CVAR_INIT ("directionchar2", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar2_OnChange), }; -consvar_t cv_autobrake = {"autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_autobrake2 = {"autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_autobrake = CVAR_INIT ("autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange); +consvar_t cv_autobrake2 = CVAR_INIT ("autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange); // hi here's some new controls static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_cam_shiftfacing[2] = { - {"cam_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("cam2_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), }; consvar_t cv_cam_turnfacing[2] = { - {"cam_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("cam2_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), }; consvar_t cv_cam_turnfacingability[2] = { - {"cam_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("cam2_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), }; consvar_t cv_cam_turnfacingspindash[2] = { - {"cam_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("cam2_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), }; consvar_t cv_cam_turnfacinginput[2] = { - {"cam_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("cam2_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), }; static CV_PossibleValue_t centertoggle_cons_t[] = {{0, "Hold"}, {1, "Toggle"}, {2, "Sticky Hold"}, {0, NULL}}; consvar_t cv_cam_centertoggle[2] = { - {"cam_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL), + CVAR_INIT ("cam2_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL), }; static CV_PossibleValue_t lockedinput_cons_t[] = {{0, "Strafe"}, {1, "Turn"}, {0, NULL}}; consvar_t cv_cam_lockedinput[2] = { - {"cam_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL), + CVAR_INIT ("cam2_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL), }; static CV_PossibleValue_t lockedassist_cons_t[] = { @@ -402,8 +402,8 @@ static CV_PossibleValue_t lockedassist_cons_t[] = { {0, NULL} }; consvar_t cv_cam_lockonboss[2] = { - {"cam_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + CVAR_INIT ("cam_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL), + CVAR_INIT ("cam2_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL), }; typedef enum @@ -422,27 +422,27 @@ typedef enum AXISFIRENORMAL, } axis_input_e; -consvar_t cv_turnaxis = {"joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis = {"joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_sideaxis = {"joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis = {"joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_jumpaxis = {"joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_spinaxis = {"joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_firenaxis = {"joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone = {"joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_digitaldeadzone = {"joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_turnaxis = CVAR_INIT ("joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_moveaxis = CVAR_INIT ("joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_sideaxis = CVAR_INIT ("joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_lookaxis = CVAR_INIT ("joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_jumpaxis = CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_spinaxis = CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_fireaxis = CVAR_INIT ("joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_firenaxis = CVAR_INIT ("joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_deadzone = CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); +consvar_t cv_digitaldeadzone = CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); -consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis2 = {"joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_sideaxis2 = {"joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis2 = {"joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_jumpaxis2 = {"joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_spinaxis2 = {"joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone2 = {"joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_digitaldeadzone2 = {"joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_turnaxis2 = CVAR_INIT ("joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_moveaxis2 = CVAR_INIT ("joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_sideaxis2 = CVAR_INIT ("joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_lookaxis2 = CVAR_INIT ("joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_jumpaxis2 = CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_spinaxis2 = CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_fireaxis2 = CVAR_INIT ("joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_firenaxis2 = CVAR_INIT ("joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_deadzone2 = CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); +consvar_t cv_digitaldeadzone2 = CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); #ifdef SEENAMES player_t *seenplayer; // player we're aiming at right now @@ -452,6 +452,8 @@ player_t *seenplayer; // player we're aiming at right now // so that it doesn't have to be updated depending on the value of MAXPLAYERS char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +INT32 player_name_changes[MAXPLAYERS]; + INT16 rw_maximums[NUM_WEAPONS] = { 800, // MAX_INFINITY @@ -1675,6 +1677,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } + // Note: Lat originally made the PlayerCmd hook for SRB2 Kart so credit goes to him. + if (gamestate == GS_LEVEL) + LUAh_PlayerCmd(player, cmd); + //Reset away view if a command is given. if (ssplayer == 1 && (cmd->forwardmove || cmd->sidemove || cmd->buttons) && displayplayer != consoleplayer) @@ -2013,7 +2019,7 @@ boolean G_Responder(event_t *ev) if (F_CreditResponder(ev)) { // Skip credits for everyone - if (! serverrunning)/* hahahahahaha */ + if (! netgame) F_StartGameEvaluation(); else if (server || IsPlayerAdmin(consoleplayer)) SendNetXCmd(XD_EXITLEVEL, NULL, 0); @@ -2345,6 +2351,11 @@ void G_Ticker(boolean run) if (camtoggledelay2) camtoggledelay2--; + + if (gametic % NAMECHANGERATE == 0) + { + memset(player_name_changes, 0, sizeof player_name_changes); + } } } @@ -3507,7 +3518,7 @@ INT32 G_GetGametypeByName(const char *gametypestr) // boolean G_IsSpecialStage(INT32 mapnum) { - if (gametype != GT_COOP || modeattacking == ATTACKING_RECORD) + if (modeattacking == ATTACKING_RECORD) return false; if (mapnum >= sstage_start && mapnum <= sstage_end) return true; @@ -3808,7 +3819,7 @@ static void G_DoCompleted(void) // a map of the proper gametype -- skip levels that don't support // the current gametype. (Helps avoid playing boss levels in Race, // for instance). - if (!spec) + if (!spec || nextmapoverride) { if (nextmap >= 0 && nextmap < NUMMAPS) { @@ -3860,7 +3871,8 @@ static void G_DoCompleted(void) if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); - lastmap = nextmap; // Remember last map for when you come out of the special stage. + if (!spec) + lastmap = nextmap; // Remember last map for when you come out of the special stage. } if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token))) @@ -3881,7 +3893,7 @@ static void G_DoCompleted(void) } } - if (spec && !gottoken) + if (spec && !gottoken && !nextmapoverride) nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001 automapactive = false; diff --git a/src/g_game.h b/src/g_game.h index c8abe560c..2bcf444c2 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -18,6 +18,7 @@ #include "doomstat.h" #include "d_event.h" #include "g_demo.h" +#include "m_cheat.h" // objectplacing extern char gamedatafilename[64]; extern char timeattackfolder[64]; @@ -27,7 +28,8 @@ extern char customversionstring[32]; #ifdef SEENAMES extern player_t *seenplayer; #endif -extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern INT32 player_name_changes[MAXPLAYERS]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; @@ -64,7 +66,7 @@ typedef enum { CS_STANDARD, CS_SIMPLE = CS_LMAOGALOG|CS_STANDARD, } controlstyle_e; -#define G_ControlStyle(ssplayer) (cv_directionchar[(ssplayer)-1].value == 3 ? CS_LMAOGALOG : ((cv_analog[(ssplayer)-1].value ? CS_LMAOGALOG : 0) | (cv_directionchar[(ssplayer)-1].value ? CS_STANDARD : 0))) +#define G_ControlStyle(ssplayer) (cv_directionchar[(ssplayer)-1].value == 3 ? CS_LMAOGALOG : ((!objectplacing && cv_analog[(ssplayer)-1].value ? CS_LMAOGALOG : 0) | (cv_directionchar[(ssplayer)-1].value ? CS_STANDARD : 0))) #define P_ControlStyle(player) ((((player)->pflags & PF_ANALOGMODE) ? CS_LMAOGALOG : 0) | (((player)->pflags & PF_DIRECTIONCHAR) ? CS_STANDARD : 0)) extern consvar_t cv_autobrake, cv_autobrake2; diff --git a/src/g_input.c b/src/g_input.c index 1cf6990c9..d3c21e774 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -25,11 +25,11 @@ static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}}; // mouse values are used once -consvar_t cv_mousesens = {"mousesens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousesens2 = {"mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mouseysens = {"mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mouseysens2 = {"mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mousesens = CVAR_INIT ("mousesens", "20", CV_SAVE, mousesens_cons_t, NULL); +consvar_t cv_mousesens2 = CVAR_INIT ("mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL); +consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL); +consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL); +consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL); INT32 mousex, mousey; INT32 mlooky; // like mousey but with a custom sensitivity for mlook diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c index 492cea5fa..a63be3a72 100644 --- a/src/hardware/hw_batching.c +++ b/src/hardware/hw_batching.c @@ -235,13 +235,13 @@ void HWR_RenderBatches(void) currently_batching = false;// no longer collecting batches if (!polygonArraySize) { - rs_hw_numpolys = rs_hw_numcalls = rs_hw_numshaders = rs_hw_numtextures = rs_hw_numpolyflags = rs_hw_numcolors = 0; + ps_hw_numpolys = ps_hw_numcalls = ps_hw_numshaders = ps_hw_numtextures = ps_hw_numpolyflags = ps_hw_numcolors = 0; return;// nothing to draw } // init stats vars - rs_hw_numpolys = polygonArraySize; - rs_hw_numcalls = rs_hw_numverts = 0; - rs_hw_numshaders = rs_hw_numtextures = rs_hw_numpolyflags = rs_hw_numcolors = 1; + ps_hw_numpolys = polygonArraySize; + ps_hw_numcalls = ps_hw_numverts = 0; + ps_hw_numshaders = ps_hw_numtextures = ps_hw_numpolyflags = ps_hw_numcolors = 1; // init polygonIndexArray for (i = 0; i < polygonArraySize; i++) { @@ -249,12 +249,12 @@ void HWR_RenderBatches(void) } // sort polygons - rs_hw_batchsorttime = I_GetTimeMicros(); + ps_hw_batchsorttime = I_GetTimeMicros(); if (cv_glshaders.value && gl_shadersavailable) qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); else qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); - rs_hw_batchsorttime = I_GetTimeMicros() - rs_hw_batchsorttime; + ps_hw_batchsorttime = I_GetTimeMicros() - ps_hw_batchsorttime; // sort order // 1. shader // 2. texture @@ -262,7 +262,7 @@ void HWR_RenderBatches(void) // 4. colors + light level // not sure about what order of the last 2 should be, or if it even matters - rs_hw_batchdrawtime = I_GetTimeMicros(); + ps_hw_batchdrawtime = I_GetTimeMicros(); currentShader = polygonArray[polygonIndexArray[0]].shader; currentTexture = polygonArray[polygonIndexArray[0]].texture; @@ -398,8 +398,8 @@ void HWR_RenderBatches(void) // execute draw call HWD.pfnDrawIndexedTriangles(¤tSurfaceInfo, finalVertexArray, finalIndexWritePos, currentPolyFlags, finalVertexIndexArray); // update stats - rs_hw_numcalls++; - rs_hw_numverts += finalIndexWritePos; + ps_hw_numcalls++; + ps_hw_numverts += finalIndexWritePos; // reset write positions finalVertexWritePos = 0; finalIndexWritePos = 0; @@ -416,7 +416,7 @@ void HWR_RenderBatches(void) currentShader = nextShader; changeShader = false; - rs_hw_numshaders++; + ps_hw_numshaders++; } if (changeTexture) { @@ -425,21 +425,21 @@ void HWR_RenderBatches(void) currentTexture = nextTexture; changeTexture = false; - rs_hw_numtextures++; + ps_hw_numtextures++; } if (changePolyFlags) { currentPolyFlags = nextPolyFlags; changePolyFlags = false; - rs_hw_numpolyflags++; + ps_hw_numpolyflags++; } if (changeSurfaceInfo) { currentSurfaceInfo = nextSurfaceInfo; changeSurfaceInfo = false; - rs_hw_numcolors++; + ps_hw_numcolors++; } // and that should be it? } @@ -447,7 +447,7 @@ void HWR_RenderBatches(void) polygonArraySize = 0; unsortedVertexArraySize = 0; - rs_hw_batchdrawtime = I_GetTimeMicros() - rs_hw_batchdrawtime; + ps_hw_batchdrawtime = I_GetTimeMicros() - ps_hw_batchdrawtime; } diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index ed3b6afee..8c85c5112 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -20,11 +20,12 @@ #include "../doomstat.h" //gamemode #include "../i_video.h" //rendermode #include "../r_data.h" +#include "../r_textures.h" #include "../w_wad.h" #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" -#include "../r_patch.h" +#include "../r_picformats.h" #include "../p_setup.h" INT32 patchformat = GL_TEXFMT_AP_88; // use alpha for holes @@ -99,17 +100,15 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap[texel]; - // If the mipmap is chromakeyed, check if the texel's color - // is equivalent to the chroma key's color index. - alpha = 0xff; - if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) - alpha = 0x00; - // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS @@ -211,17 +210,15 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap[texel]; - // If the mipmap is chromakeyed, check if the texel's color - // is equivalent to the chroma key's color index. - alpha = 0xff; - if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) - alpha = 0x00; - // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS @@ -508,13 +505,13 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) realpatch = (patch_t *)pdata; #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL); + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif #ifdef WALLFLATS if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); + realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); else #endif { @@ -550,8 +547,8 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm #ifndef NO_PNG_LUMPS // lump is a png so convert it size_t len = W_LumpLengthPwad(grPatch->wadnum, grPatch->lumpnum); - if ((patch != NULL) && R_IsLumpPNG((const UINT8 *)patch, len)) - patch = R_PNGToPatch((const UINT8 *)patch, len, NULL); + if ((patch != NULL) && Picture_IsLumpPNG((const UINT8 *)patch, len)) + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, NULL, NULL, NULL, NULL, len, NULL, 0); #endif // don't do it twice (like a cache) @@ -788,6 +785,8 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) { UINT8 *flat; + UINT8 *converted; + size_t size; // setup the texture info grMipmap->format = GL_TEXFMT_P_8; @@ -795,11 +794,12 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; + size = (grMipmap->width * grMipmap->height); - flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->data); - memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); - - R_TextureToFlat(texturenum, flat); + flat = Z_Malloc(size, PU_HWRCACHE, &grMipmap->data); + converted = (UINT8 *)Picture_TextureToFlat(texturenum); + M_Memcpy(flat, converted, size); + Z_Free(converted); } // Download a Doom 'flat' to the hardware cache and make it ready for use @@ -837,7 +837,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) INT32 texturenum = levelflat->u.texture.num; #ifdef PARANOIA if ((unsigned)texturenum >= gl_numtextures) - I_Error("HWR_GetLevelFlat: texturenum >= numtextures\n"); + I_Error("HWR_GetLevelFlat: texturenum >= numtextures"); #endif // Who knows? @@ -860,6 +860,53 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) // The system-memory data can be purged now. Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED); } + else if (levelflat->type == LEVELFLAT_PATCH) + { + GLPatch_t *patch = W_CachePatchNum(levelflat->u.flat.lumpnum, PU_CACHE); + levelflat->width = (UINT16)SHORT(patch->width); + levelflat->height = (UINT16)SHORT(patch->height); + HWR_GetPatch(patch); + } +#ifndef NO_PNG_LUMPS + else if (levelflat->type == LEVELFLAT_PNG) + { + INT32 pngwidth = 0, pngheight = 0; + GLMipmap_t *mipmap = levelflat->mipmap; + UINT8 *flat; + size_t size; + + // Cache the picture. + if (!levelflat->picture) + { + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + } + + // Make the mipmap. + if (mipmap == NULL) + { + mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_LEVEL, NULL); + mipmap->format = GL_TEXFMT_P_8; + mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; + levelflat->mipmap = mipmap; + } + + if (!mipmap->data && !mipmap->downloaded) + { + mipmap->width = levelflat->width; + mipmap->height = levelflat->height; + size = (mipmap->width * mipmap->height); + flat = Z_Malloc(size, PU_LEVEL, &mipmap->data); + if (levelflat->picture == NULL) + I_Error("HWR_GetLevelFlat: levelflat->picture == NULL"); + M_Memcpy(flat, levelflat->picture, size); + } + + // Tell the hardware driver to bind the current texture to the flat's mipmap + HWD.pfnSetTexture(mipmap); + } +#endif else // set no texture HWR_SetCurrentTexture(NULL); } diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index a7f7acd22..607d21ef5 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -132,6 +132,43 @@ typedef struct FLOAT t; // t texture ordinate (t over w) } FOutVector; +#ifdef GL_SHADERS +// Predefined shader types +enum +{ + SHADER_DEFAULT = 0, + + SHADER_FLOOR, + SHADER_WALL, + SHADER_SPRITE, + SHADER_MODEL, SHADER_MODEL_LIGHTING, + SHADER_WATER, + SHADER_FOG, + SHADER_SKY, + + NUMBASESHADERS, +}; + +// Maximum amount of shader programs +// Must be higher than NUMBASESHADERS +#define HWR_MAXSHADERS 16 + +// Shader sources (vertex and fragment) +typedef struct +{ + char *vertex; + char *fragment; +} shadersource_t; + +// Custom shader reference table +typedef struct +{ + const char *type; + INT32 id; +} customshaderxlat_t; + +#endif + typedef struct vbo_vertex_s { float x, y, z; diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 4023e3062..aaa41e86f 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -68,14 +68,13 @@ EXPORT void HWRAPI(DrawScreenFinalTexture) (int width, int height); EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]); // jimita -EXPORT boolean HWRAPI(LoadShaders) (void); -EXPORT void HWRAPI(KillShaders) (void); +EXPORT boolean HWRAPI(CompileShaders) (void); +EXPORT void HWRAPI(CleanShaders) (void); EXPORT void HWRAPI(SetShader) (int shader); EXPORT void HWRAPI(UnSetShader) (void); EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value); -EXPORT void HWRAPI(LoadCustomShader) (int number, char *shader, size_t size, boolean fragment); -EXPORT boolean HWRAPI(InitCustomShaders) (void); +EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boolean isfragment); // ========================================================================== // HWR DRIVER OBJECT, FOR CLIENT PROGRAM @@ -120,14 +119,13 @@ struct hwdriver_s MakeScreenFinalTexture pfnMakeScreenFinalTexture; DrawScreenFinalTexture pfnDrawScreenFinalTexture; - LoadShaders pfnLoadShaders; - KillShaders pfnKillShaders; + CompileShaders pfnCompileShaders; + CleanShaders pfnCleanShaders; SetShader pfnSetShader; UnSetShader pfnUnSetShader; SetShaderInfo pfnSetShaderInfo; LoadCustomShader pfnLoadCustomShader; - InitCustomShaders pfnInitCustomShaders; }; extern struct hwdriver_s hwdriver; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index b3dcc4cc7..29978c71e 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -25,7 +25,7 @@ #include "../p_local.h" #include "../p_setup.h" #include "../r_local.h" -#include "../r_patch.h" +#include "../r_picformats.h" #include "../r_bsp.h" #include "../d_clisrv.h" #include "../w_wad.h" @@ -146,21 +146,22 @@ static angle_t gl_aimingangle; static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox); // Render stats -int rs_hw_nodesorttime = 0; -int rs_hw_nodedrawtime = 0; -int rs_hw_spritesorttime = 0; -int rs_hw_spritedrawtime = 0; +int ps_hw_skyboxtime = 0; +int ps_hw_nodesorttime = 0; +int ps_hw_nodedrawtime = 0; +int ps_hw_spritesorttime = 0; +int ps_hw_spritedrawtime = 0; // Render stats for batching -int rs_hw_numpolys = 0; -int rs_hw_numverts = 0; -int rs_hw_numcalls = 0; -int rs_hw_numshaders = 0; -int rs_hw_numtextures = 0; -int rs_hw_numpolyflags = 0; -int rs_hw_numcolors = 0; -int rs_hw_batchsorttime = 0; -int rs_hw_batchdrawtime = 0; +int ps_hw_numpolys = 0; +int ps_hw_numverts = 0; +int ps_hw_numcalls = 0; +int ps_hw_numshaders = 0; +int ps_hw_numtextures = 0; +int ps_hw_numpolyflags = 0; +int ps_hw_numcolors = 0; +int ps_hw_batchsorttime = 0; +int ps_hw_batchdrawtime = 0; boolean gl_shadersavailable = true; @@ -358,7 +359,6 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -413,16 +413,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -447,9 +440,22 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWR_SetCurrentTexture(NULL); @@ -560,11 +566,11 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool PolyFlags |= PF_Masked|PF_Modulated; if (PolyFlags & PF_Fog) - shader = 6; // fog shader + shader = SHADER_FOG; // fog shader else if (PolyFlags & PF_Ripple) - shader = 5; // water shader + shader = SHADER_WATER; // water shader else - shader = 1; // floor shader + shader = SHADER_FLOOR; // floor shader HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, PolyFlags, shader, false); @@ -761,7 +767,7 @@ static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf) break; } - HWD.pfnSetShader(2); // wall shader + HWD.pfnSetShader(SHADER_WALL); // wall shader HWD.pfnDrawPolygon(&pSurf, wallVerts, 4, i|PF_Modulated|PF_Decal); } } @@ -798,7 +804,7 @@ static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIEL { HWR_Lighting(pSurf, lightlevel, wallcolormap); - HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, 2, false); // wall shader + HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, SHADER_WALL, false); // wall shader #ifdef WALLSPLATS if (gl_curline->linedef->splats && cv_splats.value) @@ -1977,7 +1983,7 @@ static cliprange_t * hw_newend; static cliprange_t gl_solidsegs[MAXSEGS]; // needs fix: walls are incorrectly clipped one column less -static consvar_t cv_glclipwalls = {"gr_clipwalls", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_glclipwalls = CVAR_INIT ("gr_clipwalls", "Off", 0, CV_OnOff, NULL); static void printsolidsegs(void) { @@ -2683,7 +2689,6 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -2717,16 +2722,9 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -2751,9 +2749,22 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWR_SetCurrentTexture(NULL); @@ -2857,7 +2868,7 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, else blendmode |= PF_Masked|PF_Modulated|PF_Clip; - HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode, 1, false); // floor shader + HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode, SHADER_FLOOR, false); // floor shader } static void HWR_AddPolyObjectPlanes(void) @@ -3202,7 +3213,7 @@ static void HWR_Subsector(size_t num) } // for render stats - rs_numpolyobjects += numpolys; + ps_numpolyobjects += numpolys; // Sort polyobjects R_SortPolyObjects(sub); @@ -3310,7 +3321,7 @@ static void HWR_RenderBSPNode(INT32 bspnum) // Decide which side the view point is on INT32 side; - rs_numbspcalls++; + ps_numbspcalls++; // Found a subsector? if (bspnum & NF_SUBSECTOR) @@ -3669,7 +3680,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) HWR_Lighting(&sSurf, 0, colormap); sSurf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&sSurf, shadowVerts, 4, PF_Translucent|PF_Modulated|PF_Clip, 3, false); // sprite shader + HWR_ProcessPolygon(&sSurf, shadowVerts, 4, PF_Translucent|PF_Modulated|PF_Clip, SHADER_SPRITE, false); // sprite shader } // This is expecting a pointer to an array containing 4 wallVerts for a sprite @@ -3943,7 +3954,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) Surf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, 3, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, SHADER_SPRITE, false); // sprite shader if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -3972,7 +3983,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) Surf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, 3, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, SHADER_SPRITE, false); // sprite shader if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -4132,7 +4143,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) if (!occlusion) use_linkdraw_hack = true; } - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, 3, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, SHADER_SPRITE, false); // sprite shader if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -4234,7 +4245,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) blend = PF_Translucent|PF_Occlude; } - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, 3, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated|PF_Clip, SHADER_SPRITE, false); // sprite shader } #endif @@ -4516,7 +4527,7 @@ static void HWR_CreateDrawNodes(void) // that is already lying around. This should all be in some sort of linked list or lists. sortindex = Z_Calloc(sizeof(size_t) * (numplanes + numpolyplanes + numwalls), PU_STATIC, NULL); - rs_hw_nodesorttime = I_GetTimeMicros(); + ps_hw_nodesorttime = I_GetTimeMicros(); for (i = 0; i < numplanes; i++, p++) { @@ -4536,7 +4547,7 @@ static void HWR_CreateDrawNodes(void) sortindex[p] = p; } - rs_numdrawnodes = p; + ps_numdrawnodes = p; // p is the number of stuff to sort @@ -4571,13 +4582,13 @@ static void HWR_CreateDrawNodes(void) } } - rs_hw_nodesorttime = I_GetTimeMicros() - rs_hw_nodesorttime; + ps_hw_nodesorttime = I_GetTimeMicros() - ps_hw_nodesorttime; - rs_hw_nodedrawtime = I_GetTimeMicros(); + ps_hw_nodedrawtime = I_GetTimeMicros(); // Okay! Let's draw it all! Woo! HWD.pfnSetTransform(&atransform); - HWD.pfnSetShader(0); + HWD.pfnSetShader(SHADER_DEFAULT); for (i = 0; i < p; i++) { @@ -4610,7 +4621,7 @@ static void HWR_CreateDrawNodes(void) } } - rs_hw_nodedrawtime = I_GetTimeMicros() - rs_hw_nodedrawtime; + ps_hw_nodedrawtime = I_GetTimeMicros() - ps_hw_nodedrawtime; numwalls = 0; numplanes = 0; @@ -5397,7 +5408,7 @@ static void HWR_DrawSkyBackground(player_t *player) HWR_BuildSkyDome(); } - HWD.pfnSetShader(7); // sky shader + HWD.pfnSetShader(SHADER_SKY); // sky shader HWD.pfnSetTransform(&dometransform); HWD.pfnRenderSkyDome(&gl_sky); } @@ -5484,7 +5495,7 @@ static void HWR_DrawSkyBackground(player_t *player) HWD.pfnDrawPolygon(NULL, v, 4, 0); } - HWD.pfnSetShader(0); + HWD.pfnSetShader(SHADER_DEFAULT); } @@ -5688,7 +5699,7 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player) // Reset the shader state. HWD.pfnSetSpecialState(HWD_SET_SHADERS, cv_glshaders.value); - HWD.pfnSetShader(0); + HWD.pfnSetShader(SHADER_DEFAULT); validcount++; @@ -5791,8 +5802,10 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) if (viewnumber == 0) // Only do it if it's the first screen being rendered HWD.pfnClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs. + ps_hw_skyboxtime = I_GetTimeMicros(); if (skybox && drawsky) // If there's a skybox and we should be drawing the sky, draw the skybox HWR_RenderSkyboxView(viewnumber, player); // This is drawn before everything else so it is placed behind + ps_hw_skyboxtime = I_GetTimeMicros() - ps_hw_skyboxtime; { // do we really need to save player (is it not the same)? @@ -5901,11 +5914,11 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) // Reset the shader state. HWD.pfnSetSpecialState(HWD_SET_SHADERS, cv_glshaders.value); - HWD.pfnSetShader(0); + HWD.pfnSetShader(SHADER_DEFAULT); - rs_numbspcalls = 0; - rs_numpolyobjects = 0; - rs_bsptime = I_GetTimeMicros(); + ps_numbspcalls = 0; + ps_numpolyobjects = 0; + ps_bsptime = I_GetTimeMicros(); validcount++; @@ -5943,7 +5956,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) } #endif - rs_bsptime = I_GetTimeMicros() - rs_bsptime; + ps_bsptime = I_GetTimeMicros() - ps_bsptime; if (cv_glbatching.value) HWR_RenderBatches(); @@ -5958,22 +5971,22 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) #endif // Draw MD2 and sprites - rs_numsprites = gl_visspritecount; - rs_hw_spritesorttime = I_GetTimeMicros(); + ps_numsprites = gl_visspritecount; + ps_hw_spritesorttime = I_GetTimeMicros(); HWR_SortVisSprites(); - rs_hw_spritesorttime = I_GetTimeMicros() - rs_hw_spritesorttime; - rs_hw_spritedrawtime = I_GetTimeMicros(); + ps_hw_spritesorttime = I_GetTimeMicros() - ps_hw_spritesorttime; + ps_hw_spritedrawtime = I_GetTimeMicros(); HWR_DrawSprites(); - rs_hw_spritedrawtime = I_GetTimeMicros() - rs_hw_spritedrawtime; + ps_hw_spritedrawtime = I_GetTimeMicros() - ps_hw_spritedrawtime; #ifdef NEWCORONAS //Hurdler: they must be drawn before translucent planes, what about gl fog? HWR_DrawCoronas(); #endif - rs_numdrawnodes = 0; - rs_hw_nodesorttime = 0; - rs_hw_nodedrawtime = 0; + ps_numdrawnodes = 0; + ps_hw_nodesorttime = 0; + ps_hw_nodedrawtime = 0; if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything { HWR_CreateDrawNodes(); @@ -6011,34 +6024,32 @@ static CV_PossibleValue_t glfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSA {0, NULL}}; CV_PossibleValue_t granisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}}; -consvar_t cv_glshaders = {"gr_shaders", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glshaders = CVAR_INIT ("gr_shaders", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_fovchange = CVAR_INIT ("gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL); #ifdef ALAM_LIGHTING -consvar_t cv_gldynamiclighting = {"gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glstaticlighting = {"gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glcoronas = {"gr_coronas", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glcoronasize = {"gr_coronasize", "1", CV_SAVE|CV_FLOAT, 0, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_gldynamiclighting = CVAR_INIT ("gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glstaticlighting = CVAR_INIT ("gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glcoronas = CVAR_INIT ("gr_coronas", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glcoronasize = CVAR_INIT ("gr_coronasize", "1", CV_SAVE|CV_FLOAT, 0, NULL); #endif -consvar_t cv_glmodels = {"gr_models", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glmodelinterpolation = {"gr_modelinterpolation", "Sometimes", CV_SAVE, grmodelinterpolation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glmodellighting = {"gr_modellighting", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glmodels = CVAR_INIT ("gr_models", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glmodelinterpolation = CVAR_INIT ("gr_modelinterpolation", "Sometimes", CV_SAVE, grmodelinterpolation_cons_t, NULL); +consvar_t cv_glmodellighting = CVAR_INIT ("gr_modellighting", "Off", CV_SAVE, CV_OnOff, NULL); -consvar_t cv_glshearing = {"gr_shearing", "Off", CV_SAVE, grshearing_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glspritebillboarding = {"gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glfakecontrast = {"gr_fakecontrast", "Smooth", CV_SAVE, grfakecontrast_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glslopecontrast = {"gr_slopecontrast", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glshearing = CVAR_INIT ("gr_shearing", "Off", CV_SAVE, grshearing_cons_t, NULL); +consvar_t cv_glspritebillboarding = CVAR_INIT ("gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glskydome = CVAR_INIT ("gr_skydome", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_glfakecontrast = CVAR_INIT ("gr_fakecontrast", "Smooth", CV_SAVE, grfakecontrast_cons_t, NULL); +consvar_t cv_glslopecontrast = CVAR_INIT ("gr_slopecontrast", "Off", CV_SAVE, CV_OnOff, NULL); -consvar_t cv_glfiltermode = {"gr_filtermode", "Nearest", CV_SAVE|CV_CALL, glfiltermode_cons_t, - CV_glfiltermode_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glanisotropicmode = {"gr_anisotropicmode", "1", CV_CALL, granisotropicmode_cons_t, - CV_glanisotropic_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glfiltermode = CVAR_INIT ("gr_filtermode", "Nearest", CV_SAVE|CV_CALL, glfiltermode_cons_t, CV_glfiltermode_OnChange); +consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_CALL, granisotropicmode_cons_t, CV_glanisotropic_OnChange); -consvar_t cv_glsolvetjoin = {"gr_solvetjoin", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL); -consvar_t cv_glbatching = {"gr_batching", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL); static void CV_glfiltermode_OnChange(void) { @@ -6077,7 +6088,6 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glfiltermode); CV_RegisterVar(&cv_glsolvetjoin); - CV_RegisterVar(&cv_renderstats); CV_RegisterVar(&cv_glbatching); #ifndef NEWCLIP @@ -6106,7 +6116,6 @@ void HWR_Startup(void) // do this once if (!startupdone) { - INT32 i; CONS_Printf("HWR_Startup()...\n"); HWR_InitPolyPool(); @@ -6117,10 +6126,8 @@ void HWR_Startup(void) HWR_InitLight(); #endif - // read every custom shader - for (i = 0; i < numwadfiles; i++) - HWR_ReadShaders(i, (wadfiles[i]->type == RET_PK3)); - if (!HWR_LoadShaders()) + HWR_LoadAllCustomShaders(); + if (!HWR_CompileShaders()) gl_shadersavailable = false; } @@ -6212,7 +6219,7 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend, pSurf->PolyColor.s.alpha = alpha; // put the alpha back after lighting - shader = 2; // wall shader + shader = SHADER_WALL; // wall shader if (blend & PF_Environment) blendmode |= PF_Occlude; // PF_Occlude must be used for solid objects @@ -6220,7 +6227,7 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend, if (fogwall) { blendmode |= PF_Fog; - shader = 6; // fog shader + shader = SHADER_FOG; // fog shader } blendmode |= PF_Modulated; // No PF_Occlude means overlapping (incorrect) transparency @@ -6405,13 +6412,7 @@ void HWR_DrawScreenFinalTexture(int width, int height) } // jimita 18032019 -typedef struct -{ - char type[16]; - INT32 id; -} shaderxlat_t; - -static inline UINT16 HWR_CheckShader(UINT16 wadnum) +static inline UINT16 HWR_FindShaderDefs(UINT16 wadnum) { UINT16 i; lumpinfo_t *lump_p; @@ -6424,12 +6425,34 @@ static inline UINT16 HWR_CheckShader(UINT16 wadnum) return INT16_MAX; } -boolean HWR_LoadShaders(void) +boolean HWR_CompileShaders(void) { - return HWD.pfnInitCustomShaders(); + return HWD.pfnCompileShaders(); } -void HWR_ReadShaders(UINT16 wadnum, boolean PK3) +customshaderxlat_t shaderxlat[] = +{ + {"Flat", SHADER_FLOOR}, + {"WallTexture", SHADER_WALL}, + {"Sprite", SHADER_SPRITE}, + {"Model", SHADER_MODEL}, + {"ModelLighting", SHADER_MODEL_LIGHTING}, + {"WaterRipple", SHADER_WATER}, + {"Fog", SHADER_FOG}, + {"Sky", SHADER_SKY}, + {NULL, 0}, +}; + +void HWR_LoadAllCustomShaders(void) +{ + INT32 i; + + // read every custom shader + for (i = 0; i < numwadfiles; i++) + HWR_LoadCustomShadersFromFile(i, (wadfiles[i]->type == RET_PK3)); +} + +void HWR_LoadCustomShadersFromFile(UINT16 wadnum, boolean PK3) { UINT16 lump; char *shaderdef, *line; @@ -6440,19 +6463,7 @@ void HWR_ReadShaders(UINT16 wadnum, boolean PK3) int shadertype = 0; int i; - #define SHADER_TYPES 7 - shaderxlat_t shaderxlat[SHADER_TYPES] = - { - {"Flat", 1}, - {"WallTexture", 2}, - {"Sprite", 3}, - {"Model", 4}, - {"WaterRipple", 5}, - {"Fog", 6}, - {"Sky", 7}, - }; - - lump = HWR_CheckShader(wadnum); + lump = HWR_FindShaderDefs(wadnum); if (lump == INT16_MAX) return; @@ -6478,7 +6489,7 @@ void HWR_ReadShaders(UINT16 wadnum, boolean PK3) value = strtok(NULL, "\r\n "); if (!value) { - CONS_Alert(CONS_WARNING, "HWR_ReadShaders: Missing shader type (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); + CONS_Alert(CONS_WARNING, "HWR_LoadCustomShadersFromFile: Missing shader type (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); stoken = strtok(NULL, "\r\n"); // skip end of line goto skip_lump; } @@ -6497,19 +6508,19 @@ skip_lump: value = strtok(NULL, "\r\n= "); if (!value) { - CONS_Alert(CONS_WARNING, "HWR_ReadShaders: Missing shader target (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); + CONS_Alert(CONS_WARNING, "HWR_LoadCustomShadersFromFile: Missing shader target (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); stoken = strtok(NULL, "\r\n"); // skip end of line goto skip_field; } if (!shadertype) { - CONS_Alert(CONS_ERROR, "HWR_ReadShaders: Missing shader type (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); + CONS_Alert(CONS_ERROR, "HWR_LoadCustomShadersFromFile: Missing shader type (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum); Z_Free(line); return; } - for (i = 0; i < SHADER_TYPES; i++) + for (i = 0; shaderxlat[i].type; i++) { if (!stricmp(shaderxlat[i].type, stoken)) { @@ -6535,7 +6546,7 @@ skip_lump: if (shader_lumpnum == INT16_MAX) { - CONS_Alert(CONS_ERROR, "HWR_ReadShaders: Missing shader source %s (file %s, line %d)\n", shader_lumpname, wadfiles[wadnum]->filename, linenum); + CONS_Alert(CONS_ERROR, "HWR_LoadCustomShadersFromFile: Missing shader source %s (file %s, line %d)\n", shader_lumpname, wadfiles[wadnum]->filename, linenum); Z_Free(shader_lumpname); continue; } @@ -6561,4 +6572,22 @@ skip_field: return; } +const char *HWR_GetShaderName(INT32 shader) +{ + INT32 i; + + if (shader) + { + for (i = 0; shaderxlat[i].type; i++) + { + if (shaderxlat[i].id == shader) + return shaderxlat[i].type; + } + + return "Unknown"; + } + + return "Default"; +} + #endif // HWRENDER diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index d70bb6d72..85072dfd9 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -70,8 +70,13 @@ void HWR_DrawScreenFinalTexture(int width, int height); void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap); UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap); // Let's see if this can work -void HWR_ReadShaders(UINT16 wadnum, boolean PK3); -boolean HWR_LoadShaders(void); +boolean HWR_CompileShaders(void); + +void HWR_LoadAllCustomShaders(void); +void HWR_LoadCustomShadersFromFile(UINT16 wadnum, boolean PK3); +const char *HWR_GetShaderName(INT32 shader); + +extern customshaderxlat_t shaderxlat[]; extern CV_PossibleValue_t granisotropicmode_cons_t[]; @@ -108,21 +113,22 @@ extern FTransform atransform; // Render stats -extern int rs_hw_nodesorttime; -extern int rs_hw_nodedrawtime; -extern int rs_hw_spritesorttime; -extern int rs_hw_spritedrawtime; +extern int ps_hw_skyboxtime; +extern int ps_hw_nodesorttime; +extern int ps_hw_nodedrawtime; +extern int ps_hw_spritesorttime; +extern int ps_hw_spritedrawtime; // Render stats for batching -extern int rs_hw_numpolys; -extern int rs_hw_numverts; -extern int rs_hw_numcalls; -extern int rs_hw_numshaders; -extern int rs_hw_numtextures; -extern int rs_hw_numpolyflags; -extern int rs_hw_numcolors; -extern int rs_hw_batchsorttime; -extern int rs_hw_batchdrawtime; +extern int ps_hw_numpolys; +extern int ps_hw_numverts; +extern int ps_hw_numcalls; +extern int ps_hw_numshaders; +extern int ps_hw_numtextures; +extern int ps_hw_numpolyflags; +extern int ps_hw_numcolors; +extern int ps_hw_batchsorttime; +extern int ps_hw_batchdrawtime; extern boolean gl_shadersavailable; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index fa5156758..0c1b14b20 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -92,7 +92,13 @@ static void md2_freeModel (model_t *model) static model_t *md2_readModel(const char *filename) { //Filename checking fixed ~Monster Iestyn and Golden - return LoadModel(va("%s"PATHSEP"%s", srb2home, filename), PU_STATIC); + if (FIL_FileExists(va("%s"PATHSEP"%s", srb2home, filename))) + return LoadModel(va("%s"PATHSEP"%s", srb2home, filename), PU_STATIC); + + if (FIL_FileExists(va("%s"PATHSEP"%s", srb2path, filename))) + return LoadModel(va("%s"PATHSEP"%s", srb2path, filename), PU_STATIC); + + return NULL; } static inline void md2_printModelInfo (model_t *model) @@ -160,8 +166,12 @@ static GLTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_ png_FILE = fopen(pngfilename, "rb"); if (!png_FILE) { + pngfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2path, filename); + FIL_ForceExtension(pngfilename, ".png"); + png_FILE = fopen(pngfilename, "rb"); //CONS_Debug(DBG_RENDER, "M_SavePNG: Error on opening %s for loading\n", filename); - return 0; + if (!png_FILE) + return 0; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, @@ -288,7 +298,13 @@ static GLTextureFormat_t PCX_Load(const char *filename, int *w, int *h, FIL_ForceExtension(pcxfilename, ".pcx"); file = fopen(pcxfilename, "rb"); if (!file) - return 0; + { + pcxfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2path, filename); + FIL_ForceExtension(pcxfilename, ".pcx"); + file = fopen(pcxfilename, "rb"); + if (!file) + return 0; + } if (fread(&header, sizeof (PcxHeader), 1, file) != 1) { @@ -493,11 +509,15 @@ void HWR_InitModels(void) if (!f) { - CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); - nomd2s = true; - return; + f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt"); + if (!f) + { + CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); + nomd2s = true; + return; + } } - + // length of the player model prefix prefixlen = strlen(PLAYERMODELPREFIX); @@ -569,9 +589,13 @@ void HWR_AddPlayerModel(int skin) // For skins that were added after startup if (!f) { - CONS_Printf("Error while loading models.dat\n"); - nomd2s = true; - return; + f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt"); + if (!f) + { + CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); + nomd2s = true; + return; + } } // length of the player model prefix @@ -624,9 +648,13 @@ void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after s if (!f) { - CONS_Printf("Error while loading models.dat\n"); - nomd2s = true; - return; + f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt"); + if (!f) + { + CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); + nomd2s = true; + return; + } } // Check for any models that match the names of sprite names! @@ -1177,6 +1205,7 @@ static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t return spr2; } +// Adjust texture coords of model to fit into a patch's max_s and max_t static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch) { int i; @@ -1185,24 +1214,35 @@ static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch) int j; mesh_t *mesh = &model->meshes[i]; int numVertices; - float *uvPtr = mesh->uvs; + float *uvReadPtr = mesh->originaluvs; + float *uvWritePtr; // i dont know if this is actually possible, just logical conclusion of structure in CreateModelVBOs - if (!mesh->frames && !mesh->tinyframes) return; + if (!mesh->frames && !mesh->tinyframes) continue; if (mesh->frames) // again CreateModelVBO and CreateModelVBOTiny iterate like this so I'm gonna do that too numVertices = mesh->numTriangles * 3; else numVertices = mesh->numVertices; + // if originaluvs points to uvs, we need to allocate new memory for adjusted uvs + // the old uvs are kept around for use in possible readjustments + if (mesh->uvs == mesh->originaluvs) + mesh->uvs = Z_Malloc(numVertices * 2 * sizeof(float), PU_STATIC, NULL); + + uvWritePtr = mesh->uvs; + // fix uvs (texture coordinates) to take into account that the actual texture // has empty space added until the next power of two for (j = 0; j < numVertices; j++) { - *uvPtr++ *= gpatch->max_s; - *uvPtr++ *= gpatch->max_t; + *uvWritePtr++ = *uvReadPtr++ * gpatch->max_s; + *uvWritePtr++ = *uvReadPtr++ * gpatch->max_t; } } + // Save the values we adjusted the uvs for + model->max_s = gpatch->max_s; + model->max_t = gpatch->max_t; } // @@ -1226,6 +1266,10 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (spr->precip) return false; + // Lactozilla: Disallow certain models from rendering + if (!HWR_AllowModel(spr->mobj)) + return false; + memset(&p, 0x00, sizeof(FTransform)); // MD2 colormap fix @@ -1330,10 +1374,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (md2->model) { md2_printModelInfo(md2->model); - // if model uses sprite patch as texture, then + // If model uses sprite patch as texture, then // adjust texture coordinates to take power of two textures into account if (!gpatch || !gpatch->mipmap->format) adjustTextureCoords(md2->model, spr->gpatch); + // note down the max_s and max_t that end up in the VBO + md2->model->vbo_max_s = md2->model->max_s; + md2->model->vbo_max_t = md2->model->max_t; HWD.pfnCreateModelVBOs(md2->model); } else @@ -1344,10 +1391,6 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } } - // Lactozilla: Disallow certain models from rendering - if (!HWR_AllowModel(spr->mobj)) - return false; - //HWD.pfnSetBlend(blend); // This seems to actually break translucency? finalscale = md2->scale; //Hurdler: arf, I don't like that implementation at all... too much crappy @@ -1391,6 +1434,14 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) { // Sprite gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE); + // Check if sprite dimensions are different from previously used sprite. + // If so, uvs need to be readjusted. + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(gpatch->max_s), &(md2->model->max_s), sizeof(md2->model->max_s)) != 0 || + memcmp(&(gpatch->max_t), &(md2->model->max_t), sizeof(md2->model->max_t)) != 0) + adjustTextureCoords(md2->model, gpatch); HWR_GetMappedPatch(gpatch, spr->colormap); } @@ -1549,7 +1600,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) p.mirror = atransform.mirror; // from Kart #endif - HWD.pfnSetShader(4); // model shader + HWD.pfnSetShader(SHADER_MODEL); // model shader HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf); } diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c index ac73f8aca..4ed03744b 100644 --- a/src/hardware/hw_model.c +++ b/src/hardware/hw_model.c @@ -221,6 +221,15 @@ model_t *LoadModel(const char *filename, int ztag) material->shininess = 25.0f; } + // Set originaluvs to point to uvs + for (i = 0; i < model->numMeshes; i++) + model->meshes[i].originaluvs = model->meshes[i].uvs; + + model->max_s = 1.0; + model->max_t = 1.0; + model->vbo_max_s = 1.0; + model->vbo_max_t = 1.0; + return model; } diff --git a/src/hardware/hw_model.h b/src/hardware/hw_model.h index 2a5240bde..6b39eb24d 100644 --- a/src/hardware/hw_model.h +++ b/src/hardware/hw_model.h @@ -59,6 +59,11 @@ typedef struct mesh_s int numTriangles; float *uvs; + // if uv adjustment is needed, uvs is changed to point to adjusted ones and + // this one retains the originals + // note: this member has been added with the assumption that models are never freed. + // (UnloadModel is called by nobody at the time of writing.) + float *originaluvs; float *lightuvs; int numFrames; @@ -99,6 +104,15 @@ typedef struct model_s char *framenames; boolean interpolate[256]; modelspr2frames_t *spr2frames; + + // the max_s and max_t values that the uvs are currently adjusted to + // (if a sprite is used as a texture) + float max_s; + float max_t; + // These are the values that the uvs in the VBO have been adjusted to. + // If they are not same as max_s and max_t, then the VBO won't be used. + float vbo_max_s; + float vbo_max_t; } model_t; extern int numModels; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 83087bbbb..db3c6a17d 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -91,9 +91,10 @@ static GLuint startScreenWipe = 0; static GLuint endScreenWipe = 0; static GLuint finalScreenTexture = 0; -// Lactozilla: Set shader programs and uniforms +// Lactozilla: Shader functions static void *Shader_Load(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade); static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade); +static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum); static GLRGBAFloat shader_defaultcolor = {1.0f, 1.0f, 1.0f, 1.0f}; @@ -578,15 +579,12 @@ static PFNglUniform2fv pglUniform2fv; static PFNglUniform3fv pglUniform3fv; static PFNglGetUniformLocation pglGetUniformLocation; -#define MAXSHADERS 16 -#define MAXSHADERPROGRAMS 16 - // 18032019 -static char *gl_customvertexshaders[MAXSHADERS]; -static char *gl_customfragmentshaders[MAXSHADERS]; static GLuint gl_currentshaderprogram = 0; static boolean gl_shaderprogramchanged = true; +static shadersource_t gl_customshaders[HWR_MAXSHADERS]; + // 13062019 typedef enum { @@ -610,17 +608,59 @@ typedef struct gl_shaderprogram_s boolean custom; GLint uniforms[gluniform_max+1]; } gl_shaderprogram_t; -static gl_shaderprogram_t gl_shaderprograms[MAXSHADERPROGRAMS]; +static gl_shaderprogram_t gl_shaderprograms[HWR_MAXSHADERS]; // Shader info static INT32 shader_leveltime = 0; -// ======================== -// Fragment shader macros -// ======================== +// ================ +// Vertex shaders +// ================ // -// GLSL Software fragment shader +// Generic vertex shader +// + +#define GLSL_DEFAULT_VERTEX_SHADER \ + "void main()\n" \ + "{\n" \ + "gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \ + "gl_FrontColor = gl_Color;\n" \ + "gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \ + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \ + "}\0" + +// replicates the way fixed function lighting is used by the model lighting option, +// stores the lighting result to gl_Color +// (ambient lighting of 0.75 and diffuse lighting from above) +#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \ + "void main()\n" \ + "{\n" \ + "float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \ + "float light = 0.75 + max(nDotVP, 0.0);\n" \ + "gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \ + "gl_FrontColor = vec4(light, light, light, 1.0);\n" \ + "gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \ + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \ + "}\0" + +// ================== +// Fragment shaders +// ================== + +// +// Generic fragment shader +// + +#define GLSL_DEFAULT_FRAGMENT_SHADER \ + "uniform sampler2D tex;\n" \ + "uniform vec4 poly_color;\n" \ + "void main(void) {\n" \ + "gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * poly_color;\n" \ + "}\0" + +// +// Software fragment shader // #define GLSL_DOOM_COLORMAP \ @@ -761,22 +801,10 @@ static INT32 shader_leveltime = 0; "gl_FragColor = final_color;\n" \ "}\0" -// -// GLSL generic fragment shader -// - -#define GLSL_DEFAULT_FRAGMENT_SHADER \ - "uniform sampler2D tex;\n" \ - "uniform vec4 poly_color;\n" \ - "void main(void) {\n" \ - "gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * poly_color;\n" \ - "}\0" - // // Sky fragment shader // Modulates poly_color with gl_Color // - #define GLSL_SKY_FRAGMENT_SHADER \ "uniform sampler2D tex;\n" \ "uniform vec4 poly_color;\n" \ @@ -784,97 +812,42 @@ static INT32 shader_leveltime = 0; "gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * gl_Color * poly_color;\n" \ "}\0" -static const char *fragment_shaders[] = { - // Default fragment shader - GLSL_DEFAULT_FRAGMENT_SHADER, +// ================ +// Shader sources +// ================ - // Floor fragment shader - GLSL_SOFTWARE_FRAGMENT_SHADER, +static struct { + const char *vertex; + const char *fragment; +} const gl_shadersources[] = { + // Default shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_DEFAULT_FRAGMENT_SHADER}, - // Wall fragment shader - GLSL_SOFTWARE_FRAGMENT_SHADER, + // Floor shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, - // Sprite fragment shader - GLSL_SOFTWARE_FRAGMENT_SHADER, + // Wall shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, - // Model fragment shader - GLSL_SOFTWARE_FRAGMENT_SHADER, + // Sprite shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, - // Water fragment shader - GLSL_WATER_FRAGMENT_SHADER, + // Model shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, - // Fog fragment shader - GLSL_FOG_FRAGMENT_SHADER, + // Model shader + diffuse lighting from above + {GLSL_MODEL_LIGHTING_VERTEX_SHADER, GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER}, - // Sky fragment shader - GLSL_SKY_FRAGMENT_SHADER, + // Water shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATER_FRAGMENT_SHADER}, - // Model fragment shader + diffuse lighting from above - GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER, + // Fog shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_FOG_FRAGMENT_SHADER}, - NULL, -}; + // Sky shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER}, -// ====================== -// Vertex shader macros -// ====================== - -// -// GLSL generic vertex shader -// - -#define GLSL_DEFAULT_VERTEX_SHADER \ - "void main()\n" \ - "{\n" \ - "gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \ - "gl_FrontColor = gl_Color;\n" \ - "gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \ - "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \ - "}\0" - -// replicates the way fixed function lighting is used by the model lighting option, -// stores the lighting result to gl_Color -// (ambient lighting of 0.75 and diffuse lighting from above) -#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \ - "void main()\n" \ - "{\n" \ - "float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \ - "float light = 0.75 + max(nDotVP, 0.0);\n" \ - "gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \ - "gl_FrontColor = vec4(light, light, light, 1.0);\n" \ - "gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \ - "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \ - "}\0" - -static const char *vertex_shaders[] = { - // Default vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Floor vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Wall vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Sprite vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Model vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Water vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Fog vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Sky vertex shader - GLSL_DEFAULT_VERTEX_SHADER, - - // Model vertex shader + diffuse lighting from above - GLSL_MODEL_LIGHTING_VERTEX_SHADER, - - NULL, + {NULL, NULL}, }; #endif // GL_SHADERS @@ -920,7 +893,7 @@ void SetupGLFunc4(void) } // jimita -EXPORT boolean HWRAPI(LoadShaders) (void) +EXPORT boolean HWRAPI(CompileShaders) (void) { #ifdef GL_SHADERS GLuint gl_vertShader, gl_fragShader; @@ -928,25 +901,23 @@ EXPORT boolean HWRAPI(LoadShaders) (void) if (!pglUseProgram) return false; - gl_customvertexshaders[0] = NULL; - gl_customfragmentshaders[0] = NULL; + gl_customshaders[0].vertex = NULL; + gl_customshaders[0].fragment = NULL; - for (i = 0; vertex_shaders[i] && fragment_shaders[i]; i++) + for (i = 0; gl_shadersources[i].vertex && gl_shadersources[i].fragment; i++) { gl_shaderprogram_t *shader; - const GLchar* vert_shader = vertex_shaders[i]; - const GLchar* frag_shader = fragment_shaders[i]; - boolean custom = ((gl_customvertexshaders[i] || gl_customfragmentshaders[i]) && (i > 0)); + const GLchar *vert_shader = gl_shadersources[i].vertex; + const GLchar *frag_shader = gl_shadersources[i].fragment; + boolean custom = ((gl_customshaders[i].vertex || gl_customshaders[i].fragment) && (i > 0)); // 18032019 - if (gl_customvertexshaders[i]) - vert_shader = gl_customvertexshaders[i]; - if (gl_customfragmentshaders[i]) - frag_shader = gl_customfragmentshaders[i]; + if (gl_customshaders[i].vertex) + vert_shader = gl_customshaders[i].vertex; + if (gl_customshaders[i].fragment) + frag_shader = gl_customshaders[i].fragment; - if (i >= MAXSHADERS) - break; - if (i >= MAXSHADERPROGRAMS) + if (i >= HWR_MAXSHADERS) break; shader = &gl_shaderprograms[i]; @@ -959,7 +930,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) gl_vertShader = pglCreateShader(GL_VERTEX_SHADER); if (!gl_vertShader) { - GL_MSG_Error("LoadShaders: Error creating vertex shader %d\n", i); + GL_MSG_Error("CompileShaders: Error creating vertex shader %s\n", HWR_GetShaderName(i)); continue; } @@ -970,15 +941,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) pglGetShaderiv(gl_vertShader, GL_COMPILE_STATUS, &result); if (result == GL_FALSE) { - GLchar* infoLog; - GLint logLength; - - pglGetShaderiv(gl_vertShader, GL_INFO_LOG_LENGTH, &logLength); - - infoLog = malloc(logLength); - pglGetShaderInfoLog(gl_vertShader, logLength, NULL, infoLog); - - GL_MSG_Error("LoadShaders: Error compiling vertex shader %d\n%s", i, infoLog); + Shader_CompileError("Error compiling vertex shader", gl_vertShader, i); continue; } @@ -988,7 +951,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) gl_fragShader = pglCreateShader(GL_FRAGMENT_SHADER); if (!gl_fragShader) { - GL_MSG_Error("LoadShaders: Error creating fragment shader %d\n", i); + GL_MSG_Error("CompileShaders: Error creating fragment shader %s\n", HWR_GetShaderName(i)); continue; } @@ -999,15 +962,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) pglGetShaderiv(gl_fragShader, GL_COMPILE_STATUS, &result); if (result == GL_FALSE) { - GLchar* infoLog; - GLint logLength; - - pglGetShaderiv(gl_fragShader, GL_INFO_LOG_LENGTH, &logLength); - - infoLog = malloc(logLength); - pglGetShaderInfoLog(gl_fragShader, logLength, NULL, infoLog); - - GL_MSG_Error("LoadShaders: Error compiling fragment shader %d\n%s", i, infoLog); + Shader_CompileError("Error compiling fragment shader", gl_fragShader, i); continue; } @@ -1028,7 +983,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) { shader->program = 0; shader->custom = false; - GL_MSG_Error("LoadShaders: Error linking shader program %d\n", i); + GL_MSG_Error("CompileShaders: Error linking shader program %s\n", HWR_GetShaderName(i)); continue; } @@ -1048,8 +1003,10 @@ EXPORT boolean HWRAPI(LoadShaders) (void) #undef GETUNI } -#endif return true; +#else + return false; +#endif } // @@ -1077,25 +1034,34 @@ EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value) // // Custom shader loading // -EXPORT void HWRAPI(LoadCustomShader) (int number, char *shader, size_t size, boolean fragment) +EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boolean isfragment) { #ifdef GL_SHADERS - if (!pglUseProgram) return; - if (number < 1 || number > MAXSHADERS) - I_Error("LoadCustomShader(): cannot load shader %d (max %d)", number, MAXSHADERS); + shadersource_t *shader; - if (fragment) - { - gl_customfragmentshaders[number] = malloc(size+1); - strncpy(gl_customfragmentshaders[number], shader, size); - gl_customfragmentshaders[number][size] = 0; + if (!pglUseProgram) + return; + + if (number < 1 || number > HWR_MAXSHADERS) + I_Error("LoadCustomShader: cannot load shader %d (min 1, max %d)", number, HWR_MAXSHADERS); + else if (code == NULL) + I_Error("LoadCustomShader: empty shader"); + + shader = &gl_customshaders[number]; + +#define COPYSHADER(source) { \ + if (shader->source) \ + free(shader->source); \ + shader->source = malloc(size+1); \ + strncpy(shader->source, code, size); \ + shader->source[size] = 0; \ } + + if (isfragment) + COPYSHADER(fragment) else - { - gl_customvertexshaders[number] = malloc(size+1); - strncpy(gl_customvertexshaders[number], shader, size); - gl_customvertexshaders[number][size] = 0; - } + COPYSHADER(vertex) + #else (void)number; (void)shader; @@ -1104,14 +1070,6 @@ EXPORT void HWRAPI(LoadCustomShader) (int number, char *shader, size_t size, boo #endif } -EXPORT boolean HWRAPI(InitCustomShaders) (void) -{ -#ifdef GL_SHADERS - KillShaders(); - return LoadShaders(); -#endif -} - EXPORT void HWRAPI(SetShader) (int shader) { #ifdef GL_SHADERS @@ -1119,9 +1077,9 @@ EXPORT void HWRAPI(SetShader) (int shader) { // If using model lighting, set the appropriate shader. // However don't override a custom shader. - // Should use an enum or something... - if (shader == 4 && model_lighting && !gl_shaderprograms[4].custom) - shader = 8; + if (shader == SHADER_MODEL && model_lighting + && !(gl_shaderprograms[SHADER_MODEL].custom && !gl_shaderprograms[SHADER_MODEL_LIGHTING].custom)) + shader = SHADER_MODEL_LIGHTING; if ((GLuint)shader != gl_currentshaderprogram) { gl_currentshaderprogram = shader; @@ -1146,9 +1104,23 @@ EXPORT void HWRAPI(UnSetShader) (void) #endif } -EXPORT void HWRAPI(KillShaders) (void) +EXPORT void HWRAPI(CleanShaders) (void) { - // unused......................... + INT32 i; + + for (i = 1; i < HWR_MAXSHADERS; i++) + { + shadersource_t *shader = &gl_customshaders[i]; + + if (shader->vertex) + free(shader->vertex); + + if (shader->fragment) + free(shader->fragment); + + shader->vertex = NULL; + shader->fragment = NULL; + } } // -----------------+ @@ -2017,6 +1989,25 @@ static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAF #endif } +static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum) +{ + GLchar *infoLog = NULL; + GLint logLength; + + pglGetShaderiv(program, GL_INFO_LOG_LENGTH, &logLength); + + if (logLength) + { + infoLog = malloc(logLength); + pglGetShaderInfoLog(program, logLength, NULL, infoLog); + } + + GL_MSG_Error("CompileShaders: %s (%s)\n%s", message, HWR_GetShaderName(shadernum), (infoLog ? infoLog : "")); + + if (infoLog) + free(infoLog); +} + // code that is common between DrawPolygon and DrawIndexedTriangles // the corona thing is there too, i have no idea if that stuff works with DrawIndexedTriangles and batching static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FBITFIELD PolyFlags) @@ -2535,6 +2526,8 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 boolean useTinyFrames; + boolean useVBO = true; + int i; // Because otherwise, scaling the screen negatively vertically breaks the lighting @@ -2678,6 +2671,15 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (useTinyFrames) pglScalef(1 / 64.0f, 1 / 64.0f, 1 / 64.0f); + // Don't use the VBO if it does not have the correct texture coordinates. + // (Can happen when model uses a sprite as a texture and the sprite changes) + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(model->vbo_max_s), &(model->max_s), sizeof(model->max_s)) != 0 || + memcmp(&(model->vbo_max_t), &(model->max_t), sizeof(model->max_t)) != 0) + useVBO = false; + pglEnableClientState(GL_NORMAL_ARRAY); for (i = 0; i < model->numMeshes; i++) @@ -2694,13 +2696,23 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (!nextframe || fpclassify(pol) == FP_ZERO) { - pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); - pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); - pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); - pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); - pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); - pglBindBuffer(GL_ARRAY_BUFFER, 0); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexPointer(3, GL_SHORT, 0, frame->vertices); + pglNormalPointer(GL_BYTE, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } } else { @@ -2736,21 +2748,25 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (!nextframe || fpclassify(pol) == FP_ZERO) { - // Zoom! Take advantage of just shoving the entire arrays to the GPU. -/* pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); - pglNormalPointer(GL_FLOAT, 0, frame->normals); - pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); - pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3);*/ + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); - pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); - pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); - pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); - pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); - - pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); - // No tinyframes, no mesh indices - //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); - pglBindBuffer(GL_ARRAY_BUFFER, 0); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + // No tinyframes, no mesh indices + //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); + pglNormalPointer(GL_FLOAT, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + } } else { diff --git a/src/http-mserv.c b/src/http-mserv.c index 47c2b5b0f..7c7d04495 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" @@ -34,20 +36,25 @@ Documentation available here. static void MasterServer_Debug_OnChange (void); -consvar_t cv_masterserver_timeout = { - "masterserver_timeout", "5", CV_SAVE, CV_Unsigned, - NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ -}; +consvar_t cv_masterserver_timeout = CVAR_INIT +( + "masterserver_timeout", "5", CV_SAVE, CV_Unsigned, + NULL +); -consvar_t cv_masterserver_debug = { +consvar_t cv_masterserver_debug = CVAR_INIT +( "masterserver_debug", "Off", CV_SAVE|CV_CALL, CV_OnOff, - MasterServer_Debug_OnChange, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ -}; + MasterServer_Debug_OnChange +); -consvar_t cv_masterserver_token = { - "masterserver_token", "", CV_SAVE, NULL, - NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ -}; +consvar_t cv_masterserver_token = CVAR_INIT +( + "masterserver_token", "", CV_SAVE, NULL, + NULL +); + +#ifdef MASTERSERVER static int hms_started; @@ -468,6 +475,7 @@ HMS_list_servers (void) { struct HMS_buffer *hms; + char *list; char *p; hms = HMS_connect("servers"); @@ -477,11 +485,17 @@ HMS_list_servers (void) if (HMS_do(hms)) { - p = &hms->buffer[strlen(hms->buffer)]; - while (*--p == '\n') - ; + list = curl_easy_unescape(hms->curl, hms->buffer, 0, NULL); - CONS_Printf("%s\n", hms->buffer); + p = strtok(list, "\n"); + + while (p != NULL) + { + CONS_Printf("\x80%s\n", p); + p = strtok(NULL, "\n"); + } + + curl_free(list); } HMS_end(hms); @@ -664,10 +678,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/hu_stuff.c b/src/hu_stuff.c index b61192533..72fb9272d 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -330,8 +330,6 @@ void HU_Init(void) // set shift translation table shiftxform = english_shiftxform; - - HU_LoadGraphics(); } static inline void HU_Stop(void) @@ -1466,7 +1464,7 @@ static void HU_drawMiniChat(void) if (cv_chatbacktint.value) // on request of wolfy V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); - V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap); + V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, true, colormap); } dx += charwidth; @@ -1559,7 +1557,7 @@ static void HU_drawChatLog(INT32 offset) else { if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy))) - V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap); + V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, true, colormap); else j++; // don't forget to increment this or we'll get stuck in the limbo. } @@ -1659,7 +1657,7 @@ static void HU_DrawChat(void) ++i; else { - V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag)); + V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, true, V_GetStringColormap(talk[i]|cflag)); i++; } @@ -1677,7 +1675,7 @@ static void HU_DrawChat(void) typelines = 1; if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4) - V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, true, NULL); while (w_chat[i]) { @@ -1687,7 +1685,7 @@ static void HU_DrawChat(void) INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down. INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight); if (hu_tick < 4) - V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, true, NULL); if (cursorx == chatx+1 && saylen == i) // a weirdo hack { @@ -1700,7 +1698,7 @@ static void HU_DrawChat(void) if (w_chat[i] < HU_FONTSTART) ++i; else - V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, true, NULL); c += charwidth; if (c > boxw-(charwidth*2) && !skippedline) @@ -1825,7 +1823,7 @@ static void HU_DrawChat_Old(void) } if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4) - V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value); + V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, true); i = 0; while (w_chat[i]) @@ -1835,7 +1833,7 @@ static void HU_DrawChat_Old(void) { INT32 cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX); // we may have to go down. INT32 cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight); - V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value); + V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, true); } //Hurdler: isn't it better like that? @@ -2038,9 +2036,6 @@ static void HU_DrawDemoInfo(void) // void HU_Drawer(void) { - if (needpatchrecache) - R_ReloadHUDGraphics(); - #ifndef NONET // draw chat string plus cursor if (chat_on) diff --git a/src/i_threads.h b/src/i_threads.h index 45a3dcc3e..ecb9fce67 100644 --- a/src/i_threads.h +++ b/src/i_threads.h @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 KART +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // diff --git a/src/info.c b/src/info.c index fccd1b269..cb5fb0889 100644 --- a/src/info.c +++ b/src/info.c @@ -5139,7 +5139,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOCLIP|MF_SPECIAL, // flags + MF_SPECIAL, // flags S_NULL // raisestate }, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 5d1db5641..674de64b0 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -33,9 +33,12 @@ #include "lua_libs.h" #include "lua_hud.h" // hud_running errors #include "taglist.h" // P_FindSpecialLineFromTag +#include "lua_hook.h" // hook_cmd_running errors #define NOHUD if (hud_running)\ -return luaL_error(L, "HUD rendering code should not call this function!"); +return luaL_error(L, "HUD rendering code should not call this function!");\ +else if (hook_cmd_running)\ +return luaL_error(L, "CMD building code should not call this function!"); boolean luaL_checkboolean(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TBOOLEAN); @@ -171,8 +174,13 @@ static const struct { {META_SEG, "seg_t"}, {META_NODE, "node_t"}, #endif + {META_SLOPE, "slope_t"}, + {META_VECTOR2, "vector2_t"}, + {META_VECTOR3, "vector3_t"}, {META_MAPHEADER, "mapheader_t"}, + {META_POLYOBJ, "polyobj_t"}, + {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, @@ -911,44 +919,52 @@ static int lib_pMaceRotate(lua_State *L) static int lib_pRailThinker(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_RailThinker(mobj)); + P_SetTarget(&tmthing, ptmthing); return 1; } static int lib_pXYMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_XYMovement(actor); + P_SetTarget(&tmthing, ptmthing); return 0; } static int lib_pRingXYMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_RingXYMovement(actor); + P_SetTarget(&tmthing, ptmthing); return 0; } static int lib_pSceneryXYMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_SceneryXYMovement(actor); + P_SetTarget(&tmthing, ptmthing); return 0; } @@ -960,6 +976,7 @@ static int lib_pZMovement(lua_State *L) if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_ZMovement(actor)); + P_CheckPosition(actor, actor->x, actor->y); return 1; } @@ -971,6 +988,7 @@ static int lib_pRingZMovement(lua_State *L) if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_RingZMovement(actor); + P_CheckPosition(actor, actor->x, actor->y); return 0; } @@ -982,6 +1000,7 @@ static int lib_pSceneryZMovement(lua_State *L) if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_SceneryZMovement(actor)); + P_CheckPosition(actor, actor->x, actor->y); return 1; } @@ -993,6 +1012,7 @@ static int lib_pPlayerZMovement(lua_State *L) if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_PlayerZMovement(actor); + P_CheckPosition(actor, actor->x, actor->y); return 0; } @@ -1716,6 +1736,18 @@ static int lib_pFloorzAtPos(lua_State *L) return 1; } +static int lib_pCeilingzAtPos(lua_State *L) +{ + fixed_t x = luaL_checkfixed(L, 1); + fixed_t y = luaL_checkfixed(L, 2); + fixed_t z = luaL_checkfixed(L, 3); + fixed_t height = luaL_checkfixed(L, 4); + //HUDSAFE + INLEVEL + lua_pushfixed(L, P_CeilingzAtPos(x, y, z, height)); + return 1; +} + static int lib_pDoSpring(lua_State *L) { mobj_t *spring = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -2587,30 +2619,56 @@ static int lib_rGetNameByColor(lua_State *L) // S_SOUND //////////// +static int GetValidSoundOrigin(lua_State *L, void **origin) +{ + const char *type; + + lua_settop(L, 1); + type = GetUserdataUType(L); + + if (fasticmp(type, "mobj_t")) + { + *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + if (!(*origin)) + return LUA_ErrInvalid(L, "mobj_t"); + return 1; + } + else if (fasticmp(type, "sector_t")) + { + *origin = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); + if (!(*origin)) + return LUA_ErrInvalid(L, "sector_t"); + + *origin = &((sector_t *)(*origin))->soundorg; + return 1; + } + + return LUA_ErrInvalid(L, "mobj_t/sector_t"); +} + static int lib_sStartSound(lua_State *L) { - const void *origin = NULL; + void *origin = NULL; sfxenum_t sound_id = luaL_checkinteger(L, 2); player_t *player = NULL; //NOHUD + if (sound_id >= NUMSFX) return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1); - if (!lua_isnil(L, 1)) - { - origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); - } + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) { player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); if (!player) return LUA_ErrInvalid(L, "player_t"); } + if (!lua_isnil(L, 1)) + if (!GetValidSoundOrigin(L, &origin)) + return 0; if (!player || P_IsLocalPlayer(player)) { - if (hud_running) - origin = NULL; // HUD rendering startsound shouldn't have an origin, just remove it instead of having a retarded error. + if (hud_running || hook_cmd_running) + origin = NULL; // HUD rendering and CMD building startsound shouldn't have an origin, just remove it instead of having a retarded error. S_StartSound(origin, sound_id); } @@ -2619,18 +2677,12 @@ static int lib_sStartSound(lua_State *L) static int lib_sStartSoundAtVolume(lua_State *L) { - const void *origin = NULL; + void *origin = NULL; sfxenum_t sound_id = luaL_checkinteger(L, 2); INT32 volume = (INT32)luaL_checkinteger(L, 3); player_t *player = NULL; //NOHUD - if (!lua_isnil(L, 1)) - { - origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); - } if (sound_id >= NUMSFX) return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1); if (!lua_isnone(L, 4) && lua_isuserdata(L, 4)) @@ -2639,30 +2691,37 @@ static int lib_sStartSoundAtVolume(lua_State *L) if (!player) return LUA_ErrInvalid(L, "player_t"); } + if (!lua_isnil(L, 1)) + if (!GetValidSoundOrigin(L, &origin)) + return LUA_ErrInvalid(L, "mobj_t/sector_t"); + if (!player || P_IsLocalPlayer(player)) - S_StartSoundAtVolume(origin, sound_id, volume); + S_StartSoundAtVolume(origin, sound_id, volume); return 0; } static int lib_sStopSound(lua_State *L) { - void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + void *origin = NULL; //NOHUD - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); + if (!GetValidSoundOrigin(L, &origin)) + return LUA_ErrInvalid(L, "mobj_t/sector_t"); + S_StopSound(origin); return 0; } static int lib_sStopSoundByID(lua_State *L) { - void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + void *origin = NULL; sfxenum_t sound_id = luaL_checkinteger(L, 2); //NOHUD - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); + if (sound_id >= NUMSFX) return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1); + if (!lua_isnil(L, 1)) + if (!GetValidSoundOrigin(L, &origin)) + return LUA_ErrInvalid(L, "mobj_t/sector_t"); S_StopSoundByID(origin, sound_id); return 0; @@ -2888,11 +2947,12 @@ static int lib_sSetMusicPosition(lua_State *L) static int lib_sOriginPlaying(lua_State *L) { - void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + void *origin = NULL; //NOHUD INLEVEL - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); + if (!GetValidSoundOrigin(L, &origin)) + return LUA_ErrInvalid(L, "mobj_t/sector_t"); + lua_pushboolean(L, S_OriginPlaying(origin)); return 1; } @@ -2909,14 +2969,15 @@ static int lib_sIdPlaying(lua_State *L) static int lib_sSoundPlaying(lua_State *L) { - void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + void *origin = NULL; sfxenum_t id = luaL_checkinteger(L, 2); //NOHUD INLEVEL - if (!origin) - return LUA_ErrInvalid(L, "mobj_t"); if (id >= NUMSFX) return luaL_error(L, "sfx %d out of range (0 - %d)", id, NUMSFX-1); + if (!GetValidSoundOrigin(L, &origin)) + return LUA_ErrInvalid(L, "mobj_t/sector_t"); + lua_pushboolean(L, S_SoundPlaying(origin, id)); return 1; } @@ -3561,6 +3622,7 @@ static luaL_Reg lib[] = { {"P_CheckHoopPosition",lib_pCheckHoopPosition}, {"P_RadiusAttack",lib_pRadiusAttack}, {"P_FloorzAtPos",lib_pFloorzAtPos}, + {"P_CeilingzAtPos",lib_pCeilingzAtPos}, {"P_DoSpring",lib_pDoSpring}, // p_inter diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index 5aae73284..1949d56bb 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous. +// Copyright (C) 2016-2020 by Iestyn "Monster Iestyn" Jealous. // Copyright (C) 2016-2020 by Sonic Team Junior. // // This program is free software distributed under the @@ -13,6 +13,7 @@ #include "doomdef.h" #include "p_local.h" #include "r_main.h" // validcount +#include "p_polyobj.h" #include "lua_script.h" #include "lua_libs.h" //#include "lua_hud.h" // hud_running errors @@ -20,6 +21,7 @@ static const char *const search_opt[] = { "objects", "lines", + "polyobjs", NULL}; // a quickly-made function pointer typedef used by lib_searchBlockmap... @@ -167,6 +169,55 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th return 0; // Everything was checked. } +// Helper function for "polyobjs" search +static UINT8 lib_searchBlockmap_PolyObjs(lua_State *L, INT32 x, INT32 y, mobj_t *thing) +{ + INT32 offset; + polymaplink_t *plink; // haleyjd 02/22/06 + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + return 0; + + offset = y*bmapwidth + x; + + // haleyjd 02/22/06: consider polyobject lines + plink = polyblocklinks[offset]; + + while (plink) + { + polyobj_t *po = plink->po; + + if (po->validcount != validcount) // if polyobj hasn't been checked + { + po->validcount = validcount; + + lua_pushvalue(L, 1); + LUA_PushUserdata(L, thing, META_MOBJ); + LUA_PushUserdata(L, po, META_POLYOBJ); + if (lua_pcall(gL, 2, 1, 0)) { + if (!blockfuncerror || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + blockfuncerror = true; + return 0; // *shrugs* + } + if (!lua_isnil(gL, -1)) + { // if nil, continue + if (lua_toboolean(gL, -1)) + return 2; // stop whole search + else + return 1; // stop block search + } + lua_pop(gL, 1); + if (P_MobjWasRemoved(thing)) + return 2; + } + plink = (polymaplink_t *)(plink->link.next); + } + + return 0; // Everything was checked. +} + // The searchBlockmap function // arguments: searchBlockmap(searchtype, function, mobj, [x1, x2, y1, y2]) // return value: @@ -195,6 +246,9 @@ static int lib_searchBlockmap(lua_State *L) case 1: // "lines" searchFunc = lib_searchBlockmap_Lines; break; + case 2: // "polyobjs" + searchFunc = lib_searchBlockmap_PolyObjs; + break; } // the mobj we are searching around, the "calling" mobj we could say diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index fae4f2d14..c6b082930 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -432,6 +432,46 @@ static int lib_cvFindVar(lua_State *L) return 1; } +static int CVarSetFunction +( + lua_State *L, + void (*Set)(consvar_t *, const char *), + void (*SetValue)(consvar_t *, INT32) +){ + consvar_t *cvar = (consvar_t *)luaL_checkudata(L, 1, META_CVAR); + + switch (lua_type(L, 2)) + { + case LUA_TSTRING: + (*Set)(cvar, lua_tostring(L, 2)); + break; + case LUA_TNUMBER: + (*SetValue)(cvar, (INT32)lua_tonumber(L, 2)); + break; + default: + return luaL_typerror(L, 1, "string or number"); + } + + return 0; +} + +static int lib_cvSet(lua_State *L) +{ + return CVarSetFunction(L, CV_Set, CV_SetValue); +} + +static int lib_cvStealthSet(lua_State *L) +{ + return CVarSetFunction(L, CV_StealthSet, CV_StealthSetValue); +} + +static int lib_cvAddValue(lua_State *L) +{ + consvar_t *cvar = (consvar_t *)luaL_checkudata(L, 1, META_CVAR); + CV_AddValue(cvar, (INT32)luaL_checknumber(L, 2)); + return 0; +} + // CONS_Printf for a single player // Use 'print' in baselib for a global message. static int lib_consPrintf(lua_State *L) @@ -472,6 +512,9 @@ static luaL_Reg lib[] = { {"COM_BufInsertText", lib_comBufInsertText}, {"CV_RegisterVar", lib_cvRegisterVar}, {"CV_FindVar", lib_cvFindVar}, + {"CV_Set", lib_cvSet}, + {"CV_StealthSet", lib_cvStealthSet}, + {"CV_AddValue", lib_cvAddValue}, {"CONS_Printf", lib_consPrintf}, {NULL, NULL} }; diff --git a/src/lua_hook.h b/src/lua_hook.h index 48f6cab32..47850812f 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -59,11 +59,14 @@ enum hook { hook_PlayerThink, hook_ShouldJingleContinue, hook_GameQuit, + hook_PlayerCmd, hook_MAX // last hook }; extern const char *const hookNames[]; +extern boolean hook_cmd_running; + void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load) void LUAh_MapLoad(void); // Hook for map load void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer @@ -113,4 +116,5 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_ #endif #define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing -void LUAh_GameQuit(void); // Hook for game quitting \ No newline at end of file +void LUAh_GameQuit(void); // Hook for game quitting +boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Hook for building player's ticcmd struct (Ported from SRB2Kart) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index ebf6f9deb..65d483dc1 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -23,6 +23,10 @@ #include "lua_hook.h" #include "lua_hud.h" // hud_running errors +#include "m_perfstats.h" +#include "d_netcmd.h" // for cv_perfstats +#include "i_system.h" // I_GetTimeMicros + static UINT8 hooksAvailable[(hook_MAX/8)+1]; const char *const hookNames[hook_MAX+1] = { @@ -71,6 +75,7 @@ const char *const hookNames[hook_MAX+1] = { "PlayerThink", "ShouldJingleContinue", "GameQuit", + "PlayerCmd", NULL }; @@ -259,6 +264,9 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which) I_Assert(mo->type < NUMMOBJTYPES); + if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -268,6 +276,7 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) LUA_PushUserdata(gL, mo, META_MOBJ); PushHook(gL, hookp); @@ -289,6 +298,7 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) LUA_PushUserdata(gL, mo, META_MOBJ); PushHook(gL, hookp); @@ -324,6 +334,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) LUA_PushUserdata(gL, plr, META_PLAYER); PushHook(gL, hookp); @@ -455,6 +466,9 @@ void LUAh_PreThinkFrame(void) void LUAh_ThinkFrame(void) { hook_p hookp; + // variables used by perf stats + int hook_index = 0; + int time_taken = 0; if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) return; @@ -465,6 +479,8 @@ void LUAh_ThinkFrame(void) if (hookp->type != hook_ThinkFrame) continue; + if (cv_perfstats.value == 3) + time_taken = I_GetTimeMicros(); PushHook(gL, hookp); if (lua_pcall(gL, 0, 0, 1)) { if (!hookp->error || cv_debug & DBG_LUA) @@ -472,6 +488,16 @@ void LUAh_ThinkFrame(void) lua_pop(gL, 1); hookp->error = true; } + if (cv_perfstats.value == 3) + { + lua_Debug ar; + time_taken = I_GetTimeMicros() - time_taken; + // we need the function, let's just retrieve it again + PushHook(gL, hookp); + lua_getinfo(gL, ">S", &ar); + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + hook_index++; + } } lua_pop(gL, 1); // Pop error handler @@ -513,6 +539,9 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) I_Assert(thing1->type < NUMMOBJTYPES); + if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type])) + return 0; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -522,6 +551,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, thing1, META_MOBJ); @@ -552,6 +582,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, thing1, META_MOBJ); @@ -590,6 +621,9 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) I_Assert(thing->type < NUMMOBJTYPES); + if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type])) + return 0; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -599,6 +633,7 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, thing, META_MOBJ); @@ -629,6 +664,7 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) if (hookp->type != which) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, thing, META_MOBJ); @@ -668,12 +704,16 @@ boolean LUAh_MobjThinker(mobj_t *mo) I_Assert(mo->type < NUMMOBJTYPES); + if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); // Look for all generic mobj thinker hooks for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) { + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) LUA_PushUserdata(gL, mo, META_MOBJ); PushHook(gL, hookp); @@ -692,6 +732,7 @@ boolean LUAh_MobjThinker(mobj_t *mo) for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) { + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) LUA_PushUserdata(gL, mo, META_MOBJ); PushHook(gL, hookp); @@ -718,10 +759,13 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) - return 0; + return false; I_Assert(special->type < NUMMOBJTYPES); + if (!(mobjhooks[MT_NULL] || mobjhooks[special->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -731,6 +775,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) if (hookp->type != hook_TouchSpecial) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, special, META_MOBJ); @@ -756,6 +801,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) if (hookp->type != hook_TouchSpecial) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, special, META_MOBJ); @@ -790,6 +836,9 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 I_Assert(target->type < NUMMOBJTYPES); + if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) + return 0; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -799,6 +848,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 if (hookp->type != hook_ShouldDamage) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -834,6 +884,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 { if (hookp->type != hook_ShouldDamage) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -875,10 +926,13 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) - return 0; + return false; I_Assert(target->type < NUMMOBJTYPES); + if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -888,6 +942,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 if (hookp->type != hook_MobjDamage) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -919,6 +974,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 if (hookp->type != hook_MobjDamage) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -955,10 +1011,13 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8)))) - return 0; + return false; I_Assert(target->type < NUMMOBJTYPES); + if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -968,6 +1027,7 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 if (hookp->type != hook_MobjDeath) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -997,6 +1057,7 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 if (hookp->type != hook_MobjDeath) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, target, META_MOBJ); @@ -1189,6 +1250,7 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) if (strcmp(hookp->s.str, line->stringargs[0])) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, line, META_LINE); @@ -1354,6 +1416,9 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8)))) return false; + if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) + return false; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -1363,6 +1428,7 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) if (hookp->type != hook_MapThingSpawn) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, mo, META_MOBJ); @@ -1388,6 +1454,7 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) if (hookp->type != hook_MapThingSpawn) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, mo, META_MOBJ); @@ -1420,6 +1487,9 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8)))) return 0; + if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type])) + return 0; + lua_settop(gL, 0); lua_pushcfunction(gL, LUA_GetErrorMessage); @@ -1429,6 +1499,7 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) if (hookp->type != hook_FollowMobj) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, player, META_PLAYER); @@ -1454,6 +1525,7 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) if (hookp->type != hook_FollowMobj) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, player, META_PLAYER); @@ -1494,6 +1566,7 @@ UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) if (hookp->type != hook_PlayerCanDamage) continue; + ps_lua_mobjhooks++; if (lua_gettop(gL) == 1) { LUA_PushUserdata(gL, player, META_PLAYER); @@ -1793,6 +1866,49 @@ void LUAh_GameQuit(void) hookp->error = true; } } - + lua_pop(gL, 1); // Pop error handler } + +// Hook for building player's ticcmd struct (Ported from SRB2Kart) +boolean hook_cmd_running = false; +boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) +{ + hook_p hookp; + boolean hooked = false; + if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8)))) + return false; + + lua_settop(gL, 0); + lua_pushcfunction(gL, LUA_GetErrorMessage); + + hook_cmd_running = true; + for (hookp = roothook; hookp; hookp = hookp->next) + { + if (hookp->type != hook_PlayerCmd) + continue; + + if (lua_gettop(gL) == 1) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, cmd, META_TICCMD); + } + PushHook(gL, hookp); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + if (lua_pcall(gL, 2, 1, 1)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + lua_settop(gL, 0); + hook_cmd_running = false; + return hooked; +} diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 4aa70574b..6b87dc930 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -992,6 +992,19 @@ static int libd_getColormap(lua_State *L) return 1; } +static int libd_getStringColormap(lua_State *L) +{ + INT32 flags = luaL_checkinteger(L, 1); + UINT8* colormap = NULL; + HUDONLY + colormap = V_GetStringColormap(flags & V_CHARCOLORMASK); + if (colormap) { + LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! + return 1; + } + return 0; +} + static int libd_fadeScreen(lua_State *L) { UINT16 color = luaL_checkinteger(L, 1); @@ -1142,6 +1155,7 @@ static luaL_Reg lib_draw[] = { {"getSpritePatch", libd_getSpritePatch}, {"getSprite2Patch", libd_getSprite2Patch}, {"getColormap", libd_getColormap}, + {"getStringColormap", libd_getStringColormap}, // drawing {"draw", libd_draw}, {"drawScaled", libd_drawScaled}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 830d97625..5e5a1dbc4 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -17,7 +17,7 @@ #include "p_mobj.h" #include "p_local.h" #include "z_zone.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" #include "r_draw.h" // R_GetColorByName #include "doomstat.h" // luabanks[] @@ -25,6 +25,7 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" // hud_running errors +#include "lua_hook.h" // hook_cmd_running errors extern CV_PossibleValue_t Color_cons_t[]; extern UINT8 skincolor_modified[]; @@ -165,6 +166,8 @@ static int lib_setSpr2default(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter spr2defaults[] in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spr2defaults[] in CMD building code!"); // todo: maybe allow setting below first freeslot..? step 1 is toggling this, step 2 is testing to see whether it's net-safe #ifdef SETALLSPR2DEFAULTS @@ -371,6 +374,8 @@ static int lib_setSpriteInfo(lua_State *L) return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!"); if (hud_running) return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!"); lua_remove(L, 1); { @@ -455,6 +460,8 @@ static int spriteinfo_set(lua_State *L) return luaL_error(L, "Do not alter spriteinfo_t from within a hook or coroutine!"); if (hud_running) return luaL_error(L, "Do not alter spriteinfo_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteinfo_t in CMD building code!"); I_Assert(sprinfo != NULL); @@ -533,6 +540,8 @@ static int pivotlist_set(lua_State *L) return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!"); if (hud_running) return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!"); I_Assert(pivotlist != NULL); @@ -587,6 +596,8 @@ static int framepivot_set(lua_State *L) return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!"); if (hud_running) return luaL_error(L, "Do not alter spriteframepivot_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!"); I_Assert(framepivot != NULL); @@ -686,6 +697,8 @@ static int lib_setState(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter states in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter states in CMD building code!"); // clear the state to start with, in case of missing table elements memset(state,0,sizeof(state_t)); @@ -906,6 +919,8 @@ static int state_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter states in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter states in CMD building code!"); if (fastcmp(field,"sprite")) { value = luaL_checknumber(L, 3); @@ -1006,6 +1021,8 @@ static int lib_setMobjInfo(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter mobjinfo in CMD building code!"); // clear the mobjinfo to start with, in case of missing table elements memset(info,0,sizeof(mobjinfo_t)); @@ -1173,6 +1190,8 @@ static int mobjinfo_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter mobjinfo in CMD building code!"); I_Assert(info != NULL); I_Assert(info >= mobjinfo); @@ -1295,6 +1314,8 @@ static int lib_setSfxInfo(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter sfxinfo in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter sfxinfo in CMD building code!"); lua_pushnil(L); while (lua_next(L, 1)) { @@ -1376,6 +1397,8 @@ static int sfxinfo_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter S_sfx in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter S_sfx in CMD building code!"); I_Assert(sfx != NULL); @@ -1443,6 +1466,8 @@ static int lib_setluabanks(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter luabanks[] in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter luabanks[] in CMD building code!"); lua_remove(L, 1); // don't care about luabanks[] dummy userdata. @@ -1523,6 +1548,8 @@ static int lib_setSkinColor(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter skincolors in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter skincolors in CMD building code!"); // clear the skincolor to start with, in case of missing table elements memset(info,0,sizeof(skincolor_t)); @@ -1542,8 +1569,10 @@ static int lib_setSkinColor(lua_State *L) strlcpy(info->name, n, MAXCOLORNAME+1); if (strlen(n) > MAXCOLORNAME) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name); +#if 0 if (strchr(info->name, ' ') != NULL) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name); +#endif if (info->name[0] != '\0') // don't check empty string for dupe { @@ -1633,8 +1662,10 @@ static int skincolor_set(lua_State *L) strlcpy(info->name, n, MAXCOLORNAME+1); if (strlen(n) > MAXCOLORNAME) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name); +#if 0 if (strchr(info->name, ' ') != NULL) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name); +#endif if (info->name[0] != '\0') // don't check empty string for dupe { @@ -1707,6 +1738,8 @@ static int colorramp_set(lua_State *L) return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1); if (hud_running) return luaL_error(L, "Do not alter skincolor_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter skincolor_t in CMD building code!"); colorramp[n] = i; skincolor_modified[cnum] = true; return 0; diff --git a/src/lua_libs.h b/src/lua_libs.h index f987c79fd..03bd99cd2 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -50,6 +50,8 @@ extern lua_State *gL; #define META_VECTOR3 "VECTOR3_T" #define META_MAPHEADER "MAPHEADER_T*" +#define META_POLYOBJ "POLYOBJ_T*" + #define META_CVAR "CONSVAR_T*" #define META_SECTORLINES "SECTOR_T*LINES" @@ -58,6 +60,8 @@ extern lua_State *gL; #define META_LINESTRINGARGS "LINE_T*STRINGARGS" #define META_THINGARGS "MAPTHING_T*ARGS" #define META_THINGSTRINGARGS "MAPTHING_T*STRINGARGS" +#define META_POLYOBJVERTICES "POLYOBJ_T*VERTICES" +#define META_POLYOBJLINES "POLYOBJ_T*LINES" #ifdef HAVE_LUA_SEGS #define META_NODEBBOX "NODE_T*BBOX" #define META_NODECHILDREN "NODE_T*CHILDREN" @@ -88,5 +92,6 @@ int LUA_PlayerLib(lua_State *L); int LUA_SkinLib(lua_State *L); int LUA_ThinkerLib(lua_State *L); int LUA_MapLib(lua_State *L); +int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index d479c9695..95cc8c101 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -16,11 +16,13 @@ #include "p_setup.h" #include "z_zone.h" #include "p_slopes.h" +#include "p_polyobj.h" #include "r_main.h" #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" // hud_running errors +#include "lua_hook.h" // hook_cmd_running errors #include "dehacked.h" #include "fastcmp.h" @@ -67,6 +69,7 @@ enum subsector_e { subsector_sector, subsector_numlines, subsector_firstline, + subsector_polyList }; static const char *const subsector_opt[] = { @@ -74,6 +77,7 @@ static const char *const subsector_opt[] = { "sector", "numlines", "firstline", + "polyList", NULL}; enum line_e { @@ -95,6 +99,7 @@ enum line_e { line_slopetype, line_frontsector, line_backsector, + line_polyobj, line_text, line_callcount }; @@ -118,6 +123,7 @@ static const char *const line_opt[] = { "slopetype", "frontsector", "backsector", + "polyobj", "text", "callcount", NULL}; @@ -218,6 +224,7 @@ enum seg_e { seg_linedef, seg_frontsector, seg_backsector, + seg_polyseg }; static const char *const seg_opt[] = { @@ -231,6 +238,7 @@ static const char *const seg_opt[] = { "linedef", "frontsector", "backsector", + "polyseg", NULL}; enum node_e { @@ -320,9 +328,9 @@ static const char *const vector_opt[] = { static const char *const array_opt[] ={"iterate",NULL}; static const char *const valid_opt[] ={"valid",NULL}; -/////////////////////////////////// -// sector list iterate functions // -/////////////////////////////////// +///////////////////////////////////////////// +// sector/subsector list iterate functions // +///////////////////////////////////////////// // iterates through a sector's thinglist! static int lib_iterateSectorThinglist(lua_State *L) @@ -394,6 +402,41 @@ static int lib_iterateSectorFFloors(lua_State *L) return 0; } +// iterates through a subsector's polyList! (for polyobj_t) +static int lib_iterateSubSectorPolylist(lua_State *L) +{ + polyobj_t *state = NULL; + polyobj_t *po = NULL; + + INLEVEL + + if (lua_gettop(L) < 2) + return luaL_error(L, "Don't call subsector.polyList() directly, use it as 'for polyobj in subsector.polyList do end'."); + + if (!lua_isnil(L, 1)) + state = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + else + return 0; // no polylist to iterate through sorry! + + lua_settop(L, 2); + lua_remove(L, 1); // remove state now. + + if (!lua_isnil(L, 1)) + { + po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + po = (polyobj_t *)(po->link.next); + } + else + po = state; // state is used as the "start" of the polylist + + if (po) + { + LUA_PushUserdata(L, po, META_POLYOBJ); + return 1; + } + return 0; +} + static int sector_iterate(lua_State *L) { lua_pushvalue(L, lua_upvalueindex(1)); // iterator function, or the "generator" @@ -442,7 +485,7 @@ static int sectorlines_get(lua_State *L) // get the "linecount" by shifting our retrieved memory address of "lines" to where "linecount" is in the sector_t, then dereferencing the result // we need this to determine the array's actual size, and therefore also the maximum value allowed as an index // this only works if seclines is actually a pointer to a sector's lines member in memory, oh boy - numoflines = (size_t)(*(size_t *)(((size_t)seclines) - (offsetof(sector_t, lines) - offsetof(sector_t, linecount)))); + numoflines = *(size_t *)FIELDFROM (sector_t, seclines, lines,/* -> */linecount); /* OLD HACK // check first linedef to figure which of its sectors owns this sector->lines pointer @@ -476,7 +519,7 @@ static int sectorlines_num(lua_State *L) return luaL_error(L, "accessed sector_t.lines doesn't exist anymore."); // see comments in the _get function above - numoflines = (size_t)(*(size_t *)(((size_t)seclines) - (offsetof(sector_t, lines) - offsetof(sector_t, linecount)))); + numoflines = *(size_t *)FIELDFROM (sector_t, seclines, lines,/* -> */linecount); lua_pushinteger(L, numoflines); return 1; } @@ -581,6 +624,8 @@ static int sector_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter sector_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter sector_t in CMD building code!"); switch(field) { @@ -680,6 +725,11 @@ static int subsector_get(lua_State *L) case subsector_firstline: lua_pushinteger(L, subsector->firstline); return 1; + case subsector_polyList: // polyList + lua_pushcfunction(L, lib_iterateSubSectorPolylist); + LUA_PushUserdata(L, subsector->polyList, META_POLYOBJ); + lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateSubSectorPolylist and subsector->polyList as upvalues for the function + return 1; } return 0; } @@ -817,6 +867,9 @@ static int line_get(lua_State *L) case line_backsector: LUA_PushUserdata(L, line->backsector, META_SECTOR); return 1; + case line_polyobj: + LUA_PushUserdata(L, line->polyobj, META_POLYOBJ); + return 1; case line_text: lua_pushstring(L, line->text); return 1; @@ -1079,6 +1132,9 @@ static int seg_get(lua_State *L) case seg_backsector: LUA_PushUserdata(L, seg->backsector, META_SECTOR); return 1; + case seg_polyseg: + LUA_PushUserdata(L, seg->polyseg, META_POLYOBJ); + return 1; } return 0; } @@ -1762,6 +1818,8 @@ static int ffloor_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter ffloor_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter ffloor_t in CMD building code!"); switch(field) { @@ -1886,6 +1944,8 @@ static int slope_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter pslope_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter pslope_t in CMD building code!"); switch(field) // todo: reorganize this shit { diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 102a396c5..824e9df6b 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -20,6 +20,7 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" // hud_running errors +#include "lua_hook.h" // hook_cmd_running errors static const char *const array_opt[] ={"iterate",NULL}; @@ -437,6 +438,8 @@ static int mobj_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter mobj_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter mobj_t in CMD building code!"); switch(field) { @@ -878,6 +881,8 @@ static int mapthing_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter mapthing_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter mapthing_t in CMD building code!"); if(fastcmp(field,"x")) mt->x = (INT16)luaL_checkinteger(L, 3); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1ce9be525..412dc3eff 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -20,6 +20,7 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" // hud_running errors +#include "lua_hook.h" // hook_cmd_running errors static int lib_iteratePlayers(lua_State *L) { @@ -400,6 +401,8 @@ static int player_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter player_t in CMD building code!"); if (fastcmp(field,"mo") || fastcmp(field,"realmo")) { mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); @@ -770,6 +773,8 @@ static int power_set(lua_State *L) return luaL_error(L, LUA_QL("powertype_t") " cannot be %d", (INT16)p); if (hud_running) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter player_t in CMD building code!"); powers[p] = i; return 0; } diff --git a/src/lua_polyobjlib.c b/src/lua_polyobjlib.c new file mode 100644 index 000000000..365d97056 --- /dev/null +++ b/src/lua_polyobjlib.c @@ -0,0 +1,486 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by Iestyn "Monster Iestyn" Jealous. +// Copyright (C) 2020 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 lua_polyobjlib.c +/// \brief polyobject library for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" +#include "p_local.h" +#include "p_polyobj.h" +#include "lua_script.h" +#include "lua_libs.h" +#include "lua_hud.h" // hud_running errors + +#define NOHUD if (hud_running)\ +return luaL_error(L, "HUD rendering code should not call this function!"); + +enum polyobj_e { + // properties + polyobj_valid = 0, + polyobj_id, + polyobj_parent, + polyobj_vertices, + polyobj_lines, + polyobj_sector, + polyobj_angle, + polyobj_damage, + polyobj_thrust, + polyobj_flags, + polyobj_translucency, + polyobj_triggertag, + // special functions - utility + polyobj_pointInside, + polyobj_mobjTouching, + polyobj_mobjInside, + // special functions - manipulation + polyobj_moveXY, + polyobj_rotate +}; +static const char *const polyobj_opt[] = { + // properties + "valid", + "id", + "parent", + "vertices", + "lines", + "sector", + "angle", + "damage", + "thrust", + "flags", + "translucency", + "triggertag", + // special functions - utility + "pointInside", + "mobjTouching", + "mobjInside", + // special functions - manipulation + "moveXY", + "rotate", + NULL}; + +static const char *const valid_opt[] ={"valid",NULL}; + +//////////////////////// +// polyobj.vertices[] // +//////////////////////// + +// polyobj.vertices, i -> polyobj.vertices[i] +// polyobj.vertices.valid, for validity checking +// +// see sectorlines_get in lua_maplib.c +// +static int polyobjvertices_get(lua_State *L) +{ + vertex_t ***polyverts = *((vertex_t ****)luaL_checkudata(L, 1, META_POLYOBJVERTICES)); + size_t i; + size_t numofverts = 0; + lua_settop(L, 2); + if (!lua_isnumber(L, 2)) + { + int field = luaL_checkoption(L, 2, NULL, valid_opt); + if (!polyverts || !(*polyverts)) + { + if (field == 0) { + lua_pushboolean(L, 0); + return 1; + } + return luaL_error(L, "accessed polyobj_t.vertices doesn't exist anymore."); + } else if (field == 0) { + lua_pushboolean(L, 1); + return 1; + } + } + + numofverts = *(size_t *)FIELDFROM (polyobj_t, polyverts, vertices,/* -> */numVertices); + + if (!numofverts) + return luaL_error(L, "no vertices found!"); + + i = (size_t)lua_tointeger(L, 2); + if (i >= numofverts) + return 0; + LUA_PushUserdata(L, (*polyverts)[i], META_VERTEX); + return 1; +} + +// #(polyobj.vertices) -> polyobj.numVertices +static int polyobjvertices_num(lua_State *L) +{ + vertex_t ***polyverts = *((vertex_t ****)luaL_checkudata(L, 1, META_POLYOBJVERTICES)); + size_t numofverts = 0; + + if (!polyverts || !(*polyverts)) + return luaL_error(L, "accessed polyobj_t.vertices doesn't exist anymore."); + + numofverts = *(size_t *)FIELDFROM (polyobj_t, polyverts, vertices,/* -> */numVertices); + lua_pushinteger(L, numofverts); + return 1; +} + +///////////////////// +// polyobj.lines[] // +///////////////////// + +// polyobj.lines, i -> polyobj.lines[i] +// polyobj.lines.valid, for validity checking +// +// see sectorlines_get in lua_maplib.c +// +static int polyobjlines_get(lua_State *L) +{ + line_t ***polylines = *((line_t ****)luaL_checkudata(L, 1, META_POLYOBJLINES)); + size_t i; + size_t numoflines = 0; + lua_settop(L, 2); + if (!lua_isnumber(L, 2)) + { + int field = luaL_checkoption(L, 2, NULL, valid_opt); + if (!polylines || !(*polylines)) + { + if (field == 0) { + lua_pushboolean(L, 0); + return 1; + } + return luaL_error(L, "accessed polyobj_t.lines doesn't exist anymore."); + } else if (field == 0) { + lua_pushboolean(L, 1); + return 1; + } + } + + numoflines = *(size_t *)FIELDFROM (polyobj_t, polylines, lines,/* -> */numLines); + + if (!numoflines) + return luaL_error(L, "no lines found!"); + + i = (size_t)lua_tointeger(L, 2); + if (i >= numoflines) + return 0; + LUA_PushUserdata(L, (*polylines)[i], META_LINE); + return 1; +} + +// #(polyobj.lines) -> polyobj.numLines +static int polyobjlines_num(lua_State *L) +{ + line_t ***polylines = *((line_t ****)luaL_checkudata(L, 1, META_POLYOBJLINES)); + size_t numoflines = 0; + + if (!polylines || !(*polylines)) + return luaL_error(L, "accessed polyobj_t.lines doesn't exist anymore."); + + numoflines = *(size_t *)FIELDFROM (polyobj_t, polylines, lines,/* -> */numLines); + lua_pushinteger(L, numoflines); + return 1; +} + +///////////////////////////////// +// polyobj_t function wrappers // +///////////////////////////////// + +// special functions - utility +static int lib_polyobj_PointInside(lua_State *L) +{ + polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + INLEVEL + if (!po) + return LUA_ErrInvalid(L, "polyobj_t"); + lua_pushboolean(L, P_PointInsidePolyobj(po, x, y)); + return 1; +} + +static int lib_polyobj_MobjTouching(lua_State *L) +{ + polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + INLEVEL + if (!po) + return LUA_ErrInvalid(L, "polyobj_t"); + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + lua_pushboolean(L, P_MobjTouchingPolyobj(po, mo)); + return 1; +} + +static int lib_polyobj_MobjInside(lua_State *L) +{ + polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + INLEVEL + if (!po) + return LUA_ErrInvalid(L, "polyobj_t"); + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + lua_pushboolean(L, P_MobjInsidePolyobj(po, mo)); + return 1; +} + +// special functions - manipulation +static int lib_polyobj_moveXY(lua_State *L) +{ + polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + boolean checkmobjs = lua_opttrueboolean(L, 4); + NOHUD + INLEVEL + if (!po) + return LUA_ErrInvalid(L, "polyobj_t"); + lua_pushboolean(L, Polyobj_moveXY(po, x, y, checkmobjs)); + return 1; +} + +static int lib_polyobj_rotate(lua_State *L) +{ + polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + angle_t delta = luaL_checkangle(L, 2); + UINT8 turnthings = (UINT8)luaL_optinteger(L, 3, 0); // don't turn anything by default? (could change this if not desired) + boolean checkmobjs = lua_opttrueboolean(L, 4); + NOHUD + INLEVEL + if (!po) + return LUA_ErrInvalid(L, "polyobj_t"); + lua_pushboolean(L, Polyobj_rotate(po, delta, turnthings, checkmobjs)); + return 1; +} + +/////////////// +// polyobj_t // +/////////////// + +static int polyobj_get(lua_State *L) +{ + polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + enum polyobj_e field = luaL_checkoption(L, 2, NULL, polyobj_opt); + + if (!polyobj) { + if (field == polyobj_valid) { + lua_pushboolean(L, false); + return 1; + } + return LUA_ErrInvalid(L, "polyobj_t"); + } + + switch (field) + { + // properties + case polyobj_valid: + lua_pushboolean(L, true); + break; + case polyobj_id: + lua_pushinteger(L, polyobj->id); + break; + case polyobj_parent: + lua_pushinteger(L, polyobj->parent); + break; + case polyobj_vertices: // vertices + LUA_PushUserdata(L, &polyobj->vertices, META_POLYOBJVERTICES); // push the address of the "vertices" member in the struct, to allow our hacks to work + break; + case polyobj_lines: // lines + LUA_PushUserdata(L, &polyobj->lines, META_POLYOBJLINES); // push the address of the "lines" member in the struct, to allow our hacks to work + break; + case polyobj_sector: // shortcut that exists only in Lua! + LUA_PushUserdata(L, polyobj->lines[0]->backsector, META_SECTOR); + break; + case polyobj_angle: + lua_pushangle(L, polyobj->angle); + break; + case polyobj_damage: + lua_pushinteger(L, polyobj->damage); + break; + case polyobj_thrust: + lua_pushfixed(L, polyobj->thrust); + break; + case polyobj_flags: + lua_pushinteger(L, polyobj->flags); + break; + case polyobj_translucency: + lua_pushinteger(L, polyobj->translucency); + break; + case polyobj_triggertag: + lua_pushinteger(L, polyobj->triggertag); + break; + // special functions - utility + case polyobj_pointInside: + lua_pushcfunction(L, lib_polyobj_PointInside); + break; + case polyobj_mobjTouching: + lua_pushcfunction(L, lib_polyobj_MobjTouching); + break; + case polyobj_mobjInside: + lua_pushcfunction(L, lib_polyobj_MobjInside); + break; + // special functions - manipulation + case polyobj_moveXY: + lua_pushcfunction(L, lib_polyobj_moveXY); + break; + case polyobj_rotate: + lua_pushcfunction(L, lib_polyobj_rotate); + break; + } + return 1; +} + +static int polyobj_set(lua_State *L) +{ + polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + enum polyobj_e field = luaL_checkoption(L, 2, NULL, polyobj_opt); + + if (!polyobj) + return LUA_ErrInvalid(L, "polyobj_t"); + + if (hud_running) + return luaL_error(L, "Do not alter polyobj_t in HUD rendering code!"); + + switch (field) + { + default: + return luaL_error(L, LUA_QL("polyobj_t") " field " LUA_QS " cannot be modified.", polyobj_opt[field]); + case polyobj_angle: + return luaL_error(L, LUA_QL("polyobj_t") " field " LUA_QS " should not be set directly. Use the function " LUA_QL("polyobj:rotate(angle)") " instead.", polyobj_opt[field]); + case polyobj_parent: + polyobj->parent = luaL_checkinteger(L, 3); + break; + case polyobj_flags: + polyobj->flags = luaL_checkinteger(L, 3); + break; + case polyobj_translucency: + polyobj->translucency = luaL_checkinteger(L, 3); + break; + } + + return 0; +} + +static int polyobj_num(lua_State *L) +{ + polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); + if (!polyobj) + return luaL_error(L, "accessed polyobj_t doesn't exist anymore."); + lua_pushinteger(L, polyobj-PolyObjects); + return 1; +} + +/////////////////// +// PolyObjects[] // +/////////////////// + +static int lib_iteratePolyObjects(lua_State *L) +{ + INT32 i = -1; + if (lua_gettop(L) < 2) + { + //return luaL_error(L, "Don't call PolyObjects.iterate() directly, use it as 'for polyobj in PolyObjects.iterate do end'."); + lua_pushcfunction(L, lib_iteratePolyObjects); + return 1; + } + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + if (!lua_isnil(L, 1)) + i = (INT32)(*((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)) - PolyObjects); + for (i++; i < numPolyObjects; i++) + { + LUA_PushUserdata(L, &PolyObjects[i], META_POLYOBJ); + return 1; + } + return 0; +} + +static int lib_PolyObject_getfornum(lua_State *L) +{ + INT32 id = (INT32)luaL_checkinteger(L, 1); + + if (!numPolyObjects) + return 0; // if there's no PolyObjects then bail out here + + LUA_PushUserdata(L, Polyobj_GetForNum(id), META_POLYOBJ); + return 1; +} + +static int lib_getPolyObject(lua_State *L) +{ + const char *field; + INT32 i; + + // find PolyObject by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i < 0 || i >= numPolyObjects) + return luaL_error(L, "PolyObjects[] index %d out of range (0 - %d)", i, numPolyObjects-1); + LUA_PushUserdata(L, &PolyObjects[i], META_POLYOBJ); + return 1; + } + + field = luaL_checkstring(L, 2); + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iteratePolyObjects); + return 1; + } + // find PolyObject by ID + else if (fastcmp(field,"GetForNum")) // name could probably be better + { + lua_pushcfunction(L, lib_PolyObject_getfornum); + return 1; + } + return 0; +} + +static int lib_numPolyObjects(lua_State *L) +{ + lua_pushinteger(L, numPolyObjects); + return 1; +} + +int LUA_PolyObjLib(lua_State *L) +{ + luaL_newmetatable(L, META_POLYOBJVERTICES); + lua_pushcfunction(L, polyobjvertices_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, polyobjvertices_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + luaL_newmetatable(L, META_POLYOBJLINES); + lua_pushcfunction(L, polyobjlines_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, polyobjlines_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + luaL_newmetatable(L, META_POLYOBJ); + lua_pushcfunction(L, polyobj_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, polyobj_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, polyobj_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getPolyObject); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numPolyObjects); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "PolyObjects"); + return 0; +} diff --git a/src/lua_script.c b/src/lua_script.c index 0260f018a..ae7f479f6 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -24,6 +24,7 @@ #include "p_saveg.h" #include "p_local.h" #include "p_slopes.h" // for P_SlopeById +#include "p_polyobj.h" // polyobj_t, PolyObjects #ifdef LUA_ALLOW_BYTECODE #include "d_netfil.h" // for LUA_DumpFile #endif @@ -50,6 +51,7 @@ static lua_CFunction liblist[] = { LUA_SkinLib, // skin_t, skins[] LUA_ThinkerLib, // thinker_t LUA_MapLib, // line_t, side_t, sector_t, subsector_t + LUA_PolyObjLib, // polyobj_t LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff NULL @@ -774,6 +776,12 @@ void LUA_InvalidateLevel(void) LUA_InvalidateUserdata(&sides[i]); for (i = 0; i < numvertexes; i++) LUA_InvalidateUserdata(&vertexes[i]); + for (i = 0; i < (size_t)numPolyObjects; i++) + { + LUA_InvalidateUserdata(&PolyObjects[i]); + LUA_InvalidateUserdata(&PolyObjects[i].vertices); + LUA_InvalidateUserdata(&PolyObjects[i].lines); + } #ifdef HAVE_LUA_SEGS for (i = 0; i < numsegs; i++) LUA_InvalidateUserdata(&segs[i]); @@ -832,6 +840,7 @@ enum ARCH_NODE, #endif ARCH_FFLOOR, + ARCH_POLYOBJ, ARCH_SLOPE, ARCH_MAPHEADER, ARCH_SKINCOLOR, @@ -858,6 +867,7 @@ static const struct { {META_NODE, ARCH_NODE}, #endif {META_FFLOOR, ARCH_FFLOOR}, + {META_POLYOBJ, ARCH_POLYOBJ}, {META_SLOPE, ARCH_SLOPE}, {META_MAPHEADER, ARCH_MAPHEADER}, {META_SKINCOLOR, ARCH_SKINCOLOR}, @@ -1126,6 +1136,17 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) } break; } + case ARCH_POLYOBJ: + { + polyobj_t *polyobj = *((polyobj_t **)lua_touserdata(gL, myindex)); + if (!polyobj) + WRITEUINT8(save_p, ARCH_NULL); + else { + WRITEUINT8(save_p, ARCH_POLYOBJ); + WRITEUINT16(save_p, polyobj-PolyObjects); + } + break; + } case ARCH_SLOPE: { pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex)); @@ -1381,6 +1402,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX) LUA_PushUserdata(gL, rover, META_FFLOOR); break; } + case ARCH_POLYOBJ: + LUA_PushUserdata(gL, &PolyObjects[READUINT16(save_p)], META_POLYOBJ); + break; case ARCH_SLOPE: LUA_PushUserdata(gL, P_SlopeById(READUINT16(save_p)), META_SLOPE); break; diff --git a/src/m_anigif.c b/src/m_anigif.c index 83bc3dddc..85118790b 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -29,10 +29,10 @@ // GIFs are always little-endian #include "byteptr.h" -consvar_t cv_gif_optimize = {"gif_optimize", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gif_downscale = {"gif_downscale", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gif_dynamicdelay = {"gif_dynamicdelay", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gif_localcolortable = {"gif_localcolortable", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_gif_optimize = CVAR_INIT ("gif_optimize", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_gif_downscale = CVAR_INIT ("gif_downscale", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_gif_dynamicdelay = CVAR_INIT ("gif_dynamicdelay", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_gif_localcolortable = CVAR_INIT ("gif_localcolortable", "On", CV_SAVE, CV_OnOff, NULL); #ifdef HAVE_ANIGIF static boolean gif_optimize = false; // So nobody can do something dumb @@ -499,20 +499,22 @@ static size_t gifframe_size = 8192; // converts an RGB frame to a frame with a palette. // #ifdef HWRENDER +static colorlookup_t gif_colorlookup; + static void GIF_rgbconvert(UINT8 *linear, UINT8 *scr) { UINT8 r, g, b; size_t src = 0, dest = 0; size_t size = (vid.width * vid.height * 3); - InitColorLUT(gif_framepalette); + InitColorLUT(&gif_colorlookup, (gif_localcolortable) ? gif_framepalette : gif_headerpalette, true); while (src < size) { r = (UINT8)linear[src]; g = (UINT8)linear[src + 1]; b = (UINT8)linear[src + 2]; - scr[dest] = colorlookup[r >> SHIFTCOLORBITS][g >> SHIFTCOLORBITS][b >> SHIFTCOLORBITS]; + scr[dest] = GetColorLUTDirect(&gif_colorlookup, r, g, b); src += (3 * scrbuf_downscaleamt); dest += scrbuf_downscaleamt; } diff --git a/src/m_cheat.c b/src/m_cheat.c index ab1454503..6fc06d353 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -967,10 +967,10 @@ static CV_PossibleValue_t op_speed_t[] = {{1, "MIN"}, {128, "MAX"}, {0, NULL}}; static CV_PossibleValue_t op_flags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}}; static CV_PossibleValue_t op_hoopflags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}}; -consvar_t cv_mapthingnum = {"op_mapthingnum", "0", CV_NOTINNET, op_mapthing_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_speed = {"op_speed", "16", CV_NOTINNET, op_speed_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_opflags = {"op_flags", "0", CV_NOTINNET, op_flags_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ophoopflags = {"op_hoopflags", "4", CV_NOTINNET, op_hoopflags_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mapthingnum = CVAR_INIT ("op_mapthingnum", "0", CV_NOTINNET, op_mapthing_t, NULL); +consvar_t cv_speed = CVAR_INIT ("op_speed", "16", CV_NOTINNET, op_speed_t, NULL); +consvar_t cv_opflags = CVAR_INIT ("op_flags", "0", CV_NOTINNET, op_flags_t, NULL); +consvar_t cv_ophoopflags = CVAR_INIT ("op_hoopflags", "4", CV_NOTINNET, op_hoopflags_t, NULL); boolean objectplacing = false; mobjtype_t op_currentthing = 0; // For the object placement mode @@ -1023,8 +1023,8 @@ static void OP_CycleThings(INT32 amt) states[S_OBJPLACE_DUMMY].frame = states[mobjinfo[op_currentthing].spawnstate].frame; } if (players[0].mo->eflags & MFE_VERTICALFLIP) // correct z when flipped - players[0].mo->z += players[0].mo->height - mobjinfo[op_currentthing].height; - players[0].mo->height = mobjinfo[op_currentthing].height; + players[0].mo->z += players[0].mo->height - FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); + players[0].mo->height = FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); op_currentdoomednum = mobjinfo[op_currentthing].doomednum; @@ -1107,6 +1107,10 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle))); mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value; + mt->scale = player->mo->scale; + memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args)); + memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs)); + mt->pitch = mt->roll = 0; return mt; } @@ -1297,27 +1301,26 @@ void OP_ObjectplaceMovement(player_t *player) { ticcmd_t *cmd = &player->cmd; - if (!player->climbing && (netgame || !cv_analog[0].value || (player->pflags & PF_SPINNING))) - player->drawangle = player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); + player->drawangle = player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); ticruned++; if (!(cmd->angleturn & TICCMD_RECEIVED)) ticmiss++; if (cmd->buttons & BT_JUMP) - player->mo->z += FRACUNIT*cv_speed.value; + player->mo->z += player->mo->scale*cv_speed.value; else if (cmd->buttons & BT_SPIN) - player->mo->z -= FRACUNIT*cv_speed.value; + player->mo->z -= player->mo->scale*cv_speed.value; if (cmd->forwardmove != 0) { - P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*FRACUNIT/MAXPLMOVE)*cv_speed.value); + P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*player->mo->scale/MAXPLMOVE)*cv_speed.value); P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } if (cmd->sidemove != 0) { - P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*FRACUNIT/MAXPLMOVE)*cv_speed.value); + P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*player->mo->scale/MAXPLMOVE)*cv_speed.value); P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } @@ -1443,62 +1446,75 @@ void Command_ObjectPlace_f(void) G_SetGameModified(multiplayer); // Entering objectplace? - if (!objectplacing) + if (!objectplacing || COM_Argc() > 1) { - objectplacing = true; - - if (players[0].powers[pw_carry] == CR_NIGHTSMODE) - return; - - if (!COM_CheckParm("-silent")) + if (!objectplacing) { - HU_SetCEchoFlags(V_RETURN8|V_MONOSPACE|V_AUTOFADEOUT); - HU_SetCEchoDuration(10); - HU_DoCEcho(va(M_GetText( - "\\\\\\\\\\\\\\\\\\\\\\\\\x82" - " Objectplace Controls: \x80\\\\" - "Weapon Next/Prev: Cycle mapthings\\" - " Jump: Float up \\" - " Spin: Float down \\" - " Fire Ring: Place object \\"))); + objectplacing = true; + + if (players[0].powers[pw_carry] == CR_NIGHTSMODE) + return; + + if (!COM_CheckParm("-silent")) + { + HU_SetCEchoFlags(V_RETURN8|V_MONOSPACE|V_AUTOFADEOUT); + HU_SetCEchoDuration(10); + HU_DoCEcho(va(M_GetText( + "\\\\\\\\\\\\\\\\\\\\\\\\\x82" + " Objectplace Controls: \x80\\\\" + "Weapon Next/Prev: Cycle mapthings\\" + " Jump: Float up \\" + " Spin: Float down \\" + " Fire Ring: Place object \\"))); + } + + // Save all the player's data. + op_oldflags1 = players[0].mo->flags; + op_oldflags2 = players[0].mo->flags2; + op_oldeflags = players[0].mo->eflags; + op_oldpflags = players[0].pflags; + op_oldmomx = players[0].mo->momx; + op_oldmomy = players[0].mo->momy; + op_oldmomz = players[0].mo->momz; + op_oldheight = players[0].mo->height; + op_oldstate = S_PLAY_STND; + op_oldcolor = players[0].mo->color; // save color too in case of super/fireflower + + // Remove ALL flags and motion. + P_UnsetThingPosition(players[0].mo); + players[0].pflags = 0; + players[0].mo->flags2 = 0; + players[0].mo->eflags = 0; + players[0].mo->flags = (MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP); + players[0].mo->momx = players[0].mo->momy = players[0].mo->momz = 0; + P_SetThingPosition(players[0].mo); + + // Take away color so things display properly + players[0].mo->color = 0; + + // Like the classics, recover from death by entering objectplace + if (players[0].mo->health <= 0) + { + players[0].mo->health = 1; + players[0].deadtimer = 0; + op_oldflags1 = mobjinfo[MT_PLAYER].flags; + ++players[0].lives; + players[0].playerstate = PST_LIVE; + P_RestoreMusic(&players[0]); + } + else + op_oldstate = (statenum_t)(players[0].mo->state-states); } - // Save all the player's data. - op_oldflags1 = players[0].mo->flags; - op_oldflags2 = players[0].mo->flags2; - op_oldeflags = players[0].mo->eflags; - op_oldpflags = players[0].pflags; - op_oldmomx = players[0].mo->momx; - op_oldmomy = players[0].mo->momy; - op_oldmomz = players[0].mo->momz; - op_oldheight = players[0].mo->height; - op_oldstate = S_PLAY_STND; - op_oldcolor = players[0].mo->color; // save color too in case of super/fireflower - - // Remove ALL flags and motion. - P_UnsetThingPosition(players[0].mo); - players[0].pflags = 0; - players[0].mo->flags2 = 0; - players[0].mo->eflags = 0; - players[0].mo->flags = (MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP); - players[0].mo->momx = players[0].mo->momy = players[0].mo->momz = 0; - P_SetThingPosition(players[0].mo); - - // Take away color so things display properly - players[0].mo->color = 0; - - // Like the classics, recover from death by entering objectplace - if (players[0].mo->health <= 0) + if (COM_Argc() > 1) { - players[0].mo->health = 1; - players[0].deadtimer = 0; - op_oldflags1 = mobjinfo[MT_PLAYER].flags; - ++players[0].lives; - players[0].playerstate = PST_LIVE; - P_RestoreMusic(&players[0]); + UINT16 mapthingnum = atoi(COM_Argv(1)); + mobjtype_t type = P_GetMobjtype(mapthingnum); + if (type == MT_UNKNOWN) + CONS_Printf(M_GetText("No mobj type delegated to thing type %d.\n"), mapthingnum); + else + op_currentthing = type; } - else - op_oldstate = (statenum_t)(players[0].mo->state-states); // If no thing set, then cycle a little if (!op_currentthing) @@ -1506,8 +1522,8 @@ void Command_ObjectPlace_f(void) op_currentthing = 1; OP_CycleThings(1); } - else // Cycle things sets this for the former. - players[0].mo->height = mobjinfo[op_currentthing].height; + else + OP_CycleThings(0); // sets all necessary height values without cycling op_currentthing P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); } diff --git a/src/m_menu.c b/src/m_menu.c index f0f4e85ae..6e0d520ae 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -406,37 +406,29 @@ static void M_ResetCvars(void); // Consvar onchange functions static void Newgametype_OnChange(void); -#ifdef HWRENDER -static void Newrenderer_OnChange(void); -#endif static void Dummymares_OnChange(void); // ========================================================================== // CONSOLE VARIABLES AND THEIR POSSIBLE VALUES GO HERE. // ========================================================================== -consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL }; +consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t map_cons_t[] = { {1,"MIN"}, {NUMMAPS, "MAX"}, {0,NULL} }; -consvar_t cv_nextmap = {"nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange); static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; -consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange); // This gametype list is integral for many different reasons. // When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; -consvar_t cv_newgametype = {"newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL}; - -#ifdef HWRENDER -consvar_t cv_newrenderer = {"newrenderer", "Software", CV_HIDEN|CV_CALL, cv_renderer_t, Newrenderer_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static int newrenderer_set = 1;/* Software doesn't need confirmation! */ -#endif +consvar_t cv_newgametype = CVAR_INIT ("newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange); static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, @@ -447,22 +439,22 @@ static CV_PossibleValue_t serversort_cons_t[] = { {5,"Gametype"}, {0,NULL} }; -consvar_t cv_serversort = {"serversort", "Ping", CV_HIDEN | CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_serversort = CVAR_INIT ("serversort", "Ping", CV_HIDEN | CV_CALL, serversort_cons_t, M_SortServerList); // first time memory -consvar_t cv_tutorialprompt = {"tutorialprompt", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_tutorialprompt = CVAR_INIT ("tutorialprompt", "On", CV_SAVE, CV_OnOff, NULL); // autorecord demos for time attack -static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_autorecord = CVAR_INIT ("autorecord", "Yes", 0, CV_YesNo, NULL); CV_PossibleValue_t ghost_cons_t[] = {{0, "Hide"}, {1, "Show"}, {2, "Show All"}, {0, NULL}}; CV_PossibleValue_t ghost2_cons_t[] = {{0, "Hide"}, {1, "Show"}, {0, NULL}}; -consvar_t cv_ghost_bestscore = {"ghost_bestscore", "Show", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ghost_besttime = {"ghost_besttime", "Show", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ghost_bestrings = {"ghost_bestrings", "Show", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ghost_last = {"ghost_last", "Show", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ghost_guest = {"ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_ghost_bestscore = CVAR_INIT ("ghost_bestscore", "Show", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_besttime = CVAR_INIT ("ghost_besttime", "Show", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_bestrings = CVAR_INIT ("ghost_bestrings", "Show", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_last = CVAR_INIT ("ghost_last", "Show", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_guest = CVAR_INIT ("ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL); //Console variables used solely in the menu system. //todo: add a way to use non-console variables in the menu @@ -476,19 +468,19 @@ static CV_PossibleValue_t dummymares_cons_t[] = { {-1, "END"}, {0,"Overall"}, {1,"Mare 1"}, {2,"Mare 2"}, {3,"Mare 3"}, {4,"Mare 4"}, {5,"Mare 5"}, {6,"Mare 6"}, {7,"Mare 7"}, {8,"Mare 8"}, {0,NULL} }; -static consvar_t cv_dummyteam = {"dummyteam", "Spectator", CV_HIDEN, dummyteam_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummyscramble = {"dummyscramble", "Random", CV_HIDEN, dummyscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummyrings = {"dummyrings", "0", CV_HIDEN, ringlimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummylives = {"dummylives", "0", CV_HIDEN, liveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, contlimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDEN, dummyteam_cons_t, NULL); +static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDEN, dummyscramble_cons_t, NULL); +static consvar_t cv_dummyrings = CVAR_INIT ("dummyrings", "0", CV_HIDEN, ringlimit_cons_t, NULL); +static consvar_t cv_dummylives = CVAR_INIT ("dummylives", "0", CV_HIDEN, liveslimit_cons_t, NULL); +static consvar_t cv_dummycontinues = CVAR_INIT ("dummycontinues", "0", CV_HIDEN, contlimit_cons_t, NULL); +static consvar_t cv_dummymares = CVAR_INIT ("dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange); CV_PossibleValue_t marathon_cons_t[] = {{0, "Standard"}, {1, "Live Event Backup"}, {2, "Ultimate"}, {0, NULL}}; CV_PossibleValue_t loadless_cons_t[] = {{0, "Realtime"}, {1, "In-game"}, {0, NULL}}; -consvar_t cv_dummymarathon = {"dummymarathon", "Standard", CV_HIDEN, marathon_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_dummycutscenes = {"dummycutscenes", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_dummyloadless = {"dummyloadless", "In-game", CV_HIDEN, loadless_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_dummymarathon = CVAR_INIT ("dummymarathon", "Standard", CV_HIDEN, marathon_cons_t, NULL); +consvar_t cv_dummycutscenes = CVAR_INIT ("dummycutscenes", "Off", CV_HIDEN, CV_OnOff, NULL); +consvar_t cv_dummyloadless = CVAR_INIT ("dummyloadless", "In-game", CV_HIDEN, loadless_cons_t, NULL); // ========================================================================== // ORGANIZATION START. @@ -1347,7 +1339,7 @@ static menuitem_t OP_VideoOptionsMenu[] = #endif {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 16}, #ifdef HWRENDER - {IT_STRING | IT_CVAR, NULL, "Renderer", &cv_newrenderer, 21}, + {IT_STRING | IT_CVAR, NULL, "Renderer", &cv_renderer, 21}, #else {IT_TRANSTEXT | IT_PAIR, "Renderer", "Software", &cv_renderer, 21}, #endif @@ -1663,8 +1655,11 @@ static menuitem_t OP_ServerOptionsMenu[] = #ifndef NONET {IT_HEADER, NULL, "Advanced", NULL, 225}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231}, + {IT_STRING | IT_CVAR, NULL, "Join delay", &cv_joindelay, 246}, {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 251}, + + {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 256}, #endif }; @@ -2455,46 +2450,6 @@ static void Newgametype_OnChange(void) } } -#ifdef HWRENDER -static void Newrenderer_AREYOUSURE(INT32 c) -{ - int n; - switch (c) - { - case 'y': - case KEY_ENTER: - n = cv_newrenderer.value; - newrenderer_set |= n; - CV_SetValue(&cv_renderer, n); - break; - default: - CV_StealthSetValue(&cv_newrenderer, cv_renderer.value); - } -} - -static void Newrenderer_OnChange(void) -{ - /* Well this works for now because there's only two options. */ - int n; - n = cv_newrenderer.value; - newrenderer_set |= cv_renderer.value; - if (( newrenderer_set & n )) - CV_SetValue(&cv_renderer, n); - else - { - M_StartMessage( - "The OpenGL renderer is incomplete.\n" - "Some visuals may fail to appear, or\n" - "appear incorrectly.\n" - "Do you still want to switch to it?\n" - "\n" - "(Press 'y' or 'n')", - Newrenderer_AREYOUSURE, MM_YESNO - ); - } -} -#endif/*HWRENDER*/ - void Screenshot_option_Onchange(void) { OP_ScreenshotOptionsMenu[op_screenshot_folder].status = @@ -3839,7 +3794,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); @@ -3927,7 +3882,7 @@ void M_Ticker(void) if (currentMenu == &OP_ScreenshotOptionsDef) M_SetupScreenshotMenu(); -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) I_lock_mutex(&ms_ServerList_mutex); { if (ms_ServerList) @@ -8017,7 +7972,7 @@ static void M_SecretsMenu(INT32 choice) skyRoomMenuTranslations[i-1] = (UINT8)ul; SR_MainMenu[i].text = unlockables[ul].name; - SR_MainMenu[i].alphaKey = (UINT8)unlockables[ul].height; + SR_MainMenu[i].alphaKey = (UINT16)unlockables[ul].height; if (unlockables[ul].type == SECRET_HEADER) { @@ -8480,7 +8435,7 @@ static void M_DrawLoadGameData(void) sprdef = &charbotskin->sprites[SPR2_SIGN]; if (!sprdef->numframes) goto skipbot; - colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0); + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin-1, charbotskin->prefcolor, 0); sprframe = &sprdef->spriteframes[0]; patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); @@ -9002,8 +8957,6 @@ void M_ForceSaveSlotSelected(INT32 sslot) // ================ // CHARACTER SELECT // ================ - -// lactozilla: sometimes the renderer changes and these patches don't exist anymore static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) { if (!(description[i].picname[0])) @@ -9246,7 +9199,6 @@ static void M_DrawSetupChoosePlayerMenu(void) INT32 x, y; INT32 w = (vid.width/vid.dupx); - // lactozilla: the renderer changed so recache patches if (needpatchrecache) M_CacheCharacterSelect(); @@ -11191,8 +11143,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) { @@ -11201,7 +11154,9 @@ Check_new_version_thread (int *id) okay = 0; +#ifdef UPDATE_ALERT if (M_CheckMODVersion(*id)) +#endif { I_lock_mutex(&ms_QueryId_mutex); { @@ -11221,6 +11176,7 @@ Check_new_version_thread (int *id) GetRoomsList(hosting, *id); } } +#ifdef UPDATE_ALERT else { I_lock_mutex(&ms_QueryId_mutex); @@ -11229,6 +11185,7 @@ Check_new_version_thread (int *id) } I_unlock_mutex(ms_QueryId_mutex); } +#endif if (okay) { @@ -11245,8 +11202,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) { @@ -11275,7 +11231,7 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("Add-ons are currently loaded.\n\nYou will only be able to join a server if\nit has the same ones loaded in the same order, which may be unlikely.\n\nIf you wish to play on other servers,\nrestart the game to clear existing add-ons.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); return; } @@ -11287,7 +11243,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 @@ -11309,9 +11265,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); @@ -11325,17 +11286,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/m_menu.h b/src/m_menu.h index 88c06ae6f..0465128ef 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -327,7 +327,7 @@ typedef struct menuitem_s void *itemaction; // hotkey in menu or y of the item - UINT8 alphaKey; + UINT16 alphaKey; } menuitem_t; extern menuitem_t MP_RoomMenu[]; diff --git a/src/m_misc.c b/src/m_misc.c index 42890cb08..d97d8f94b 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -102,16 +102,16 @@ typedef off_t off64_t; #endif static CV_PossibleValue_t screenshot_cons_t[] = {{0, "Default"}, {1, "HOME"}, {2, "SRB2"}, {3, "CUSTOM"}, {0, NULL}}; -consvar_t cv_screenshot_option = {"screenshot_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Screenshot_option_Onchange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_screenshot_folder = {"screenshot_folder", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_screenshot_option = CVAR_INIT ("screenshot_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Screenshot_option_Onchange); +consvar_t cv_screenshot_folder = CVAR_INIT ("screenshot_folder", "", CV_SAVE, NULL, NULL); -consvar_t cv_screenshot_colorprofile = {"screenshot_colorprofile", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_screenshot_colorprofile = CVAR_INIT ("screenshot_colorprofile", "Yes", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t moviemode_cons_t[] = {{MM_GIF, "GIF"}, {MM_APNG, "aPNG"}, {MM_SCREENSHOT, "Screenshots"}, {0, NULL}}; -consvar_t cv_moviemode = {"moviemode_mode", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_moviemode = CVAR_INIT ("moviemode_mode", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange); -consvar_t cv_movie_option = {"movie_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Moviemode_option_Onchange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_movie_folder = {"movie_folder", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_movie_option = CVAR_INIT ("movie_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Moviemode_option_Onchange); +consvar_t cv_movie_folder = CVAR_INIT ("movie_folder", "", CV_SAVE, NULL, NULL); static CV_PossibleValue_t zlib_mem_level_t[] = { {1, "(Min Memory) 1"}, @@ -153,16 +153,16 @@ static CV_PossibleValue_t apng_delay_t[] = { // zlib memory usage is as follows: // (1 << (zlib_window_bits+2)) + (1 << (zlib_level+9)) -consvar_t cv_zlib_memory = {"png_memory_level", "7", CV_SAVE, zlib_mem_level_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_level = {"png_compress_level", "(Optimal) 6", CV_SAVE, zlib_level_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_strategy = {"png_strategy", "Normal", CV_SAVE, zlib_strategy_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_window_bits = {"png_window_size", "32k", CV_SAVE, zlib_window_bits_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_zlib_memory = CVAR_INIT ("png_memory_level", "7", CV_SAVE, zlib_mem_level_t, NULL); +consvar_t cv_zlib_level = CVAR_INIT ("png_compress_level", "(Optimal) 6", CV_SAVE, zlib_level_t, NULL); +consvar_t cv_zlib_strategy = CVAR_INIT ("png_strategy", "Normal", CV_SAVE, zlib_strategy_t, NULL); +consvar_t cv_zlib_window_bits = CVAR_INIT ("png_window_size", "32k", CV_SAVE, zlib_window_bits_t, NULL); -consvar_t cv_zlib_memorya = {"apng_memory_level", "(Max Memory) 9", CV_SAVE, zlib_mem_level_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_levela = {"apng_compress_level", "4", CV_SAVE, zlib_level_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_strategya = {"apng_strategy", "RLE", CV_SAVE, zlib_strategy_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_zlib_window_bitsa = {"apng_window_size", "32k", CV_SAVE, zlib_window_bits_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_apng_delay = {"apng_speed", "1x", CV_SAVE, apng_delay_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_zlib_memorya = CVAR_INIT ("apng_memory_level", "(Max Memory) 9", CV_SAVE, zlib_mem_level_t, NULL); +consvar_t cv_zlib_levela = CVAR_INIT ("apng_compress_level", "4", CV_SAVE, zlib_level_t, NULL); +consvar_t cv_zlib_strategya = CVAR_INIT ("apng_strategy", "RLE", CV_SAVE, zlib_strategy_t, NULL); +consvar_t cv_zlib_window_bitsa = CVAR_INIT ("apng_window_size", "32k", CV_SAVE, zlib_window_bits_t, NULL); +consvar_t cv_apng_delay = CVAR_INIT ("apng_speed", "1x", CV_SAVE, apng_delay_t, NULL); boolean takescreenshot = false; // Take a screenshot this tic diff --git a/src/m_perfstats.c b/src/m_perfstats.c new file mode 100644 index 000000000..df1e31b5e --- /dev/null +++ b/src/m_perfstats.c @@ -0,0 +1,541 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 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 m_perfstats.c +/// \brief Performance measurement tools. + +#include "m_perfstats.h" +#include "v_video.h" +#include "i_video.h" +#include "d_netcmd.h" +#include "r_main.h" +#include "i_system.h" +#include "z_zone.h" +#include "p_local.h" + +#ifdef HWRENDER +#include "hardware/hw_main.h" +#endif + +int ps_tictime = 0; + +int ps_playerthink_time = 0; +int ps_thinkertime = 0; + +int ps_thlist_times[NUM_THINKERLISTS]; +static const char* thlist_names[] = { + "Polyobjects: %d", + "Main: %d", + "Mobjs: %d", + "Dynamic slopes: %d", + "Precipitation: %d", + NULL +}; +static const char* thlist_shortnames[] = { + "plyobjs %d", + "main %d", + "mobjs %d", + "dynslop %d", + "precip %d", + NULL +}; + +int ps_checkposition_calls = 0; + +int ps_lua_thinkframe_time = 0; +int ps_lua_mobjhooks = 0; + +// dynamically allocated resizeable array for thinkframe hook stats +ps_hookinfo_t *thinkframe_hooks = NULL; +int thinkframe_hooks_length = 0; +int thinkframe_hooks_capacity = 16; + +void PS_SetThinkFrameHookInfo(int index, UINT32 time_taken, char* short_src) +{ + if (!thinkframe_hooks) + { + // array needs to be initialized + thinkframe_hooks = Z_Malloc(sizeof(ps_hookinfo_t) * thinkframe_hooks_capacity, PU_STATIC, NULL); + } + if (index >= thinkframe_hooks_capacity) + { + // array needs more space, realloc with double size + thinkframe_hooks_capacity *= 2; + thinkframe_hooks = Z_Realloc(thinkframe_hooks, + sizeof(ps_hookinfo_t) * thinkframe_hooks_capacity, PU_STATIC, NULL); + } + thinkframe_hooks[index].time_taken = time_taken; + memcpy(thinkframe_hooks[index].short_src, short_src, LUA_IDSIZE * sizeof(char)); + // since the values are set sequentially from begin to end, the last call should leave + // the correct value to this variable + thinkframe_hooks_length = index + 1; +} + +void M_DrawPerfStats(void) +{ + char s[100]; + int currenttime = I_GetTimeMicros(); + int frametime = currenttime - ps_prevframetime; + ps_prevframetime = currenttime; + + if (cv_perfstats.value == 1) // rendering + { + if (vid.width < 640 || vid.height < 400) // low resolution + { + snprintf(s, sizeof s - 1, "frmtime %d", frametime); + V_DrawThinString(20, 10, V_MONOSPACE | V_YELLOWMAP, s); + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + { + snprintf(s, sizeof s - 1, "ui %d", ps_uitime); + V_DrawThinString(20, 18, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); + V_DrawThinString(20, 26, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "logic %d", ps_tictime); + V_DrawThinString(20, 38, V_MONOSPACE | V_GRAYMAP, s); + return; + } + snprintf(s, sizeof s - 1, "drwtime %d", ps_rendercalltime); + V_DrawThinString(20, 18, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "bspcall %d", ps_numbspcalls); + V_DrawThinString(90, 10, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "sprites %d", ps_numsprites); + V_DrawThinString(90, 18, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "drwnode %d", ps_numdrawnodes); + V_DrawThinString(90, 26, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "plyobjs %d", ps_numpolyobjects); + V_DrawThinString(90, 34, V_MONOSPACE | V_BLUEMAP, s); +#ifdef HWRENDER + if (rendermode == render_opengl) // OpenGL specific stats + { + snprintf(s, sizeof s - 1, "skybox %d", ps_hw_skyboxtime); + V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "bsptime %d", ps_bsptime); + V_DrawThinString(24, 34, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "nodesrt %d", ps_hw_nodesorttime); + V_DrawThinString(24, 42, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "nodedrw %d", ps_hw_nodedrawtime); + V_DrawThinString(24, 50, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "sprsort %d", ps_hw_spritesorttime); + V_DrawThinString(24, 58, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "sprdraw %d", ps_hw_spritedrawtime); + V_DrawThinString(24, 66, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "other %d", + ps_rendercalltime - ps_hw_skyboxtime - ps_bsptime - ps_hw_nodesorttime + - ps_hw_nodedrawtime - ps_hw_spritesorttime - ps_hw_spritedrawtime + - ps_hw_batchsorttime - ps_hw_batchdrawtime); + V_DrawThinString(24, 74, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "ui %d", ps_uitime); + V_DrawThinString(20, 82, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); + V_DrawThinString(20, 90, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "logic %d", ps_tictime); + V_DrawThinString(20, 102, V_MONOSPACE | V_GRAYMAP, s); + if (cv_glbatching.value) + { + snprintf(s, sizeof s - 1, "batsort %d", ps_hw_batchsorttime); + V_DrawThinString(90, 46, V_MONOSPACE | V_REDMAP, s); + snprintf(s, sizeof s - 1, "batdraw %d", ps_hw_batchdrawtime); + V_DrawThinString(90, 54, V_MONOSPACE | V_REDMAP, s); + + snprintf(s, sizeof s - 1, "polygon %d", ps_hw_numpolys); + V_DrawThinString(155, 10, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "drwcall %d", ps_hw_numcalls); + V_DrawThinString(155, 18, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "shaders %d", ps_hw_numshaders); + V_DrawThinString(155, 26, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "vertex %d", ps_hw_numverts); + V_DrawThinString(155, 34, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "texture %d", ps_hw_numtextures); + V_DrawThinString(220, 10, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "polyflg %d", ps_hw_numpolyflags); + V_DrawThinString(220, 18, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "colors %d", ps_hw_numcolors); + V_DrawThinString(220, 26, V_MONOSPACE | V_PURPLEMAP, s); + } + else + { + // reset these vars so the "other" measurement isn't off + ps_hw_batchsorttime = 0; + ps_hw_batchdrawtime = 0; + } + } + else // software specific stats +#endif + { + snprintf(s, sizeof s - 1, "bsptime %d", ps_bsptime); + V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "sprclip %d", ps_sw_spritecliptime); + V_DrawThinString(24, 34, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "portals %d", ps_sw_portaltime); + V_DrawThinString(24, 42, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "planes %d", ps_sw_planetime); + V_DrawThinString(24, 50, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "masked %d", ps_sw_maskedtime); + V_DrawThinString(24, 58, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "other %d", + ps_rendercalltime - ps_bsptime - ps_sw_spritecliptime + - ps_sw_portaltime - ps_sw_planetime - ps_sw_maskedtime); + V_DrawThinString(24, 66, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "ui %d", ps_uitime); + V_DrawThinString(20, 74, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); + V_DrawThinString(20, 82, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "logic %d", ps_tictime); + V_DrawThinString(20, 94, V_MONOSPACE | V_GRAYMAP, s); + } + } + else // high resolution + { + snprintf(s, sizeof s - 1, "Frame time: %d", frametime); + V_DrawSmallString(20, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + { + snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); + V_DrawSmallString(20, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); + V_DrawSmallString(20, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); + V_DrawSmallString(20, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + return; + } + snprintf(s, sizeof s - 1, "3d rendering: %d", ps_rendercalltime); + V_DrawSmallString(20, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "BSP calls: %d", ps_numbspcalls); + V_DrawSmallString(115, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Sprites: %d", ps_numsprites); + V_DrawSmallString(115, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Drawnodes: %d", ps_numdrawnodes); + V_DrawSmallString(115, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Polyobjects: %d", ps_numpolyobjects); + V_DrawSmallString(115, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); +#ifdef HWRENDER + if (rendermode == render_opengl) // OpenGL specific stats + { + snprintf(s, sizeof s - 1, "Skybox render: %d", ps_hw_skyboxtime); + V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "RenderBSPNode: %d", ps_bsptime); + V_DrawSmallString(24, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Drwnode sort: %d", ps_hw_nodesorttime); + V_DrawSmallString(24, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Drwnode render: %d", ps_hw_nodedrawtime); + V_DrawSmallString(24, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Sprite sort: %d", ps_hw_spritesorttime); + V_DrawSmallString(24, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Sprite render: %d", ps_hw_spritedrawtime); + V_DrawSmallString(24, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + // Remember to update this calculation when adding more 3d rendering stats! + snprintf(s, sizeof s - 1, "Other: %d", + ps_rendercalltime - ps_hw_skyboxtime - ps_bsptime - ps_hw_nodesorttime + - ps_hw_nodedrawtime - ps_hw_spritesorttime - ps_hw_spritedrawtime + - ps_hw_batchsorttime - ps_hw_batchdrawtime); + V_DrawSmallString(24, 50, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); + V_DrawSmallString(20, 55, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); + V_DrawSmallString(20, 60, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); + V_DrawSmallString(20, 70, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + if (cv_glbatching.value) + { + snprintf(s, sizeof s - 1, "Batch sort: %d", ps_hw_batchsorttime); + V_DrawSmallString(115, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_REDMAP, s); + snprintf(s, sizeof s - 1, "Batch render: %d", ps_hw_batchdrawtime); + V_DrawSmallString(115, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_REDMAP, s); + + snprintf(s, sizeof s - 1, "Polygons: %d", ps_hw_numpolys); + V_DrawSmallString(200, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Vertices: %d", ps_hw_numverts); + V_DrawSmallString(200, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Draw calls: %d", ps_hw_numcalls); + V_DrawSmallString(200, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Shaders: %d", ps_hw_numshaders); + V_DrawSmallString(200, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Textures: %d", ps_hw_numtextures); + V_DrawSmallString(200, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Polyflags: %d", ps_hw_numpolyflags); + V_DrawSmallString(200, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Colors: %d", ps_hw_numcolors); + V_DrawSmallString(200, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + } + else + { + // reset these vars so the "other" measurement isn't off + ps_hw_batchsorttime = 0; + ps_hw_batchdrawtime = 0; + } + } + else // software specific stats +#endif + { + snprintf(s, sizeof s - 1, "RenderBSPNode: %d", ps_bsptime); + V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "R_ClipSprites: %d", ps_sw_spritecliptime); + V_DrawSmallString(24, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Portals+Skybox: %d", ps_sw_portaltime); + V_DrawSmallString(24, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "R_DrawPlanes: %d", ps_sw_planetime); + V_DrawSmallString(24, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "R_DrawMasked: %d", ps_sw_maskedtime); + V_DrawSmallString(24, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + // Remember to update this calculation when adding more 3d rendering stats! + snprintf(s, sizeof s - 1, "Other: %d", + ps_rendercalltime - ps_bsptime - ps_sw_spritecliptime + - ps_sw_portaltime - ps_sw_planetime - ps_sw_maskedtime); + V_DrawSmallString(24, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); + V_DrawSmallString(20, 50, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); + V_DrawSmallString(20, 55, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); + V_DrawSmallString(20, 65, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + } + } + } + else if (cv_perfstats.value == 2) // logic + { + int i = 0; + thinker_t *thinker; + int thinkercount = 0; + int polythcount = 0; + int mainthcount = 0; + int mobjcount = 0; + int nothinkcount = 0; + int scenerycount = 0; + int dynslopethcount = 0; + int precipcount = 0; + int removecount = 0; + // y offset for drawing columns + int yoffset1 = 0; + int yoffset2 = 0; + + for (i = 0; i < NUM_THINKERLISTS; i++) + { + for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next) + { + thinkercount++; + if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + removecount++; + else if (i == THINK_POLYOBJ) + polythcount++; + else if (i == THINK_MAIN) + mainthcount++; + else if (i == THINK_MOBJ) + { + if (thinker->function.acp1 == (actionf_p1)P_MobjThinker) + { + mobj_t *mobj = (mobj_t*)thinker; + mobjcount++; + if (mobj->flags & MF_NOTHINK) + nothinkcount++; + else if (mobj->flags & MF_SCENERY) + scenerycount++; + } + } + else if (i == THINK_DYNSLOPE) + dynslopethcount++; + else if (i == THINK_PRECIP) + precipcount++; + } + } + + if (vid.width < 640 || vid.height < 400) // low resolution + { + snprintf(s, sizeof s - 1, "logic %d", ps_tictime); + V_DrawThinString(20, 10, V_MONOSPACE | V_YELLOWMAP, s); + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + return; + snprintf(s, sizeof s - 1, "plrthnk %d", ps_playerthink_time); + V_DrawThinString(24, 18, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "thnkers %d", ps_thinkertime); + V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); + for (i = 0; i < NUM_THINKERLISTS; i++) + { + yoffset1 += 8; + snprintf(s, sizeof s - 1, thlist_shortnames[i], ps_thlist_times[i]); + V_DrawThinString(28, 26+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); + } + snprintf(s, sizeof s - 1, "lthinkf %d", ps_lua_thinkframe_time); + V_DrawThinString(24, 34+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "other %d", + ps_tictime - ps_playerthink_time - ps_thinkertime - ps_lua_thinkframe_time); + V_DrawThinString(24, 42+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "thnkers %d", thinkercount); + V_DrawThinString(90, 10, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "plyobjs %d", polythcount); + V_DrawThinString(94, 18, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "main %d", mainthcount); + V_DrawThinString(94, 26, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "mobjs %d", mobjcount); + V_DrawThinString(94, 34, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "regular %d", mobjcount - scenerycount - nothinkcount); + V_DrawThinString(98, 42, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "scenery %d", scenerycount); + V_DrawThinString(98, 50, V_MONOSPACE | V_BLUEMAP, s); + if (nothinkcount) + { + snprintf(s, sizeof s - 1, "nothink %d", nothinkcount); + V_DrawThinString(98, 58, V_MONOSPACE | V_BLUEMAP, s); + yoffset2 += 8; + } + snprintf(s, sizeof s - 1, "dynslop %d", dynslopethcount); + V_DrawThinString(94, 58+yoffset2, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "precip %d", precipcount); + V_DrawThinString(94, 66+yoffset2, V_MONOSPACE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "remove %d", removecount); + V_DrawThinString(94, 74+yoffset2, V_MONOSPACE | V_BLUEMAP, s); + + snprintf(s, sizeof s - 1, "lmhooks %d", ps_lua_mobjhooks); + V_DrawThinString(170, 10, V_MONOSPACE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "chkpos %d", ps_checkposition_calls); + V_DrawThinString(170, 18, V_MONOSPACE | V_PURPLEMAP, s); + } + else // high resolution + { + snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); + V_DrawSmallString(20, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + return; + snprintf(s, sizeof s - 1, "P_PlayerThink: %d", ps_playerthink_time); + V_DrawSmallString(24, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "P_RunThinkers: %d", ps_thinkertime); + V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + for (i = 0; i < NUM_THINKERLISTS; i++) + { + yoffset1 += 5; + snprintf(s, sizeof s - 1, thlist_names[i], ps_thlist_times[i]); + V_DrawSmallString(28, 20+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + } + snprintf(s, sizeof s - 1, "LUAh_ThinkFrame: %d", ps_lua_thinkframe_time); + V_DrawSmallString(24, 25+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "Other: %d", + ps_tictime - ps_playerthink_time - ps_thinkertime - ps_lua_thinkframe_time); + V_DrawSmallString(24, 30+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "Thinkers: %d", thinkercount); + V_DrawSmallString(115, 10+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Polyobjects: %d", polythcount); + V_DrawSmallString(119, 15+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Main: %d", mainthcount); + V_DrawSmallString(119, 20+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Mobjs: %d", mobjcount); + V_DrawSmallString(119, 25+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Regular: %d", mobjcount - scenerycount - nothinkcount); + V_DrawSmallString(123, 30+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Scenery: %d", scenerycount); + V_DrawSmallString(123, 35+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + if (nothinkcount) + { + snprintf(s, sizeof s - 1, "Nothink: %d", nothinkcount); + V_DrawSmallString(123, 40+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + yoffset2 += 5; + } + snprintf(s, sizeof s - 1, "Dynamic slopes: %d", dynslopethcount); + V_DrawSmallString(119, 40+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Precipitation: %d", precipcount); + V_DrawSmallString(119, 45+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + snprintf(s, sizeof s - 1, "Pending removal: %d", removecount); + V_DrawSmallString(119, 50+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); + + snprintf(s, sizeof s - 1, "Calls:"); + V_DrawSmallString(212, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "Lua mobj hooks: %d", ps_lua_mobjhooks); + V_DrawSmallString(216, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + snprintf(s, sizeof s - 1, "P_CheckPosition: %d", ps_checkposition_calls); + V_DrawSmallString(216, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); + } + } + else if (cv_perfstats.value == 3) // lua thinkframe + { + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + return; + if (vid.width < 640 || vid.height < 400) // low resolution + { + // it's not gonna fit very well.. + V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not available for resolutions below 640x400"); + } + else // high resolution + { + int i; + // text writing position + int x = 2; + int y = 4; + UINT32 text_color; + char tempbuffer[LUA_IDSIZE]; + char last_mod_name[LUA_IDSIZE]; + last_mod_name[0] = '\0'; + for (i = 0; i < thinkframe_hooks_length; i++) + { + char* str = thinkframe_hooks[i].short_src; + char* tempstr = tempbuffer; + int len = (int)strlen(str); + char* str_ptr; + if (strcmp(".lua", str + len - 4) == 0) + { + str[len-4] = '\0'; // remove .lua at end + len -= 4; + } + // we locate the wad/pk3 name in the string and compare it to + // what we found on the previous iteration. + // if the name has changed, print it out on the screen + strcpy(tempstr, str); + str_ptr = strrchr(tempstr, '|'); + if (str_ptr) + { + *str_ptr = '\0'; + str = str_ptr + 1; // this is the name of the hook without the mod file name + str_ptr = strrchr(tempstr, PATHSEP[0]); + if (str_ptr) + tempstr = str_ptr + 1; + // tempstr should now point to the mod name, (wad/pk3) possibly truncated + if (strcmp(tempstr, last_mod_name) != 0) + { + strcpy(last_mod_name, tempstr); + len = (int)strlen(tempstr); + if (len > 25) + tempstr += len - 25; + snprintf(s, sizeof s - 1, "%s", tempstr); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + } + text_color = V_YELLOWMAP; + } + else + { + // probably a standalone lua file + // cut off the folder if it's there + str_ptr = strrchr(tempstr, PATHSEP[0]); + if (str_ptr) + str = str_ptr + 1; + text_color = 0; // white + } + len = (int)strlen(str); + if (len > 20) + str += len - 20; + snprintf(s, sizeof s - 1, "%20s: %u", str, thinkframe_hooks[i].time_taken); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | text_color, s); + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + } + } + } +} diff --git a/src/m_perfstats.h b/src/m_perfstats.h new file mode 100644 index 000000000..1db46025e --- /dev/null +++ b/src/m_perfstats.h @@ -0,0 +1,42 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 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 m_perfstats.h +/// \brief Performance measurement tools. + +#ifndef __M_PERFSTATS_H__ +#define __M_PERFSTATS_H__ + +#include "doomdef.h" +#include "lua_script.h" +#include "p_local.h" + +extern int ps_tictime; + +extern int ps_playerthink_time; +extern int ps_thinkertime; + +extern int ps_thlist_times[]; + +extern int ps_checkposition_calls; + +extern int ps_lua_thinkframe_time; +extern int ps_lua_mobjhooks; + +typedef struct +{ + UINT32 time_taken; + char short_src[LUA_IDSIZE]; +} ps_hookinfo_t; + +void PS_SetThinkFrameHookInfo(int index, UINT32 time_taken, char* short_src); + +void M_DrawPerfStats(void); + +#endif diff --git a/src/mserv.c b/src/mserv.c index 44fa8e3ae..dfb417415 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,27 +45,30 @@ 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[] = { {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}; -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_masterserver = CVAR_INIT ("masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); +consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, NULL, Update_parameters); -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 = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters); 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*/ } diff --git a/src/p_enemy.c b/src/p_enemy.c index a1dafce05..f9baf1813 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3040,11 +3040,11 @@ void A_Boss1Laser(mobj_t *actor) z = actor->z + FixedMul(56*FRACUNIT, actor->scale); break; case 2: - var2 = 3; // Fire middle laser + var1 = locvar1; var2 = 3; // Fire middle laser A_Boss1Laser(actor); - var2 = 0; // Fire left laser + var1 = locvar1; var2 = 0; // Fire left laser A_Boss1Laser(actor); - var2 = 1; // Fire right laser + var1 = locvar1; var2 = 1; // Fire right laser A_Boss1Laser(actor); return; break; @@ -8840,25 +8840,26 @@ void A_Dye(mobj_t *actor) INT32 locvar2 = var2; mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); - UINT8 color = (UINT8)locvar2; + UINT16 color = (UINT16)locvar2; if (LUA_CallAction("A_Dye", actor)) return; if (color >= numskincolors) return; - if (!color) - target->colorized = false; - else - target->colorized = true; - // What if it's a player? if (target->player) - { target->player->powers[pw_dye] = color; - return; - } - target->color = color; + if (!color) + { + target->colorized = false; + target->color = target->player ? target->player->skincolor : SKINCOLOR_NONE; + } + else if (!(target->player)) + { + target->colorized = true; + target->color = color; + } } // Function: A_MoveRelative diff --git a/src/p_local.h b/src/p_local.h index 4077fecf6..8a5084962 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -274,6 +274,8 @@ extern tic_t itemrespawntime[ITEMQUESIZE]; extern size_t iquehead, iquetail; extern consvar_t cv_gravity, cv_movebob; +mobjtype_t P_GetMobjtype(UINT16 mthingtype); + void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); @@ -426,6 +428,7 @@ void P_Initsecnode(void); void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck); fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height); +fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height); boolean PIT_PushableMoved(mobj_t *thing); boolean P_DoSpring(mobj_t *spring, mobj_t *object); diff --git a/src/p_map.c b/src/p_map.c index 74c2790f7..0a9107ee5 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -33,6 +33,8 @@ #include "lua_hook.h" +#include "m_perfstats.h" // ps_checkposition_calls + fixed_t tmbbox[4]; mobj_t *tmthing; static INT32 tmflags; @@ -2019,6 +2021,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) subsector_t *newsubsec; boolean blockval = true; + ps_checkposition_calls++; + I_Assert(thing != NULL); #ifdef PARANOIA if (P_MobjWasRemoved(thing)) @@ -2661,7 +2665,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) fixed_t tryx = thing->x; fixed_t tryy = thing->y; fixed_t radius = thing->radius; - fixed_t thingtop = thing->z + thing->height; + fixed_t thingtop; fixed_t startingonground = P_IsObjectOnGround(thing); floatok = false; @@ -2702,6 +2706,11 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13) maxstep <<= 1; + // If using type Section1:14, no maxstep. + if (P_PlayerTouchingSectorSpecial(thing->player, 1, 14) + || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14) + maxstep = 0; + // Don't 'step up' while springing, // Only step up "if needed". if (thing->player->panim == PA_SPRING @@ -2721,39 +2730,45 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) floatok = true; - if (thing->eflags & MFE_VERTICALFLIP) + thingtop = thing->z + thing->height; + + // Step up + if (thing->z < tmfloorz) { - if (thing->z < tmfloorz) + if (tmfloorz - thing->z <= maxstep) + { + thing->z = thing->floorz = tmfloorz; + thing->floorrover = tmfloorrover; + thing->eflags |= MFE_JUSTSTEPPEDDOWN; + } + else + { return false; // mobj must raise itself to fit + } } else if (tmceilingz < thingtop) - return false; // mobj must lower itself to fit - - // Ramp test - if (maxstep > 0 && !( - thing->player && ( - P_PlayerTouchingSectorSpecial(thing->player, 1, 14) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14) - ) - ) + { + if (thingtop - tmceilingz <= maxstep) + { + thing->z = ( thing->ceilingz = tmceilingz ) - thing->height; + thing->ceilingrover = tmceilingrover; + thing->eflags |= MFE_JUSTSTEPPEDDOWN; + } + else + { + return false; // mobj must lower itself to fit + } + } + else if (maxstep > 0) // Step down { // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more. - if (thing->eflags & MFE_VERTICALFLIP) + if (thingtop == thing->ceilingz && tmceilingz > thingtop && tmceilingz - thingtop <= maxstep) { - if (thingtop == thing->ceilingz && tmceilingz > thingtop && tmceilingz - thingtop <= maxstep) - { - thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height; - thing->ceilingrover = tmceilingrover; - thing->eflags |= MFE_JUSTSTEPPEDDOWN; - } - else if (tmceilingz < thingtop && thingtop - tmceilingz <= maxstep) - { - thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height; - thing->ceilingrover = tmceilingrover; - thing->eflags |= MFE_JUSTSTEPPEDDOWN; - } + thing->z = (thing->ceilingz = tmceilingz) - thing->height; + thing->ceilingrover = tmceilingrover; + thing->eflags |= MFE_JUSTSTEPPEDDOWN; } else if (thing->z == thing->floorz && tmfloorz < thing->z && thing->z - tmfloorz <= maxstep) { @@ -2761,28 +2776,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) thing->floorrover = tmfloorrover; thing->eflags |= MFE_JUSTSTEPPEDDOWN; } - else if (tmfloorz > thing->z && tmfloorz - thing->z <= maxstep) - { - thing->z = thing->floorz = tmfloorz; - thing->floorrover = tmfloorrover; - thing->eflags |= MFE_JUSTSTEPPEDDOWN; - } - } - - if (thing->eflags & MFE_VERTICALFLIP) - { - if (thingtop - tmceilingz > maxstep) - { - if (tmfloorthing) - tmhitthing = tmfloorthing; - return false; // too big a step up - } - } - else if (tmfloorz - thing->z > maxstep) - { - if (tmfloorthing) - tmhitthing = tmfloorthing; - return false; // too big a step up } if (!allowdropoff && !(thing->flags & MF_FLOAT) && thing->type != MT_SKIM && !tmfloorthing) @@ -3351,7 +3344,7 @@ static void PTR_GlideClimbTraverse(line_t *li) { for (rover = checksector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (slidemo->player->charflags & SF_CANBUSTWALLS))) continue; topheight = P_GetFFloorTopZAt (rover, slidemo->x, slidemo->y); @@ -4963,7 +4956,7 @@ void P_MapEnd(void) } // P_FloorzAtPos -// Returns the floorz of the XYZ position // TODO: Need ceilingpos function too +// Returns the floorz of the XYZ position // Tails 05-26-2003 fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) { @@ -5007,3 +5000,47 @@ fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) return floorz; } + +// P_CeilingZAtPos +// Returns the ceilingz of the XYZ position +fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) +{ + sector_t *sec = R_PointInSubsector(x, y)->sector; + fixed_t ceilingz = P_GetSectorCeilingZAt(sec, x, y); + + if (sec->ffloors) + { + ffloor_t *rover; + fixed_t delta1, delta2, thingtop = z + height; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + fixed_t topheight, bottomheight; + if (!(rover->flags & FF_EXISTS)) + continue; + + if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE))) + continue; + + topheight = P_GetFFloorTopZAt (rover, x, y); + bottomheight = P_GetFFloorBottomZAt(rover, x, y); + + if (rover->flags & FF_QUICKSAND) + { + if (thingtop > bottomheight && topheight > z) + { + if (ceilingz > z) + ceilingz = z; + } + continue; + } + + delta1 = z - (bottomheight + ((topheight - bottomheight)/2)); + delta2 = thingtop - (bottomheight + ((topheight - bottomheight)/2)); + if (bottomheight < ceilingz && abs(delta1) > abs(delta2)) + ceilingz = bottomheight; + } + } + + return ceilingz; +} diff --git a/src/p_maputl.c b/src/p_maputl.c index c6e064d18..90718a41c 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -18,6 +18,7 @@ #include "p_local.h" #include "r_main.h" #include "r_data.h" +#include "r_textures.h" #include "p_maputl.h" #include "p_polyobj.h" #include "p_slopes.h" diff --git a/src/p_mobj.c b/src/p_mobj.c index 7c526b417..61a376729 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -36,10 +36,10 @@ #include "m_cond.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; -consvar_t cv_movebob = {"movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); #ifdef WALLSPLATS -consvar_t cv_splats = {"splats", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_splats = CVAR_INIT ("splats", "On", CV_SAVE, CV_OnOff, NULL); #endif actioncache_t actioncachehead; @@ -4198,7 +4198,7 @@ boolean P_SupermanLook4Players(mobj_t *actor) for (c = 0; c < MAXPLAYERS; c++) { - if (playeringame[c]) + if (playeringame[c] && !players[c].spectator) { if (players[c].pflags & PF_INVIS) continue; // ignore notarget @@ -11049,10 +11049,10 @@ void P_RemoveSavegameMobj(mobj_t *mobj) } static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_itemrespawntime = {"respawnitemtime", "30", CV_NETVAR|CV_CHEAT, respawnitemtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_itemrespawn = {"respawnitem", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_itemrespawntime = CVAR_INIT ("respawnitemtime", "30", CV_SAVE|CV_NETVAR|CV_CHEAT, respawnitemtime_cons_t, NULL); +consvar_t cv_itemrespawn = CVAR_INIT ("respawnitem", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); static CV_PossibleValue_t flagtime_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_flagtime = {"flagtime", "30", CV_NETVAR|CV_CHEAT, flagtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_flagtime = CVAR_INIT ("flagtime", "30", CV_SAVE|CV_NETVAR|CV_CHEAT, flagtime_cons_t, NULL); void P_SpawnPrecipitation(void) { @@ -11251,7 +11251,7 @@ void P_PrecipitationEffects(void) * \param mthingtype Mapthing number in question. * \return Mobj type; MT_UNKNOWN if nothing found. */ -static mobjtype_t P_GetMobjtype(UINT16 mthingtype) +mobjtype_t P_GetMobjtype(UINT16 mthingtype) { mobjtype_t i; for (i = 0; i < NUMMOBJTYPES; i++) @@ -11958,9 +11958,6 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i) return MT_SCORE1K_BOX; // 1,000 } - if (mariomode && i == MT_ROSY) - return MT_TOAD; // don't remove on penalty of death - return i; } diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 132fd6760..874edbd50 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -977,7 +977,7 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line) // Moves a polyobject on the x-y plane. -static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y, boolean checkmobjs) +boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y, boolean checkmobjs) { size_t i; vertex_t vec; @@ -1163,7 +1163,7 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta, } // Rotates a polyobject around its start point. -static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings, boolean checkmobjs) +boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings, boolean checkmobjs) { size_t i; angle_t angle; diff --git a/src/p_polyobj.h b/src/p_polyobj.h index f24caca4e..8c2946965 100644 --- a/src/p_polyobj.h +++ b/src/p_polyobj.h @@ -336,6 +336,8 @@ typedef struct polyfadedata_s // Functions // +boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y, boolean checkmobjs); +boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings, boolean checkmobjs); polyobj_t *Polyobj_GetForNum(INT32 id); void Polyobj_InitLevel(void); void Polyobj_MoveOnLoad(polyobj_t *po, angle_t angle, fixed_t x, fixed_t y); diff --git a/src/p_saveg.c b/src/p_saveg.c index e06a7b40b..8e9bcf110 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -22,6 +22,8 @@ #include "p_setup.h" #include "p_saveg.h" #include "r_data.h" +#include "r_textures.h" +#include "r_things.h" #include "r_skins.h" #include "r_state.h" #include "w_wad.h" diff --git a/src/p_setup.c b/src/p_setup.c index 3ab381ecc..80510c9c3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -28,7 +28,8 @@ #include "r_data.h" #include "r_things.h" // for R_AddSpriteDefs -#include "r_patch.h" +#include "r_textures.h" +#include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" @@ -549,6 +550,8 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize) lumpnum_t flatnum; int texturenum; + patch_t *flatpatch; + size_t lumplength; size_t i; @@ -605,7 +608,9 @@ texturefound: { flatfound: /* This could be a flat, patch, or PNG. */ - if (R_CheckIfPatch(flatnum)) + flatpatch = W_CacheLumpNum(flatnum, PU_CACHE); + lumplength = W_LumpLength(flatnum); + if (Picture_CheckIfPatch(flatpatch, lumplength)) levelflat->type = LEVELFLAT_PATCH; else { @@ -615,12 +620,14 @@ flatfound: FIXME: Put this elsewhere. */ W_ReadLumpHeader(flatnum, buffer, 8, 0); - if (R_IsLumpPNG(buffer, W_LumpLength(flatnum))) + if (Picture_IsLumpPNG(buffer, lumplength)) levelflat->type = LEVELFLAT_PNG; else #endif/*NO_PNG_LUMPS*/ levelflat->type = LEVELFLAT_FLAT;/* phew */ } + if (flatpatch) + Z_Free(flatpatch); levelflat->u.flat. lumpnum = flatnum; levelflat->u.flat.baselumpnum = LUMPERROR; @@ -4048,23 +4055,23 @@ boolean P_LoadLevel(boolean fromnetsave) wipegamestate = FORCEWIPEOFF; wipestyleflags = 0; - // Special stage fade to white + // Special stage & record attack retry fade to white // This is handled BEFORE sounds are stopped. - if (modeattacking && !demoplayback && (pausedelay == INT32_MIN)) - ranspecialwipe = 2; + if (G_GetModeAttackRetryFlag()) + { + if (modeattacking && !demoplayback) + { + ranspecialwipe = 2; + wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); + } + G_ClearModeAttackRetryFlag(); + } else if (rendermode != render_none && G_IsSpecialStage(gamemap)) { P_RunSpecialStageWipe(); ranspecialwipe = 1; } - if (G_GetModeAttackRetryFlag()) - { - if (modeattacking) - wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); - G_ClearModeAttackRetryFlag(); - } - // Make sure all sounds are stopped before Z_FreeTags. S_StopSounds(); S_ClearSfx(); @@ -4224,9 +4231,9 @@ boolean P_LoadLevel(boolean fromnetsave) nextmapoverride = 0; skipstats = 0; - if (!(netgame || multiplayer) && (!modifiedgame || savemoddata)) + if (!(netgame || multiplayer || demoplayback) && (!modifiedgame || savemoddata)) mapvisited[gamemap-1] |= MV_VISITED; - else + else if (netgame || multiplayer) mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently levelloading = false; diff --git a/src/p_setup.h b/src/p_setup.h index e7150c0ae..ef903e103 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -37,9 +37,7 @@ enum LEVELFLAT_NONE,/* HOM time my friend */ LEVELFLAT_FLAT, LEVELFLAT_PATCH, -#ifndef NO_PNG_LUMPS LEVELFLAT_PNG, -#endif LEVELFLAT_TEXTURE, }; @@ -72,15 +70,17 @@ typedef struct u; UINT16 width, height; - fixed_t topoffset, leftoffset; // for flat animation INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; - // for patchflats - UINT8 *flatpatch; + // for textures + UINT8 *picture; +#ifdef HWRENDER + void *mipmap; +#endif } levelflat_t; extern size_t numlevelflats; diff --git a/src/p_spec.c b/src/p_spec.c index 647f5d643..39d7e9223 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_textures.h" #include "m_random.h" #include "p_mobj.h" #include "i_system.h" @@ -1786,7 +1787,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller case 336: // object dye - once { INT32 triggercolor = (INT32)sides[triggerline->sidenum[0]].toptexture; - UINT8 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); + UINT16 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); boolean invert = (triggerline->flags & ML_NOCLIMB ? true : false); if (invert ^ (triggercolor != color)) @@ -4250,7 +4251,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers // Ignore dead players. // If this strange phenomenon could be potentially used in levels, // TODO: modify this to accommodate for it. - if (player->playerstate == PST_DEAD) + if (player->playerstate != PST_LIVE) return; // Conveyor stuff diff --git a/src/p_tick.c b/src/p_tick.c index f84ae96c0..451e5e626 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -21,6 +21,8 @@ #include "m_random.h" #include "lua_script.h" #include "lua_hook.h" +#include "m_perfstats.h" +#include "i_system.h" // I_GetTimeMicros // Object place #include "m_cheat.h" @@ -321,6 +323,7 @@ static inline void P_RunThinkers(void) size_t i; for (i = 0; i < NUM_THINKERLISTS; i++) { + ps_thlist_times[i] = I_GetTimeMicros(); for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next) { #ifdef PARANOIA @@ -328,6 +331,7 @@ static inline void P_RunThinkers(void) #endif currentthinker->function.acp1(currentthinker); } + ps_thlist_times[i] = I_GetTimeMicros() - ps_thlist_times[i]; } } @@ -641,11 +645,16 @@ void P_Ticker(boolean run) if (demoplayback) G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0); + ps_lua_mobjhooks = 0; + ps_checkposition_calls = 0; + LUAh_PreThinkFrame(); + ps_playerthink_time = I_GetTimeMicros(); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerThink(&players[i]); + ps_playerthink_time = I_GetTimeMicros() - ps_playerthink_time; } // Keep track of how long they've been playing! @@ -660,14 +669,18 @@ void P_Ticker(boolean run) if (run) { + ps_thinkertime = I_GetTimeMicros(); P_RunThinkers(); + ps_thinkertime = I_GetTimeMicros() - ps_thinkertime; // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); + ps_lua_thinkframe_time = I_GetTimeMicros(); LUAh_ThinkFrame(); + ps_lua_thinkframe_time = I_GetTimeMicros() - ps_lua_thinkframe_time; } // Run shield positioning diff --git a/src/p_user.c b/src/p_user.c index 651eb3e86..c8516e519 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2564,7 +2564,7 @@ static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover) } // Strong abilities can break even FF_STRONGBUST. - if (player->charability == CA_GLIDEANDCLIMB) + if (player->charflags & SF_CANBUSTWALLS) return true; if (player->pflags & PF_BOUNCING) @@ -2611,10 +2611,10 @@ static void P_CheckBustableBlocks(player_t *player) if ((netgame || multiplayer) && player->spectator) return; - + oldx = player->mo->x; oldy = player->mo->y; - + if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways { P_UnsetThingPosition(player->mo); @@ -2633,7 +2633,7 @@ static void P_CheckBustableBlocks(player_t *player) if (!node->m_sector->ffloors) continue; - + for (rover = node->m_sector->ffloors; rover; rover = rover->next) { if (!P_PlayerCanBust(player, rover)) @@ -3214,7 +3214,7 @@ static void P_DoClimbing(player_t *player) for (rover = glidesector->sector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS))) continue; floorclimb = true; @@ -3255,7 +3255,7 @@ static void P_DoClimbing(player_t *player) // Is there a FOF directly below this one that we can move onto? for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next) { - if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || (roverbelow->flags & FF_BUSTUP)) + if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS))) continue; if (roverbelow == rover) @@ -3300,7 +3300,7 @@ static void P_DoClimbing(player_t *player) // Is there a FOF directly below this one that we can move onto? for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next) { - if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || (roverbelow->flags & FF_BUSTUP)) + if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS))) continue; if (roverbelow == rover) @@ -3357,7 +3357,7 @@ static void P_DoClimbing(player_t *player) ffloor_t *rover; for (rover = glidesector->sector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS))) continue; bottomheight = P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y); @@ -3397,7 +3397,7 @@ static void P_DoClimbing(player_t *player) ffloor_t *rover; for (rover = glidesector->sector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (player->charflags & SF_CANBUSTWALLS))) continue; topheight = P_GetFFloorTopZAt(rover, player->mo->x, player->mo->y); @@ -5006,6 +5006,118 @@ static void P_DoTwinSpin(player_t *player) P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN); } +// +// returns true if the player used a shield ability, false otherwise +// passing in the mobjs from P_DoJumpStuff is a bit hackily specific, but I don't care enough to make a more elaborate solution (I think that is more appropriately approached with a more general MT_LOCKON spawning system) +// +static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lockonthok, mobj_t *visual) +{ + mobj_t *lockonshield = NULL; + + if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) + && ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted + { + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) + { + if ((lockonshield = P_LookForEnemies(player, false, false))) + { + if (P_IsLocalPlayer(player)) // Only display it on your own view. + { + boolean dovis = true; + if (lockonshield == lockonthok) + { + if (leveltime & 2) + dovis = false; + else if (visual) + P_RemoveMobj(visual); + } + if (dovis) + { + visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker + P_SetTarget(&visual->target, lockonshield); + P_SetMobjStateNF(visual, visual->info->spawnstate+1); + } + } + } + } + if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects + { + // Force stop + if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) + { + player->pflags |= PF_THOKKED|PF_SHIELDABILITY; + player->mo->momx = player->mo->momy = player->mo->momz = 0; + S_StartSound(player->mo, sfx_ngskid); + } + else + { + switch (player->powers[pw_shield] & SH_NOSTACK) + { + // Whirlwind jump/Thunder jump + case SH_WHIRLWIND: + case SH_THUNDERCOIN: + P_DoJumpShield(player); + break; + // Armageddon pow + case SH_ARMAGEDDON: + player->pflags |= PF_THOKKED|PF_SHIELDABILITY; + P_BlackOw(player); + break; + // Attraction blast + case SH_ATTRACT: + player->pflags |= PF_THOKKED|PF_SHIELDABILITY; + player->homing = 2; + P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonshield)); + if (lockonshield) + { + player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonshield->x, lockonshield->y); + player->pflags &= ~PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + S_StartSound(player->mo, sfx_s3k40); + player->homing = 3*TICRATE; + } + else + S_StartSound(player->mo, sfx_s3ka6); + break; + // Elemental stomp/Bubble bounce + case SH_ELEMENTAL: + case SH_BUBBLEWRAP: + { + boolean elem = ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL); + player->pflags |= PF_THOKKED|PF_SHIELDABILITY; + if (elem) + { + player->mo->momx = player->mo->momy = 0; + S_StartSound(player->mo, sfx_s3k43); + } + else + { + player->mo->momx -= (player->mo->momx/3); + player->mo->momy -= (player->mo->momy/3); + player->pflags &= ~PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + S_StartSound(player->mo, sfx_s3k44); + } + player->secondjump = 0; + P_SetObjectMomZ(player->mo, -24*FRACUNIT, false); + break; + } + // Flame burst + case SH_FLAMEAURA: + player->pflags |= PF_THOKKED|PF_SHIELDABILITY; + P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); + player->drawangle = player->mo->angle; + S_StartSound(player->mo, sfx_s3k43); + default: + break; + } + } + } + return player->pflags & PF_SHIELDABILITY; + } + return false; +} + // // P_DoJumpStuff // @@ -5013,7 +5125,7 @@ static void P_DoTwinSpin(player_t *player) // static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { - mobj_t *lockonthok = NULL, *lockonshield = NULL, *visual = NULL; + mobj_t *lockonthok = NULL, *visual = NULL; if (player->pflags & PF_JUMPSTASIS) return; @@ -5040,106 +5152,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) ; else if (player->pflags & (PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player has used an ability previously ; - else if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) - && ((!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX)))) // thokked is optional if you're bubblewrapped - { - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) - { - if ((lockonshield = P_LookForEnemies(player, false, false))) - { - if (P_IsLocalPlayer(player)) // Only display it on your own view. - { - boolean dovis = true; - if (lockonshield == lockonthok) - { - if (leveltime & 2) - dovis = false; - else if (visual) - P_RemoveMobj(visual); - } - if (dovis) - { - visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockonshield); - P_SetMobjStateNF(visual, visual->info->spawnstate+1); - } - } - } - } - if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects - { - // Force stop - if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) - { - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - player->mo->momx = player->mo->momy = player->mo->momz = 0; - S_StartSound(player->mo, sfx_ngskid); - } - else - { - switch (player->powers[pw_shield] & SH_NOSTACK) - { - // Whirlwind jump/Thunder jump - case SH_WHIRLWIND: - case SH_THUNDERCOIN: - P_DoJumpShield(player); - break; - // Armageddon pow - case SH_ARMAGEDDON: - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - P_BlackOw(player); - break; - // Attraction blast - case SH_ATTRACT: - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - player->homing = 2; - P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonshield)); - if (lockonshield) - { - player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonshield->x, lockonshield->y); - player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_s3k40); - player->homing = 3*TICRATE; - } - else - S_StartSound(player->mo, sfx_s3ka6); - break; - // Elemental stomp/Bubble bounce - case SH_ELEMENTAL: - case SH_BUBBLEWRAP: - { - boolean elem = ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL); - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - if (elem) - { - player->mo->momx = player->mo->momy = 0; - S_StartSound(player->mo, sfx_s3k43); - } - else - { - player->mo->momx -= (player->mo->momx/3); - player->mo->momy -= (player->mo->momy/3); - player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_s3k44); - } - player->secondjump = 0; - P_SetObjectMomZ(player->mo, -24*FRACUNIT, false); - break; - } - // Flame burst - case SH_FLAMEAURA: - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); - player->drawangle = player->mo->angle; - S_StartSound(player->mo, sfx_s3k43); - default: - break; - } - } - } - } + else if (P_PlayerShieldThink(player, cmd, lockonthok, visual)) + ; else if ((cmd->buttons & BT_SPIN)) { if (!(player->pflags & PF_SPINDOWN) && P_SuperReady(player)) @@ -5484,6 +5498,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { if (!P_HomingAttack(player->mo, player->mo->tracer)) { + player->pflags &= ~PF_SHIELDABILITY; + player->secondjump = UINT8_MAX; P_SetObjectMomZ(player->mo, 6*FRACUNIT, false); if (player->mo->eflags & MFE_UNDERWATER) player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3); @@ -9631,45 +9647,45 @@ static CV_PossibleValue_t rotation_cons_t[] = {{1, "MIN"}, {25, "MAX"}, {0, NULL static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}}; static CV_PossibleValue_t multiplier_cons_t[] = {{0, "MIN"}, {3*FRACUNIT, "MAX"}, {0, NULL}}; -consvar_t cv_cam_dist = {"cam_curdist", "160", CV_FLOAT, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_height = {"cam_curheight", "25", CV_FLOAT, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_still = {"cam_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_speed = {"cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_rotate = {"cam_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_rotspeed = {"cam_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_turnmultiplier = {"cam_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_adjust = {"cam_adjust", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_dist = {"cam2_curdist", "160", CV_FLOAT, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_height = {"cam2_curheight", "25", CV_FLOAT, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_still = {"cam2_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_speed = {"cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_rotate = {"cam2_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_rotspeed = {"cam2_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_turnmultiplier = {"cam2_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_adjust = {"cam2_adjust", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cam_dist = CVAR_INIT ("cam_curdist", "160", CV_FLOAT, NULL, NULL); +consvar_t cv_cam_height = CVAR_INIT ("cam_curheight", "25", CV_FLOAT, NULL, NULL); +consvar_t cv_cam_still = CVAR_INIT ("cam_still", "Off", 0, CV_OnOff, NULL); +consvar_t cv_cam_speed = CVAR_INIT ("cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL); +consvar_t cv_cam_rotate = CVAR_INIT ("cam_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate_OnChange); +consvar_t cv_cam_rotspeed = CVAR_INIT ("cam_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL); +consvar_t cv_cam_turnmultiplier = CVAR_INIT ("cam_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL); +consvar_t cv_cam_orbit = CVAR_INIT ("cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_cam_adjust = CVAR_INIT ("cam_adjust", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_cam2_dist = CVAR_INIT ("cam2_curdist", "160", CV_FLOAT, NULL, NULL); +consvar_t cv_cam2_height = CVAR_INIT ("cam2_curheight", "25", CV_FLOAT, NULL, NULL); +consvar_t cv_cam2_still = CVAR_INIT ("cam2_still", "Off", 0, CV_OnOff, NULL); +consvar_t cv_cam2_speed = CVAR_INIT ("cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL); +consvar_t cv_cam2_rotate = CVAR_INIT ("cam2_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate2_OnChange); +consvar_t cv_cam2_rotspeed = CVAR_INIT ("cam2_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL); +consvar_t cv_cam2_turnmultiplier = CVAR_INIT ("cam2_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL); +consvar_t cv_cam2_orbit = CVAR_INIT ("cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_cam2_adjust = CVAR_INIT ("cam2_adjust", "On", CV_SAVE, CV_OnOff, NULL); // [standard vs simple][p1 or p2] consvar_t cv_cam_savedist[2][2] = { { // standard - {"cam_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("cam_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist), + CVAR_INIT ("cam2_dist", "160", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist), }, { // simple - {"cam_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("cam_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist), + CVAR_INIT ("cam2_simpledist", "224", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist), } }; consvar_t cv_cam_saveheight[2][2] = { { // standard - {"cam_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("cam_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist), + CVAR_INIT ("cam2_height", "25", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist), }, { // simple - {"cam_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist, 0, NULL, NULL, 0, 0, NULL} + CVAR_INIT ("cam_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCamDist), + CVAR_INIT ("cam2_simpleheight", "48", CV_FLOAT|CV_SAVE|CV_CALL, NULL, CV_UpdateCam2Dist), } }; diff --git a/src/r_bsp.c b/src/r_bsp.c index 2f71c4729..2a3c662aa 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -804,7 +804,7 @@ static void R_AddPolyObjects(subsector_t *sub) } // for render stats - rs_numpolyobjects += numpolys; + ps_numpolyobjects += numpolys; // sort polyobjects R_SortPolyObjects(sub); @@ -1244,7 +1244,7 @@ void R_RenderBSPNode(INT32 bspnum) node_t *bsp; INT32 side; - rs_numbspcalls++; + ps_numbspcalls++; while (!(bspnum & NF_SUBSECTOR)) // Found a subsector? { diff --git a/src/r_data.c b/src/r_data.c index befb73c20..dd36c2ee2 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -19,7 +19,8 @@ #include "p_local.h" #include "m_misc.h" #include "r_data.h" -#include "r_patch.h" +#include "r_textures.h" +#include "r_picformats.h" #include "w_wad.h" #include "z_zone.h" #include "p_setup.h" // levelflats @@ -32,64 +33,6 @@ #include // alloca(sizeof) #endif -#ifdef HWRENDER -#include "hardware/hw_main.h" // HWR_LoadTextures -#endif - -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog -#if 0 -#define AVOID_ERRNO -#else -#include -#endif - -// -// Texture definition. -// Each texture is composed of one or more patches, -// with patches being lumps stored in the WAD. -// The lumps are referenced by number, and patched -// into the rectangular texture space using origin -// and possibly other attributes. -// -typedef struct -{ - INT16 originx, originy; - INT16 patch, stepdir, colormap; -} ATTRPACK mappatch_t; - -// -// Texture definition. -// An SRB2 wall texture is a list of patches -// which are to be combined in a predefined order. -// -typedef struct -{ - char name[8]; - INT32 masked; - INT16 width; - INT16 height; - INT32 columndirectory; // FIXTHIS: OBSOLETE - INT16 patchcount; - mappatch_t patches[1]; -} ATTRPACK maptexture_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - - -// Store lists of lumps for F_START/F_END etc. -typedef struct -{ - UINT16 wadfile; - UINT16 firstlump; - size_t numlumps; -} lumplist_t; - // // Graphics. // SRB2 graphics for walls and sprites @@ -100,20 +43,6 @@ typedef struct size_t numspritelumps, max_spritelumps; -// textures -INT32 numtextures = 0; // total number of textures found, -// size of following tables - -texture_t **textures = NULL; -textureflat_t *texflats = NULL; -static UINT32 **texturecolumnofs; // column offset lookup table for each texture -static UINT8 **texturecache; // graphics data for each generated full-size texture - -INT32 *texturewidth; -fixed_t *textureheight; // needed for texture pegging - -INT32 *texturetranslation; - // needed for pre rendering sprcache_t *spritecachedinfo; @@ -127,106 +56,6 @@ size_t flatmemory, spritememory, texturememory; INT16 color8to16[256]; // remap color index to highcolor rgb value INT16 *hicolormaps; // test a 32k colormap remaps high -> high -// Painfully simple texture id cacheing to make maps load faster. :3 -static struct { - char name[9]; - INT32 id; -} *tidcache = NULL; -static INT32 tidcachelen = 0; - -// -// MAPTEXTURE_T CACHING -// When a texture is first needed, it counts the number of composite columns -// required in the texture and allocates space for a column directory and -// any new columns. -// The directory will simply point inside other patches if there is only one -// patch in a given column, but any columns with multiple patches will have -// new column_ts generated. -// - -// -// R_DrawColumnInCache -// Clip and draw a column from a patch into a cached post. -// -static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)patch + 3; - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source -= position; // start further down the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - if (count > 0) - M_Memcpy(cache + position, source, count); - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_DrawFlippedColumnInCache -// Similar to R_DrawColumnInCache; it draws the column inverted, however. -// -static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source) - *dest++ = *source; - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - // Blends two pixels together, using the equation // that matches the specified alpha style. UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) @@ -376,1183 +205,6 @@ UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT return background; } -// -// R_DrawBlendColumnInCache -// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). -// -static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)patch + 3; - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source -= position; // start further down the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; source++, dest++) - if (*source != 0xFF) - *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_DrawBlendFlippedColumnInCache -// Similar to the one above except that the column is inverted. -// -static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source, dest++) - if (*source != 0xFF) - *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_GenerateTexture -// -// Allocate space for full size texture, either single patch or 'composite' -// Build the full textures from patches. -// The texture caching system is a little more hungry of memory, but has -// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. -// -// This is not optimised, but it's supposed to be executed only once -// per level, when enough memory is available. -// -static UINT8 *R_GenerateTexture(size_t texnum) -{ - UINT8 *block; - UINT8 *blocktex; - texture_t *texture; - texpatch_t *patch; - patch_t *realpatch; - UINT8 *pdata; - int x, x1, x2, i, width, height; - size_t blocksize; - column_t *patchcol; - UINT8 *colofs; - - UINT16 wadnum; - lumpnum_t lumpnum; - size_t lumplength; - - I_Assert(texnum <= (size_t)numtextures); - texture = textures[texnum]; - I_Assert(texture != NULL); - - // allocate texture column offset lookup - - // single-patch textures can have holes in them and may be used on - // 2sided lines so they need to be kept in 'packed' format - // BUT this is wrong for skies and walls with over 255 pixels, - // so check if there's holes and if not strip the posts. - if (texture->patchcount == 1) - { - boolean holey = false; - patch = texture->patches; - - wadnum = patch->wad; - lumpnum = patch->lump; - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - realpatch = (patch_t *)pdata; - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - goto multipatch; -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - goto multipatch; -#endif - - // Check the patch for holes. - if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) - holey = true; - colofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width && !holey; x++) - { - column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); - INT32 topdelta, prevdelta = -1, y = 0; - while (col->topdelta != 0xff) - { - topdelta = col->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - if (topdelta > y) - break; - y = topdelta + col->length + 1; - col = (column_t *)((UINT8 *)col + col->length + 4); - } - if (y < texture->height) - holey = true; // this texture is HOLEy! D: - } - - // If the patch uses transparency, we have to save it this way. - if (holey) - { - texture->holes = true; - texture->flip = patch->flip; - blocksize = lumplength; - block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function - &texturecache[texnum]); - M_Memcpy(block, realpatch, blocksize); - texturememory += blocksize; - - // use the patch's column lookup - colofs = (block + 8); - texturecolumnofs[texnum] = (UINT32 *)colofs; - blocktex = block; - if (patch->flip & 1) // flip the patch horizontally - { - UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture - } - // we can't as easily flip the patch vertically sadly though, - // we have wait until the texture itself is drawn to do that - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); - goto done; - } - - // Otherwise, do multipatch format. - } - - // multi-patch textures (or 'composite') - multipatch: - texture->holes = false; - texture->flip = 0; - blocksize = (texture->width * 4) + (texture->width * texture->height); - texturememory += blocksize; - block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); - - memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack - - // columns lookup table - colofs = block; - texturecolumnofs[texnum] = (UINT32 *)colofs; - - // texture data after the lookup table - blocktex = block + (texture->width*4); - - // Composite the columns together. - for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) - { - boolean dealloc = true; - static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. - if (patch->style != AST_COPY) - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; - else - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; - - wadnum = patch->wad; - lumpnum = patch->lump; - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - realpatch = (patch_t *)pdata; - dealloc = true; - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL); - else -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); - else -#endif - { - (void)lumplength; - dealloc = false; - } - - x1 = patch->originx; - width = SHORT(realpatch->width); - height = SHORT(realpatch->height); - x2 = x1 + width; - - if (x1 > texture->width || x2 < 0) - continue; // patch not located within texture's x bounds, ignore - - if (patch->originy > texture->height || (patch->originy + height) < 0) - continue; // patch not located within texture's y bounds, ignore - - // patch is actually inside the texture! - // now check if texture is partly off-screen and adjust accordingly - - // left edge - if (x1 < 0) - x = 0; - else - x = x1; - - // right edge - if (x2 > texture->width) - x2 = texture->width; - - for (; x < x2; x++) - { - if (patch->flip & 1) - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); - else - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); - - // generate column ofset lookup - *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); - ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); - } - - if (dealloc) - Z_Free(realpatch); - } - -done: - // Now that the texture has been built in column cache, it is purgable from zone memory. - Z_ChangeTag(block, PU_CACHE); - return blocktex; -} - -// -// R_GetTextureNum -// -// Returns the actual texture id that we should use. -// This can either be texnum, the current frame for texnum's anim (if animated), -// or 0 if not valid. -// -INT32 R_GetTextureNum(INT32 texnum) -{ - if (texnum < 0 || texnum >= numtextures) - return 0; - return texturetranslation[texnum]; -} - -// -// R_CheckTextureCache -// -// Use this if you need to make sure the texture is cached before R_GetColumn calls -// e.g.: midtextures and FOF walls -// -void R_CheckTextureCache(INT32 tex) -{ - if (!texturecache[tex]) - R_GenerateTexture(tex); -} - -// -// R_GetColumn -// -UINT8 *R_GetColumn(fixed_t tex, INT32 col) -{ - UINT8 *data; - INT32 width = texturewidth[tex]; - - if (width & (width - 1)) - col = (UINT32)col % width; - else - col &= (width - 1); - - data = texturecache[tex]; - if (!data) - data = R_GenerateTexture(tex); - - return data + LONG(texturecolumnofs[tex][col]); -} - -// convert flats to hicolor as they are requested -// -UINT8 *R_GetFlat(lumpnum_t flatlumpnum) -{ - return W_CacheLumpNum(flatlumpnum, PU_CACHE); -} - -// -// Empty the texture cache (used for load wad at runtime) -// -void R_FlushTextureCache(void) -{ - INT32 i; - - if (numtextures) - for (i = 0; i < numtextures; i++) - Z_Free(texturecache[i]); -} - -// Need these prototypes for later; defining them here instead of r_data.h so they're "private" -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); - -#ifdef WALLFLATS -static INT32 -Rloadflats (INT32 i, INT32 w) -{ - UINT16 j; - UINT16 texstart, texend; - texture_t *texture; - texpatch_t *patch; - - // Yes - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT8 *flatlump; - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; - size_t lumplength; - size_t flatsize = 0; - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - - switch (lumplength) - { - case 4194304: // 2048x2048 lump - flatsize = 2048; - break; - case 1048576: // 1024x1024 lump - flatsize = 1024; - break; - case 262144:// 512x512 lump - flatsize = 512; - break; - case 65536: // 256x256 lump - flatsize = 256; - break; - case 16384: // 128x128 lump - flatsize = 128; - break; - case 1024: // 32x32 lump - flatsize = 32; - break; - default: // 64x64 lump - flatsize = 64; - break; - } - - //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)flatlump, lumplength)) - { - INT16 width, height; - R_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - texture->width = texture->height = flatsize; - - texture->type = TEXTURETYPE_FLAT; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(flatlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - } - - return i; -} -#endif/*WALLFLATS*/ - -#define TX_START "TX_START" -#define TX_END "TX_END" - -static INT32 -Rloadtextures (INT32 i, INT32 w) -{ - UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - texture_t *texture; - patch_t *patchlump; - texpatch_t *patch; - - // Get the lump numbers for the markers in the WAD, if they exist. - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - } - else - { - texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - if (texturesLumpPos != INT16_MAX) - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; -#ifndef NO_PNG_LUMPS - size_t lumplength; -#endif - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); -#ifndef NO_PNG_LUMPS - lumplength = W_LumpLengthPwad(wadnum, lumpnum); -#endif - - //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) - { - INT16 width, height; - R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); - } - - texture->type = TEXTURETYPE_SINGLEPATCH; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(patchlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - } - - return i; -} - -// -// R_LoadTextures -// Initializes the texture list with the textures from the world map. -// -void R_LoadTextures(void) -{ - INT32 i, w; - UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - - // Free previous memory before numtextures change. - if (numtextures) - { - for (i = 0; i < numtextures; i++) - { - Z_Free(textures[i]); - Z_Free(texturecache[i]); - } - Z_Free(texturetranslation); - Z_Free(textures); - Z_Free(texflats); - } - - // Load patches and textures. - - // Get the number of textures to check. - // NOTE: Make SURE the system does not process - // the markers. - // This system will allocate memory for all duplicate/patched textures even if it never uses them, - // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. - for (w = 0, numtextures = 0; w < numwadfiles; w++) - { -#ifdef WALLFLATS - // Count flats - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between F_START and F_END - { - numtextures += (UINT32)(texend - texstart); - } - } -#endif/*WALLFLATS*/ - - // Count the textures from TEXTURES lumps - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - - // Count single-patch textures - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between TX_START and TX_END - { - numtextures += (UINT32)(texend - texstart); - } - } - - // If no textures found by this point, bomb out - if (!numtextures) - I_Error("No textures detected in any WADs!\n"); - - // Allocate memory and initialize to 0 for all the textures we are initialising. - // There are actually 5 buffers allocated in one for convenience. - textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); - texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); - - // Allocate texture column offset table. - texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); - // Allocate texture referencing cache. - texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width table. - texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height table. - textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); - // Create translation table for global animation. - texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); - - for (i = 0; i < numtextures; i++) - texturetranslation[i] = i; - - for (i = 0, w = 0; w < numwadfiles; w++) - { -#ifdef WALLFLATS - i = Rloadflats(i, w); -#endif - i = Rloadtextures(i, w); - } - -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_LoadTextures(numtextures); -#endif -} - -static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) -{ - char *texturesToken; - size_t texturesTokenLength; - char *endPos; - char *patchName = NULL; - INT16 patchXPos; - INT16 patchYPos; - UINT8 flip = 0; - UINT8 alpha = 255; - enum patchalphastyle style = AST_COPY; - texpatch_t *resultPatch = NULL; - lumpnum_t patchLumpNum; - - // Patch identifier - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch name should be"); - } - texturesTokenLength = strlen(texturesToken); - if (texturesTokenLength>8) - { - I_Error("Error parsing TEXTURES lump: Patch name \"%s\" exceeds 8 characters",texturesToken); - } - else - { - if (patchName != NULL) - { - Z_Free(patchName); - } - patchName = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL); - M_Memcpy(patchName,texturesToken,texturesTokenLength*sizeof(char)); - patchName[texturesTokenLength] = '\0'; - } - - // Comma 1 - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after \"%s\"'s patch name should be",patchName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after %s's patch name, got \"%s\"",patchName,texturesToken); - } - - // XPos - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s x coordinate should be",patchName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - patchXPos = strtol(texturesToken,&endPos,10); - (void)patchXPos; //unused for now - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - ) - { - I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); - } - - // Comma 2 - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after patch \"%s\"'s x coordinate should be",patchName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); - } - - // YPos - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s y coordinate should be",patchName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - patchYPos = strtol(texturesToken,&endPos,10); - (void)patchYPos; //unused for now - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - ) - { - I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s y coordinate, got \"%s\"",patchName,texturesToken); - } - Z_Free(texturesToken); - - // Patch parameters block (OPTIONAL) - // added by Monster Iestyn (22/10/16) - - // Left Curly Brace - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - ; // move on and ignore, R_ParseTextures will deal with this - else - { - if (strcmp(texturesToken,"{")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); - } - while (strcmp(texturesToken,"}")!=0) - { - if (stricmp(texturesToken, "ALPHA")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - alpha = 255*strtof(texturesToken, NULL); - } - else if (stricmp(texturesToken, "STYLE")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (stricmp(texturesToken, "TRANSLUCENT")==0) - style = AST_TRANSLUCENT; - else if (stricmp(texturesToken, "ADD")==0) - style = AST_ADD; - else if (stricmp(texturesToken, "SUBTRACT")==0) - style = AST_SUBTRACT; - else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) - style = AST_REVERSESUBTRACT; - else if (stricmp(texturesToken, "MODULATE")==0) - style = AST_MODULATE; - } - else if (stricmp(texturesToken, "FLIPX")==0) - flip |= 1; - else if (stricmp(texturesToken, "FLIPY")==0) - flip |= 2; - Z_Free(texturesToken); - - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); - } - } - } - else - { - // this is not what we wanted... - // undo last read so R_ParseTextures can re-get the token for its own purposes - M_UnGetToken(); - } - Z_Free(texturesToken); - } - - if (actuallyLoadPatch == true) - { - // Check lump exists - patchLumpNum = W_GetNumForName(patchName); - // If so, allocate memory for texpatch_t and fill 'er up - resultPatch = (texpatch_t *)Z_Malloc(sizeof(texpatch_t),PU_STATIC,NULL); - resultPatch->originx = patchXPos; - resultPatch->originy = patchYPos; - resultPatch->lump = patchLumpNum & 65535; - resultPatch->wad = patchLumpNum>>16; - resultPatch->flip = flip; - resultPatch->alpha = alpha; - resultPatch->style = style; - // Clean up a little after ourselves - Z_Free(patchName); - // Then return it - return resultPatch; - } - else - { - Z_Free(patchName); - return NULL; - } -} - -static texture_t *R_ParseTexture(boolean actuallyLoadTexture) -{ - char *texturesToken; - size_t texturesTokenLength; - char *endPos; - INT32 newTextureWidth; - INT32 newTextureHeight; - texture_t *resultTexture = NULL; - texpatch_t *newPatch; - char newTextureName[9]; // no longer dynamically allocated - - // Texture name - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture name should be"); - } - texturesTokenLength = strlen(texturesToken); - if (texturesTokenLength>8) - { - I_Error("Error parsing TEXTURES lump: Texture name \"%s\" exceeds 8 characters",texturesToken); - } - else - { - memset(&newTextureName, 0, 9); - M_Memcpy(newTextureName, texturesToken, texturesTokenLength); - // ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer - strupr(newTextureName); // Just do this now so we don't have to worry about it - } - Z_Free(texturesToken); - - // Comma 1 - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s name should be",newTextureName); - } - else if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s name, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Width - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s width should be",newTextureName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - newTextureWidth = strtol(texturesToken,&endPos,10); - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || newTextureWidth < 0) // Number is not positive - { - I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Comma 2 - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s width should be",newTextureName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Height - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s height should be",newTextureName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - newTextureHeight = strtol(texturesToken,&endPos,10); - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || newTextureHeight < 0) // Number is not positive - { - I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s height, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Left Curly Brace - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \"%s\" should be",newTextureName); - } - if (strcmp(texturesToken,"{")==0) - { - if (actuallyLoadTexture) - { - // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. - resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL); - M_Memcpy(resultTexture->name, newTextureName, 8); - resultTexture->width = newTextureWidth; - resultTexture->height = newTextureHeight; - resultTexture->type = TEXTURETYPE_COMPOSITE; - } - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \"%s\" should be",newTextureName); - } - while (strcmp(texturesToken,"}")!=0) - { - if (stricmp(texturesToken, "PATCH")==0) - { - Z_Free(texturesToken); - if (resultTexture) - { - // Get that new patch - newPatch = R_ParsePatch(true); - // Make room for the new patch - resultTexture = Z_Realloc(resultTexture, sizeof(texture_t) + (resultTexture->patchcount+1)*sizeof(texpatch_t), PU_STATIC, NULL); - // Populate the uninitialized values in the new patch entry of our array - M_Memcpy(&resultTexture->patches[resultTexture->patchcount], newPatch, sizeof(texpatch_t)); - // Account for the new number of patches in the texture - resultTexture->patchcount++; - // Then free up the memory assigned to R_ParsePatch, as it's unneeded now - Z_Free(newPatch); - } - else - { - R_ParsePatch(false); - } - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"PATCH\" in texture \"%s\", got \"%s\"",newTextureName,texturesToken); - } - - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \"%s\" should be",newTextureName); - } - } - if (resultTexture && resultTexture->patchcount == 0) - { - I_Error("Error parsing TEXTURES lump: Texture \"%s\" must have at least one patch",newTextureName); - } - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"{\" for texture \"%s\", got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - if (actuallyLoadTexture) return resultTexture; - else return NULL; -} - -// Parses the TEXTURES lump... but just to count the number of textures. -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) -{ - char *texturesLump; - size_t texturesLumpLength; - char *texturesText; - UINT32 numTexturesInLump = 0; - char *texturesToken; - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (texturesLump == NULL) return 0; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(texturesText,texturesLump,texturesLumpLength); - // Make damn well sure the last character in our new memory location is \0. - texturesText[texturesLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(texturesLump); - - texturesToken = M_GetToken(texturesText); - while (texturesToken != NULL) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - numTexturesInLump++; - Z_Free(texturesToken); - R_ParseTexture(false); - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); - - return numTexturesInLump; -} - -// Parses the TEXTURES lump... for real, this time. -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) -{ - char *texturesLump; - size_t texturesLumpLength; - char *texturesText; - char *texturesToken; - texture_t *newTexture; - - I_Assert(texindex != NULL); - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (texturesLump == NULL) return; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(texturesText,texturesLump,texturesLumpLength); - // Make damn well sure the last character in our new memory location is \0. - texturesText[texturesLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(texturesLump); - - texturesToken = M_GetToken(texturesText); - while (texturesToken != NULL) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - Z_Free(texturesToken); - // Get the new texture - newTexture = R_ParseTexture(true); - // Store the new texture - textures[*texindex] = newTexture; - texturewidth[*texindex] = newTexture->width; - textureheight[*texindex] = newTexture->height << FRACBITS; - // Increment i back in R_LoadTextures() - (*texindex)++; - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); -} - #ifdef EXTRACOLORMAPLUMPS static lumplist_t *colormaplumps = NULL; ///\todo free leak static size_t numcolormaplumps = 0; @@ -1607,54 +259,6 @@ static void R_InitExtraColormaps(void) } #endif -// Search for flat name. -lumpnum_t R_GetFlatNumForName(const char *name) -{ - INT32 i; - lumpnum_t lump; - lumpnum_t start; - lumpnum_t end; - - // Scan wad files backwards so patched flats take preference. - for (i = numwadfiles - 1; i >= 0; i--) - { - switch (wadfiles[i]->type) - { - case RET_WAD: - if ((start = W_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX) - { - if ((start = W_CheckNumForMarkerStartPwad("FF_START", (UINT16)i, 0)) == INT16_MAX) - continue; - else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX) - continue; - } - else - if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX) - continue; - break; - case RET_PK3: - if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX) - continue; - if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX) - continue; - break; - default: - continue; - } - - // Now find lump with specified name in that range. - lump = W_CheckNumForNamePwad(name, (UINT16)i, start); - if (lump < end) - { - lump += (i<<16); // found it, in our constraints - break; - } - lump = LUMPERROR; - } - - return lump; -} - // // R_InitSpriteLumps // Finds the width and hoffset of all sprites in the wad, so the sprite does not need to be @@ -2619,74 +1223,6 @@ void R_InitData(void) R_InitColormaps(); } -void R_ClearTextureNumCache(boolean btell) -{ - if (tidcache) - Z_Free(tidcache); - tidcache = NULL; - if (btell) - CONS_Debug(DBG_SETUP, "Fun Fact: There are %d textures used in this map.\n", tidcachelen); - tidcachelen = 0; -} - -// -// R_CheckTextureNumForName -// -// Check whether texture is available. Filter out NoTexture indicator. -// -INT32 R_CheckTextureNumForName(const char *name) -{ - INT32 i; - - // "NoTexture" marker. - if (name[0] == '-') - return 0; - - for (i = 0; i < tidcachelen; i++) - if (!strncasecmp(tidcache[i].name, name, 8)) - return tidcache[i].id; - - // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier - //for (i = 0; i < numtextures; i++) <- old - for (i = (numtextures - 1); i >= 0; i--) // <- new - if (!strncasecmp(textures[i]->name, name, 8)) - { - tidcachelen++; - Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); - strncpy(tidcache[tidcachelen-1].name, name, 8); - tidcache[tidcachelen-1].name[8] = '\0'; -#ifndef ZDEBUG - CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); -#endif - tidcache[tidcachelen-1].id = i; - return i; - } - - return -1; -} - -// -// R_TextureNumForName -// -// Calls R_CheckTextureNumForName, aborts with error message. -// -INT32 R_TextureNumForName(const char *name) -{ - const INT32 i = R_CheckTextureNumForName(name); - - if (i == -1) - { - static INT32 redwall = -2; - CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name); - if (redwall == -2) - redwall = R_CheckTextureNumForName("REDWALL"); - if (redwall != -1) - return redwall; - return 1; - } - return i; -} - // // R_PrecacheLevel // diff --git a/src/r_data.h b/src/r_data.h index fda342083..aec52b54b 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -22,6 +22,14 @@ #pragma interface #endif +// Store lists of lumps for F_START/F_END etc. +typedef struct +{ + UINT16 wadfile; + UINT16 firstlump; + size_t numlumps; +} lumplist_t; + // Possible alpha types for a patch. enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; @@ -31,97 +39,17 @@ UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT extern INT32 ASTTextureBlendingThreshold[2]; -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); - -// moved here for r_sky.c (texpatch_t is used) - -// A single patch from a texture definition, -// basically a rectangular area within -// the texture rectangle. -typedef struct -{ - // Block origin (always UL), which has already accounted for the internal origin of the patch. - INT16 originx, originy; - UINT16 wad, lump; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - UINT8 alpha; // Translucency value - enum patchalphastyle style; -} texpatch_t; - -// texture type -enum -{ - TEXTURETYPE_UNKNOWN, - TEXTURETYPE_SINGLEPATCH, - TEXTURETYPE_COMPOSITE, -#ifdef WALLFLATS - TEXTURETYPE_FLAT, -#endif -}; - -// A maptexturedef_t describes a rectangular texture, -// which is composed of one or more mappatch_t structures -// that arrange graphic patches. -typedef struct -{ - // Keep name for switch changing, etc. - char name[8]; - UINT8 type; // TEXTURETYPE_ - INT16 width, height; - boolean holes; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - - // All the patches[patchcount] are drawn back to front into the cached texture. - INT16 patchcount; - texpatch_t patches[0]; -} texture_t; - -typedef struct -{ - UINT8 *flat; - INT16 width, height; -} textureflat_t; - -// all loaded and prepared textures from the start of the game -extern texture_t **textures; -extern textureflat_t *texflats; - -extern INT32 *texturewidth; -extern fixed_t *textureheight; // needed for texture pegging - extern INT16 color8to16[256]; // remap color index to highcolor extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; -// Load TEXTURES definitions, create lookup tables -void R_LoadTextures(void); -void R_FlushTextureCache(void); - -INT32 R_GetTextureNum(INT32 texnum); -void R_CheckTextureCache(INT32 tex); - -// Retrieve column data for span blitting. -UINT8 *R_GetColumn(fixed_t tex, INT32 col); -UINT8 *R_GetFlat(lumpnum_t flatnum); - // I/O, setting up the stuff. void R_InitData(void); void R_PrecacheLevel(void); extern size_t flatmemory, spritememory, texturememory; -// Retrieval. -// Floor/ceiling opaque texture tiles, -// lookup by name. For animation? -lumpnum_t R_GetFlatNumForName(const char *name); - -// Called by P_Ticker for switches and animations, -// returns the texture number for the texture name. -void R_ClearTextureNumCache(boolean btell); -INT32 R_TextureNumForName(const char *name); -INT32 R_CheckTextureNumForName(const char *name); - // Extra Colormap lumps (C_START/C_END) are not used anywhere // Uncomment to enable //#define EXTRACOLORMAPLUMPS @@ -195,6 +123,4 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette); #define NearestColor(r, g, b) NearestPaletteColor(r, g, b, NULL) -extern INT32 numtextures; - #endif diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c index 020155694..630b36e6f 100644 --- a/src/r_draw8_npo2.c +++ b/src/r_draw8_npo2.c @@ -23,6 +23,8 @@ void R_DrawSpan_NPO2_8 (void) fixed_t xposition; fixed_t yposition; fixed_t xstep, ystep; + fixed_t x, y; + fixed_t fixedwidth, fixedheight; UINT8 *source; UINT8 *colormap; @@ -41,19 +43,39 @@ void R_DrawSpan_NPO2_8 (void) if (dest+8 > deststop) return; + fixedwidth = ds_flatwidth << FRACBITS; + fixedheight = ds_flatheight << FRACBITS; + + // Fix xposition and yposition if they are out of bounds. + if (xposition < 0) + xposition = fixedwidth - ((UINT32)(fixedwidth - xposition) % fixedwidth); + else if (xposition >= fixedwidth) + xposition %= fixedwidth; + if (yposition < 0) + yposition = fixedheight - ((UINT32)(fixedheight - yposition) % fixedheight); + else if (yposition >= fixedheight) + yposition %= fixedheight; + while (count-- && dest <= deststop) { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); + // The loops here keep the texture coordinates within the texture. + // They will rarely iterate multiple times, and are cheaper than a modulo operation, + // even if using libdivide. + if (xstep < 0) // These if statements are hopefully hoisted by the compiler to above this loop + while (xposition < 0) + xposition += fixedwidth; + else + while (xposition >= fixedwidth) + xposition -= fixedwidth; + if (ystep < 0) + while (yposition < 0) + yposition += fixedheight; + else + while (yposition >= fixedheight) + yposition -= fixedheight; - // Carefully align all of my Friends. - if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); - if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + x = (xposition >> FRACBITS); + y = (yposition >> FRACBITS); *dest++ = colormap[source[((y * ds_flatwidth) + x)]]; xposition += xstep; @@ -668,6 +690,8 @@ void R_DrawSplat_NPO2_8 (void) fixed_t xposition; fixed_t yposition; fixed_t xstep, ystep; + fixed_t x, y; + fixed_t fixedwidth, fixedheight; UINT8 *source; UINT8 *colormap; @@ -684,20 +708,39 @@ void R_DrawSplat_NPO2_8 (void) colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + fixedwidth = ds_flatwidth << FRACBITS; + fixedheight = ds_flatheight << FRACBITS; + + // Fix xposition and yposition if they are out of bounds. + if (xposition < 0) + xposition = fixedwidth - ((UINT32)(fixedwidth - xposition) % fixedwidth); + else if (xposition >= fixedwidth) + xposition %= fixedwidth; + if (yposition < 0) + yposition = fixedheight - ((UINT32)(fixedheight - yposition) % fixedheight); + else if (yposition >= fixedheight) + yposition %= fixedheight; + while (count-- && dest <= deststop) { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); - - // Carefully align all of my Friends. - if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); - if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + // The loops here keep the texture coordinates within the texture. + // They will rarely iterate multiple times, and are cheaper than a modulo operation, + // even if using libdivide. + if (xstep < 0) // These if statements are hopefully hoisted by the compiler to above this loop + while (xposition < 0) + xposition += fixedwidth; + else + while (xposition >= fixedwidth) + xposition -= fixedwidth; + if (ystep < 0) + while (yposition < 0) + yposition += fixedheight; + else + while (yposition >= fixedheight) + yposition -= fixedheight; + x = (xposition >> FRACBITS); + y = (yposition >> FRACBITS); val = source[((y * ds_flatwidth) + x)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; @@ -715,6 +758,8 @@ void R_DrawTranslucentSplat_NPO2_8 (void) fixed_t xposition; fixed_t yposition; fixed_t xstep, ystep; + fixed_t x, y; + fixed_t fixedwidth, fixedheight; UINT8 *source; UINT8 *colormap; @@ -731,20 +776,39 @@ void R_DrawTranslucentSplat_NPO2_8 (void) colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + fixedwidth = ds_flatwidth << FRACBITS; + fixedheight = ds_flatheight << FRACBITS; + + // Fix xposition and yposition if they are out of bounds. + if (xposition < 0) + xposition = fixedwidth - ((UINT32)(fixedwidth - xposition) % fixedwidth); + else if (xposition >= fixedwidth) + xposition %= fixedwidth; + if (yposition < 0) + yposition = fixedheight - ((UINT32)(fixedheight - yposition) % fixedheight); + else if (yposition >= fixedheight) + yposition %= fixedheight; + while (count-- && dest <= deststop) { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); - - // Carefully align all of my Friends. - if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); - if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + // The loops here keep the texture coordinates within the texture. + // They will rarely iterate multiple times, and are cheaper than a modulo operation, + // even if using libdivide. + if (xstep < 0) // These if statements are hopefully hoisted by the compiler to above this loop + while (xposition < 0) + xposition += fixedwidth; + else + while (xposition >= fixedwidth) + xposition -= fixedwidth; + if (ystep < 0) + while (yposition < 0) + yposition += fixedheight; + else + while (yposition >= fixedheight) + yposition -= fixedheight; + x = (xposition >> FRACBITS); + y = (yposition >> FRACBITS); val = source[((y * ds_flatwidth) + x)]; if (val != TRANSPARENTPIXEL) *dest = *(ds_transmap + (colormap[val] << 8) + *dest); @@ -762,6 +826,8 @@ void R_DrawTranslucentSpan_NPO2_8 (void) fixed_t xposition; fixed_t yposition; fixed_t xstep, ystep; + fixed_t x, y; + fixed_t fixedwidth, fixedheight; UINT8 *source; UINT8 *colormap; @@ -778,20 +844,39 @@ void R_DrawTranslucentSpan_NPO2_8 (void) colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + fixedwidth = ds_flatwidth << FRACBITS; + fixedheight = ds_flatheight << FRACBITS; + + // Fix xposition and yposition if they are out of bounds. + if (xposition < 0) + xposition = fixedwidth - ((UINT32)(fixedwidth - xposition) % fixedwidth); + else if (xposition >= fixedwidth) + xposition %= fixedwidth; + if (yposition < 0) + yposition = fixedheight - ((UINT32)(fixedheight - yposition) % fixedheight); + else if (yposition >= fixedheight) + yposition %= fixedheight; + while (count-- && dest <= deststop) { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); - - // Carefully align all of my Friends. - if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); - if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + // The loops here keep the texture coordinates within the texture. + // They will rarely iterate multiple times, and are cheaper than a modulo operation, + // even if using libdivide. + if (xstep < 0) // These if statements are hopefully hoisted by the compiler to above this loop + while (xposition < 0) + xposition += fixedwidth; + else + while (xposition >= fixedwidth) + xposition -= fixedwidth; + if (ystep < 0) + while (yposition < 0) + yposition += fixedheight; + else + while (yposition >= fixedheight) + yposition -= fixedheight; + x = (xposition >> FRACBITS); + y = (yposition >> FRACBITS); val = ((y * ds_flatwidth) + x); *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); dest++; @@ -806,6 +891,8 @@ void R_DrawTranslucentWaterSpan_NPO2_8(void) fixed_t xposition; fixed_t yposition; fixed_t xstep, ystep; + fixed_t x, y; + fixed_t fixedwidth, fixedheight; UINT8 *source; UINT8 *colormap; @@ -823,20 +910,39 @@ void R_DrawTranslucentWaterSpan_NPO2_8(void) dest = ylookup[ds_y] + columnofs[ds_x1]; dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; + fixedwidth = ds_flatwidth << FRACBITS; + fixedheight = ds_flatheight << FRACBITS; + + // Fix xposition and yposition if they are out of bounds. + if (xposition < 0) + xposition = fixedwidth - ((UINT32)(fixedwidth - xposition) % fixedwidth); + else if (xposition >= fixedwidth) + xposition %= fixedwidth; + if (yposition < 0) + yposition = fixedheight - ((UINT32)(fixedheight - yposition) % fixedheight); + else if (yposition >= fixedheight) + yposition %= fixedheight; + while (count-- && dest <= deststop) { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); - - // Carefully align all of my Friends. - if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); - if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + // The loops here keep the texture coordinates within the texture. + // They will rarely iterate multiple times, and are cheaper than a modulo operation, + // even if using libdivide. + if (xstep < 0) // These if statements are hopefully hoisted by the compiler to above this loop + while (xposition < 0) + xposition += fixedwidth; + else + while (xposition >= fixedwidth) + xposition -= fixedwidth; + if (ystep < 0) + while (yposition < 0) + yposition += fixedheight; + else + while (yposition >= fixedheight) + yposition -= fixedheight; + x = (xposition >> FRACBITS); + y = (yposition >> FRACBITS); *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; diff --git a/src/r_local.h b/src/r_local.h index 48044118d..4ccb766cf 100644 --- a/src/r_local.h +++ b/src/r_local.h @@ -31,6 +31,7 @@ #include "r_plane.h" #include "r_sky.h" #include "r_data.h" +#include "r_textures.h" #include "r_things.h" #include "r_draw.h" diff --git a/src/r_main.c b/src/r_main.c index 4f79dd8db..5165b3c87 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -100,20 +100,22 @@ lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; extracolormap_t *extra_colormaps = NULL; // Render stats -int rs_prevframetime = 0; -int rs_rendercalltime = 0; -int rs_swaptime = 0; +int ps_prevframetime = 0; +int ps_rendercalltime = 0; +int ps_uitime = 0; +int ps_swaptime = 0; -int rs_bsptime = 0; +int ps_bsptime = 0; -int rs_sw_portaltime = 0; -int rs_sw_planetime = 0; -int rs_sw_maskedtime = 0; +int ps_sw_spritecliptime = 0; +int ps_sw_portaltime = 0; +int ps_sw_planetime = 0; +int ps_sw_maskedtime = 0; -int rs_numbspcalls = 0; -int rs_numsprites = 0; -int rs_numdrawnodes = 0; -int rs_numpolyobjects = 0; +int ps_numbspcalls = 0; +int ps_numsprites = 0; +int ps_numdrawnodes = 0; +int ps_numpolyobjects = 0; static CV_PossibleValue_t drawdist_cons_t[] = { {256, "256"}, {512, "512"}, {768, "768"}, @@ -141,31 +143,32 @@ static void FlipCam2_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); -consvar_t cv_tailspickup = {"tailspickup", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasecam = {"chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_chasecam = CVAR_INIT ("chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange); +consvar_t cv_chasecam2 = CVAR_INIT ("chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange); +consvar_t cv_flipcam = CVAR_INIT ("flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange); +consvar_t cv_flipcam2 = CVAR_INIT ("flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange); -consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_shadow = CVAR_INIT ("shadow", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_skybox = CVAR_INIT ("skybox", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_ffloorclip = CVAR_INIT ("ffloorclip", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_allowmlook = CVAR_INIT ("allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL); +consvar_t cv_showhud = CVAR_INIT ("showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize); +consvar_t cv_translucenthud = CVAR_INIT ("translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL); -consvar_t cv_translucency = {"translucency", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -//consvar_t cv_precipdensity = {"precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fov = {"fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_translucency = CVAR_INIT ("translucency", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_drawdist = CVAR_INIT ("drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL); +consvar_t cv_drawdist_nights = CVAR_INIT ("drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL); +consvar_t cv_drawdist_precip = CVAR_INIT ("drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL); +//consvar_t cv_precipdensity = CVAR_INIT ("precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL); +consvar_t cv_fov = CVAR_INIT ("fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange); // Okay, whoever said homremoval causes a performance hit should be shot. -consvar_t cv_homremoval = {"homremoval", "No", CV_SAVE, homremoval_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_homremoval = CVAR_INIT ("homremoval", "No", CV_SAVE, homremoval_cons_t, NULL); -consvar_t cv_maxportals = {"maxportals", "2", CV_SAVE, maxportals_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxportals = CVAR_INIT ("maxportals", "2", CV_SAVE, maxportals_cons_t, NULL); -consvar_t cv_renderstats = {"renderstats", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_renderstats = CVAR_INIT ("renderstats", "Off", 0, CV_OnOff, NULL); void SplitScreen_OnChange(void) { @@ -1074,15 +1077,22 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y) // recalc necessary stuff for mouseaiming // slopes are already calculated for the full possible view (which is 4*viewheight). // 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) -static void R_SetupFreelook(void) +static void R_SetupFreelook(player_t *player, boolean skybox) { INT32 dy = 0; +#ifndef HWRENDER + (void)player; + (void)skybox; +#endif + // clip it in the case we are looking a hardware 90 degrees full aiming // (lmps, network and use F12...) if (rendermode == render_soft #ifdef HWRENDER - || cv_glshearing.value + || (rendermode == render_opengl + && (cv_glshearing.value == 1 + || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) #endif ) { @@ -1203,7 +1213,7 @@ void R_SetupFrame(player_t *player) viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(); + R_SetupFreelook(player, false); } void R_SkyboxFrame(player_t *player) @@ -1340,7 +1350,7 @@ void R_SkyboxFrame(player_t *player) viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(); + R_SetupFreelook(player, true); } boolean R_ViewpointHasChasecam(player_t *player) @@ -1480,11 +1490,11 @@ void R_RenderPlayerView(player_t *player) mytotal = 0; ProfZeroTimer(); #endif - rs_numbspcalls = rs_numpolyobjects = rs_numdrawnodes = 0; - rs_bsptime = I_GetTimeMicros(); + ps_numbspcalls = ps_numpolyobjects = ps_numdrawnodes = 0; + ps_bsptime = I_GetTimeMicros(); R_RenderBSPNode((INT32)numnodes - 1); - rs_bsptime = I_GetTimeMicros() - rs_bsptime; - rs_numsprites = visspritecount; + ps_bsptime = I_GetTimeMicros() - ps_bsptime; + ps_numsprites = visspritecount; #ifdef TIMING RDMSR(0x10, &mycount); mytotal += mycount; // 64bit add @@ -1494,7 +1504,9 @@ void R_RenderPlayerView(player_t *player) //profile stuff --------------------------------------------------------- Mask_Post(&masks[nummasks - 1]); + ps_sw_spritecliptime = I_GetTimeMicros(); R_ClipSprites(drawsegs, NULL); + ps_sw_spritecliptime = I_GetTimeMicros() - ps_sw_spritecliptime; // Add skybox portals caused by sky visplanes. @@ -1502,7 +1514,7 @@ void R_RenderPlayerView(player_t *player) Portal_AddSkyboxPortals(); // Portal rendering. Hijacks the BSP traversal. - rs_sw_portaltime = I_GetTimeMicros(); + ps_sw_portaltime = I_GetTimeMicros(); if (portal_base) { portal_t *portal; @@ -1542,25 +1554,24 @@ void R_RenderPlayerView(player_t *player) Portal_Remove(portal); } } - rs_sw_portaltime = I_GetTimeMicros() - rs_sw_portaltime; + ps_sw_portaltime = I_GetTimeMicros() - ps_sw_portaltime; - rs_sw_planetime = I_GetTimeMicros(); + ps_sw_planetime = I_GetTimeMicros(); R_DrawPlanes(); #ifdef FLOORSPLATS R_DrawVisibleFloorSplats(); #endif - rs_sw_planetime = I_GetTimeMicros() - rs_sw_planetime; + ps_sw_planetime = I_GetTimeMicros() - ps_sw_planetime; // draw mid texture and sprite // And now 3D floors/sides! - rs_sw_maskedtime = I_GetTimeMicros(); + ps_sw_maskedtime = I_GetTimeMicros(); R_DrawMasked(masks, nummasks); - rs_sw_maskedtime = I_GetTimeMicros() - rs_sw_maskedtime; + ps_sw_maskedtime = I_GetTimeMicros() - ps_sw_maskedtime; free(masks); } -// Lactozilla: Renderer switching #ifdef HWRENDER void R_InitHardwareMode(void) { @@ -1574,7 +1585,6 @@ void R_InitHardwareMode(void) void R_ReloadHUDGraphics(void) { - CONS_Debug(DBG_RENDER, "R_ReloadHUDGraphics()...\n"); ST_LoadGraphics(); HU_LoadGraphics(); ST_ReloadSkinFaceGraphics(); @@ -1608,6 +1618,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_shadow); CV_RegisterVar(&cv_skybox); + CV_RegisterVar(&cv_ffloorclip); CV_RegisterVar(&cv_cam_dist); CV_RegisterVar(&cv_cam_still); diff --git a/src/r_main.h b/src/r_main.h index 729ec6973..379b5b8df 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -16,6 +16,7 @@ #include "d_player.h" #include "r_data.h" +#include "r_textures.h" // // POV related. @@ -77,22 +78,22 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe // Render stats -extern consvar_t cv_renderstats; +extern int ps_prevframetime;// time when previous frame was rendered +extern int ps_rendercalltime; +extern int ps_uitime; +extern int ps_swaptime; -extern int rs_prevframetime;// time when previous frame was rendered -extern int rs_rendercalltime; -extern int rs_swaptime; +extern int ps_bsptime; -extern int rs_bsptime; +extern int ps_sw_spritecliptime; +extern int ps_sw_portaltime; +extern int ps_sw_planetime; +extern int ps_sw_maskedtime; -extern int rs_sw_portaltime; -extern int rs_sw_planetime; -extern int rs_sw_maskedtime; - -extern int rs_numbspcalls; -extern int rs_numsprites; -extern int rs_numdrawnodes; -extern int rs_numpolyobjects; +extern int ps_numbspcalls; +extern int ps_numsprites; +extern int ps_numdrawnodes; +extern int ps_numpolyobjects; // // REFRESH - the actual rendering functions. @@ -104,6 +105,7 @@ extern consvar_t cv_chasecam, cv_chasecam2; extern consvar_t cv_flipcam, cv_flipcam2; extern consvar_t cv_shadow; +extern consvar_t cv_ffloorclip; extern consvar_t cv_translucency; extern consvar_t cv_drawdist, cv_drawdist_nights, cv_drawdist_precip; extern consvar_t cv_fov; diff --git a/src/r_patch.h b/src/r_patch.h deleted file mode 100644 index a2db6320c..000000000 --- a/src/r_patch.h +++ /dev/null @@ -1,74 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019-2020 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 r_patch.h -/// \brief Patch generation. - -#ifndef __R_PATCH__ -#define __R_PATCH__ - -#include "r_defs.h" -#include "doomdef.h" - -// Structs -typedef enum -{ - ROTAXIS_X, // Roll (the default) - ROTAXIS_Y, // Pitch - ROTAXIS_Z // Yaw -} rotaxis_t; - -typedef struct -{ - INT32 x, y; - rotaxis_t rotaxis; -} spriteframepivot_t; - -typedef struct -{ - spriteframepivot_t pivot[64]; - boolean available; -} spriteinfo_t; - -// Conversions between patches / flats / textures... -boolean R_CheckIfPatch(lumpnum_t lump); -void R_TextureToFlat(size_t tex, UINT8 *flat); -void R_PatchToFlat(patch_t *patch, UINT8 *flat); -void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip); -patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency); -patch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize); - -// Portable Network Graphics -boolean R_IsLumpPNG(const UINT8 *d, size_t s); -#define W_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic - -#ifndef NO_PNG_LUMPS -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size); -patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize); -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); -#endif - -// SpriteInfo -extern spriteinfo_t spriteinfo[NUMSPRITES]; -void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); -void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); - -// Sprite rotation -#ifdef ROTSPRITE -INT32 R_GetRollAngle(angle_t rollangle); -void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip); -void R_FreeSingleRotSprite(spritedef_t *spritedef); -void R_FreeSkinRotSprite(size_t skinnum); -extern fixed_t rollcosang[ROTANGLES]; -extern fixed_t rollsinang[ROTANGLES]; -void R_FreeAllRotSprite(void); -#endif - -#endif // __R_PATCH__ diff --git a/src/r_patch.c b/src/r_picformats.c similarity index 55% rename from src/r_patch.c rename to src/r_picformats.c index 8980eda58..95fe23aeb 100644 --- a/src/r_patch.c +++ b/src/r_picformats.c @@ -9,16 +9,18 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file r_patch.c -/// \brief Patch generation. +/// \file r_picformats.c +/// \brief Picture generation. #include "byteptr.h" #include "dehacked.h" #include "i_video.h" #include "r_data.h" +#include "r_textures.h" #include "r_draw.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" +#include "v_video.h" #include "z_zone.h" #include "w_wad.h" @@ -50,30 +52,578 @@ static unsigned char imgbuf[1<<26]; -// -// R_CheckIfPatch -// -// Returns true if the lump is a valid patch. -// -boolean R_CheckIfPatch(lumpnum_t lump) -{ - size_t size; - INT16 width, height; - patch_t *patch; - boolean result; +#ifdef PICTURE_PNG_USELOOKUP +static colorlookup_t png_colorlookup; +#endif - size = W_LumpLength(lump); +/** Converts a picture between two formats. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + * \sa Picture_PatchConvert + * \sa Picture_FlatConvert + */ +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags) +{ + if (informat == PICFMT_NONE) + I_Error("Picture_Convert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_Convert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_Convert: input and output formats were the same!"); + + if (Picture_IsPatchFormat(outformat)) + return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else if (Picture_IsFlatFormat(outformat)) + return Picture_FlatConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else + I_Error("Picture_Convert: unsupported input format!"); + + return NULL; +} + +/** Converts a picture to a patch. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + INT16 x, y; + UINT8 *img; + UINT8 *imgptr = imgbuf; + UINT8 *colpointers, *startofspan; + size_t size = 0; + patch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + + (void)insize; // ignore + + if (informat == PICFMT_NONE) + I_Error("Picture_PatchConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_PatchConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_PatchConvert: input and output formats were the same!"); + + if (inbpp == PICDEPTH_NONE) + I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); + if (Picture_FormatBPP(outformat) == PICDEPTH_NONE) + I_Error("Picture_PatchConvert: unknown output bits per pixel?!"); + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + inleftoffset = SHORT(inpatch->leftoffset); + intopoffset = SHORT(inpatch->topoffset); + } + + // Write image size and offset + WRITEINT16(imgptr, inwidth); + WRITEINT16(imgptr, inheight); + WRITEINT16(imgptr, inleftoffset); + WRITEINT16(imgptr, intopoffset); + + // Leave placeholder to column pointers + colpointers = imgptr; + imgptr += inwidth*4; + + // Write columns + for (x = 0; x < inwidth; x++) + { + int lastStartY = 0; + int spanSize = 0; + startofspan = NULL; + + // Write column pointer + WRITEINT32(colpointers, imgptr - imgbuf); + + // Write pixels + for (y = 0; y < inheight; y++) + { + void *input = NULL; + boolean opaque = false; + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + { + size_t offs = ((y * inwidth) + x); + switch (informat) + { + case PICFMT_FLAT32: + input = (UINT32 *)picture + offs; + break; + case PICFMT_FLAT16: + input = (UINT16 *)picture + offs; + break; + case PICFMT_FLAT: + input = (UINT8 *)picture + offs; + break; + default: + I_Error("Picture_PatchConvert: unsupported flat input format!"); + break; + } + } + else + I_Error("Picture_PatchConvert: unsupported input format!"); + + // Determine opacity + if (input != NULL) + { + UINT8 alpha = 0xFF; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t px = *(RGBA_t *)input; + alpha = px.s.alpha; + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 px = *(UINT16 *)input; + alpha = (px & 0xFF00) >> 8; + } + else if (inbpp == PICDEPTH_8BPP) + { + UINT8 px = *(UINT8 *)input; + if (px == TRANSPARENTPIXEL) + alpha = 0; + } + opaque = (alpha > 1); + } + + // End span if we have a transparent pixel + if (!opaque) + { + if (startofspan) + WRITEUINT8(imgptr, 0); + startofspan = NULL; + continue; + } + + // Start new column if we need to + if (!startofspan || spanSize == 255) + { + int writeY = y; + + // If we reached the span size limit, finish the previous span + if (startofspan) + WRITEUINT8(imgptr, 0); + + if (y > 254) + { + // Make sure we're aligned to 254 + if (lastStartY < 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + lastStartY = 254; + } + + // Write stopgap empty spans if needed + writeY = y - lastStartY; + + while (writeY > 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + writeY -= 254; + } + } + + startofspan = imgptr; + WRITEUINT8(imgptr, writeY); + imgptr += 2; + spanSize = 0; + + lastStartY = y; + } + + // Write the pixel + switch (outformat) + { + case PICFMT_PATCH32: + { + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t out = *(RGBA_t *)input; + WRITEUINT32(imgptr, out.rgba); + } + else if (inbpp == PICDEPTH_16BPP) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + break; + } + case PICFMT_PATCH16: + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT16(imgptr, (0xFF00 | out)); + } + else if (inbpp == PICDEPTH_16BPP) + WRITEUINT16(imgptr, *(UINT16 *)input); + else // PICFMT_PATCH + WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input))); + break; + default: // PICFMT_PATCH + { + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT8(imgptr, out); + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 out = *(UINT16 *)input; + WRITEUINT8(imgptr, (out & 0xFF)); + } + else // PICFMT_PATCH + WRITEUINT8(imgptr, *(UINT8 *)input); + break; + } + } + + spanSize++; + startofspan[1] = spanSize; + } + + if (startofspan) + WRITEUINT8(imgptr, 0); + + WRITEUINT8(imgptr, 0xFF); + } + + size = imgptr-imgbuf; + img = Z_Malloc(size, PU_STATIC, NULL); + memcpy(img, imgbuf, size); + + if (outsize != NULL) + *outsize = size; + return img; +} + +/** Converts a picture to a flat. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + void *outflat; + patch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + INT32 outbpp = Picture_FormatBPP(outformat); + INT32 x, y; + size_t size; + + (void)insize; // ignore + (void)inleftoffset; // ignore + (void)intopoffset; // ignore + + if (informat == PICFMT_NONE) + I_Error("Picture_FlatConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_FlatConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_FlatConvert: input and output formats were the same!"); + + if (inbpp == PICDEPTH_NONE) + I_Error("Picture_FlatConvert: unknown input bits per pixel?!"); + if (outbpp == PICDEPTH_NONE) + I_Error("Picture_FlatConvert: unknown output bits per pixel?!"); + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + } + + size = (inwidth * inheight) * (outbpp / 8); + outflat = Z_Calloc(size, PU_STATIC, NULL); + if (outsize) + *outsize = size; + + // Set transparency + if (outbpp == PICDEPTH_8BPP) + memset(outflat, TRANSPARENTPIXEL, size); + + for (y = 0; y < inheight; y++) + for (x = 0; x < inwidth; x++) + { + void *input; + size_t offs = ((y * inwidth) + x); + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + input = (UINT8 *)picture + (offs * (inbpp / 8)); + else + I_Error("Picture_FlatConvert: unsupported input format!"); + + if (!input) + continue; + + switch (outformat) + { + case PICFMT_FLAT32: + { + UINT32 *f32 = (UINT32 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t out = *(RGBA_t *)input; + f32[offs] = out.rgba; + } + else if (inbpp == PICDEPTH_16BPP) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + break; + } + case PICFMT_FLAT16: + { + UINT16 *f16 = (UINT16 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f16[offs] = (0xFF00 | out); + } + else if (inbpp == PICDEPTH_16BPP) + f16[offs] = *(UINT16 *)input; + else // PICFMT_PATCH + f16[offs] = (0xFF00 | *((UINT8 *)input)); + break; + } + case PICFMT_FLAT: + { + UINT8 *f8 = (UINT8 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f8[offs] = out; + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 out = *(UINT16 *)input; + f8[offs] = (out & 0xFF); + } + else // PICFMT_PATCH + f8[offs] = *(UINT8 *)input; + break; + } + default: + I_Error("Picture_FlatConvert: unsupported output format!"); + } + } + + return outflat; +} + +/** Returns a pixel from a patch. + * + * \param patch Input patch. + * \param informat Input picture format. + * \param x Pixel X position. + * \param y Pixel Y position. + * \param flags Input picture flags. + * \return A pointer to a pixel in the patch. Returns NULL if not opaque. + */ +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags) +{ + fixed_t ofs; + column_t *column; + UINT8 *s8 = NULL; + UINT16 *s16 = NULL; + UINT32 *s32 = NULL; + + if (patch == NULL) + I_Error("Picture_GetPatchPixel: patch == NULL"); + + if (x >= 0 && x < SHORT(patch->width)) + { + INT32 topdelta, prevdelta = -1; + INT32 colofs = 0; + + if (flags & PICFLAGS_XFLIP) + colofs = LONG(patch->columnofs[(SHORT(patch->width)-1)-x]); + else + colofs = LONG(patch->columnofs[x]); + + // Column offsets are pointers so no casting required + column = (column_t *)((UINT8 *)patch + colofs); + + while (column->topdelta != 0xff) + { + topdelta = column->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + s8 = (UINT8 *)(column) + 3; + if (informat == PICFMT_PATCH32) + s32 = (UINT32 *)s8; + else if (informat == PICFMT_PATCH16) + s16 = (UINT16 *)s8; + for (ofs = 0; ofs < column->length; ofs++) + { + if ((topdelta + ofs) == y) + { + if (informat == PICFMT_PATCH32) + return &s32[ofs]; + else if (informat == PICFMT_PATCH16) + return &s16[ofs]; + else // PICFMT_PATCH + return &s8[ofs]; + } + } + if (informat == PICFMT_PATCH32) + column = (column_t *)((UINT32 *)column + column->length); + else if (informat == PICFMT_PATCH16) + column = (column_t *)((UINT16 *)column + column->length); + else + column = (column_t *)((UINT8 *)column + column->length); + column = (column_t *)((UINT8 *)column + 4); + } + } + + return NULL; +} + +/** Returns the amount of bits per pixel in the specified picture format. + * + * \param format Input picture format. + * \return The bits per pixel amount of the picture format. + */ +INT32 Picture_FormatBPP(pictureformat_t format) +{ + INT32 bpp = PICDEPTH_NONE; + switch (format) + { + case PICFMT_PATCH32: + case PICFMT_FLAT32: + case PICFMT_PNG: + bpp = PICDEPTH_32BPP; + break; + case PICFMT_PATCH16: + case PICFMT_FLAT16: + bpp = PICDEPTH_16BPP; + break; + case PICFMT_PATCH: + case PICFMT_FLAT: + bpp = PICDEPTH_8BPP; + break; + default: + break; + } + return bpp; +} + +/** Checks if the specified picture format is a patch. + * + * \param format Input picture format. + * \return True if the picture format is a patch, false if not. + */ +boolean Picture_IsPatchFormat(pictureformat_t format) +{ + return (format == PICFMT_PATCH || format == PICFMT_PATCH16 || format == PICFMT_PATCH32); +} + +/** Checks if the specified picture format is a flat. + * + * \param format Input picture format. + * \return True if the picture format is a flat, false if not. + */ +boolean Picture_IsFlatFormat(pictureformat_t format) +{ + return (format == PICFMT_FLAT || format == PICFMT_FLAT16 || format == PICFMT_FLAT32); +} + +/** Returns true if the lump is a valid patch. + * PICFMT_PATCH only, I think?? + * + * \param patch Input patch. + * \param picture Input patch size. + * \return True if the input patch is valid. + */ +boolean Picture_CheckIfPatch(patch_t *patch, size_t size) +{ + INT16 width, height; + boolean result; // minimum length of a valid Doom patch if (size < 13) return false; - patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC); - width = SHORT(patch->width); height = SHORT(patch->height); - - result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(size / 4)); + result = (height > 0 && height <= 16384 && width > 0 && width <= 16384); if (result) { @@ -99,26 +649,40 @@ boolean R_CheckIfPatch(lumpnum_t lump) return result; } -// -// R_TextureToFlat -// -// Convert a texture to a flat. -// -void R_TextureToFlat(size_t tex, UINT8 *flat) +/** Converts a texture to a flat. + * + * \param trickytex The texture number. + * \return The converted flat. + */ +void *Picture_TextureToFlat(size_t trickytex) { - texture_t *texture = textures[tex]; + texture_t *texture; + size_t tex; + UINT8 *converted; + size_t flatsize; fixed_t col, ofs; column_t *column; UINT8 *desttop, *dest, *deststop; UINT8 *source; - // yea + if (trickytex >= (unsigned)numtextures) + I_Error("Picture_TextureToFlat: invalid texture number!"); + + // Check the texture cache + // If the texture's not there, it'll be generated right now + tex = trickytex; + texture = textures[tex]; R_CheckTextureCache(tex); - desttop = flat; - deststop = desttop + (texture->width * texture->height); + // Allocate the flat + flatsize = (texture->width * texture->height); + converted = Z_Malloc(flatsize, PU_STATIC, NULL); + memset(converted, TRANSPARENTPIXEL, flatsize); + // Now we're gonna write to it + desttop = converted; + deststop = desttop + flatsize; for (col = 0; col < texture->width; col++, desttop++) { // no post_t info @@ -157,322 +721,17 @@ void R_TextureToFlat(size_t tex, UINT8 *flat) } } } + + return converted; } -// -// R_PatchToFlat -// -// Convert a patch to a flat. -// -void R_PatchToFlat(patch_t *patch, UINT8 *flat) -{ - fixed_t col, ofs; - column_t *column; - UINT8 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = flat; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[col])); - - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } -} - -// -// R_PatchToMaskedFlat -// -// Convert a patch to a masked flat. -// Now, what is a "masked" flat anyway? -// It means the flat uses two bytes to store image data. -// The upper byte is used to store the transparent pixel, -// and the lower byte stores a palette index. -// -void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip) -{ - fixed_t col, ofs; - column_t *column; - UINT16 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = raw; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-col) : col])); - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } -} - -// -// R_FlatToPatch -// -// Convert a flat to a patch. -// -patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency) -{ - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - size_t size = 0; - - if (!raw) - return NULL; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - WRITEINT16(imgptr, leftoffset); - WRITEINT16(imgptr, topoffset); - - // Leave placeholder to column pointers - colpointers = imgptr; - imgptr += width*4; - - // Write columns - for (x = 0; x < width; x++) - { - int lastStartY = 0; - int spanSize = 0; - startofspan = NULL; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT8 paletteIndex = raw[((y * width) + x)]; - boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true; - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(imgptr, 0); - startofspan = NULL; - continue; - } - - // Start new column if we need to - if (!startofspan || spanSize == 255) - { - int writeY = y; - - // If we reached the span size limit, finish the previous span - if (startofspan) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - size = imgptr-imgbuf; - img = Z_Malloc(size, PU_STATIC, NULL); - memcpy(img, imgbuf, size); - - Z_Free(raw); - - if (destsize != NULL) - *destsize = size; - return (patch_t *)img; -} - -// -// R_MaskedFlatToPatch -// -// Convert a masked flat to a patch. -// Explanation of "masked" flats in R_PatchToMaskedFlat. -// -patch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize) -{ - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - size_t size = 0; - - if (!raw) - return NULL; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - WRITEINT16(imgptr, leftoffset); - WRITEINT16(imgptr, topoffset); - - // Leave placeholder to column pointers - colpointers = imgptr; - imgptr += width*4; - - // Write columns - for (x = 0; x < width; x++) - { - int lastStartY = 0; - int spanSize = 0; - startofspan = NULL; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT16 pixel = raw[((y * width) + x)]; - UINT8 paletteIndex = (pixel & 0xFF); - UINT8 opaque = (pixel != 0xFF00); // If 1, we have a pixel - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(imgptr, 0); - startofspan = NULL; - continue; - } - - // Start new column if we need to - if (!startofspan || spanSize == 255) - { - int writeY = y; - - // If we reached the span size limit, finish the previous span - if (startofspan) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - size = imgptr-imgbuf; - img = Z_Malloc(size, PU_STATIC, NULL); - memcpy(img, imgbuf, size); - - if (destsize != NULL) - *destsize = size; - return (patch_t *)img; -} - -// -// R_IsLumpPNG -// -// Returns true if the lump is a valid PNG. -// -boolean R_IsLumpPNG(const UINT8 *d, size_t s) +/** Returns true if the lump is a valid PNG. + * + * \param d The lump to be checked. + * \param s The lump size. + * \return True if the lump is a PNG image. + */ +boolean Picture_IsLumpPNG(const UINT8 *d, size_t s) { if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ return false; @@ -539,13 +798,26 @@ static void PNG_warn(png_structp PNG, png_const_charp pngtext) CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext); } -static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; + +static png_bytep *PNG_Read( + const UINT8 *png, + INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, + boolean *use_palette, size_t size) { png_structp png_ptr; png_infop png_info_ptr; png_uint_32 width, height; int bit_depth, color_type; png_uint_32 y; + + png_colorp palette; + int palette_size; + + png_bytep trans; + int trans_num; + png_color_16p trans_values; + #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; @@ -554,23 +826,17 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff png_io_t png_io; png_bytep *row_pointers; - - png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; png_voidp *user_chunk_ptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) - { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on initialize libpng\n"); - return NULL; - } + I_Error("PNG_Read: Couldn't initialize libpng!"); png_info_ptr = png_create_info_struct(png_ptr); if (!png_info_ptr) { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on allocate for libpng\n"); png_destroy_read_struct(&png_ptr, NULL, NULL); - return NULL; + I_Error("PNG_Read: libpng couldn't allocate memory!"); } #ifdef USE_FAR_KEYWORD @@ -579,15 +845,13 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff if (setjmp(png_jmpbuf(png_ptr))) #endif { - //CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename); png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - return NULL; + I_Error("PNG_Read: libpng load error!"); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // set our own read function png_io.buffer = png; png_io.size = size; png_io.position = 0; @@ -610,10 +874,48 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff if (bit_depth == 16) png_set_strip_16(png_ptr); + palette = NULL; + *use_palette = false; + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); else if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); + { + boolean usepal = false; + + // Lactozilla: Check if the PNG has a palette, and if its color count + // matches the color count of SRB2's palette: 256 colors. + if (png_get_PLTE(png_ptr, png_info_ptr, &palette, &palette_size)) + { + if (palette_size == 256) + usepal = true; + } + + // If any of the tRNS colors have an alpha lower than 0xFF, and that + // color is present on the image, the palette flag is disabled. + png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); + + if (trans && trans_num == 256) + { + int i; + for (i = 0; i < trans_num; i++) + { + // libpng will transform this image into RGB even if + // the transparent index does not exist in the image, + // and there is no way around that. + if (trans[i] < 0xFF) + { + usepal = false; + break; + } + } + } + + if (usepal) + *use_palette = true; + else + png_set_palette_to_rgb(png_ptr); + } if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); @@ -647,113 +949,265 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff *topoffset = (INT16)BIGENDIAN_LONG(*offsets); } - // bye png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); if (chunk.data) Z_Free(chunk.data); *w = (INT32)width; *h = (INT32)height; + return row_pointers; } -// Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +/** Converts a PNG to a picture. + * + * \param png The PNG image. + * \param outformat The output picture's format. + * \param w The output picture's width, as a pointer. + * \param h The output picture's height, as a pointer. + * \param topoffset The output picture's top offset, for sprites, as a pointer. + * \param leftoffset The output picture's left offset, for sprites, as a pointer. + * \param insize The input picture's size. + * \param outsize A pointer to the output picture's size. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags) { - UINT8 *flat; + void *flat; + INT32 outbpp; + size_t flatsize; png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); - png_uint_32 width = *w, height = *h; + png_bytep row; + boolean palette = false; + png_bytep *row_pointers = NULL; + png_uint_32 width, height; - if (!row_pointers) - I_Error("PNG_RawConvert: conversion failed"); + INT32 pngwidth, pngheight; + INT16 loffs = 0, toffs = 0; - // Convert the image to 8bpp - flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); - for (y = 0; y < height; y++) + if (png == NULL) + I_Error("Picture_PNGConvert: picture was NULL!"); + + if (w == NULL) + w = &pngwidth; + if (h == NULL) + h = &pngheight; + if (topoffset == NULL) + topoffset = &toffs; + if (leftoffset == NULL) + leftoffset = &loffs; + + row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize); + width = *w; + height = *h; + + if (row_pointers == NULL) + I_Error("Picture_PNGConvert: row_pointers was NULL!"); + + // Find the output format's bits per pixel amount + outbpp = Picture_FormatBPP(outformat); + + // Hack for patches because you'll want to preserve transparency. + if (Picture_IsPatchFormat(outformat)) { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) + // Force a higher bit depth + if (outbpp == PICDEPTH_8BPP) + outbpp = PICDEPTH_16BPP; + } + + // Shouldn't happen. + if (outbpp == PICDEPTH_NONE) + I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + + // Figure out the size + flatsize = (width * height) * (outbpp / 8); + if (outsize) + *outsize = flatsize; + + // Convert the image + flat = Z_Calloc(flatsize, PU_STATIC, NULL); + + // Set transparency + if (outbpp == PICDEPTH_8BPP) + memset(flat, TRANSPARENTPIXEL, (width * height)); + +#ifdef PICTURE_PNG_USELOOKUP + if (outbpp != PICDEPTH_32BPP) + InitColorLUT(&png_colorlookup, pMasterPalette, false); +#endif + + if (outbpp == PICDEPTH_32BPP) + { + RGBA_t out; + UINT32 *outflat = (UINT32 *)flat; + + if (palette) { - png_bytep px = &(row[x * 4]); - if ((UINT8)px[3]) - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + out = V_GetColor(row[x]); + outflat[((y * width) + x)] = out.rgba; + } + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + if ((UINT8)px[3]) + { + out.s.red = (UINT8)px[0]; + out.s.green = (UINT8)px[1]; + out.s.blue = (UINT8)px[2]; + out.s.alpha = (UINT8)px[3]; + outflat[((y * width) + x)] = out.rgba; + } + else + outflat[((y * width) + x)] = 0x00000000; + } + } } } + else if (outbpp == PICDEPTH_16BPP) + { + UINT16 *outflat = (UINT16 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = (0xFF << 8) | row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) + { +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif + outflat[((y * width) + x)] = (0xFF << 8) | palidx; + } + else + outflat[((y * width) + x)] = 0x0000; + } + } + } + } + else // 8bpp + { + UINT8 *outflat = (UINT8 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) + { +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif + outflat[((y * width) + x)] = palidx; + } + } + } + } + } + + // Free the row pointers that we allocated for libpng. + for (y = 0; y < height; y++) + free(row_pointers[y]); free(row_pointers); + // But wait, there's more! + if (Picture_IsPatchFormat(outformat)) + { + void *converted; + pictureformat_t informat = PICFMT_NONE; + + // Figure out the format of the flat, from the bit depth of the output format + switch (outbpp) + { + case 32: + informat = PICFMT_FLAT32; + break; + case 16: + informat = PICFMT_FLAT16; + break; + default: + informat = PICFMT_FLAT; + break; + } + + // Now, convert it! + converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, *leftoffset, *topoffset, flags); + Z_Free(flat); + return converted; + } + + // Return the converted flat! return flat; } -// Convert a PNG with transparency to a raw image. -static UINT16 *PNG_MaskedRawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) -{ - UINT16 *flat; - png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); - png_uint_32 width = *w, height = *h; - size_t flatsize, i; - - if (!row_pointers) - I_Error("PNG_MaskedRawConvert: conversion failed"); - - // Convert the image to 16bpp - flatsize = (width * height); - flat = Z_Malloc(flatsize * sizeof(UINT16), PU_LEVEL, NULL); - - // can't memset here - for (i = 0; i < flatsize; i++) - flat[i] = 0xFF00; - - for (y = 0; y < height; y++) - { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) - { - png_bytep px = &(row[x * 4]); - if ((UINT8)px[3]) - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); - } - } - free(row_pointers); - - return flat; -} - -// -// R_PNGToFlat -// -// Convert a PNG to a flat. -// -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size) -{ - return PNG_RawConvert(png, width, height, NULL, NULL, size); -} - -// -// R_PNGToPatch -// -// Convert a PNG to a patch. -// -patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize) -{ - UINT16 width, height; - INT16 topoffset = 0, leftoffset = 0; - UINT16 *raw = PNG_MaskedRawConvert(png, &width, &height, &topoffset, &leftoffset, size); - - if (!raw) - I_Error("R_PNGToPatch: conversion failed"); - - return R_MaskedFlatToPatch(raw, width, height, leftoffset, topoffset, destsize); -} - -// -// R_PNGDimensions -// -// Get the dimensions of a PNG file. -// -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) +/** Returns the dimensions of a PNG image, but doesn't perform any conversions. + * + * \param png The PNG image. + * \param width A pointer to the input picture's width. + * \param height A pointer to the input picture's height. + * \param topoffset A pointer to the input picture's vertical offset. + * \param leftoffset A pointer to the input picture's horizontal offset. + * \param size The input picture's size. + * \return True if reading the file succeeded, false if it failed. + */ +boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -766,21 +1220,17 @@ boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) #endif png_io_t png_io; + png_voidp *user_chunk_ptr; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, - PNG_error, PNG_warn); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) - { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on initialize libpng\n"); - return false; - } + I_Error("Picture_PNGDimensions: Couldn't initialize libpng!"); png_info_ptr = png_create_info_struct(png_ptr); if (!png_info_ptr) { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on allocate for libpng\n"); png_destroy_read_struct(&png_ptr, NULL, NULL); - return false; + I_Error("Picture_PNGDimensions: libpng couldn't allocate memory!"); } #ifdef USE_FAR_KEYWORD @@ -789,31 +1239,48 @@ boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) if (setjmp(png_jmpbuf(png_ptr))) #endif { - //CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename); png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - return false; + I_Error("Picture_PNGDimensions: libpng load error!"); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // set our own read function png_io.buffer = png; png_io.size = size; png_io.position = 0; png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + memset(&chunk, 0x00, sizeof(png_chunk_t)); + chunkname = grAb_chunk; // I want to read a grAb chunk + + user_chunk_ptr = png_get_user_chunk_ptr(png_ptr); + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader); + png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1); + #ifdef PNG_SET_USER_LIMITS_SUPPORTED png_set_user_limits(png_ptr, 2048, 2048); #endif png_read_info(png_ptr, png_info_ptr); + png_get_IHDR(png_ptr, png_info_ptr, &w, &h, &bit_depth, &color_type, NULL, NULL, NULL); - png_get_IHDR(png_ptr, png_info_ptr, &w, &h, &bit_depth, &color_type, - NULL, NULL, NULL); + // Read grAB chunk + if ((topoffset || leftoffset) && (chunk.data != NULL)) + { + INT32 *offsets = (INT32 *)chunk.data; + // read left offset + if (leftoffset != NULL) + *leftoffset = (INT16)BIGENDIAN_LONG(*offsets); + offsets++; + // read top offset + if (topoffset != NULL) + *topoffset = (INT16)BIGENDIAN_LONG(*offsets); + } - // okay done. stop. png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + if (chunk.data) + Z_Free(chunk.data); *width = (INT32)w; *height = (INT32)h; @@ -1137,35 +1604,6 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) } } -static UINT16 GetPatchPixel(patch_t *patch, INT32 x, INT32 y, boolean flip) -{ - fixed_t ofs; - column_t *column; - UINT8 *source; - - if (x >= 0 && x < SHORT(patch->width)) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-x) : x])); - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)(column) + 3; - for (ofs = 0; ofs < column->length; ofs++) - { - if ((topdelta + ofs) == y) - return source[ofs]; - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } - - return 0xFF00; -} - #ifdef ROTSPRITE // // R_GetRollAngle @@ -1192,13 +1630,12 @@ INT32 R_GetRollAngle(angle_t rollangle) // void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip) { - UINT32 i; INT32 angle; patch_t *patch; patch_t *newpatch; UINT16 *rawdst; size_t size; - INT32 bflip = (flip != 0x00); + pictureflags_t bflip = (flip) ? PICFLAGS_XFLIP : 0; #define SPRITE_XCENTER (leftoffset) #define SPRITE_YCENTER (height / 2) @@ -1223,12 +1660,12 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp #ifndef NO_PNG_LUMPS lumplength = W_LumpLength(lump); - if (R_IsLumpPNG((UINT8 *)patch, lumplength)) - patch = R_PNGToPatch((UINT8 *)patch, lumplength, NULL); + if (Picture_IsLumpPNG((const UINT8 *)patch, lumplength)) + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif // Because there's something wrong with SPR_DFLM, I guess - if (!R_CheckIfPatch(lump)) + if (!Picture_CheckIfPatch(patch, lumplength)) return; width = SHORT(patch->width); @@ -1324,10 +1761,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp size = (newwidth * newheight); if (!size) size = (width * height); - - rawdst = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL); - for (i = 0; i < size; i++) - rawdst[i] = 0xFF00; + rawdst = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL); for (dy = 0; dy < newheight; dy++) { @@ -1340,12 +1774,16 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp sx >>= FRACBITS; sy >>= FRACBITS; if (sx >= 0 && sy >= 0 && sx < width && sy < height) - rawdst[(dy*newwidth)+dx] = GetPatchPixel(patch, sx, sy, bflip); + { + void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip); + if (input != NULL) + rawdst[(dy*newwidth)+dx] = (0xFF00 | (*(UINT8 *)input)); + } } } // make patch - newpatch = R_MaskedFlatToPatch(rawdst, newwidth, newheight, 0, 0, &size); + newpatch = (patch_t *)Picture_Convert(PICFMT_FLAT16, rawdst, PICFMT_PATCH, 0, &size, newwidth, newheight, 0, 0, 0); { newpatch->leftoffset = (newpatch->width / 2) + (leftoffset - px); newpatch->topoffset = (newpatch->height / 2) + (SHORT(patch->topoffset) - py); diff --git a/src/r_picformats.h b/src/r_picformats.h new file mode 100644 index 000000000..3ee76a92f --- /dev/null +++ b/src/r_picformats.h @@ -0,0 +1,134 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2020 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 r_picformats.h +/// \brief Patch generation. + +#ifndef __R_PICFORMATS__ +#define __R_PICFORMATS__ + +#include "r_defs.h" +#include "doomdef.h" + +typedef enum +{ + PICFMT_NONE = 0, + + // Doom formats + PICFMT_PATCH, + PICFMT_FLAT, + + // PNG + PICFMT_PNG, + + // 16bpp + PICFMT_PATCH16, + PICFMT_FLAT16, + + // 32bpp + PICFMT_PATCH32, + PICFMT_FLAT32 +} pictureformat_t; + +typedef enum +{ + PICFLAGS_XFLIP = 1, + PICFLAGS_YFLIP = 1<<1 +} pictureflags_t; + +enum +{ + PICDEPTH_NONE = 0, + PICDEPTH_8BPP = 8, + PICDEPTH_16BPP = 16, + PICDEPTH_32BPP = 32 +}; + +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags); + +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags); + +void *Picture_TextureToFlat(size_t trickytex); + +INT32 Picture_FormatBPP(pictureformat_t format); +boolean Picture_IsPatchFormat(pictureformat_t format); +boolean Picture_IsFlatFormat(pictureformat_t format); +boolean Picture_CheckIfPatch(patch_t *patch, size_t size); + +// Structs +typedef enum +{ + ROTAXIS_X, // Roll (the default) + ROTAXIS_Y, // Pitch + ROTAXIS_Z // Yaw +} rotaxis_t; + +typedef struct +{ + INT32 x, y; + rotaxis_t rotaxis; +} spriteframepivot_t; + +typedef struct +{ + spriteframepivot_t pivot[64]; + boolean available; +} spriteinfo_t; + +// Portable Network Graphics +boolean Picture_IsLumpPNG(const UINT8 *d, size_t s); +#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic + +#ifndef NO_PNG_LUMPS +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags); +boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size); +#endif + +#define PICTURE_PNG_USELOOKUP + +// SpriteInfo +extern spriteinfo_t spriteinfo[NUMSPRITES]; +void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); +void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); + +// Sprite rotation +#ifdef ROTSPRITE +INT32 R_GetRollAngle(angle_t rollangle); +void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip); +void R_FreeSingleRotSprite(spritedef_t *spritedef); +void R_FreeSkinRotSprite(size_t skinnum); +extern fixed_t rollcosang[ROTANGLES]; +extern fixed_t rollsinang[ROTANGLES]; +void R_FreeAllRotSprite(void); +#endif + +#endif // __R_PATCH__ diff --git a/src/r_plane.c b/src/r_plane.c index 92795d0fb..9d36c07dc 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -19,6 +19,7 @@ #include "p_setup.h" // levelflats #include "p_slopes.h" #include "r_data.h" +#include "r_textures.h" #include "r_local.h" #include "r_state.h" #include "r_splats.h" // faB(21jan):testing @@ -59,7 +60,7 @@ INT32 numffloors; //SoM: 3/23/2000: Boom visplane hashing routine. #define visplane_hash(picnum,lightlevel,height) \ - ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & (MAXVISPLANES-1)) + ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & VISPLANEHASHMASK) //SoM: 3/23/2000: Use boom opening limit removal size_t maxopenings; @@ -379,28 +380,30 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, lightlevel = 0; } - // New visplane algorithm uses hash table - hash = visplane_hash(picnum, lightlevel, height); - - for (check = visplanes[hash]; check; check = check->next) + if (!pfloor) { - if (check->polyobj && pfloor) - continue; - if (polyobj != check->polyobj) - continue; - if (height == check->height && picnum == check->picnum - && lightlevel == check->lightlevel - && xoff == check->xoffs && yoff == check->yoffs - && planecolormap == check->extra_colormap - && !pfloor && !check->ffloor - && check->viewx == viewx && check->viewy == viewy && check->viewz == viewz - && check->viewangle == viewangle - && check->plangle == plangle - && check->slope == slope) + hash = visplane_hash(picnum, lightlevel, height); + for (check = visplanes[hash]; check; check = check->next) { - return check; + if (polyobj != check->polyobj) + continue; + if (height == check->height && picnum == check->picnum + && lightlevel == check->lightlevel + && xoff == check->xoffs && yoff == check->yoffs + && planecolormap == check->extra_colormap + && check->viewx == viewx && check->viewy == viewy && check->viewz == viewz + && check->viewangle == viewangle + && check->plangle == plangle + && check->slope == slope) + { + return check; + } } } + else + { + hash = MAXVISPLANES - 1; + } check = new_visplane(hash); @@ -470,9 +473,17 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) } else /* Cannot use existing plane; create a new one */ { - unsigned hash = - visplane_hash(pl->picnum, pl->lightlevel, pl->height); - visplane_t *new_pl = new_visplane(hash); + visplane_t *new_pl; + if (pl->ffloor) + { + new_pl = new_visplane(MAXVISPLANES - 1); + } + else + { + unsigned hash = + visplane_hash(pl->picnum, pl->lightlevel, pl->height); + new_pl = new_visplane(hash); + } new_pl->height = pl->height; new_pl->picnum = pl->picnum; @@ -644,188 +655,6 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -// -// R_CheckPowersOfTwo -// -// Self-explanatory? -// -boolean R_CheckPowersOfTwo(void) -{ - boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); - boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); - - // Initially, the flat isn't powers-of-two-sized. - ds_powersoftwo = false; - - // But if the width and height are powers of two, - // and are EQUAL, then it's okay :] - if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) - ds_powersoftwo = true; - - // Just return ds_powersoftwo. - return ds_powersoftwo; -} - -// -// R_CheckFlatLength -// -// Determine the flat's dimensions from the lump length. -// -void R_CheckFlatLength(size_t size) -{ - switch (size) - { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - ds_flatwidth = ds_flatheight = 2048; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - ds_flatwidth = ds_flatheight = 1024; - break; - case 262144:// 512x512 lump - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - ds_flatwidth = ds_flatheight = 512; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - ds_flatwidth = ds_flatheight = 256; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - ds_flatwidth = ds_flatheight = 128; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - ds_flatwidth = ds_flatheight = 32; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - ds_flatwidth = ds_flatheight = 64; - break; - } -} - -// -// R_GenerateFlat -// -// Generate a flat from specified width and height. -// -static UINT8 *R_GenerateFlat(UINT16 width, UINT16 height) -{ - UINT8 *flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); - return flat; -} - -// -// R_GetTextureFlat -// -// Convert a texture or patch to a flat. -// -static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) -{ - UINT8 *flat; - textureflat_t *texflat = &texflats[levelflat->u.texture.num]; - patch_t *patch = NULL; - boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); - - (void)ispng; - - // Check if the texture changed. - if (leveltexture && (!texturechanged)) - { - if (texflat != NULL && texflat->flat) - { - flat = texflat->flat; - ds_flatwidth = texflat->width; - ds_flatheight = texflat->height; - texturechanged = false; - } - else - texturechanged = true; - } - - // If the texture changed, or the patch doesn't exist, convert either of them to a flat. - if (levelflat->flatpatch == NULL || texturechanged) - { - // Level texture - if (leveltexture) - { - texture_t *texture = textures[levelflat->u.texture.num]; - texflat->width = ds_flatwidth = texture->width; - texflat->height = ds_flatheight = texture->height; - - texflat->flat = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_TextureToFlat(levelflat->u.texture.num, texflat->flat); - flat = texflat->flat; - - levelflat->flatpatch = flat; - levelflat->width = ds_flatwidth; - levelflat->height = ds_flatheight; - } - // Patch (never happens yet) - else - { - patch = (patch_t *)ds_source; -#ifndef NO_PNG_LUMPS - if (ispng) - { - levelflat->flatpatch = R_PNGToFlat(&levelflat->width, &levelflat->height, ds_source, W_LumpLength(levelflat->u.flat.lumpnum)); - levelflat->topoffset = levelflat->leftoffset = 0; - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - else -#endif - { - levelflat->width = ds_flatwidth = SHORT(patch->width); - levelflat->height = ds_flatheight = SHORT(patch->height); - - levelflat->topoffset = patch->topoffset * FRACUNIT; - levelflat->leftoffset = patch->leftoffset * FRACUNIT; - - levelflat->flatpatch = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_PatchToFlat(patch, levelflat->flatpatch); - } - flat = levelflat->flatpatch; - } - } - else - { - flat = levelflat->flatpatch; - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - - xoffs += levelflat->leftoffset; - yoffs += levelflat->topoffset; - - levelflat->u.texture.lastnum = levelflat->u.texture.num; - return flat; -} - static void R_SlopeVectors(visplane_t *pl, INT32 i, float fudge) { // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! @@ -919,12 +748,11 @@ d.z = (v1.x * v2.y) - (v1.y * v2.x) void R_DrawSinglePlane(visplane_t *pl) { - UINT8 *flat; + levelflat_t *levelflat; INT32 light = 0; INT32 x; INT32 stop, angle; ffloor_t *rover; - levelflat_t *levelflat; int type; int spanfunctype = BASEDRAWFUNC; @@ -1077,30 +905,15 @@ void R_DrawSinglePlane(visplane_t *pl) case LEVELFLAT_NONE: return; case LEVELFLAT_FLAT: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + ds_source = (UINT8 *)R_GetFlat(levelflat->u.flat.lumpnum); R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); // Raw flats always have dimensions that are powers-of-two numbers. ds_powersoftwo = true; break; default: - switch (type) - { - case LEVELFLAT_TEXTURE: - /* Textures get cached differently and don't need ds_source */ - ds_source = R_GetTextureFlat(levelflat, true, false); - break; - default: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_STATIC); - flat = R_GetTextureFlat(levelflat, false, -#ifndef NO_PNG_LUMPS - ( type == LEVELFLAT_PNG ) -#else - false -#endif - ); - Z_ChangeTag(ds_source, PU_CACHE); - ds_source = flat; - } + ds_source = (UINT8 *)R_GetLevelFlat(levelflat); + if (!ds_source) + return; // Check if this texture or patch has power-of-two dimensions. if (R_CheckPowersOfTwo()) R_CheckFlatLength(ds_flatwidth * ds_flatheight); diff --git a/src/r_plane.h b/src/r_plane.h index 67fa19f38..8d5ce9ee4 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -16,9 +16,13 @@ #include "screen.h" // needs MAXVIDWIDTH/MAXVIDHEIGHT #include "r_data.h" +#include "r_textures.h" #include "p_polyobj.h" -#define MAXVISPLANES 512 +#define VISPLANEHASHBITS 9 +#define VISPLANEHASHMASK ((1<polyobj) + return (pfloor->polyobj->translucency > 0); + + // Polyobjects have no ffloors, and they're handled in the conditional above. + if (pfloor->ffloor != NULL) + return (pfloor->ffloor->flags & FF_TRANSLUCENT); + + return false; +} + // // R_RenderThickSideRange // Renders all the thick sides in the given range. @@ -1189,7 +1211,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) // R_ExpandPlaneY // -// A simple function to modify a vsplane's top and bottom for a particular column +// A simple function to modify a visplane's top and bottom for a particular column // Sort of like R_ExpandPlane in r_plane.c, except this is vertical expansion static inline void R_ExpandPlaneY(visplane_t *pl, INT32 x, INT16 top, INT16 bottom) { @@ -1199,6 +1221,14 @@ static inline void R_ExpandPlaneY(visplane_t *pl, INT32 x, INT16 top, INT16 bott if (pl->bottom[x] < bottom) pl->bottom[x] = bottom; } +// R_FFloorCanClip +// +// Returns true if a fake floor can clip a column away. +static boolean R_FFloorCanClip(visffloor_t *pfloor) +{ + return (cv_ffloorclip.value && !R_IsFFloorTranslucent(pfloor) && !pfloor->polyobj); +} + // // R_RenderSegLoop // Draws zero, one, or two textures (and possibly a masked @@ -1280,8 +1310,13 @@ static void R_RenderSegLoop (void) R_ExpandPlaneY(floorplane, rw_x, top, bottom); } + rw_floormarked = false; + rw_ceilingmarked = false; + if (numffloors) { + INT16 fftop, ffbottom; + firstseg->frontscale[rw_x] = frontscale[rw_x]; top = ceilingclip[rw_x]+1; // PRBoom bottom = floorclip[rw_x]-1; // PRBoom @@ -1312,8 +1347,30 @@ static void R_RenderSegLoop (void) { if (top_w <= bottom_w) { - ffloor[i].plane->top[rw_x] = (INT16)top_w; - ffloor[i].plane->bottom[rw_x] = (INT16)bottom_w; + fftop = (INT16)top_w; + ffbottom = (INT16)bottom_w; + + ffloor[i].plane->top[rw_x] = fftop; + ffloor[i].plane->bottom[rw_x] = ffbottom; + + // Lactozilla: Cull part of the column by the 3D floor if it can't be seen + // "bottom" is the top pixel of the floor column + if (ffbottom >= bottom-1 && R_FFloorCanClip(&ffloor[i])) + { + rw_floormarked = true; + floorclip[rw_x] = fftop; + if (yh > fftop) + yh = fftop; + + if (markfloor && floorplane) + floorplane->top[rw_x] = bottom; + + if (rw_silhouette) + { + (*rw_silhouette) |= SIL_BOTTOM; + (*rw_bsilheight) = INT32_MAX; + } + } } } } @@ -1338,8 +1395,30 @@ static void R_RenderSegLoop (void) { if (top_w <= bottom_w) { - ffloor[i].plane->top[rw_x] = (INT16)top_w; - ffloor[i].plane->bottom[rw_x] = (INT16)bottom_w; + fftop = (INT16)top_w; + ffbottom = (INT16)bottom_w; + + ffloor[i].plane->top[rw_x] = fftop; + ffloor[i].plane->bottom[rw_x] = ffbottom; + + // Lactozilla: Cull part of the column by the 3D floor if it can't be seen + // "top" is the height of the ceiling column + if (fftop <= top+1 && R_FFloorCanClip(&ffloor[i])) + { + rw_ceilingmarked = true; + ceilingclip[rw_x] = ffbottom; + if (yl < ffbottom) + yl = ffbottom; + + if (markceiling && ceilingplane) + ceilingplane->bottom[rw_x] = top; + + if (rw_silhouette) + { + (*rw_silhouette) |= SIL_TOP; + (*rw_tsilheight) = INT32_MIN; + } + } } } } @@ -1445,20 +1524,25 @@ static void R_RenderSegLoop (void) // dont draw anything more for this column, since // a midtexture blocks the view - ceilingclip[rw_x] = (INT16)viewheight; - floorclip[rw_x] = -1; + if (!rw_ceilingmarked) + ceilingclip[rw_x] = (INT16)viewheight; + if (!rw_floormarked) + floorclip[rw_x] = -1; } else { // note: don't use min/max macros, since casting from INT32 to INT16 is involved here - if (markceiling) + if (markceiling && (!rw_ceilingmarked)) ceilingclip[rw_x] = (yl >= 0) ? ((yl > viewheight) ? (INT16)viewheight : (INT16)((INT16)yl - 1)) : -1; - if (markfloor) + if (markfloor && (!rw_floormarked)) floorclip[rw_x] = (yh < viewheight) ? ((yh < -1) ? -1 : (INT16)((INT16)yh + 1)) : (INT16)viewheight; } } else { + INT16 topclip = (yl >= 0) ? ((yl > viewheight) ? (INT16)viewheight : (INT16)((INT16)yl - 1)) : -1; + INT16 bottomclip = (yh < viewheight) ? ((yh < -1) ? -1 : (INT16)((INT16)yh + 1)) : (INT16)viewheight; + // two sided line if (toptexture) { @@ -1472,7 +1556,10 @@ static void R_RenderSegLoop (void) if (mid >= yl) // back ceiling lower than front ceiling ? { if (yl >= viewheight) // entirely off bottom of screen - ceilingclip[rw_x] = (INT16)viewheight; + { + if (!rw_ceilingmarked) + ceilingclip[rw_x] = (INT16)viewheight; + } else if (mid >= 0) // safe to draw top texture { dc_yl = yl; @@ -1483,14 +1570,14 @@ static void R_RenderSegLoop (void) colfunc(); ceilingclip[rw_x] = (INT16)mid; } - else // entirely off top of screen + else if (!rw_ceilingmarked) // entirely off top of screen ceilingclip[rw_x] = -1; } - else - ceilingclip[rw_x] = (yl >= 0) ? ((yl > viewheight) ? (INT16)viewheight : (INT16)((INT16)yl - 1)) : -1; + else if (!rw_ceilingmarked) + ceilingclip[rw_x] = topclip; } - else if (markceiling) // no top wall - ceilingclip[rw_x] = (yl >= 0) ? ((yl > viewheight) ? (INT16)viewheight : (INT16)((INT16)yl - 1)) : -1; + else if (markceiling && (!rw_ceilingmarked)) // no top wall + ceilingclip[rw_x] = topclip; if (bottomtexture) { @@ -1505,7 +1592,10 @@ static void R_RenderSegLoop (void) if (mid <= yh) // back floor higher than front floor ? { if (yh < 0) // entirely off top of screen - floorclip[rw_x] = -1; + { + if (!rw_floormarked) + floorclip[rw_x] = -1; + } else if (mid < viewheight) // safe to draw bottom texture { dc_yl = mid; @@ -1517,14 +1607,14 @@ static void R_RenderSegLoop (void) colfunc(); floorclip[rw_x] = (INT16)mid; } - else // entirely off bottom of screen + else if (!rw_floormarked) // entirely off bottom of screen floorclip[rw_x] = (INT16)viewheight; } - else - floorclip[rw_x] = (yh < viewheight) ? ((yh < -1) ? -1 : (INT16)((INT16)yh + 1)) : (INT16)viewheight; + else if (!rw_floormarked) + floorclip[rw_x] = bottomclip; } - else if (markfloor) // no bottom wall - floorclip[rw_x] = (yh < viewheight) ? ((yh < -1) ? -1 : (INT16)((INT16)yh + 1)) : (INT16)viewheight; + else if (markfloor && (!rw_floormarked)) // no bottom wall + floorclip[rw_x] = bottomclip; } if (maskedtexture || numthicksides) @@ -2793,6 +2883,10 @@ void R_StoreWallRange(INT32 start, INT32 stop) } } + rw_silhouette = &(ds_p->silhouette); + rw_tsilheight = &(ds_p->tsilheight); + rw_bsilheight = &(ds_p->bsilheight); + #ifdef WALLSPLATS if (linedef->splats && cv_splats.value) { diff --git a/src/r_skins.c b/src/r_skins.c index a4fe1982f..25904e95e 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -286,6 +286,11 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) else if (playernum == secondarydisplayplayer) CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); player->skincolor = newcolor = skin->prefcolor; + if (player->bot && botingame) + { + botskin = (UINT8)(skinnum + 1); + botcolor = skin->prefcolor; + } } if (player->followmobj) diff --git a/src/r_skins.h b/src/r_skins.h index 45c90bdb4..04ce459a3 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -17,7 +17,7 @@ #include "info.h" #include "sounds.h" #include "d_player.h" // skinflags -#include "r_patch.h" // spriteinfo_t +#include "r_picformats.h" // spriteinfo_t #include "r_defs.h" // spritedef_t /// Defaults diff --git a/src/r_textures.c b/src/r_textures.c new file mode 100644 index 000000000..a34c29c72 --- /dev/null +++ b/src/r_textures.c @@ -0,0 +1,1648 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 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 r_textures.c +/// \brief Texture generation. + +#include "doomdef.h" +#include "g_game.h" +#include "i_video.h" +#include "r_local.h" +#include "r_sky.h" +#include "p_local.h" +#include "m_misc.h" +#include "r_data.h" +#include "r_textures.h" +#include "r_picformats.h" +#include "w_wad.h" +#include "z_zone.h" +#include "p_setup.h" // levelflats +#include "byteptr.h" +#include "dehacked.h" + +// I don't know what this is even for, but r_data.c had it. +#ifdef _WIN32 +#include // alloca(sizeof) +#endif + +#ifdef HWRENDER +#include "hardware/hw_main.h" // HWR_LoadTextures +#endif + +#include + +// +// TEXTURE_T CACHING +// When a texture is first needed, it counts the number of composite columns +// required in the texture and allocates space for a column directory and +// any new columns. +// The directory will simply point inside other patches if there is only one +// patch in a given column, but any columns with multiple patches will have +// new column_ts generated. +// + +INT32 numtextures = 0; // total number of textures found, +// size of following tables + +texture_t **textures = NULL; +UINT32 **texturecolumnofs; // column offset lookup table for each texture +UINT8 **texturecache; // graphics data for each generated full-size texture + +INT32 *texturewidth; +fixed_t *textureheight; // needed for texture pegging + +INT32 *texturetranslation; + +// Painfully simple texture id cacheing to make maps load faster. :3 +static struct { + char name[9]; + INT32 id; +} *tidcache = NULL; +static INT32 tidcachelen = 0; + +// +// MAPTEXTURE_T CACHING +// When a texture is first needed, it counts the number of composite columns +// required in the texture and allocates space for a column directory and +// any new columns. +// The directory will simply point inside other patches if there is only one +// patch in a given column, but any columns with multiple patches will have +// new column_ts generated. +// + +// +// R_DrawColumnInCache +// Clip and draw a column from a patch into a cached post. +// +static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + source = (UINT8 *)patch + 3; + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source -= position; // start further down the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + if (count > 0) + M_Memcpy(cache + position, source, count); + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawFlippedColumnInCache +// Similar to R_DrawColumnInCache; it draws the column inverted, however. +// +static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source) + *dest++ = *source; + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendColumnInCache +// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). +// +static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + source = (UINT8 *)patch + 3; + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source -= position; // start further down the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; source++, dest++) + if (*source != 0xFF) + *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendFlippedColumnInCache +// Similar to the one above except that the column is inverted. +// +static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source, dest++) + if (*source != 0xFF) + *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_GenerateTexture +// +// Allocate space for full size texture, either single patch or 'composite' +// Build the full textures from patches. +// The texture caching system is a little more hungry of memory, but has +// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. +// +// This is not optimised, but it's supposed to be executed only once +// per level, when enough memory is available. +// +UINT8 *R_GenerateTexture(size_t texnum) +{ + UINT8 *block; + UINT8 *blocktex; + texture_t *texture; + texpatch_t *patch; + patch_t *realpatch; + UINT8 *pdata; + int x, x1, x2, i, width, height; + size_t blocksize; + column_t *patchcol; + UINT8 *colofs; + + UINT16 wadnum; + lumpnum_t lumpnum; + size_t lumplength; + + I_Assert(texnum <= (size_t)numtextures); + texture = textures[texnum]; + I_Assert(texture != NULL); + + // allocate texture column offset lookup + + // single-patch textures can have holes in them and may be used on + // 2sided lines so they need to be kept in 'packed' format + // BUT this is wrong for skies and walls with over 255 pixels, + // so check if there's holes and if not strip the posts. + if (texture->patchcount == 1) + { + boolean holey = false; + patch = texture->patches; + + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + realpatch = (patch_t *)pdata; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + goto multipatch; +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + goto multipatch; +#endif + + // Check the patch for holes. + if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) + holey = true; + colofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width && !holey; x++) + { + column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); + INT32 topdelta, prevdelta = -1, y = 0; + while (col->topdelta != 0xff) + { + topdelta = col->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + if (topdelta > y) + break; + y = topdelta + col->length + 1; + col = (column_t *)((UINT8 *)col + col->length + 4); + } + if (y < texture->height) + holey = true; // this texture is HOLEy! D: + } + + // If the patch uses transparency, we have to save it this way. + if (holey) + { + texture->holes = true; + texture->flip = patch->flip; + blocksize = lumplength; + block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function + &texturecache[texnum]); + M_Memcpy(block, realpatch, blocksize); + texturememory += blocksize; + + // use the patch's column lookup + colofs = (block + 8); + texturecolumnofs[texnum] = (UINT32 *)colofs; + blocktex = block; + if (patch->flip & 1) // flip the patch horizontally + { + UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture + } + // we can't as easily flip the patch vertically sadly though, + // we have wait until the texture itself is drawn to do that + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); + goto done; + } + + // Otherwise, do multipatch format. + } + + // multi-patch textures (or 'composite') + multipatch: + texture->holes = false; + texture->flip = 0; + blocksize = (texture->width * 4) + (texture->width * texture->height); + texturememory += blocksize; + block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); + + memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack + + // columns lookup table + colofs = block; + texturecolumnofs[texnum] = (UINT32 *)colofs; + + // texture data after the lookup table + blocktex = block + (texture->width*4); + + // Composite the columns together. + for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) + { + boolean dealloc = true; + static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. + if (patch->style != AST_COPY) + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; + else + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; + + wadnum = patch->wad; + lumpnum = patch->lump; + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = (patch_t *)pdata; + dealloc = true; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); + else +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); + else +#endif + { + (void)lumplength; + dealloc = false; + } + + x1 = patch->originx; + width = SHORT(realpatch->width); + height = SHORT(realpatch->height); + x2 = x1 + width; + + if (x1 > texture->width || x2 < 0) + { + if (dealloc) + Z_Free(realpatch); + continue; // patch not located within texture's x bounds, ignore + } + + if (patch->originy > texture->height || (patch->originy + height) < 0) + { + if (dealloc) + Z_Free(realpatch); + continue; // patch not located within texture's y bounds, ignore + } + + // patch is actually inside the texture! + // now check if texture is partly off-screen and adjust accordingly + + // left edge + if (x1 < 0) + x = 0; + else + x = x1; + + // right edge + if (x2 > texture->width) + x2 = texture->width; + + for (; x < x2; x++) + { + if (patch->flip & 1) + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); + else + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); + + // generate column ofset lookup + *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); + ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); + } + + if (dealloc) + Z_Free(realpatch); + } + +done: + // Now that the texture has been built in column cache, it is purgable from zone memory. + Z_ChangeTag(block, PU_CACHE); + return blocktex; +} + +// +// R_GenerateTextureAsFlat +// +// Generates a flat picture for a texture. +// +UINT8 *R_GenerateTextureAsFlat(size_t texnum) +{ + texture_t *texture = textures[texnum]; + UINT8 *converted = NULL; + size_t size = (texture->width * texture->height); + + // The flat picture for this texture was not generated yet. + if (!texture->flat) + { + // Well, let's do it now, then. + texture->flat = Z_Malloc(size, PU_STATIC, NULL); + + // Picture_TextureToFlat handles everything for us. + converted = (UINT8 *)Picture_TextureToFlat(texnum); + M_Memcpy(texture->flat, converted, size); + Z_Free(converted); + } + + return texture->flat; +} + +// +// R_GetTextureNum +// +// Returns the actual texture id that we should use. +// This can either be texnum, the current frame for texnum's anim (if animated), +// or 0 if not valid. +// +INT32 R_GetTextureNum(INT32 texnum) +{ + if (texnum < 0 || texnum >= numtextures) + return 0; + return texturetranslation[texnum]; +} + +// +// R_CheckTextureCache +// +// Use this if you need to make sure the texture is cached before R_GetColumn calls +// e.g.: midtextures and FOF walls +// +void R_CheckTextureCache(INT32 tex) +{ + if (!texturecache[tex]) + R_GenerateTexture(tex); +} + +// +// R_GetColumn +// +UINT8 *R_GetColumn(fixed_t tex, INT32 col) +{ + UINT8 *data; + INT32 width = texturewidth[tex]; + + if (width & (width - 1)) + col = (UINT32)col % width; + else + col &= (width - 1); + + data = texturecache[tex]; + if (!data) + data = R_GenerateTexture(tex); + + return data + LONG(texturecolumnofs[tex][col]); +} + +void *R_GetFlat(lumpnum_t flatlumpnum) +{ + return W_CacheLumpNum(flatlumpnum, PU_CACHE); +} + +// +// R_GetLevelFlat +// +// If needed, convert a texture or patch to a flat. +// +void *R_GetLevelFlat(levelflat_t *levelflat) +{ + boolean isleveltexture = (levelflat->type == LEVELFLAT_TEXTURE); + texture_t *texture = (isleveltexture ? textures[levelflat->u.texture.num] : NULL); + boolean texturechanged = (isleveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); + UINT8 *flatdata = NULL; + + // Check if the texture changed. + if (isleveltexture && (!texturechanged)) + { + if (texture->flat) + { + flatdata = texture->flat; + ds_flatwidth = texture->width; + ds_flatheight = texture->height; + texturechanged = false; + } + else + texturechanged = true; + } + + // If the texture changed, or the flat wasn't generated, convert. + if (levelflat->picture == NULL || texturechanged) + { + // Level texture + if (isleveltexture) + { + levelflat->picture = R_GenerateTextureAsFlat(levelflat->u.texture.num); + ds_flatwidth = levelflat->width = texture->width; + ds_flatheight = levelflat->height = texture->height; + } + else + { +#ifndef NO_PNG_LUMPS + if (levelflat->type == LEVELFLAT_PNG) + { + INT32 pngwidth, pngheight; + + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + else +#endif + if (levelflat->type == LEVELFLAT_PATCH) + { + UINT8 *converted; + size_t size; + patch_t *patch = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + + levelflat->width = ds_flatwidth = SHORT(patch->width); + levelflat->height = ds_flatheight = SHORT(patch->height); + + levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); + converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); + M_Memcpy(levelflat->picture, converted, size); + Z_Free(converted); + } + } + } + else + { + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + + levelflat->u.texture.lastnum = levelflat->u.texture.num; + + if (flatdata == NULL) + flatdata = levelflat->picture; + return flatdata; +} + +// +// R_CheckPowersOfTwo +// +// Self-explanatory? +// +boolean R_CheckPowersOfTwo(void) +{ + boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); + boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); + + // Initially, the flat isn't powers-of-two-sized. + ds_powersoftwo = false; + + // But if the width and height are powers of two, + // and are EQUAL, then it's okay :] + if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) + ds_powersoftwo = true; + + // Just return ds_powersoftwo. + return ds_powersoftwo; +} + +// +// R_CheckFlatLength +// +// Determine the flat's dimensions from its lump length. +// +void R_CheckFlatLength(size_t size) +{ + switch (size) + { + case 4194304: // 2048x2048 lump + nflatmask = 0x3FF800; + nflatxshift = 21; + nflatyshift = 10; + nflatshiftup = 5; + ds_flatwidth = ds_flatheight = 2048; + break; + case 1048576: // 1024x1024 lump + nflatmask = 0xFFC00; + nflatxshift = 22; + nflatyshift = 12; + nflatshiftup = 6; + ds_flatwidth = ds_flatheight = 1024; + break; + case 262144:// 512x512 lump + nflatmask = 0x3FE00; + nflatxshift = 23; + nflatyshift = 14; + nflatshiftup = 7; + ds_flatwidth = ds_flatheight = 512; + break; + case 65536: // 256x256 lump + nflatmask = 0xFF00; + nflatxshift = 24; + nflatyshift = 16; + nflatshiftup = 8; + ds_flatwidth = ds_flatheight = 256; + break; + case 16384: // 128x128 lump + nflatmask = 0x3F80; + nflatxshift = 25; + nflatyshift = 18; + nflatshiftup = 9; + ds_flatwidth = ds_flatheight = 128; + break; + case 1024: // 32x32 lump + nflatmask = 0x3E0; + nflatxshift = 27; + nflatyshift = 22; + nflatshiftup = 11; + ds_flatwidth = ds_flatheight = 32; + break; + default: // 64x64 lump + nflatmask = 0xFC0; + nflatxshift = 26; + nflatyshift = 20; + nflatshiftup = 10; + ds_flatwidth = ds_flatheight = 64; + break; + } +} + +// +// Empty the texture cache (used for load wad at runtime) +// +void R_FlushTextureCache(void) +{ + INT32 i; + + if (numtextures) + for (i = 0; i < numtextures; i++) + Z_Free(texturecache[i]); +} + +// Need these prototypes for later; defining them here instead of r_textures.h so they're "private" +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); + +#ifdef WALLFLATS +static INT32 +Rloadflats (INT32 i, INT32 w) +{ + UINT16 j; + UINT16 texstart, texend; + texture_t *texture; + texpatch_t *patch; + + // Yes + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT8 *flatlump; + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; + size_t lumplength; + size_t flatsize = 0; + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + + switch (lumplength) + { + case 4194304: // 2048x2048 lump + flatsize = 2048; + break; + case 1048576: // 1024x1024 lump + flatsize = 1024; + break; + case 262144:// 512x512 lump + flatsize = 512; + break; + case 65536: // 256x256 lump + flatsize = 256; + break; + case 16384: // 128x128 lump + flatsize = 128; + break; + case 1024: // 32x32 lump + flatsize = 32; + break; + default: // 64x64 lump + flatsize = 64; + break; + } + + //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) + { + INT32 width, height; + Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength); + texture->width = (INT16)width; + texture->height = (INT16)height; + } + else +#endif + texture->width = texture->height = flatsize; + + texture->type = TEXTURETYPE_FLAT; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(flatlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + } + + return i; +} +#endif/*WALLFLATS*/ + +#define TX_START "TX_START" +#define TX_END "TX_END" + +static INT32 +Rloadtextures (INT32 i, INT32 w) +{ + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + texture_t *texture; + patch_t *patchlump; + texpatch_t *patch; + + // Get the lump numbers for the markers in the WAD, if they exist. + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + } + else + { + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + if (texturesLumpPos != INT16_MAX) + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; +#ifndef NO_PNG_LUMPS + size_t lumplength; +#endif + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS + lumplength = W_LumpLengthPwad(wadnum, lumpnum); +#endif + + //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) + { + INT32 width, height; + Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength); + texture->width = (INT16)width; + texture->height = (INT16)height; + } + else +#endif + { + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); + } + + texture->type = TEXTURETYPE_SINGLEPATCH; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(patchlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + } + + return i; +} + +// +// R_LoadTextures +// Initializes the texture list with the textures from the world map. +// +void R_LoadTextures(void) +{ + INT32 i, w; + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + + // Free previous memory before numtextures change. + if (numtextures) + { + for (i = 0; i < numtextures; i++) + { + Z_Free(textures[i]); + Z_Free(texturecache[i]); + } + Z_Free(texturetranslation); + Z_Free(textures); + } + + // Load patches and textures. + + // Get the number of textures to check. + // NOTE: Make SURE the system does not process + // the markers. + // This system will allocate memory for all duplicate/patched textures even if it never uses them, + // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. + for (w = 0, numtextures = 0; w < numwadfiles; w++) + { +#ifdef WALLFLATS + // Count flats + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between F_START and F_END + { + numtextures += (UINT32)(texend - texstart); + } + } +#endif/*WALLFLATS*/ + + // Count the textures from TEXTURES lumps + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + + // Count single-patch textures + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) + continue; + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between TX_START and TX_END + { + numtextures += (UINT32)(texend - texstart); + } + } + + // If no textures found by this point, bomb out + if (!numtextures) + I_Error("No textures detected in any WADs!\n"); + + // Allocate memory and initialize to 0 for all the textures we are initialising. + // There are actually 5 buffers allocated in one for convenience. + textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); + + // Allocate texture column offset table. + texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); + // Allocate texture referencing cache. + texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); + // Allocate texture width table. + texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); + // Allocate texture height table. + textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); + // Create translation table for global animation. + texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + for (i = 0, w = 0; w < numwadfiles; w++) + { +#ifdef WALLFLATS + i = Rloadflats(i, w); +#endif + i = Rloadtextures(i, w); + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_LoadTextures(numtextures); +#endif +} + +static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) +{ + char *texturesToken; + size_t texturesTokenLength; + char *endPos; + char *patchName = NULL; + INT16 patchXPos; + INT16 patchYPos; + UINT8 flip = 0; + UINT8 alpha = 255; + enum patchalphastyle style = AST_COPY; + texpatch_t *resultPatch = NULL; + lumpnum_t patchLumpNum; + + // Patch identifier + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch name should be"); + } + texturesTokenLength = strlen(texturesToken); + if (texturesTokenLength>8) + { + I_Error("Error parsing TEXTURES lump: Patch name \"%s\" exceeds 8 characters",texturesToken); + } + else + { + if (patchName != NULL) + { + Z_Free(patchName); + } + patchName = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL); + M_Memcpy(patchName,texturesToken,texturesTokenLength*sizeof(char)); + patchName[texturesTokenLength] = '\0'; + } + + // Comma 1 + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after \"%s\"'s patch name should be",patchName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after %s's patch name, got \"%s\"",patchName,texturesToken); + } + + // XPos + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s x coordinate should be",patchName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + patchXPos = strtol(texturesToken,&endPos,10); + (void)patchXPos; //unused for now + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + ) + { + I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); + } + + // Comma 2 + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after patch \"%s\"'s x coordinate should be",patchName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); + } + + // YPos + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s y coordinate should be",patchName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + patchYPos = strtol(texturesToken,&endPos,10); + (void)patchYPos; //unused for now + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + ) + { + I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s y coordinate, got \"%s\"",patchName,texturesToken); + } + Z_Free(texturesToken); + + // Patch parameters block (OPTIONAL) + // added by Monster Iestyn (22/10/16) + + // Left Curly Brace + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + ; // move on and ignore, R_ParseTextures will deal with this + else + { + if (strcmp(texturesToken,"{")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); + } + while (strcmp(texturesToken,"}")!=0) + { + if (stricmp(texturesToken, "ALPHA")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + alpha = 255*strtof(texturesToken, NULL); + } + else if (stricmp(texturesToken, "STYLE")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (stricmp(texturesToken, "TRANSLUCENT")==0) + style = AST_TRANSLUCENT; + else if (stricmp(texturesToken, "ADD")==0) + style = AST_ADD; + else if (stricmp(texturesToken, "SUBTRACT")==0) + style = AST_SUBTRACT; + else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) + style = AST_REVERSESUBTRACT; + else if (stricmp(texturesToken, "MODULATE")==0) + style = AST_MODULATE; + } + else if (stricmp(texturesToken, "FLIPX")==0) + flip |= 1; + else if (stricmp(texturesToken, "FLIPY")==0) + flip |= 2; + Z_Free(texturesToken); + + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); + } + } + } + else + { + // this is not what we wanted... + // undo last read so R_ParseTextures can re-get the token for its own purposes + M_UnGetToken(); + } + Z_Free(texturesToken); + } + + if (actuallyLoadPatch == true) + { + // Check lump exists + patchLumpNum = W_GetNumForName(patchName); + // If so, allocate memory for texpatch_t and fill 'er up + resultPatch = (texpatch_t *)Z_Malloc(sizeof(texpatch_t),PU_STATIC,NULL); + resultPatch->originx = patchXPos; + resultPatch->originy = patchYPos; + resultPatch->lump = patchLumpNum & 65535; + resultPatch->wad = patchLumpNum>>16; + resultPatch->flip = flip; + resultPatch->alpha = alpha; + resultPatch->style = style; + // Clean up a little after ourselves + Z_Free(patchName); + // Then return it + return resultPatch; + } + else + { + Z_Free(patchName); + return NULL; + } +} + +static texture_t *R_ParseTexture(boolean actuallyLoadTexture) +{ + char *texturesToken; + size_t texturesTokenLength; + char *endPos; + INT32 newTextureWidth; + INT32 newTextureHeight; + texture_t *resultTexture = NULL; + texpatch_t *newPatch; + char newTextureName[9]; // no longer dynamically allocated + + // Texture name + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture name should be"); + } + texturesTokenLength = strlen(texturesToken); + if (texturesTokenLength>8) + { + I_Error("Error parsing TEXTURES lump: Texture name \"%s\" exceeds 8 characters",texturesToken); + } + else + { + memset(&newTextureName, 0, 9); + M_Memcpy(newTextureName, texturesToken, texturesTokenLength); + // ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer + strupr(newTextureName); // Just do this now so we don't have to worry about it + } + Z_Free(texturesToken); + + // Comma 1 + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s name should be",newTextureName); + } + else if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s name, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Width + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s width should be",newTextureName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + newTextureWidth = strtol(texturesToken,&endPos,10); + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + || newTextureWidth < 0) // Number is not positive + { + I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Comma 2 + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s width should be",newTextureName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Height + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s height should be",newTextureName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + newTextureHeight = strtol(texturesToken,&endPos,10); + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + || newTextureHeight < 0) // Number is not positive + { + I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s height, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Left Curly Brace + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \"%s\" should be",newTextureName); + } + if (strcmp(texturesToken,"{")==0) + { + if (actuallyLoadTexture) + { + // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. + resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL); + M_Memcpy(resultTexture->name, newTextureName, 8); + resultTexture->width = newTextureWidth; + resultTexture->height = newTextureHeight; + resultTexture->type = TEXTURETYPE_COMPOSITE; + } + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \"%s\" should be",newTextureName); + } + while (strcmp(texturesToken,"}")!=0) + { + if (stricmp(texturesToken, "PATCH")==0) + { + Z_Free(texturesToken); + if (resultTexture) + { + // Get that new patch + newPatch = R_ParsePatch(true); + // Make room for the new patch + resultTexture = Z_Realloc(resultTexture, sizeof(texture_t) + (resultTexture->patchcount+1)*sizeof(texpatch_t), PU_STATIC, NULL); + // Populate the uninitialized values in the new patch entry of our array + M_Memcpy(&resultTexture->patches[resultTexture->patchcount], newPatch, sizeof(texpatch_t)); + // Account for the new number of patches in the texture + resultTexture->patchcount++; + // Then free up the memory assigned to R_ParsePatch, as it's unneeded now + Z_Free(newPatch); + } + else + { + R_ParsePatch(false); + } + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"PATCH\" in texture \"%s\", got \"%s\"",newTextureName,texturesToken); + } + + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \"%s\" should be",newTextureName); + } + } + if (resultTexture && resultTexture->patchcount == 0) + { + I_Error("Error parsing TEXTURES lump: Texture \"%s\" must have at least one patch",newTextureName); + } + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"{\" for texture \"%s\", got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + if (actuallyLoadTexture) return resultTexture; + else return NULL; +} + +// Parses the TEXTURES lump... but just to count the number of textures. +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) +{ + char *texturesLump; + size_t texturesLumpLength; + char *texturesText; + UINT32 numTexturesInLump = 0; + char *texturesToken; + + // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll + // need to make a space of memory where I can ensure that it will terminate + // correctly. Start by loading the relevant data from the WAD. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // If that didn't exist, we have nothing to do here. + if (texturesLump == NULL) return 0; + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); + // Now move the contents of the lump into this new location. + memmove(texturesText,texturesLump,texturesLumpLength); + // Make damn well sure the last character in our new memory location is \0. + texturesText[texturesLumpLength] = '\0'; + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(texturesLump); + + texturesToken = M_GetToken(texturesText); + while (texturesToken != NULL) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + numTexturesInLump++; + Z_Free(texturesToken); + R_ParseTexture(false); + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); + + return numTexturesInLump; +} + +// Parses the TEXTURES lump... for real, this time. +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) +{ + char *texturesLump; + size_t texturesLumpLength; + char *texturesText; + char *texturesToken; + texture_t *newTexture; + + I_Assert(texindex != NULL); + + // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll + // need to make a space of memory where I can ensure that it will terminate + // correctly. Start by loading the relevant data from the WAD. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // If that didn't exist, we have nothing to do here. + if (texturesLump == NULL) return; + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); + // Now move the contents of the lump into this new location. + memmove(texturesText,texturesLump,texturesLumpLength); + // Make damn well sure the last character in our new memory location is \0. + texturesText[texturesLumpLength] = '\0'; + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(texturesLump); + + texturesToken = M_GetToken(texturesText); + while (texturesToken != NULL) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + Z_Free(texturesToken); + // Get the new texture + newTexture = R_ParseTexture(true); + // Store the new texture + textures[*texindex] = newTexture; + texturewidth[*texindex] = newTexture->width; + textureheight[*texindex] = newTexture->height << FRACBITS; + // Increment i back in R_LoadTextures() + (*texindex)++; + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); +} + +// Search for flat name. +lumpnum_t R_GetFlatNumForName(const char *name) +{ + INT32 i; + lumpnum_t lump; + lumpnum_t start; + lumpnum_t end; + + // Scan wad files backwards so patched flats take preference. + for (i = numwadfiles - 1; i >= 0; i--) + { + switch (wadfiles[i]->type) + { + case RET_WAD: + if ((start = W_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX) + { + if ((start = W_CheckNumForMarkerStartPwad("FF_START", (UINT16)i, 0)) == INT16_MAX) + continue; + else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX) + continue; + } + else + if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX) + continue; + break; + case RET_PK3: + if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX) + continue; + break; + default: + continue; + } + + // Now find lump with specified name in that range. + lump = W_CheckNumForNamePwad(name, (UINT16)i, start); + if (lump < end) + { + lump += (i<<16); // found it, in our constraints + break; + } + lump = LUMPERROR; + } + + return lump; +} + +void R_ClearTextureNumCache(boolean btell) +{ + if (tidcache) + Z_Free(tidcache); + tidcache = NULL; + if (btell) + CONS_Debug(DBG_SETUP, "Fun Fact: There are %d textures used in this map.\n", tidcachelen); + tidcachelen = 0; +} + +// +// R_CheckTextureNumForName +// +// Check whether texture is available. Filter out NoTexture indicator. +// +INT32 R_CheckTextureNumForName(const char *name) +{ + INT32 i; + + // "NoTexture" marker. + if (name[0] == '-') + return 0; + + for (i = 0; i < tidcachelen; i++) + if (!strncasecmp(tidcache[i].name, name, 8)) + return tidcache[i].id; + + // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier + //for (i = 0; i < numtextures; i++) <- old + for (i = (numtextures - 1); i >= 0; i--) // <- new + if (!strncasecmp(textures[i]->name, name, 8)) + { + tidcachelen++; + Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); + strncpy(tidcache[tidcachelen-1].name, name, 8); + tidcache[tidcachelen-1].name[8] = '\0'; +#ifndef ZDEBUG + CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); +#endif + tidcache[tidcachelen-1].id = i; + return i; + } + + return -1; +} + +// +// R_TextureNumForName +// +// Calls R_CheckTextureNumForName, aborts with error message. +// +INT32 R_TextureNumForName(const char *name) +{ + const INT32 i = R_CheckTextureNumForName(name); + + if (i == -1) + { + static INT32 redwall = -2; + CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name); + if (redwall == -2) + redwall = R_CheckTextureNumForName("REDWALL"); + if (redwall != -1) + return redwall; + return 1; + } + return i; +} diff --git a/src/r_textures.h b/src/r_textures.h new file mode 100644 index 000000000..74a94a9ed --- /dev/null +++ b/src/r_textures.h @@ -0,0 +1,103 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 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 r_textures.h +/// \brief Texture generation. + +#ifndef __R_TEXTURES__ +#define __R_TEXTURES__ + +#include "r_defs.h" +#include "r_state.h" +#include "p_setup.h" // levelflats +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, +// basically a rectangular area within +// the texture rectangle. +typedef struct +{ + // Block origin (always UL), which has already accounted for the internal origin of the patch. + INT16 originx, originy; + UINT16 wad, lump; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + UINT8 alpha; // Translucency value + enum patchalphastyle style; +} texpatch_t; + +// texture type +enum +{ + TEXTURETYPE_UNKNOWN, + TEXTURETYPE_SINGLEPATCH, + TEXTURETYPE_COMPOSITE, +#ifdef WALLFLATS + TEXTURETYPE_FLAT, +#endif +}; + +// A texture_t describes a rectangular texture, +// which is composed of one or more texpatch_t structures +// that arrange graphic patches. +typedef struct +{ + // Keep name for switch changing, etc. + char name[8]; + UINT8 type; // TEXTURETYPE_ + INT16 width, height; + boolean holes; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + void *flat; // The texture, as a flat. + + // All the patches[patchcount] are drawn back to front into the cached texture. + INT16 patchcount; + texpatch_t patches[0]; +} texture_t; + +// all loaded and prepared textures from the start of the game +extern texture_t **textures; + +extern INT32 *texturewidth; +extern fixed_t *textureheight; // needed for texture pegging + +extern UINT32 **texturecolumnofs; // column offset lookup table for each texture +extern UINT8 **texturecache; // graphics data for each generated full-size texture + +// Load TEXTURES definitions, create lookup tables +void R_LoadTextures(void); +void R_FlushTextureCache(void); + +// Texture generation +UINT8 *R_GenerateTexture(size_t texnum); +UINT8 *R_GenerateTextureAsFlat(size_t texnum); +INT32 R_GetTextureNum(INT32 texnum); +void R_CheckTextureCache(INT32 tex); +void R_ClearTextureNumCache(boolean btell); + +// Retrieve texture data. +void *R_GetLevelFlat(levelflat_t *levelflat); +UINT8 *R_GetColumn(fixed_t tex, INT32 col); +void *R_GetFlat(lumpnum_t flatnum); + +boolean R_CheckPowersOfTwo(void); +void R_CheckFlatLength(size_t size); + +// Returns the texture number for the texture name. +INT32 R_TextureNumForName(const char *name); +INT32 R_CheckTextureNumForName(const char *name); +lumpnum_t R_GetFlatNumForName(const char *name); + +extern INT32 numtextures; + +#endif diff --git a/src/r_things.c b/src/r_things.c index c0795acd5..cc205f9ea 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -24,7 +24,7 @@ #include "i_video.h" // rendermode #include "i_system.h" #include "r_things.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_plane.h" #include "r_portal.h" #include "p_tick.h" @@ -259,6 +259,12 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 { if (memcmp(lumpinfo[l].name,sprname,4)==0) { + INT32 width, height; + INT16 topoffset, leftoffset; +#ifndef NO_PNG_LUMPS + boolean isPNG = false; +#endif + frame = R_Char2Frame(lumpinfo[l].name[4]); rotation = R_Char2Rotation(lumpinfo[l].name[5]); @@ -274,24 +280,35 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 // store sprite info in lookup tables //FIXME : numspritelumps do not duplicate sprite replacements - W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof (patch_t), 0); + #ifndef NO_PNG_LUMPS { patch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); size_t len = W_LumpLengthPwad(wadnum, l); - // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)png, len)) + + if (Picture_IsLumpPNG((UINT8 *)png, len)) { - png = R_PNGToPatch((UINT8 *)png, len, NULL); - M_Memcpy(&patch, png, sizeof(INT16)*4); + Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len); + isPNG = true; } + Z_Free(png); } + + if (!isPNG) #endif - spritecachedinfo[numspritelumps].width = SHORT(patch.width)<ffloor = NULL; node->sprite = NULL; - rs_numdrawnodes++; + ps_numdrawnodes++; return node; } diff --git a/src/r_things.h b/src/r_things.h index 7a0fe3a60..b13c5dc55 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -15,7 +15,7 @@ #define __R_THINGS__ #include "r_plane.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_portal.h" #include "r_defs.h" #include "r_skins.h" diff --git a/src/s_sound.c b/src/s_sound.c index 0442c6219..793794aa7 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -65,21 +65,21 @@ static boolean S_CheckQueue(void); #endif #ifdef _WINDOWS -consvar_t cv_samplerate = {"samplerate", "44100", 0, CV_Unsigned, NULL, 44100, NULL, NULL, 0, 0, NULL}; //Alam: For easy hacking? +consvar_t cv_samplerate = CVAR_INIT ("samplerate", "44100", 0, CV_Unsigned, NULL); //Alam: For easy hacking? #else -consvar_t cv_samplerate = {"samplerate", "22050", 0, CV_Unsigned, NULL, 22050, NULL, NULL, 0, 0, NULL}; //Alam: For easy hacking? +consvar_t cv_samplerate = CVAR_INIT ("samplerate", "22050", 0, CV_Unsigned, NULL); //Alam: For easy hacking? #endif // stereo reverse -consvar_t stereoreverse = {"stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t stereoreverse = CVAR_INIT ("stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL); // if true, all sounds are loaded at game startup -static consvar_t precachesound = {"precachesound", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t precachesound = CVAR_INIT ("precachesound", "Off", CV_SAVE, CV_OnOff, NULL); // actual general (maximum) sound & music volume, saved into the config -consvar_t cv_soundvolume = {"soundvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_digmusicvolume = {"digmusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_midimusicvolume = {"midimusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_soundvolume = CVAR_INIT ("soundvolume", "18", CV_SAVE, soundvolume_cons_t, NULL); +consvar_t cv_digmusicvolume = CVAR_INIT ("digmusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL); +consvar_t cv_midimusicvolume = CVAR_INIT ("midimusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL); static void Captioning_OnChange(void) { @@ -88,27 +88,27 @@ static void Captioning_OnChange(void) S_StartSound(NULL, sfx_menu1); } -consvar_t cv_closedcaptioning = {"closedcaptioning", "Off", CV_SAVE|CV_CALL, CV_OnOff, Captioning_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_closedcaptioning = CVAR_INIT ("closedcaptioning", "Off", CV_SAVE|CV_CALL, CV_OnOff, Captioning_OnChange); // number of channels available -consvar_t cv_numChannels = {"snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_numChannels = CVAR_INIT ("snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum); -static consvar_t surround = {"surround", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t surround = CVAR_INIT ("surround", "Off", CV_SAVE, CV_OnOff, NULL); -consvar_t cv_resetmusic = {"resetmusic", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_resetmusicbyheader = {"resetmusicbyheader", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_resetmusic = CVAR_INIT ("resetmusic", "Off", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_resetmusicbyheader = CVAR_INIT ("resetmusicbyheader", "Yes", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t cons_1upsound_t[] = { {0, "Jingle"}, {1, "Sound"}, {0, NULL} }; -consvar_t cv_1upsound = {"1upsound", "Jingle", CV_SAVE, cons_1upsound_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_1upsound = CVAR_INIT ("1upsound", "Jingle", CV_SAVE, cons_1upsound_t, NULL); // Sound system toggles, saved into the config -consvar_t cv_gamedigimusic = {"digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gamemidimusic = {"midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gamesounds = {"sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_gamedigimusic = CVAR_INIT ("digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange); +consvar_t cv_gamemidimusic = CVAR_INIT ("midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange); +consvar_t cv_gamesounds = CVAR_INIT ("sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange); // Music preference static CV_PossibleValue_t cons_musicpref_t[] = { @@ -116,16 +116,16 @@ static CV_PossibleValue_t cons_musicpref_t[] = { {1, "MIDI"}, {0, NULL} }; -consvar_t cv_musicpref = {"musicpref", "Digital", CV_SAVE|CV_CALL|CV_NOINIT, cons_musicpref_t, MusicPref_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_musicpref = CVAR_INIT ("musicpref", "Digital", CV_SAVE|CV_CALL|CV_NOINIT, cons_musicpref_t, MusicPref_OnChange); // Window focus sound sytem toggles -consvar_t cv_playmusicifunfocused = {"playmusicifunfocused", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_playsoundsifunfocused = {"playsoundsifunfocused", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_playmusicifunfocused = CVAR_INIT ("playmusicifunfocused", "No", CV_SAVE, CV_YesNo, NULL); +consvar_t cv_playsoundsifunfocused = CVAR_INIT ("playsoundsifunfocused", "No", CV_SAVE, CV_YesNo, NULL); #ifdef HAVE_OPENMPT openmpt_module *openmpt_mhandle = NULL; static CV_PossibleValue_t interpolationfilter_cons_t[] = {{0, "Default"}, {1, "None"}, {2, "Linear"}, {4, "Cubic"}, {8, "Windowed sinc"}, {0, NULL}}; -consvar_t cv_modfilter = {"modfilter", "0", CV_SAVE|CV_CALL, interpolationfilter_cons_t, ModFilter_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_modfilter = CVAR_INIT ("modfilter", "0", CV_SAVE|CV_CALL, interpolationfilter_cons_t, ModFilter_OnChange); #endif #define S_MAX_VOLUME 127 @@ -521,6 +521,7 @@ void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan) void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) { + const INT32 initial_volume = volume; INT32 sep, pitch, priority, cnum; const sfxenum_t actual_id = sfx_id; sfxinfo_t *sfx; @@ -718,6 +719,7 @@ dontplay: // 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); } @@ -929,7 +931,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; @@ -1043,7 +1045,6 @@ void S_ClearSfx(void) static void S_StopChannel(INT32 cnum) { - INT32 i; channel_t *c = &channels[cnum]; if (c->sfxinfo) @@ -1052,17 +1053,12 @@ static void S_StopChannel(INT32 cnum) if (I_SoundIsPlaying(c->handle)) I_StopSound(c->handle); - // check to see - // if other channels are playing the sound - for (i = 0; i < numofchannels; i++) - if (cnum != i && c->sfxinfo == channels[i].sfxinfo) - break; - // degrade usefulness of sound data c->sfxinfo->usefulness--; - c->sfxinfo = 0; } + + c->origin = NULL; } // @@ -1204,15 +1200,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; } return (*vol > 0); diff --git a/src/s_sound.h b/src/s_sound.h index 6e3de6101..4ac3c70bf 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -87,6 +87,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; diff --git a/src/screen.c b/src/screen.c index e7ff9e735..048480fb2 100644 --- a/src/screen.c +++ b/src/screen.c @@ -58,24 +58,24 @@ UINT8 setrenderneeded = 0; static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}}; //added : 03-02-98: default screen mode, as loaded/saved in config -consvar_t cv_scr_width = {"scr_width", "1280", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_scr_height = {"scr_height", "800", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_scr_width = CVAR_INIT ("scr_width", "1280", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL); +consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL); static void SCR_ActuallyChangeRenderer(void); -CV_PossibleValue_t cv_renderer_t[] = { +static CV_PossibleValue_t cv_renderer_t[] = { {1, "Software"}, #ifdef HWRENDER {2, "OpenGL"}, #endif {0, NULL} }; -consvar_t cv_renderer = {"renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_ChangeRenderer, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_renderer = CVAR_INIT ("renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_ChangeRenderer); static void SCR_ChangeFullscreen(void); -consvar_t cv_fullscreen = {"fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_fullscreen = CVAR_INIT ("fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen); // ========================================================================= // SCREEN VARIABLES @@ -499,9 +499,6 @@ void SCR_ChangeRendererCVars(INT32 mode) CV_StealthSetValue(&cv_renderer, 1); else if (mode == render_opengl) CV_StealthSetValue(&cv_renderer, 2); -#ifdef HWRENDER - CV_StealthSetValue(&cv_newrenderer, cv_renderer.value); -#endif } boolean SCR_IsAspectCorrect(INT32 width, INT32 height) diff --git a/src/screen.h b/src/screen.h index 91ec175f4..2cb2cf839 100644 --- a/src/screen.h +++ b/src/screen.h @@ -168,7 +168,6 @@ extern boolean R_SSE2; // ---------------- // screen variables // ---------------- - extern viddef_t vid; extern INT32 setmodeneeded; // mode number to set if needed, or 0 @@ -179,12 +178,7 @@ extern UINT8 setrenderneeded; extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders -extern CV_PossibleValue_t cv_renderer_t[]; - extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen; -#ifdef HWRENDER -extern consvar_t cv_newrenderer; -#endif // wait for page flipping to end or not extern consvar_t cv_vidwait; diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index 72f78188f..bb5edf817 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -55,6 +55,10 @@ set(SRB2_SDL2_HEADERS sdlmain.h ) +if(${SRB2_CONFIG_HAVE_THREADS}) + set(SRB2_SDL2_SOURCES ${SRB2_SDL2_SOURCES} i_threads.c) +endif() + source_group("Interface Code" FILES ${SRB2_SDL2_SOURCES} ${SRB2_SDL2_HEADERS}) # Dependency diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index 68e616f01..45d0d6ba7 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 diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index c6cef56de..c2d6456e4 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -262,6 +262,7 @@ + @@ -282,14 +283,15 @@ + - + @@ -400,6 +402,7 @@ + @@ -416,6 +419,7 @@ + @@ -448,13 +452,14 @@ true + - + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 04a1b5fa5..438746ac7 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -348,6 +348,9 @@ M_Misc + + M_Misc + M_Misc @@ -474,7 +477,10 @@ Hw_Hardware - + + R_Rend + + R_Rend @@ -720,6 +726,9 @@ LUA + + LUA + LUA @@ -759,6 +768,9 @@ M_Misc + + M_Misc + M_Misc @@ -949,7 +961,10 @@ Hw_Hardware - + + R_Rend + + R_Rend diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 416c8d2f5..e545bbb63 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -104,14 +104,13 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(MakeScreenFinalTexture); GETFUNC(DrawScreenFinalTexture); - GETFUNC(LoadShaders); - GETFUNC(KillShaders); + GETFUNC(CompileShaders); + GETFUNC(CleanShaders); GETFUNC(SetShader); GETFUNC(UnSetShader); GETFUNC(SetShaderInfo); GETFUNC(LoadCustomShader); - GETFUNC(InitCustomShaders); #else //HWRENDER if (0 == strcmp("FinishUpdate", funcName)) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 88f45c302..10c0747bf 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -560,14 +560,12 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) case VK_TAB: event.data1 = KEY_NULL; break; - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; case VK_RETURN: entering_con_command = false; /* FALLTHRU */ default: - event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + event.data1 = evt.uChar.AsciiChar; } if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) { @@ -586,18 +584,6 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) } } } - else - { - event.type = ev_keyup; - switch (evt.wVirtualKeyCode) - { - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; - default: - break; - } - } if (event.data1) D_PostEvent(&event); } @@ -2602,7 +2588,7 @@ void I_GetDiskFreeSpace(INT64 *freespace) return; #else // Both Linux and BSD have this, apparently. struct statfs stfs; - if (statfs(".", &stfs) == -1) + if (statfs(srb2home, &stfs) == -1) { *freespace = INT32_MAX; return; @@ -2621,7 +2607,7 @@ void I_GetDiskFreeSpace(INT64 *freespace) } if (pfnGetDiskFreeSpaceEx) { - if (pfnGetDiskFreeSpaceEx(NULL, &lfreespace, &usedbytes, NULL)) + if (pfnGetDiskFreeSpaceEx(srb2home, &lfreespace, &usedbytes, NULL)) *freespace = lfreespace.QuadPart; else *freespace = INT32_MAX; diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c index 078f4e0f4..3b1c20b9a 100644 --- a/src/sdl/i_threads.c +++ b/src/sdl/i_threads.c @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 KART +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 01194a02f..a49adb508 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -100,9 +100,9 @@ static rendermode_t chosenrendermode = render_soft; // set by command line argum boolean highcolor = false; // synchronize page flipping with screen refresh -consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL); +static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL); +static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL); UINT8 graphics_started = 0; // Is used in console.c and screen.c INT32 vid_opengl_state = 0; @@ -1478,6 +1478,7 @@ static SDL_bool Impl_CreateContext(void) void VID_CheckGLLoaded(rendermode_t oldrender) { + (void)oldrender; #ifdef HWRENDER if (vid_opengl_state == -1) // Well, it didn't work the first time anyway. { @@ -1488,7 +1489,6 @@ void VID_CheckGLLoaded(rendermode_t oldrender) if (setrenderneeded) { CV_StealthSetValue(&cv_renderer, oldrender); - CV_StealthSetValue(&cv_newrenderer, oldrender); setrenderneeded = 0; } } @@ -1660,7 +1660,7 @@ static void Impl_SetWindowName(const char *title) static void Impl_SetWindowIcon(void) { if (window && icoSurface) - SDL_SetWindowIcon(window, icoSurface); + SDL_SetWindowIcon(window, icoSurface); } static void Impl_VideoSetupSDLBuffer(void) @@ -1770,7 +1770,7 @@ void I_StartupGraphics(void) // Window icon #ifdef HAVE_IMAGE icoSurface = IMG_ReadXPMFromArray(SDL_icon_xpm); -#endif +#endif // Fury: we do window initialization after GL setup to allow // SDL_GL_LoadLibrary to work well on Windows @@ -1855,14 +1855,13 @@ void VID_StartupOpenGL(void) HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); - HWD.pfnLoadShaders = hwSym("LoadShaders",NULL); - HWD.pfnKillShaders = hwSym("KillShaders",NULL); + HWD.pfnCompileShaders = hwSym("CompileShaders",NULL); + HWD.pfnCleanShaders = hwSym("CleanShaders",NULL); HWD.pfnSetShader = hwSym("SetShader",NULL); HWD.pfnUnSetShader = hwSym("UnSetShader",NULL); HWD.pfnSetShaderInfo = hwSym("SetShaderInfo",NULL); HWD.pfnLoadCustomShader = hwSym("LoadCustomShader",NULL); - HWD.pfnInitCustomShaders= hwSym("InitCustomShaders",NULL); vid_opengl_state = HWD.pfnInit() ? 1 : -1; // let load the OpenGL library diff --git a/src/sdl/macosx/Srb2mac.icns b/src/sdl/macosx/Srb2mac.icns index a3e37aab3..2ac2faf33 100644 Binary files a/src/sdl/macosx/Srb2mac.icns and b/src/sdl/macosx/Srb2mac.icns differ diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index ef34b266d..c64164caa 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -196,9 +196,9 @@ static void MidiSoundfontPath_Onchange(void) // make sure that s_sound.c does not already verify these // which happens when: defined(HAVE_MIXERX) && !defined(HAVE_MIXER) static CV_PossibleValue_t midiplayer_cons_t[] = {{MIDI_OPNMIDI, "OPNMIDI"}, {MIDI_Fluidsynth, "Fluidsynth"}, {MIDI_Timidity, "Timidity"}, {MIDI_Native, "Native"}, {0, NULL}}; -consvar_t cv_midiplayer = {"midiplayer", "OPNMIDI" /*MIDI_OPNMIDI*/, CV_CALL|CV_NOINIT|CV_SAVE, midiplayer_cons_t, Midiplayer_Onchange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_midisoundfontpath = {"midisoundfont", "sf2/8bitsf.SF2", CV_CALL|CV_NOINIT|CV_SAVE, NULL, MidiSoundfontPath_Onchange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_miditimiditypath = {"midisoundbank", "./timidity", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_midiplayer = CVAR_INIT ("midiplayer", "OPNMIDI" /*MIDI_OPNMIDI*/, CV_CALL|CV_NOINIT|CV_SAVE, midiplayer_cons_t, Midiplayer_Onchange); +consvar_t cv_midisoundfontpath = CVAR_INIT ("midisoundfont", "sf2/8bitsf.SF2", CV_CALL|CV_NOINIT|CV_SAVE, NULL, MidiSoundfontPath_Onchange); +consvar_t cv_miditimiditypath = CVAR_INIT ("midisoundbank", "./timidity", CV_SAVE, NULL, NULL); #endif static void var_cleanup(void) @@ -220,6 +220,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) @@ -240,6 +241,7 @@ static const char* get_zlib_error(int zErr) return "unknown error"; } } +#endif /// ------------------------ /// Audio System diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index edc69b21d..04214ad03 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -167,6 +167,18 @@ boolean OglSdlSurface(INT32 w, INT32 h) GL_DBG_Printf("OpenGL %s\n", gl_version); GL_DBG_Printf("GPU: %s\n", gl_renderer); GL_DBG_Printf("Extensions: %s\n", gl_extensions); + + if (strcmp((const char*)gl_renderer, "GDI Generic") == 0 && + strcmp((const char*)gl_version, "1.1.0") == 0) + { + // Oh no... Windows gave us the GDI Generic rasterizer, so something is wrong... + // The game will crash later on when unsupported OpenGL commands are encountered. + // Instead of a nondescript crash, show a more informative error message. + // Also set the renderer variable back to software so the next launch won't + // repeat this error. + CV_StealthSet(&cv_renderer, "Software"); + I_Error("OpenGL Error: Failed to access the GPU. There may be an issue with your graphics drivers."); + } } first_init = true; diff --git a/src/st_stuff.c b/src/st_stuff.c index 2fd1bda77..86e0b3754 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2195,7 +2195,7 @@ static void ST_drawMatchHUD(void) { sprintf(penaltystr, "-%d", stplyr->ammoremoval); V_DrawString(offset + 8 + stplyr->ammoremovalweapon * 20, y, - V_REDMAP, penaltystr); + V_REDMAP|V_SNAPTOBOTTOM, penaltystr); } } diff --git a/src/v_video.c b/src/v_video.c index b88c4838b..9c91261de 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -43,47 +43,45 @@ UINT8 *screens[5]; // screens[4] = fade screen end, postimage tempoarary buffer static CV_PossibleValue_t ticrate_cons_t[] = {{0, "No"}, {1, "Full"}, {2, "Compact"}, {0, NULL}}; -consvar_t cv_ticrate = {"showfps", "No", CV_SAVE, ticrate_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_ticrate = CVAR_INIT ("showfps", "No", CV_SAVE, ticrate_cons_t, NULL); static void CV_palette_OnChange(void); static CV_PossibleValue_t gamma_cons_t[] = {{-15, "MIN"}, {5, "MAX"}, {0, NULL}}; -consvar_t cv_globalgamma = {"gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_globalgamma = CVAR_INIT ("gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); static CV_PossibleValue_t saturation_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}}; -consvar_t cv_globalsaturation = {"saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_globalsaturation = CVAR_INIT ("saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); #define huecoloursteps 4 static CV_PossibleValue_t hue_cons_t[] = {{0, "MIN"}, {(huecoloursteps*6)-1, "MAX"}, {0, NULL}}; -consvar_t cv_rhue = {"rhue", "0", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_yhue = {"yhue", "4", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ghue = {"ghue", "8", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chue = {"chue", "12", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_bhue = {"bhue", "16", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mhue = {"mhue", "20", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_rhue = CVAR_INIT ("rhue", "0", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); +consvar_t cv_yhue = CVAR_INIT ("yhue", "4", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); +consvar_t cv_ghue = CVAR_INIT ("ghue", "8", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); +consvar_t cv_chue = CVAR_INIT ("chue", "12", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); +consvar_t cv_bhue = CVAR_INIT ("bhue", "16", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); +consvar_t cv_mhue = CVAR_INIT ("mhue", "20", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange); -consvar_t cv_rgamma = {"rgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ygamma = {"ygamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ggamma = {"ggamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cgamma = {"cgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_bgamma = {"bgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mgamma = {"mgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_rgamma = CVAR_INIT ("rgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); +consvar_t cv_ygamma = CVAR_INIT ("ygamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); +consvar_t cv_ggamma = CVAR_INIT ("ggamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); +consvar_t cv_cgamma = CVAR_INIT ("cgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); +consvar_t cv_bgamma = CVAR_INIT ("bgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); +consvar_t cv_mgamma = CVAR_INIT ("mgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange); -consvar_t cv_rsaturation = {"rsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_ysaturation = {"ysaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_gsaturation = {"gsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_csaturation = {"csaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_bsaturation = {"bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_msaturation = {"msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; - -consvar_t cv_allcaps = {"allcaps", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_rsaturation = CVAR_INIT ("rsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); +consvar_t cv_ysaturation = CVAR_INIT ("ysaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); +consvar_t cv_gsaturation = CVAR_INIT ("gsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); +consvar_t cv_csaturation = CVAR_INIT ("csaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); +consvar_t cv_bsaturation = CVAR_INIT ("bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); +consvar_t cv_msaturation = CVAR_INIT ("msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange); static CV_PossibleValue_t constextsize_cons_t[] = { {V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"}, {0, NULL}}; static void CV_constextsize_OnChange(void); -consvar_t cv_constextsize = {"con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_constextsize = CVAR_INIT ("con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange); // local copy of the palette for V_GetColor() RGBA_t *pLocalPalette = NULL; @@ -2120,6 +2118,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + switch (option & V_SPACINGMASK) { case V_MONOSPACE: @@ -2233,6 +2234,9 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2348,6 +2352,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2483,6 +2490,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2596,6 +2606,9 @@ void V_DrawSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *st scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2710,6 +2723,9 @@ void V_DrawThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *str scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2824,6 +2840,9 @@ void V_DrawSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2917,7 +2936,7 @@ void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) INT32 w = SHORT(tallnum[0]->width); boolean neg; - if (flags & V_NOSCALESTART) + if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) w *= vid.dupx; if ((neg = num < 0)) @@ -2942,7 +2961,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) { INT32 w = SHORT(tallnum[0]->width); - if (flags & V_NOSCALESTART) + if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) w *= vid.dupx; if (num < 0) @@ -2995,6 +3014,9 @@ void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string) else dupx = dupy = 1; + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;) { c = *ch++; @@ -3052,6 +3074,9 @@ static void V_DrawNameTagLine(INT32 x, INT32 y, INT32 option, fixed_t scale, UIN scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;ch++) { if (!*ch) @@ -3267,6 +3292,9 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;ch++) { if (!*ch) @@ -3402,8 +3430,8 @@ INT32 V_StringWidth(const char *string, INT32 option) w += (charwidth ? charwidth : SHORT(hu_font[c]->width)); } - if (option & V_NOSCALESTART) - w *= vid.dupx; + if (option & (V_NOSCALESTART|V_NOSCALEPATCH)) + w *= vid.dupx; return w; } @@ -3666,28 +3694,51 @@ Unoptimized version #endif } -// Generates a color look-up table -// which has up to 64 colors at each channel -// (see the defines in v_video.h) - -UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE]; - -void InitColorLUT(RGBA_t *palette) +// Generates a RGB565 color look-up table +void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors) { - UINT8 r, g, b; - static boolean clutinit = false; - static RGBA_t *lastpalette = NULL; - if ((!clutinit) || (lastpalette != palette)) + size_t palsize = (sizeof(RGBA_t) * 256); + + if (!lut->init || memcmp(lut->palette, palette, palsize)) { - for (r = 0; r < CLUTSIZE; r++) - for (g = 0; g < CLUTSIZE; g++) - for (b = 0; b < CLUTSIZE; b++) - colorlookup[r][g][b] = NearestPaletteColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS, palette); - clutinit = true; - lastpalette = palette; + INT32 i; + + lut->init = true; + memcpy(lut->palette, palette, palsize); + + for (i = 0; i < 0xFFFF; i++) + lut->table[i] = 0xFFFF; + + if (makecolors) + { + UINT8 r, g, b; + + for (r = 0; r < 0xFF; r++) + for (g = 0; g < 0xFF; g++) + for (b = 0; b < 0xFF; b++) + { + i = CLUTINDEX(r, g, b); + if (lut->table[i] == 0xFFFF) + lut->table[i] = NearestPaletteColor(r, g, b, palette); + } + } } } +UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b) +{ + INT32 i = CLUTINDEX(r, g, b); + if (lut->table[i] == 0xFFFF) + lut->table[i] = NearestPaletteColor(r, g, b, lut->palette); + return lut->table[i]; +} + +UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b) +{ + INT32 i = CLUTINDEX(r, g, b); + return lut->table[i]; +} + // V_Init // old software stuff, buffers are allocated at video mode setup // here we set the screens[x] pointers accordingly diff --git a/src/v_video.h b/src/v_video.h index 9f7a9a9e9..2af4fe293 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -27,24 +27,28 @@ extern UINT8 *screens[5]; -extern consvar_t cv_ticrate, cv_constextsize,\ -cv_globalgamma, cv_globalsaturation, \ -cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,\ -cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, \ -cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation,\ -cv_allcaps; +extern consvar_t cv_ticrate, cv_constextsize, +cv_globalgamma, cv_globalsaturation, +cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue, +cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, +cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation; // Allocates buffer screens, call before R_Init. void V_Init(void); // Color look-up table -#define COLORBITS 6 -#define SHIFTCOLORBITS (8-COLORBITS) -#define CLUTSIZE (1<> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3) -extern UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE]; +typedef struct +{ + boolean init; + RGBA_t palette[256]; + UINT16 table[0xFFFF]; +} colorlookup_t; -void InitColorLUT(RGBA_t *palette); +void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors); +UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); +UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); // Set the current RGB palette lookup to use for palettized graphics void V_SetPalette(INT32 palettenum); diff --git a/src/version.h b/src/version.h index 31cf85bdc..ece084beb 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define SRB2VERSION "2.2.6"/* this must be the first line, for cmake !! */ +#define SRB2VERSION "2.2.8"/* this must be the first line, for cmake !! */ // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ). // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. @@ -9,4 +9,7 @@ // 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.2.0 is not version "1". -#define MODVERSION 47 +#define MODVERSION 49 + +// Define this as a prerelease version suffix +// #define BETAVERSION "RC1" diff --git a/src/w_wad.c b/src/w_wad.c index 548d1bc00..fd70f8ec3 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -56,6 +56,8 @@ #include "d_clisrv.h" #include "r_defs.h" #include "r_data.h" +#include "r_textures.h" +#include "r_picformats.h" #include "i_system.h" #include "md5.h" #include "lua_script.h" @@ -65,7 +67,6 @@ #include "m_misc.h" // M_MapNumber #ifdef HWRENDER -#include "r_data.h" #include "hardware/hw_main.h" #include "hardware/hw_glob.h" #endif @@ -776,6 +777,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) { CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename); + if (important) + packetsizetally -= nameonlylength(filename) + 22; if (handle) fclose(handle); return W_InitFileError(filename, false); @@ -846,8 +849,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) // Read shaders from file if (rendermode == render_opengl && (vid_opengl_state == 1)) { - HWR_ReadShaders(numwadfiles - 1, (type == RET_PK3)); - HWR_LoadShaders(); + HWR_LoadCustomShadersFromFile(numwadfiles - 1, (type == RET_PK3)); + HWR_CompileShaders(); } #endif // HWRENDER @@ -884,16 +887,13 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) * * \param filenames A null-terminated list of files to use. */ -void W_InitMultipleFiles(char **filenames, UINT16 mainfiles) +void W_InitMultipleFiles(char **filenames) { - // open all the files, load headers, and count lumps - numwadfiles = 0; - // will be realloced as lumps are added for (; *filenames; filenames++) { //CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames); - W_InitFile(*filenames, numwadfiles < mainfiles, true); + W_InitFile(*filenames, numwadfiles < mainwads, true); } } @@ -1383,8 +1383,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - if (R_IsLumpPNG((UINT8 *)dest, bytesread)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, bytesread)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); return bytesread; } #else @@ -1425,8 +1425,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); #endif return size; #else @@ -1488,8 +1488,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); #endif return size; } @@ -1683,10 +1683,10 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) #ifndef NO_PNG_LUMPS // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)lumpdata, len)) + if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) { size_t newlen; - srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen); + srcdata = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, len, &newlen, 0); ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]); M_Memcpy(ptr, srcdata, newlen); Z_Free(srcdata); diff --git a/src/w_wad.h b/src/w_wad.h index fddc65529..41232cba1 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -150,7 +150,7 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors); UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup); // W_InitMultipleFiles exits if a file was not found, but not if all is okay. -void W_InitMultipleFiles(char **filenames, UINT16 mainfiles); +void W_InitMultipleFiles(char **filenames); const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index 6855a4135..52617037b 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -298,12 +298,13 @@ true + - + @@ -453,13 +454,14 @@ + - + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index 4a980c6bd..0689a4ac0 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -469,7 +469,7 @@ Hw_Hardware - + R_Rend @@ -886,7 +886,10 @@ Hw_Hardware - + + R_Rend + + R_Rend diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index b90947a9e..d5d59922c 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -22,6 +22,16 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32*/ +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif + +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST srb2win.exe.manifest + ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -66,8 +76,8 @@ END #include "../doomdef.h" // Needed for version string VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,2,6,0 - PRODUCTVERSION 2,2,6,0 + FILEVERSION 2,2,8,0 + PRODUCTVERSION 2,2,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -85,14 +95,14 @@ BEGIN VALUE "Comments", "Visit our web site at www.srb2.org for news and updates!\0" VALUE "CompanyName", "Sonic Team Junior\0" VALUE "FileDescription", "Sonic Robo Blast 2\0" - VALUE "FileVersion", VERSIONSTRING + VALUE "FileVersion", VERSIONSTRING_RC VALUE "InternalName", "srb2\0" VALUE "LegalCopyright", "Copyright 1998-2020 by Sonic Team Junior\0" VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0" VALUE "OriginalFilename", "srb2win.exe\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Sonic Robo Blast 2\0" - VALUE "ProductVersion", VERSIONSTRING + VALUE "ProductVersion", VERSIONSTRING_RC VALUE "SpecialBuild", "\0" END END diff --git a/src/win32/srb2win.exe.manifest b/src/win32/srb2win.exe.manifest new file mode 100644 index 000000000..d3b8355cb --- /dev/null +++ b/src/win32/srb2win.exe.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/win32/win_cd.c b/src/win32/win_cd.c index 2586b8440..324c24928 100644 --- a/src/win32/win_cd.c +++ b/src/win32/win_cd.c @@ -161,13 +161,13 @@ static BOOL wasPlaying; //static INT cdVolume = 0; // current cd volume (0-31) // 0-31 like Music & Sfx, though CD hardware volume is 0-255. -consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = CVAR_INIT ("cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL); // allow Update for next/loop track // some crap cd drivers take up to // a second for a simple 'busy' check.. // (on those Update can be disabled) -consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cdUpdate = CVAR_INIT ("cd_update","1",CV_SAVE, NULL, NULL); #if (__GNUC__ > 6) #pragma GCC diagnostic push diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index a374a2587..da0d5b47e 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -3658,7 +3658,7 @@ const CPUInfoFlags *I_CPUInfo(void) } static void CPUAffinity_OnChange(void); -static consvar_t cv_cpuaffinity = {"cpuaffinity", "-1", CV_CALL, NULL, CPUAffinity_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_cpuaffinity = CVAR_INIT ("cpuaffinity", "-1", CV_CALL, NULL, CPUAffinity_OnChange); typedef HANDLE (WINAPI *p_GetCurrentProcess) (VOID); static p_GetCurrentProcess pfnGetCurrentProcess = NULL; diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index 716f38089..931e006eb 100644 --- a/src/win32/win_vid.c +++ b/src/win32/win_vid.c @@ -51,9 +51,9 @@ rendermode_t rendermode = render_soft; static void OnTop_OnChange(void); // synchronize page flipping with screen refresh static CV_PossibleValue_t CV_NeverOnOff[] = {{-1, "Never"}, {0, "Off"}, {1, "On"}, {0, NULL}}; -consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, OnTop_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_stretch = {"stretch", "On", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_ontop = {"ontop", "Never", 0, CV_NeverOnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, OnTop_OnChange); +static consvar_t cv_stretch = CVAR_INIT ("stretch", "On", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL); +static consvar_t cv_ontop = CVAR_INIT ("ontop", "Never", 0, CV_NeverOnOff, NULL); boolean highcolor; int vid_opengl_state = 0; diff --git a/src/z_zone.c b/src/z_zone.c index 2387a1143..2c7384c3d 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -27,7 +27,7 @@ #include "doomdef.h" #include "doomstat.h" -#include "r_patch.h" +#include "r_picformats.h" #include "i_system.h" // I_GetFreeMem #include "i_video.h" // rendermode #include "z_zone.h" @@ -517,7 +517,6 @@ void Z_FlushCachedPatches(void) Z_FreeTag(PU_HWRMODELTEXTURE_UNLOCKED); } -// happens before a renderer switch void Z_PreparePatchFlush(void) { CONS_Debug(DBG_RENDER, "Z_PreparePatchFlush()...\n");