From 6694b7d679dfa33a307a935788d289980c0eb615 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 6 Dec 2018 10:54:58 -0500 Subject: [PATCH 001/196] Controller hotplugging by always keeping joy subsystem on (thanks WOLFS) (with log messages) --- src/m_menu.c | 2 +- src/m_menu.h | 5 +- src/sdl/i_system.c | 213 ++++++++++++++++++++++----------------------- src/sdl/i_video.c | 35 ++++++++ src/sdl/sdlmain.h | 4 + 5 files changed, 148 insertions(+), 111 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 1e1b1e696..a4c62c0f3 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6761,7 +6761,7 @@ static void M_DrawJoystick(void) } } -static void M_SetupJoystickMenu(INT32 choice) +void M_SetupJoystickMenu(INT32 choice) { INT32 i = 0; const char *joyNA = "Unavailable"; diff --git a/src/m_menu.h b/src/m_menu.h index de76a2710..3066a261c 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -69,7 +69,6 @@ void M_QuitResponse(INT32 ch); // Determines whether to show a level in the list boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); - // flags for items in the menu // menu handle (what we do when key is pressed #define IT_TYPE 14 // (2+4+8) @@ -171,6 +170,10 @@ extern menu_t *currentMenu; extern menu_t MainDef; extern menu_t SP_LoadDef; +// Call upon joystick hotplug +void M_SetupJoystickMenu(INT32 choice); +extern menu_t OP_JoystickSetDef; + // Stuff for customizing the player select screen typedef struct { diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 50c3018aa..b18ea9571 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -843,7 +843,7 @@ static UINT64 lastjoyhats = 0; */ -static void I_ShutdownJoystick(void) +void I_ShutdownJoystick(void) { INT32 i; event_t event; @@ -877,14 +877,8 @@ static void I_ShutdownJoystick(void) joystick_started = 0; JoyReset(&JoyInfo); - if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - { - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - if (cv_usejoystick.value == 0) - { - I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n"); - } - } + + // don't shut down the subsystem here, because hotplugging } void I_GetJoystickEvents(void) @@ -1031,37 +1025,20 @@ static int joy_open(const char *fname) int num_joy = 0; int i; - if (joystick_started == 0 && joystick2_started == 0) + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) - { - CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); - return -1; - } - else - { - num_joy = SDL_NumJoysticks(); - } + CONS_Printf(M_GetText("Joystick subsystem not started\n")); + return -1; + } - if (num_joy < joyindex) - { - CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - I_ShutdownJoystick(); - return -1; - } - } - else - { - JoyReset(&JoyInfo); - //I_ShutdownJoystick(); - //joy_open(fname); - } + JoyReset(&JoyInfo); + + if (joyindex <= 0) + return 0; num_joy = SDL_NumJoysticks(); - if (joyindex <= 0 || num_joy == 0 || JoyInfo.oldjoy == joyindex) + if (num_joy == 0 || JoyInfo.oldjoy == joyindex) { // I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname); if (num_joy != 0) @@ -1069,10 +1046,20 @@ static int joy_open(const char *fname) CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy); for (i = 0; i < num_joy; i++) CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); + + if (num_joy < joyindex) + { + CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); + for (i = 0; i < num_joy; i++) + CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); + return 0; + } } else + { CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - if (joyindex <= 0 || num_joy == 0) return 0; + return 0; + } } JoyInfo.dev = SDL_JoystickOpen(joyindex-1); @@ -1080,7 +1067,6 @@ static int joy_open(const char *fname) if (JoyInfo.dev == NULL) { CONS_Printf(M_GetText("Couldn't open joystick: %s\n"), SDL_GetError()); - I_ShutdownJoystick(); return -1; } else @@ -1092,7 +1078,6 @@ static int joy_open(const char *fname) /* if (joyaxes<2) { I_OutputMsg("Not enought axes?\n"); - I_ShutdownJoystick(); return 0; }*/ @@ -1127,7 +1112,7 @@ static UINT64 lastjoy2hats = 0; \return void */ -static void I_ShutdownJoystick2(void) +void I_ShutdownJoystick2(void) { INT32 i; event_t event; @@ -1161,14 +1146,8 @@ static void I_ShutdownJoystick2(void) joystick2_started = 0; JoyReset(&JoyInfo2); - if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - { - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - if (cv_usejoystick2.value == 0) - { - DEBFILE("I_Joystick2: SDL's Joystick system has been shutdown\n"); - } - } + + // don't shut down the subsystem here, because hotplugging } void I_GetJoystick2Events(void) @@ -1317,35 +1296,20 @@ static int joy_open2(const char *fname) int num_joy = 0; int i; - if (joystick_started == 0 && joystick2_started == 0) + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) - { - CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); - return -1; - } - else - num_joy = SDL_NumJoysticks(); + CONS_Printf(M_GetText("Joystick subsystem not started\n")); + return -1; + } - if (num_joy < joyindex) - { - CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - I_ShutdownJoystick2(); - return -1; - } - } - else - { - JoyReset(&JoyInfo2); - //I_ShutdownJoystick(); - //joy_open(fname); - } + JoyReset(&JoyInfo2); + + if (joyindex <= 0) + return 0; num_joy = SDL_NumJoysticks(); - if (joyindex <= 0 || num_joy == 0 || JoyInfo2.oldjoy == joyindex) + if (num_joy == 0 || JoyInfo2.oldjoy == joyindex) { // I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname); if (num_joy != 0) @@ -1353,18 +1317,27 @@ static int joy_open2(const char *fname) CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy); for (i = 0; i < num_joy; i++) CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); + + if (num_joy < joyindex) + { + CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); + for (i = 0; i < num_joy; i++) + CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); + return 0; + } } else + { CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - if (joyindex <= 0 || num_joy == 0) return 0; + return 0; + } } JoyInfo2.dev = SDL_JoystickOpen(joyindex-1); - if (!JoyInfo2.dev) + if (JoyInfo2.dev == NULL) { CONS_Printf(M_GetText("Couldn't open joystick2: %s\n"), SDL_GetError()); - I_ShutdownJoystick2(); return -1; } else @@ -1373,10 +1346,9 @@ static int joy_open2(const char *fname) JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); if (JoyInfo2.axises > JOYAXISSET*2) JoyInfo2.axises = JOYAXISSET*2; -/* if (joyaxes < 2) +/* if (joyaxes<2) { I_OutputMsg("Not enought axes?\n"); - I_ShutdownJoystick2(); return 0; }*/ @@ -1401,57 +1373,89 @@ static int joy_open2(const char *fname) // void I_InitJoystick(void) { - I_ShutdownJoystick(); - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); - if (!strcmp(cv_usejoystick.string, "0") || M_CheckParm("-nojoy")) + //I_ShutdownJoystick(); + if (M_CheckParm("-nojoy")) return; - if (joy_open(cv_usejoystick.string) != -1) + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf("Initing joy system\n"); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); + return; + } + else + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + } + + if (strcmp(cv_usejoystick.string, "0") && joy_open(cv_usejoystick.string) != -1) + { JoyInfo.oldjoy = atoi(cv_usejoystick.string); + joystick_started = 1; + } else { + if (JoyInfo.oldjoy) + I_ShutdownJoystick(); cv_usejoystick.value = 0; - return; + joystick_started = 0; } - joystick_started = 1; } void I_InitJoystick2(void) { - I_ShutdownJoystick2(); - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); - if (!strcmp(cv_usejoystick2.string, "0") || M_CheckParm("-nojoy")) + //I_ShutdownJoystick2(); + if (M_CheckParm("-nojoy")) return; - if (joy_open2(cv_usejoystick2.string) != -1) + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf("Initing joy system\n"); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); + return; + } + else + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + } + + if (strcmp(cv_usejoystick2.string, "0") && joy_open2(cv_usejoystick2.string) != -1) + { JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); + joystick2_started = 1; + } else { + if (JoyInfo2.oldjoy) + I_ShutdownJoystick2(); cv_usejoystick2.value = 0; - return; + joystick2_started = 0; } - joystick2_started = 1; + } static void I_ShutdownInput(void) { + // Yes, the name is misleading: these send neutral events to + // clean up the unplugged joystick's input + // Note these methods are internal to this file, not called elsewhere. + I_ShutdownJoystick(); + I_ShutdownJoystick2(); + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) { - JoyReset(&JoyInfo); - JoyReset(&JoyInfo2); + CONS_Printf("Shutting down joy system\n"); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n"); } - } INT32 I_NumJoys(void) { INT32 numjoy = 0; - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != -1) - numjoy = SDL_NumJoysticks(); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - } - else + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) numjoy = SDL_NumJoysticks(); return numjoy; } @@ -1461,18 +1465,9 @@ static char joyname[255]; // MAX_PATH; joystick name is straight from the driver const char *I_GetJoyName(INT32 joyindex) { const char *tempname = NULL; + joyname[0] = 0; joyindex--; //SDL's Joystick System starts at 0, not 1 - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != -1) - { - tempname = SDL_JoystickNameForIndex(joyindex); - if (tempname) - strncpy(joyname, tempname, 255); - } - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - } - else + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) { tempname = SDL_JoystickNameForIndex(joyindex); if (tempname) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 2c199c2d0..835ba1660 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -116,6 +116,9 @@ static INT32 firstEntry = 0; // Total mouse motion X/Y offsets static INT32 mousemovex = 0, mousemovey = 0; +// Keep track of joy unplugged count +static INT32 joyunplugcount = 0; + // SDL vars static SDL_Surface *vidSurface = NULL; static SDL_Surface *bufSurface = NULL; @@ -882,6 +885,38 @@ void I_GetEvent(void) case SDL_JOYBUTTONDOWN: Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); break; + case SDL_JOYDEVICEADDED: + CONS_Printf("Joy device %d added\n", evt.jdevice.which); + + // recounts hotplugged joysticks + I_InitJoystick(); + I_InitJoystick2(); + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); + break; + case SDL_JOYDEVICEREMOVED: + { + // every time a device is unplugged, the "which" index increments by 1? + INT32 deviceIdx = evt.jdevice.which - joyunplugcount++; + + CONS_Printf("Joy device %d removed%s\n", deviceIdx, + (JoyInfo.oldjoy-1 == deviceIdx) ? " was first joystick" : + (JoyInfo2.oldjoy-1 == deviceIdx) ? " was second joystick" : ""); + + // I_ShutdownJoystick doesn't shut down the subsystem + // It just fires neutral joy events to clean up the unplugged joy + if (JoyInfo.oldjoy-1 == deviceIdx) + I_ShutdownJoystick(); + if (JoyInfo2.oldjoy-1 == deviceIdx) + I_ShutdownJoystick2(); + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); + } + break; case SDL_QUIT: I_Quit(); M_QuitResponse('y'); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index d12daaa8a..4acbce209 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -67,6 +67,10 @@ extern SDLJoyInfo_t JoyInfo; */ extern SDLJoyInfo_t JoyInfo2; +// So we can call this from i_video event loop +void I_ShutdownJoystick(void); +void I_ShutdownJoystick2(void); + void I_GetConsoleEvents(void); void SDLforceUngrabMouse(void); From 60afce5771bc9b50ce039c9bca6c4ca284768c8c Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 6 Dec 2018 14:03:46 -0500 Subject: [PATCH 002/196] Disable XINPUT before initing the joy subsystem --- src/sdl/i_system.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index b18ea9571..ae462c527 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1380,13 +1380,12 @@ void I_InitJoystick(void) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { CONS_Printf("Initing joy system\n"); + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); return; } - else - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); } if (strcmp(cv_usejoystick.string, "0") && joy_open(cv_usejoystick.string) != -1) @@ -1412,13 +1411,12 @@ void I_InitJoystick2(void) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { CONS_Printf("Initing joy system\n"); + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); return; } - else - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); } if (strcmp(cv_usejoystick2.string, "0") && joy_open2(cv_usejoystick2.string) != -1) From bcd747c1cd24bc50b95857f5d995c4612917f467 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 13 Dec 2018 22:26:13 -0500 Subject: [PATCH 003/196] Adjust SDL_JOYDEVICEREMOVED handler by checking the player's joy device explicitly --- src/sdl/i_video.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 835ba1660..82f26d23d 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -116,9 +116,6 @@ static INT32 firstEntry = 0; // Total mouse motion X/Y offsets static INT32 mousemovex = 0, mousemovey = 0; -// Keep track of joy unplugged count -static INT32 joyunplugcount = 0; - // SDL vars static SDL_Surface *vidSurface = NULL; static SDL_Surface *bufSurface = NULL; @@ -897,25 +894,21 @@ void I_GetEvent(void) M_SetupJoystickMenu(0); break; case SDL_JOYDEVICEREMOVED: + if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev)) { - // every time a device is unplugged, the "which" index increments by 1? - INT32 deviceIdx = evt.jdevice.which - joyunplugcount++; - - CONS_Printf("Joy device %d removed%s\n", deviceIdx, - (JoyInfo.oldjoy-1 == deviceIdx) ? " was first joystick" : - (JoyInfo2.oldjoy-1 == deviceIdx) ? " was second joystick" : ""); - - // I_ShutdownJoystick doesn't shut down the subsystem - // It just fires neutral joy events to clean up the unplugged joy - if (JoyInfo.oldjoy-1 == deviceIdx) - I_ShutdownJoystick(); - if (JoyInfo2.oldjoy-1 == deviceIdx) - I_ShutdownJoystick2(); - - // update the menu - if (currentMenu == &OP_JoystickSetDef) - M_SetupJoystickMenu(0); + CONS_Printf("Joy device %d removed, was first joystick\n", JoyInfo.oldjoy); + I_ShutdownJoystick(); } + + if (JoyInfo2.dev && !SDL_JoystickGetAttached(JoyInfo2.dev)) + { + CONS_Printf("Joy device %d removed, was second joystick\n", JoyInfo2.oldjoy); + I_ShutdownJoystick2(); + } + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); break; case SDL_QUIT: I_Quit(); From e81f05c496c9a7bb24c8bbdbf054b9116eecfadf Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 13 Dec 2018 23:09:05 -0500 Subject: [PATCH 004/196] Handle unstable device index when hotplugging controller --- src/sdl/i_system.c | 72 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index ae462c527..e48807830 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1021,6 +1021,7 @@ void I_GetJoystickEvents(void) */ static int joy_open(const char *fname) { + SDL_Joystick *newdev = NULL; int joyindex = atoi(fname); int num_joy = 0; int i; @@ -1062,7 +1063,28 @@ static int joy_open(const char *fname) } } - JoyInfo.dev = SDL_JoystickOpen(joyindex-1); + newdev = SDL_JoystickOpen(joyindex-1); + + // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (JoyInfo.dev) + { + if (JoyInfo.dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device + return JoyInfo.axises; + // Else, we're changing devices, so send neutral joy events + I_ShutdownJoystick(); + } + + JoyInfo.dev = newdev; if (JoyInfo.dev == NULL) { @@ -1075,7 +1097,7 @@ static int joy_open(const char *fname) JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); if (JoyInfo.axises > JOYAXISSET*2) JoyInfo.axises = JOYAXISSET*2; -/* if (joyaxes<2) + /* if (joyaxes<2) { I_OutputMsg("Not enought axes?\n"); return 0; @@ -1292,6 +1314,7 @@ void I_GetJoystick2Events(void) */ static int joy_open2(const char *fname) { + SDL_Joystick *newdev = NULL; int joyindex = atoi(fname); int num_joy = 0; int i; @@ -1333,7 +1356,28 @@ static int joy_open2(const char *fname) } } - JoyInfo2.dev = SDL_JoystickOpen(joyindex-1); + newdev = SDL_JoystickOpen(joyindex-1); + + // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (JoyInfo2.dev) + { + if (JoyInfo2.dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device + return JoyInfo.axises; + // Else, we're changing devices, so send neutral joy events + I_ShutdownJoystick2(); + } + + JoyInfo2.dev = newdev; if (JoyInfo2.dev == NULL) { @@ -1390,7 +1434,16 @@ void I_InitJoystick(void) if (strcmp(cv_usejoystick.string, "0") && joy_open(cv_usejoystick.string) != -1) { - JoyInfo.oldjoy = atoi(cv_usejoystick.string); + // JoyInfo.oldjoy may already be filled because we attempted to hotplug + // a device and the device index has changed + // So in this case, get the new device index + // + // For now, it does not actually matter if the JoyInfo.oldjoy value is inaccurate. We don't use its + // exact value; we just use it like a boolean. + if (JoyInfo.oldjoy <= 0) + JoyInfo.oldjoy = atoi(cv_usejoystick.string); + else + JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; joystick_started = 1; } else @@ -1421,7 +1474,16 @@ void I_InitJoystick2(void) if (strcmp(cv_usejoystick2.string, "0") && joy_open2(cv_usejoystick2.string) != -1) { - JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); + // JoyInfo.oldjoy may already be filled because we attempted to hotplug + // a device and the device index has changed + // So in this case, get the new device index + // + // For now, it does not actually matter if the JoyInfo2.oldjoy value is inaccurate. We don't use its + // exact value; we just use it like a boolean. + if (JoyInfo2.oldjoy <= 0) + JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); + else + JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; joystick2_started = 1; } else From 14cde2d227520bff464773442d11935e4bbfdedc Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 13 Dec 2018 23:26:07 -0500 Subject: [PATCH 005/196] Change joystick log messages to DBG_GAMELOGIC --- src/sdl/i_system.c | 16 ++++++++++++---- src/sdl/i_video.c | 14 ++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index e48807830..7a9439791 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1088,12 +1088,12 @@ static int joy_open(const char *fname) if (JoyInfo.dev == NULL) { - CONS_Printf(M_GetText("Couldn't open joystick: %s\n"), SDL_GetError()); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError()); return -1; } else { - CONS_Printf(M_GetText("Joystick: %s\n"), SDL_JoystickName(JoyInfo.dev)); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev)); JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); if (JoyInfo.axises > JOYAXISSET*2) JoyInfo.axises = JOYAXISSET*2; @@ -1381,12 +1381,12 @@ static int joy_open2(const char *fname) if (JoyInfo2.dev == NULL) { - CONS_Printf(M_GetText("Couldn't open joystick2: %s\n"), SDL_GetError()); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError()); return -1; } else { - CONS_Printf(M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); if (JoyInfo2.axises > JOYAXISSET*2) JoyInfo2.axises = JOYAXISSET*2; @@ -1443,7 +1443,11 @@ void I_InitJoystick(void) if (JoyInfo.oldjoy <= 0) JoyInfo.oldjoy = atoi(cv_usejoystick.string); else + { + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index has changed: was %d, now %d\n", + JoyInfo.oldjoy, SDL_JoystickInstanceID(JoyInfo.dev) + 1); JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + } joystick_started = 1; } else @@ -1483,7 +1487,11 @@ void I_InitJoystick2(void) if (JoyInfo2.oldjoy <= 0) JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); else + { + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index has changed: was %d, now %d\n", + JoyInfo2.oldjoy, SDL_JoystickInstanceID(JoyInfo2.dev) + 1); JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + } joystick2_started = 1; } else diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 82f26d23d..51da55cb2 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -883,12 +883,15 @@ void I_GetEvent(void) Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); break; case SDL_JOYDEVICEADDED: - CONS_Printf("Joy device %d added\n", evt.jdevice.which); + CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); - // recounts hotplugged joysticks + // recount hotplugged joysticks I_InitJoystick(); I_InitJoystick2(); + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); + // update the menu if (currentMenu == &OP_JoystickSetDef) M_SetupJoystickMenu(0); @@ -896,16 +899,19 @@ void I_GetEvent(void) case SDL_JOYDEVICEREMOVED: if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev)) { - CONS_Printf("Joy device %d removed, was first joystick\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick1 removed, device index: %d\n", JoyInfo.oldjoy); I_ShutdownJoystick(); } if (JoyInfo2.dev && !SDL_JoystickGetAttached(JoyInfo2.dev)) { - CONS_Printf("Joy device %d removed, was second joystick\n", JoyInfo2.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 removed, device index: %d\n", JoyInfo2.oldjoy); I_ShutdownJoystick2(); } + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); + // update the menu if (currentMenu == &OP_JoystickSetDef) M_SetupJoystickMenu(0); From 216e710b8705dc3b26d5d09aa026064c68e7c8e2 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 13 Dec 2018 23:50:36 -0500 Subject: [PATCH 006/196] Improve hotplug edge case with changing device indexes; return proper joy_open output on error --- src/sdl/i_system.c | 90 +++++++--------------------------------------- 1 file changed, 12 insertions(+), 78 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 7a9439791..d1ce041a5 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1024,7 +1024,6 @@ static int joy_open(const char *fname) SDL_Joystick *newdev = NULL; int joyindex = atoi(fname); int num_joy = 0; - int i; if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { @@ -1032,35 +1031,15 @@ static int joy_open(const char *fname) return -1; } - JoyReset(&JoyInfo); - if (joyindex <= 0) - return 0; + return -1; num_joy = SDL_NumJoysticks(); - if (num_joy == 0 || JoyInfo.oldjoy == joyindex) + if (num_joy == 0) { -// I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname); - if (num_joy != 0) - { - CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - - if (num_joy < joyindex) - { - CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - return 0; - } - } - else - { - CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - return 0; - } + CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); + return -1; } newdev = SDL_JoystickOpen(joyindex-1); @@ -1081,6 +1060,7 @@ static int joy_open(const char *fname) || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device return JoyInfo.axises; // Else, we're changing devices, so send neutral joy events + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n"); I_ShutdownJoystick(); } @@ -1317,7 +1297,6 @@ static int joy_open2(const char *fname) SDL_Joystick *newdev = NULL; int joyindex = atoi(fname); int num_joy = 0; - int i; if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { @@ -1325,35 +1304,15 @@ static int joy_open2(const char *fname) return -1; } - JoyReset(&JoyInfo2); - if (joyindex <= 0) - return 0; + return -1; num_joy = SDL_NumJoysticks(); - if (num_joy == 0 || JoyInfo2.oldjoy == joyindex) + if (num_joy == 0) { -// I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname); - if (num_joy != 0) - { - CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - - if (num_joy < joyindex) - { - CONS_Printf(M_GetText("Cannot use joystick #%d/(%s), it doesn't exist\n"),joyindex,fname); - for (i = 0; i < num_joy; i++) - CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i)); - return 0; - } - } - else - { - CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - return 0; - } + CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); + return -1; } newdev = SDL_JoystickOpen(joyindex-1); @@ -1374,6 +1333,7 @@ static int joy_open2(const char *fname) || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device return JoyInfo.axises; // Else, we're changing devices, so send neutral joy events + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n"); I_ShutdownJoystick2(); } @@ -1434,20 +1394,7 @@ void I_InitJoystick(void) if (strcmp(cv_usejoystick.string, "0") && joy_open(cv_usejoystick.string) != -1) { - // JoyInfo.oldjoy may already be filled because we attempted to hotplug - // a device and the device index has changed - // So in this case, get the new device index - // - // For now, it does not actually matter if the JoyInfo.oldjoy value is inaccurate. We don't use its - // exact value; we just use it like a boolean. - if (JoyInfo.oldjoy <= 0) - JoyInfo.oldjoy = atoi(cv_usejoystick.string); - else - { - CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index has changed: was %d, now %d\n", - JoyInfo.oldjoy, SDL_JoystickInstanceID(JoyInfo.dev) + 1); - JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; - } + JoyInfo.oldjoy = atoi(cv_usejoystick.string); joystick_started = 1; } else @@ -1478,20 +1425,7 @@ void I_InitJoystick2(void) if (strcmp(cv_usejoystick2.string, "0") && joy_open2(cv_usejoystick2.string) != -1) { - // JoyInfo.oldjoy may already be filled because we attempted to hotplug - // a device and the device index has changed - // So in this case, get the new device index - // - // For now, it does not actually matter if the JoyInfo2.oldjoy value is inaccurate. We don't use its - // exact value; we just use it like a boolean. - if (JoyInfo2.oldjoy <= 0) - JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); - else - { - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index has changed: was %d, now %d\n", - JoyInfo2.oldjoy, SDL_JoystickInstanceID(JoyInfo2.dev) + 1); - JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; - } + JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); joystick2_started = 1; } else From 44d6a1d2368157f03bbc0620504ed25a6cf68a92 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 00:22:25 -0500 Subject: [PATCH 007/196] Attempt to handle unstable device IDs --- src/sdl/i_system.c | 22 ++++++++++++---------- src/sdl/i_video.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d1ce041a5..6ac41de16 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1019,10 +1019,9 @@ void I_GetJoystickEvents(void) */ -static int joy_open(const char *fname) +static int joy_open(int joyindex) { SDL_Joystick *newdev = NULL; - int joyindex = atoi(fname); int num_joy = 0; if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) @@ -1292,10 +1291,9 @@ void I_GetJoystick2Events(void) */ -static int joy_open2(const char *fname) +static int joy_open2(int joyindex) { SDL_Joystick *newdev = NULL; - int joyindex = atoi(fname); int num_joy = 0; if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) @@ -1392,16 +1390,18 @@ void I_InitJoystick(void) } } - if (strcmp(cv_usejoystick.string, "0") && joy_open(cv_usejoystick.string) != -1) + if (cv_usejoystick.value && joy_open(cv_usejoystick.value) != -1) { - JoyInfo.oldjoy = atoi(cv_usejoystick.string); + // SDL's device indexes are unstable, so cv_usejoystick may not match + // the actual device index. So let's cheat a bit and use the instance ID. + // oldjoy's exact value doesn't matter, because we use it like a boolean + JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; joystick_started = 1; } else { if (JoyInfo.oldjoy) I_ShutdownJoystick(); - cv_usejoystick.value = 0; joystick_started = 0; } } @@ -1423,16 +1423,18 @@ void I_InitJoystick2(void) } } - if (strcmp(cv_usejoystick2.string, "0") && joy_open2(cv_usejoystick2.string) != -1) + if (cv_usejoystick2.value && joy_open2(cv_usejoystick2.value) != -1) { - JoyInfo2.oldjoy = atoi(cv_usejoystick2.string); + // SDL's device indexes are unstable, so cv_usejoystick2 may not match + // the actual device index. So let's cheat a bit and use the instance ID. + // oldjoy's exact value doesn't matter, because we use it like a boolean + JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; joystick2_started = 1; } else { if (JoyInfo2.oldjoy) I_ShutdownJoystick2(); - cv_usejoystick2.value = 0; joystick2_started = 0; } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 51da55cb2..6fc8f5779 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -885,7 +885,27 @@ void I_GetEvent(void) case SDL_JOYDEVICEADDED: CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); - // recount hotplugged joysticks + // Because SDL's device index is unstable, we're going to cheat here a bit: + // For the first joystick setting that is NOT active: + // Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) + // Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed + if (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) + { + cv_usejoystick.value = evt.jdevice.which + 1; + + if (JoyInfo2.dev) + cv_usejoystick2.value = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + } + else if (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) + { + cv_usejoystick2.value = evt.jdevice.which + 1; + + if (JoyInfo.dev) + cv_usejoystick.value = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + } + + // If an active joystick's index has changed, these will just + // change the corresponding JoyInfo.oldjoy I_InitJoystick(); I_InitJoystick2(); @@ -909,6 +929,13 @@ void I_GetEvent(void) I_ShutdownJoystick2(); } + // Update the device indexes, because they likely changed + if (JoyInfo.dev) + JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + + if (JoyInfo2.dev) + JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); From 6b9fe87b60de3da7ef2f2b4e3553fb34b1b557ad Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 01:45:18 -0500 Subject: [PATCH 008/196] Properly handle unstable device indexes for hotplug --- src/sdl/i_system.c | 27 +++++++++++++++++++++------ src/sdl/i_video.c | 36 ++++++++++++++++++++++++++++++++---- src/sdl/sdlmain.h | 3 +++ 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 6ac41de16..7cbaf6d35 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -828,6 +828,23 @@ void I_JoyScale2(void) JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value; } +// Cheat to get the device index for a joystick handle +INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev) +{ + INT32 i, count = SDL_NumJoysticks(); + + for (i = 0; dev && i < count; i++) + { + SDL_Joystick *test = SDL_JoystickOpen(i); + if (test && test == dev) + return i; + else if (JoyInfo.dev != test && JoyInfo2.dev != test) + SDL_JoystickClose(test); + } + + return -1; +} + /** \brief Joystick 1 buttons states */ static UINT64 lastjoybuttons = 0; @@ -1393,9 +1410,8 @@ void I_InitJoystick(void) if (cv_usejoystick.value && joy_open(cv_usejoystick.value) != -1) { // SDL's device indexes are unstable, so cv_usejoystick may not match - // the actual device index. So let's cheat a bit and use the instance ID. - // oldjoy's exact value doesn't matter, because we use it like a boolean - JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + // the actual device index. So let's cheat a bit and find the device's current index. + JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; joystick_started = 1; } else @@ -1426,9 +1442,8 @@ void I_InitJoystick2(void) if (cv_usejoystick2.value && joy_open2(cv_usejoystick2.value) != -1) { // SDL's device indexes are unstable, so cv_usejoystick2 may not match - // the actual device index. So let's cheat a bit and use the instance ID. - // oldjoy's exact value doesn't matter, because we use it like a boolean - JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + // the actual device index. So let's cheat a bit and find the device's current index. + JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; joystick2_started = 1; } else diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 6fc8f5779..f6f193038 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -889,19 +889,33 @@ void I_GetEvent(void) // For the first joystick setting that is NOT active: // Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) // Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed + // If device doesn't exist, switch cv_usejoystick back to default value (.string) + // BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! if (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) { cv_usejoystick.value = evt.jdevice.which + 1; if (JoyInfo2.dev) - cv_usejoystick2.value = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick2.string); + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick.string); + else // we tried... + cv_usejoystick2.value = 0; } else if (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) { cv_usejoystick2.value = evt.jdevice.which + 1; if (JoyInfo.dev) - cv_usejoystick.value = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick.string); + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick2.string); + else // we tried... + cv_usejoystick.value = 0; } // If an active joystick's index has changed, these will just @@ -930,11 +944,25 @@ void I_GetEvent(void) } // Update the device indexes, because they likely changed + // If device doesn't exist, switch cv_usejoystick back to default value (.string) + // BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! if (JoyInfo.dev) - JoyInfo.oldjoy = SDL_JoystickInstanceID(JoyInfo.dev) + 1; + cv_usejoystick.value = JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick.string); + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick2.string); + else // we tried... + cv_usejoystick.value = 0; if (JoyInfo2.dev) - JoyInfo2.oldjoy = SDL_JoystickInstanceID(JoyInfo2.dev) + 1; + cv_usejoystick2.value = JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick2.string); + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick.string); + else // we tried... + cv_usejoystick2.value = 0; CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index 4acbce209..b858e1f6f 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -71,6 +71,9 @@ extern SDLJoyInfo_t JoyInfo2; void I_ShutdownJoystick(void); void I_ShutdownJoystick2(void); +// Cheat to get the device index for a joystick handle +INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev); + void I_GetConsoleEvents(void); void SDLforceUngrabMouse(void); From 3b6de810e11ea90177ab65e21184766172111fea Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 03:58:07 -0500 Subject: [PATCH 009/196] Lots of changes to better support hotplugging and unstable device indexes * Don't save cv_usejoystick/2 because hotplugging changes can be made invalid by next run * Properly set cv_usejoystick/2 for menu toggling * Force-disable a player's joystick if setting another player's joystick to the same device * Properly set cv_usejoystick/2 value of BOTH target player AND other players when hotplugging and unplugging --- src/d_netcmd.c | 24 +++++++++++++++++---- src/m_menu.c | 29 ++++++++++++++++++++++++++ src/sdl/i_system.c | 18 ++++++++++++++++ src/sdl/i_video.c | 52 +++++++++++++++++++++++++++++++++++----------- src/sdl/sdlmain.h | 3 +++ 5 files changed, 110 insertions(+), 16 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8abfb8709..16c08309f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -46,6 +46,13 @@ #include "m_cond.h" #include "m_anigif.h" +#if defined(HAVE_SDL) +#include "SDL.h" +#if SDL_VERSION_ATLEAST(2,0,0) +#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG +#endif +#endif + #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR #else @@ -243,10 +250,19 @@ consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_S consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL}; #if defined (DC) || defined (_XBOX) || defined (WMINPUT) || defined (_WII) || defined(HAVE_SDL) || defined(_WINDOWS) //joystick 1 and 2 -consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, - I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, - I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; +// JOYSTICK_HOTPLUG is set by sdlmain.h (SDL2) +// because SDL joystick indexes are unstable, and hotplugging can change a device's index. +// So let's not save any index changes to the config +consvar_t cv_usejoystick = {"use_joystick", "1", CV_CALL +#ifndef JOYSTICK_HOTPLUG + |CV_SAVE +#endif + , usejoystick_cons_t, I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_CALL +#ifndef JOYSTICK_HOTPLUG + |CV_SAVE +#endif + , usejoystick_cons_t, I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; #elif defined (PSP) || defined (GP2X) || defined (_NDS) //only one joystick consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/m_menu.c b/src/m_menu.c index a4c62c0f3..7bac5fa24 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -62,6 +62,13 @@ // And just some randomness for the exits. #include "m_random.h" +#if defined(HAVE_SDL) +#include "SDL.h" +#if SDL_VERSION_ATLEAST(2,0,0) +#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG +#endif +#endif + #ifdef PC_DOS #include // for snprintf int snprintf(char *str, size_t n, const char *fmt, ...); @@ -6770,12 +6777,34 @@ void M_SetupJoystickMenu(INT32 choice) strcpy(joystickInfo[i], "None"); +#ifdef JOYSTICK_HOTPLUG + if (0 == cv_usejoystick.value) + CV_SetValue(&cv_usejoystick, 0); + if (0 == cv_usejoystick2.value) + CV_SetValue(&cv_usejoystick2, 0); +#endif + for (i = 1; i < 8; i++) { if (i <= n && (I_GetJoyName(i)) != NULL) strncpy(joystickInfo[i], I_GetJoyName(i), 28); else strcpy(joystickInfo[i], joyNA); + +#ifdef JOYSTICK_HOTPLUG + // We use cv_usejoystick.string as the USER-SET var + // and cv_usejoystick.value as the INTERNAL var + // + // In practice, if cv_usejoystick.string == 0, this overrides + // cv_usejoystick.value and always disables + // + // Update cv_usejoystick.string here so that the user can + // properly change this value. + if (i == cv_usejoystick.value) + CV_SetValue(&cv_usejoystick, i); + if (i == cv_usejoystick2.value) + CV_SetValue(&cv_usejoystick2, i); +#endif } M_SetupNextMenu(&OP_JoystickSetDef); diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 7cbaf6d35..e6c1b0b5e 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1413,11 +1413,20 @@ void I_InitJoystick(void) // the actual device index. So let's cheat a bit and find the device's current index. JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; joystick_started = 1; + + // If another joystick occupied this device, deactivate that joystick + if (JoyInfo2.dev == JoyInfo.dev) + { + CONS_Debug(DBG_GAMELOGIC, "Joystick2 was set to the same device; disabling...\n"); + cv_usejoystick2.value = 0; + I_InitJoystick2(); + } } else { if (JoyInfo.oldjoy) I_ShutdownJoystick(); + cv_usejoystick.value = 0; joystick_started = 0; } } @@ -1445,11 +1454,20 @@ void I_InitJoystick2(void) // the actual device index. So let's cheat a bit and find the device's current index. JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; joystick2_started = 1; + + // If another joystick occupied this device, deactivate that joystick + if (JoyInfo.dev == JoyInfo2.dev) + { + CONS_Debug(DBG_GAMELOGIC, "Joystick1 was set to the same device; disabling...\n"); + cv_usejoystick.value = 0; + I_InitJoystick(); + } } else { if (JoyInfo2.oldjoy) I_ShutdownJoystick2(); + cv_usejoystick2.value = 0; joystick2_started = 0; } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index f6f193038..24228aa8e 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -887,19 +887,21 @@ void I_GetEvent(void) // Because SDL's device index is unstable, we're going to cheat here a bit: // For the first joystick setting that is NOT active: - // Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) - // Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed - // If device doesn't exist, switch cv_usejoystick back to default value (.string) - // BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! + // 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) + // 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed + // * If device doesn't exist, switch cv_usejoystick back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! if (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) { cv_usejoystick.value = evt.jdevice.which + 1; if (JoyInfo2.dev) cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy) + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick.value) cv_usejoystick2.value = atoi(cv_usejoystick2.string); - else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy) + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick.value) cv_usejoystick2.value = atoi(cv_usejoystick.string); else // we tried... cv_usejoystick2.value = 0; @@ -910,16 +912,31 @@ void I_GetEvent(void) if (JoyInfo.dev) cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick2.value) cv_usejoystick.value = atoi(cv_usejoystick.string); - else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy) + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick2.value) cv_usejoystick.value = atoi(cv_usejoystick2.string); else // we tried... cv_usejoystick.value = 0; } - // If an active joystick's index has changed, these will just - // change the corresponding JoyInfo.oldjoy + // Was cv_usejoystick disabled in settings? + if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) + cv_usejoystick.value = 0; + else if (cv_usejoystick.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) + cv_usejoystick2.value = 0; + else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + // Update all joysticks' init states + // This is a little wasteful since cv_usejoystick already calls this, but + // we need to do this in case CV_SetValue did nothing because the string was already same. + // if the device is already active, this should do nothing, effectively. I_InitJoystick(); I_InitJoystick2(); @@ -944,8 +961,8 @@ void I_GetEvent(void) } // Update the device indexes, because they likely changed - // If device doesn't exist, switch cv_usejoystick back to default value (.string) - // BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! + // * If device doesn't exist, switch cv_usejoystick back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! if (JoyInfo.dev) cv_usejoystick.value = JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) @@ -964,6 +981,17 @@ void I_GetEvent(void) else // we tried... cv_usejoystick2.value = 0; + // Was cv_usejoystick disabled in settings? + if (!strcmp(cv_usejoystick.string, "0")) + cv_usejoystick.value = 0; + else if (cv_usejoystick.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (!strcmp(cv_usejoystick2.string, "0")) + cv_usejoystick2.value = 0; + else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index b858e1f6f..810c7dce1 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -31,6 +31,9 @@ extern SDL_bool framebuffer; #define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__) #endif +// So m_menu knows whether to store cv_usejoystick value or string +#define JOYSTICK_HOTPLUG + /** \brief The JoyInfo_s struct info about joystick From e548f6f139a69cfd65bb19ac0d7b8db17983bcb6 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 05:02:41 -0500 Subject: [PATCH 010/196] Don't override an already-active controller * Menu improvements to tell the user that they can't set a controller if it's already active --- src/m_menu.c | 54 +++++++++++++++++++- src/sdl/i_system.c | 41 ++++++++------- src/sdl/i_video.c | 125 ++++++++++++++++++++++++--------------------- 3 files changed, 142 insertions(+), 78 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 7bac5fa24..21d7d25f2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6777,7 +6777,9 @@ void M_SetupJoystickMenu(INT32 choice) strcpy(joystickInfo[i], "None"); -#ifdef JOYSTICK_HOTPLUG + // Hotplugging breaks if this block is run + // Because the cvar is set to 0, which disables controllers for that player +#if 0 // #ifdef JOYSTICK_HOTPLUG if (0 == cv_usejoystick.value) CV_SetValue(&cv_usejoystick, 0); if (0 == cv_usejoystick2.value) @@ -6826,10 +6828,60 @@ static void M_Setup2PJoystickMenu(INT32 choice) static void M_AssignJoystick(INT32 choice) { +#ifdef JOYSTICK_HOTPLUG + INT32 oldchoice; + + if (choice > I_NumJoys()) + return; + + if (setupcontrols_secondaryplayer) + { + oldchoice = cv_usejoystick2.value; + CV_SetValue(&cv_usejoystick2, choice); + + // Just in case last-minute changes were made to cv_usejoystick.value, + // update the string too + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + if (oldchoice != choice) + { + if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick2, oldchoice); + + if (oldchoice == cv_usejoystick2.value) + M_StartMessage("This joystick is used by another\n" + "player. Reset the joystick\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } + } + else + { + oldchoice = cv_usejoystick.value; + CV_SetValue(&cv_usejoystick, choice); + + // Just in case last-minute changes were made to cv_usejoystick.value, + // update the string too + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (oldchoice != choice) + { + if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick, oldchoice); + + if (oldchoice == cv_usejoystick.value) + M_StartMessage("This joystick is used by another\n" + "player. Reset the joystick\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } + } +#else if (setupcontrols_secondaryplayer) CV_SetValue(&cv_usejoystick2, choice); else CV_SetValue(&cv_usejoystick, choice); +#endif } // ============= diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index e6c1b0b5e..bf8fdbcf3 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1392,6 +1392,8 @@ static int joy_open2(int joyindex) // void I_InitJoystick(void) { + SDL_Joystick *newjoy = NULL; + //I_ShutdownJoystick(); if (M_CheckParm("-nojoy")) return; @@ -1407,20 +1409,17 @@ void I_InitJoystick(void) } } - if (cv_usejoystick.value && joy_open(cv_usejoystick.value) != -1) + if (cv_usejoystick.value) + newjoy = SDL_JoystickOpen(cv_usejoystick.value-1); + + if (newjoy && JoyInfo2.dev == newjoy) // don't override an active device + cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (newjoy && joy_open(cv_usejoystick.value) != -1) { // SDL's device indexes are unstable, so cv_usejoystick may not match // the actual device index. So let's cheat a bit and find the device's current index. JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; joystick_started = 1; - - // If another joystick occupied this device, deactivate that joystick - if (JoyInfo2.dev == JoyInfo.dev) - { - CONS_Debug(DBG_GAMELOGIC, "Joystick2 was set to the same device; disabling...\n"); - cv_usejoystick2.value = 0; - I_InitJoystick2(); - } } else { @@ -1429,10 +1428,15 @@ void I_InitJoystick(void) cv_usejoystick.value = 0; joystick_started = 0; } + + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); } void I_InitJoystick2(void) { + SDL_Joystick *newjoy = NULL; + //I_ShutdownJoystick2(); if (M_CheckParm("-nojoy")) return; @@ -1448,20 +1452,17 @@ void I_InitJoystick2(void) } } - if (cv_usejoystick2.value && joy_open2(cv_usejoystick2.value) != -1) + if (cv_usejoystick2.value) + newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1); + + if (newjoy && JoyInfo.dev == newjoy) // don't override an active device + cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (newjoy && joy_open2(cv_usejoystick2.value) != -1) { - // SDL's device indexes are unstable, so cv_usejoystick2 may not match + // SDL's device indexes are unstable, so cv_usejoystick may not match // the actual device index. So let's cheat a bit and find the device's current index. JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; joystick2_started = 1; - - // If another joystick occupied this device, deactivate that joystick - if (JoyInfo.dev == JoyInfo2.dev) - { - CONS_Debug(DBG_GAMELOGIC, "Joystick1 was set to the same device; disabling...\n"); - cv_usejoystick.value = 0; - I_InitJoystick(); - } } else { @@ -1471,6 +1472,8 @@ void I_InitJoystick2(void) joystick2_started = 0; } + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); } static void I_ShutdownInput(void) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 24228aa8e..318777f1c 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -883,69 +883,78 @@ void I_GetEvent(void) Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); break; case SDL_JOYDEVICEADDED: - CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); - - // Because SDL's device index is unstable, we're going to cheat here a bit: - // For the first joystick setting that is NOT active: - // 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) - // 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed - // * If device doesn't exist, switch cv_usejoystick back to default value (.string) - // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! - if (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) { - cv_usejoystick.value = evt.jdevice.which + 1; + SDL_Joystick *newjoy = SDL_JoystickOpen(evt.jdevice.which); - if (JoyInfo2.dev) - cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy - && atoi(cv_usejoystick2.string) != cv_usejoystick.value) - cv_usejoystick2.value = atoi(cv_usejoystick2.string); - else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy - && atoi(cv_usejoystick.string) != cv_usejoystick.value) - cv_usejoystick2.value = atoi(cv_usejoystick.string); - else // we tried... - cv_usejoystick2.value = 0; - } - else if (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) - { - cv_usejoystick2.value = evt.jdevice.which + 1; + CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); - if (JoyInfo.dev) - cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy - && atoi(cv_usejoystick.string) != cv_usejoystick2.value) - cv_usejoystick.value = atoi(cv_usejoystick.string); - else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy - && atoi(cv_usejoystick2.string) != cv_usejoystick2.value) - cv_usejoystick.value = atoi(cv_usejoystick2.string); - else // we tried... + // Because SDL's device index is unstable, we're going to cheat here a bit: + // For the first joystick setting that is NOT active: + // 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) + // 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed + // * If device doesn't exist, switch cv_usejoystick back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! + if (newjoy && (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) + && JoyInfo2.dev != newjoy) // don't override a currently active device + { + cv_usejoystick.value = evt.jdevice.which + 1; + + if (JoyInfo2.dev) + cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick.value) + cv_usejoystick2.value = atoi(cv_usejoystick2.string); + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick.value) + cv_usejoystick2.value = atoi(cv_usejoystick.string); + else // we tried... + cv_usejoystick2.value = 0; + } + else if (newjoy && (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) + && JoyInfo.dev != newjoy) // don't override a currently active device + { + cv_usejoystick2.value = evt.jdevice.which + 1; + + if (JoyInfo.dev) + cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick2.value) + cv_usejoystick.value = atoi(cv_usejoystick.string); + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick2.value) + cv_usejoystick.value = atoi(cv_usejoystick2.string); + else // we tried... + cv_usejoystick.value = 0; + } + + // Was cv_usejoystick disabled in settings? + if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) cv_usejoystick.value = 0; + else if (cv_usejoystick.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) + cv_usejoystick2.value = 0; + else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + // Update all joysticks' init states + // This is a little wasteful since cv_usejoystick already calls this, but + // we need to do this in case CV_SetValue did nothing because the string was already same. + // if the device is already active, this should do nothing, effectively. + I_InitJoystick(); + I_InitJoystick2(); + + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); + + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); } - - // Was cv_usejoystick disabled in settings? - if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) - cv_usejoystick.value = 0; - else if (cv_usejoystick.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - - if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) - cv_usejoystick2.value = 0; - else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - - // Update all joysticks' init states - // This is a little wasteful since cv_usejoystick already calls this, but - // we need to do this in case CV_SetValue did nothing because the string was already same. - // if the device is already active, this should do nothing, effectively. - I_InitJoystick(); - I_InitJoystick2(); - - CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); - - // update the menu - if (currentMenu == &OP_JoystickSetDef) - M_SetupJoystickMenu(0); break; case SDL_JOYDEVICEREMOVED: if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev)) From b2c02838c4a3ab2641ab1ca69b6d987c4330dd13 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 05:31:47 -0500 Subject: [PATCH 011/196] Display "joystick used" prompt correctly if setting use_joystick from an old value > I_NumJoys() --- src/m_menu.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 21d7d25f2..7b440d46f 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6751,7 +6751,7 @@ static void M_ScreenshotOptions(INT32 choice) static void M_DrawJoystick(void) { - INT32 i; + INT32 i, compareval2, compareval; M_DrawGenericMenu(); @@ -6760,8 +6760,23 @@ static void M_DrawJoystick(void) M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); //M_DrawSaveLoadBorder(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i); - if ((setupcontrols_secondaryplayer && (i == cv_usejoystick2.value)) - || (!setupcontrols_secondaryplayer && (i == cv_usejoystick.value))) +#ifdef JOYSTICK_HOTPLUG + if (atoi(cv_usejoystick2.string) > I_NumJoys()) + compareval2 = atoi(cv_usejoystick2.string); + else + compareval2 = cv_usejoystick2.value; + + if (atoi(cv_usejoystick.string) > I_NumJoys()) + compareval = atoi(cv_usejoystick.string); + else + compareval = cv_usejoystick.value; +#else + compareval2 = cv_usejoystick2.value; + compareval = cv_usejoystick.value +#endif + + if ((setupcontrols_secondaryplayer && (i == compareval2)) + || (!setupcontrols_secondaryplayer && (i == compareval))) V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]); else V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]); @@ -6836,7 +6851,7 @@ static void M_AssignJoystick(INT32 choice) if (setupcontrols_secondaryplayer) { - oldchoice = cv_usejoystick2.value; + oldchoice = atoi(cv_usejoystick2.string) > I_NumJoys() ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; CV_SetValue(&cv_usejoystick2, choice); // Just in case last-minute changes were made to cv_usejoystick.value, @@ -6848,7 +6863,8 @@ static void M_AssignJoystick(INT32 choice) if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick2, oldchoice); - if (oldchoice == cv_usejoystick2.value) + if (oldchoice == + (atoi(cv_usejoystick2.string) > I_NumJoys() ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" @@ -6857,7 +6873,7 @@ static void M_AssignJoystick(INT32 choice) } else { - oldchoice = cv_usejoystick.value; + oldchoice = atoi(cv_usejoystick.string) > I_NumJoys() ? atoi(cv_usejoystick.string) : cv_usejoystick.value; CV_SetValue(&cv_usejoystick, choice); // Just in case last-minute changes were made to cv_usejoystick.value, @@ -6869,7 +6885,8 @@ static void M_AssignJoystick(INT32 choice) if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick, oldchoice); - if (oldchoice == cv_usejoystick.value) + if (oldchoice == + (atoi(cv_usejoystick.string) > I_NumJoys() ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" From 70d6845d6cb8d542709d5818432f2cc833224e6f Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 05:57:10 -0500 Subject: [PATCH 012/196] Fix menu where cv_usejoystick.value > I_JoyNum and selecting an unused controller and the "used!" prompt pops up erroneously --- src/m_menu.c | 77 +++++++++++++++++++++++++---------------------- src/sdl/i_video.c | 12 +++++--- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 7b440d46f..a07123812 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6792,15 +6792,6 @@ void M_SetupJoystickMenu(INT32 choice) strcpy(joystickInfo[i], "None"); - // Hotplugging breaks if this block is run - // Because the cvar is set to 0, which disables controllers for that player -#if 0 // #ifdef JOYSTICK_HOTPLUG - if (0 == cv_usejoystick.value) - CV_SetValue(&cv_usejoystick, 0); - if (0 == cv_usejoystick2.value) - CV_SetValue(&cv_usejoystick2, 0); -#endif - for (i = 1; i < 8; i++) { if (i <= n && (I_GetJoyName(i)) != NULL) @@ -6845,52 +6836,66 @@ static void M_AssignJoystick(INT32 choice) { #ifdef JOYSTICK_HOTPLUG INT32 oldchoice; - - if (choice > I_NumJoys()) - return; + INT32 numjoys = I_NumJoys(); if (setupcontrols_secondaryplayer) { - oldchoice = atoi(cv_usejoystick2.string) > I_NumJoys() ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; + oldchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; CV_SetValue(&cv_usejoystick2, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too - CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - - if (oldchoice != choice) + // But don't do this if we're intentionally setting higher than numjoys + if (choice <= numjoys) { - if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick2, oldchoice); + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - if (oldchoice == - (atoi(cv_usejoystick2.string) > I_NumJoys() ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) - M_StartMessage("This joystick is used by another\n" - "player. Reset the joystick\n" - "for that player first.\n\n" - "(Press a key)\n", NULL, MM_NOTHING); + // reset this so the comparison is valid + if (oldchoice > numjoys) + oldchoice = cv_usejoystick2.value; + + if (oldchoice != choice) + { + if (choice && oldchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick2, oldchoice); + + if (oldchoice == + (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) + M_StartMessage("This joystick is used by another\n" + "player. Reset the joystick\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } } } else { - oldchoice = atoi(cv_usejoystick.string) > I_NumJoys() ? atoi(cv_usejoystick.string) : cv_usejoystick.value; + oldchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; CV_SetValue(&cv_usejoystick, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too - CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - - if (oldchoice != choice) + // But don't do this if we're intentionally setting higher than numjoys + if (choice <= numjoys) { - if (choice && oldchoice > I_NumJoys()) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick, oldchoice); + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - if (oldchoice == - (atoi(cv_usejoystick.string) > I_NumJoys() ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) - M_StartMessage("This joystick is used by another\n" - "player. Reset the joystick\n" - "for that player first.\n\n" - "(Press a key)\n", NULL, MM_NOTHING); + // reset this so the comparison is valid + if (oldchoice > numjoys) + oldchoice = cv_usejoystick.value; + + if (oldchoice != choice) + { + if (choice && oldchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick, oldchoice); + + if (oldchoice == + (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) + M_StartMessage("This joystick is used by another\n" + "player. Reset the joystick\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } } } #else diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 318777f1c..4419284e9 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -930,12 +930,14 @@ void I_GetEvent(void) // Was cv_usejoystick disabled in settings? if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) cv_usejoystick.value = 0; - else if (cv_usejoystick.value) // update the cvar ONLY if a device exists + else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick.value) // update the cvar ONLY if a device exists CV_SetValue(&cv_usejoystick, cv_usejoystick.value); if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) cv_usejoystick2.value = 0; - else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists + else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick2.value) // update the cvar ONLY if a device exists CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); // Update all joysticks' init states @@ -993,12 +995,14 @@ void I_GetEvent(void) // Was cv_usejoystick disabled in settings? if (!strcmp(cv_usejoystick.string, "0")) cv_usejoystick.value = 0; - else if (cv_usejoystick.value) // update the cvar ONLY if a device exists + else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick.value) // update the cvar ONLY if a device exists CV_SetValue(&cv_usejoystick, cv_usejoystick.value); if (!strcmp(cv_usejoystick2.string, "0")) cv_usejoystick2.value = 0; - else if (cv_usejoystick2.value) // update the cvar ONLY if a device exists + else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick2.value) // update the cvar ONLY if a device exists CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); From fa63ddda86fac929a4e6dea92eb0c627e8d6f090 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 06:05:31 -0500 Subject: [PATCH 013/196] Fix cv_usejoystick being reset to None when cv_usejoystick was > NumJoys and changing to already-used joystick --- src/m_menu.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index a07123812..9b5e10987 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6835,12 +6835,12 @@ static void M_Setup2PJoystickMenu(INT32 choice) static void M_AssignJoystick(INT32 choice) { #ifdef JOYSTICK_HOTPLUG - INT32 oldchoice; + INT32 oldchoice, oldstringchoice; INT32 numjoys = I_NumJoys(); if (setupcontrols_secondaryplayer) { - oldchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; + oldchoice = oldstringchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; CV_SetValue(&cv_usejoystick2, choice); // Just in case last-minute changes were made to cv_usejoystick.value, @@ -6856,10 +6856,10 @@ static void M_AssignJoystick(INT32 choice) if (oldchoice != choice) { - if (choice && oldchoice > numjoys) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick2, oldchoice); + if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick2, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); - if (oldchoice == + if (oldstringchoice == (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" @@ -6870,7 +6870,7 @@ static void M_AssignJoystick(INT32 choice) } else { - oldchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; + oldchoice = oldstringchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; CV_SetValue(&cv_usejoystick, choice); // Just in case last-minute changes were made to cv_usejoystick.value, @@ -6886,10 +6886,10 @@ static void M_AssignJoystick(INT32 choice) if (oldchoice != choice) { - if (choice && oldchoice > numjoys) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick, oldchoice); + if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); - if (oldchoice == + if (oldstringchoice == (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" From d094a70ec3872276b1bd2fb8cfddf15b59bcc7e9 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 06:06:37 -0500 Subject: [PATCH 014/196] Allow use_joystick/2 to be saved once again --- src/d_netcmd.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 16c08309f..8abfb8709 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -46,13 +46,6 @@ #include "m_cond.h" #include "m_anigif.h" -#if defined(HAVE_SDL) -#include "SDL.h" -#if SDL_VERSION_ATLEAST(2,0,0) -#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG -#endif -#endif - #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR #else @@ -250,19 +243,10 @@ consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_S consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL}; #if defined (DC) || defined (_XBOX) || defined (WMINPUT) || defined (_WII) || defined(HAVE_SDL) || defined(_WINDOWS) //joystick 1 and 2 -// JOYSTICK_HOTPLUG is set by sdlmain.h (SDL2) -// because SDL joystick indexes are unstable, and hotplugging can change a device's index. -// So let's not save any index changes to the config -consvar_t cv_usejoystick = {"use_joystick", "1", CV_CALL -#ifndef JOYSTICK_HOTPLUG - |CV_SAVE -#endif - , usejoystick_cons_t, I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_CALL -#ifndef JOYSTICK_HOTPLUG - |CV_SAVE -#endif - , usejoystick_cons_t, I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, + I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, + I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; #elif defined (PSP) || defined (GP2X) || defined (_NDS) //only one joystick consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; From c49a5ed8d2947ed4d14dc1626c161f6f55333ccf Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 14 Dec 2018 06:14:21 -0500 Subject: [PATCH 015/196] Better I_InitJoystick log message --- src/sdl/i_system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index bf8fdbcf3..c152346c5 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1400,7 +1400,7 @@ void I_InitJoystick(void) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { - CONS_Printf("Initing joy system\n"); + CONS_Printf("I_InitJoystick()...\n"); SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { @@ -1443,7 +1443,7 @@ void I_InitJoystick2(void) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { - CONS_Printf("Initing joy system\n"); + CONS_Printf("I_InitJoystick2()...\n"); SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { From 89206931598609c2d93f2459f8506c180fe51336 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 28 Feb 2019 21:52:01 +0000 Subject: [PATCH 016/196] Saving work made so far improving and optimizing addons menu code, also adding some macros and comments for readability --- src/m_menu.c | 83 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 1c3873613..daf1a4b57 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -304,7 +304,8 @@ static void M_Addons(INT32 choice); static void M_AddonsOptions(INT32 choice); static patch_t *addonsp[NUM_EXT+5]; -#define numaddonsshown 4 +#define addonmenusize 9 // number of items actually displayed in the addons menu view, formerly (2*numaddonsshown + 1) +#define numaddonsshown 4 // number of items to each side of the currently selected item, unless at top/bottom ends of directory static void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight, boolean allowlowercase); @@ -4987,7 +4988,8 @@ static boolean M_AddonsRefresh(void) static void M_DrawAddons(void) { INT32 x, y; - ssize_t i, m; + size_t i, m; + size_t t, b; // top and bottom item #s to draw in directory const UINT8 *flashcol = NULL; UINT8 hilicol; @@ -5026,52 +5028,63 @@ static void M_DrawAddons(void) hilicol = 0; // white +#define boxwidth (MAXSTRINGLENGTH*8+6) + + // draw the file path and the top white + black lines of the box V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath()); - V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol); - V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30); + V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), boxwidth, 1, hilicol); + V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), boxwidth, 1, 30); m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1); // addons menu back color - V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159); + V_DrawFill(x-21, y - 1, boxwidth, m, 159); - // scrollbar! - if (sizedirmenu <= (2*numaddonsshown + 1)) - i = 0; + // The directory is too small for a scrollbar, so just draw a tall white line + if (sizedirmenu <= addonmenusize) + { + t = 0; // first item + b = sizedirmenu - 1; // last item + i = 0; // "scrollbar" at "top" position + } else { - ssize_t q = m; - m = ((2*numaddonsshown + 1) * m)/sizedirmenu; + size_t q = m; + m = (addonmenusize * m)/sizedirmenu; // height of scroll bar if (dir_on[menudepthleft] <= numaddonsshown) // all the way up - i = 0; - else if (sizedirmenu <= (dir_on[menudepthleft] + numaddonsshown + 1)) // all the way down - i = q-m; - else - i = ((dir_on[menudepthleft] - numaddonsshown) * (q-m))/(sizedirmenu - (2*numaddonsshown + 1)); + { + t = 0; // first item + b = addonmenusize - 1; //9th item + i = 0; // scrollbar at top position + } + else if (dir_on[menudepthleft] >= sizedirmenu - (numaddonsshown + 1)) // all the way down + { + t = sizedirmenu - addonmenusize; // # 9th last + b = sizedirmenu - 1; // last item + i = q-m; // scrollbar at bottom position + } + else // somewhere in the middle + { + t = dir_on[menudepthleft] - numaddonsshown; // 4 items above + b = dir_on[menudepthleft] + numaddonsshown; // 4 items below + i = (t * (q-m))/(sizedirmenu - addonmenusize); // calculate position of scrollbar + } } - V_DrawFill(x + MAXSTRINGLENGTH*8+5 - 21, (y - 1) + i, 1, m, hilicol); + // draw the scrollbar! + V_DrawFill((x-21) + addons_boxwidth-1, (y - 1) + i, 1, m, hilicol); - // get bottom... - m = dir_on[menudepthleft] + numaddonsshown + 1; - if (m > (ssize_t)sizedirmenu) - m = sizedirmenu; +#undef boxwidth - // then compute top and adjust bottom if needed! - if (m < (2*numaddonsshown + 1)) - { - m = min(sizedirmenu, 2*numaddonsshown + 1); - i = 0; - } - else - i = m - (2*numaddonsshown + 1); - - if (i != 0) + // draw up arrow that bobs up and down + if (t != 0) V_DrawString(19, y+4 - (skullAnimCounter/5), highlightflags, "\x1A"); + // make the selection box flash yellow if (skullAnimCounter < 4) flashcol = V_GetStringColormap(highlightflags); - for (; i < m; i++) + // draw icons and item names + for (i = t; i <= b; i++) { UINT32 flags = V_ALLOWLOWERCASE; if (y > BASEVIDHEIGHT) break; @@ -5087,12 +5100,14 @@ static void M_DrawAddons(void) else V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]); + // draw selection box for the item currently selected if ((size_t)i == dir_on[menudepthleft]) { V_DrawFixedPatch((x-(16+4))< (charsonside*2 + 3)) V_DrawString(x, y+4, flags, va("%.*s...%s", charsonside, dirmenu[i]+DIR_STRING, dirmenu[i]+DIR_STRING+dirmenu[i][DIR_LEN]-(charsonside+1))); @@ -5104,9 +5119,11 @@ static void M_DrawAddons(void) y += 16; } - if (m != (ssize_t)sizedirmenu) + // draw down arrow that bobs down and up + if (b != sizedirmenu) V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B"); + // draw search box y = BASEVIDHEIGHT - currentMenu->y + 1; M_DrawTextBox(x - (21 + 5), y, MAXSTRINGLENGTH, 1); @@ -5118,9 +5135,11 @@ static void M_DrawAddons(void) V_DrawCharacter(x - 18 + V_StringWidth(menusearch+1, 0), y + 8, '_' | 0x80, false); + // draw search icon x -= (21 + 5 + 16); V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]); + // draw save icon x = BASEVIDWIDTH - x - 16; V_DrawSmallScaledPatch(x, y + 4, ((!modifiedgame || savemoddata) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); From 27147a9e0b08662889909894c3527ef31979802f Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 31 Mar 2019 14:11:27 +0100 Subject: [PATCH 017/196] fix slipup of mine that would have prevented this thing compiling --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index daf1a4b57..ece590679 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5071,7 +5071,7 @@ static void M_DrawAddons(void) } // draw the scrollbar! - V_DrawFill((x-21) + addons_boxwidth-1, (y - 1) + i, 1, m, hilicol); + V_DrawFill((x-21) + boxwidth-1, (y - 1) + i, 1, m, hilicol); #undef boxwidth From 714c997aace85b7f873fa0c1a97224cec691baeb Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 21 May 2019 00:28:52 -0300 Subject: [PATCH 018/196] Support for patches and textures to be used in place of sector flats Still a work in progress. --- src/dehacked.c | 9 +- src/hardware/hw_cache.c | 81 ++++++- src/hardware/hw_glob.h | 3 + src/hardware/hw_main.c | 178 ++++++++------ src/p_setup.c | 4 + src/p_setup.h | 11 + src/p_spec.c | 4 +- src/r_data.c | 204 ++++++++++++++-- src/r_data.h | 12 +- src/r_draw.c | 2 + src/r_draw.h | 13 +- src/r_draw8.c | 499 ++++++++++++++++++---------------------- src/r_plane.c | 354 ++++++++++++++++------------ src/screen.c | 2 +- 14 files changed, 841 insertions(+), 535 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index a726ecbc1..bdef5f1be 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2699,7 +2699,7 @@ static void readtexture(MYFILE *f, const char *name) char *word; char *word2; char *tmp; - INT32 i, j, value; + INT32 i, value; UINT16 width = 0, height = 0; INT16 patchcount = 0; texture_t *texture; @@ -2783,13 +2783,8 @@ static void readtexture(MYFILE *f, const char *name) while (textures[i]) i++; - // Fill the global texture buffer entries. - j = 1; - while (j << 1 <= texture->width) - j <<= 1; - textures[i] = texture; - texturewidthmask[i] = j - 1; + texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; // Clean up. diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 78fc31afc..4ae7a43b9 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -30,6 +30,7 @@ #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" +#include "../p_setup.h" //Hurdler: 25/04/2000: used for new colormap code in hardware mode //static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized) @@ -551,11 +552,13 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm static size_t gr_numtextures; static GLTexture_t *gr_textures; // for ALL Doom textures +static GLTexture_t *gr_textures2; void HWR_InitTextureCache(void) { gr_numtextures = 0; gr_textures = NULL; + gr_textures2 = NULL; } @@ -594,7 +597,10 @@ void HWR_FreeTextureCache(void) // texturecache info, we can free it if (gr_textures) free(gr_textures); + if (gr_textures2) + free(gr_textures2); gr_textures = NULL; + gr_textures2 = NULL; gr_numtextures = 0; } @@ -612,6 +618,9 @@ void HWR_PrepLevelCache(size_t pnumtextures) gr_textures = calloc(pnumtextures, sizeof (*gr_textures)); if (gr_textures == NULL) I_Error("3D can't alloc gr_textures"); + gr_textures2 = calloc(pnumtextures, sizeof (*gr_textures2)); + if (gr_textures2 == NULL) + I_Error("3D can't alloc gr_textures2"); } void HWR_SetPalette(RGBA_t *palette) @@ -642,7 +651,7 @@ GLTexture_t *HWR_GetTexture(INT32 tex) GLTexture_t *grtex; #ifdef PARANOIA if ((unsigned)tex >= gr_numtextures) - I_Error(" HWR_GetTexture: tex >= numtextures\n"); + I_Error("HWR_GetTexture: tex >= numtextures\n"); #endif grtex = &gr_textures[tex]; @@ -657,6 +666,35 @@ GLTexture_t *HWR_GetTexture(INT32 tex) return grtex; } +// Lactozilla +lumpnum_t gr_patchflat; + +static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) +{ + patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); + + grMipmap->width = (UINT16)SHORT(patch->width); + grMipmap->height = (UINT16)SHORT(patch->height); + + R_FlatPatch(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); + + Z_Free(patch); +} + +static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) +{ + // setup the texture info + grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; + grMipmap->grInfo.format = GR_TEXFMT_P_8; + grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; + + grMipmap->width = (UINT16)textures[texturenum]->width; + grMipmap->height = (UINT16)textures[texturenum]->height; + + R_FlatTexture(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); +} static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { @@ -695,15 +733,20 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) pflatsize = 64; break; } - grMipmap->width = (UINT16)pflatsize; - grMipmap->height = (UINT16)pflatsize; - // the flat raw data needn't be converted with palettized textures - W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), - PU_HWRCACHE, &grMipmap->grInfo.data)); + if (R_CheckIfPatch(flatlumpnum)) + HWR_LoadPatchFlat(grMipmap, flatlumpnum); + else + { + grMipmap->width = (UINT16)pflatsize; + grMipmap->height = (UINT16)pflatsize; + + // the flat raw data needn't be converted with palettized textures + W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), + PU_HWRCACHE, &grMipmap->grInfo.data)); + } } - // Download a Doom 'flat' to the hardware cache and make it ready for use void HWR_GetFlat(lumpnum_t flatlumpnum) { @@ -718,6 +761,30 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) // The system-memory data can be purged now. Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED); + + gr_patchflat = 0; + if (R_CheckIfPatch(flatlumpnum)) + gr_patchflat = flatlumpnum; +} + +void HWR_GetTextureFlat(INT32 texturenum) +{ + GLTexture_t *grtex; +#ifdef PARANOIA + if ((unsigned)texturenum >= gr_numtextures) + I_Error("HWR_GetTextureFlat: texturenum >= numtextures\n"); +#endif + if (texturenum == 0 || texturenum == -1) + return; + grtex = &gr_textures2[texturenum]; + + if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded) + HWR_LoadTextureFlat(&grtex->mipmap, texturenum); + + HWD.pfnSetTexture(&grtex->mipmap); + + // The system-memory data can be purged now. + Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED); } // diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index bdf219464..2085f7050 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -101,6 +101,7 @@ void HWR_FreeTextureCache(void); void HWR_FreeExtraSubsectors(void); void HWR_GetFlat(lumpnum_t flatlumpnum); +void HWR_GetTextureFlat(INT32 texturenum); GLTexture_t *HWR_GetTexture(INT32 tex); void HWR_GetPatch(GLPatch_t *gpatch); void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap); @@ -114,6 +115,8 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum); // -------- // hw_draw.c // -------- +extern lumpnum_t gr_patchflat; + extern float gr_patch_scalex; extern float gr_patch_scaley; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 7e0b369eb..7d0e7e490 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -70,9 +70,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); #endif #ifdef SORTING -void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); #else static void HWR_Add3DWater(lumpnum_t lumpnum, extrasubsector_t *xsub, fixed_t fixedheight, @@ -522,7 +522,7 @@ static UINT8 HWR_FogBlockAlpha(INT32 light, UINT32 color) // Let's see if this c // HWR_RenderPlane : Render a floor or ceiling convex polygon // -----------------+ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, - FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) + FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) { polyvertex_t * pv; float height; //constant y for all points on the convex flat polygon @@ -530,8 +530,7 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is INT32 nrPlaneVerts; //verts original define of convex flat polygon INT32 i; float flatxref,flatyref; - float fflatsize; - INT32 flatflag; + float fflatwidth, fflatheight; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; @@ -540,6 +539,7 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is #ifdef ESLOPE pslope_t *slope = NULL; #endif + patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -599,38 +599,44 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is switch (len) { case 4194304: // 2048x2048 lump - fflatsize = 2048.0f; - flatflag = 2047; + fflatwidth = fflatheight = 2048.0f; break; case 1048576: // 1024x1024 lump - fflatsize = 1024.0f; - flatflag = 1023; + fflatwidth = fflatheight = 1024.0f; break; case 262144:// 512x512 lump - fflatsize = 512.0f; - flatflag = 511; + fflatwidth = fflatheight = 512.0f; break; case 65536: // 256x256 lump - fflatsize = 256.0f; - flatflag = 255; + fflatwidth = fflatheight = 256.0f; break; case 16384: // 128x128 lump - fflatsize = 128.0f; - flatflag = 127; + fflatwidth = fflatheight = 128.0f; break; case 1024: // 32x32 lump - fflatsize = 32.0f; - flatflag = 31; + fflatwidth = fflatheight = 32.0f; break; default: // 64x64 lump - fflatsize = 64.0f; - flatflag = 63; + fflatwidth = fflatheight = 64.0f; break; } + if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = patch->width; + fflatheight = patch->height; + } + + if (texturenum != 0 && texturenum != -1) + { + fflatwidth = textures[texturenum]->width; + fflatheight = textures[texturenum]->height; + } + // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatsize); - flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatsize); + flatxref = (float)((FLOAT_TO_FIXED(pv->x) % llrint(fflatwidth)) / fflatwidth); + flatyref = (float)((FLOAT_TO_FIXED(pv->y) % llrint(fflatheight)) / fflatheight); // transform v3d = planeVerts; @@ -639,14 +645,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; angle = FOFsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; angle = FOFsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -654,14 +660,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; angle = gr_frontsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; angle = gr_frontsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -686,8 +692,8 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is for (i = 0; i < nrPlaneVerts; i++,v3d++,pv++) { // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((pv->x / fflatsize) - flatxref + scrollx); - v3d->tow = (float)(flatyref - (pv->y / fflatsize) + scrolly); + v3d->sow = (float)((pv->x / fflatwidth) - flatxref + scrollx); + v3d->tow = (float)(flatyref - (pv->y / fflatheight) + scrolly); //v3d->sow = (float)(pv->x / fflatsize); //v3d->tow = (float)(pv->y / fflatsize); @@ -3145,21 +3151,21 @@ static inline void HWR_AddPolyObjectSegs(void) #ifdef POLYOBJECTS_PLANES static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, - FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, + FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { float height; //constant y for all points on the convex flat polygon FOutVector *v3d; INT32 i; float flatxref,flatyref; - float fflatsize; - INT32 flatflag; + float fflatwidth, fflatheight; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; fixed_t tempxsow, tempytow; size_t nrPlaneVerts; + patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -3190,38 +3196,44 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, switch (len) { case 4194304: // 2048x2048 lump - fflatsize = 2048.0f; - flatflag = 2047; + fflatwidth = fflatheight = 2048.0f; break; case 1048576: // 1024x1024 lump - fflatsize = 1024.0f; - flatflag = 1023; + fflatwidth = fflatheight = 1024.0f; break; case 262144:// 512x512 lump - fflatsize = 512.0f; - flatflag = 511; + fflatwidth = fflatheight = 512.0f; break; case 65536: // 256x256 lump - fflatsize = 256.0f; - flatflag = 255; + fflatwidth = fflatheight = 256.0f; break; case 16384: // 128x128 lump - fflatsize = 128.0f; - flatflag = 127; + fflatwidth = fflatheight = 128.0f; break; case 1024: // 32x32 lump - fflatsize = 32.0f; - flatflag = 31; + fflatwidth = fflatheight = 32.0f; break; default: // 64x64 lump - fflatsize = 64.0f; - flatflag = 63; + fflatwidth = fflatheight = 64.0f; break; } + if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = patch->width; + fflatheight = patch->height; + } + + if (texturenum != 0 && texturenum != -1) + { + fflatwidth = textures[texturenum]->width; + fflatheight = textures[texturenum]->height; + } + // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].x) & (~flatflag)) / fflatsize); - flatyref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].y) & (~flatflag)) / fflatsize); + flatxref = (float)((polysector->origVerts[0].x % llrint(fflatwidth)) / fflatwidth); + flatyref = (float)((polysector->origVerts[0].y % llrint(fflatheight)) / fflatheight); // transform v3d = planeVerts; @@ -3230,14 +3242,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; angle = FOFsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; angle = FOFsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3245,14 +3257,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; angle = gr_frontsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; angle = gr_frontsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3276,8 +3288,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++) { // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatsize) - flatxref + scrollx); // Go from the polysector's original vertex locations - v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatsize) + scrolly); // Means the flat is offset based on the original vertex locations + v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); // Go from the polysector's original vertex locations + v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); // Means the flat is offset based on the original vertex locations // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle @@ -3336,14 +3348,15 @@ static void HWR_AddPolyObjectPlanes(void) { FSurfaceInfo Surf; FBITFIELD blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); - HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], false, polyobjsector->floorheight, + HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, po_ptrs[i], false, polyobjsector->floorheight, polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL); } else { HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum); + HWR_GetTextureFlat(levelflats[polyobjsector->floorpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude, - polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, + polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, polyobjsector, 255, NULL); } } @@ -3358,14 +3371,15 @@ static void HWR_AddPolyObjectPlanes(void) FBITFIELD blendmode; memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); - HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], true, polyobjsector->ceilingheight, + HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, levelflats[polyobjsector->ceilingpic].texturenum, po_ptrs[i], true, polyobjsector->ceilingheight, polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL); } else { HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum); + HWR_GetTextureFlat(levelflats[polyobjsector->ceilingpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude, - polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, + polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, polyobjsector, 255, NULL); } } @@ -3517,11 +3531,12 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->floorpic].lumpnum); + HWR_GetTextureFlat(levelflats[gr_frontsector->floorpic].texturenum); HWR_RenderPlane(gr_frontsector, &extrasubsectors[num], false, // Hack to make things continue to work around slopes. locFloorHeight == cullFloorHeight ? locFloorHeight : gr_frontsector->floorheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, NULL, 255, false, floorcolormap); + PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, levelflats[gr_frontsector->floorpic].texturenum, NULL, 255, false, floorcolormap); } } else @@ -3539,11 +3554,12 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->ceilingpic].lumpnum); + HWR_GetTextureFlat(levelflats[gr_frontsector->ceilingpic].texturenum); HWR_RenderPlane(NULL, &extrasubsectors[num], true, // Hack to make things continue to work around slopes. locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gr_frontsector->ceilingheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum,NULL, 255, false, ceilingcolormap); + PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum, levelflats[gr_frontsector->ceilingpic].texturenum, NULL, 255, false, ceilingcolormap); } } else @@ -3602,7 +3618,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, + HWR_AddTransparentFloor(0, 0, &extrasubsectors[num], false, *rover->bottomheight, @@ -3621,6 +3637,7 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->bottompic].lumpnum, + levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], false, *rover->bottomheight, @@ -3632,8 +3649,9 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->bottompic].lumpnum); + HWR_GetTextureFlat(levelflats[*rover->bottompic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, + HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, levelflats[*rover->bottompic].texturenum, rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap); } } @@ -3665,7 +3683,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, + HWR_AddTransparentFloor(0, 0, &extrasubsectors[num], true, *rover->topheight, @@ -3684,6 +3702,7 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->toppic].lumpnum, + levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], true, *rover->topheight, @@ -3696,8 +3715,9 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->toppic].lumpnum); + HWR_GetTextureFlat(levelflats[*rover->toppic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, + HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, levelflats[*rover->toppic].texturenum, rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap); } } @@ -5021,6 +5041,7 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; + INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5039,6 +5060,7 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; + INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5071,7 +5093,7 @@ static INT32 drawcount = 0; #define MAX_TRANSPARENTFLOOR 512 // This will likely turn into a copy of HWR_Add3DWater and replace it. -void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) { static size_t allocedplanes = 0; @@ -5090,6 +5112,7 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean planeinfo[numplanes].fixedheight = fixedheight; planeinfo[numplanes].lightlevel = lightlevel; planeinfo[numplanes].lumpnum = lumpnum; + planeinfo[numplanes].texturenum = texturenum; planeinfo[numplanes].xsub = xsub; planeinfo[numplanes].alpha = alpha; planeinfo[numplanes].FOFSector = FOFSector; @@ -5103,7 +5126,7 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean // Adding this for now until I can create extrasubsector info for polyobjects // When that happens it'll just be done through HWR_AddTransparentFloor and HWR_RenderPlane -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap) { static size_t allocedpolyplanes = 0; @@ -5122,6 +5145,7 @@ void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, polyplaneinfo[numpolyplanes].fixedheight = fixedheight; polyplaneinfo[numpolyplanes].lightlevel = lightlevel; polyplaneinfo[numpolyplanes].lumpnum = lumpnum; + polyplaneinfo[numpolyplanes].texturenum = texturenum; polyplaneinfo[numpolyplanes].polysector = polysector; polyplaneinfo[numpolyplanes].alpha = alpha; polyplaneinfo[numpolyplanes].FOFSector = FOFSector; @@ -5283,9 +5307,12 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].plane->blend & PF_NoTexture)) + { HWR_GetFlat(sortnode[sortindex[i]].plane->lumpnum); + HWR_GetTextureFlat(sortnode[sortindex[i]].plane->texturenum); + } HWR_RenderPlane(NULL, sortnode[sortindex[i]].plane->xsub, sortnode[sortindex[i]].plane->isceiling, sortnode[sortindex[i]].plane->fixedheight, sortnode[sortindex[i]].plane->blend, sortnode[sortindex[i]].plane->lightlevel, - sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); + sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->texturenum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); } else if (sortnode[sortindex[i]].polyplane) { @@ -5293,9 +5320,12 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].polyplane->blend & PF_NoTexture)) + { HWR_GetFlat(sortnode[sortindex[i]].polyplane->lumpnum); + HWR_GetTextureFlat(sortnode[sortindex[i]].polyplane->texturenum); + } HWR_RenderPolyObjectPlane(sortnode[sortindex[i]].polyplane->polysector, sortnode[sortindex[i]].polyplane->isceiling, sortnode[sortindex[i]].polyplane->fixedheight, sortnode[sortindex[i]].polyplane->blend, sortnode[sortindex[i]].polyplane->lightlevel, - sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); + sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->texturenum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); } else if (sortnode[sortindex[i]].wall) { diff --git a/src/p_setup.c b/src/p_setup.c index 5e1355981..1061dbd0c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -564,6 +564,8 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); + // Lactozilla + levelflat->texturenum = R_CheckTextureNumForName(flatname); #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); @@ -608,6 +610,8 @@ INT32 P_AddLevelFlatRuntime(const char *flatname) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); + // Lactozilla + levelflat->texturenum = R_CheckTextureNumForName(flatname); #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); diff --git a/src/p_setup.h b/src/p_setup.h index 41c2bf133..6f54bceae 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -42,6 +42,17 @@ typedef struct INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; + + // Lactozilla + UINT8 *flatpatch; + UINT16 width, height; + fixed_t topoffset, leftoffset; + size_t texturenum; + +#ifdef ESLOPE + UINT8 *resizedflat; + UINT16 resizedwidth, resizedheight; +#endif } levelflat_t; extern size_t numlevelflats; diff --git a/src/p_spec.c b/src/p_spec.c index 50b8aec9d..60d784324 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -322,8 +322,8 @@ void P_InitPicAnims(void) if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR) continue; - lastanim->picnum = R_FlatNumForName(animdefs[i].endname); - lastanim->basepic = R_FlatNumForName(animdefs[i].startname); + lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname); + lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname); } lastanim->istexture = animdefs[i].istexture; diff --git a/src/r_data.c b/src/r_data.c index a21ba49ae..496a0f944 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -101,9 +101,7 @@ texture_t **textures = NULL; static UINT32 **texturecolumnofs; // column offset lookup table for each texture static UINT8 **texturecache; // graphics data for each generated full-size texture -// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask -INT32 *texturewidthmask; - +INT32 *texturewidth; fixed_t *textureheight; // needed for texture pegging INT32 *texturetranslation; @@ -335,10 +333,14 @@ void R_CheckTextureCache(INT32 tex) 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); - col &= texturewidthmask[tex]; data = texturecache[tex]; - if (!data) data = R_GenerateTexture(tex); @@ -376,7 +378,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); #define TX_END "TX_END" void R_LoadTextures(void) { - INT32 i, k, w; + INT32 i, w; UINT16 j; UINT16 texstart, texend, texturesLumpPos; patch_t *patchlump; @@ -443,9 +445,9 @@ void R_LoadTextures(void) texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); // Allocate texture referencing cache. texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width mask table. - texturewidthmask = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height mask table. + // 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); @@ -513,11 +515,7 @@ void R_LoadTextures(void) Z_Unlock(patchlump); - k = 1; - while (k << 1 <= texture->width) - k <<= 1; - - texturewidthmask[i] = k - 1; + texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; } } @@ -905,7 +903,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) newTexture = R_ParseTexture(true); // Store the new texture textures[*texindex] = newTexture; - texturewidthmask[*texindex] = newTexture->width - 1; + texturewidth[*texindex] = newTexture->width; textureheight[*texindex] = newTexture->height << FRACBITS; // Increment i back in R_LoadTextures() (*texindex)++; @@ -1017,6 +1015,41 @@ lumpnum_t R_GetFlatNumForName(const char *name) lump = LUMPERROR; } + // Lactozilla + if (lump == LUMPERROR) + { + // 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_CheckNumForNamePwad("TX_START", (UINT16)i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForNamePwad("TX_END", (UINT16)i, start)) == INT16_MAX) + continue; + break; + case RET_PK3: + if ((start = W_CheckNumForFolderStartPK3("Textures/", i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForFolderEndPK3("Textures/", 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; + } + } + if (lump == LUMPERROR) { if (strcmp(name, SKYFLATNAME)) @@ -1603,3 +1636,144 @@ void R_PrecacheLevel(void) "texturememory: %s k\n" "spritememory: %s k\n", sizeu1(flatmemory>>10), sizeu2(texturememory>>10), sizeu3(spritememory>>10)); } + +// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350 +boolean R_CheckIfPatch(lumpnum_t lump) +{ + size_t size; + INT16 width, height; + patch_t *patch; + boolean result; + + size = W_LumpLength(lump); + + // 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 < size / 4); + + if (result) + { + // The dimensions seem like they might be valid for a patch, so + // check the column directory for extra security. All columns + // must begin after the column directory, and none of them must + // point past the end of the patch. + INT16 x; + + for (x = 0; x < width; x++) + { + UINT32 ofs = LONG(patch->columnofs[x]); + + // Need one byte for an empty column (but there's patches that don't know that!) + if (ofs < (UINT32)width * 4 + 8 || ofs >= (UINT32)size) + { + result = false; + break; + + } + } + } + + Z_Free(patch); + return result; +} + +// Lactozilla +void R_FlatPatch(patch_t *patch, UINT8 *flat) +{ + fixed_t col, ofs; + column_t *column; + UINT8 *desttop, *dest, *deststop; + UINT8 *source; + + desttop = flat; + deststop = desttop + (patch->width * 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 * patch->width); + source = (UINT8 *)(column) + 3; + for (ofs = 0; dest < deststop && ofs < column->length; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += patch->width; + } + column = (column_t *)((UINT8 *)column + column->length + 4); + } + } +} + +void R_FlatTexture(size_t tex, UINT8 *flat) +{ + texture_t *texture = textures[tex]; + + fixed_t col, ofs; + column_t *column; + UINT8 *desttop, *dest, *deststop; + UINT8 *source; + + desttop = flat; + deststop = desttop + (texture->width * texture->height); + + for (col = 0; col < SHORT(texture->width); col++, desttop++) + { + INT32 topdelta, prevdelta = -1; + column = (column_t *)R_GetColumn(tex, col); + if (!texture->holes) + { + dest = desttop; + source = (UINT8 *)(column); + for (ofs = 0; dest < deststop && ofs < texture->height; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += texture->width; + } + } + else + { + while (column->topdelta != 0xff) + { + topdelta = column->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + + dest = desttop + (topdelta * texture->width); + source = (UINT8 *)(column) + 3; + for (ofs = 0; dest < deststop && ofs < column->length; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += texture->width; + } + column = (column_t *)((UINT8 *)column + column->length + 4); + } + } + } +} + +void R_CropFlat(UINT8 *origflat, UINT8 *cropflat, UINT16 origwidth, UINT16 origheight, UINT16 cropwidth, UINT16 cropheight) +{ + UINT16 x, y; + for (y = 0; y < cropheight; y++) + for (x = 0; x < cropwidth; x++) + cropflat[(y * cropwidth) + x] = origflat[(y * origwidth) + x]; +} diff --git a/src/r_data.h b/src/r_data.h index 5de51ccd4..a1e7cd127 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -51,9 +51,7 @@ typedef struct // all loaded and prepared textures from the start of the game extern texture_t **textures; -// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask -extern INT32 *texturewidthmask; - +extern INT32 *texturewidth; extern fixed_t *textureheight; // needed for texture pegging extern INT16 color8to16[256]; // remap color index to highcolor @@ -81,7 +79,6 @@ void R_PrecacheLevel(void); // Floor/ceiling opaque texture tiles, // lookup by name. For animation? lumpnum_t R_GetFlatNumForName(const char *name); -#define R_FlatNumForName(x) R_GetFlatNumForName(x) // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. @@ -95,6 +92,13 @@ INT32 R_ColormapNumForName(char *name); INT32 R_CreateColormap(char *p1, char *p2, char *p3); const char *R_ColormapNameForNum(INT32 num); +boolean R_CheckIfPatch(lumpnum_t lump); + +// Lactozilla +void R_FlatPatch(patch_t *patch, UINT8 *flat); +void R_FlatTexture(size_t tex, UINT8 *flat); +void R_CropFlat(UINT8 *origflat, UINT8 *cropflat, UINT16 origwidth, UINT16 origheight, UINT16 cropwidth, UINT16 cropheight); + extern INT32 numtextures; #endif diff --git a/src/r_draw.c b/src/r_draw.c index bbc9a79b0..babcbef08 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -99,6 +99,8 @@ INT32 dc_numlights = 0, dc_maxlights, dc_texheight; INT32 ds_y, ds_x1, ds_x2; lighttable_t *ds_colormap; fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; +UINT16 ds_flatwidth, ds_flatheight; +boolean ds_powersoftwo; UINT8 *ds_source; // start of a 64*64 tile image UINT8 *ds_transmap; // one of the translucency tables diff --git a/src/r_draw.h b/src/r_draw.h index 12f556b7a..e82f60839 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -57,7 +57,9 @@ extern INT32 dc_texheight; extern INT32 ds_y, ds_x1, ds_x2; extern lighttable_t *ds_colormap; extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; -extern UINT8 *ds_source; // start of a 64*64 tile image +extern UINT16 ds_flatwidth, ds_flatheight; +extern boolean ds_powersoftwo; +extern UINT8 *ds_source; extern UINT8 *ds_transmap; #ifdef ESLOPE @@ -125,6 +127,8 @@ void R_FillBackScreen(void); void R_DrawViewBorder(void); #endif +#define TRANSPARENTPIXEL 247 + // ----------------- // 8bpp DRAWING CODE // ----------------- @@ -166,6 +170,13 @@ void R_DrawFogSpan_8(void); void R_DrawFogColumn_8(void); void R_DrawColumnShadowed_8(void); +#ifndef NOWATER +void R_DrawTranslucentWaterSpan_8(void); + +extern INT32 ds_bgofs; +extern INT32 ds_waterofs; +#endif + // ------------------ // 16bpp DRAWING CODE // ------------------ diff --git a/src/r_draw8.c b/src/r_draw8.c index 886b72dae..f829707d3 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -105,8 +105,6 @@ void R_DrawColumn_8(void) } } -#define TRANSPARENTPIXEL 247 - void R_Draw2sMultiPatchColumn_8(void) { INT32 count; @@ -543,80 +541,60 @@ void R_DrawTranslatedColumn_8(void) */ void R_DrawSpan_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + UINT32 flatsize = ds_flatwidth * ds_flatheight; + size_t count = (ds_x2 - ds_x1 + 1); - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; + + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - if (dest+8 > deststop) + if (dest > deststop) return; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - dest[1] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + // 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); - dest[2] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[3] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[4] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[5] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[6] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[7] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + *dest++ = colormap[source[((y * ds_flatwidth) + x) % flatsize]]; + xposition += xstep; + yposition += ystep; + } } - while (count-- && dest <= deststop) + else { - *dest++ = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + *dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + } } } @@ -1065,117 +1043,64 @@ void R_DrawTiltedSplat_8(void) */ void R_DrawSplat_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + UINT32 flatsize = ds_flatwidth * ds_flatheight; + size_t count = (ds_x2 - ds_x1 + 1); UINT32 val; - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; + + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - // - // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[0] = colormap[val]; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[1] = colormap[val]; - xposition += xstep; - yposition += ystep; + // 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); - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[2] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[3] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[4] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[5] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[6] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[7] = colormap[val]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = source[((y * ds_flatwidth) + x) % flatsize]; + if (val != TRANSPARENTPIXEL) + *dest = colormap[val]; + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - *dest = colormap[val]; - - dest++; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + *dest = colormap[val]; + dest++; + xposition += xstep; + yposition += ystep; + } } } @@ -1184,97 +1109,64 @@ void R_DrawSplat_8 (void) */ void R_DrawTranslucentSplat_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; - UINT8 val; + UINT32 flatsize = ds_flatwidth * ds_flatheight; + size_t count = (ds_x2 - ds_x1 + 1); + UINT32 val; - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; + + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; + // 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); - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = source[((y * ds_flatwidth) + x) % flatsize]; + if (val != TRANSPARENTPIXEL) + *dest = *(ds_transmap + (colormap[val] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - *dest = *(ds_transmap + (colormap[val] << 8) + *dest); - - dest++; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + *dest = *(ds_transmap + (colormap[val] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } } @@ -1283,80 +1175,125 @@ void R_DrawTranslucentSplat_8 (void) */ void R_DrawTranslucentSpan_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + UINT32 flatsize = ds_flatwidth * ds_flatheight; + size_t count = (ds_x2 - ds_x1 + 1); + UINT32 val; - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; + + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - dest[1] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; + // 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); - dest[2] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; - - dest[3] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - dest[4] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - dest[5] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - dest[6] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - dest[7] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = ((y * ds_flatwidth) + x) % flatsize; + *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - *dest = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } } +#ifndef NOWATER +void R_DrawTranslucentWaterSpan_8(void) +{ + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; + + UINT8 *source; + UINT8 *colormap; + UINT8 *dest; + UINT8 *dsrc; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; + + UINT32 flatsize = ds_flatwidth * ds_flatheight; + size_t count = (ds_x2 - ds_x1 + 1); + + xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs); + xstep = ds_xstep; ystep = ds_ystep; + + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } + + source = ds_source; + colormap = ds_colormap; + dest = ylookup[ds_y] + columnofs[ds_x1]; + dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; + + if (!ds_powersoftwo) + { + 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); + + *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x) % flatsize] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + } + } + else + { + while (count-- && dest <= deststop) + { + *dest++ = colormap[*(ds_transmap + (source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + } + } +} +#endif + /** \brief The R_DrawFogSpan_8 function Draws the actual span with fogging. */ diff --git a/src/r_plane.c b/src/r_plane.c index 5cb53a530..39f1d220a 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -173,91 +173,13 @@ void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor // viewheight #ifndef NOWATER -static INT32 bgofs; +INT32 ds_bgofs; +INT32 ds_waterofs; + static INT32 wtofs=0; -static INT32 waterofs; static boolean itswater; #endif -#ifndef NOWATER -static void R_DrawTranslucentWaterSpan_8(void) -{ - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; - - UINT8 *source; - UINT8 *colormap; - UINT8 *dest; - UINT8 *dsrc; - - size_t count; - - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = (ds_yfrac + waterofs) << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; - - source = ds_source; - colormap = ds_colormap; - dest = ylookup[ds_y] + columnofs[ds_x1]; - dsrc = screens[1] + (ds_y+bgofs)*vid.width + ds_x1; - count = ds_x2 - ds_x1 + 1; - - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count--) - { - *dest++ = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - } -} -#endif - void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; @@ -304,17 +226,17 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { const INT32 yay = (wtofs + (distance>>9) ) & 8191; // ripples da water texture - bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; + ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; angle = (angle + 2048) & 8191; // 90 degrees - ds_xfrac += FixedMul(FINECOSINE(angle), (bgofs<=viewheight) - bgofs = viewheight-y-1; - if (y+bgofs<0) - bgofs = -y; + if (y+ds_bgofs>=viewheight) + ds_bgofs = viewheight-y-1; + if (y+ds_bgofs<0) + ds_bgofs = -y; } #endif @@ -726,11 +648,142 @@ void R_DrawPlanes(void) } } #ifndef NOWATER - waterofs = (leveltime & 1)*16384; + ds_waterofs = (leveltime & 1)*16384; wtofs = leveltime * 140; #endif } +// Lactozilla +static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) +{ + patch_t *patch = NULL; + + if (levelflat->flatpatch == NULL) + { +#ifdef ESLOPE + INT32 resizewidth, resizeheight, newresize; +#endif // ESLOPE + + if (!leveltexture) + { + patch = (patch_t *)ds_source; + levelflat->width = ds_flatwidth = patch->width; + levelflat->height = ds_flatheight = patch->height; + + levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); + R_FlatPatch(patch, levelflat->flatpatch); + + levelflat->topoffset = patch->topoffset * FRACUNIT; + levelflat->leftoffset = patch->leftoffset * FRACUNIT; + } + else + { + texture_t *texture = textures[levelflat->texturenum]; + levelflat->width = ds_flatwidth = texture->width; + levelflat->height = ds_flatheight = texture->height; + + levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); + R_FlatTexture(levelflat->texturenum, levelflat->flatpatch); + + levelflat->topoffset = levelflat->leftoffset = 0; + } + ds_source = levelflat->flatpatch; + + // If GZDoom has the same limitation then I'm not even going to bother. + // Crop the texture. +#ifdef ESLOPE + // Scale up to nearest power of 2 + resizewidth = resizeheight = 1; + while (resizewidth < levelflat->width) + resizewidth <<= 1; + while (resizeheight < levelflat->height) + resizeheight <<= 1; + // Scale down to fit in 2048x2048 + if (resizewidth > 2048) + resizewidth = 2048; + if (resizeheight > 2048) + resizeheight = 2048; + // Then scale down to fit the actual flat dimensions + while (resizewidth > levelflat->width) + resizewidth >>= 1; + while (resizeheight > levelflat->height) + resizeheight >>= 1; + + levelflat->resizedwidth = levelflat->resizedheight = (newresize = min(resizewidth, resizeheight)); + levelflat->resizedflat = Z_Malloc(newresize * newresize, PU_LEVEL, NULL); + memset(levelflat->resizedflat, TRANSPARENTPIXEL, newresize * newresize); + R_CropFlat(levelflat->flatpatch, levelflat->resizedflat, levelflat->width, levelflat->height, newresize, newresize); +#endif // ESLOPE + } + else + { + ds_source = levelflat->flatpatch; + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + + xoffs += levelflat->leftoffset; + yoffs += levelflat->topoffset; + } + +#ifdef ESLOPE + if (currentplane->slope) + { + ds_source = levelflat->resizedflat; + ds_flatwidth = levelflat->resizedwidth; + ds_flatheight = levelflat->resizedheight; + + // uuuuuuuhhhhhhhh....................... + switch (ds_flatwidth * ds_flatheight) + { + case 4194304: // 2048x2048 lump + nflatmask = 0x3FF800; + nflatxshift = 21; + nflatyshift = 10; + nflatshiftup = 5; + break; + case 1048576: // 1024x1024 lump + nflatmask = 0xFFC00; + nflatxshift = 22; + nflatyshift = 12; + nflatshiftup = 6; + break; + case 262144:// 512x512 lump + nflatmask = 0x3FE00; + nflatxshift = 23; + nflatyshift = 14; + nflatshiftup = 7; + break; + case 65536: // 256x256 lump + nflatmask = 0xFF00; + nflatxshift = 24; + nflatyshift = 16; + nflatshiftup = 8; + break; + case 16384: // 128x128 lump + nflatmask = 0x3F80; + nflatxshift = 25; + nflatyshift = 18; + nflatshiftup = 9; + break; + case 1024: // 32x32 lump + nflatmask = 0x3E0; + nflatxshift = 27; + nflatyshift = 22; + nflatshiftup = 11; + break; + default: // 64x64 lump + nflatmask = 0xFC0; + nflatxshift = 26; + nflatyshift = 20; + nflatshiftup = 10; + break; + } + } +#endif // ESLOPE +} + void R_DrawSinglePlane(visplane_t *pl) { INT32 light = 0; @@ -738,6 +791,7 @@ void R_DrawSinglePlane(visplane_t *pl) INT32 stop, angle; size_t size; ffloor_t *rover; + levelflat_t *levelflat; if (!(pl->minx <= pl->maxx)) return; @@ -878,64 +932,78 @@ void R_DrawSinglePlane(visplane_t *pl) viewangle = pl->viewangle+pl->plangle; } - currentplane = pl; - - ds_source = (UINT8 *) - W_CacheLumpNum(levelflats[pl->picnum].lumpnum, - PU_STATIC); // Stay here until Z_ChangeTag - - size = W_LumpLength(levelflats[pl->picnum].lumpnum); - - switch (size) - { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - break; - case 262144:// 512x512 lump' - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - break; - } - xoffs = pl->xoffs; yoffs = pl->yoffs; planeheight = abs(pl->height - pl->viewz); + currentplane = pl; + levelflat = &levelflats[pl->picnum]; + + if (levelflat->texturenum != 0 && levelflat->texturenum != -1) + R_GetPatchFlat(levelflat, true); + else + { + ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag + size = W_LumpLength(levelflat->lumpnum); + + 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; + } + } + + if (R_CheckIfPatch(levelflat->lumpnum)) + R_GetPatchFlat(levelflat, false); + ds_powersoftwo = (!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))); + if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; diff --git a/src/screen.c b/src/screen.c index af6aed03c..893fb851f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -135,7 +135,7 @@ void SCR_SetMode(void) //fuzzcolfunc = R_DrawTranslucentColumn_8_ASM; walldrawerfunc = R_DrawWallColumn_8_MMX; twosmultipatchfunc = R_Draw2sMultiPatchColumn_8_MMX; - spanfunc = basespanfunc = R_DrawSpan_8_MMX; + //spanfunc = basespanfunc = R_DrawSpan_8_MMX; } else { From 35d6da159d91428060bf1a0c47242d9a53aa17d9 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 21 May 2019 09:50:39 -0300 Subject: [PATCH 019/196] HOLD UP --- src/hardware/hw_cache.c | 2 -- src/r_data.c | 1 - 2 files changed, 3 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 4ae7a43b9..84ad4c55b 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -677,8 +677,6 @@ static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) grMipmap->height = (UINT16)SHORT(patch->height); R_FlatPatch(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); - - Z_Free(patch); } static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) diff --git a/src/r_data.c b/src/r_data.c index 496a0f944..8ceb59dd4 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1680,7 +1680,6 @@ boolean R_CheckIfPatch(lumpnum_t lump) } } - Z_Free(patch); return result; } From 869f1e4e8d18a6c02d4c9df095f0ff99e268e210 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 21 May 2019 11:03:53 -0300 Subject: [PATCH 020/196] Fix warnings --- src/hardware/hw_main.c | 5 +++-- src/p_setup.h | 2 +- src/r_data.c | 37 +++++++++++++++++++++++++++++++------ src/r_data.h | 5 ++++- src/r_plane.c | 40 ++++++++++++++++++++++++++++++++++------ 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 7d0e7e490..793050aa2 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -580,9 +580,10 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is if (nrPlaneVerts < 3) //not even a triangle ? return; - if (nrPlaneVerts > UINT16_MAX) // FIXME: exceeds plVerts size + // This check is so inconsistent between functions, it hurts. + if (nrPlaneVerts > INT16_MAX) // FIXME: exceeds plVerts size { - CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, UINT16_MAX); + CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, INT16_MAX); return; } diff --git a/src/p_setup.h b/src/p_setup.h index 6f54bceae..eda6066d3 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -47,7 +47,7 @@ typedef struct UINT8 *flatpatch; UINT16 width, height; fixed_t topoffset, leftoffset; - size_t texturenum; + INT32 texturenum; #ifdef ESLOPE UINT8 *resizedflat; diff --git a/src/r_data.c b/src/r_data.c index 8ceb59dd4..00d8de629 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1656,7 +1656,7 @@ boolean R_CheckIfPatch(lumpnum_t lump) width = SHORT(patch->width); height = SHORT(patch->height); - result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < size / 4); + result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(size / 4)); if (result) { @@ -1769,10 +1769,35 @@ void R_FlatTexture(size_t tex, UINT8 *flat) } } -void R_CropFlat(UINT8 *origflat, UINT8 *cropflat, UINT16 origwidth, UINT16 origheight, UINT16 cropwidth, UINT16 cropheight) +void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, + UINT16 srcwidth, UINT16 srcheight, + UINT16 resizewidth, UINT16 resizeheight, + UINT16 destwidth, UINT16 destheight) { - UINT16 x, y; - for (y = 0; y < cropheight; y++) - for (x = 0; x < cropwidth; x++) - cropflat[(y * cropwidth) + x] = origflat[(y * origwidth) + x]; + UINT16 y; + UINT16 position = 0; + for (y = 0; y < destheight; y++) + { + if (position > (srcwidth * srcheight)) + break; + if (srcwidth != resizewidth) + { + if (resizewidth > srcwidth) + { + UINT8 *pos2 = srcflat+position; + UINT8 lastpixel = *(pos2-1); + M_Memcpy(destflat, srcflat+position, destwidth); + memset(pos2, lastpixel, resizewidth-srcwidth); + } + else + M_Memcpy(destflat, srcflat+position, resizewidth); + } + else + M_Memcpy(destflat, srcflat+position, destwidth); + destflat += destwidth; + position += srcwidth; + } + + while (y++ < min(resizeheight, srcheight)) + memset(destflat + (y * destwidth), *(destflat - 1), destwidth); } diff --git a/src/r_data.h b/src/r_data.h index a1e7cd127..8cb41cd2f 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -97,7 +97,10 @@ boolean R_CheckIfPatch(lumpnum_t lump); // Lactozilla void R_FlatPatch(patch_t *patch, UINT8 *flat); void R_FlatTexture(size_t tex, UINT8 *flat); -void R_CropFlat(UINT8 *origflat, UINT8 *cropflat, UINT16 origwidth, UINT16 origheight, UINT16 cropwidth, UINT16 cropheight); +void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, + UINT16 srcwidth, UINT16 srcheight, + UINT16 resizewidth, UINT16 resizeheight, + UINT16 destwidth, UINT16 destheight); extern INT32 numtextures; diff --git a/src/r_plane.c b/src/r_plane.c index 39f1d220a..91b4f5f2c 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -662,6 +662,7 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) { #ifdef ESLOPE INT32 resizewidth, resizeheight, newresize; + INT32 checkresizewidth, checkresizeheight; #endif // ESLOPE if (!leveltexture) @@ -700,21 +701,48 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) resizewidth <<= 1; while (resizeheight < levelflat->height) resizeheight <<= 1; + // Scale down to fit in 2048x2048 if (resizewidth > 2048) resizewidth = 2048; if (resizeheight > 2048) resizeheight = 2048; - // Then scale down to fit the actual flat dimensions - while (resizewidth > levelflat->width) - resizewidth >>= 1; - while (resizeheight > levelflat->height) - resizeheight >>= 1; + + // A single pixel difference is negligible. + checkresizewidth = levelflat->width - 1; + if (checkresizewidth & (checkresizewidth - 1)) + { + checkresizewidth += 2; + if (checkresizewidth & (checkresizewidth - 1)) + { + while (resizewidth > levelflat->width) + resizewidth >>= 1; + } + else + resizewidth = checkresizewidth; + } + else + resizewidth = checkresizewidth; + + checkresizeheight = levelflat->height - 1; + if (checkresizeheight & (checkresizeheight - 1)) + { + checkresizeheight += 2; + if (checkresizeheight & (checkresizeheight - 1)) + { + while (resizeheight > levelflat->height) + resizeheight >>= 1; + } + else + resizeheight = checkresizeheight; + } + else + resizeheight = checkresizeheight; levelflat->resizedwidth = levelflat->resizedheight = (newresize = min(resizewidth, resizeheight)); levelflat->resizedflat = Z_Malloc(newresize * newresize, PU_LEVEL, NULL); memset(levelflat->resizedflat, TRANSPARENTPIXEL, newresize * newresize); - R_CropFlat(levelflat->flatpatch, levelflat->resizedflat, levelflat->width, levelflat->height, newresize, newresize); + R_CropFlat(levelflat->flatpatch, levelflat->resizedflat, levelflat->width, levelflat->height, min(resizewidth, newresize), min(resizeheight, newresize), newresize, newresize); #endif // ESLOPE } else From dbb1575a6da394d95dcaaae108db991c8d2b8619 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 21 May 2019 15:24:26 -0300 Subject: [PATCH 021/196] Animations, better flat management. --- src/p_setup.c | 5 +- src/p_setup.h | 10 +- src/p_spec.c | 26 ++-- src/r_data.c | 8 +- src/r_data.h | 13 +- src/r_plane.c | 336 ++++++++++++++++++++++++++------------------------ src/r_plane.h | 2 + 7 files changed, 224 insertions(+), 176 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 1061dbd0c..97bace860 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -564,8 +564,11 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); - // Lactozilla levelflat->texturenum = R_CheckTextureNumForName(flatname); + levelflat->lasttexturenum = levelflat->texturenum; + + levelflat->baselumpnum = LUMPERROR; + levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); diff --git a/src/p_setup.h b/src/p_setup.h index eda6066d3..824584be7 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -36,20 +36,22 @@ typedef struct { char name[9]; // resource name from wad lumpnum_t lumpnum; // lump number of the flat + INT32 texturenum, lasttexturenum; // texture number of the flat + UINT16 width, height; + fixed_t topoffset, leftoffset; // for flat animation lumpnum_t baselumpnum; + INT32 basetexturenum; INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; - // Lactozilla + // for patchflats UINT8 *flatpatch; - UINT16 width, height; - fixed_t topoffset, leftoffset; - INT32 texturenum; #ifdef ESLOPE + // rescaled version of the above UINT8 *resizedflat; UINT16 resizedwidth, resizedheight; #endif diff --git a/src/p_spec.c b/src/p_spec.c index 60d784324..7fe18eec1 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -584,7 +584,19 @@ static inline void P_FindAnimatedFlat(INT32 animnum) for (i = 0; i < numlevelflats; i++, foundflats++) { // is that levelflat from the flat anim sequence ? - if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) + if ((anims[animnum].istexture) && (foundflats->texturenum != 0 && foundflats->texturenum != -1) + && ((UINT16)foundflats->texturenum >= startflatnum && (UINT16)foundflats->texturenum <= endflatnum)) + { + foundflats->basetexturenum = startflatnum; + foundflats->animseq = foundflats->texturenum - startflatnum; + foundflats->numpics = endflatnum - startflatnum + 1; + foundflats->speed = anims[animnum].speed; + + CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n", + atoi(sizeu1(i)), foundflats->name, foundflats->animseq, + foundflats->numpics,foundflats->speed); + } + else if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) { foundflats->baselumpnum = startflatnum; foundflats->animseq = foundflats->lumpnum - startflatnum; @@ -608,10 +620,7 @@ void P_SetupLevelFlatAnims(void) // the original game flat anim sequences for (i = 0; anims[i].istexture != -1; i++) - { - if (!anims[i].istexture) - P_FindAnimatedFlat(i); - } + P_FindAnimatedFlat(i); } // @@ -4794,9 +4803,12 @@ void P_UpdateSpecials(void) { if (foundflats->speed) // it is an animated flat { + // update the levelflat texture number + if (foundflats->basetexturenum != -1) + foundflats->texturenum = foundflats->basetexturenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); // update the levelflat lump number - foundflats->lumpnum = foundflats->baselumpnum + - ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); + else if (foundflats->baselumpnum != LUMPERROR) + foundflats->lumpnum = foundflats->baselumpnum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); } } } diff --git a/src/r_data.c b/src/r_data.c index 00d8de629..4157a8850 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -98,6 +98,7 @@ 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 @@ -395,6 +396,7 @@ void R_LoadTextures(void) } Z_Free(texturetranslation); Z_Free(textures); + Z_Free(texflats); } // Load patches and textures. @@ -440,6 +442,7 @@ void R_LoadTextures(void) // 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 *))); @@ -1015,10 +1018,10 @@ lumpnum_t R_GetFlatNumForName(const char *name) lump = LUMPERROR; } - // Lactozilla + // Detect textures if (lump == LUMPERROR) { - // Scan wad files backwards so patched flats take preference. + // Scan wad files backwards so patched textures take preference. for (i = numwadfiles - 1; i >= 0; i--) { switch (wadfiles[i]->type) @@ -1683,7 +1686,6 @@ boolean R_CheckIfPatch(lumpnum_t lump) return result; } -// Lactozilla void R_FlatPatch(patch_t *patch, UINT8 *flat) { fixed_t col, ofs; diff --git a/src/r_data.h b/src/r_data.h index 8cb41cd2f..855daa06d 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -48,8 +48,20 @@ typedef struct texpatch_t patches[0]; } texture_t; +typedef struct +{ + UINT8 *flat; + INT16 width, height; + +#ifdef ESLOPE + UINT8 *resizedflat; + INT16 resizedwidth, resizedheight; +#endif +} 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 @@ -94,7 +106,6 @@ const char *R_ColormapNameForNum(INT32 num); boolean R_CheckIfPatch(lumpnum_t lump); -// Lactozilla void R_FlatPatch(patch_t *patch, UINT8 *flat); void R_FlatTexture(size_t tex, UINT8 *flat); void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, diff --git a/src/r_plane.c b/src/r_plane.c index 91b4f5f2c..01d0fdd37 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -653,96 +653,191 @@ void R_DrawPlanes(void) #endif } -// Lactozilla +boolean R_CheckPowersOfTwo(void) +{ + return (ds_powersoftwo = ((!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))) && (ds_flatwidth == ds_flatheight))); +} + +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; + } +} + static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) { + textureflat_t *texflat = &texflats[levelflat->texturenum]; patch_t *patch = NULL; + UINT8 *tex; + boolean texturechanged = (leveltexture ? (levelflat->texturenum != levelflat->lasttexturenum) : false); - if (levelflat->flatpatch == NULL) + // Check if the texture changed. + if (leveltexture && (!texturechanged)) + { + if (texflat != NULL && texflat->flat) + { + ds_source = 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) { #ifdef ESLOPE INT32 resizewidth, resizeheight, newresize; INT32 checkresizewidth, checkresizeheight; #endif // ESLOPE - if (!leveltexture) + if (leveltexture) + { + texture_t *texture = textures[levelflat->texturenum]; + texflat->width = ds_flatwidth = texture->width; + texflat->height = ds_flatheight = texture->height; + + texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); + R_FlatTexture(levelflat->texturenum, texflat->flat); + + ds_source = texflat->flat; + } + else { patch = (patch_t *)ds_source; levelflat->width = ds_flatwidth = patch->width; levelflat->height = ds_flatheight = patch->height; + levelflat->topoffset = patch->topoffset * FRACUNIT; + levelflat->leftoffset = patch->leftoffset * FRACUNIT; + levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); R_FlatPatch(patch, levelflat->flatpatch); - levelflat->topoffset = patch->topoffset * FRACUNIT; - levelflat->leftoffset = patch->leftoffset * FRACUNIT; + ds_source = levelflat->flatpatch; } - else - { - texture_t *texture = textures[levelflat->texturenum]; - levelflat->width = ds_flatwidth = texture->width; - levelflat->height = ds_flatheight = texture->height; - levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); - memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); - R_FlatTexture(levelflat->texturenum, levelflat->flatpatch); - - levelflat->topoffset = levelflat->leftoffset = 0; - } - ds_source = levelflat->flatpatch; - - // If GZDoom has the same limitation then I'm not even going to bother. - // Crop the texture. #ifdef ESLOPE - // Scale up to nearest power of 2 - resizewidth = resizeheight = 1; - while (resizewidth < levelflat->width) - resizewidth <<= 1; - while (resizeheight < levelflat->height) - resizeheight <<= 1; - - // Scale down to fit in 2048x2048 - if (resizewidth > 2048) - resizewidth = 2048; - if (resizeheight > 2048) - resizeheight = 2048; - - // A single pixel difference is negligible. - checkresizewidth = levelflat->width - 1; - if (checkresizewidth & (checkresizewidth - 1)) + // Crop the flat, if necessary. + if (!R_CheckPowersOfTwo()) { - checkresizewidth += 2; + // Scale up to nearest power of 2 + resizewidth = resizeheight = 1; + while (resizewidth < ds_flatwidth) + resizewidth <<= 1; + while (resizeheight < ds_flatheight) + resizeheight <<= 1; + + // Scale down to fit in 2048x2048 + if (resizewidth > 2048) + resizewidth = 2048; + if (resizeheight > 2048) + resizeheight = 2048; + + // A single pixel difference is negligible. + checkresizewidth = ds_flatwidth - 1; if (checkresizewidth & (checkresizewidth - 1)) { - while (resizewidth > levelflat->width) - resizewidth >>= 1; + checkresizewidth += 2; + if (checkresizewidth & (checkresizewidth - 1)) + { + while (resizewidth > ds_flatwidth) + resizewidth >>= 1; + } + else + resizewidth = checkresizewidth; } else resizewidth = checkresizewidth; - } - else - resizewidth = checkresizewidth; - checkresizeheight = levelflat->height - 1; - if (checkresizeheight & (checkresizeheight - 1)) - { - checkresizeheight += 2; + checkresizeheight = ds_flatheight - 1; if (checkresizeheight & (checkresizeheight - 1)) { - while (resizeheight > levelflat->height) - resizeheight >>= 1; + checkresizeheight += 2; + if (checkresizeheight & (checkresizeheight - 1)) + { + while (resizeheight > ds_flatheight) + resizeheight >>= 1; + } + else + resizeheight = checkresizeheight; } else resizeheight = checkresizeheight; - } - else - resizeheight = checkresizeheight; - levelflat->resizedwidth = levelflat->resizedheight = (newresize = min(resizewidth, resizeheight)); - levelflat->resizedflat = Z_Malloc(newresize * newresize, PU_LEVEL, NULL); - memset(levelflat->resizedflat, TRANSPARENTPIXEL, newresize * newresize); - R_CropFlat(levelflat->flatpatch, levelflat->resizedflat, levelflat->width, levelflat->height, min(resizewidth, newresize), min(resizeheight, newresize), newresize, newresize); + // Find smallest size. + newresize = min(resizewidth, resizeheight); + + // Allocate texture. + tex = Z_Malloc(newresize * newresize, PU_LEVEL, NULL); + memset(tex, TRANSPARENTPIXEL, newresize * newresize); + R_CropFlat(ds_source, tex, ds_flatwidth, ds_flatheight, min(resizewidth, newresize), min(resizeheight, newresize), newresize, newresize); + + if (leveltexture) + { + texflat->resizedflat = tex; + texflat->resizedwidth = texflat->resizedheight = newresize; + } + else + { + levelflat->resizedflat = tex; + levelflat->resizedwidth = levelflat->resizedheight = newresize; + } + } #endif // ESLOPE } else @@ -758,58 +853,26 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) #ifdef ESLOPE if (currentplane->slope) { - ds_source = levelflat->resizedflat; - ds_flatwidth = levelflat->resizedwidth; - ds_flatheight = levelflat->resizedheight; - - // uuuuuuuhhhhhhhh....................... - switch (ds_flatwidth * ds_flatheight) + if (R_CheckPowersOfTwo()) { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - break; - case 262144:// 512x512 lump - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - break; + if (leveltexture) + { + ds_source = texflat->resizedflat; + ds_flatwidth = texflat->resizedwidth; + ds_flatheight = texflat->resizedheight; + } + else + { + ds_source = levelflat->resizedflat; + ds_flatwidth = levelflat->resizedwidth; + ds_flatheight = levelflat->resizedheight; + } } + R_CheckFlatLength(ds_flatwidth * ds_flatheight); } #endif // ESLOPE + + levelflat->lasttexturenum = levelflat->texturenum; } void R_DrawSinglePlane(visplane_t *pl) @@ -966,71 +1029,24 @@ void R_DrawSinglePlane(visplane_t *pl) currentplane = pl; levelflat = &levelflats[pl->picnum]; + size = W_LumpLength(levelflat->lumpnum); + // Check if the flat is actually a texture. if (levelflat->texturenum != 0 && levelflat->texturenum != -1) R_GetPatchFlat(levelflat, true); + // Check if the flat is actually a patch. + else if (R_CheckIfPatch(levelflat->lumpnum)) + R_GetPatchFlat(levelflat, false); + // Raw flat. else { ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag - size = W_LumpLength(levelflat->lumpnum); - - 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_CheckFlatLength(size); } - if (R_CheckIfPatch(levelflat->lumpnum)) - R_GetPatchFlat(levelflat, false); - ds_powersoftwo = (!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))); + // Check if the flat has dimensions that are powers-of-two numbers. + if (R_CheckPowersOfTwo()) + R_CheckFlatLength(ds_flatwidth * ds_flatheight); if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; diff --git a/src/r_plane.h b/src/r_plane.h index 6e6a6d49d..78aae3fa1 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -94,6 +94,8 @@ void R_PlaneBounds(visplane_t *plane); // Draws a single visplane. void R_DrawSinglePlane(visplane_t *pl); +void R_CheckFlatLength(size_t size); +boolean R_CheckPowersOfTwo(void); typedef struct planemgr_s { From 36036b6cfdeeacc1ded8d869dfb5dc2777e95e27 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Sun, 26 May 2019 12:02:43 +0200 Subject: [PATCH 022/196] Permit textures to use the "TEXTURE" tag in addition to "WALLTEXTURE". It's pointless to make any distinctions anymore IMO, given flats can load them just fine now. --- src/r_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 4157a8850..738fc0727 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -850,7 +850,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE")==0) + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) { numTexturesInLump++; Z_Free(texturesToken); @@ -858,7 +858,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) } else { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\", got \"%s\"",texturesToken); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } @@ -899,7 +899,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE")==0) + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) { Z_Free(texturesToken); // Get the new texture @@ -913,7 +913,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) } else { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\", got \"%s\"",texturesToken); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } From 93f60267c1bd3842cdc961977b22163761f70c26 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 26 May 2019 16:22:33 -0300 Subject: [PATCH 023/196] Minor fixes --- src/hardware/hw_cache.c | 30 ++--- src/hardware/hw_main.c | 38 ++++-- src/r_data.c | 10 +- src/r_draw8.c | 277 ++++++++++++++++++++++++++++++++++++++-- src/r_plane.c | 10 +- 5 files changed, 316 insertions(+), 49 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 84ad4c55b..504ded35b 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -679,21 +679,6 @@ static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) R_FlatPatch(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); } -static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) -{ - // setup the texture info - grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; - grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; - grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; - grMipmap->grInfo.format = GR_TEXFMT_P_8; - grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; - - grMipmap->width = (UINT16)textures[texturenum]->width; - grMipmap->height = (UINT16)textures[texturenum]->height; - - R_FlatTexture(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); -} - static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { size_t size, pflatsize; @@ -765,6 +750,21 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) gr_patchflat = flatlumpnum; } +static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) +{ + // setup the texture info + grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; + grMipmap->grInfo.format = GR_TEXFMT_P_8; + grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; + + grMipmap->width = (UINT16)textures[texturenum]->width; + grMipmap->height = (UINT16)textures[texturenum]->height; + + R_FlatTexture(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); +} + void HWR_GetTextureFlat(INT32 texturenum) { GLTexture_t *grtex; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 793050aa2..02e731164 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -531,6 +531,8 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is INT32 i; float flatxref,flatyref; float fflatwidth, fflatheight; + INT32 flatflag; + boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; @@ -622,22 +624,25 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is break; } - if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? - { - patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); - fflatwidth = patch->width; - fflatheight = patch->height; - } + flatflag = ((INT32)fflatwidth)-1; if (texturenum != 0 && texturenum != -1) { fflatwidth = textures[texturenum]->width; fflatheight = textures[texturenum]->height; } + else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = SHORT(patch->width); + fflatheight = SHORT(patch->height); + } + else + texflat = false; // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)((FLOAT_TO_FIXED(pv->x) % llrint(fflatwidth)) / fflatwidth); - flatyref = (float)((FLOAT_TO_FIXED(pv->y) % llrint(fflatheight)) / fflatheight); + flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatwidth); + flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatheight); // transform v3d = planeVerts; @@ -693,17 +698,24 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is for (i = 0; i < nrPlaneVerts; i++,v3d++,pv++) { // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((pv->x / fflatwidth) - flatxref + scrollx); - v3d->tow = (float)(flatyref - (pv->y / fflatheight) + scrolly); - - //v3d->sow = (float)(pv->x / fflatsize); - //v3d->tow = (float)(pv->y / fflatsize); + if (texflat) + { + v3d->sow = (float)(pv->x / fflatwidth) + scrollx; + v3d->tow = -(float)(pv->y / fflatheight) + scrolly; + } + else + { + v3d->sow = (float)((pv->x / fflatwidth) - flatxref + scrollx); + v3d->tow = (float)(flatyref - (pv->y / fflatheight) + scrolly); + } // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); + if (texflat) + tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle)))); } diff --git a/src/r_data.c b/src/r_data.c index 4157a8850..092dc069a 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1694,7 +1694,7 @@ void R_FlatPatch(patch_t *patch, UINT8 *flat) UINT8 *source; desttop = flat; - deststop = desttop + (patch->width * patch->height); + deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); for (col = 0; col < SHORT(patch->width); col++, desttop++) { @@ -1708,13 +1708,13 @@ void R_FlatPatch(patch_t *patch, UINT8 *flat) topdelta += prevdelta; prevdelta = topdelta; - dest = desttop + (topdelta * patch->width); + dest = desttop + (topdelta * SHORT(patch->width)); source = (UINT8 *)(column) + 3; for (ofs = 0; dest < deststop && ofs < column->length; ofs++) { if (source[ofs] != TRANSPARENTPIXEL) *dest = source[ofs]; - dest += patch->width; + dest += SHORT(patch->width); } column = (column_t *)((UINT8 *)column + column->length + 4); } @@ -1733,9 +1733,8 @@ void R_FlatTexture(size_t tex, UINT8 *flat) desttop = flat; deststop = desttop + (texture->width * texture->height); - for (col = 0; col < SHORT(texture->width); col++, desttop++) + for (col = 0; col < texture->width; col++, desttop++) { - INT32 topdelta, prevdelta = -1; column = (column_t *)R_GetColumn(tex, col); if (!texture->holes) { @@ -1750,6 +1749,7 @@ void R_FlatTexture(size_t tex, UINT8 *flat) } else { + INT32 topdelta, prevdelta = -1; while (column->topdelta != 0xff) { topdelta = column->topdelta; diff --git a/src/r_draw8.c b/src/r_draw8.c index f829707d3..542572707 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -550,12 +550,18 @@ void R_DrawSpan_8 (void) UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - UINT32 flatsize = ds_flatwidth * ds_flatheight; size_t count = (ds_x2 - ds_x1 + 1); xposition = ds_xfrac; yposition = ds_yfrac; xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? if (ds_powersoftwo) { xposition <<= nflatshiftup; yposition <<= nflatshiftup; @@ -566,7 +572,7 @@ void R_DrawSpan_8 (void) colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - if (dest > deststop) + if (dest+8 > deststop) return; if (!ds_powersoftwo) @@ -582,13 +588,56 @@ void R_DrawSpan_8 (void) if (y < 0) y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - *dest++ = colormap[source[((y * ds_flatwidth) + x) % flatsize]]; + x %= ds_flatwidth; + y %= ds_flatheight; + + *dest++ = colormap[source[((y * ds_flatwidth) + x)]]; xposition += xstep; yposition += ystep; } } else { + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[1] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[2] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[3] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[4] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[5] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[6] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[7] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } while (count-- && dest <= deststop) { *dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; @@ -1052,13 +1101,19 @@ void R_DrawSplat_8 (void) UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - UINT32 flatsize = ds_flatwidth * ds_flatheight; size_t count = (ds_x2 - ds_x1 + 1); UINT32 val; xposition = ds_xfrac; yposition = ds_yfrac; xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? if (ds_powersoftwo) { xposition <<= nflatshiftup; yposition <<= nflatshiftup; @@ -1082,7 +1137,10 @@ void R_DrawSplat_8 (void) if (y < 0) y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - val = source[((y * ds_flatwidth) + x) % flatsize]; + x %= ds_flatwidth; + y %= ds_flatheight; + + val = source[((y * ds_flatwidth) + x)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1092,6 +1150,80 @@ void R_DrawSplat_8 (void) } else { + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + // + // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[0] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[1] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[2] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[3] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[4] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[5] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[6] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[7] = colormap[val]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } while (count-- && dest <= deststop) { val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; @@ -1118,13 +1250,19 @@ void R_DrawTranslucentSplat_8 (void) UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - UINT32 flatsize = ds_flatwidth * ds_flatheight; size_t count = (ds_x2 - ds_x1 + 1); UINT32 val; xposition = ds_xfrac; yposition = ds_yfrac; xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? if (ds_powersoftwo) { xposition <<= nflatshiftup; yposition <<= nflatshiftup; @@ -1148,7 +1286,10 @@ void R_DrawTranslucentSplat_8 (void) if (y < 0) y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - val = source[((y * ds_flatwidth) + x) % flatsize]; + x %= ds_flatwidth; + y %= ds_flatheight; + + val = source[((y * ds_flatwidth) + x)]; if (val != TRANSPARENTPIXEL) *dest = *(ds_transmap + (colormap[val] << 8) + *dest); dest++; @@ -1158,6 +1299,62 @@ void R_DrawTranslucentSplat_8 (void) } else { + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } while (count-- && dest <= deststop) { val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; @@ -1184,13 +1381,19 @@ void R_DrawTranslucentSpan_8 (void) UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - UINT32 flatsize = ds_flatwidth * ds_flatheight; size_t count = (ds_x2 - ds_x1 + 1); UINT32 val; xposition = ds_xfrac; yposition = ds_yfrac; xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? if (ds_powersoftwo) { xposition <<= nflatshiftup; yposition <<= nflatshiftup; @@ -1214,7 +1417,10 @@ void R_DrawTranslucentSpan_8 (void) if (y < 0) y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - val = ((y * ds_flatwidth) + x) % flatsize; + x %= ds_flatwidth; + y %= ds_flatheight; + + val = ((y * ds_flatwidth) + x); *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); dest++; xposition += xstep; @@ -1223,6 +1429,46 @@ void R_DrawTranslucentSpan_8 (void) } else { + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; + + dest[1] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; + + dest[2] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; + + dest[3] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + dest[4] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + dest[5] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + dest[6] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + dest[7] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } while (count-- && dest <= deststop) { val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); @@ -1247,12 +1493,18 @@ void R_DrawTranslucentWaterSpan_8(void) UINT8 *dsrc; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - UINT32 flatsize = ds_flatwidth * ds_flatheight; size_t count = (ds_x2 - ds_x1 + 1); xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs); xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? if (ds_powersoftwo) { xposition <<= nflatshiftup; yposition <<= nflatshiftup; @@ -1277,7 +1529,10 @@ void R_DrawTranslucentWaterSpan_8(void) if (y < 0) y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x) % flatsize] << 8) + *dsrc++)]; + x %= ds_flatwidth; + y %= ds_flatheight; + + *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)]; xposition += xstep; yposition += ystep; } diff --git a/src/r_plane.c b/src/r_plane.c index 01d0fdd37..37a76e2cd 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -758,8 +758,8 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) else { patch = (patch_t *)ds_source; - levelflat->width = ds_flatwidth = patch->width; - levelflat->height = ds_flatheight = patch->height; + levelflat->width = ds_flatwidth = SHORT(patch->width); + levelflat->height = ds_flatheight = SHORT(patch->height); levelflat->topoffset = patch->topoffset * FRACUNIT; levelflat->leftoffset = patch->leftoffset * FRACUNIT; @@ -1031,13 +1031,13 @@ void R_DrawSinglePlane(visplane_t *pl) levelflat = &levelflats[pl->picnum]; size = W_LumpLength(levelflat->lumpnum); - // Check if the flat is actually a texture. + // Check if the flat is actually a wall texture. if (levelflat->texturenum != 0 && levelflat->texturenum != -1) R_GetPatchFlat(levelflat, true); - // Check if the flat is actually a patch. + // Maybe it's just a patch, then? else if (R_CheckIfPatch(levelflat->lumpnum)) R_GetPatchFlat(levelflat, false); - // Raw flat. + // It's a raw flat. else { ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag From a4a529bdb5af0a9bd2cc5d8dfef1a2d0d08c48b6 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 26 May 2019 17:41:10 -0300 Subject: [PATCH 024/196] Hardware renderer: Disable Glide-specific texture handling --- src/hardware/hw_cache.c | 15 +++++++++++++++ src/hardware/hw_glide.h | 2 ++ src/hardware/hw_light.c | 2 ++ src/hardware/hw_md2.c | 4 ++++ 4 files changed, 23 insertions(+) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 504ded35b..72b78a985 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -190,6 +190,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap, static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, GrTexInfo *grInfo) { +#ifdef GLIDE_API_COMPATIBILITY // Build the full textures from patches. static const GrLOD_t gr_lods[9] = { @@ -226,6 +227,9 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, INT32 j,k; INT32 max,min; +#else + (void)grInfo; +#endif // find a power of 2 width/height if (cv_grrounddown.value) @@ -281,6 +285,7 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, } else { +#ifdef GLIDE_API_COMPATIBILITY //size up to nearest power of 2 blockwidth = 1; while (blockwidth < originalwidth) @@ -298,9 +303,14 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockheight > 2048) blockheight = 2048; //I_Error("3D GenerateTexture : too big"); +#else + blockwidth = originalwidth; + blockheight = originalheight; +#endif } // do the boring LOD stuff.. blech! +#ifdef GLIDE_API_COMPATIBILITY if (blockwidth >= blockheight) { max = blockwidth; @@ -332,6 +342,7 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockwidth < blockheight) j += 4; grInfo->aspectRatioLog2 = gr_aspects[j].aspect; +#endif blocksize = blockwidth * blockheight; @@ -684,9 +695,11 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) size_t size, pflatsize; // setup the texture info +#ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif grMipmap->grInfo.format = GR_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; @@ -753,9 +766,11 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) { // setup the texture info +#ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif grMipmap->grInfo.format = GR_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; diff --git a/src/hardware/hw_glide.h b/src/hardware/hw_glide.h index 2625d5864..bf91229ef 100644 --- a/src/hardware/hw_glide.h +++ b/src/hardware/hw_glide.h @@ -59,9 +59,11 @@ typedef FxI32 GrTextureFormat_t; typedef struct { +#ifdef GLIDE_API_COMPATIBILITY GrLOD_t smallLodLog2; GrLOD_t largeLodLog2; GrAspectRatio_t aspectRatioLog2; +#endif GrTextureFormat_t format; void *data; } GrTexInfo; diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index a93e96dc3..23e8a3431 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -1112,9 +1112,11 @@ static void HWR_SetLight(void) lightmappatch.height = 128; lightmappatch.mipmap.width = 128; lightmappatch.mipmap.height = 128; +#ifdef GLIDE_API_COMPATIBILITY lightmappatch.mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif lightmappatch.mipmap.flags = 0; //TF_WRAPXY; // DEBUG: view the overdraw ! } HWD.pfnSetTexture(&lightmappatch.mipmap); diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index cb33562d8..e3ac82fa8 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -716,10 +716,12 @@ static void md2_loadTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; +#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif } HWD.pfnSetTexture(&grpatch->mipmap); HWR_UnlockCachedPatch(grpatch); @@ -767,10 +769,12 @@ static void md2_loadBlendTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; +#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif } HWD.pfnSetTexture(&grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary HWR_UnlockCachedPatch(grpatch); From 0bcf89679e7fa52d04c184475b0da67164e30f14 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 26 May 2019 18:16:13 -0300 Subject: [PATCH 025/196] Hardware renderer: Fix polyobjects --- src/hardware/hw_cache.c | 2 +- src/hardware/hw_main.c | 38 +++++++++++++++++++++++++++----------- src/p_setup.c | 5 ++++- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 72b78a985..54a1f6695 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -677,7 +677,7 @@ GLTexture_t *HWR_GetTexture(INT32 tex) return grtex; } -// Lactozilla +// HWR_RenderPlane and HWR_RenderPolyObjectPlane need this to get the flat dimensions from a patch. lumpnum_t gr_patchflat; static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 02e731164..de273cfed 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3172,6 +3172,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, INT32 i; float flatxref,flatyref; float fflatwidth, fflatheight; + INT32 flatflag; + boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; @@ -3231,22 +3233,25 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, break; } - if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? - { - patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); - fflatwidth = patch->width; - fflatheight = patch->height; - } + flatflag = ((INT32)fflatwidth)-1; if (texturenum != 0 && texturenum != -1) { fflatwidth = textures[texturenum]->width; fflatheight = textures[texturenum]->height; } + else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = SHORT(patch->width); + fflatheight = SHORT(patch->height); + } + else + texflat = false; // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)((polysector->origVerts[0].x % llrint(fflatwidth)) / fflatwidth); - flatyref = (float)((polysector->origVerts[0].y % llrint(fflatheight)) / fflatheight); + flatxref = (float)((polysector->origVerts[0].x & (~flatflag)) / fflatwidth); + flatyref = (float)((polysector->origVerts[0].y & (~flatflag)) / fflatheight); // transform v3d = planeVerts; @@ -3300,15 +3305,26 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++) { - // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); // Go from the polysector's original vertex locations - v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); // Means the flat is offset based on the original vertex locations + // Go from the polysector's original vertex locations + // Means the flat is offset based on the original vertex locations + if (texflat) + { + v3d->sow = (float)(FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) + scrollx; + v3d->tow = -(float)(FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly; + } + else + { + v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); + v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); + } // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); + if (texflat) + tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle)))); } diff --git a/src/p_setup.c b/src/p_setup.c index 97bace860..c1ba4c67a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -613,8 +613,11 @@ INT32 P_AddLevelFlatRuntime(const char *flatname) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); - // Lactozilla levelflat->texturenum = R_CheckTextureNumForName(flatname); + levelflat->lasttexturenum = levelflat->texturenum; + + levelflat->baselumpnum = LUMPERROR; + levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); From 39857a846a47af5bad63992542636dae956e8fd3 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 26 May 2019 23:37:23 -0300 Subject: [PATCH 026/196] PNG support --- src/doomdef.h | 2 + src/hardware/hw_cache.c | 10 +- src/r_data.c | 459 ++++++++++++++++++++++++++++++++++++++-- src/r_data.h | 14 +- src/r_plane.c | 54 +++-- src/w_wad.c | 2 - 6 files changed, 508 insertions(+), 33 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 527cdf05f..2bf9efa68 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -536,4 +536,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// SRB2CB itself ported this from PrBoom+ #define NEWCLIP +//#define NO_PNG_LUMPS + #endif // __DOOMDEF__ diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 54a1f6695..457f628d0 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -451,7 +451,10 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { + size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump); realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); HWR_DrawPatchInCache(&grtex->mipmap, blockwidth, blockheight, blockwidth*format2bpp[grtex->mipmap.grInfo.format], @@ -683,11 +686,14 @@ lumpnum_t gr_patchflat; static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); + size_t lumplength = W_LumpLength(flatlumpnum); + if (R_IsLumpPNG((UINT8 *)patch, lumplength)) + patch = R_PNGToPatch((UINT8 *)patch, lumplength); grMipmap->width = (UINT16)SHORT(patch->width); grMipmap->height = (UINT16)SHORT(patch->height); - R_FlatPatch(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); + R_PatchToFlat(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); } static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) @@ -777,7 +783,7 @@ static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; - R_FlatTexture(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); + R_TextureToFlat(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); } void HWR_GetTextureFlat(INT32 texturenum) diff --git a/src/r_data.c b/src/r_data.c index db92c11fe..ea8785afb 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -40,6 +40,28 @@ #include #endif +#ifdef HAVE_PNG + +#ifndef _MSC_VER +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#endif + +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE +#endif + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif + +#include "png.h" +#ifndef PNG_READ_SUPPORTED +#undef HAVE_PNG +#endif +#endif + // // Texture definition. // Each texture is composed of one or more patches, @@ -178,7 +200,7 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig // 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, dynamic ligthing, & speed. +// 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. @@ -195,6 +217,10 @@ static UINT8 *R_GenerateTexture(size_t texnum) column_t *patchcol; UINT32 *colofs; + UINT16 wadnum; + lumpnum_t lumpnum; + size_t lumplength; + I_Assert(texnum <= (size_t)numtextures); texture = textures[texnum]; I_Assert(texture != NULL); @@ -209,7 +235,13 @@ static UINT8 *R_GenerateTexture(size_t texnum) { boolean holey = false; patch = texture->patches; - realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); + + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); // Check the patch for holes. if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) @@ -238,7 +270,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) if (holey) { texture->holes = true; - blocksize = W_LumpLengthPwad(patch->wad, patch->lump); + blocksize = lumplength; block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function &texturecache[texnum]); M_Memcpy(block, realpatch, blocksize); @@ -274,7 +306,13 @@ static UINT8 *R_GenerateTexture(size_t texnum) // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { - realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); @@ -487,7 +525,10 @@ void R_LoadTextures(void) // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); i++, j++) { - patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE); + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; + size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum); + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); // Then, check the lump directly to see if it's a texture SOC, // and if it is, load it using dehacked instead. @@ -503,9 +544,19 @@ void R_LoadTextures(void) texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name)); - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) + { + INT16 width, height; + R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else + { + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); + } texture->patchcount = 1; texture->holes = false; @@ -1178,7 +1229,6 @@ INT32 R_ColormapNumForName(char *name) // static double deltas[256][3], map[256][3]; -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); static int RoundUp(double number); INT32 R_CreateColormap(char *p1, char *p2, char *p3) @@ -1358,7 +1408,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3) // Thanks to quake2 source! // utils3/qdata/images.c -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; @@ -1686,7 +1736,7 @@ boolean R_CheckIfPatch(lumpnum_t lump) return result; } -void R_FlatPatch(patch_t *patch, UINT8 *flat) +void R_PatchToFlat(patch_t *patch, UINT8 *flat) { fixed_t col, ofs; column_t *column; @@ -1721,7 +1771,392 @@ void R_FlatPatch(patch_t *patch, UINT8 *flat) } } -void R_FlatTexture(size_t tex, UINT8 *flat) +#ifndef NO_PNG_LUMPS +boolean R_IsLumpPNG(UINT8 *d, size_t s) +{ + if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ + return false; + // Check for PNG file signature using memcmp + // As it may be faster on CPUs with slow unaligned memory access + // Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature + return (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0); +} + +#ifdef HAVE_PNG +typedef struct { + png_bytep buffer; + png_uint_32 bufsize; + png_uint_32 current_pos; +} png_ioread; + +static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_ioread *f = png_get_io_ptr(png_ptr); + if (length > (f->bufsize - f->current_pos)) + png_error(png_ptr, "read error in read_data_memory (loadpng)"); + memcpy(data, f->buffer + f->current_pos, length); + f->current_pos += length; +} + +static void PNG_error(png_structp PNG, png_const_charp pngtext) +{ + CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext); + //I_Error("libpng error at %p: %s", PNG, pngtext); +} + +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(UINT8 *png, UINT16 *w, UINT16 *h, 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; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + png_ioread png_io; + png_bytep *row_pointers; + + 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; + } + + 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; + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + 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; + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // png_source is array which have png data + png_io.buffer = (png_bytep)png; + png_io.bufsize = size; + png_io.current_pos = 0; + // set our own read_function + png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + +#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, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + 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); + + if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + else if (color_type != PNG_COLOR_TYPE_RGB_ALPHA && color_type != PNG_COLOR_TYPE_GRAY_ALPHA) + { +#if PNG_LIBPNG_VER < 10207 + png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); +#else + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); +#endif + } + + png_read_update_info(png_ptr, png_info_ptr); + + // Read the image + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + for (y = 0; y < height; y++) + row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, png_info_ptr)); + png_read_image(png_ptr, row_pointers); + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + + *w = (INT32)width; + *h = (INT32)height; + return row_pointers; +} + +// Convert a PNG to a raw image. +static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) +{ + UINT8 *flat; + png_uint_32 x, y; + png_bytep *row_pointers = PNG_Read(png, w, h, size); + png_uint_32 width = *w, height = *h; + + if (!row_pointers) + return NULL; + + // Convert the image to 8bpp + flat = Z_Malloc(width * height, PU_STATIC, NULL); + memset(flat, TRANSPARENTPIXEL, width * height); + for (y = 0; y < height; y++) + { + png_bytep row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); + } + } + free(row_pointers); + + return flat; +} + +// Get the alpha mask of the image. +static UINT8 *PNG_GetAlphaMask(UINT8 *png, size_t size) +{ + UINT8 *mask; + png_uint_32 x, y; + UINT16 width, height; + png_bytep *row_pointers = PNG_Read(png, &width, &height, size); + + if (!row_pointers) + return NULL; + + // Convert the image to 8bpp + mask = Z_Malloc(width * height, PU_STATIC, NULL); + memset(mask, 0, width * height); + for (y = 0; y < height; y++) + { + png_bytep row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + mask[((y * width) + x)] = (UINT8)px[3]; + } + } + free(row_pointers); + + return mask; +} + +// Convert a PNG to a flat. +UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) +{ + return PNG_RawConvert(png, &levelflat->width, &levelflat->height, size); +} + +// Convert a PNG to a patch. +// This is adapted from the "kartmaker" utility +static unsigned char imgbuf[1<<26]; +patch_t *R_PNGToPatch(UINT8 *png, size_t size) +{ + UINT16 width, height; + UINT8 *raw = PNG_RawConvert(png, &width, &height, size); + UINT8 *alphamask = PNG_GetAlphaMask(png, size); + + UINT32 x, y; + UINT8 *img; + UINT8 *imgptr = imgbuf; + UINT8 *colpointers, *startofspan; + + #define WRITE8(buf, a) ({*buf = (a); buf++;}) + #define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;}) + #define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);}) + + if (!raw) + return NULL; + + // Write image size and offset + WRITE16(imgptr, width); + WRITE16(imgptr, height); + // no offsets + WRITE16(imgptr, 0); + WRITE16(imgptr, 0); + + // 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; + + //printf("%d ", x); + // Write column pointer (@TODO may be wrong) + WRITE32(colpointers, imgptr - imgbuf); + + // Write pixels + for (y = 0; y < height; y++) + { + UINT8 paletteIndex = raw[((y * width) + x)]; + UINT8 opaque = alphamask[((y * width) + x)]; // If 1, we have a pixel + + // End span if we have a transparent pixel + if (!opaque) + { + if (startofspan) + WRITE8(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) + WRITE8(imgptr, 0); + + if (y > 254) + { + // Make sure we're aligned to 254 + if (lastStartY < 254) + { + WRITE8(imgptr, 254); + WRITE8(imgptr, 0); + imgptr += 2; + lastStartY = 254; + } + + // Write stopgap empty spans if needed + writeY = y - lastStartY; + + while (writeY > 254) + { + WRITE8(imgptr, 254); + WRITE8(imgptr, 0); + imgptr += 2; + writeY -= 254; + } + } + + startofspan = imgptr; + WRITE8(imgptr, writeY);///@TODO calculate starting y pos + imgptr += 2; + spanSize = 0; + + lastStartY = y; + } + + // Write the pixel + WRITE8(imgptr, paletteIndex); + spanSize++; + startofspan[1] = spanSize; + } + + if (startofspan) + WRITE8(imgptr, 0); + + WRITE8(imgptr, 0xFF); + } + + #undef WRITE8 + #undef WRITE16 + #undef WRITE32 + + size = imgptr-imgbuf; + img = malloc(size); + memcpy(img, imgbuf, size); + return (patch_t *)img; +} + +boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) +{ + png_structp png_ptr; + png_infop png_info_ptr; + png_uint_32 w, h; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + png_ioread png_io; + + 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; + } + + 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; + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + 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; + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // png_source is array which have png data + png_io.buffer = (png_bytep)png; + png_io.bufsize = size; + png_io.current_pos = 0; + // set our own read_function + png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + +#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); + + // okay done. stop. + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + + *width = (INT32)w; + *height = (INT32)h; + return true; +} +#endif +#endif + +void R_TextureToFlat(size_t tex, UINT8 *flat) { texture_t *texture = textures[tex]; diff --git a/src/r_data.h b/src/r_data.h index 855daa06d..5f3f10d59 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -16,6 +16,7 @@ #include "r_defs.h" #include "r_state.h" +#include "p_setup.h" // levelflats #ifdef __GNUG__ #pragma interface @@ -105,14 +106,23 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3); const char *R_ColormapNameForNum(INT32 num); boolean R_CheckIfPatch(lumpnum_t lump); +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); -void R_FlatPatch(patch_t *patch, UINT8 *flat); -void R_FlatTexture(size_t tex, UINT8 *flat); +void R_PatchToFlat(patch_t *patch, UINT8 *flat); +void R_TextureToFlat(size_t tex, UINT8 *flat); void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, UINT16 srcwidth, UINT16 srcheight, UINT16 resizewidth, UINT16 resizeheight, UINT16 destwidth, UINT16 destheight); +#ifndef NO_PNG_LUMPS +boolean R_IsLumpPNG(UINT8 *d, size_t s); + +UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size); +patch_t *R_PNGToPatch(UINT8 *png, size_t size); +boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); +#endif + extern INT32 numtextures; #endif diff --git a/src/r_plane.c b/src/r_plane.c index 37a76e2cd..d07a68759 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -714,7 +714,7 @@ void R_CheckFlatLength(size_t size) } } -static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) +static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) { textureflat_t *texflat = &texflats[levelflat->texturenum]; patch_t *patch = NULL; @@ -751,23 +751,46 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); - R_FlatTexture(levelflat->texturenum, texflat->flat); + R_TextureToFlat(levelflat->texturenum, texflat->flat); ds_source = texflat->flat; } else { patch = (patch_t *)ds_source; - levelflat->width = ds_flatwidth = SHORT(patch->width); - levelflat->height = ds_flatheight = SHORT(patch->height); +#ifndef NO_PNG_LUMPS +#ifdef HAVE_PNG + if (ispng) + { + levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->lumpnum)); + levelflat->topoffset = levelflat->leftoffset = 0; + if (levelflat->flatpatch == NULL) + { + lumpnum_t redflr = W_CheckNumForName("REDFLR"); + levelflat->flatpatch = (UINT8 *)W_CacheLumpNum(redflr, PU_STATIC); + R_CheckFlatLength(W_LumpLength(redflr)); + R_CheckPowersOfTwo(); + } + else + { + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + } + else +#endif +#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 = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); - memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); - R_FlatPatch(patch, levelflat->flatpatch); + levelflat->topoffset = patch->topoffset * FRACUNIT; + levelflat->leftoffset = patch->leftoffset * FRACUNIT; + levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); + R_PatchToFlat(patch, levelflat->flatpatch); + } ds_source = levelflat->flatpatch; } @@ -1030,19 +1053,20 @@ void R_DrawSinglePlane(visplane_t *pl) currentplane = pl; levelflat = &levelflats[pl->picnum]; size = W_LumpLength(levelflat->lumpnum); + ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag // Check if the flat is actually a wall texture. if (levelflat->texturenum != 0 && levelflat->texturenum != -1) - R_GetPatchFlat(levelflat, true); + R_GetPatchFlat(levelflat, true, false); // Maybe it's just a patch, then? else if (R_CheckIfPatch(levelflat->lumpnum)) - R_GetPatchFlat(levelflat, false); + R_GetPatchFlat(levelflat, false, false); + // Maybe it's a PNG?! + else if (R_IsLumpPNG(ds_source, size)) + R_GetPatchFlat(levelflat, false, true); // It's a raw flat. else - { - ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag R_CheckFlatLength(size); - } // Check if the flat has dimensions that are powers-of-two numbers. if (R_CheckPowersOfTwo()) diff --git a/src/w_wad.c b/src/w_wad.c index c4f9ceca8..5e25dea97 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1179,8 +1179,6 @@ void zerr(int ret) } #endif -#define NO_PNG_LUMPS - #ifdef NO_PNG_LUMPS static void ErrorIfPNG(UINT8 *d, size_t s, char *f, char *l) { From 0e162f8f6118f3fd22311ac2cb9930dead33e0a1 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Thu, 20 Jun 2019 13:33:31 +0200 Subject: [PATCH 027/196] Add Sryder's orbital camera thing as an option. --- src/m_menu.c | 22 ++++++++++++---------- src/p_local.h | 4 ++-- src/p_user.c | 25 ++++++++++++++++++++----- src/r_main.c | 2 ++ 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 4f78d0adc..538e2ebf1 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1000,12 +1000,13 @@ static menuitem_t OP_P1ControlsMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Joystick1Def , 30}, {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam , 50}, - {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 60}, - {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 70}, + {IT_STRING | IT_CVAR, NULL, "Third-person Orbital" , &cv_cam_orbit , 60}, + {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 70}, + {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 80}, - //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog, 90}, - {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar, 90}, - {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake, 100}, + //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog, 100}, + {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar, 100}, + {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake, 110}, }; static menuitem_t OP_P2ControlsMenu[] = @@ -1015,12 +1016,13 @@ static menuitem_t OP_P2ControlsMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Joystick2Def , 30}, {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam2 , 50}, - {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 60}, - {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 70}, + {IT_STRING | IT_CVAR, NULL, "Third-person Orbital" , &cv_cam2_orbit , 60}, + {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 70}, + {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 80}, - //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog2, 90}, - {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar2, 90}, - {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake2, 100}, + //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog2, 100}, + {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar2, 100}, + {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake2, 110}, }; static menuitem_t OP_ChangeControlsMenu[] = diff --git a/src/p_local.h b/src/p_local.h index b686b9f09..d094b6399 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -109,10 +109,10 @@ typedef struct camera_s extern camera_t camera, camera2; extern consvar_t cv_cam_dist, cv_cam_still, cv_cam_height; -extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed; +extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed, cv_cam_orbit; extern consvar_t cv_cam2_dist, cv_cam2_still, cv_cam2_height; -extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed; +extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed, cv_cam2_orbit; extern fixed_t t_cam_dist, t_cam_height, t_cam_rotate; extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate; diff --git a/src/p_user.c b/src/p_user.c index ca14c64d4..c38514db9 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8898,12 +8898,14 @@ consvar_t cv_cam_still = {"cam_still", "Off", 0, CV_OnOff, NULL, 0, NULL, 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_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_dist = {"cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_height = {"cam2_height", "25", CV_FLOAT|CV_SAVE, 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_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; fixed_t t_cam_dist = -42; fixed_t t_cam_height = -42; @@ -8957,7 +8959,7 @@ void P_ResetCamera(player_t *player, camera_t *thiscam) boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled) { angle_t angle = 0, focusangle = 0, focusaiming = 0; - fixed_t x, y, z, dist, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; + fixed_t x, y, z, dist, distxy, distz, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; INT32 camrotate; boolean camstill, cameranoclip; mobj_t *mo; @@ -9165,13 +9167,26 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist <<= 1; } + + checkdist = (dist = FixedMul(dist, player->camerascale)); if (checkdist < 128*FRACUNIT) checkdist = 128*FRACUNIT; - x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); - y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); + if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) + { + distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); + distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); + } + else + { + distxy = dist; + distz = 0; + } + + x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); + y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); #if 0 if (twodlevel || (mo->flags2 & MF2_TWOD)) @@ -9208,9 +9223,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall pviewheight = FixedMul(41*player->height/48, mo->scale); if (mo->eflags & MFE_VERTICALFLIP) - z = mo->z + mo->height - pviewheight - camheight; + z = mo->z + mo->height - pviewheight - camheight + distz; else - z = mo->z + pviewheight + camheight; + z = mo->z + pviewheight + camheight + distz; // move camera down to move under lower ceilings newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1)); diff --git a/src/r_main.c b/src/r_main.c index 273d13a56..5ed411046 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1176,6 +1176,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_cam_speed); CV_RegisterVar(&cv_cam_rotate); CV_RegisterVar(&cv_cam_rotspeed); + CV_RegisterVar(&cv_cam_orbit); CV_RegisterVar(&cv_cam2_dist); CV_RegisterVar(&cv_cam2_still); @@ -1183,6 +1184,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_cam2_speed); CV_RegisterVar(&cv_cam2_rotate); CV_RegisterVar(&cv_cam2_rotspeed); + CV_RegisterVar(&cv_cam2_orbit); CV_RegisterVar(&cv_showhud); CV_RegisterVar(&cv_translucenthud); From 02c3710211b408e103ce78d1d6cb3dbd93e43042 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 25 Jun 2019 14:40:00 -0300 Subject: [PATCH 028/196] hHA --- src/p_setup.h | 6 -- src/r_data.h | 5 -- src/r_draw8.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++--- src/r_plane.c | 164 ++++++++++-------------------------- 4 files changed, 264 insertions(+), 140 deletions(-) diff --git a/src/p_setup.h b/src/p_setup.h index 824584be7..a123f757a 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -49,12 +49,6 @@ typedef struct // for patchflats UINT8 *flatpatch; - -#ifdef ESLOPE - // rescaled version of the above - UINT8 *resizedflat; - UINT16 resizedwidth, resizedheight; -#endif } levelflat_t; extern size_t numlevelflats; diff --git a/src/r_data.h b/src/r_data.h index 855daa06d..c528fcfe1 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -52,11 +52,6 @@ typedef struct { UINT8 *flat; INT16 width, height; - -#ifdef ESLOPE - UINT8 *resizedflat; - INT16 resizedwidth, resizedheight; -#endif } textureflat_t; // all loaded and prepared textures from the start of the game diff --git a/src/r_draw8.c b/src/r_draw8.c index 542572707..1c4527a8e 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -724,7 +724,24 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; iz += ds_sz.x; uz += ds_su.x; @@ -761,7 +778,24 @@ void R_DrawTiltedSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -777,7 +811,24 @@ void R_DrawTiltedSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; } else { @@ -798,7 +849,24 @@ void R_DrawTiltedSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -859,7 +927,24 @@ void R_DrawTiltedTranslucentSpan_8(void) v = (INT64)(vz*z) + viewy; colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; iz += ds_sz.x; uz += ds_su.x; @@ -896,7 +981,24 @@ void R_DrawTiltedTranslucentSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -912,7 +1014,24 @@ void R_DrawTiltedTranslucentSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); } else { @@ -933,7 +1052,24 @@ void R_DrawTiltedTranslucentSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -994,9 +1130,28 @@ void R_DrawTiltedSplat_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) *dest = colormap[val]; + dest++; iz += ds_sz.x; uz += ds_su.x; @@ -1033,7 +1188,24 @@ void R_DrawTiltedSplat_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1051,7 +1223,24 @@ void R_DrawTiltedSplat_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; } @@ -1075,6 +1264,24 @@ void R_DrawTiltedSplat_8(void) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = ((u-viewx) >> FRACBITS); + fixed_t y = ((v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; diff --git a/src/r_plane.c b/src/r_plane.c index 37a76e2cd..f79a5f90a 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -718,7 +718,6 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) { textureflat_t *texflat = &texflats[levelflat->texturenum]; patch_t *patch = NULL; - UINT8 *tex; boolean texturechanged = (leveltexture ? (levelflat->texturenum != levelflat->lasttexturenum) : false); // Check if the texture changed. @@ -738,11 +737,6 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) // If the texture changed, or the patch doesn't exist, convert either of them to a flat. if (levelflat->flatpatch == NULL || texturechanged) { -#ifdef ESLOPE - INT32 resizewidth, resizeheight, newresize; - INT32 checkresizewidth, checkresizeheight; -#endif // ESLOPE - if (leveltexture) { texture_t *texture = textures[levelflat->texturenum]; @@ -770,75 +764,6 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) ds_source = levelflat->flatpatch; } - -#ifdef ESLOPE - // Crop the flat, if necessary. - if (!R_CheckPowersOfTwo()) - { - // Scale up to nearest power of 2 - resizewidth = resizeheight = 1; - while (resizewidth < ds_flatwidth) - resizewidth <<= 1; - while (resizeheight < ds_flatheight) - resizeheight <<= 1; - - // Scale down to fit in 2048x2048 - if (resizewidth > 2048) - resizewidth = 2048; - if (resizeheight > 2048) - resizeheight = 2048; - - // A single pixel difference is negligible. - checkresizewidth = ds_flatwidth - 1; - if (checkresizewidth & (checkresizewidth - 1)) - { - checkresizewidth += 2; - if (checkresizewidth & (checkresizewidth - 1)) - { - while (resizewidth > ds_flatwidth) - resizewidth >>= 1; - } - else - resizewidth = checkresizewidth; - } - else - resizewidth = checkresizewidth; - - checkresizeheight = ds_flatheight - 1; - if (checkresizeheight & (checkresizeheight - 1)) - { - checkresizeheight += 2; - if (checkresizeheight & (checkresizeheight - 1)) - { - while (resizeheight > ds_flatheight) - resizeheight >>= 1; - } - else - resizeheight = checkresizeheight; - } - else - resizeheight = checkresizeheight; - - // Find smallest size. - newresize = min(resizewidth, resizeheight); - - // Allocate texture. - tex = Z_Malloc(newresize * newresize, PU_LEVEL, NULL); - memset(tex, TRANSPARENTPIXEL, newresize * newresize); - R_CropFlat(ds_source, tex, ds_flatwidth, ds_flatheight, min(resizewidth, newresize), min(resizeheight, newresize), newresize, newresize); - - if (leveltexture) - { - texflat->resizedflat = tex; - texflat->resizedwidth = texflat->resizedheight = newresize; - } - else - { - levelflat->resizedflat = tex; - levelflat->resizedwidth = levelflat->resizedheight = newresize; - } - } -#endif // ESLOPE } else { @@ -850,28 +775,6 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture) yoffs += levelflat->topoffset; } -#ifdef ESLOPE - if (currentplane->slope) - { - if (R_CheckPowersOfTwo()) - { - if (leveltexture) - { - ds_source = texflat->resizedflat; - ds_flatwidth = texflat->resizedwidth; - ds_flatheight = texflat->resizedheight; - } - else - { - ds_source = levelflat->resizedflat; - ds_flatwidth = levelflat->resizedwidth; - ds_flatheight = levelflat->resizedheight; - } - } - R_CheckFlatLength(ds_flatwidth * ds_flatheight); - } -#endif // ESLOPE - levelflat->lasttexturenum = levelflat->texturenum; } @@ -1061,22 +964,32 @@ void R_DrawSinglePlane(visplane_t *pl) floatv3_t p, m, n; float ang; float vx, vy, vz; - float fudge; + float fudge = 0; // compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly // use this as a temp var to store P_GetZAt's return value each time fixed_t temp; - xoffs &= ((1 << (32-nflatshiftup))-1); - yoffs &= ((1 << (32-nflatshiftup))-1); + if (ds_powersoftwo) + { + // But xoffs and yoffs are zero..... ???!?!?!???!?!?! + xoffs &= ((1 << (32-nflatshiftup))-1); + yoffs &= ((1 << (32-nflatshiftup))-1); - xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red - fudge = ((1<slope->o.x; + yoffs = pl->slope->o.y; + } vx = FIXED_TO_FLOAT(pl->viewx+xoffs); vy = FIXED_TO_FLOAT(pl->viewy-yoffs); @@ -1111,13 +1024,16 @@ void R_DrawSinglePlane(visplane_t *pl) temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang))); n.y = FIXED_TO_FLOAT(temp) - zeroheight; - m.x /= fudge; - m.y /= fudge; - m.z /= fudge; + if (ds_powersoftwo) + { + m.x /= fudge; + m.y /= fudge; + m.z /= fudge; - n.x *= fudge; - n.y *= fudge; - n.z *= fudge; + n.x *= fudge; + n.y *= fudge; + n.z *= fudge; + } // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. #define CROSS(d, v1, v2) \ @@ -1134,14 +1050,26 @@ void R_DrawSinglePlane(visplane_t *pl) ds_sz.z *= focallengthf; // Premultiply the texture vectors with the scale factors + if (ds_powersoftwo) + { #define SFMULT 65536.f*(1< Date: Tue, 25 Jun 2019 14:41:07 -0300 Subject: [PATCH 029/196] Delete R_CropFlat --- src/r_data.c | 33 --------------------------------- src/r_data.h | 4 ---- 2 files changed, 37 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index db92c11fe..0e44ec8c7 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1770,36 +1770,3 @@ void R_FlatTexture(size_t tex, UINT8 *flat) } } } - -void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, - UINT16 srcwidth, UINT16 srcheight, - UINT16 resizewidth, UINT16 resizeheight, - UINT16 destwidth, UINT16 destheight) -{ - UINT16 y; - UINT16 position = 0; - for (y = 0; y < destheight; y++) - { - if (position > (srcwidth * srcheight)) - break; - if (srcwidth != resizewidth) - { - if (resizewidth > srcwidth) - { - UINT8 *pos2 = srcflat+position; - UINT8 lastpixel = *(pos2-1); - M_Memcpy(destflat, srcflat+position, destwidth); - memset(pos2, lastpixel, resizewidth-srcwidth); - } - else - M_Memcpy(destflat, srcflat+position, resizewidth); - } - else - M_Memcpy(destflat, srcflat+position, destwidth); - destflat += destwidth; - position += srcwidth; - } - - while (y++ < min(resizeheight, srcheight)) - memset(destflat + (y * destwidth), *(destflat - 1), destwidth); -} diff --git a/src/r_data.h b/src/r_data.h index c528fcfe1..9af621142 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -103,10 +103,6 @@ boolean R_CheckIfPatch(lumpnum_t lump); void R_FlatPatch(patch_t *patch, UINT8 *flat); void R_FlatTexture(size_t tex, UINT8 *flat); -void R_CropFlat(UINT8 *srcflat, UINT8 *destflat, - UINT16 srcwidth, UINT16 srcheight, - UINT16 resizewidth, UINT16 resizeheight, - UINT16 destwidth, UINT16 destheight); extern INT32 numtextures; From 5047f4e7f08f10410f970ea50de745be7a73edb2 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 25 Jun 2019 14:58:34 -0300 Subject: [PATCH 030/196] Fix slope flat offsets --- src/r_plane.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index f79a5f90a..e982f41d6 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -972,6 +972,7 @@ void R_DrawSinglePlane(visplane_t *pl) if (ds_powersoftwo) { // But xoffs and yoffs are zero..... ???!?!?!???!?!?! + // (Except when flat alignment is involved) xoffs &= ((1 << (32-nflatshiftup))-1); yoffs &= ((1 << (32-nflatshiftup))-1); @@ -986,9 +987,10 @@ void R_DrawSinglePlane(visplane_t *pl) } else { - // The origin vector is a vertex from whatever linedef defined this slope - xoffs = -pl->slope->o.x; - yoffs = pl->slope->o.y; + // Whoops, this is actually incorrect behaviour. + // Keep xoffs and yoffs as they are if this flat has offsets + //xoffs = -pl->slope->o.x; + //yoffs = pl->slope->o.y; } vx = FIXED_TO_FLOAT(pl->viewx+xoffs); From afa6afa593d8aa721928c044e1c4f6f200649210 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 28 Jun 2019 19:43:37 -0300 Subject: [PATCH 031/196] something something memory leaks --- src/r_data.c | 4 ++-- src/r_plane.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 9e9e70bc3..0e15c4689 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1916,7 +1916,7 @@ static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) return NULL; // Convert the image to 8bpp - flat = Z_Malloc(width * height, PU_STATIC, NULL); + flat = Z_Malloc(width * height, PU_LEVEL, NULL); memset(flat, TRANSPARENTPIXEL, width * height); for (y = 0; y < height; y++) { @@ -1944,7 +1944,7 @@ static UINT8 *PNG_GetAlphaMask(UINT8 *png, size_t size) return NULL; // Convert the image to 8bpp - mask = Z_Malloc(width * height, PU_STATIC, NULL); + mask = Z_Malloc(width * height, PU_LEVEL, NULL); memset(mask, 0, width * height); for (y = 0; y < height; y++) { diff --git a/src/r_plane.c b/src/r_plane.c index 1cbdc4b8b..4cfa4a49c 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -714,8 +714,9 @@ void R_CheckFlatLength(size_t size) } } -static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) +static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) { + UINT8 *flat; textureflat_t *texflat = &texflats[levelflat->texturenum]; patch_t *patch = NULL; boolean texturechanged = (leveltexture ? (levelflat->texturenum != levelflat->lasttexturenum) : false); @@ -725,7 +726,7 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean { if (texflat != NULL && texflat->flat) { - ds_source = texflat->flat; + flat = texflat->flat; ds_flatwidth = texflat->width; ds_flatheight = texflat->height; texturechanged = false; @@ -746,8 +747,11 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); R_TextureToFlat(levelflat->texturenum, texflat->flat); + flat = texflat->flat; - ds_source = texflat->flat; + levelflat->flatpatch = flat; + levelflat->width = ds_flatwidth; + levelflat->height = ds_flatheight; } else { @@ -761,7 +765,7 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean if (levelflat->flatpatch == NULL) { lumpnum_t redflr = W_CheckNumForName("REDFLR"); - levelflat->flatpatch = (UINT8 *)W_CacheLumpNum(redflr, PU_STATIC); + levelflat->flatpatch = (UINT8 *)W_CacheLumpNum(redflr, PU_CACHE); R_CheckFlatLength(W_LumpLength(redflr)); R_CheckPowersOfTwo(); } @@ -785,12 +789,12 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); R_PatchToFlat(patch, levelflat->flatpatch); } - ds_source = levelflat->flatpatch; + flat = levelflat->flatpatch; } } else { - ds_source = levelflat->flatpatch; + flat = levelflat->flatpatch; ds_flatwidth = levelflat->width; ds_flatheight = levelflat->height; @@ -799,10 +803,12 @@ static void R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean } levelflat->lasttexturenum = levelflat->texturenum; + return flat; } void R_DrawSinglePlane(visplane_t *pl) { + UINT8 *flat; INT32 light = 0; INT32 x; INT32 stop, angle; @@ -960,16 +966,25 @@ void R_DrawSinglePlane(visplane_t *pl) // Check if the flat is actually a wall texture. if (levelflat->texturenum != 0 && levelflat->texturenum != -1) - R_GetPatchFlat(levelflat, true, false); + flat = R_GetPatchFlat(levelflat, true, false); // Maybe it's just a patch, then? else if (R_CheckIfPatch(levelflat->lumpnum)) - R_GetPatchFlat(levelflat, false, false); + flat = R_GetPatchFlat(levelflat, false, false); // Maybe it's a PNG?! else if (R_IsLumpPNG(ds_source, size)) - R_GetPatchFlat(levelflat, false, true); + flat = R_GetPatchFlat(levelflat, false, true); // It's a raw flat. else + { R_CheckFlatLength(size); + flat = ds_source; + } + + Z_ChangeTag(ds_source, PU_CACHE); + ds_source = flat; + + if (ds_source == NULL) + return; // Check if the flat has dimensions that are powers-of-two numbers. if (R_CheckPowersOfTwo()) @@ -1202,8 +1217,6 @@ using the palette colors. } } #endif - - Z_ChangeTag(ds_source, PU_CACHE); } void R_PlaneBounds(visplane_t *plane) From 3ffb7b619241c950f4b1fab9f250d4f31a9a763a Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 16 Jul 2019 19:18:13 -0400 Subject: [PATCH 032/196] New icons Also update IMG_xpm.c --- src/sdl/IMG_xpm.c | 64 +++++++---- src/sdl/SDL_icon.xpm | 240 ++++++++++++++---------------------------- src/sdl/Srb2SDL.ico | Bin 372798 -> 63946 bytes src/win32/Srb2win.ico | Bin 372798 -> 63946 bytes 4 files changed, 122 insertions(+), 182 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index af76ec1dd..59cca934d 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -1,6 +1,6 @@ /* SDL_image: An example image loading library for use with SDL - Copyright (C) 1997-2018 Sam Lantinga + Copyright (C) 1997-2019 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -48,7 +48,7 @@ // SDLCALL terms removed from original SDL_image declarations int IMG_isXPM(SDL_RWops *src); SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src); -SDL_Surface *IMG_ReadXPMFromArray(const char **xpm); +SDL_Surface *IMG_ReadXPMFromArray(char **xpm); #define IMG_SetError SDL_SetError #define IMG_GetError SDL_GetError #endif @@ -79,7 +79,7 @@ int IMG_isXPM(SDL_RWops *src) #define STARTING_HASH_SIZE 256 struct hash_entry { - const char *key; + char *key; Uint32 color; struct hash_entry *next; }; @@ -110,7 +110,7 @@ static struct color_hash *create_colorhash(int maxnum) /* we know how many entries we need, so we can allocate everything here */ - hash = (struct color_hash *)SDL_malloc(sizeof *hash); + hash = (struct color_hash *)SDL_calloc(1, sizeof(*hash)); if (!hash) return NULL; @@ -119,15 +119,29 @@ static struct color_hash *create_colorhash(int maxnum) ; hash->size = s; hash->maxnum = maxnum; + bytes = hash->size * sizeof(struct hash_entry **); - hash->entries = NULL; /* in case malloc fails */ - hash->table = (struct hash_entry **)SDL_malloc(bytes); + /* Check for overflow */ + if ((bytes / sizeof(struct hash_entry **)) != hash->size) { + IMG_SetError("memory allocation overflow"); + SDL_free(hash); + return NULL; + } + hash->table = (struct hash_entry **)SDL_calloc(1, bytes); if (!hash->table) { SDL_free(hash); return NULL; } - SDL_memset(hash->table, 0, bytes); - hash->entries = (struct hash_entry *)SDL_malloc(maxnum * sizeof(struct hash_entry)); + + bytes = maxnum * sizeof(struct hash_entry); + /* Check for overflow */ + if ((bytes / sizeof(struct hash_entry)) != maxnum) { + IMG_SetError("memory allocation overflow"); + SDL_free(hash->table); + SDL_free(hash); + return NULL; + } + hash->entries = (struct hash_entry *)SDL_calloc(1, bytes); if (!hash->entries) { SDL_free(hash->table); SDL_free(hash); @@ -150,7 +164,7 @@ static int add_colorhash(struct color_hash *hash, } /* fast lookup that works if cpp == 1 */ -#define QUICK_COLORHASH(hash, key) ((hash)->table[*(const Uint8 *)(key)]->color) +#define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color) static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp) { @@ -174,14 +188,16 @@ static void free_colorhash(struct color_hash *hash) } } +#define EXTENDED_XPM_COLORS + /* * convert colour spec to RGB (in 0xrrggbb format). * return 1 if successful. */ -static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) +static int color_to_rgb(char *spec, int speclen, Uint32 *rgb) { /* poor man's rgb.txt */ - static struct { const char *name; Uint32 rgb; } known[] = { + static struct { char *name; Uint32 rgb; } known[] = { { "none", 0xFFFFFFFF }, { "black", 0x000000 }, { "white", 0xFFFFFF }, @@ -895,7 +911,7 @@ static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) *rgb = (Uint32)SDL_strtol(buf, NULL, 16); return 1; } else { - size_t i; + int i; for (i = 0; i < SDL_arraysize(known); i++) { if (SDL_strncasecmp(known[i].name, spec, speclen) == 0) { *rgb = known[i].rgb; @@ -912,14 +928,14 @@ static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) static char *linebuf; static int buflen; -static const char *error; +static char *error; /* * Read next line from the source. * If len > 0, it's assumed to be at least len chars (for efficiency). * Return NULL and set error upon EOF or parse error. */ -static const char *get_next_line(const char ***lines, SDL_RWops *src, int len) +static char *get_next_line(char ***lines, SDL_RWops *src, int len) { char *linebufnew; @@ -991,7 +1007,7 @@ do { \ } while (0) /* read XPM from either array or RWops */ -static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) +static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src) { Sint64 start = 0; SDL_Surface *image = NULL; @@ -1003,8 +1019,8 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) struct color_hash *colors = NULL; SDL_Color *im_colors = NULL; char *keystrings = NULL, *nextkey; - const char *line; - const char ***xpmlines = NULL; + char *line; + char ***xpmlines = NULL; int pixels_len; error = NULL; @@ -1035,6 +1051,11 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) goto done; } + /* Check for allocation overflow */ + if ((size_t)(ncolors * cpp)/cpp != ncolors) { + error = "Invalid color specification"; + goto done; + } keystrings = (char *)SDL_malloc(ncolors * cpp); if (!keystrings) { error = "Out of memory"; @@ -1066,7 +1087,7 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) goto done; } for (index = 0; index < ncolors; ++index ) { - const char *p; + char *p; line = get_next_line(xpmlines, src, 0); if (!line) goto done; @@ -1076,7 +1097,7 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) /* parse a colour definition */ for (;;) { char nametype; - const char *colname; + char *colname; Uint32 rgb, pixel; SKIPSPACE(p); @@ -1102,8 +1123,9 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) c->g = (Uint8)(rgb >> 8); c->b = (Uint8)(rgb); pixel = index; - } else + } else { pixel = rgb; + } add_colorhash(colors, nextkey, cpp, pixel); nextkey += cpp; if (rgb == 0xffffffff) @@ -1168,7 +1190,7 @@ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) return load_xpm(NULL, src); } -SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) +SDL_Surface *IMG_ReadXPMFromArray(char **xpm) { if (!xpm) { IMG_SetError("array is NULL"); diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm index 1d0f9d314..ccd39f12c 100644 --- a/src/sdl/SDL_icon.xpm +++ b/src/sdl/SDL_icon.xpm @@ -1,163 +1,81 @@ /* XPM */ -const char * SDL_icon_xpm[] = { -"96 96 64 1", +static char * SDL_icon_xpm[] = { +"32 32 46 1", " c None", -". c #040656", -"+ c #0100B2", -"@ c #04056E", -"# c #0000BD", -"$ c #0B0C09", -"% c #0B0D26", -"& c #090C42", -"* c #060AA7", -"= c #1604DA", -"- c #020CD5", -"; c #100F8D", -"> c #040DE4", -", c #11129B", -"' c #1D1A83", -") c #2A10FD", -"! c #1318FA", -"~ c #25225B", -"{ c #252271", -"] c #312E2B", -"^ c #33334D", -"/ c #363775", -"( c #3D3B69", -"_ c #3A3B8B", -": c #373AFF", -"< c #4142AA", -"[ c #4B4864", -"} c #4D4B4A", -"| c #60492F", -"1 c #4F4C57", -"2 c #4A4A9E", -"3 c #4F4E85", -"4 c #474ADE", -"5 c #4E4FFE", -"6 c #5D5CB3", -"7 c #686663", -"8 c #666682", -"9 c #676875", -"0 c #66659E", -"a c #8B6538", -"b c #6465D5", -"c c #7F694F", -"d c #6767FF", -"e c #7272FF", -"f c #91795C", -"g c #7677FD", -"h c #828396", -"i c #A78153", -"j c #888989", -"k c #8D897E", -"l c #9190FD", -"m c #CA9048", -"n c #C09968", -"o c #A9A8A1", -"p c #A6A8B0", -"q c #B0B1FB", -"r c #EEAC61", -"s c #E3B478", -"t c #C3C4BE", -"u c #FFC68C", -"v c #FCCD90", -"w c #D4D7D3", -"x c #E3E5E0", -"y c #FCFFFB", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ttj7777777joot ", -" 9hh8830000088hh9 ", -" 9888(//__-*{^kt ", -" &,5b60^ (02*{} ", -" tp,-)!5egb3} ~_<4dgggeeeeeeeeeeeeeegb6/_2[amusf'#!<_'>))))))))))))!)>+{~ ", -" p;-))!5gb2^^'#5eggeeeeeeeeeeeeeeegg6/_23amrusi{#!+;;>))))))))))))))!!-'8p ", -" tp'#!)):d6(@*>5egeeeeeeeeeeeeeeeegg6_/<(amrrvvn{+)-,;>))))))!!!!!!)))!!>,~j ", -" p;#!))-'{'+-5eggeeeeeeeeeeeeeeeegb222(cmrruvvn{+)>,@>!)!!)!!>>>>======>-,/8 ", -" ;#)!-*.;-!5eggeeeeeeeeeeeeeeeegb2_<6|mrrsvvvn{+)!,.-!!!!>>=--######+++-#@(k ", -" h@-)+@.*>!5egeeeeeeeeeeeeeeeeeegb_>--###++++++###+;@{(9j ", -" kh,#+@@,>!:dggeeeeeeeeeeeeeeeeeeebbb_]mrruuvvsf'#)!*.+-###+++++++##+*;'3(&^9 ", -" 8*,@@*)):dggeeeeeeeeeeeeeeeeeeeeggg<(|iruvvvsc,=!!*.;*++++++++###+,@&1o ", -" 8@@@-!)!5eeeeeeeeeeeeeeeeeeeeeeeeeggb2[csvvvn^#)!!+@;*#+++++###*@~[ ", -" 9&@*!)):5geeeeeeeeeeeeeeeeeeeeeeeeegge637nsvf{>))!+;;*-######*;{.^ ", -" 9%;!!)):dgeeeeeeeeeeeeeeeeeeeeeeeeeeeggb_1ir7;>))!+;;,++++++*'(} ", -" 9{+!))!5egeeeeeeeeeeeeeeeeeddddeeeeeeeege2}|~#!))!#;@...@@@.^hp ", -" 8,=!))):dggeeeeeeeeeeeeeeeeggggeeeeeeeeggb_~,>!))!+@@@;;;;@&^o ", -" }(-)))))!:eegeeeeeeeeeeeeeegllllgeeeeeeeegd5+=))))!+;,#>--#,'/hj ", -" o8.>))))))!:dgggeeeeeeeeeeellqqqqlgeeeeggg5:!!!)))))-*+>)!:55db631 ", -" p8<*!)))))))!:5deggggggeeeegqqqqqqqqlggged5:!))))))))>->!!:5ddeegb3/ ", -" oh'#!))))))))))!:ddeeeeeeeeglqqqqqqqqlgedd:!)))))))))))))!:dggggeggg239 ", -" ^*>!))!)))))))))!::55dddeegglll600333_4:!!)))))))))))))):dggeeeeeeggb6(9o ", -" ~+=-+#>))))))))))!!!:::::5554<3889988[/,=)))))))))))))):5gggeeeeeeeggb6087 ", -" ~**@~'+>!))))))))))))))))!!>*{1kkooook7(,-!)))))))))))!:5deeeeeeeeeeeggb289 ", -" ~,'1o7(*>!))))))))))))))))=,[jtttwxxxwto^;>!))))))))))!!!::5deggeeeeeeegbb3] ", -" ~@/oxt7'#))))))))))))))))=,3ktwxxyyyyyyxk/+!))))))))))))))!:::5degggeeegggb3^ ", -" ^&8xyyt^,)))))))))))))))>,3otwxyyyyyyyyyxh'>)))))))))))))))))):5ddeeeeeeeggb3^ ", -" 771pyyyx7'=!)))))))))))!!#(jtxxyyyyyyyyyyyt3-)))))))))))))))))))!!::degggeeegb2[o ", -" 77tyyyxk/+!!)))))))))))-;9owxyyyyyyyyyyyywh*>)))))))))))))))))))))!::5ddgggggb68j ", -" owyyyyt8;>))))))))))))*(otwyyyyyyyyyyyyyxp'-)))))))))))))))))))))))!!:5deeeggg_8j ", -" jtxyyyyxh'>)))))))))!!#_ktxyyyyyyyyyyyyyyyt_+))))))))))))))))))))))))))!!:5deggg63j ", -" 7jwyyyyyyp/=))))))))))>,3owxyyyyyyyyyyyyyyyw/+))))))))))))))))))))))))))))!::5degb689 ", -" 7xyyyyyyo[#))))))))))-/jtwyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))!:5dgg_/ ", -" }xyyyyyyt9*=))))))))=*9owyyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))))!!:5d3} ", -" }xyyyyyywj'#!))))))!#@7oxyyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))))))!!:4/7 ", -" 7xyyyyyyxj&,!!))))!!,%}oyyyyyyyyyyyyyyyyyyw/*))))))))))))))))))))))))))))))))))))))>487 ", -" 7xyyyyyywk$@!!)))!!-.$]oyyyyyyyyyyyyyyyyyyw/+))))))))))))))))))))))))))))))!!!!))))!>' ", -" }xyyyyyywj$&+!!)!)>;%$]jyyyyyyyyyyyyyyyyyyt{#)))))))))))))))))))))!!!!!!))!)!!!!!!))!#' ", -" 7xyyyyyyt7$%@-!)!>*[]$$jyyyyyyyyyyyyyyyyyxp;-))))))))))))))))))!!!!!!!!!!!!>>>>>>>>>>!,^ ", -" 7xyyyyyyt}$][;-)=,(o7$$7yyyyyyyyyyyyyyyyyxp,-)))))))))))!!!!)!!!!>>>>=-----########--=+'9 ", -" jwyyyyyyo}$}o(';@~7wj$$7yyyyyyyyyyyyyyyyywh*>)))))))))))!>>>=>=---#####+########+++***;@17 ", -" otxyyyyyt}$7t7}1}7kw7$$7yyyyyyyyyyyyyyyyyt0-)))))))))!!!>--####+++++++++++++##+***,;''.&] ", -" ooowyyyyyt}$}j7owwojo}$$jyyyyyyyyyyyyyyyyyp2>)))))))!!!=##++++++++++++++###+*;@.~[8[9hph ", -" ojtyyyyywj$$}jwyyxo}$$]jyyyyyyyyyyyyyyyyyp'>))))))!>>-#++++++++++++####+,;'_3/&^}77kot ", -" 7tyyyyyxo]$$oxyyyt]$$}tyyyyyyyyyyyyyyyyx0*!)))!!!>-#++++++++++++#+##+*;.&1ko ", -" 7tyyyyyyx7]}xyyyyxj}]oxyyyyyyyyyyyyyyyyp<=)!!!!>-#++++++++++++####*;.(8h ", -" owxyxxyytooywptwwtppxyyyyyyyyyyyyyyyxp3,-=!)!>-#++++++++++###+*,'_{&1k ", -" jtwwttwtwwtj7kjowxyyyyyyyyyyyyyyyyxt7~'',+>=#+++++++++++###*;@&^j ", -" ]joojj7}]}]|innfc7jtwyyyyyyyyyyyxtjcfnnnf[@*#+++++++++###+@.&%% ", -" ]$}77}}$$$$]fsssnnifkkotwwwwwwwtpjkfinvvvsi}@*#++++++###*;@.@@&[ ", -" o7$]]]]]$$]|isvvvvvusifckopppopok7cisvvvvvvvn(,#++++++#+*@.&@*#;3o ", -" }}$]|||fnnsvvvuvvvuuvvsniffffffnnsvvvvuuuvvvc{*+#++##*@&.@*+#--<7 ", -" }]cninsuvvvvuuuuuuvvvvusnnnnnssuvvvvvuuuuvvc~*+#+++*@.@;*##=>>,^ ", -" 7fvvvvvvuuuuuuuuuuuuvvvvvvvvvvvuuuuuuuuuvvc~*+#+#+,.@*###->!!*~ ", -" pkivvvvuuuuuuuuuuuuuuuvvvvvvvvuuuvsnsuuuvvf~*+#++++*+++->!!)!#. ", -" kfsuvvuuuuuuuuuuuuuuuuuuuuuuuuuvvnfsuvuvvc{++#++++###->!!))!-;h ", -" kisvvvuuuuuuuuuuuuuuuuuuuuuuuvvvicsvvvvs1@##+++++++#>!!))))=,ho ", -" 7imuvvvuuuuuuuuuuuuuuuuuuuuvusfcivvuvvn~;##+++++++#>!!))))!#8k ", -" cimruuuuuvuuuuuuuuuuuuuuuuvsnfisuvvvsc@*#+++++++++#>!!))))-3} ", -" 7amrruuuuuuuuuuuuuuuuuuuuvsnnsvvuvvi^,##++++++++++#>!!)))>/^ ", -" kfamrruuuuvvvuuuuuuuuuuuuuvvvvvvvn1@+#++++++++++++#>!)))>{~ ", -" 7|iimrrruuuuuuuuuuuuuuuuvvvvuusn1'+#########++++++->!))>; ", -" 7cammrrrrruuuuuuvvvvvuuuuurrm|.*-#+#######+###+++->!!!*' ", -" ookcaimmrrrrrruuuuurrrrrmi|]%.@@@@@;,*,*+########->!!*6o ", -" p7}|ainiimmmmmmmmmmminnia|$%.....{3322_{''',,**+#=!!#6k ", -" j7||aaiiiiiaa||7j ookok711^&.';,*+=!>> c #1E1AED", +", c #1B1CFE", +"' c #272576", +") c #313179", +"! c #3A3AB1", +"~ c #3A3AFF", +"{ c #434497", +"] c #4F4AAE", +"^ c #4748FE", +"/ c #494DE8", +"( c #7A5137", +"_ c #5E57A7", +": c #5957C4", +"< c #616360", +"[ c #855E93", +"} c #796979", +"| c #9C6473", +"1 c #6D6EF4", +"2 c #7171FF", +"3 c #817F9D", +"4 c #B37C5A", +"5 c #898A8D", +"6 c #A28572", +"7 c #A3829B", +"8 c #8988FF", +"9 c #C39D82", +"0 c #ABADAB", +"a c #DCA57C", +"b c #ABAAFE", +"c c #C1C3C0", +"d c #F7C095", +"e c #CFD1CE", +"f c #DDE0DD", +"g c #FAFCF9", +" ", +" {]]]]]]) ", +" {:1222222221::) ", +" :1222222222^^>,,>% ", +" ' ]122222222:]>,,;=%$& ", +" @~] :222222221://-=%%%#. ", +" #>^{122222222:1][~&%%%@ ", +" #>=122221221:/|a4,&%$& ", +" #-!22228bb12/4ad9,&$+ ", +" ##^2221bbb2~,!ad7,@'{ ", +" )!,/22281^,,,,:d_,-221:) ", +" /,;,,>,,-_!;,,,[=>=~2222: ", +" {=,,,,;3fgg0;,,,,;,,,~^221{ ", +" 3_,,,>5ggggg3,,,,,,,,,,,>^2] ", +" 05,,,_fgg0egc>,,,,,,,,,,,,,~! ", +" 00>,>0ggf00gf;,,,,,,,,,,,,,,,- ", +" 5e%,!fggf55gf=,,,,,,,,,,>>>>,, ", +" ==-%%%%%%$$#", +" f*@cgggg*5g0>,>-%%%%%%$@@+ ", +"5e< ", +" 4ddddda9a6$#%%%%=,,- ", +" 4addddda*#%%%%%%>,; ", +" (444 +@$%%%-,, ", +" &$%%=,$ ", +" &$%>- ", +" +#-= ", +" @- ", +" & ", +" "}; diff --git a/src/sdl/Srb2SDL.ico b/src/sdl/Srb2SDL.ico index 700276fd4b9ac2810a6981eb054921f3708c702b..3b37433dbd0aeb1315eaae48e5a2831de7e305e7 100644 GIT binary patch literal 63946 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 literal 372798 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 G1OE?SzcGja diff --git a/src/win32/Srb2win.ico b/src/win32/Srb2win.ico index 700276fd4b9ac2810a6981eb054921f3708c702b..3b37433dbd0aeb1315eaae48e5a2831de7e305e7 100644 GIT binary patch literal 63946 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 literal 372798 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 G1OE?SzcGja From 181ecee202dca4d8a24ec27f38e4b30e61c7f1f5 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 16 Jul 2019 19:26:16 -0400 Subject: [PATCH 033/196] Let's not use this --- src/sdl/IMG_xpm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index 59cca934d..51cedfd6d 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -188,8 +188,6 @@ static void free_colorhash(struct color_hash *hash) } } -#define EXTENDED_XPM_COLORS - /* * convert colour spec to RGB (in 0xrrggbb format). * return 1 if successful. From 64bb70bef4d2f02c3bc12533b8364d2f89a8d9d1 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 14 Jul 2019 14:55:04 -0400 Subject: [PATCH 034/196] Fix crash with GME sounds when being freed --- src/sdl/mixer_sound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 004c60605..6d485179a 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -390,7 +390,7 @@ void *I_GetSfx(sfxinfo_t *sfx) gme_track_info(emu, &info, 0); len = (info->play_length * 441 / 10) << 2; - mem = malloc(len); + mem = Z_Malloc(len, PU_SOUND, 0); gme_play(emu, len >> 1, mem); gme_free_info(info); gme_delete(emu); @@ -463,7 +463,7 @@ void *I_GetSfx(sfxinfo_t *sfx) gme_track_info(emu, &info, 0); len = (info->play_length * 441 / 10) << 2; - mem = malloc(len); + mem = Z_Malloc(len, PU_SOUND, 0); gme_play(emu, len >> 1, mem); gme_free_info(info); gme_delete(emu); From 9813cbc8577991e232655468608ce73fe86b91e2 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sat, 27 Jul 2019 01:13:35 -0400 Subject: [PATCH 035/196] More icon stuff --- srb2.png | Bin 6208 -> 25245 bytes src/sdl/SDL_icon.xpm | 174 ++++++++++++++++++++++++------------------- src/sdl/i_video.c | 2 +- src/sdl/srb2icon.png | Bin 11668 -> 0 bytes src/win32/Srb2win.rc | 13 ++-- 5 files changed, 105 insertions(+), 84 deletions(-) delete mode 100644 src/sdl/srb2icon.png diff --git a/srb2.png b/srb2.png index 72a08f6648b8c8849d5804889977a412b35e6deb..3bbe2c3e66af543be6de806d8d893735e10e4062 100644 GIT binary patch literal 25245 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 AS^xk5 literal 6208 zcmdT|`9IX_+aC#~Go)iVhD_xUA&Q|emTWo5(jt^CHIs3&B->!fRwNZ!V?sI8v5X~4 zS;jVH%h+Nj%%E(cnJ}9f=K1LR{pI-&p67>o&F8+a_kF$J_kCY8*L8g!I$f{gXM#X- z)wY)Amm)mZrl+M=o6?hgQf8~88{^MB0sAPE`&-$s>w?tkUR@~NU+i^n1^bFq{JjP- zbjV67ZuJCqoxg@B>>PS(?&D(lADICpH0YHr1G^>x?!)ibF&Z+ zy4>xia^4DOj-;GHqPI7&n6-1LH5r=2k9gbLu8J?bkU?}yw7W)I^9#w$k;iAGrNGw% zms(pQR6~^P|GFLwItD2*8ZJ8hK1|!)v?xLtL5r7e@zliVPd)ypII3bTh4FR++Ii#;4uRBYEdAJF+rrA#?TA-ZN4!! ziM_IJ!#JEgEDym(1h0AVTPa@<4Fd#HBW+Hs9kZ9u9gQU4I|ZdT7p(PnM`i5I)T;G$ zHg^bk+cVxXw`IqN1NeFX??RPqgYE=E<04Lm6_SSGFf!G?f~N)u(vX%JxB+a8@$u#lJ4IE0$77(h1Ha&Am)d(EcOF(U=Wehjvtb^KG}N*IWG7%n?8wH- z0-+4$K1Wke+h$+N4R|V(oN5!D@ViGw!1eNbJJZ>*r5&U>2AvCvZmv>^CAh729Pb1a zs9kDOsi7!)j849{Ug0-uZCfVbd715Vn9 zCw4@GEij+(3-*rkqc>%dkV{~2wBO2*d=v^_uEHKQoL}_~Wc>zs6(R2ksj+wd7ZwiT z^o*C>-Q?e0W@|)C(B1l)yLVP)R6Q<92pmu07_X+-twLYhH+!fXfLewU*rg`7bNn1@;xd9HTuBS{6EGlSjq&FstF1+DWhGr0Y-)I46S1ML#>tdPD zH-dw3jU}FUz^WdZbC+}JBCi(GkmrjAvCh+yX4bHl0vhQziFK(ll2*qqx%k^_=lzY( z37_7Bxk4G9B`rngWbu5#^h+$I8Aef3)O(ps)Iyo@#02By zeBoim@p31xMo?y@?avcm5aw?v*+XWJ`UW$S`33OBB1+6gM@v5ZuBh0i#MorLPc2N{ zlUN{^4f%UNxMQjBUD=IfpA0@T^aCvmy&dQIdUdp;xJ8&5s`2X_j}4~Tr8gvsnw51G z7IzqgfeY!{^YX!0NtdWy01mTzuAxZdnnx#ITe)nc$H4J0{dXd6*d zXEe8Jc&ni-p=%q=oI0;VNtLG0xd^XXfxQ&KTe|SjRZpQ zUSr9|n=&9+O-d~pu`Z^H`YBQ> z`VImceU5y>E@5Jdb#Y0o@GSa2nyiA|{G@e7&Hss!v+nhHQSQDd(c1DFuLbR-ZS=|r_YDN0FbQo0{<5|3UMT!?_nOjjTy}*G=zuN=*?1s$66yMv| zYNvrDxN5|dWtb)RLjH38!UrAnuP)Epa7$rLLZV2`-5aUfXT{m7 zls*o%xtr0W8zZjN9Jh%B)*bB=o7oSIuk1TJu)X3XaMz;Rk={H^u2IEYsnNztWWutJ z4VZ$g)LpF8KN|^R^~fGv=IN+gV7kAaW*f{7tf^q9oW482m9T-Yt9v9_DfhWkAq1{B(4Bt#n;c z%${AEEuL6=$;UGN1}}kx-DA;8%4FnCy_exN8+|SVTMgm6MCCG|n^s3u}q(Q1zJqNNI3I0Wwlf$-Ak3YkN{- zmnnRj;XxiE!^bqWs<$bzy@kVDk(E{nQ7T4_IX?uB)_|8Y|d;x1Fun5r##>9?DMXb#Z;rZB%e;(r8{S?_0vC>%TyLk$RmSDehBu+nhhwko?|h6 zC^mOB);|>zU>cj@xrSV>s50w9%a&J;PN#q}VHBS``%Rs~Okoz_1}MP>ANDrP)3sR0 zB%u&H`Wf&5qnO_TE2uSN^Jnff$eA)c4-Ah)PCrcM?lbzBbH`*)ZUs>y$dS`gdk@D5 z??W?qsqPQ14E`$%S!i9=^m@jB>E0x^Fuh*3o>Z=)H|9Z!Y}4D#P+DO~beE)?XNq{V7+R75#n)GFk2F6@ z6-6H~8qK){q^AlM==f8H#jO-aV(T;b1f7UF~H?{1S5m!5f(}Ej^eCBOg5sR?zdvNR5y|mq8=&pK-xgT1$Prd>0G! z*perReqg-tUzkYE0CE8Q1oiT2UhI-Xzw_6MqWhvIwHq%0t*Dk|cz{Z{{19}5J^rDpIJY26~wJ;R7}TiVoQ4_(qM z+s^5LD(d{H!-MiZ2>>L1(2w-ff?lr_UO4(d)&%7CJ5sc|It}i4Ri2F{P4m{5hV=u-Fk|=pTsW%G+&0@yAprmBfq0Eb2T8Z|qS7PWs;yA?n6k@?sJgh;GpgJpal5=fnk zC7zuuJGv$}dKG&)vffQF#R38f(3)ea5zyKruom%}qc>J7E~^9(HU}NMyCzcIB&XMR zgHC;fQ22YKmhbG{F|`j~a$N#h=Aj0!C^nVh#9WxR*a9g&Y?1vbh2aI@xb#zVbWiQ4 zYzVFSp79ySQ!AzYzoL@3IR}W5A-+VF@Ws&UgpUsyc#zPBr(^=EXba>_tCo(bW%yw;7uX2Lc^7BT_O74Xwl8s z&4UfDkiPPtI>w(7n2JPboMsOFH{h4>UruhdA~Tg*t1=R>h?`7+s(b6 z^@oevk3aJ@e5Bhso*I4iu_(3f>U+RlBv$X0>rC;o{KU4H;zDc4{)|pP2jEr>l>;h! z1Pv-nfGz8zu;Q4;yo*wNZ@!CtN~AT}offcv*A1YGT*o-VoU5w>rL13g zmu2H!TP8g^TO`#mZs3tH{vhf>izDQVF2js@AGLMg;;P5VMbF#{En?`5P1m?v?Qvuw zj#(Wu!cgiE{+vL9w9T~FtLB7i+mC1ao@fWo=qcQMB?6wf_B=U4Qt4l%=yAG;Smot+ zuh2fV4bm5n4vKq_j1IjmT0XjNa{CE;ro&%;LaSr;yZOoGUo@ApfT-Gd{6Pjql>)Rb zXhk%+v)u4x`{Q;a)j{jE-ZzOF;LFMY8_ zdNHx^IGE{f>{powj!F~`Z@l*Sig&GydRgbR46DTm8)-u}8g{~WB#h^~G=owY7vAj! zDqRhimIIMDIg8PwH-^xf%Yhs&{E%XI^S2vx5mk?8opLN`S2jDmqRc{_sk2|KAUNv z4FQUkAAW&D?o!}xZ7Ax}CX%F z4->hR`ZMk5Z#QBMe-VFI!7Y;#NyZ1?dRmiA`mur|uSuV~des>>9 za1j{yFjCuQNVm$V$krmfjLZ8Qv%oV^{O*Ml0EOEwW;!YVz5bU)peDQCXM$ur&L=Du zE%LcoO$2J@iOtyE(hfg67f$DnC0O%g*2cWhRG_C(+kz2K_rR=joNyqHWS!J2`#M4{ zH~&)A#lfPKAgqhy&bE;$$HN3P<^go@X~31(JDiwMN07Rj9Nv5_g572~Tvady$ulQ_ zmi=F8`ksrWeXzcF8%|ZO-y0Ed$W~8fMpqz?V4c_t5EU_xXnk0QT1N9mIw)6uRa%}IeDe;%dE8fc~TEnF>o0EPYBf*=lXKi zv-E!%H!(Yl1l6%7m*kvSkMb7=k}DKs^~4Eu;S59Jd*!fqt(bb*u}LvAX#7p?3pGM^ zAq)&4aE&K;^Bo3buE3#ayVxg<+srgaTW5K#kj`$j7d=dllw3*M3rKKG7tMs*o|5r^ z3f)??AXNIWM**{FB)%4*6rB;}|7Y&1L+YEEt$hQOcYd2Z$W5@)RNc@K`aI33190!C z+~JquVlOgb^RimEm^NMuLn29*3DK4m1Ie~xPsn~%bk^MUP=VARObc&>Iid3MU{(q% zK(7b=ymezn;rr{S-nFjk)FdcVd4?1hODm}$3Zq3J3-GD4Gl01iFeB8d#0+Tsam+qG z8WH+Q_NJE7n8vVK;QZhoF+c5~Z zfFU&Bln;QAt#L!>xbh6bEv}I4L+ChUSD?M ze8>fFx$D`(f6`qB@2B8p^u7_A40ri6rMyG-;yZbwd0?Wfp0`#>D~S{*hF1Yv+$J{f zkEo0&zrDGNNhlCa?Ti{~c;@pt-o^s`lkmhb$fnAV!w6wVoK2BLrgAWNrr_o|i#i$b z!sR9Iw~gzOL+;jH7pBmI$%RfBp-yj_ZpA^6&&U)R1AwyJ5o1CPpHgZOp-Bt&* ziE@r9z0Sns^U=bs!$I|rsI$hqXIRYcjJ*+qz#ZSENOPFwSmzfPm#4Gb!vEQ)OgjnnmZ$tEU0o#dE@#bWkOG2j09?K2-BAP_%1_23rAy6x<1T0vII z|I5tuacOwy@Mq)CuI+WMR1jel0>yoBOtPhD>t{}NN^_6K` z;`*HADy|tR_JsXkF*WLqFPTMZZEU$MkY@X<^Y}R(y7`KUjH*@95eb(2+wqG&F5hhD z;sobV#=Dbh5?hDfv)|~bulohhD8F9>o=)FC12~L1VW0ZQ5(<)v#OzyY{HOEPgGWM{ v$4-mdr}q4zqkim0`~QszWzI^VKr%GK{8)yZVmk1GA7pEF!LsU{_x=9?mlJ*= diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm index ccd39f12c..c5c957468 100644 --- a/src/sdl/SDL_icon.xpm +++ b/src/sdl/SDL_icon.xpm @@ -1,81 +1,99 @@ /* XPM */ static char * SDL_icon_xpm[] = { -"32 32 46 1", +"64 64 32 1", " c None", -". c #000033", -"+ c #000045", -"@ c #00006C", -"# c #01007E", -"$ c #000193", -"% c #00009F", -"& c #060766", -"* c #0F0F10", -"= c #0C11CC", -"- c #0D13B8", -"; c #1415E0", -"> c #1E1AED", -", c #1B1CFE", -"' c #272576", -") c #313179", -"! c #3A3AB1", -"~ c #3A3AFF", -"{ c #434497", -"] c #4F4AAE", -"^ c #4748FE", -"/ c #494DE8", -"( c #7A5137", -"_ c #5E57A7", -": c #5957C4", -"< c #616360", -"[ c #855E93", -"} c #796979", -"| c #9C6473", -"1 c #6D6EF4", -"2 c #7171FF", -"3 c #817F9D", -"4 c #B37C5A", -"5 c #898A8D", -"6 c #A28572", -"7 c #A3829B", -"8 c #8988FF", -"9 c #C39D82", -"0 c #ABADAB", -"a c #DCA57C", -"b c #ABAAFE", -"c c #C1C3C0", -"d c #F7C095", -"e c #CFD1CE", -"f c #DDE0DD", -"g c #FAFCF9", -" ", -" {]]]]]]) ", -" {:1222222221::) ", -" :1222222222^^>,,>% ", -" ' ]122222222:]>,,;=%$& ", -" @~] :222222221://-=%%%#. ", -" #>^{122222222:1][~&%%%@ ", -" #>=122221221:/|a4,&%$& ", -" #-!22228bb12/4ad9,&$+ ", -" ##^2221bbb2~,!ad7,@'{ ", -" )!,/22281^,,,,:d_,-221:) ", -" /,;,,>,,-_!;,,,[=>=~2222: ", -" {=,,,,;3fgg0;,,,,;,,,~^221{ ", -" 3_,,,>5ggggg3,,,,,,,,,,,>^2] ", -" 05,,,_fgg0egc>,,,,,,,,,,,,,~! ", -" 00>,>0ggf00gf;,,,,,,,,,,,,,,,- ", -" 5e%,!fggf55gf=,,,,,,,,,,>>>>,, ", -" ==-%%%%%%$$#", -" f*@cgggg*5g0>,>-%%%%%%$@@+ ", -"5e< ", -" 4ddddda9a6$#%%%%=,,- ", -" 4addddda*#%%%%%%>,; ", -" (444 +@$%%%-,, ", -" &$%%=,$ ", -" &$%>- ", -" +#-= ", -" @- ", -" & ", -" "}; +". c #000271", +"+ c #03035D", +"@ c #00009F", +"# c #0A0A1B", +"$ c #08058E", +"% c #060E4C", +"& c #11110E", +"* c #101339", +"= c #0D11CC", +"- c #1B1CFD", +"; c #342B24", +"> c #2325EC", +", c #3C3883", +"' c #3D3A9E", +") c #5B5170", +"! c #4B4CFF", +"~ c #795339", +"{ c #5E5B5C", +"] c #5F5ED3", +"^ c #5E5EFB", +"/ c #7271FF", +"( c #B37F5D", +"_ c #8F8883", +": c #8887FF", +"< c #D59E76", +"[ c #ABABA9", +"} c #A9AAFF", +"| c #C1C3C1", +"1 c #FAC296", +"2 c #D4D6D3", +"3 c #F9FCF8", +" ", +" ", +" #***,,,**** ", +" *,,]]]]]]]]]]]]]',,** ", +" *,']//////////////////]]',* ", +" *,']/////////////////////////]'% ", +" *,]////////////////////^^!!>>>>>>>$+ ", +" *,]////////////////////!>---------->=@% ", +" * ,]///////////////////]^>------->==@@@$.# ", +" +', *']//////////////////],,,=---->=@@@@@$.% ", +" #.=^'* ,]//////////////////],']^]$--=@@@@@@@$+# ", +" %$=-^'* ,]/////////////////],]/!>-^'=@@@@@@@$.% ", +" +$=->/,*,//////////////////]'/^!,$-!,$@@@@@@$+* ", +" +@=--!''/////////////////]']^!,(()->%$@@@@@$+# ", +" +@=--='/////////////////]']^${(<<)->,$@@@@$% ", +" +@=->']/////////:::////]]/^'(<111)->,$@@@$% ", +" #+@@>$]////////::}}}://///!,(<1111)--%$@@.% ", +" #+@@$$^////////:}}}}}://^>$(<<1111)--+$@.% ", +" +@$.>^///////:}}}}}}:/^>->,(<111<'--+$$*# ", +" +$.=-!///////:}}}}}:^!-----@(111<@--+$,'],,* ", +" %+%=->^///////:}}}:!--------@(11(=--$=^////],* ", +" ,]]'>->^//////^^!!-----------'<1_>--@-!//////]'* ", +" '!->@--->>>>>>--->===>--------)<,-->@->^///////]', ", +" *$--->----------='){__{'>------>'=--=@-->!^///////],* ", +" %$.=---------->$)[22332[)=----------=>----->^^//////], ", +" %$_,--------->'_|3333333['----------=--------->!^////],# ", +" *'[{=--------'_2333333333_=---------------------->!^///,* ", +" #)[_@-------@_|33333333332,------------------------->!^/'* ", +" #)2[$------=)|333332|23333{>--------------------------->^'* ", +" {2|,------$[233333___3333_=----------------------------->$ ", +" ;22)=---->)|333332{2_2333[@-------------------------------$ ", +" &22{@----$_233333|{2||333|'--------------------------------$ ", +" &|3_.----,|333333[;2|[333|'--------------------------------=+ ", +" [3_%=--={2333333[&___333|'-------------------->>>====>>----@ ", +" _3[#$=@.[2333333[&&&_333[$------------->>==@@@@@@@@@@@@@@@==+", +" {3|;+$$)|3333333[&&&[333_=-------->==@@@@@@@@@@@@@@@$$$$.+++%", +" {23{*$${23333333|;&&|332)>----->=@@@@@@@@@@@@@@$$$.++%** ", +";{{;[3{&*)[333333333{&&|332,=---==@@@@@@@@@@@@$$.++%* ", +"{22_{|[;_|2333333333_&;233_$@@@@@@@@@@@@@@@@$$+%* ", +"&_|2{;{{[233333333332_[33[,$@@@@@@@@@@@@@$$+%# ", +" &;{&&&;~(_|3333333333332)$@@@@@@@@@@@@$.+%# ", +" &&&&&;(11([33333333332{$@@@@@@@@@@@$...$@$* ", +" &~((1111<[333333332{%.$@@@@@@@@@$$$$@=--$ ", +" ~<<11111<[33333|[_(<~,$@@@@@@@@@@@@@>-->. ", +" ;(<111111<(____(11111(+@@@@@@@@@@@@=----=% ", +" ~(<11111111<11111<(<<;$@@$$@@@@@@@=-----. ", +" ~(<1111111111111(~<1{$$$.$@@@@@@@=-----= ", +" ~(<1111111<<(((<11<*$+.$@@@@@@@@@>---->+ ", +" ;(<1111111<<1111<~%+$@@@@@@@@@@@=-----$ ", +" ~(<<111111111(~&*+$$$@@@@@@@@@@=----=% ", +" ;~((<<<<(~~; *%+$$@@@@@@@@@>----+ ", +" ;;; #%+$$@@@@@@@----. ", +" *+$$@@@@@=---@ ", +" *+$@@@@@>--= ", +" *.$@@@@-->% ", +" #%.$@@=->+ ", +" *+$@@>-$ ", +" %$@=-$ ", +" %.@>@ ", +" +=@ ", +" .. ", +" * ", +" ", +" "}; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index b4f1a2a9d..ede7787f7 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -41,7 +41,7 @@ #ifdef HAVE_IMAGE #include "SDL_image.h" -#elif 1 +#elif defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) // Windows doesn't need this, as SDL will do it for us. #define LOAD_XPM //I want XPM! #include "IMG_xpm.c" //Alam: I don't want to add SDL_Image.dll/so #define HAVE_IMAGE //I have SDL_Image, sortof diff --git a/src/sdl/srb2icon.png b/src/sdl/srb2icon.png deleted file mode 100644 index cdee18a8412313410fb4f0f0a132b710c1670c54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11668 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 literal 424760 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

VubN zPF1m>AwaBU1Q4fco^TflS|)OK(AyBF-o4T4#F@*Leij92@l;y)-u|w2?xy z?VM*{v7Bv}uCrd`Hb~eVLfa{{1426@w03yLu|9Iu=nbLA@7me_#Ck&2%+}y7EAQFRG z&h1(N4%Ot~XgRlQS-=dTPUapVLvXvJxgnQ^xD2})8Kq@tUP#M>=JL{kjvRXIo3DU0ZR5t|0B06;h44 zU2_}ir-?)oX{!!zukH|qJPWbkpOGR;~;{{$~$L zhK|3#ufTx*EKak}<+=d-;pkf0-6 zmnjC!!EJGTUe7U7e1_3Zb=7xu4K|eEXRv{IL_>yZZ_8t(A?eY9OEow37>CQh-@(k7 JUG*uY{vX;3zMB95 diff --git a/src/r_data.c b/src/r_data.c index 71c909b88..2c10e0143 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2603,17 +2603,40 @@ typedef struct { png_bytep buffer; png_uint_32 bufsize; png_uint_32 current_pos; -} png_ioread; +} png_io_t; static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) { - png_ioread *f = png_get_io_ptr(png_ptr); + png_io_t *f = png_get_io_ptr(png_ptr); if (length > (f->bufsize - f->current_pos)) png_error(png_ptr, "PNG_IOReader: buffer overrun"); memcpy(data, f->buffer + f->current_pos, length); f->current_pos += length; } +typedef struct +{ + char name[4]; + void *data; + size_t size; +} png_chunk_t; + +static png_byte *chunkname = NULL; +static png_chunk_t chunk; + +static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk) +{ + if (!memcmp(chonk->name, chunkname, 4)) + { + memcpy(chunk.name, chonk->name, 4); + chunk.size = chonk->size; + chunk.data = Z_Malloc(chunk.size, PU_STATIC, NULL); + memcpy(chunk.data, chonk->data, chunk.size); + return 1; + } + return 0; +} + static void PNG_error(png_structp PNG, png_const_charp pngtext) { CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext); @@ -2638,11 +2661,13 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I #endif #endif - png_ioread png_io; + png_io_t png_io; png_bytep *row_pointers; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, - PNG_error, PNG_warn); + 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"); @@ -2677,12 +2702,18 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I png_io.current_pos = 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, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (bit_depth == 16) @@ -2715,35 +2746,31 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I // Read grAB chunk if (topoffset || leftoffset) { - UINT8 *header = png; - while (size--) - { - if (!memcmp(header, "grAb", 4)) - { - // The grAb chunk stores offsets as big-endian numbers. - #ifdef SRB2_BIG_ENDIAN - #define ENDIANESS(x) (x) - #else - #define ENDIANESS(x) ((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000) - #endif - // skip name - header += 4; - // read left offset - if (leftoffset != NULL) - *leftoffset = (INT16)ENDIANESS(*(INT32 *)header); - // read top offset - header += 4; - if (topoffset != NULL) - *topoffset = (INT16)ENDIANESS(*(INT32 *)header); - #undef ENDIANESS - break; - } - header++; - } + INT32 *offsets = (INT32 *)chunk.data; + + // The grAb chunk stores offsets as big-endian numbers. + #ifdef SRB2_BIG_ENDIAN + #define ENDIANESS(x) (x) + #else + #define ENDIANESS(x) ((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000) + #endif + + // read left offset + if (leftoffset != NULL) + *leftoffset = (INT16)ENDIANESS(*offsets); + offsets++; + + // read top offset + if (topoffset != NULL) + *topoffset = (INT16)ENDIANESS(*offsets); + + #undef ENDIANESS } // bye png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + if (chunk.data) + Z_Free(chunk.data); *w = (INT32)width; *h = (INT32)height; @@ -2920,7 +2947,7 @@ boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) #endif #endif - png_ioread png_io; + png_io_t png_io; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); From e3df9cc6b106566f3aaada857ce36f868ef5db5d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 11 Sep 2019 20:38:15 -0300 Subject: [PATCH 124/196] Crash prevention --- src/r_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_data.c b/src/r_data.c index 2c10e0143..485de3c40 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2744,7 +2744,7 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I png_read_image(png_ptr, row_pointers); // Read grAB chunk - if (topoffset || leftoffset) + if ((topoffset || leftoffset) && (chunk.data != NULL)) { INT32 *offsets = (INT32 *)chunk.data; From 9006bdd6a3576e217875e365d160645f1fd04481 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 12 Sep 2019 12:52:25 +0100 Subject: [PATCH 125/196] I_Error if no frames are found for a loaded skin's SPR2_STND subspriteset, given this is what everything will default to if nothing else is provided, and I really don't wanna go across the code adding checks for sprite2s not existing (since R_GetSkinSprite2, under all circumstances other than this one, is capable of bouncing back). --- src/r_things.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/r_things.c b/src/r_things.c index bf4b3ca40..ea8b8b73c 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2871,6 +2871,8 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski for (sprite2 = 0; sprite2 < free_spr2; sprite2++) R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[sprite2], wadnum, *lump, *lastlump); + if (skin->sprites[0].numframes == 0) + I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]); } // returns whether found appropriate property From a0ec86ce0159dd6a59921ddb7de6814b0e61ade4 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 12 Sep 2019 14:32:31 -0300 Subject: [PATCH 126/196] Fix powers-of-two checks --- src/r_plane.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 51a69336e..db5fb0f24 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -652,12 +652,18 @@ static void R_DrawSkyPlane(visplane_t *pl) boolean R_CheckPowersOfTwo(void) { - if (ds_flatwidth & (ds_flatwidth - 1)) - ds_powersoftwo = false; - else if (ds_flatheight & (ds_flatheight - 1)) - ds_powersoftwo = false; - else if (ds_flatwidth == ds_flatheight) + 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; } @@ -806,6 +812,7 @@ void R_DrawSinglePlane(visplane_t *pl) size_t size; ffloor_t *rover; levelflat_t *levelflat; + boolean rawflat = false; if (!(pl->minx <= pl->maxx)) return; @@ -968,6 +975,7 @@ void R_DrawSinglePlane(visplane_t *pl) // It's a raw flat. else { + rawflat = true; R_CheckFlatLength(size); flat = ds_source; } @@ -978,8 +986,11 @@ void R_DrawSinglePlane(visplane_t *pl) if (ds_source == NULL) return; - // Check if the flat has dimensions that are powers-of-two numbers. - if (R_CheckPowersOfTwo()) + // Raw flats always have dimensions that are powers-of-two numbers. + if (rawflat) + ds_powersoftwo = true; + // Otherwise, check if this texture or patch has such dimensions. + else if (R_CheckPowersOfTwo()) { R_CheckFlatLength(ds_flatwidth * ds_flatheight); if (spanfunc == basespanfunc) @@ -1116,26 +1127,27 @@ void R_DrawSinglePlane(visplane_t *pl) ds_sz.z *= focallengthf; // Premultiply the texture vectors with the scale factors +#define SFMULT 65536.f if (ds_powersoftwo) { -#define SFMULT 65536.f*(1< Date: Thu, 12 Sep 2019 16:03:44 -0300 Subject: [PATCH 127/196] Moved this macro --- src/m_swap.h | 42 ++++++++++++++++++++++++++---------------- src/r_data.c | 15 ++------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/m_swap.h b/src/m_swap.h index 2d42f6138..4318ce7be 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -14,29 +14,39 @@ #ifndef __M_SWAP__ #define __M_SWAP__ -#include "endian.h" - // Endianess handling. // WAD files are stored little endian. +#include "endian.h" + +// Little to big endian #ifdef SRB2_BIG_ENDIAN -#define SHORT(x) ((INT16)(\ -(((UINT16)(x) & (UINT16)0x00ffU) << 8) \ -| \ -(((UINT16)(x) & (UINT16)0xff00U) >> 8))) \ + #define SHORT(x) ((INT16)(\ + (((UINT16)(x) & (UINT16)0x00ffU) << 8) \ + | \ + (((UINT16)(x) & (UINT16)0xff00U) >> 8))) \ -#define LONG(x) ((INT32)(\ -(((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \ -| \ -(((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \ -| \ -(((UINT32)(x) & (UINT32)0x00ff0000UL) >> 8) \ -| \ -(((UINT32)(x) & (UINT32)0xff000000UL) >> 24))) + #define LONG(x) ((INT32)(\ + (((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \ + | \ + (((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \ + | \ + (((UINT32)(x) & (UINT32)0x00ff0000UL) >> 8) \ + | \ + (((UINT32)(x) & (UINT32)0xff000000UL) >> 24))) #else -#define SHORT(x) ((INT16)(x)) -#define LONG(x) ((INT32)(x)) + #define SHORT(x) ((INT16)(x)) + #define LONG(x) ((INT32)(x)) +#endif + +// Big to little endian +#ifdef SRB2_LITTLE_ENDIAN + #define BIGENDIAN_LONG(x) ((INT32)((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000)) + #define BIGENDIAN_SHORT(x) ((INT16)((x>>8)|(x<<8))) +#else + #define BIGENDIAN_LONG(x) ((INT32)(x)) + #define BIGENDIAN_SHORT(x) ((INT16)(x)) #endif #endif diff --git a/src/r_data.c b/src/r_data.c index 485de3c40..172a61da5 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2747,24 +2747,13 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I if ((topoffset || leftoffset) && (chunk.data != NULL)) { INT32 *offsets = (INT32 *)chunk.data; - - // The grAb chunk stores offsets as big-endian numbers. - #ifdef SRB2_BIG_ENDIAN - #define ENDIANESS(x) (x) - #else - #define ENDIANESS(x) ((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000) - #endif - // read left offset if (leftoffset != NULL) - *leftoffset = (INT16)ENDIANESS(*offsets); + *leftoffset = (INT16)BIGENDIAN_LONG(*offsets); offsets++; - // read top offset if (topoffset != NULL) - *topoffset = (INT16)ENDIANESS(*offsets); - - #undef ENDIANESS + *topoffset = (INT16)BIGENDIAN_LONG(*offsets); } // bye From 2a04ac69e187b3d8870e372284cb19481ac4b703 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 12 Sep 2019 16:12:31 -0300 Subject: [PATCH 128/196] () --- src/m_swap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_swap.h b/src/m_swap.h index 4318ce7be..3b50dc623 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -42,8 +42,8 @@ // Big to little endian #ifdef SRB2_LITTLE_ENDIAN - #define BIGENDIAN_LONG(x) ((INT32)((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000)) - #define BIGENDIAN_SHORT(x) ((INT16)((x>>8)|(x<<8))) + #define BIGENDIAN_LONG(x) ((INT32)(((x)>>24)&0xff)|(((x)<<8)&0xff0000)|(((x)>>8)&0xff00)|(((x)<<24)&0xff000000)) + #define BIGENDIAN_SHORT(x) ((INT16)(((x)>>8)|((x)<<8))) #else #define BIGENDIAN_LONG(x) ((INT32)(x)) #define BIGENDIAN_SHORT(x) ((INT16)(x)) From 86f96096238cb0b849dcb0524784200ed4b66e58 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 12 Sep 2019 17:06:57 -0700 Subject: [PATCH 129/196] Support W_VerifyFile on PK3 --- src/w_wad.c | 202 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 59 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 2fda8674c..9dd5cb822 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1619,13 +1619,145 @@ void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5) #endif } +// Verify versions for different archive +// formats. checklist assumed to be valid. + +static int +W_VerifyName (const char *name, lumpchecklist_t *checklist, boolean status) +{ + size_t j; + for (j = 0; checklist[j].len && checklist[j].name; ++j) + { + if (( strncmp(name, checklist[j].name, + checklist[j].len) != false ) == status) + { + return true; + } + } + return false; +} + +static int +W_VerifyWAD (FILE *fp, lumpchecklist_t *checklist, boolean status) +{ + size_t i; + + // assume wad file + wadinfo_t header; + filelump_t lumpinfo; + + // read the header + if (fread(&header, 1, sizeof header, fp) == sizeof header + && header.numlumps < INT16_MAX + && strncmp(header.identification, "ZWAD", 4) + && strncmp(header.identification, "IWAD", 4) + && strncmp(header.identification, "PWAD", 4) + && strncmp(header.identification, "SDLL", 4)) + { + return true; + } + + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + + // let seek to the lumpinfo list + if (fseek(fp, header.infotableofs, SEEK_SET) == -1) + return true; + + for (i = 0; i < header.numlumps; i++) + { + // fill in lumpinfo for this wad file directory + if (fread(&lumpinfo, sizeof (lumpinfo), 1 , fp) != 1) + return true; + + lumpinfo.filepos = LONG(lumpinfo.filepos); + lumpinfo.size = LONG(lumpinfo.size); + + if (lumpinfo.size == 0) + continue; + + if (! W_VerifyName(lumpinfo.name, checklist, status)) + return false; + } + + return true; +} + +static int +W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) +{ + zend_t zend; + zentry_t zentry; + + UINT16 numlumps; + size_t i; + + char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00}; + char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00}; + + char lumpname[9]; + + // Haha the ResGetLumpsZip function doesn't + // check for file errors, so neither will I. + + // Central directory bullshit + + fseek(fp, 0, SEEK_END); + if (!ResFindSignature(fp, pat_end, max(0, ftell(fp) - (22 + 65536)))) + return true; + + fseek(fp, -4, SEEK_CUR); + if (fread(&zend, 1, sizeof zend, fp) < sizeof zend) + return true; + + numlumps = zend.entries; + + fseek(fp, zend.cdiroffset, SEEK_SET); + for (i = 0; i < numlumps; i++) + { + char* fullname; + char* trimname; + char* dotpos; + + if (fread(&zentry, 1, sizeof(zentry_t), fp) < sizeof(zentry_t)) + return true; + if (memcmp(zentry.signature, pat_central, 4)) + return true; + + fullname = malloc(zentry.namelen + 1); + if (fgets(fullname, zentry.namelen + 1, fp) != fullname) + return true; + + // Strip away file address and extension for the 8char name. + if ((trimname = strrchr(fullname, '/')) != 0) + trimname++; + else + trimname = fullname; // Care taken for root files. + + if (*trimname) // Ignore directories + { + if ((dotpos = strrchr(trimname, '.')) == 0) + dotpos = fullname + strlen(fullname); // Watch for files without extension. + + memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? + strncpy(lumpname, trimname, min(8, dotpos - trimname)); + + if (! W_VerifyName(lumpname, checklist, status)) + return false; + } + + free(fullname); + } + + return true; +} + // Note: This never opens lumps themselves and therefore doesn't have to // deal with compressed lumps. static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist, boolean status) { FILE *handle; - size_t i, j; int goodfile = false; if (!checklist) @@ -1634,66 +1766,18 @@ static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist, if ((handle = W_OpenWadFile(&filename, false)) == NULL) return -1; - // detect wad file by the absence of the other supported extensions - if (stricmp(&filename[strlen(filename) - 4], ".soc") -#ifdef HAVE_BLUA - && stricmp(&filename[strlen(filename) - 4], ".lua") -#endif - && stricmp(&filename[strlen(filename) - 4], ".pk3")) + if (stricmp(&filename[strlen(filename) - 4], ".pk3") == 0) + goodfile = W_VerifyPK3(handle, checklist, status); + else { - // assume wad file - wadinfo_t header; - filelump_t lumpinfo; - - // read the header - if (fread(&header, 1, sizeof header, handle) == sizeof header - && header.numlumps < INT16_MAX - && strncmp(header.identification, "ZWAD", 4) - && strncmp(header.identification, "IWAD", 4) - && strncmp(header.identification, "PWAD", 4) - && strncmp(header.identification, "SDLL", 4)) + // detect wad file by the absence of the other supported extensions + if (stricmp(&filename[strlen(filename) - 4], ".soc") +#ifdef HAVE_BLUA + && stricmp(&filename[strlen(filename) - 4], ".lua") +#endif + ) { - fclose(handle); - return true; - } - - header.numlumps = LONG(header.numlumps); - header.infotableofs = LONG(header.infotableofs); - - // let seek to the lumpinfo list - if (fseek(handle, header.infotableofs, SEEK_SET) == -1) - { - fclose(handle); - return false; - } - - goodfile = true; - for (i = 0; i < header.numlumps; i++) - { - // fill in lumpinfo for this wad file directory - if (fread(&lumpinfo, sizeof (lumpinfo), 1 , handle) != 1) - { - fclose(handle); - return -1; - } - - lumpinfo.filepos = LONG(lumpinfo.filepos); - lumpinfo.size = LONG(lumpinfo.size); - - if (lumpinfo.size == 0) - continue; - - for (j = 0; j < NUMSPRITES; j++) - if (sprnames[j] && !strncmp(lumpinfo.name, sprnames[j], 4)) // Sprites - continue; - - goodfile = false; - for (j = 0; checklist[j].len && checklist[j].name && !goodfile; j++) - if ((strncmp(lumpinfo.name, checklist[j].name, checklist[j].len) != false) == status) - goodfile = true; - - if (!goodfile) - break; + goodfile = W_VerifyWAD(handle, checklist, status); } } fclose(handle); From 3ebb9c2c496a45d13b8470afff7774ed1f745b65 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 12 Sep 2019 21:36:13 -0400 Subject: [PATCH 130/196] More harmful cactus --- src/dehacked.c | 4 ++++ src/info.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/info.h | 4 ++++ 3 files changed, 64 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 37995cc9f..7b0a07a2f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5737,6 +5737,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CACTI7", "S_CACTI8", "S_CACTI9", + "S_CACTI10", + "S_CACTI11", // Warning signs sprites "S_ARIDSIGN_CAUTION", @@ -7519,6 +7521,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CACTI7", "MT_CACTI8", "MT_CACTI9", + "MT_CACTI10", + "MT_CACTI11", "MT_ARIDSIGN_CAUTION", "MT_ARIDSIGN_CACTI", "MT_ARIDSIGN_SHARPTURN", diff --git a/src/info.c b/src/info.c index 5baf28943..23bee18a2 100644 --- a/src/info.c +++ b/src/info.c @@ -2321,6 +2321,8 @@ state_t states[NUMSTATES] = {SPR_CACT, 6, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI7 {SPR_CACT, 7, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI8 {SPR_CACT, 8, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI9 + {SPR_CACT, 9, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI10 + {SPR_CACT, 10, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI11 // Warning Signs {SPR_WWSG, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_ARIDSIGN_CAUTION @@ -12294,6 +12296,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_CACTI10 + 1230, // doomednum + S_CACTI10, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SCENERY|MF_PAIN, // flags + S_NULL // raisestate + }, + + { // MT_CACTI11 + 1231, // doomednum + S_CACTI11, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SCENERY|MF_PAIN, // flags + S_NULL // raisestate + }, + { // MT_FLAMEJET 1300, // doomednum S_FLAMEJETSTND, // spawnstate diff --git a/src/info.h b/src/info.h index a9d4bdde0..f4fd63c81 100644 --- a/src/info.h +++ b/src/info.h @@ -2442,6 +2442,8 @@ typedef enum state S_CACTI7, S_CACTI8, S_CACTI9, + S_CACTI10, + S_CACTI11, // Warning signs sprites S_ARIDSIGN_CAUTION, @@ -4246,6 +4248,8 @@ typedef enum mobj_type MT_CACTI7, // Harmful Cactus 3 MT_CACTI8, // Harmful Cactus 4 MT_CACTI9, // Harmful Cactus 5 + MT_CACTI10, // Harmful Cactus 6 + MT_CACTI11, // Harmful Cactus 7 MT_ARIDSIGN_CAUTION, // Caution Sign MT_ARIDSIGN_CACTI, // Cacti Sign MT_ARIDSIGN_SHARPTURN, // Sharp Turn Sign From 97710875549fa1bd54e5a82b59c46e85b1261330 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Sun, 15 Sep 2019 17:43:19 +0200 Subject: [PATCH 131/196] Always force player camera direction for springs with horizontal thrust component. --- src/p_map.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 15fa97c8f..28bfd2806 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -345,17 +345,14 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (horizspeed) { object->player->drawangle = spring->angle; - if (vertispeed || (object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0)) - { - object->angle = spring->angle; + object->angle = spring->angle; - if (!demoplayback || P_AnalogMove(object->player)) - { - if (object->player == &players[consoleplayer]) - localangle = spring->angle; - else if (object->player == &players[secondarydisplayplayer]) - localangle2 = spring->angle; - } + if (!demoplayback || P_AnalogMove(object->player)) + { + if (object->player == &players[consoleplayer]) + localangle = spring->angle; + else if (object->player == &players[secondarydisplayplayer]) + localangle2 = spring->angle; } } From 78075ecd8919052ef83c002673e20b48d354bc2b Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Tue, 17 Sep 2019 14:18:27 -0400 Subject: [PATCH 132/196] Fix FALLTHRU for Win32 console interface code --- src/sdl/i_system.c | 2 +- src/win32/win_sys.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index a18443c52..96e3d5b23 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -531,7 +531,7 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) break; case VK_RETURN: entering_con_command = false; - // Fall through. + /* FALLTHRU */ default: event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char } diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index d10f73b58..93b3ff523 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -327,7 +327,7 @@ static inline VOID I_GetConsoleEvents(VOID) break; case VK_RETURN: entering_con_command = false; - // Fall through. + /* FALLTHRU */ default: ev.data1 = MapVirtualKey(input.Event.KeyEvent.wVirtualKeyCode,2); // convert in to char } From 2a33ffb7d784d0c5c54ee5fd29fcbd4aeb35fbd7 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Tue, 17 Sep 2019 16:18:54 -0400 Subject: [PATCH 133/196] PNG support: use png_const_bytep, so we do not drop const type --- src/hardware/hw_cache.c | 4 ++-- src/r_data.c | 17 +++++++++++------ src/r_data.h | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 2574bc011..b1a685ff4 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -727,8 +727,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((UINT8 *)patch, len)) - patch = R_PNGToPatch((UINT8 *)patch, len, NULL, true); + if ((patch != NULL) && R_IsLumpPNG((const UINT8 *)patch, len)) + patch = R_PNGToPatch((const UINT8 *)patch, len, NULL, true); #endif // don't do it twice (like a cache) diff --git a/src/r_data.c b/src/r_data.c index 172a61da5..be27fecad 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2588,7 +2588,7 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat) } #ifndef NO_PNG_LUMPS -boolean R_IsLumpPNG(UINT8 *d, size_t s) +boolean R_IsLumpPNG(const UINT8 *d, size_t s) { if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ return false; @@ -2599,8 +2599,12 @@ boolean R_IsLumpPNG(UINT8 *d, size_t s) } #ifdef HAVE_PNG + +#if PNG_LIBPNG_VER_DLLNUM < 14 +typedef PNG_CONST png_byte *png_const_bytep; +#endif typedef struct { - png_bytep buffer; + png_const_bytep buffer; png_uint_32 bufsize; png_uint_32 current_pos; } png_io_t; @@ -2626,6 +2630,7 @@ static png_chunk_t chunk; static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk) { + (void)png_ptr; if (!memcmp(chonk->name, chunkname, 4)) { memcpy(chunk.name, chonk->name, 4); @@ -2648,7 +2653,7 @@ 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(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -2697,7 +2702,7 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I #endif // set our own read_function - png_io.buffer = (png_bytep)png; + png_io.buffer = (png_const_bytep)png; png_io.bufsize = size; png_io.current_pos = 0; png_set_read_fn(png_ptr, &png_io, PNG_IOReader); @@ -2767,7 +2772,7 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I } // Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { UINT8 *flat; png_uint_32 x, y; @@ -2803,7 +2808,7 @@ UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) // Convert a PNG to a patch. static unsigned char imgbuf[1<<26]; -patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean transparency) +patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency) { UINT16 width, height; INT16 topoffset = 0, leftoffset = 0; diff --git a/src/r_data.h b/src/r_data.h index 38b7ba0ce..ea48985dc 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -165,10 +165,10 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat); void R_TextureToFlat(size_t tex, UINT8 *flat); #ifndef NO_PNG_LUMPS -boolean R_IsLumpPNG(UINT8 *d, size_t s); +boolean R_IsLumpPNG(const UINT8 *d, size_t s); UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size); -patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean transparency); +patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency); boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); #endif From 35e3d9acab3e36ae370ce192bf370d512f375647 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Tue, 17 Sep 2019 20:20:09 +0000 Subject: [PATCH 134/196] Revert "Merge branch 'gl_skydome' into 'master'" This reverts merge request !326 --- src/hardware/hw_drv.h | 2 - src/hardware/hw_main.c | 186 ++++++++++--------------- src/hardware/hw_main.h | 1 - src/hardware/r_opengl/r_opengl.c | 226 ------------------------------- src/r_main.c | 1 - src/sdl/hwsym_sdl.c | 1 - src/sdl/i_video.c | 1 - src/v_video.c | 1 - src/win32/win_dll.c | 2 - 9 files changed, 75 insertions(+), 346 deletions(-) diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index e0507bc70..e2fa90eb0 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -47,7 +47,6 @@ EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal, RGBA_t *pgamma); EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl); EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color); EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags); -EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform); EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags); EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor); EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo); @@ -90,7 +89,6 @@ struct hwdriver_s FinishUpdate pfnFinishUpdate; Draw2DLine pfnDraw2DLine; DrawPolygon pfnDrawPolygon; - RenderSkyDome pfnRenderSkyDome; SetBlend pfnSetBlend; ClearBuffer pfnClearBuffer; SetTexture pfnSetTexture; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 6d3e6c8ce..4a075d376 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5869,122 +5869,86 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) // ========================================================================== // // ========================================================================== -static void HWR_DrawSkyBackground(player_t *player) +static void HWR_DrawSkyBackground(void) { - if (cv_grskydome.value) + FOutVector v[4]; + angle_t angle; + float dimensionmultiply; + float aspectratio; + float angleturn; + + HWR_GetTexture(texturetranslation[skytexture]); + aspectratio = (float)vid.width/(float)vid.height; + + //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 + // because it's called just after clearing the screen + // and thus, the near clipping plane is set to 3.99 + // Sryder: Just use the near clipping plane value then + + // 3--2 + // | /| + // |/ | + // 0--1 + v[0].x = v[3].x = -ZCLIP_PLANE-1; + v[1].x = v[2].x = ZCLIP_PLANE+1; + v[0].y = v[1].y = -ZCLIP_PLANE-1; + v[2].y = v[3].y = ZCLIP_PLANE+1; + + v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; + + // X + + // NOTE: This doesn't work right with texture widths greater than 1024 + // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly + // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture + + angle = (dup_viewangle + gr_xtoviewangle[0]); + + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); + + v[0].sow = v[3].sow = (-1.0f * angle) / ((ANGLE_90-1)*dimensionmultiply); // left + v[2].sow = v[1].sow = v[0].sow + (1.0f/dimensionmultiply); // right (or left + 1.0f) + // use +angle and -1.0f above instead if you wanted old backwards behavior + + // Y + angle = aimingangle; + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); + + if (splitscreen) { - FTransform transform; - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); - postimg_t *type; + dimensionmultiply *= 2; + angle *= 2; + } - if (splitscreen && player == &players[secondarydisplayplayer]) - type = &postimgtype2; - else - type = &postimgtype; - - memset(&transform, 0x00, sizeof(FTransform)); - - //04/01/2000: Hurdler: added for T&L - // It should replace all other gr_viewxxx when finished - transform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); - transform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); - - if (*type == postimg_flip) - transform.flip = true; - else - transform.flip = false; - - transform.scalex = 1; - transform.scaley = (float)vid.width/vid.height; - transform.scalez = 1; - transform.fovxangle = fpov; // Tails - transform.fovyangle = fpov; // Tails - transform.splitscreen = splitscreen; - - HWR_GetTexture(texturetranslation[skytexture]); - HWD.pfnRenderSkyDome(skytexture, textures[skytexture]->width, textures[skytexture]->height, transform); + // Middle of the sky should always be at angle 0 + // need to keep correct aspect ratio with X + if (atransform.flip) + { + // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously + v[3].tow = v[2].tow = -(0.5f-(0.5f/dimensionmultiply)); // top + v[0].tow = v[1].tow = v[3].tow - (1.0f/dimensionmultiply); // bottom (or top - 1.0f) } else { - FOutVector v[4]; - angle_t angle; - float dimensionmultiply; - float aspectratio; - float angleturn; - - HWR_GetTexture(texturetranslation[skytexture]); - aspectratio = (float)vid.width/(float)vid.height; - - //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 - // because it's called just after clearing the screen - // and thus, the near clipping plane is set to 3.99 - // Sryder: Just use the near clipping plane value then - - // 3--2 - // | /| - // |/ | - // 0--1 - v[0].x = v[3].x = -ZCLIP_PLANE-1; - v[1].x = v[2].x = ZCLIP_PLANE+1; - v[0].y = v[1].y = -ZCLIP_PLANE-1; - v[2].y = v[3].y = ZCLIP_PLANE+1; - - v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; - - // X - - // NOTE: This doesn't work right with texture widths greater than 1024 - // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly - // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture - - angle = (dup_viewangle + gr_xtoviewangle[0]); - - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); - - v[0].sow = v[3].sow = (-1.0f * angle) / ((ANGLE_90-1)*dimensionmultiply); // left - v[2].sow = v[1].sow = v[0].sow + (1.0f/dimensionmultiply); // right (or left + 1.0f) - // use +angle and -1.0f above instead if you wanted old backwards behavior - - // Y - angle = aimingangle; - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); - - if (splitscreen) - { - dimensionmultiply *= 2; - angle *= 2; - } - - // Middle of the sky should always be at angle 0 - // need to keep correct aspect ratio with X - if (atransform.flip) - { - // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously - v[3].tow = v[2].tow = -(0.5f-(0.5f/dimensionmultiply)); // top - v[0].tow = v[1].tow = v[3].tow - (1.0f/dimensionmultiply); // bottom (or top - 1.0f) - } - else - { - v[0].tow = v[1].tow = -(0.5f-(0.5f/dimensionmultiply)); // bottom - v[3].tow = v[2].tow = v[0].tow - (1.0f/dimensionmultiply); // top (or bottom - 1.0f) - } - - angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; - - if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa - { - angle = InvAngle(angle); - v[3].tow = v[2].tow += ((float) angle / angleturn); - v[0].tow = v[1].tow += ((float) angle / angleturn); - } - else - { - v[3].tow = v[2].tow -= ((float) angle / angleturn); - v[0].tow = v[1].tow -= ((float) angle / angleturn); - } - - HWD.pfnDrawPolygon(NULL, v, 4, 0); + v[0].tow = v[1].tow = -(0.5f-(0.5f/dimensionmultiply)); // bottom + v[3].tow = v[2].tow = v[0].tow - (1.0f/dimensionmultiply); // top (or bottom - 1.0f) } + + angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; + + if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa + { + angle = InvAngle(angle); + v[3].tow = v[2].tow += ((float) angle / angleturn); + v[0].tow = v[1].tow += ((float) angle / angleturn); + } + else + { + v[3].tow = v[2].tow -= ((float) angle / angleturn); + v[0].tow = v[1].tow -= ((float) angle / angleturn); + } + + HWD.pfnDrawPolygon(NULL, v, 4, 0); } @@ -6136,7 +6100,7 @@ if (0) } if (drawsky) - HWR_DrawSkyBackground(player); + HWR_DrawSkyBackground(); //Hurdler: it doesn't work in splitscreen mode drawsky = splitscreen; @@ -6353,7 +6317,7 @@ if (0) } if (!skybox && drawsky) // Don't draw the regular sky if there's a skybox - HWR_DrawSkyBackground(player); + HWR_DrawSkyBackground(); //Hurdler: it doesn't work in splitscreen mode drawsky = splitscreen; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 31e97cc13..f8524990f 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -98,7 +98,6 @@ extern consvar_t cv_voodoocompatibility; extern consvar_t cv_grfovchange; extern consvar_t cv_grsolvetjoin; extern consvar_t cv_grspritebillboarding; -extern consvar_t cv_grskydome; extern float gr_viewwidth, gr_viewheight, gr_baseviewwindowy; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 2fe6741e2..dfee19857 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1427,232 +1427,6 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, Clamp2D(GL_TEXTURE_WRAP_T); } -// PRBoom sky dome -typedef struct vbo_vertex_s -{ - float x, y, z; - float u, v; - unsigned char r, g, b, a; -} vbo_vertex_t; - -typedef struct -{ - int mode; - int vertexcount; - int vertexindex; - int use_texture; -} GLSkyLoopDef; - -typedef struct -{ - int id; - int rows, columns; - int loopcount; - GLSkyLoopDef *loops; - vbo_vertex_t *data; -} GLSkyVBO; - -// The texture offset to be applied to the texture coordinates in SkyVertex(). -static int rows, columns; -static boolean yflip; -static int texw, texh; -static float yMult, yAdd; -static boolean foglayer; -static float delta = 0.0f; -static int gl_sky_detail = 16; -static INT32 lasttex = -1; - -static RGBA_t SkyColor; - -#define MAP_COEFF 128.0f -#define MAP_SCALE (MAP_COEFF*(float)FRACUNIT) - -static void SkyVertex(vbo_vertex_t *vbo, int r, int c) -{ - static fixed_t scale = 10000 << FRACBITS; - static angle_t maxSideAngle = ANGLE_180 / 3; - - angle_t topAngle = (angle_t)(c / (float)columns * ANGLE_MAX); - angle_t sideAngle = maxSideAngle * (rows - r) / rows; - fixed_t height = FINESINE(sideAngle>>ANGLETOFINESHIFT); - fixed_t realRadius = FixedMul(scale, FINECOSINE(sideAngle>>ANGLETOFINESHIFT)); - fixed_t x = FixedMul(realRadius, FINECOSINE(topAngle>>ANGLETOFINESHIFT)); - fixed_t y = (!yflip) ? FixedMul(scale, height) : FixedMul(scale, height) * -1; - fixed_t z = FixedMul(realRadius, FINESINE(topAngle>>ANGLETOFINESHIFT)); - float timesRepeat; - - timesRepeat = (short)(4 * (256.0f / texw)); - if (timesRepeat == 0.0f) - timesRepeat = 1.0f; - - if (!foglayer) - { - boolean flip = yflip; - vbo->r = 255; - vbo->g = 255; - vbo->b = 255; - vbo->a = (r == 0 ? 0 : 255); - - // Flip Y coordinate anyway for the top part of the hemisphere - if (r <= 1) - flip = !flip; - - // And the texture coordinates. - vbo->u = (-timesRepeat * c / (float)columns); - if (!flip) // Flipped Y is for the lower hemisphere. - vbo->v = (r / (float)rows) * 1.f * yMult + yAdd; - else - vbo->v = ((rows-r)/(float)rows) * 1.f * yMult + yAdd; - } - - // And finally the vertex. - vbo->x = (float)x/(float)MAP_SCALE; - vbo->y = (float)y/(float)MAP_SCALE + delta; - vbo->z = (float)z/(float)MAP_SCALE; -} - -GLSkyVBO sky_vbo; - -static void gld_BuildSky(int row_count, int col_count) -{ - int c, r; - vbo_vertex_t *vertex_p; - int vertex_count = 2 * row_count * (col_count * 2 + 2) + col_count * 2; - - GLSkyVBO *vbo = &sky_vbo; - - if ((vbo->columns != col_count) || (vbo->rows != row_count)) - { - free(vbo->loops); - free(vbo->data); - memset(vbo, 0, sizeof(&vbo)); - } - - if (!vbo->data) - { - memset(vbo, 0, sizeof(&vbo)); - vbo->loops = malloc((row_count * 2 + 2) * sizeof(vbo->loops[0])); - // create vertex array - vbo->data = malloc(vertex_count * sizeof(vbo->data[0])); - } - - vbo->columns = col_count; - vbo->rows = row_count; - - vertex_p = &vbo->data[0]; - vbo->loopcount = 0; - - memset(&SkyColor, 0xFF, sizeof(SkyColor)); - - // Why not? - for (yflip = false; yflip <= true; yflip++) - { - vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_FAN; - vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; - vbo->loops[vbo->loopcount].vertexcount = col_count; - vbo->loops[vbo->loopcount].use_texture = false; - vbo->loopcount++; - - yAdd = 0.5f; - yMult = 1.0f; - /*if (yflip == 0) - SkyColor = &sky->CeilingSkyColor[vbo_idx]; - else - SkyColor = &sky->FloorSkyColor[vbo_idx];*/ - - delta = 0.0f; - foglayer = true; - for (c = 0; c < col_count; c++) - { - SkyVertex(vertex_p, 1, c); - vertex_p->r = SkyColor.s.red; - vertex_p->g = SkyColor.s.green; - vertex_p->b = SkyColor.s.blue; - vertex_p->a = 255; - vertex_p++; - } - foglayer = false; - - delta = (yflip ? 5.0f : -5.0f) / MAP_COEFF; - - for (r = 0; r < row_count; r++) - { - vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_STRIP; - vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; - vbo->loops[vbo->loopcount].vertexcount = 2 * col_count + 2; - vbo->loops[vbo->loopcount].use_texture = true; //(r > 1) ? true : false; - vbo->loopcount++; - - for (c = 0; c <= col_count; c++) - { - SkyVertex(vertex_p++, r + (yflip ? 1 : 0), (c ? c : 0)); - SkyVertex(vertex_p++, r + (yflip ? 0 : 1), (c ? c : 0)); - } - } - } -} - -static void RenderDomeForReal(INT32 skytexture) -{ - int i, j; - GLSkyVBO *vbo = &sky_vbo; - - pglRotatef(270.f, 0.f, 1.f, 0.f); - - rows = 4; - columns = 4 * gl_sky_detail; - - if (lasttex != skytexture) - { - lasttex = skytexture; - gld_BuildSky(rows, columns); - } - - pglScalef(1.0f, (float)texh / 230.0f, 1.0f); - - for (j = 0; j < 2; j++) - { - for (i = 0; i < vbo->loopcount; i++) - { - GLSkyLoopDef *loop = &vbo->loops[i]; - - if (j == 0 ? loop->use_texture : !loop->use_texture) - continue; - else - { - int k; - pglBegin(loop->mode); - for (k = loop->vertexindex; k < (loop->vertexindex + loop->vertexcount); k++) - { - vbo_vertex_t *v = &vbo->data[k]; - if (loop->use_texture) - pglTexCoord2f(v->u, v->v); - pglColor4f(v->r, v->g, v->b, v->a); - pglVertex3f(v->x, v->y, v->z); - } - pglEnd(); - } - } - } - - pglScalef(1.0f, 1.0f, 1.0f); - - // current color is undefined after glDrawArrays - pglColor4f(1.0f, 1.0f, 1.0f, 1.0f); -} - -EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform) -{ - SetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated); - SetTransform(&transform); - - texw = texture_width; - texh = texture_height; - RenderDomeForReal(tex); - - // HWR_DrawSkyBackground left no blend flags after rendering the sky - SetBlend(0); -} // ========================================================================== // diff --git a/src/r_main.c b/src/r_main.c index 5135e57ce..db351e991 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1215,7 +1215,6 @@ void R_RegisterEngineStuff(void) #endif CV_RegisterVar(&cv_grmd2); CV_RegisterVar(&cv_grspritebillboarding); - CV_RegisterVar(&cv_grskydome); #endif #ifdef HWRENDER diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 103398405..05ac6450e 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -79,7 +79,6 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(Init); GETFUNC(Draw2DLine); GETFUNC(DrawPolygon); - GETFUNC(RenderSkyDome); GETFUNC(SetBlend); GETFUNC(ClearBuffer); GETFUNC(SetTexture); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 2f8bfb40a..2f5f1f684 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1499,7 +1499,6 @@ void I_StartupGraphics(void) HWD.pfnFinishUpdate = NULL; HWD.pfnDraw2DLine = hwSym("Draw2DLine",NULL); HWD.pfnDrawPolygon = hwSym("DrawPolygon",NULL); - HWD.pfnRenderSkyDome = hwSym("RenderSkyDome",NULL); HWD.pfnSetBlend = hwSym("SetBlend",NULL); HWD.pfnClearBuffer = hwSym("ClearBuffer",NULL); HWD.pfnSetTexture = hwSym("SetTexture",NULL); diff --git a/src/v_video.c b/src/v_video.c index 747300114..93fefdd97 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -112,7 +112,6 @@ static CV_PossibleValue_t CV_MD2[] = {{0, "Off"}, {1, "On"}, {2, "Old"}, {0, NUL // console variables in development consvar_t cv_grmd2 = {"gr_md2", "Off", CV_SAVE, CV_MD2, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grspritebillboarding = {"gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; #endif // local copy of the palette for V_GetColor() diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c index ce007af25..71eda0437 100644 --- a/src/win32/win_dll.c +++ b/src/win32/win_dll.c @@ -102,7 +102,6 @@ static loadfunc_t hwdFuncTable[] = { {"FinishUpdate@4", &hwdriver.pfnFinishUpdate}, {"Draw2DLine@12", &hwdriver.pfnDraw2DLine}, {"DrawPolygon@16", &hwdriver.pfnDrawPolygon}, - {"RenderSkyDome@16", &hwdriver.pfnRenderDome}, {"SetBlend@4", &hwdriver.pfnSetBlend}, {"ClearBuffer@12", &hwdriver.pfnClearBuffer}, {"SetTexture@4", &hwdriver.pfnSetTexture}, @@ -134,7 +133,6 @@ static loadfunc_t hwdFuncTable[] = { {"FinishUpdate", &hwdriver.pfnFinishUpdate}, {"Draw2DLine", &hwdriver.pfnDraw2DLine}, {"DrawPolygon", &hwdriver.pfnDrawPolygon}, - {"RenderSkyDome", &hwdriver.pfnRenderDome}, {"SetBlend", &hwdriver.pfnSetBlend}, {"ClearBuffer", &hwdriver.pfnClearBuffer}, {"SetTexture", &hwdriver.pfnSetTexture}, From 81d1453dc47e7d6e1aaa16531cecba1eda36e3ac Mon Sep 17 00:00:00 2001 From: sphere Date: Wed, 18 Sep 2019 02:05:19 +0200 Subject: [PATCH 135/196] New animated DSZ kelp. --- src/dehacked.c | 7 ++++++ src/info.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/info.h | 9 ++++++++ 3 files changed, 77 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 7b0a07a2f..b19607d8e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5593,6 +5593,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Kelp, "S_KELP", + // Animated algae + "S_ANIMALGAETOP1", + "S_ANIMALGAETOP2", + "S_ANIMALGAESEG", + // DSZ Stalagmites "S_DSZSTALAGMITE", "S_DSZ2STALAGMITE", @@ -7462,6 +7467,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CORAL3", // Coral 3 "MT_BLUECRYSTAL", // Blue Crystal "MT_KELP", // Kelp + "MT_ANIMALGAETOP", // Animated algae top + "MT_ANIMALGAESEG", // Animated algae segment "MT_DSZSTALAGMITE", // Deep Sea 1 Stalagmite "MT_DSZ2STALAGMITE", // Deep Sea 2 Stalagmite "MT_LIGHTBEAM", // DSZ Light beam diff --git a/src/info.c b/src/info.c index b6d6d49a7..702466d23 100644 --- a/src/info.c +++ b/src/info.c @@ -217,6 +217,8 @@ char sprnames[NUMSPRITES + 1][5] = "CRL3", // Coral 3 "BCRY", // Blue Crystal "KELP", // Kelp + "ALGA", // Animated algae top + "ALGB", // Animated algae segment "DSTG", // DSZ Stalagmites "LIBE", // DSZ Light beam @@ -2179,6 +2181,11 @@ state_t states[NUMSTATES] = // Kelp {SPR_KELP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KELP + // Animated algae + {SPR_ALGA, 0, 1, {A_ConnectToGround}, MT_ANIMALGAESEG, 0, S_ANIMALGAETOP2}, // S_ANIMALGAETOP1 + {SPR_ALGA, 0|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 11, 4, S_NULL}, // S_ANIMALGAETOP2 + {SPR_ALGB, 0|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 11, 4, S_NULL}, // S_ANIMALGAESEG + // DSZ Stalagmites {SPR_DSTG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_DSZSTALAGMITE {SPR_DSTG, 1, -1, {NULL}, 0, 0, S_NULL}, // S_DSZ2STALAGMITE @@ -10202,6 +10209,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_ANIMALGAETOP + 1013, // doomednum + S_ANIMALGAETOP1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 120*FRACUNIT, // height + 0, // display offset + 4, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags + S_NULL // raisestate + }, + + { // MT_ANIMALGAESEG + -1, // doomednum + S_ANIMALGAESEG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 120*FRACUNIT, // height + 0, // display offset + 4, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_DSZSTALAGMITE 1008, // doomednum S_DSZSTALAGMITE, // spawnstate diff --git a/src/info.h b/src/info.h index 46279746e..ae979df31 100644 --- a/src/info.h +++ b/src/info.h @@ -463,6 +463,8 @@ typedef enum sprite SPR_CRL3, // Coral 3 SPR_BCRY, // Blue Crystal SPR_KELP, // Kelp + SPR_ALGA, // Animated algae top + SPR_ALGB, // Animated algae segment SPR_DSTG, // DSZ Stalagmites SPR_LIBE, // DSZ Light beam @@ -2304,6 +2306,11 @@ typedef enum state // Kelp, S_KELP, + // Animated algae + S_ANIMALGAETOP1, + S_ANIMALGAETOP2, + S_ANIMALGAESEG, + // DSZ Stalagmites S_DSZSTALAGMITE, S_DSZ2STALAGMITE, @@ -4195,6 +4202,8 @@ typedef enum mobj_type MT_CORAL3, // Coral 3 MT_BLUECRYSTAL, // Blue Crystal MT_KELP, // Kelp + MT_ANIMALGAETOP, // Animated algae top + MT_ANIMALGAESEG, // Animated algae segment MT_DSZSTALAGMITE, // Deep Sea 1 Stalagmite MT_DSZ2STALAGMITE, // Deep Sea 2 Stalagmite MT_LIGHTBEAM, // DSZ Light beam From a6831aff9ca5a890feb376ea9ba651089e0d1441 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 17 Sep 2019 22:29:53 -0300 Subject: [PATCH 136/196] compile fix --- src/hardware/hw_cache.c | 8 ++++---- src/r_data.c | 16 ++++++++-------- src/r_data.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index b1a685ff4..02df290b8 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -149,7 +149,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp.rgba = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; @@ -159,7 +159,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp.rgba = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; @@ -263,7 +263,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp.rgba = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; @@ -273,7 +273,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp.rgba = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; diff --git a/src/r_data.c b/src/r_data.c index be27fecad..496f6bdd7 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -241,7 +241,7 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, tex } } -RGBA_t ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) +UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) { RGBA_t output; if (style == AST_TRANSLUCENT) @@ -298,13 +298,13 @@ RGBA_t ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alph } // just copy the pixel else if (style == AST_COPY) - return background; + output.rgba = foreground.rgba; + + output.s.alpha = 0xFF; + return output.rgba; } #undef clamp - // unimplemented blend modes return the background pixel - output = background; - output.s.alpha = 0xFF; - return output; + return 0; } UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha) @@ -321,7 +321,7 @@ UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 al } // just copy the pixel else if (style == AST_COPY) - return background; + return foreground; // use ASTBlendPixel for all other blend modes // and find the nearest colour in the palette else if (style != AST_TRANSLUCENT) @@ -329,7 +329,7 @@ UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 al RGBA_t texel; RGBA_t bg = V_GetColor(background); RGBA_t fg = V_GetColor(foreground); - texel = ASTBlendPixel(bg, fg, style, alpha); + texel.rgba = ASTBlendPixel(bg, fg, style, alpha); return NearestColor(texel.s.red, texel.s.green, texel.s.blue); } // fallback if all above fails, somehow diff --git a/src/r_data.h b/src/r_data.h index ea48985dc..c2fd284ff 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -25,7 +25,7 @@ // Possible alpha types for a patch. enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; -RGBA_t ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha); +UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha); UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha); UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); From 45a99ce318f32009eeb0de525db07160f4689ab7 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 12:26:34 +0100 Subject: [PATCH 137/196] Change startchar's default value to 0 (Resolves #222). --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index d05048a9d..d3442fcf9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -118,7 +118,7 @@ const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 description_t description[MAXSKINS]; -INT16 char_on = -1, startchar = 1; +INT16 char_on = -1, startchar = 0; static char *char_notes = NULL; static fixed_t char_scroll = 0; From e230d7351c379818ab0ae188456fe267b27aec1c Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 12:46:18 +0100 Subject: [PATCH 138/196] Completely untested cuz I need to commit before I can merge the compile fix, but the intent of this commit is: * Fix S_PLAY_FLY_TIRED animation being fast. * Add moving tails to S_PLAY_SWIM animation! * Fix autobrake happening when your controls are locked by pw_nocontrol/PF_STASIS. (Resolves #219, hopefully..?) --- src/p_user.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 43138d2e9..ff887a171 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8200,7 +8200,7 @@ static void P_MovePlayer(player_t *player) else { // Tails-gets-tired Stuff - if (player->panim == PA_ABILITY) + if (player->panim == PA_ABILITY && player->mo->state-states != S_PLAY_FLY_TIRED) P_SetPlayerMobjState(player->mo, S_PLAY_FLY_TIRED); if (player->charability == CA_FLY && (leveltime % 10 == 0) @@ -10604,7 +10604,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) angle_t horizangle = player->drawangle; fixed_t zoffs = 0; fixed_t backwards = -1*FRACUNIT; - boolean doroll = (player->panim == PA_ROLL || player->panim == PA_JUMP); + boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || player->mo->sprite2 == SPR2_SWIM); angle_t rollangle; boolean panimchange; INT32 ticnum = 0; @@ -10636,12 +10636,17 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) else zdist = player->mo->momz; rollangle = R_PointToAngle2(0, 0, testval, -P_MobjFlip(player->mo)*zdist); - zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT); - backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT); + if (player->mo->sprite2 == SPR2_SWIM) + backwards = -5*FRACUNIT; + else + { + zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT); + backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT); + } } else if (player->panim == PA_RUN) backwards = -5*FRACUNIT; - else if (player->panim == PA_SPRING) + else if (player->panim == PA_SPRING || player->panim == PA_JUMP) { zoffs += 4*FRACUNIT; backwards /= 2; @@ -10663,7 +10668,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) zoffs = -7*FRACUNIT; backwards = -9*FRACUNIT; } - else if (player->mo->sprite2 == SPR2_FLY || player->mo->sprite2 == SPR2_TIRE) + else if (player->panim == PA_ABILITY) backwards = -5*FRACUNIT; // sprite... @@ -10680,7 +10685,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) else chosenstate = S_TAILSOVERLAY_0DEGREES; } - else if (player->panim == PA_SPRING) + else if (player->panim == PA_SPRING || player->panim == PA_JUMP) chosenstate = S_TAILSOVERLAY_MINUS60DEGREES; else if (player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE) chosenstate = S_TAILSOVERLAY_PLUS60DEGREES; @@ -10703,6 +10708,8 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) } else if (player->mo->sprite2 == SPR2_FLY) chosenstate = S_TAILSOVERLAY_FLY; + else if (player->mo->sprite2 == SPR2_SWIM) + chosenstate = S_TAILSOVERLAY_FLY; else if (player->mo->sprite2 == SPR2_TIRE) chosenstate = S_TAILSOVERLAY_TIRE; else if (player->panim == PA_ABILITY2) @@ -11269,8 +11276,8 @@ void P_PlayerThink(player_t *player) { boolean currentlyonground = P_IsObjectOnGround(player->mo); - if (!player->powers[pw_carry] - && ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) + if (!player->powers[pw_carry] && !player->powers[pw_nocontrol] + && ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE|PF_STASIS)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) && !(cmd->forwardmove || cmd->sidemove) && (player->rmomx || player->rmomy) && (!player->capsule || (player->capsule->reactiontime != (player-players)+1))) From 9d774f7578a3e4b709980b1f9fbb588b77a69264 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 13:46:17 +0100 Subject: [PATCH 139/196] More swim stuff! * Prevent being able to damage enemies from below while swimming. * Make the swim-specific bubbles happen at the hands instead of where the propeller would be. * Improve placement/angle of swimming tails overlay. * Immediately set flight time to 0 if a player is being carried underwater. --- src/p_user.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index ff887a171..699069592 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1109,7 +1109,7 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing) } else if (P_MobjFlip(player->mo)*(topheight - (thing->z + thing->height/2)) < 0) { - if (player->charability == CA_FLY && player->panim == PA_ABILITY && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0)) + if (player->charability == CA_FLY && player->panim == PA_ABILITY && !(player->mo->eflags & MFE_UNDERWATER) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0)) return true; } @@ -2964,22 +2964,19 @@ static void P_DoBubbleBreath(player_t *player) P_SetScale(bubble, bubble->destscale); } - if (player->powers[pw_carry] == CR_NIGHTSMODE) // NiGHTS Super doesn't spawn flight bubbles - return; - // Tails stirs up the water while flying in it if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM) { - fixed_t radius = (3*player->mo->radius)>>1; + fixed_t radius = player->mo->radius; angle_t fa = ((leveltime%45)*FINEANGLES/8) & FINEMASK; fixed_t stirwaterx = FixedMul(FINECOSINE(fa),radius); fixed_t stirwatery = FixedMul(FINESINE(fa),radius); fixed_t stirwaterz; if (player->mo->eflags & MFE_VERTICALFLIP) - stirwaterz = player->mo->z + player->mo->height - FixedDiv(player->mo->height,3*FRACUNIT/2); + stirwaterz = player->mo->z + player->mo->height - (4<mo->z + FixedDiv(player->mo->height,3*FRACUNIT/2); + stirwaterz = player->mo->z + (4<mo->x + stirwaterx, @@ -10604,7 +10601,8 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) angle_t horizangle = player->drawangle; fixed_t zoffs = 0; fixed_t backwards = -1*FRACUNIT; - boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || player->mo->sprite2 == SPR2_SWIM); + boolean doswim = (player->panim == PA_ABILITY && (player->mo->eflags & MFE_UNDERWATER)); + boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || doswim); angle_t rollangle; boolean panimchange; INT32 ticnum = 0; @@ -10631,14 +10629,17 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) if (testval < FRACUNIT) testval = FRACUNIT; } - if (smilesonground && !player->mo->reactiontime) + + if (doswim) + zdist = player->mo->momz<<1; + else if (smilesonground && !player->mo->reactiontime) zdist = (player->mo->z - tails->threshold); else zdist = player->mo->momz; + rollangle = R_PointToAngle2(0, 0, testval, -P_MobjFlip(player->mo)*zdist); - if (player->mo->sprite2 == SPR2_SWIM) - backwards = -5*FRACUNIT; - else + + if (!doswim) { zoffs = 3*FRACUNIT + 12*FINESINE(rollangle >> ANGLETOFINESHIFT); backwards = -12*FINECOSINE(rollangle >> ANGLETOFINESHIFT); @@ -11819,6 +11820,8 @@ void P_PlayerAfterThink(player_t *player) { if (player->mo->state-states != S_PLAY_RIDE) P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + if (tails->eflags & MFE_UNDERWATER) + tails->player->powers[pw_tailsfly] = 0; } else P_SetTarget(&player->mo->tracer, NULL); From e993b8981ecf1f960de2ada89c590a79049302b4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 13:55:20 +0100 Subject: [PATCH 140/196] Fix solidity of lava in reverse gravity. (Resolves #216) --- src/p_mobj.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 44c6b9f6e..236347eae 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2381,22 +2381,42 @@ boolean P_CheckDeathPitCollide(mobj_t *mo) boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover) { + fixed_t topheight; + I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); - { - fixed_t topheight = - #ifdef ESLOPE - *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : - #endif - *rover->topheight; + // not a lava block with solid planes + if (!(rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3 + && !(rover->master->flags & ML_BLOCKMONSTERS))) + return false; - if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3 - && !(rover->master->flags & ML_BLOCKMONSTERS) - && ((rover->master->flags & ML_EFFECT3) || mo->z-mo->momz > topheight - FixedMul(16*FRACUNIT, mo->scale))) + // is solid from the sides + if (rover->master->flags & ML_EFFECT3) + return true; + + if (mo->eflags & MFE_VERTICALFLIP) + { + topheight = + #ifdef ESLOPE + *rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) : + #endif + *rover->bottomheight; + + if (mo->z+mo->height-mo->momz < topheight + FixedMul(16*FRACUNIT, mo->scale)) return true; + return false; } + topheight = + #ifdef ESLOPE + *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : + #endif + *rover->topheight; + + if (mo->z-mo->momz > topheight - FixedMul(16*FRACUNIT, mo->scale)) + return true; + return false; } From a8a8ae0d0bdfb8a6c6eb1db644b24250ee8326f2 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 14:11:06 +0100 Subject: [PATCH 141/196] Only set flight time to 0 if skin has SPR2_SWIM spriteset. --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 699069592..b719ae33b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11820,7 +11820,7 @@ void P_PlayerAfterThink(player_t *player) { if (player->mo->state-states != S_PLAY_RIDE) P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); - if (tails->eflags & MFE_UNDERWATER) + if ((tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) tails->player->powers[pw_tailsfly] = 0; } else From 5d37ddd676a75a68eb47b8cca3218df5cc17e060 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 14:16:52 +0100 Subject: [PATCH 142/196] Change threshold on autobrake skidding sound/animaton to runspeed rather than half of runspeed. --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index b719ae33b..e0a4d710c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11289,7 +11289,7 @@ void P_PlayerThink(player_t *player) if (!currentlyonground) acceleration /= 2; // fake skidding! see P_SkidStuff for reference on conditionals - else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed/2, player->mo->scale)) + else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed, player->mo->scale)) // modified from player->runspeed/2 'cuz the skid was just TOO frequent ngl { if (player->mo->state-states != S_PLAY_SKID) P_SetPlayerMobjState(player->mo, S_PLAY_SKID); From 2ed9d957b082d5717bb4acbe51317cc4c0042478 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 15:26:21 +0100 Subject: [PATCH 143/196] Fix Knuckles being able to climb up solid midtextures by repeatedly gliding at them. (addresses #174 checkbox 2) --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index 28bfd2806..dee1b5c40 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -3199,7 +3199,7 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle) && glidesector->sector->ceilingpic == skyflatnum) return false; - if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < ceilingz) + if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < floorz) || (player->mo->z >= ceilingz)) floorclimb = true; } From 3285cc9a0df647cf5d09cd56a4e11ca1abb512d4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 16:06:13 +0100 Subject: [PATCH 144/196] Properly scrub the player struct clean between level transitions. (Good chance this fixes our new bug where the player gets stuck in their standing pose when the map begins..?) --- src/g_game.c | 50 ++++++++++++++++++++++++-------------------------- src/g_game.h | 2 +- src/p_mobj.c | 2 +- src/p_setup.c | 40 +++++++++------------------------------- 4 files changed, 35 insertions(+), 59 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 7a876e968..a3025f949 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2100,7 +2100,7 @@ static inline void G_PlayerFinishLevel(INT32 player) // G_PlayerReborn // Called after a player dies. Almost everything is cleared and initialized. // -void G_PlayerReborn(INT32 player) +void G_PlayerReborn(INT32 player, boolean betweenmaps) { player_t *p; INT32 score; @@ -2205,7 +2205,7 @@ void G_PlayerReborn(INT32 player) bot = players[player].bot; pity = players[player].pity; - if (!G_IsSpecialStage(gamemap)) + if (betweenmaps || !G_IsSpecialStage(gamemap)) { rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings); spheres = 0; @@ -2284,6 +2284,28 @@ void G_PlayerReborn(INT32 player) //if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there //p->powers[pw_flashing] = flashingtics-1; // Babysitting deterrent + // Check to make sure their color didn't change somehow... + if (G_GametypeHasTeams()) + { + if (p->ctfteam == 1 && p->skincolor != skincolor_redteam) + { + if (p == &players[consoleplayer]) + CV_SetValue(&cv_playercolor, skincolor_redteam); + else if (p == &players[secondarydisplayplayer]) + CV_SetValue(&cv_playercolor2, skincolor_redteam); + } + else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam) + { + if (p == &players[consoleplayer]) + CV_SetValue(&cv_playercolor, skincolor_blueteam); + else if (p == &players[secondarydisplayplayer]) + CV_SetValue(&cv_playercolor2, skincolor_blueteam); + } + } + + if (betweenmaps) + return; + if (p-players == consoleplayer) { if (mapmusflags & MUSIC_RELOADRESET) @@ -2303,9 +2325,6 @@ void G_PlayerReborn(INT32 player) if (gametype == GT_COOP) P_FindEmerald(); // scan for emeralds to hunt for - // Reset Nights score and max link to 0 on death - p->marescore = p->maxlink = 0; - // If NiGHTS, find lowest mare to start with. p->mare = P_FindLowestMare(); @@ -2313,27 +2332,6 @@ void G_PlayerReborn(INT32 player) if (p->mare == 255) p->mare = 0; - - p->marelap = p->marebonuslap = 0; - - // Check to make sure their color didn't change somehow... - if (G_GametypeHasTeams()) - { - if (p->ctfteam == 1 && p->skincolor != skincolor_redteam) - { - if (p == &players[consoleplayer]) - CV_SetValue(&cv_playercolor, skincolor_redteam); - else if (p == &players[secondarydisplayplayer]) - CV_SetValue(&cv_playercolor2, skincolor_redteam); - } - else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam) - { - if (p == &players[consoleplayer]) - CV_SetValue(&cv_playercolor, skincolor_blueteam); - else if (p == &players[secondarydisplayplayer]) - CV_SetValue(&cv_playercolor2, skincolor_blueteam); - } - } } // diff --git a/src/g_game.h b/src/g_game.h index e161bc8ed..df1301dd7 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -100,7 +100,7 @@ extern INT32 localaiming, localaiming2; // should be an angle_t but signed // void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo); void G_DoReborn(INT32 playernum); -void G_PlayerReborn(INT32 player); +void G_PlayerReborn(INT32 player, boolean betweenmaps); void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS); char *G_BuildMapTitle(INT32 mapnum); diff --git a/src/p_mobj.c b/src/p_mobj.c index 236347eae..994ddcfe7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10432,7 +10432,7 @@ void P_SpawnPlayer(INT32 playernum) mobj_t *mobj; if (p->playerstate == PST_REBORN) - G_PlayerReborn(playernum); + G_PlayerReborn(playernum, false); // spawn as spectator determination if (!G_GametypeHasSpectators()) diff --git a/src/p_setup.c b/src/p_setup.c index 0753f01d3..c83c8cd5c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2246,6 +2246,8 @@ static void P_LevelInitStuff(void) for (i = 0; i < MAXPLAYERS; i++) { + G_PlayerReborn(i, true); + if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0)) { // In Co-Op, replenish a user's lives if they are depleted. @@ -2253,42 +2255,18 @@ static void P_LevelInitStuff(void) } // obliteration station... - players[i].spheres =\ - players[i].xtralife = players[i].deadtimer =\ - players[i].numboxes = players[i].totalring =\ - players[i].laps = players[i].aiming =\ - players[i].losstime = players[i].timeshit =\ - players[i].marescore = players[i].lastmarescore =\ - players[i].maxlink = players[i].startedtime =\ - players[i].finishedtime = players[i].finishedspheres =\ - players[i].finishedrings = players[i].lastmare =\ - players[i].lastmarelap = players[i].lastmarebonuslap =\ - players[i].totalmarelap = players[i].totalmarebonuslap =\ - players[i].marebegunat = players[i].textvar =\ - players[i].texttimer = players[i].linkcount =\ - players[i].linktimer = players[i].flyangle =\ - players[i].anotherflyangle = players[i].nightstime =\ - players[i].oldscale = players[i].mare = players[i].marelap =\ - players[i].marebonuslap = players[i].lapbegunat =\ - players[i].lapstartedtime = players[i].totalmarescore =\ - players[i].realtime = players[i].exiting = 0; + players[i].numboxes = players[i].totalring =\ + players[i].laps = players[i].marescore = players[i].lastmarescore =\ + players[i].mare = players[i].exiting = 0; - // i guess this could be part of the above but i feel mildly uncomfortable implicitly casting - players[i].gotcontinue = false; - - // aha, the first evidence this shouldn't be a memset! players[i].drillmeter = 40*20; - players[i].rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings); - P_ResetPlayer(&players[i]); // hit these too - players[i].pflags &= ~(PF_GAMETYPEOVER|PF_TRANSFERTOCLOSEST); - - // unset ALL the pointers. P_SetTarget isn't needed here because if this - // function is being called we're just going to clobber the data anyways - players[i].mo = players[i].followmobj = players[i].awayviewmobj =\ - players[i].capsule = players[i].axis1 = players[i].axis2 = players[i].drone = NULL; + players[i].pflags &= ~(PF_GAMETYPEOVER); } + + if (botingame) + CV_SetValue(&cv_analog2, true); } // From a5460c74512f071d4fedf501ce073897e0ecc86f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 17:24:22 +0100 Subject: [PATCH 145/196] Correct location of MT_CACTI10 and MT_CACTI11 in relation to their positions given in info.h and dehacked.c (fixes several ACZ-related crashes) --- src/info.c | 108 ++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/info.c b/src/info.c index b6d6d49a7..54573e94f 100644 --- a/src/info.c +++ b/src/info.c @@ -11687,6 +11687,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_CACTI10 + 1230, // doomednum + S_CACTI10, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SCENERY|MF_PAIN, // flags + S_NULL // raisestate + }, + + { // MT_CACTI11 + 1231, // doomednum + S_CACTI11, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SCENERY|MF_PAIN, // flags + S_NULL // raisestate + }, + { // MT_ARIDSIGN_CAUTION 1212, // doomednum S_ARIDSIGN_CAUTION, // spawnstate @@ -12308,60 +12362,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_CACTI10 - 1230, // doomednum - S_CACTI10, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 16*FRACUNIT, // radius - 64*FRACUNIT, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - MF_SOLID|MF_SCENERY|MF_PAIN, // flags - S_NULL // raisestate - }, - - { // MT_CACTI11 - 1231, // doomednum - S_CACTI11, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - MF_SOLID|MF_SCENERY|MF_PAIN, // flags - S_NULL // raisestate - }, - { // MT_FLAMEJET 1300, // doomednum S_FLAMEJETSTND, // spawnstate From f870b5237f5dabbb80fd7778e35745e31a1ecb81 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 18 Sep 2019 17:48:41 +0100 Subject: [PATCH 146/196] * Fix native MF_PAIN sound extension being limited to UINT8 range, which only became evident when the new s1/2/cd/3db/kc sfx were added. * Make the pain-causing cactodes have DMG_SPIKE. --- src/info.c | 16 ++++++++-------- src/p_map.c | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/info.c b/src/info.c index 54573e94f..0fea743f6 100644 --- a/src/info.c +++ b/src/info.c @@ -11599,7 +11599,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 20*FRACUNIT, // radius 128*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11624,9 +11624,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 24*FRACUNIT, // radius - 224*FRACUNIT, // height + 224*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11651,9 +11651,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 24*FRACUNIT, // radius - 256*FRACUNIT, // height + 256*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11680,7 +11680,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 48*FRACUNIT, // radius 96*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11707,7 +11707,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16*FRACUNIT, // radius 64*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11734,7 +11734,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16*FRACUNIT, // radius 32*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags diff --git a/src/p_map.c b/src/p_map.c index 28bfd2806..27e8f2f7d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1019,7 +1019,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath if (tmthing->flags & MF_SHOOTABLE && thing->health > 0) { - UINT8 damagetype = (thing->info->mass & 0xFF); + UINT32 damagetype = (thing->info->mass & 0xFF); if (!damagetype && thing->flags & MF_FIRE) // BURN! damagetype = DMG_FIRE; if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8))) @@ -1036,7 +1036,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath if (thing->flags & MF_SHOOTABLE && tmthing->health > 0) { - UINT8 damagetype = (tmthing->info->mass & 0xFF); + UINT32 damagetype = (tmthing->info->mass & 0xFF); if (!damagetype && tmthing->flags & MF_FIRE) // BURN! damagetype = DMG_FIRE; if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8))) From c81452211d23b7feb31e3c570a214d3614d36109 Mon Sep 17 00:00:00 2001 From: sphere Date: Wed, 18 Sep 2019 21:40:33 +0200 Subject: [PATCH 147/196] Updated ACZ cacti. --- src/dehacked.c | 4 ++ src/info.c | 128 +++++++++++++++++++++++++++++++++++-------------- src/info.h | 30 +++++++----- 3 files changed, 113 insertions(+), 49 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index b19607d8e..a65a2249b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5744,6 +5744,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CACTI9", "S_CACTI10", "S_CACTI11", + "S_CACTITINYSEG", + "S_CACTISMALLSEG", // Warning signs sprites "S_ARIDSIGN_CAUTION", @@ -7530,6 +7532,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CACTI9", "MT_CACTI10", "MT_CACTI11", + "MT_CACTITINYSEG", + "MT_CACTISMALLSEG", "MT_ARIDSIGN_CAUTION", "MT_ARIDSIGN_CACTI", "MT_ARIDSIGN_SHARPTURN", diff --git a/src/info.c b/src/info.c index 96c5ebb0f..10ace455d 100644 --- a/src/info.c +++ b/src/info.c @@ -2330,18 +2330,20 @@ state_t states[NUMSTATES] = {SPR_STBL, 6, 5, {NULL}, 0, 0, S_LITTLETUMBLEWEED_ROLL8}, // S_LITTLETUMBLEWEED_ROLL7 {SPR_STBL, 7, 5, {NULL}, 0, 0, S_LITTLETUMBLEWEED_ROLL1}, // S_LITTLETUMBLEWEED_ROLL8 - // Cacti Sprites - {SPR_CACT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI1 - {SPR_CACT, 1, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI2 - {SPR_CACT, 2, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI3 - {SPR_CACT, 3, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI4 + // Cacti + {SPR_CACT, 0, -1, {A_ConnectToGround}, MT_CACTITINYSEG, 0, S_NULL}, // S_CACTI1 + {SPR_CACT, 1, -1, {A_ConnectToGround}, MT_CACTISMALLSEG, 0, S_NULL}, // S_CACTI2 + {SPR_CACT, 2, -1, {A_ConnectToGround}, MT_CACTITINYSEG, 0, S_NULL}, // S_CACTI3 + {SPR_CACT, 3, -1, {A_ConnectToGround}, MT_CACTISMALLSEG, 0, S_NULL}, // S_CACTI4 {SPR_CACT, 4, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI5 {SPR_CACT, 5, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI6 {SPR_CACT, 6, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI7 {SPR_CACT, 7, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI8 {SPR_CACT, 8, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI9 - {SPR_CACT, 9, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI10 - {SPR_CACT, 10, -1, {NULL}, 0, 0, S_NULL}, // S_CACTI11 + {SPR_CACT, 9, -1, {A_ConnectToGround}, MT_CACTITINYSEG, 0, S_NULL}, // S_CACTI10 + {SPR_CACT, 10, -1, {A_ConnectToGround}, MT_CACTISMALLSEG, 0, S_NULL}, // S_CACTI11 + {SPR_CACT, 11, -1, {NULL}, 0, 0, S_NULL}, // S_CACTITINYSEG + {SPR_CACT, 12, -1, {NULL}, 0, 0, S_NULL}, // S_CACTISMALLSEG // Warning Signs {SPR_WWSG, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_ARIDSIGN_CAUTION @@ -11522,13 +11524,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height + 13*FRACUNIT, // radius + 24*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags S_NULL // raisestate }, @@ -11549,13 +11551,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 64*FRACUNIT, // height + 15*FRACUNIT, // radius + 52*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags S_NULL // raisestate }, @@ -11576,13 +11578,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height + 13*FRACUNIT, // radius + 24*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags S_NULL // raisestate }, @@ -11603,13 +11605,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 80*FRACUNIT, // height + 15*FRACUNIT, // radius + 52*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags S_NULL // raisestate }, @@ -11633,7 +11635,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 32*FRACUNIT, // radius 96*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11660,7 +11662,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 20*FRACUNIT, // radius 128*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11687,7 +11689,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 24*FRACUNIT, // radius 224*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11714,7 +11716,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 24*FRACUNIT, // radius 256*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11741,7 +11743,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 48*FRACUNIT, // radius 96*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SCENERY|MF_PAIN, // flags @@ -11765,13 +11767,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 64*FRACUNIT, // height + 13*FRACUNIT, // radius + 28*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SCENERY|MF_PAIN, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags S_NULL // raisestate }, @@ -11792,13 +11794,67 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height + 15*FRACUNIT, // radius + 60*FRACUNIT, // height 0, // display offset - 100, // mass + DMG_SPIKE, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SCENERY|MF_PAIN, // flags + MF_SCENERY|MF_PAIN|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags + S_NULL // raisestate + }, + + { // MT_CACTITINYSEG + -1, // doomednum + S_CACTITINYSEG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 13*FRACUNIT, // radius + 28*FRACUNIT, // height + 0, // display offset + DMG_SPIKE, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_CACTISMALLSEG + -1, // doomednum + S_CACTISMALLSEG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 15*FRACUNIT, // radius + 60*FRACUNIT, // height + 0, // display offset + DMG_SPIKE, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index ae979df31..e125b05f3 100644 --- a/src/info.h +++ b/src/info.h @@ -2445,7 +2445,7 @@ typedef enum state S_LITTLETUMBLEWEED_ROLL7, S_LITTLETUMBLEWEED_ROLL8, - // Cacti Sprites + // Cacti S_CACTI1, S_CACTI2, S_CACTI3, @@ -2457,8 +2457,10 @@ typedef enum state S_CACTI9, S_CACTI10, S_CACTI11, + S_CACTITINYSEG, + S_CACTISMALLSEG, - // Warning signs sprites + // Warning signs S_ARIDSIGN_CAUTION, S_ARIDSIGN_CACTI, S_ARIDSIGN_SHARPTURN, @@ -4254,17 +4256,19 @@ typedef enum mobj_type // Arid Canyon Scenery MT_BIGTUMBLEWEED, MT_LITTLETUMBLEWEED, - MT_CACTI1, - MT_CACTI2, - MT_CACTI3, - MT_CACTI4, - MT_CACTI5, // Harmful Cactus 1 - MT_CACTI6, // Harmful Cactus 2 - MT_CACTI7, // Harmful Cactus 3 - MT_CACTI8, // Harmful Cactus 4 - MT_CACTI9, // Harmful Cactus 5 - MT_CACTI10, // Harmful Cactus 6 - MT_CACTI11, // Harmful Cactus 7 + MT_CACTI1, // Tiny Red Flower Cactus + MT_CACTI2, // Small Red Flower Cactus + MT_CACTI3, // Tiny Blue Flower Cactus + MT_CACTI4, // Small Blue Flower Cactus + MT_CACTI5, // Prickly Pear + MT_CACTI6, // Barrel Cactus + MT_CACTI7, // Tall Barrel Cactus + MT_CACTI8, // Armed Cactus + MT_CACTI9, // Ball Cactus + MT_CACTI10, // Tiny Cactus + MT_CACTI11, // Small Cactus + MT_CACTITINYSEG, // Tiny Cactus Segment + MT_CACTISMALLSEG, // Small Cactus Segment MT_ARIDSIGN_CAUTION, // Caution Sign MT_ARIDSIGN_CACTI, // Cacti Sign MT_ARIDSIGN_SHARPTURN, // Sharp Turn Sign From 5015f8e14272c922fc002897668019d1a49239b5 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 19 Sep 2019 17:34:04 +0800 Subject: [PATCH 148/196] Added Camera Options submenu (provided my files aren't broken again (please D:)) --- src/m_menu.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- src/m_menu.h | 4 +++ src/p_user.c | 4 +-- 3 files changed, 73 insertions(+), 20 deletions(-) mode change 100644 => 100755 src/m_menu.c mode change 100644 => 100755 src/m_menu.h diff --git a/src/m_menu.c b/src/m_menu.c old mode 100644 new mode 100755 index e9c5715fc..4350d6c73 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -275,9 +275,10 @@ menu_t MP_MainDef; // Split into multiple parts due to size // Controls menu_t OP_ChangeControlsDef; -menu_t OP_MPControlsDef, OP_CameraControlsDef, OP_MiscControlsDef; +menu_t OP_MPControlsDef, OP_MiscControlsDef; menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef; menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def; +menu_t OP_CameraOptionsDef, OP_Camera2OptionsDef; static void M_VideoModeMenu(INT32 choice); static void M_Setup1PControlsMenu(INT32 choice); static void M_Setup2PControlsMenu(INT32 choice); @@ -1002,14 +1003,11 @@ static menuitem_t OP_P1ControlsMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Mouse Options...", &OP_MouseOptionsDef, 20}, {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Joystick1Def , 30}, - {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam , 50}, - {IT_STRING | IT_CVAR, NULL, "Third-person Orbital" , &cv_cam_orbit , 60}, - {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 70}, - {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 80}, + {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_CameraOptionsDef, 50}, //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog, 100}, - {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar, 100}, - {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake, 110}, + {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar, 70}, + {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake, 80}, }; static menuitem_t OP_P2ControlsMenu[] = @@ -1018,14 +1016,11 @@ static menuitem_t OP_P2ControlsMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Second Mouse Options...", &OP_Mouse2OptionsDef, 20}, {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Joystick2Def , 30}, - {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam2 , 50}, - {IT_STRING | IT_CVAR, NULL, "Third-person Orbital" , &cv_cam2_orbit , 60}, - {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 70}, - {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 80}, + {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_Camera2OptionsDef, 50}, //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog2, 100}, - {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar2, 100}, - {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake2, 110}, + {IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar2, 70}, + {IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake2, 80}, }; static menuitem_t OP_ChangeControlsMenu[] = @@ -1154,6 +1149,34 @@ static menuitem_t OP_Mouse2OptionsMenu[] = NULL, "Mouse Y Sensitivity", &cv_mouseysens2, 80}, }; +static menuitem_t OP_CameraOptionsMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam , 10}, + {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 20}, + {IT_STRING | IT_CVAR, NULL, "Orbital Looking" , &cv_cam_orbit , 30}, + {IT_STRING | IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam_adjust, 40}, + + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam_dist, 60}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam_height, 70}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Speed", &cv_cam_speed, 80}, + + {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 100}, +}; + +static menuitem_t OP_Camera2OptionsMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Third-person Camera" , &cv_chasecam2 , 10}, + {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 20}, + {IT_STRING | IT_CVAR, NULL, "Orbital Looking" , &cv_cam2_orbit , 30}, + {IT_STRING | IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam2_adjust, 40}, + + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam2_dist, 60}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam2_height, 70}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Speed", &cv_cam2_speed, 80}, + + {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 100}, +}; + static menuitem_t OP_VideoOptionsMenu[] = { {IT_HEADER, NULL, "Screen", NULL, 0}, @@ -1879,6 +1902,13 @@ menu_t OP_JoystickSetDef = 0, NULL }; +menu_t OP_CameraOptionsDef = DEFAULTMENUSTYLE( + MN_OP_MAIN + (MN_OP_P1CONTROLS << 6) + (MN_OP_P1CAMERA << 12), + "M_CONTRO", OP_CameraOptionsMenu, &OP_P1ControlsDef, 35, 30); +menu_t OP_Camera2OptionsDef = DEFAULTMENUSTYLE( + MN_OP_MAIN + (MN_OP_P2CONTROLS << 6) + (MN_OP_P2CAMERA << 12), + "M_CONTRO", OP_Camera2OptionsMenu, &OP_P2ControlsDef, 35, 30); + menu_t OP_VideoOptionsDef = { @@ -2740,13 +2770,27 @@ static void M_ChangeCvar(INT32 choice) ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER) ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD)) { - CV_SetValue(cv,cv->value+(choice)); + if (cv->flags & CV_FLOAT && (currentMenu->menuitems[itemOn].status & IT_CV_FLOATSLIDER) == IT_CV_FLOATSLIDER) + { + char s[20]; + sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); + CV_Set(cv,s); + } + else + CV_SetValue(cv,cv->value+(choice)); } else if (cv->flags & CV_FLOAT) { - char s[20]; - sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); - CV_Set(cv,s); + if (currentMenu->menuitems[itemOn].status & IT_CV_INTEGERSTEP) + { + CV_SetValue(cv,FIXED_TO_FLOAT(cv->value)+(choice)); + } + else + { + char s[20]; + sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); + CV_Set(cv,s); + } } else CV_AddValue(cv,choice); @@ -3636,7 +3680,12 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop) for (i = 0; cv->PossibleValue[i+1].strvalue; i++); - if ((range = atoi(cv->defaultvalue)) != cv->value) + if (cv->flags & CV_FLOAT) + range = (INT32)(atof(cv->defaultvalue)*FRACUNIT); + else + range = atoi(cv->defaultvalue); + + if (range != cv->value) { range = ((range - cv->PossibleValue[0].value) * 100 / (cv->PossibleValue[i].value - cv->PossibleValue[0].value)); diff --git a/src/m_menu.h b/src/m_menu.h old mode 100644 new mode 100755 index 347725e10..67b1ddb94 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -72,10 +72,12 @@ typedef enum MN_OP_P1MOUSE, MN_OP_P1JOYSTICK, MN_OP_JOYSTICKSET, // OP_JoystickSetDef shared with P2 + MN_OP_P1CAMERA, MN_OP_P2CONTROLS, MN_OP_P2MOUSE, MN_OP_P2JOYSTICK, + MN_OP_P2CAMERA, MN_OP_VIDEO, MN_OP_VIDEOMODE, @@ -242,6 +244,8 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); #define IT_CV_NOPRINT 1536 #define IT_CV_NOMOD 2048 #define IT_CV_INVISSLIDER 2560 +#define IT_CV_INTEGERSTEP 4096 // if IT_CV_NORMAL and cvar is CV_FLOAT, modify it by 1 instead of 0.0625 +#define IT_CV_FLOATSLIDER 4608 // IT_CV_SLIDER, value modified by 0.0625 instead of 1 (for CV_FLOAT cvars) //call/submenu specific // There used to be a lot more here but ... diff --git a/src/p_user.c b/src/p_user.c index 13450bc9f..22cc919ab 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9295,7 +9295,7 @@ consvar_t cv_cam_speed = {"cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NUL 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_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_adjust = {"cam_adjust", "Off", CV_SAVE|CV_SHOWMODIF, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cam_adjust = {"cam_adjust", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_dist = {"cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_height = {"cam2_height", "25", CV_FLOAT|CV_SAVE, 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}; @@ -9303,7 +9303,7 @@ consvar_t cv_cam2_speed = {"cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, N 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_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_adjust = {"cam2_adjust", "Off", CV_SAVE|CV_SHOWMODIF, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cam2_adjust = {"cam2_adjust", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; fixed_t t_cam_dist = -42; fixed_t t_cam_height = -42; From cf14d5fe5d4290f52e4ea04519edcc705ac6750a Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 19 Sep 2019 12:30:30 +0100 Subject: [PATCH 149/196] * Remove sounds from swimming. * Lower default swim animation speed. * Make speed of flight/swim animation increase whenever mashing jump. --- src/info.c | 2 +- src/p_user.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 0fea743f6..05204fc86 100644 --- a/src/info.c +++ b/src/info.c @@ -739,7 +739,7 @@ state_t states[NUMSTATES] = // CA_FLY/CA_SWIM {SPR_PLAY, SPR2_FLY , 2, {NULL}, 0, 0, S_PLAY_FLY}, // S_PLAY_FLY - {SPR_PLAY, SPR2_SWIM, 2, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM + {SPR_PLAY, SPR2_SWIM, 4, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM {SPR_PLAY, SPR2_TIRE, 12, {NULL}, 0, 0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED // CA_GLIDEANDCLIMB diff --git a/src/p_user.c b/src/p_user.c index e0a4d710c..83c154fdf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8180,12 +8180,18 @@ static void P_MovePlayer(player_t *player) if (P_MobjFlip(player->mo)*player->mo->momz < FixedMul(5*actionspd, player->mo->scale)) P_SetObjectMomZ(player->mo, actionspd/2, true); + P_SetPlayerMobjState(player->mo, player->mo->state->nextstate); + player->fly1--; } } // Tails Put-Put noise - if (player->charability == CA_FLY && player->bot != 1 && leveltime % 10 == 0 && !player->spectator) + if (player->charability == CA_FLY + && player->bot != 1 + && !(player->mo->eflags & MFE_UNDERWATER) + && leveltime % 10 == 0 + && !player->spectator) S_StartSound(player->mo, sfx_putput); // Descend @@ -8202,6 +8208,7 @@ static void P_MovePlayer(player_t *player) if (player->charability == CA_FLY && (leveltime % 10 == 0) && player->mo->state-states == S_PLAY_FLY_TIRED + && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator) S_StartSound(player->mo, sfx_pudpud); } @@ -10736,8 +10743,10 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) } } +#if 0 if (player->fly1 != 0 && player->powers[pw_tailsfly] != 0 && !smilesonground) P_SetMobjState(tails, chosenstate); +#endif // animation... if (player->panim == PA_SPRING || player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE) @@ -10752,7 +10761,7 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) else if (player->mo->state-states == S_PLAY_GASP) tails->tics = -1; else if (player->mo->sprite2 == SPR2_TIRE) - ticnum = 4; + ticnum = (doswim ? 2 : 4); else if (player->panim != PA_IDLE) ticnum = player->mo->tics; From ca9e6e31dab7ee71a74c68e3a1dd992df933fb46 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 19 Sep 2019 13:20:05 +0100 Subject: [PATCH 150/196] * Resolve #224 (landing frames on ceiling contact). * Fix some other landing weirdness with CA_BOUNCE. --- src/p_map.c | 7 +- src/p_user.c | 283 ++++++++++++++++++++++++++------------------------- 2 files changed, 146 insertions(+), 144 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index dfac3ae05..01c41131b 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2881,11 +2881,8 @@ static boolean P_ThingHeightClip(mobj_t *thing) thing->z = thing->ceilingz - thing->height; } - if (thing->z != oldz) - { - if (thing->player) - P_PlayerHitFloor(thing->player, !onfloor); - } + if (P_MobjFlip(thing)*(thing->z - oldz) > 0 && thing->player) + P_PlayerHitFloor(thing->player, !onfloor); // debug: be sure it falls to the floor thing->eflags &= ~MFE_ONGROUND; diff --git a/src/p_user.c b/src/p_user.c index 83c154fdf..572cf01fb 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2217,148 +2217,153 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) player->pflags &= ~PF_SPINNING; } - if (player->pflags & PF_SPINNING) - { - if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH)) - { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_spin); - } - } - else if (player->pflags & PF_GLIDING) // ground gliding - { - if (dorollstuff) - { - player->skidtime = TICRATE; - player->mo->tics = -1; - } - else if (!player->skidtime) - player->pflags &= ~PF_GLIDING; - } - else if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) - { - if (player->mo->state-states != S_PLAY_MELEE_LANDING) - { - mobjtype_t type = player->revitem; - P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING); - player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; - S_StartSound(player->mo, sfx_s3k8b); - player->pflags |= PF_FULLSTASIS; - - // hearticles - if (type) - { - UINT8 i = 0; - angle_t throwang = -(2*ANG30); - fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale); - fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale); - fixed_t zo = 6*player->mo->scale; - fixed_t mu = FixedMul(player->maxdash, player->mo->scale); - fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy); - fixed_t ev; - mobj_t *missile = NULL; - if (mu2 < mu) - mu2 = mu; - ev = (50*FRACUNIT - (mu/25))/50; - while (i < 5) - { - missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type); - P_SetTarget(&missile->target, player->mo); - missile->angle = throwang + player->drawangle; - P_Thrust(missile, player->drawangle + ANGLE_90, - P_ReturnThrustY(missile, throwang, mu)); // side to side component - P_Thrust(missile, player->drawangle, mu2); // forward component - P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true); - missile->momz += player->mo->pmomz; - missile->fuse = TICRATE/2; - missile->extravalue2 = ev; - - i++; - throwang += ANG30; - } - if (mobjinfo[type].seesound && missile) - S_StartSound(missile, missile->info->seesound); - } - } - } - else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2) - ; - else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH) - { - if (player->cmomx || player->cmomy) - { - if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); - else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) - && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); - else if ((player->rmomx || player->rmomy) - && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); - else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); - } - else - { - if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); - else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) - && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); - else if ((player->mo->momx || player->mo->momy) - && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); - else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); - } - } - - if (!(player->pflags & PF_GLIDING)) - player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE); - player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/); - player->secondjump = 0; - player->glidetime = 0; - player->climbing = 0; - player->powers[pw_tailsfly] = 0; - - if (player->pflags & PF_SHIELDABILITY) - { - player->pflags &= ~PF_SHIELDABILITY; - - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack. - { - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound - S_StartSound(player->mo, sfx_s3k4c); - else // create a fire pattern on the ground - { - S_StartSound(player->mo, sfx_s3k47); - P_ElementalFire(player, true); - } - P_SetObjectMomZ(player->mo, - (player->mo->eflags & MFE_UNDERWATER) - ? 6*FRACUNIT/5 - : 5*FRACUNIT/2, - false); - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); - player->mo->momx = player->mo->momy = 0; - clipmomz = false; - } - else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack. - { - P_DoBubbleBounce(player); - clipmomz = false; - } - } - if (player->pflags & PF_BOUNCING) { - P_MobjCheckWater(player->mo); - player->mo->momz *= -1; - P_DoAbilityBounce(player, true); - if (player->scoreadd) - player->scoreadd--; + if (dorollstuff && player->mo->state-states != S_PLAY_BOUNCE_LANDING) + { + P_MobjCheckWater(player->mo); + player->mo->momz *= -1; + P_DoAbilityBounce(player, true); + if (player->scoreadd) + player->scoreadd--; + } clipmomz = false; } + else + { + if (player->pflags & PF_SPINNING) + { + if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH)) + { + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + S_StartSound(player->mo, sfx_spin); + } + } + else if (player->pflags & PF_GLIDING) // ground gliding + { + if (dorollstuff) + { + player->skidtime = TICRATE; + player->mo->tics = -1; + } + else if (!player->skidtime) + player->pflags &= ~PF_GLIDING; + } + else if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) + { + if (player->mo->state-states != S_PLAY_MELEE_LANDING) + { + mobjtype_t type = player->revitem; + P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING); + player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; + S_StartSound(player->mo, sfx_s3k8b); + player->pflags |= PF_FULLSTASIS; + + // hearticles + if (type) + { + UINT8 i = 0; + angle_t throwang = -(2*ANG30); + fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale); + fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale); + fixed_t zo = 6*player->mo->scale; + fixed_t mu = FixedMul(player->maxdash, player->mo->scale); + fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy); + fixed_t ev; + mobj_t *missile = NULL; + if (mu2 < mu) + mu2 = mu; + ev = (50*FRACUNIT - (mu/25))/50; + while (i < 5) + { + missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type); + P_SetTarget(&missile->target, player->mo); + missile->angle = throwang + player->drawangle; + P_Thrust(missile, player->drawangle + ANGLE_90, + P_ReturnThrustY(missile, throwang, mu)); // side to side component + P_Thrust(missile, player->drawangle, mu2); // forward component + P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true); + missile->momz += player->mo->pmomz; + missile->fuse = TICRATE/2; + missile->extravalue2 = ev; + + i++; + throwang += ANG30; + } + if (mobjinfo[type].seesound && missile) + S_StartSound(missile, missile->info->seesound); + } + } + } + else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2) + ; + else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH) + { + if (player->cmomx || player->cmomy) + { + if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) + P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) + && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) + P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + else if ((player->rmomx || player->rmomy) + && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) + P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE) + P_SetPlayerMobjState(player->mo, S_PLAY_STND); + } + else + { + if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) + P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) + && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) + P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + else if ((player->mo->momx || player->mo->momy) + && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) + P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE) + P_SetPlayerMobjState(player->mo, S_PLAY_STND); + } + } + + if (!(player->pflags & PF_GLIDING)) + player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE); + player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/); + player->secondjump = 0; + player->glidetime = 0; + player->climbing = 0; + player->powers[pw_tailsfly] = 0; + + if (player->pflags & PF_SHIELDABILITY) + { + player->pflags &= ~PF_SHIELDABILITY; + + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack. + { + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound + S_StartSound(player->mo, sfx_s3k4c); + else // create a fire pattern on the ground + { + S_StartSound(player->mo, sfx_s3k47); + P_ElementalFire(player, true); + } + P_SetObjectMomZ(player->mo, + (player->mo->eflags & MFE_UNDERWATER) + ? 6*FRACUNIT/5 + : 5*FRACUNIT/2, + false); + P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + player->mo->momx = player->mo->momy = 0; + clipmomz = false; + } + else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack. + { + P_DoBubbleBounce(player); + clipmomz = false; + } + } + } } return clipmomz; @@ -8080,7 +8085,7 @@ static void P_MovePlayer(player_t *player) } else if (player->pflags & PF_BOUNCING) { - if (!(player->pflags & PF_JUMPDOWN) || (onground && P_MobjFlip(player->mo)*player->mo->momz <= 0)) // If not holding the jump button OR on flat ground + if (!(player->pflags & PF_JUMPDOWN)) // If not holding the jump button { P_ResetPlayer(player); // down, stop bouncing. player->pflags |= PF_THOKKED; From f8475eef26f4197d32a55cff19ded58e2fbdff3d Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 19 Sep 2019 13:20:52 +0100 Subject: [PATCH 151/196] Disable some code which sometimes makes the player enter walking frames when jumping up to a platform, such as the CEZ3 buttons, despite still being in jumping mode. If this causes more problems than it solves it can be reverted, but doing a bunch of playthroughs of DSZ1/2, GFZ1, and CEZ3 didn't seem to uncover anything, so I'm tentatively putting it in this branch. --- src/p_mobj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 994ddcfe7..4cede843c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4001,6 +4001,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) } else { +#if 0 // i don't know why this is here, it's causing a few undesired state glitches, and disabling it doesn't appear to negatively affect the game, but i don't want it gone permanently just in case some obscure bug crops up if (!(mobj->player->powers[pw_carry] == CR_NIGHTSMODE)) // used for drilling mobj->player->pflags &= ~PF_STARTJUMP; mobj->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE); @@ -4010,6 +4011,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) mobj->player->powers[pw_tailsfly] = 0; P_SetPlayerMobjState(mobj, S_PLAY_WALK); } +#endif mobj->eflags &= ~MFE_JUSTHITFLOOR; } From a3ea2274f42e9a7b4ab023c850dbc4e2f62dc629 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 19 Sep 2019 17:43:23 +0100 Subject: [PATCH 152/196] Fix inconsistency between score tally screen and timerres cvar (Resolves #213). --- src/y_inter.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/y_inter.c b/src/y_inter.c index 1cb1b9cd8..0d6a3d03c 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -261,7 +261,7 @@ void Y_IntermissionDrawer(void) // draw time ST_DrawPatchFromHud(HUD_TIME, sbotime); - if (cv_timetic.value == 1) + if (cv_timetic.value == 3) ST_DrawNumFromHud(HUD_SECONDS, data.coop.tics); else { @@ -275,8 +275,7 @@ void Y_IntermissionDrawer(void) ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon); // Colon ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2); // Seconds - // we should show centiseconds on the intermission screen too, if the conditions are right. - if (modeattacking || cv_timetic.value == 2) + if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking) // there's not enough room for tics in splitscreen, don't even bother trying! { ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod); // Period ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2); // Tics From 802ddf94b15e6d4b1c5513f0a7cae554357727dd Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 19 Sep 2019 18:44:55 +0100 Subject: [PATCH 153/196] Always pretend cv_playersforexit is 4 in co-op special stages. --- src/p_user.c | 5 +++-- src/st_stuff.c | 60 +++++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 572cf01fb..0438075f2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -10989,7 +10989,8 @@ void P_PlayerThink(player_t *player) if (player->exiting == 2 || countdown2 == 2) { - if (cv_playersforexit.value) // Count to be sure everyone's exited + UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value); + if (numneeded) // Count to be sure everyone's exited { INT32 i, total = 0, exiting = 0; @@ -11005,7 +11006,7 @@ void P_PlayerThink(player_t *player) exiting++; } - if (!total || ((4*exiting)/total) >= cv_playersforexit.value) + if (!total || ((4*exiting)/total) >= numneeded) { if (server) SendNetXCmd(XD_EXITLEVEL, NULL, 0); diff --git a/src/st_stuff.c b/src/st_stuff.c index c4f1327c0..20a132b3a 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2100,39 +2100,43 @@ static void ST_drawTextHUD(void) textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game")) } - if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting && cv_playersforexit.value) + if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting) { - INT32 i, total = 0, exiting = 0; - - for (i = 0; i < MAXPLAYERS; i++) + UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value); + if (numneeded) { - if (!playeringame[i] || players[i].spectator) - continue; - if (players[i].lives <= 0) - continue; + INT32 i, total = 0, exiting = 0; - total++; - if (players[i].exiting) - exiting++; - } - - if (cv_playersforexit.value != 4) - { - total *= cv_playersforexit.value; - if (total & 3) - total += 4; // round up - total /= 4; - } - - if (exiting < total) - { - if (!splitscreen && !donef12) + for (i = 0; i < MAXPLAYERS; i++) { - textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) - donef12 = true; + if (!playeringame[i] || players[i].spectator) + continue; + if (players[i].lives <= 0) + continue; + + total++; + if (players[i].exiting) + exiting++; + } + + if (numneeded != 4) + { + total *= cv_playersforexit.value; + if (total & 3) + total += 4; // round up + total /= 4; + } + + if (exiting < total) + { + if (!splitscreen && !donef12) + { + textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) + donef12 = true; + } + total -= exiting; + textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s"))) } - total -= exiting; - textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s"))) } } else if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator)) From 222d4f2b7c85efde262280dbca9476cc1de3f22f Mon Sep 17 00:00:00 2001 From: lachwright Date: Fri, 20 Sep 2019 02:02:17 +0800 Subject: [PATCH 154/196] Improved orbital camera in Software; cam_adjust cvars changed to on by default --- src/p_user.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 22cc919ab..c3f862952 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9295,7 +9295,7 @@ consvar_t cv_cam_speed = {"cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NUL 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_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam_adjust = {"cam_adjust", "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_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_height = {"cam2_height", "25", CV_FLOAT|CV_SAVE, 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}; @@ -9303,7 +9303,7 @@ consvar_t cv_cam2_speed = {"cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, N 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_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_cam2_adjust = {"cam2_adjust", "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}; fixed_t t_cam_dist = -42; fixed_t t_cam_height = -42; @@ -9359,7 +9359,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall angle_t angle = 0, focusangle = 0, focusaiming = 0; fixed_t x, y, z, dist, distxy, distz, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight, slopez = 0; INT32 camrotate; - boolean camstill, cameranoclip; + boolean camstill, cameranoclip, camorbit; mobj_t *mo; subsector_t *newsubsec; fixed_t f1, f2; @@ -9440,6 +9440,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // force defaults because we have a camera look section camspeed = (INT32)(atof(cv_cam_speed.defaultvalue) * FRACUNIT); camstill = (!stricmp(cv_cam_still.defaultvalue, "off")) ? false : true; + camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true; camrotate = atoi(cv_cam_rotate.defaultvalue); camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale); camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), FixedMul(player->camerascale, mo->scale)); @@ -9448,6 +9449,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { camspeed = cv_cam_speed.value; camstill = cv_cam_still.value; + camorbit = cv_cam_orbit.value; camrotate = cv_cam_rotate.value; camdist = FixedMul(cv_cam_dist.value, mo->scale); camheight = FixedMul(cv_cam_height.value, FixedMul(player->camerascale, mo->scale)); @@ -9456,6 +9458,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { camspeed = cv_cam2_speed.value; camstill = cv_cam2_still.value; + camorbit = cv_cam2_orbit.value; camrotate = cv_cam2_rotate.value; camdist = FixedMul(cv_cam2_dist.value, mo->scale); camheight = FixedMul(cv_cam2_height.value, FixedMul(player->camerascale, mo->scale)); @@ -9595,7 +9598,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) //Sev here, I'm guessing this is where orbital cam lives { - distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); + if (rendermode == render_opengl) + distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); + else + distxy = dist; distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)) + slopez; } else From 4c7a7a7e0b77c5beea1f09a07a47249014b8a261 Mon Sep 17 00:00:00 2001 From: sphere Date: Fri, 20 Sep 2019 00:51:44 +0200 Subject: [PATCH 155/196] Small DSZ coral tweaks. --- src/dehacked.c | 16 +++++----- src/info.c | 86 +++++++++++++++++++++++++++++++++++++++----------- src/info.h | 22 ++++++------- 3 files changed, 86 insertions(+), 38 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index a65a2249b..8334de61a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5578,14 +5578,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIPC1", "S_DRIPC2", - // Coral 1 + // Coral "S_CORAL1", - - // Coral 2 "S_CORAL2", - - // Coral 3 "S_CORAL3", + "S_CORAL4", + "S_CORAL5", // Blue Crystal "S_BLUECRYSTAL1", @@ -7464,9 +7462,11 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SEAWEED", // DSZ Seaweed "MT_WATERDRIP", // Dripping Water source "MT_WATERDROP", // Water drop from dripping water - "MT_CORAL1", // Coral 1 - "MT_CORAL2", // Coral 2 - "MT_CORAL3", // Coral 3 + "MT_CORAL1", // Coral + "MT_CORAL2", + "MT_CORAL3", + "MT_CORAL4", + "MT_CORAL5", "MT_BLUECRYSTAL", // Blue Crystal "MT_KELP", // Kelp "MT_ANIMALGAETOP", // Animated algae top diff --git a/src/info.c b/src/info.c index 10ace455d..e627b949d 100644 --- a/src/info.c +++ b/src/info.c @@ -212,9 +212,7 @@ char sprnames[NUMSPRITES + 1][5] = "GARG", // Deep Sea Gargoyle "SEWE", // Deep Sea Seaweed "DRIP", // Dripping water - "CRL1", // Coral 1 - "CRL2", // Coral 2 - "CRL3", // Coral 3 + "CORL", // Coral "BCRY", // Blue Crystal "KELP", // Kelp "ALGA", // Animated algae top @@ -248,7 +246,7 @@ char sprnames[NUMSPRITES + 1][5] = // Arid Canyon Scenery "BTBL", // Big tumbleweed "STBL", // Small tumbleweed - "CACT", // Cacti sprites + "CACT", // Cacti "WWSG", // Caution Sign "WWS2", // Cacti Sign "WWS3", // Sharp Turn Sign @@ -2166,14 +2164,12 @@ state_t states[NUMSTATES] = {SPR_DRIP, FF_TRANS30|4, 1, {NULL}, 0, 0, S_DRIPC2}, // S_DRIPC1 {SPR_DRIP, FF_TRANS30|5, 1, {NULL}, 0, 0, S_NULL}, // S_DRIPC2 - // Coral 1 - {SPR_CRL1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL1 - - // Coral 2 - {SPR_CRL2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL2 - - // Coral 3 - {SPR_CRL3, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL3 + // Coral + {SPR_CORL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL1 + {SPR_CORL, 1, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL2 + {SPR_CORL, 2, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL3 + {SPR_CORL, 3, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL4 + {SPR_CORL, 4, -1, {NULL}, 0, 0, S_NULL}, // S_CORAL5 // Blue Crystal {SPR_BCRY, FF_TRANS30, -1, {NULL}, 0, 0, S_NULL}, // S_BLUECRYSTAL1 @@ -10093,8 +10089,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 29*FRACUNIT, // radius + 40*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage @@ -10120,8 +10116,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 30*FRACUNIT, // radius + 53*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage @@ -10147,8 +10143,62 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 28*FRACUNIT, // radius + 41*FRACUNIT, // height + 0, // display offset + 4, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CORAL4 + 1014, // doomednum + S_CORAL4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 56*FRACUNIT, // radius + 112*FRACUNIT, // height + 0, // display offset + 4, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CORAL5 + 1015, // doomednum + S_CORAL5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 56*FRACUNIT, // radius + 112*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage diff --git a/src/info.h b/src/info.h index e125b05f3..74e4b87a2 100644 --- a/src/info.h +++ b/src/info.h @@ -458,9 +458,7 @@ typedef enum sprite SPR_GARG, // Deep Sea Gargoyle SPR_SEWE, // Deep Sea Seaweed SPR_DRIP, // Dripping water - SPR_CRL1, // Coral 1 - SPR_CRL2, // Coral 2 - SPR_CRL3, // Coral 3 + SPR_CORL, // Coral SPR_BCRY, // Blue Crystal SPR_KELP, // Kelp SPR_ALGA, // Animated algae top @@ -494,7 +492,7 @@ typedef enum sprite // Arid Canyon Scenery SPR_BTBL, // Big tumbleweed SPR_STBL, // Small tumbleweed - SPR_CACT, // Cacti sprites + SPR_CACT, // Cacti SPR_WWSG, // Caution Sign SPR_WWS2, // Cacti Sign SPR_WWS3, // Sharp Turn Sign @@ -2291,14 +2289,12 @@ typedef enum state S_DRIPC1, S_DRIPC2, - // Coral 1 + // Coral S_CORAL1, - - // Coral 2 S_CORAL2, - - // Coral 3 S_CORAL3, + S_CORAL4, + S_CORAL5, // Blue Crystal S_BLUECRYSTAL1, @@ -4199,9 +4195,11 @@ typedef enum mobj_type MT_SEAWEED, // DSZ Seaweed MT_WATERDRIP, // Dripping Water source MT_WATERDROP, // Water drop from dripping water - MT_CORAL1, // Coral 1 - MT_CORAL2, // Coral 2 - MT_CORAL3, // Coral 3 + MT_CORAL1, // Coral + MT_CORAL2, + MT_CORAL3, + MT_CORAL4, + MT_CORAL5, MT_BLUECRYSTAL, // Blue Crystal MT_KELP, // Kelp MT_ANIMALGAETOP, // Animated algae top From ef7e8c5d588f12f4827ad6dee870550b9d7a09c7 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 19 Sep 2019 22:40:23 -0300 Subject: [PATCH 156/196] use byteptr.h macros --- src/r_data.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index be27fecad..9c5a574b4 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -23,6 +23,7 @@ #include "z_zone.h" #include "p_setup.h" // levelflats #include "v_video.h" // pMasterPalette +#include "byteptr.h" #include "dehacked.h" #ifdef _WIN32 @@ -2819,18 +2820,14 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t UINT8 *imgptr = imgbuf; UINT8 *colpointers, *startofspan; - #define WRITE8(buf, a) ({*buf = (a); buf++;}) - #define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;}) - #define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);}) - if (!raw) I_Error("R_PNGToPatch: conversion failed"); // Write image size and offset - WRITE16(imgptr, width); - WRITE16(imgptr, height); - WRITE16(imgptr, leftoffset); - WRITE16(imgptr, topoffset); + WRITEINT16(imgptr, width); + WRITEINT16(imgptr, height); + WRITEINT16(imgptr, leftoffset); + WRITEINT16(imgptr, topoffset); // Leave placeholder to column pointers colpointers = imgptr; @@ -2845,7 +2842,7 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t //printf("%d ", x); // Write column pointer (@TODO may be wrong) - WRITE32(colpointers, imgptr - imgbuf); + WRITEINT32(colpointers, imgptr - imgbuf); // Write pixels for (y = 0; y < height; y++) @@ -2857,7 +2854,7 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t if (!opaque) { if (startofspan) - WRITE8(imgptr, 0); + WRITEUINT8(imgptr, 0); startofspan = NULL; continue; } @@ -2869,15 +2866,15 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t // If we reached the span size limit, finish the previous span if (startofspan) - WRITE8(imgptr, 0); + WRITEUINT8(imgptr, 0); if (y > 254) { // Make sure we're aligned to 254 if (lastStartY < 254) { - WRITE8(imgptr, 254); - WRITE8(imgptr, 0); + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); imgptr += 2; lastStartY = 254; } @@ -2887,15 +2884,15 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t while (writeY > 254) { - WRITE8(imgptr, 254); - WRITE8(imgptr, 0); + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); imgptr += 2; writeY -= 254; } } startofspan = imgptr; - WRITE8(imgptr, writeY);///@TODO calculate starting y pos + WRITEUINT8(imgptr, writeY);///@TODO calculate starting y pos imgptr += 2; spanSize = 0; @@ -2903,21 +2900,17 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t } // Write the pixel - WRITE8(imgptr, paletteIndex); + WRITEUINT8(imgptr, paletteIndex); spanSize++; startofspan[1] = spanSize; } if (startofspan) - WRITE8(imgptr, 0); + WRITEUINT8(imgptr, 0); - WRITE8(imgptr, 0xFF); + WRITEUINT8(imgptr, 0xFF); } - #undef WRITE8 - #undef WRITE16 - #undef WRITE32 - size = imgptr-imgbuf; img = Z_Malloc(size, PU_STATIC, NULL); memcpy(img, imgbuf, size); From 530b5784b8bc6021b9577ae48ad8877a5ff6a953 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Thu, 19 Sep 2019 21:42:59 -0400 Subject: [PATCH 157/196] platersprite_t is not the same as unsigned --- src/hardware/hw_md2.c | 2 +- src/r_things.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 7b6367cf3..db1c1f727 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1202,7 +1202,7 @@ static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *p if (!md2 || !skin) return 0; - if ((unsigned)(spr2 & ~FF_SPR2SUPER) >= free_spr2) + if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) return 0; while (!(md2->model->spr2frames[spr2*2 + 1]) diff --git a/src/r_things.c b/src/r_things.c index 392821869..43e006a30 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2524,7 +2524,7 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) if (!skin) return 0; - if ((unsigned)(spr2 & ~FF_SPR2SUPER) >= free_spr2) + if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) return 0; while (!(skin->sprites[spr2].numframes) From 19e0e43e87472764eba7bad17991c56714492a01 Mon Sep 17 00:00:00 2001 From: lachwright Date: Fri, 20 Sep 2019 12:07:57 +0800 Subject: [PATCH 158/196] test?? --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index c3f862952..415fcc1ed 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9596,7 +9596,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } } - if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) //Sev here, I'm guessing this is where orbital cam lives + if (camorbit) //Sev here, I'm guessing this is where orbital cam lives { if (rendermode == render_opengl) distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); From 8d3d5566b802b78952535be8572224abd84ba44e Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Fri, 20 Sep 2019 12:18:57 -0400 Subject: [PATCH 159/196] Undo file mode change on menu files --- src/m_menu.c | 0 src/m_menu.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/m_menu.c mode change 100755 => 100644 src/m_menu.h diff --git a/src/m_menu.c b/src/m_menu.c old mode 100755 new mode 100644 diff --git a/src/m_menu.h b/src/m_menu.h old mode 100755 new mode 100644 From 113568095a609c662e33e67f311c94e28be127d9 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 20 Sep 2019 17:22:09 +0100 Subject: [PATCH 160/196] * Resolve compiling issues with logmessages. * Improve logfile print. (I know Steel wanted it gone entirely, but I feel like it's relevant to have it in game..?) --- src/sdl/i_main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c index 2a67e88fe..029febc05 100644 --- a/src/sdl/i_main.c +++ b/src/sdl/i_main.c @@ -131,11 +131,12 @@ int main(int argc, char **argv) #ifdef LOGMESSAGES if (!M_CheckParm("-nolog")) { - logdir = D_Home(); - time_t my_time; struct tm * timeinfo; char buf[26]; + + logdir = D_Home(); + my_time = time(NULL); timeinfo = localtime(&my_time); @@ -183,8 +184,11 @@ int main(int argc, char **argv) // startup SRB2 CONS_Printf("Setting up SRB2...\n"); D_SRB2Main(); +#ifdef LOGMESSAGES + if (!M_CheckParm("-nolog")) + CONS_Printf("Logfile: %s\n", logfile); +#endif CONS_Printf("Entering main game loop...\n"); - CONS_Printf("%s\n", logfile); // never return D_SRB2Loop(); From 92779487a4130a244dbb90d12061a031ba5253d0 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Fri, 20 Sep 2019 12:32:18 -0400 Subject: [PATCH 161/196] avoid the source code from getting the wrong EOL --- .gitattributes | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitattributes b/.gitattributes index d45620912..7751149ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,12 @@ +#Source code +/src/*.c text=auto +/src/*.h text=auto +/src/*.s text=auto +/src/*.m text=auto +/src/*.xpm text=auto +/src/Makefile text=auto +/src/Make*.cfg text=auto +/src/CMakeLists.txt text=auto # Windows EOL *.cs -crlf -whitespace *.mk -crlf -whitespace From c36123aa5687c5151aad98fd1941412f7407ae52 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 20 Sep 2019 17:43:41 +0100 Subject: [PATCH 162/196] Mark new-style log names as loaded. --- src/filesrch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filesrch.c b/src/filesrch.c index 8f157bdd5..13d73b6f4 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -751,7 +751,7 @@ boolean preparefilemenu(boolean samedepth) } else if (ext == EXT_TXT) { - if (!strcmp(dent->d_name, "log.txt") || !strcmp(dent->d_name, "errorlog.txt")) + if (!strncmp(dent->d_name, "log-", 4) || !strcmp(dent->d_name, "errorlog.txt")) ext |= EXT_LOADED; } From f8c97aeb2775556b8b6e1c2167c668fffcf860b0 Mon Sep 17 00:00:00 2001 From: sphere Date: Sun, 22 Sep 2019 04:05:22 +0200 Subject: [PATCH 163/196] Tweak bubble grabbing behavior. --- src/p_inter.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 79491e245..bfdec3e23 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1701,13 +1701,15 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; if (mariomode) return; + if (special->state-states != S_EXTRALARGEBUBBLE) + return; // Don't grab the bubble during its spawn animation else if (toucher->eflags & MFE_VERTICALFLIP) { - if (special->z+special->height < toucher->z + toucher->height / 3 - || special->z+special->height > toucher->z + (toucher->height*2/3)) + if (special->z+special->height < toucher->z + || special->z+special->height > toucher->z + (toucher->height*2/3)) return; // Only go in the mouth } - else if (special->z < toucher->z + toucher->height / 3 + else if (special->z < toucher->z || special->z > toucher->z + (toucher->height*2/3)) return; // Only go in the mouth From f7ad5501e12db30beecc0eba7bf119280208ddd7 Mon Sep 17 00:00:00 2001 From: sphere Date: Sun, 22 Sep 2019 04:19:09 +0200 Subject: [PATCH 164/196] Increase emblem and token hitbox sizes. --- src/info.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/info.c b/src/info.c index dae028f62..da8022cd4 100644 --- a/src/info.c +++ b/src/info.c @@ -6504,8 +6504,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 16*FRACUNIT, // radius + 32*FRACUNIT, // height 0, // display offset 100, // mass 1, // damage @@ -6585,8 +6585,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_ncitem, // deathsound 1, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 16*FRACUNIT, // radius + 30*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage From 73146a833882f2041b0e59e26c6d764e9ac3aa5d Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 22 Sep 2019 20:30:07 -0400 Subject: [PATCH 165/196] Restore code that somehow got reverted??? How did this even happen?? --- src/hardware/hw_md2.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index db1c1f727..cd1b957f0 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -26,6 +26,7 @@ #include #include +#include "../d_main.h" #include "../doomdef.h" #include "../doomstat.h" #include "../fastcmp.h" @@ -70,6 +71,10 @@ #endif #endif +#ifndef errno +#include "errno.h" +#endif + #define NUMVERTEXNORMALS 162 float avertexnormals[NUMVERTEXNORMALS][3] = { {-0.525731f, 0.000000f, 0.850651f}, @@ -294,7 +299,8 @@ static md2_model_t *md2_readModel(const char *filename) if (model == NULL) return 0; - file = fopen(filename, "rb"); + //Filename checking fixed ~Monster Iestyn and Golden + file = fopen(va("%s"PATHSEP"%s", srb2home, filename), "rb"); if (!file) { free(model); @@ -523,7 +529,8 @@ static GrTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_ #endif #endif png_FILE_p png_FILE; - char *pngfilename = va("md2/%s", filename); + //Filename checking fixed ~Monster Iestyn and Golden + char *pngfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2home, filename); FIL_ForceExtension(pngfilename, ".png"); png_FILE = fopen(pngfilename, "rb"); @@ -651,7 +658,8 @@ static GrTextureFormat_t PCX_Load(const char *filename, int *w, int *h, size_t pw, ph, size, ptr = 0; INT32 ch, rep; FILE *file; - char *pcxfilename = va("md2/%s", filename); + //Filename checking fixed ~Monster Iestyn and Golden + char *pcxfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2home, filename); FIL_ForceExtension(pcxfilename, ".pcx"); file = fopen(pcxfilename, "rb"); @@ -845,11 +853,12 @@ void HWR_InitMD2(void) } // read the md2.dat file - f = fopen("md2.dat", "rt"); + //Filename checking fixed ~Monster Iestyn and Golden + f = fopen(va("%s"PATHSEP"%s", srb2home, "md2.dat"), "rt"); if (!f) { - CONS_Printf("%s", M_GetText("Error while loading md2.dat\n")); + CONS_Printf("%s %s\n", M_GetText("Error while loading md2.dat:"), strerror(errno)); nomd2s = true; return; } @@ -911,7 +920,8 @@ void HWR_AddPlayerMD2(int skin) // For MD2's that were added after startup CONS_Printf("AddPlayerMD2()...\n"); // read the md2.dat file - f = fopen("md2.dat", "rt"); + //Filename checking fixed ~Monster Iestyn and Golden + f = fopen(va("%s"PATHSEP"%s", srb2home, "md2.dat"), "rt"); if (!f) { @@ -956,7 +966,8 @@ void HWR_AddSpriteMD2(size_t spritenum) // For MD2s that were added after startu return; // Read the md2.dat file - f = fopen("md2.dat", "rt"); + //Filename checking fixed ~Monster Iestyn and Golden + f = fopen(va("%s"PATHSEP"%s", srb2home, "md2.dat"), "rt"); if (!f) { From 9c7b3f1d80527da6b31ddc8ca3fdc3649e487473 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 23 Sep 2019 18:25:16 -0300 Subject: [PATCH 166/196] patch fixes --- src/r_data.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 8594c8595..982966ea2 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -262,6 +262,8 @@ UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alph // if there's no pixel in here if (!background.rgba) output.s.alpha = foreground.s.alpha; + output.s.alpha = 0xFF; + return output.rgba; } #define clamp(c) max(min(c, 0xFF), 0x00); else @@ -310,15 +312,23 @@ UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alph UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha) { - if ((style == AST_TRANSLUCENT) && (alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? + // Alpha style set to translucent? + if (style == AST_TRANSLUCENT) { - UINT8 *mytransmap; - if (alpha < 255/11) // Is the patch way too translucent? Don't render then. - return background; - // The equation's not exact but it works as intended. I'll call it a day for now. - mytransmap = transtables + ((8*(alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); - if (background != 0xFF) - return *(mytransmap + (background<<8) + foreground); + // Is the alpha small enough for translucency? + if (alpha <= (10*255/11)) + { + UINT8 *mytransmap; + // Is the patch way too translucent? Don't blend then. + if (alpha < 255/11) + return background; + // The equation's not exact but it works as intended. I'll call it a day for now. + mytransmap = transtables + ((8*(alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); + if (background != 0xFF) + return *(mytransmap + (background<<8) + foreground); + } + else // just copy the pixel + return foreground; } // just copy the pixel else if (style == AST_COPY) @@ -375,8 +385,7 @@ static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpa if (count > 0) { for (; dest < cache + position + count; source++, dest++) - if (*dest != 0xFF) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); @@ -384,7 +393,7 @@ static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpa } // -// R_DrawTransColumnInCache +// 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) @@ -419,8 +428,7 @@ static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache if (count > 0) { for (; dest < cache + position + count; --source, dest++) - if (*dest != 0xFF) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); From 61aa84d15a5393cc7b0e8049ecd7ab8d628c5916 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 23 Sep 2019 18:27:43 -0300 Subject: [PATCH 167/196] pixel alpha mistake --- src/r_data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/r_data.c b/src/r_data.c index 982966ea2..f2ad42c6a 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -262,7 +262,8 @@ UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alph // if there's no pixel in here if (!background.rgba) output.s.alpha = foreground.s.alpha; - output.s.alpha = 0xFF; + else + output.s.alpha = 0xFF; return output.rgba; } #define clamp(c) max(min(c, 0xFF), 0x00); From d9507d62f8b3d0d69dcfb8d66e1de5716de5e8d1 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 23 Sep 2019 19:20:59 -0300 Subject: [PATCH 168/196] ignore TRANSPARENTPIXEL --- src/r_data.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index f2ad42c6a..b8b363021 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -386,7 +386,8 @@ static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpa if (count > 0) { for (; dest < cache + position + count; source++, dest++) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + if (*source != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); @@ -429,7 +430,8 @@ static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache if (count > 0) { for (; dest < cache + position + count; --source, dest++) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + if (*source != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); From 6a870b4467cd24b84db4d0edb8e306eb90fc56b6 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 23 Sep 2019 22:46:44 -0400 Subject: [PATCH 169/196] Various of changes Changed any instance of "joystick" in strings to "gamepad" Renamed some cvars Added a define for MAX_JOYSTICK Added back the missing command line params. --- src/d_netcmd.c | 16 ++++++++-------- src/doomdef.h | 3 +++ src/m_menu.c | 14 +++++++------- src/sdl/i_system.c | 18 +++++++++++++++--- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7d5963ecc..bd57c2481 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -246,20 +246,20 @@ 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_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, +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_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, +consvar_t cv_usejoystick2 = {"use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; #if (defined (LJOYSTICK) || defined (HAVE_SDL)) #ifdef LJOYSTICK -consvar_t cv_joyport = {"joyport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_joyport2 = {"joyport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: for later +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 #endif -consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, 0, NULL, NULL, 0, 0, NULL}; +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}; #else -consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save -consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save +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 #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}; diff --git a/src/doomdef.h b/src/doomdef.h index 4a0174369..8d44d0896 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -506,6 +506,9 @@ INT32 I_GetKey(void); #define max(x, y) (((x) > (y)) ? (x) : (y)) #endif +// Max gamepad/joysticks that can be detected/used. +#define MAX_JOYSTICKS 4 + // Floating point comparison epsilons from float.h #ifndef FLT_EPSILON #define FLT_EPSILON 1.1920928955078125e-7f diff --git a/src/m_menu.c b/src/m_menu.c index e1b2b5c90..1b52e400e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -143,7 +143,7 @@ typedef enum levellist_mode_t levellistmode = LLM_CREATESERVER; UINT8 maplistoption = 0; -static char joystickInfo[8][29]; +static char joystickInfo[MAX_JOYSTICKS][29]; #ifndef NONET static UINT32 serverlistpage; #endif @@ -9993,7 +9993,7 @@ static void M_DrawJoystick(void) // draw title (or big pic) M_DrawMenuTitle(); - for (i = 0; i <= 4; i++) // See MAX_JOYSTICKS + for (i = 0; i <= MAX_JOYSTICKS; i++) // See MAX_JOYSTICKS { M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); //M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i); @@ -10036,7 +10036,7 @@ void M_SetupJoystickMenu(INT32 choice) strcpy(joystickInfo[i], "None"); - for (i = 1; i < 8; i++) + for (i = 1; i < MAX_JOYSTICKS+1; i++) { if (i <= n && (I_GetJoyName(i)) != NULL) strncpy(joystickInfo[i], I_GetJoyName(i), 28); @@ -10113,8 +10113,8 @@ static void M_AssignJoystick(INT32 choice) if (oldstringchoice == (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) - M_StartMessage("This joystick is used by another\n" - "player. Reset the joystick\n" + M_StartMessage("This gamepad is used by another\n" + "player. Reset the gamepad\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } @@ -10143,8 +10143,8 @@ static void M_AssignJoystick(INT32 choice) if (oldstringchoice == (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) - M_StartMessage("This joystick is used by another\n" - "player. Reset the joystick\n" + M_StartMessage("This gamepad is used by another\n" + "player. Reset the gamepad\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 97690cf7e..d7926e5b2 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1397,10 +1397,16 @@ void I_InitJoystick(void) if (M_CheckParm("-nojoy")) return; + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { CONS_Printf("I_InitJoystick()...\n"); - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); @@ -1440,10 +1446,16 @@ void I_InitJoystick2(void) if (M_CheckParm("-nojoy")) return; + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { CONS_Printf("I_InitJoystick2()...\n"); - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); @@ -1499,7 +1511,7 @@ INT32 I_NumJoys(void) return numjoy; } -static char joyname[255]; // MAX_PATH; joystick name is straight from the driver +static char joyname[255]; // joystick name is straight from the driver const char *I_GetJoyName(INT32 joyindex) { From 5cdbde25c1313cac23f069f3a03bfd2352e82d6c Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 23 Sep 2019 20:23:40 -0700 Subject: [PATCH 170/196] All for a cvar... --- src/m_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 1b52e400e..fbc21db9e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -143,7 +143,7 @@ typedef enum levellist_mode_t levellistmode = LLM_CREATESERVER; UINT8 maplistoption = 0; -static char joystickInfo[MAX_JOYSTICKS][29]; +static char joystickInfo[MAX_JOYSTICKS+1][29]; #ifndef NONET static UINT32 serverlistpage; #endif @@ -10036,7 +10036,7 @@ void M_SetupJoystickMenu(INT32 choice) strcpy(joystickInfo[i], "None"); - for (i = 1; i < MAX_JOYSTICKS+1; i++) + for (i = 1; i <= MAX_JOYSTICKS; i++) { if (i <= n && (I_GetJoyName(i)) != NULL) strncpy(joystickInfo[i], I_GetJoyName(i), 28); From e69dd9bd75ae4fcfa949bda409c704c447374b43 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 23 Sep 2019 20:54:53 -0700 Subject: [PATCH 171/196] Automate the silly joystick menu items definition --- src/m_menu.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index fbc21db9e..ca7593e54 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1118,14 +1118,7 @@ static menuitem_t OP_Joystick2Menu[] = {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130}, }; -static menuitem_t OP_JoystickSetMenu[] = -{ - {IT_CALL | IT_NOTHING, "None", NULL, M_AssignJoystick, '0'}, - {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '1'}, - {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '2'}, - {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '3'}, - {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '4'}, -}; +static menuitem_t OP_JoystickSetMenu[1+MAX_JOYSTICKS]; static menuitem_t OP_MouseOptionsMenu[] = { @@ -3550,6 +3543,8 @@ void M_Ticker(void) // void M_Init(void) { + int i; + CV_RegisterVar(&cv_nextmap); CV_RegisterVar(&cv_newgametype); CV_RegisterVar(&cv_chooseskin); @@ -3599,6 +3594,17 @@ void M_Init(void) OP_ScreenshotOptionsMenu[op_screenshot_colorprofile].status = IT_GRAYEDOUT; #endif + /* + Well the menu sucks for forcing us to have an item set + at all if every item just calls the same function, and + nothing more. Now just automate the definition. + */ + for (i = 0; i <= MAX_JOYSTICKS; ++i) + { + OP_JoystickSetMenu[i].status = ( IT_NOTHING|IT_CALL ); + OP_JoystickSetMenu[i].itemaction = M_AssignJoystick; + } + #ifndef NONET CV_RegisterVar(&cv_serversort); #endif From dc637736ea9440263cd319c1d4618183697b3abf Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 03:12:56 +0800 Subject: [PATCH 172/196] Trigonometric input capping --- src/g_game.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index a3025f949..851eb5675 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -886,6 +886,7 @@ static fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) { boolean forcestrafe = false; + boolean forcefullinput = false; INT32 tspeed, forward, side, axis, altaxis, i; const INT32 speed = 1; // these ones used for multiple conditions @@ -959,6 +960,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) if (turnleft) cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]); } + if (twodlevel + || (player->mo && (player->mo->flags2 & MF2_TWOD)) + || (!demoplayback && (player->pflags & PF_SLIDING))) + forcefullinput = true; if (twodlevel || (player->mo && (player->mo->flags2 & MF2_TWOD)) || (!demoplayback && (player->climbing @@ -1172,11 +1177,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) // No additional acceleration when moving forward/backward and strafing simultaneously. // do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this. - // 9-18-2017: ALSO, only do this when using keys to move. Gamepad analog sticks get severely gimped by this - if (!forcestrafe && (((movefkey || movebkey) && side) || ((strafelkey || straferkey) && forward))) + if (!forcefullinput && forward && side) { - forward = FixedMul(forward, 3*FRACUNIT/4); - side = FixedMul(side, 3*FRACUNIT/4); + angle_t angle = R_PointToAngle2(0, 0, side << FRACBITS, forward << FRACBITS); + INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE)); + INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE)); + forward = max(min(forward, maxforward), -maxforward); + side = max(min(side, maxside), -maxside); } //Silly hack to make 2d mode *somewhat* playable with no chasecam. @@ -1212,6 +1219,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) { boolean forcestrafe = false; + boolean forcefullinput = false; INT32 tspeed, forward, side, axis, altaxis, i; const INT32 speed = 1; // these ones used for multiple conditions @@ -1283,6 +1291,10 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) if (turnleft) cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]); } + if (twodlevel + || (player->mo && (player->mo->flags2 & MF2_TWOD)) + || (!demoplayback && (player->pflags & PF_SLIDING))) + forcefullinput = true; if (twodlevel || (player->mo && (player->mo->flags2 & MF2_TWOD)) || player->climbing @@ -1494,10 +1506,13 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) // No additional acceleration when moving forward/backward and strafing simultaneously. // do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this. // 9-18-2017: ALSO, only do this when using keys to move. Gamepad analog sticks get severely gimped by this - if (!forcestrafe && (((movefkey || movebkey) && side) || ((strafelkey || straferkey) && forward))) + if (!forcefullinput && forward && side) { - forward = FixedMul(forward, 3*FRACUNIT/4); - side = FixedMul(side, 3*FRACUNIT/4); + angle_t angle = R_PointToAngle2(0, 0, side << FRACBITS, forward << FRACBITS); + INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE)); + INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE)); + forward = max(min(forward, maxforward), -maxforward); + side = max(min(side, maxside), -maxside); } //Silly hack to make 2d mode *somewhat* playable with no chasecam. @@ -6165,4 +6180,3 @@ INT32 G_TicsToMilliseconds(tic_t tics) { return (INT32)((tics%TICRATE) * (1000.00f/TICRATE)); } - From 51cee6d5bd4206b3d10efae6a9f3ac80967d4845 Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 03:16:04 +0800 Subject: [PATCH 173/196] removed outdated comment --- src/g_game.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 851eb5675..3820bc0c9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1505,7 +1505,6 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) // No additional acceleration when moving forward/backward and strafing simultaneously. // do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this. - // 9-18-2017: ALSO, only do this when using keys to move. Gamepad analog sticks get severely gimped by this if (!forcefullinput && forward && side) { angle_t angle = R_PointToAngle2(0, 0, side << FRACBITS, forward << FRACBITS); From f7c1727959466517033443ecc81e150c2b1c0394 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 24 Sep 2019 19:11:52 -0300 Subject: [PATCH 174/196] fix automap FRACBITS confusion --- src/am_map.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 6d7042f1c..d5000b267 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -816,17 +816,18 @@ static void AM_drawGrid(INT32 color) fixed_t x, y; fixed_t start, end; mline_t ml; + fixed_t gridsize = (MAPBLOCKUNITS<>FRACTOMAPBITS)) % gridsize) + start += gridsize - ((start - (bmaporgx>>FRACTOMAPBITS)) % gridsize); end = m_x + m_w; // draw vertical gridlines ml.a.y = m_y; ml.b.y = m_y + m_h; - for (x = start; x < end; x += (MAPBLOCKUNITS<>FRACTOMAPBITS)) % gridsize) + start += gridsize - ((start - (bmaporgy>>FRACTOMAPBITS)) % gridsize); end = m_y + m_h; // draw horizontal gridlines ml.a.x = m_x; ml.b.x = m_x + m_w; - for (y = start; y < end; y += (MAPBLOCKUNITS<mo->angle, DWHITE, plr->mo->x, plr->mo->y); + AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 16<mo->angle, DWHITE, plr->mo->x, plr->mo->y); return; } @@ -1053,7 +1054,7 @@ static inline void AM_drawPlayers(void) if (p->skincolor > 0) color = R_GetTranslationColormap(TC_DEFAULT, p->skincolor, GTC_CACHE)[GREENS + 8]; - AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, color, p->mo->x, p->mo->y); + AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 16<mo->angle, color, p->mo->x, p->mo->y); } } From b022ebe9125384c636f71a366bbb58803ac0025b Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 24 Sep 2019 19:44:30 -0300 Subject: [PATCH 175/196] better crosshair --- src/am_map.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index d5000b267..ff4d97da5 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -62,7 +62,7 @@ static const UINT8 NOCLIMBYELLOWS = (11*16); #define NOCLIMBCDWALLCOLORS NOCLIMBYELLOWS #define THINGCOLORS GREENS #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) -#define XHAIRCOLORS GRAYS +#define XHAIRCOLORS DWHITE // controls #define AM_PANUPKEY KEY_UPARROW @@ -137,16 +137,14 @@ static const mline_t player_arrow[] = { #undef R #define NUMPLYRLINES (sizeof (player_arrow)/sizeof (mline_t)) -#if 0 #define R (FRACUNIT) -static mline_t triangle_guy[] = { - { { (fixed_t)-.867f*R, (fixed_t)-.5f*R }, { (fixed_t) .867f*R, (fixed_t)-.5f*R } }, - { { (fixed_t) .867f*R, (fixed_t)-.5f*R }, { (fixed_t) 0, (fixed_t) R } }, - { { (fixed_t) 0, (fixed_t) R }, { (fixed_t)-.867f*R, (fixed_t)-.5f*R } } +static const mline_t cross_mark[] = +{ + { { -R, 0 }, { R, 0} }, + { { 0, -R }, { 0, R } }, }; #undef R -#define NUMTRIANGLEGUYLINES (sizeof (triangle_guy)/sizeof (mline_t)) -#endif +#define NUMCROSSMARKLINES (sizeof(cross_mark)/sizeof(mline_t)) #define R (FRACUNIT) static const mline_t thintriangle_guy[] = { @@ -1074,13 +1072,30 @@ static inline void AM_drawThings(UINT8 colors) } } -/** Draws the crosshair, actually just a dot in software mode. +/** Draws the crosshair. * * \param color Color for the crosshair. */ static inline void AM_drawCrosshair(UINT8 color) { - V_DrawFill(f_w/2 + f_x, f_h/2 + f_y, 1, 1, color|V_NOSCALESTART); + const fixed_t scale = 4<> FRACBITS; + fl.a.y = FixedMul(cross_mark[i].a.y, scale) >> FRACBITS; + fl.b.x = FixedMul(cross_mark[i].b.x, scale) >> FRACBITS; + fl.b.y = FixedMul(cross_mark[i].b.y, scale) >> FRACBITS; + + fl.a.x += f_x + (f_w / 2); + fl.a.y += f_y + (f_h / 2); + fl.b.x += f_x + (f_w / 2); + fl.b.y += f_y + (f_h / 2); + + AM_drawFline(&fl, color); + } } /** Draws the automap. @@ -1096,5 +1111,5 @@ void AM_Drawer(void) AM_drawPlayers(); AM_drawThings(THINGCOLORS); - AM_drawCrosshair(XHAIRCOLORS); + if (!followplayer) AM_drawCrosshair(XHAIRCOLORS); } From 886fe2ad86870216585518d21ff24e39707cb375 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 24 Sep 2019 19:46:52 -0300 Subject: [PATCH 176/196] remove unused struct --- src/am_map.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index ff4d97da5..dabc154d5 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -111,11 +111,6 @@ typedef struct mpoint_t a, b; } mline_t; -typedef struct -{ - fixed_t slp, islp; -} islope_t; - // // The vector graphics for the automap. // A line drawing of the player pointing right, From a19744985438dc9987502f61e8a23a5d0081d980 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 24 Sep 2019 19:49:24 -0300 Subject: [PATCH 177/196] INT32 -> boolean --- src/am_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/am_map.c b/src/am_map.c index dabc154d5..a184ea768 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -199,7 +199,7 @@ static fixed_t scale_ftom; static player_t *plr; // the player represented by an arrow -static INT32 followplayer = true; // specifies whether to follow the player around +static boolean followplayer = true; // specifies whether to follow the player around // function for drawing lines, depends on rendermode typedef void (*AMDRAWFLINEFUNC) (const fline_t *fl, INT32 color); From c7f6db5c07463d09f21c5a8a6974932cb8f275a4 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 24 Sep 2019 19:54:16 -0300 Subject: [PATCH 178/196] remove unused macros --- src/am_map.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/am_map.h b/src/am_map.h index 4e8c782a9..d59532819 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -26,11 +26,6 @@ typedef struct fpoint_t a, b; } fline_t; -// Used by ST StatusBar stuff. -#define AM_MSGHEADER (('a'<<24)+('m'<<16)) -#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) -#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) - extern boolean am_recalc; // true if screen size changes extern boolean automapactive; // In AutoMap mode? From 637490e2def8e5ca9bebeee2c16f005b960c3d9c Mon Sep 17 00:00:00 2001 From: Lachlan Wright Date: Tue, 24 Sep 2019 23:49:20 -0400 Subject: [PATCH 179/196] AI players no longer take shield damage in Ultimate mode --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index bfdec3e23..baf5c175b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3582,7 +3582,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) return true; #endif - else if (player->powers[pw_shield] || player->bot) //If One-Hit Shield + else if (player->powers[pw_shield] || player->bot && !ultimatemode) //If One-Hit Shield { P_ShieldDamage(player, inflictor, source, damage, damagetype); damage = 0; From 09868d29995a4505508135fbf571b90cd77452ea Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 16:32:06 +0800 Subject: [PATCH 180/196] Dust devil fixes --- src/p_enemy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index bc3665237..4dd077ce5 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13104,11 +13104,14 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing) if (dustdevil->height - pos > thresh) { fixed_t dist = FixedHypot(thing->x - dustdevil->x, thing->y - dustdevil->y); - fixed_t dragamount = 6 * FRACUNIT; + fixed_t dragamount = player->speed; fixed_t x, y; if (player->powers[pw_nocontrol] == 0) + { + P_ResetPlayer(player); A_PlayActiveSound(dustdevil); + } player->powers[pw_nocontrol] = 2; player->drawangle += ANG20; P_SetPlayerMobjState(thing, S_PLAY_PAIN); From 3b30b9d656875602a9e0b2917f72a087c21bdce5 Mon Sep 17 00:00:00 2001 From: Lachlan Wright Date: Wed, 25 Sep 2019 05:00:43 -0400 Subject: [PATCH 181/196] Add parentheses over change --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index baf5c175b..cbf6c0916 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3582,7 +3582,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) return true; #endif - else if (player->powers[pw_shield] || player->bot && !ultimatemode) //If One-Hit Shield + else if (player->powers[pw_shield] || (player->bot && !ultimatemode)) //If One-Hit Shield { P_ShieldDamage(player, inflictor, source, damage, damagetype); damage = 0; From 5de1225b3e363c5916625982490e2afd8ee9ac89 Mon Sep 17 00:00:00 2001 From: Lachlan Wright Date: Wed, 25 Sep 2019 05:06:04 -0400 Subject: [PATCH 182/196] Hopefully added the newline back? --- src/g_game.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_game.c b/src/g_game.c index 3820bc0c9..6c31ce9e3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6179,3 +6179,4 @@ INT32 G_TicsToMilliseconds(tic_t tics) { return (INT32)((tics%TICRATE) * (1000.00f/TICRATE)); } + From 68ee7a6658b2aa60bccb78554d34b1fb8b3a5e77 Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 17:13:03 +0800 Subject: [PATCH 183/196] Fixed spectators taking unwarranted damage in splitscreen --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index bfdec3e23..8b3a9805c 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3408,7 +3408,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Spectator handling - if (netgame) + if (multiplayer) { if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator) return false; From 264d82c8d0039606ed2eb030627445a3265f4dee Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 20:32:56 +0800 Subject: [PATCH 184/196] Fixed Brak's flames not animating --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index da8022cd4..9da1dcc8c 100644 --- a/src/info.c +++ b/src/info.c @@ -1596,7 +1596,7 @@ state_t states[NUMSTATES] = {SPR_FLME, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3 {SPR_FLME, FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE - {SPR_FLAM, FF_FULLBRIGHT, 0, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST + {SPR_FLAM, FF_FULLBRIGHT, 1, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST {SPR_ELEC, 0 + FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CYBRAKDEMONELECTRICBARRIER_INIT2}, // S_CYBRAKDEMONELECTRICBARRIER_INIT1 {SPR_ELEC, 0 + FF_FULLBRIGHT, 0, {A_RemoteAction}, -1, S_CYBRAKDEMON_INVINCIBLERIZE, S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND}, // S_CYBRAKDEMONELECTRICBARRIER_INIT2 From 77613018f8f832abef9387fe4e1525eb0a3ff2a9 Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 21:15:19 +0800 Subject: [PATCH 185/196] (Crudely) fixed camera jitter on certain FOF slopes --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 88751c143..4bb01d746 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9620,7 +9620,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) // This block here is like 90% Lach's work, thanks bud { - if ((thiscam == &camera && cv_cam_adjust.value) || (thiscam == &camera2 && cv_cam2_adjust.value)) + if (!resetcalled && ((thiscam == &camera && cv_cam_adjust.value) || (thiscam == &camera2 && cv_cam2_adjust.value))) { if (!(mo->eflags & MFE_JUSTHITFLOOR) && (P_IsObjectOnGround(mo)) // Check that player is grounded && thiscam->ceilingz - thiscam->floorz >= P_GetPlayerHeight(player)) // Check that camera's sector is large enough for the player to fit into, at least From 2a452ddcf5d8e014afeec6d5bdeb93b4c3943fc6 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 Sep 2019 14:36:20 +0100 Subject: [PATCH 186/196] Remember when Lach recorded their live reaction to 2.2 so far? Do you recall what the reaction to ACZ3 was? If you don't, the reaction was "WHAT?? Why is he here? This is GREAT but, what??" And while the reaction is on the positive end of things, it's stll clearly not advisable for our defining Big Reveal of the already huge update. To this end: Here is a mini cutscene! https://cdn.discordapp.com/attachments/428262628893261828/626207624043429898/srb20005.gif * He's digging through Eggman's trash. * Clearly doesn't expect to see you! * Ready for a fight all the same. * You can attack him during the mini cutscene if you're impatient. * Skipped if you give him MTF_AMBUSH. * Requires new assets (including map) to test, but I'm not ready to make a MR yet because I have other thoughts first. Also, since I was poking around in p_enemy.c, I fixed A_Boss1Laser's issues (not working with direct 2.1 port states and having the weird secondary attack). --- src/dehacked.c | 27 ++++++++++++ src/hardware/hw_light.c | 2 + src/info.c | 90 ++++++++++++++++++++++++++++++-------- src/info.h | 29 +++++++++++++ src/p_enemy.c | 95 ++++++++++++++++++++++++++++++++--------- src/p_mobj.c | 9 ++-- src/sounds.c | 5 ++- src/sounds.h | 1 + 8 files changed, 216 insertions(+), 42 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 8334de61a..dcce68404 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2431,6 +2431,7 @@ static actionpointer_t actionpointers[] = {{A_Boss5CheckFalling}, "A_BOSS5CHECKFALLING"}, {{A_Boss5PinchShot}, "A_BOSS5PINCHSHOT"}, {{A_Boss5MakeItRain}, "A_BOSS5MAKEITRAIN"}, + {{A_Boss5MakeJunk}, "A_BOSS5MAKEJUNK"}, {{A_LookForBetter}, "A_LOOKFORBETTER"}, {{A_Boss5BombExplode}, "A_BOSS5BOMBEXPLODE"}, {{A_DustDevilThink}, "A_DUSTDEVILTHINK"}, @@ -4784,6 +4785,20 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGROBOJET", // Boss 5 + "S_FANG_SETUP", + "S_FANG_INTRO1", + "S_FANG_INTRO2", + "S_FANG_INTRO3", + "S_FANG_INTRO4", + "S_FANG_INTRO5", + "S_FANG_INTRO6", + "S_FANG_INTRO7", + "S_FANG_INTRO8", + "S_FANG_INTRO9", + "S_FANG_INTRO10", + "S_FANG_INTRO11", + "S_FANG_INTRO12", + "S_FANG_IDLE0", "S_FANG_IDLE1", "S_FANG_IDLE2", "S_FANG_IDLE3", @@ -4855,6 +4870,17 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FANG_FLEEBOUNCE2", "S_FANG_KO", + "S_BROKENROBOTRANDOM", + "S_BROKENROBOTA", + "S_BROKENROBOTB", + "S_BROKENROBOTC", + "S_BROKENROBOTD", + "S_BROKENROBOTE", + "S_BROKENROBOTF", + + "S_ALART1", + "S_ALART2", + "S_FBOMB1", "S_FBOMB2", "S_FBOMB_EXPL1", @@ -7264,6 +7290,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Boss 5 "MT_FANG", + "MT_BROKENROBOT", "MT_FBOMB", "MT_TNTDUST", // also used by barrel "MT_FSGNA", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 1de20cad7..36ebbf133 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -205,6 +205,8 @@ light_t *t_lspr[NUMSPRITES] = // Boss 5 (Arid Canyon) &lspr[NOLIGHT], //SPR_FANG // replaces EGGQ + &lspr[NOLIGHT], //SPR_BRKN + &lspr[NOLIGHT], //SPR_WHAT &lspr[NOLIGHT], //SPR_FBOM &lspr[NOLIGHT], //SPR_FSGN &lspr[REDBALL_L], //SPR_BARX // bomb explosion (also used by barrel) diff --git a/src/info.c b/src/info.c index da8022cd4..27c2a9c02 100644 --- a/src/info.c +++ b/src/info.c @@ -93,6 +93,8 @@ char sprnames[NUMSPRITES + 1][5] = // Boss 5 (Arid Canyon) "FANG", // replaces EGGQ + "BRKN", // broken robot chunk + "WHAT", // alart "FBOM", "FSGN", "BARX", // bomb explosion (also used by barrel) @@ -1348,6 +1350,22 @@ state_t states[NUMSTATES] = {SPR_EFIR, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_EGGROBOJET // Boss 5 + {SPR_NULL, 0, 2, {A_CheckFlags2}, MF2_AMBUSH, S_FANG_IDLE0, S_FANG_INTRO1}, // S_FANG_SETUP + + {SPR_NULL, 0, 2, {A_Boss5MakeJunk}, 0, 0, S_FANG_INTRO2}, // S_FANG_INTRO1 + {SPR_NULL, 0, 0, {A_Repeat}, 25, S_FANG_INTRO1, S_FANG_INTRO3}, // S_FANG_INTRO2 + {SPR_NULL, 0, 0, {A_Boss5MakeJunk}, 0, 1, S_FANG_INTRO4}, // S_FANG_INTRO3 + {SPR_FANG, 30, 1, {A_ZThrust}, 9, (1<<16)|1, S_FANG_INTRO5}, // S_FANG_INTRO4 + {SPR_FANG, 27, 1, {A_Boss5CheckOnGround}, S_FANG_INTRO9, 0, S_FANG_INTRO6}, // S_FANG_INTRO5 + {SPR_FANG, 28, 1, {A_Boss5CheckOnGround}, S_FANG_INTRO9, 0, S_FANG_INTRO7}, // S_FANG_INTRO6 + {SPR_FANG, 29, 1, {A_Boss5CheckOnGround}, S_FANG_INTRO9, 0, S_FANG_INTRO8}, // S_FANG_INTRO7 + {SPR_FANG, 30, 1, {A_Boss5CheckOnGround}, S_FANG_INTRO9, 0, S_FANG_INTRO5}, // S_FANG_INTRO8 + {SPR_FANG, 23|FF_ANIMATE, 50, {NULL}, 1, 4, S_FANG_INTRO10}, // S_FANG_INTRO9 + {SPR_FANG, 25, 5, {NULL}, 0, 0, S_FANG_INTRO11}, // S_FANG_INTRO10 + {SPR_FANG, 26, 2, {A_Boss5MakeJunk}, S_BROKENROBOTD, 2, S_FANG_INTRO12}, // S_FANG_INTRO11 + {SPR_FANG, 31|FF_ANIMATE, 50, {NULL}, 3, 4, S_FANG_IDLE1}, // S_FANG_INTRO12 + + {SPR_FANG, 0, 0, {A_SetObjectFlags}, MF_NOCLIPTHING, 1, S_FANG_IDLE1}, // S_FANG_IDLE0 {SPR_FANG, 2, 16, {A_Look}, 1, 0, S_FANG_IDLE2}, // S_FANG_IDLE1 {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE3}, // S_FANG_IDLE2 {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE4}, // S_FANG_IDLE3 @@ -1437,6 +1455,17 @@ state_t states[NUMSTATES] = {SPR_FANG, 17, 7*TICRATE, {NULL}, 0, 0, S_NULL}, // S_FANG_KO + {SPR_NULL, 0, -1, {A_RandomStateRange}, S_BROKENROBOTA, S_BROKENROBOTF, S_NULL}, // S_BROKENROBOTRANDOM + {SPR_BRKN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTA + {SPR_BRKN, 4|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTB + {SPR_BRKN, 8|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTC + {SPR_BRKN, 12|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTD + {SPR_BRKN, 16|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTE + {SPR_BRKN, 20|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 3, 4, S_NULL}, // S_BROKENROBOTF + + {SPR_WHAT, FF_ANIMATE|FF_FULLBRIGHT, 4, {NULL}, 1, 2, S_ALART2}, // S_ALART1 + {SPR_WHAT, 2|FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 1, 2, S_NULL}, // S_ALART2 + {SPR_FBOM, 0, 1, {A_GhostMe}, 0, 0, S_FBOMB2}, // S_FBOMB1 {SPR_FBOM, 1, 1, {A_GhostMe}, 0, 0, S_FBOMB1}, // S_FBOMB2 {SPR_BARX, 0|FF_FULLBRIGHT, 3, {A_SetObjectFlags}, MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP, 0, S_FBOMB_EXPL2}, // S_FBOMB_EXPL1 @@ -3816,21 +3845,21 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN {SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEA - {SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEB - {SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEC - {SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLED - {SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEE - {SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEF - {SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEG - {SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEH - {SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEI - {SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEJ - {SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEK - {SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEL - {SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEM - {SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEN - {SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEO - {SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEP + {SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEB + {SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEC + {SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLED + {SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEE + {SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEF + {SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEG + {SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEH + {SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEI + {SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEJ + {SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEK + {SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEL + {SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEM + {SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEN + {SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEO + {SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEP {SPR_BRIC, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_BRICKDEBRIS @@ -5625,7 +5654,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_FANG 204, // doomednum - S_FANG_IDLE1, // spawnstate + S_FANG_SETUP, // spawnstate 8, // spawnhealth S_FANG_PATHINGSTART1, // seestate sfx_None, // seesound @@ -5646,10 +5675,37 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 3, // damage sfx_boingf, // activesound - MF_SPECIAL|MF_BOSS|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags + MF_RUNSPAWNFUNC|MF_SPECIAL|MF_BOSS|MF_SHOOTABLE|MF_GRENADEBOUNCE|MF_NOCLIPTHING, // flags -- MF_NOCLIPTHING will be removed after intro event ends S_NULL // raisestate }, + { // MT_BROKENROBOT + -1, // doomednum + S_BROKENROBOTRANDOM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_ambint, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 255, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 1000, // mass + 0, // damage + sfx_crumbl, // activesound + MF_RUNSPAWNFUNC|MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + { // MT_FBOMB -1, // doomednum S_FBOMB1, // spawnstate diff --git a/src/info.h b/src/info.h index 74e4b87a2..771eb18d3 100644 --- a/src/info.h +++ b/src/info.h @@ -252,6 +252,7 @@ void A_Boss5CheckOnGround(); void A_Boss5CheckFalling(); void A_Boss5PinchShot(); void A_Boss5MakeItRain(); +void A_Boss5MakeJunk(); void A_LookForBetter(); void A_Boss5BombExplode(); void A_DustDevilThink(); @@ -339,6 +340,8 @@ typedef enum sprite // Boss 5 (Arid Canyon) SPR_FANG, // replaces EGGQ + SPR_BRKN, + SPR_WHAT, SPR_FBOM, SPR_FSGN, SPR_BARX, // bomb explosion (also used by barrel) @@ -1495,6 +1498,20 @@ typedef enum state S_EGGROBOJET, // Boss 5 + S_FANG_SETUP, + S_FANG_INTRO1, + S_FANG_INTRO2, + S_FANG_INTRO3, + S_FANG_INTRO4, + S_FANG_INTRO5, + S_FANG_INTRO6, + S_FANG_INTRO7, + S_FANG_INTRO8, + S_FANG_INTRO9, + S_FANG_INTRO10, + S_FANG_INTRO11, + S_FANG_INTRO12, + S_FANG_IDLE0, S_FANG_IDLE1, S_FANG_IDLE2, S_FANG_IDLE3, @@ -1566,6 +1583,17 @@ typedef enum state S_FANG_FLEEBOUNCE2, S_FANG_KO, + S_BROKENROBOTRANDOM, + S_BROKENROBOTA, + S_BROKENROBOTB, + S_BROKENROBOTC, + S_BROKENROBOTD, + S_BROKENROBOTE, + S_BROKENROBOTF, + + S_ALART1, + S_ALART2, + S_FBOMB1, S_FBOMB2, S_FBOMB_EXPL1, @@ -3997,6 +4025,7 @@ typedef enum mobj_type // Boss 5 MT_FANG, + MT_BROKENROBOT, MT_FBOMB, MT_TNTDUST, // also used by barrel MT_FSGNA, diff --git a/src/p_enemy.c b/src/p_enemy.c index bc3665237..6eec0e9b6 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -282,6 +282,7 @@ void A_Boss5CheckOnGround(mobj_t *actor); void A_Boss5CheckFalling(mobj_t *actor); void A_Boss5PinchShot(mobj_t *actor); void A_Boss5MakeItRain(mobj_t *actor); +void A_Boss5MakeJunk(mobj_t *actor); void A_LookForBetter(mobj_t *actor); void A_Boss5BombExplode(mobj_t *actor); void A_DustDevilThink(mobj_t *actor); @@ -3027,10 +3028,15 @@ void A_Boss1Laser(mobj_t *actor) if (!actor->target) return; - if ((upperend & 1) && (actor->extravalue2 > 1)) - actor->extravalue2--; + if (actor->state->tics > 1) + dur = actor->tics; + else + { + if ((upperend & 1) && (actor->extravalue2 > 1)) + actor->extravalue2--; - dur = actor->extravalue2; + dur = actor->extravalue2; + } switch (locvar2) { @@ -3076,23 +3082,15 @@ void A_Boss1Laser(mobj_t *actor) actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); if (mobjinfo[locvar1].seesound) S_StartSound(actor, mobjinfo[locvar1].seesound); - if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) - { - point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); - point->angle = actor->angle; - point->fuse = dur+1; - P_SetTarget(&point->target, actor->target); - P_SetTarget(&actor->target, point); - } - } - /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path - else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) - actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/ - /*if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) - angle = FixedAngle(FixedDiv(dur*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); - else*/ - angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); + point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); + point->angle = actor->angle; + point->fuse = dur+1; + P_SetTarget(&point->target, actor->target); + P_SetTarget(&actor->target, point); + } + + angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); point = P_SpawnMobj(x, y, z, locvar1); P_SetTarget(&point->target, actor); @@ -4034,7 +4032,7 @@ bossjustdie: A_Boss5Jump(mo); mo->momx = ((16 - 1)*mo->momx)/16; mo->momy = ((16 - 1)*mo->momy)/16; - if (!(mo->flags2 & MF2_AMBUSH)) + if (!(mo->spawnpoint && mo->spawnpoint->options & MTF_EXTRA)) { const fixed_t time = FixedHypot(mo->tracer->x - mo->x, mo->tracer->y - mo->y)/FixedHypot(mo->momx, mo->momy); const fixed_t speed = 64*FRACUNIT; @@ -12978,6 +12976,63 @@ void A_Boss5MakeItRain(mobj_t *actor) actor->extravalue2 = 0; } +// Function: A_Boss5MakeJunk +// +// Description: Make a mess. +// +// var1 = state # to set on MT_BROKENROBOT (if 0 do nothing) +// var2 = mode (0 = make 1, & 1 make 8, & 2 alart mode) +// +void A_Boss5MakeJunk(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *broked; + angle_t ang; + INT32 i = ((locvar2 & 1) ? 8 : 1); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5MakeJunk", actor)) + return; +#endif + + if (leveltime < 2) + return; + + ang = FixedAngle((P_RandomKey(36)*10)<fuse = TICRATE; + else + broked->fuse = (((locvar2 & 1) ? 4 : 2)*TICRATE)/3; + broked->angle = ang; + P_InstaThrust(broked, ang, ((locvar2 & 2) ? 8 : 5)*actor->scale); + P_SetObjectMomZ(broked, (((locvar2) ? 4 : 0) + P_RandomRange(2, 5))<x + broked->momx, broked->y + broked->momy, broked->z); + ang += ANGLE_45; + } + + if (locvar2 & 2) + { + broked = P_SpawnMobjFromMobj(actor, 0, 0, 64<fuse = states[S_FANG_INTRO12].tics+10; + P_SetMobjState(broked, S_ALART1); + } + else if (locvar2 & 1) + { + broked->z += broked->momz; + S_StartSound(actor, sfx_s3kccs); + actor->flags &= ~MF_NOCLIPTHING; + } + else + S_StartSound(actor, sfx_s3kd3s); +} + // Function: A_LookForBetter // // Description: A_Look, except it finds a better target in multiplayer, and doesn't lose the target in singleplayer. diff --git a/src/p_mobj.c b/src/p_mobj.c index 5dfb7a5ab..a459300a1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9098,9 +9098,12 @@ void P_MobjThinker(mobj_t *mobj) { if (mobj->state->action.acp1 == (actionf_p1)A_Boss1Laser) { - /*var1 = mobj->state->var1; - var2 = mobj->state->var2 & 65535; - mobj->state->action.acp1(mobj);*/ + if (mobj->state->tics > 1) + { + var1 = mobj->state->var1; + var2 = mobj->state->var2 & 65535; + mobj->state->action.acp1(mobj); + } } else if (leveltime & 1) // Fire mode { diff --git a/src/sounds.c b/src/sounds.c index 43225a615..1bfcf4cc8 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -212,6 +212,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bouncing"}, {"corkp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork fired"}, {"corkh", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork hit"}, + {"alart", false, 200, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Caught red handed!"}, {"bowl", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bowling"}, {"chuchu", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Train horn"}, {"bsnipe", false, 200, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Home-run smash"}, @@ -609,8 +610,8 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3kcal", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Energy"}, // ditto {"s3kcbs", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous rumbling"}, {"s3kcbl", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous rumbling"}, // ditto - {"s3kccs", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Collapsing"}, - {"s3kccl", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Collapsing"}, // ditto + {"s3kccs", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bursting"}, + {"s3kccl", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bursting"}, // ditto {"s3kcds", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous rumbling"}, {"s3kcdl", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous rumbling"}, // ditto {"s3kces", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wind tunnel"}, diff --git a/src/sounds.h b/src/sounds.h index 674f51d79..d25a22619 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -261,6 +261,7 @@ typedef enum sfx_boingf, sfx_corkp, sfx_corkh, + sfx_alart, sfx_bowl, sfx_chuchu, sfx_bsnipe, From 9504d078e440d3330264952f9486e77892fc3458 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 Sep 2019 15:12:19 +0100 Subject: [PATCH 187/196] Don't lock on if something is literally intangible. --- src/p_user.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 88751c143..7ecce3d87 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8890,12 +8890,16 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) continue; mo = (mobj_t *)think; - if (!((mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (mo->flags & MF_SHOOTABLE)) || (mo->flags & MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag - continue; // not a valid target + + if (mo->flags & MF_NOCLIPTHING) + continue; if (mo->health <= 0) // dead continue; + if (!((mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (mo->flags & MF_SHOOTABLE)) || (mo->flags & MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag + continue; // not a valid target + if (mo == player->mo) continue; From c1750d9359d19b1c9de7eed53def1c4d4c3905ef Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 22:36:48 +0800 Subject: [PATCH 188/196] Fixes shield ability usage when pressing spin on ACZ ropes (and also changes flame shield angling) --- src/p_mobj.c | 3 ++- src/p_user.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5dfb7a5ab..8c80f1bcf 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7311,7 +7311,8 @@ void P_MobjThinker(mobj_t *mobj) case MT_FLAMEAURA_ORB: if (!(mobj->flags2 & MF2_SHIELD)) return; - mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield + if (mobj->state-states < mobj->info->painstate) + mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield if (mobj->tracer /* && mobj->target -- the following is implicit by P_AddShield && mobj->target->player diff --git a/src/p_user.c b/src/p_user.c index 4bb01d746..98f91768d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8663,6 +8663,7 @@ static void P_DoRopeHang(player_t *player) if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope { player->pflags |= P_GetJumpFlags(player); + player->pflags |= PF_USEDOWN; P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); P_SetTarget(&player->mo->tracer, NULL); From 313fed2a59951765d4fd72a01d603de2613b38ff Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 Sep 2019 15:49:37 +0100 Subject: [PATCH 189/196] Correctly restrict MF_NOCLIPTHING-objects from being interacted with from both moving and non-moving side. --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index 01c41131b..8035d64a5 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -746,7 +746,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; } - if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) + if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) return true; // Don't collide with your buddies while NiGHTS-flying. From fb9248793a1360c0b1418368eb8dfca43638a2e2 Mon Sep 17 00:00:00 2001 From: lachwright Date: Wed, 25 Sep 2019 23:09:05 +0800 Subject: [PATCH 190/196] Fix errormode compilation error --- src/p_mobj.c | 2 +- src/p_user.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8c80f1bcf..ce97024ba 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7311,7 +7311,7 @@ void P_MobjThinker(mobj_t *mobj) case MT_FLAMEAURA_ORB: if (!(mobj->flags2 & MF2_SHIELD)) return; - if (mobj->state-states < mobj->info->painstate) + if ((statenum_t)(mobj->state-states) < mobj->info->painstate) mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield if (mobj->tracer /* && mobj->target -- the following is implicit by P_AddShield diff --git a/src/p_user.c b/src/p_user.c index 98f91768d..49ed8aa7f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8662,8 +8662,7 @@ static void P_DoRopeHang(player_t *player) if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope { - player->pflags |= P_GetJumpFlags(player); - player->pflags |= PF_USEDOWN; + player->pflags |= (P_GetJumpFlags(player)|PF_USEDOWN); P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); P_SetTarget(&player->mo->tracer, NULL); From 0cd3e4fd6258677a3c8ad663131c5a1dfa5559a2 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 26 Sep 2019 05:27:51 +0800 Subject: [PATCH 191/196] Hardcoded DSZ3 shockwave --- src/dehacked.c | 7 ++++++- src/info.c | 24 ++++++++++++++--------- src/info.h | 10 ++++++++-- src/p_enemy.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 31 ++++++++++++++++++++++-------- src/sounds.c | 2 +- 6 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 8334de61a..7548a96aa 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4742,6 +4742,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BOSSSEBH1", "S_BOSSSEBH2", + // Boss 3 Shockwave + + "S_SHOCKWAVE1", + "S_SHOCKWAVE2", + // Boss 4 "S_EGGMOBILE4_STND", "S_EGGMOBILE4_LATK1", @@ -7253,7 +7258,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Boss 3 "MT_EGGMOBILE3", "MT_FAKEMOBILE", - "MT_SHOCK", + "MT_SHOCKWAVE", // Boss 4 "MT_EGGMOBILE4", diff --git a/src/info.c b/src/info.c index da8022cd4..974dd84fe 100644 --- a/src/info.c +++ b/src/info.c @@ -85,6 +85,7 @@ char sprnames[NUMSPRITES + 1][5] = "EGGO", // Boss 3 "SEBH", // Boss 3 Junk "FAKE", // Boss 3 Fakemobile + "SHCK", // Boss 3 Shockwave // Boss 4 (Castle Eggman) "EGGP", @@ -1306,6 +1307,11 @@ state_t states[NUMSTATES] = {SPR_SEBH, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH1 {SPR_SEBH, 1, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH2 + // Boss 3 Shockwave + + {SPR_SHCK, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, 8, {A_Boss3ShockThink}, 4, 2, S_SHOCKWAVE2}, // S_SHOCKWAVE1 + {SPR_SHCK, 3|FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, 8, {A_Boss3ShockThink}, 4, 2, S_SHOCKWAVE1}, // S_SHOCKWAVE2 + // Boss 4 {SPR_EGGP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGMOBILE4_STND {SPR_EGGP, 1, 3, {NULL}, 0, 0, S_EGGMOBILE4_LATK2}, // S_EGGMOBILE4_LATK1 @@ -5461,30 +5467,30 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SHOCK + { // MT_SHOCKWAVE -1, // doomednum - S_THUNDERCOIN_SPARK, // spawnstate + S_SHOCKWAVE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate - sfx_None, // seesound + sfx_s3k5e, // seesound 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance + 8*TICRATE, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_SPRK1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - 10*FRACUNIT, // speed - 16*FRACUNIT, // radius - 35*FRACUNIT, // height + 16*FRACUNIT, // speed + 48*FRACUNIT, // radius + 8*FRACUNIT, // height 0, // display offset DMG_ELECTRIC|(sfx_buzz2<<8), // mass - 20, // damage + 3, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags + MF_NOBLOCKMAP|MF_MISSILE|MF_PAIN|MF_NOGRAVITY|MF_PAPERCOLLISION, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index 74e4b87a2..3ff3c3715 100644 --- a/src/info.h +++ b/src/info.h @@ -138,6 +138,7 @@ void A_SetReactionTime(); void A_Boss1Spikeballs(); void A_Boss3TakeDamage(); void A_Boss3Path(); +void A_Boss3ShockThink(); void A_LinedefExecute(); void A_PlaySeeSound(); void A_PlayAttackSound(); @@ -331,6 +332,7 @@ typedef enum sprite SPR_EGGO, // Boss 3 SPR_SEBH, // Boss 3 Junk SPR_FAKE, // Boss 3 Fakemobile + SPR_SHCK, // Boss 3 Shockwave // Boss 4 (Castle Eggman) SPR_EGGP, @@ -1453,6 +1455,10 @@ typedef enum state S_BOSSSEBH1, S_BOSSSEBH2, + // Boss 3 Shockwave + S_SHOCKWAVE1, + S_SHOCKWAVE2, + // Boss 4 S_EGGMOBILE4_STND, S_EGGMOBILE4_LATK1, @@ -3986,7 +3992,7 @@ typedef enum mobj_type // Boss 3 MT_EGGMOBILE3, MT_FAKEMOBILE, - MT_SHOCK, + MT_SHOCKWAVE, // Boss 4 MT_EGGMOBILE4, @@ -4195,7 +4201,7 @@ typedef enum mobj_type MT_SEAWEED, // DSZ Seaweed MT_WATERDRIP, // Dripping Water source MT_WATERDROP, // Water drop from dripping water - MT_CORAL1, // Coral + MT_CORAL1, // Coral MT_CORAL2, MT_CORAL3, MT_CORAL4, diff --git a/src/p_enemy.c b/src/p_enemy.c index bc3665237..dceaa6c46 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -170,6 +170,7 @@ void A_SetReactionTime(mobj_t *actor); void A_Boss1Spikeballs(mobj_t *actor); void A_Boss3TakeDamage(mobj_t *actor); void A_Boss3Path(mobj_t *actor); +void A_Boss3ShockThink(mobj_t *actor); void A_LinedefExecute(mobj_t *actor); void A_PlaySeeSound(mobj_t *actor); void A_PlayAttackSound(mobj_t *actor); @@ -8079,6 +8080,57 @@ void A_Boss3Path(mobj_t *actor) } } +// Function: A_Boss3ShockThink +// +// Description: Inserts new interstitial shockwave objects when the space between others spreads too much. +// +// var1 = unused +// var2 = unused +// +void A_Boss3ShockThink(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss3ShockThink", actor)) + return; +#endif + + if (actor->momx || actor->momy) + actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy) + ANGLE_90; + + if (actor->hnext && !P_MobjWasRemoved(actor->hnext)) + { + mobj_t *snext = actor->hnext; + mobj_t *snew; + fixed_t x0, y0, x1, y1; + + // Break the link if movements are too different + if (FixedHypot(snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale) + { + actor->hnext = NULL; + return; + } + + // Check distance between shockwave objects to determine whether interstitial ones should be spawned + x0 = actor->x; + y0 = actor->y; + x1 = snext->x; + y1 = snext->y; + if (FixedHypot(x1 - x0, y1 - y0) > 2*actor->radius) + { + snew = P_SpawnMobj((x0 + x1) >> 1, (y0 + y1) >> 1, (actor->z + snext->z) >> 1, actor->type); + snew->momx = (actor->momx + snext->momx) >> 1; + snew->momy = (actor->momy + snext->momy) >> 1; + snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed? + snew->angle = (actor->angle + snext->angle) >> 1; + P_SetTarget(&snew->target, actor->target); + snew->fuse = actor->fuse; + + actor->hnext = snew; + snew->hnext = snext; + } + } +} + // Function: A_LinedefExecute // // Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well. diff --git a/src/p_mobj.c b/src/p_mobj.c index 5dfb7a5ab..cdc6e4be1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4605,22 +4605,37 @@ static void P_Boss3Thinker(mobj_t *mobj) if (!mobj->movefactor) // to firing mode { - UINT8 i; - angle_t ang = 0; + UINT8 i, numtospawn = 24; + angle_t ang = 0, interval = FixedAngle((360 << FRACBITS) / numtospawn); + mobj_t *shock, *sfirst, *sprev; mobj->movecount = mobj->health+1; mobj->movefactor = -512*FRACUNIT; // shock the water! - for (i = 0; i < 64; i++) + for (i = 0; i < numtospawn; i++) { - mobj_t *shock = P_SpawnMobjFromMobj(mobj, 0, 0, 4*FRACUNIT, MT_SHOCK); + shock = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SHOCKWAVE); P_SetTarget(&shock->target, mobj); - P_InstaThrust(shock, ang, shock->info->speed); - P_CheckMissileSpawn(shock); - ang += (ANGLE_MAX/64); + shock->fuse = shock->info->painchance; + + if (i % 2 == 0) + P_SetMobjState(shock, shock->state->nextstate); + + if (i == 0) + sfirst = shock; + else + { + if (i == numtospawn - 1) + shock->hnext = sfirst; + sprev->hnext = shock; + } + + P_Thrust(shock, ang, shock->info->speed); + ang += interval; + sprev = shock; } - S_StartSound(mobj, sfx_fizzle); + S_StartSound(mobj, shock->info->seesound); // look for a new target P_BossTargetPlayer(mobj, false); diff --git a/src/sounds.c b/src/sounds.c index 43225a615..3c8cfa44f 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -484,7 +484,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k5b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Menu beep"}, {"s3k5c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Electric spark"}, {"s3k5d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy hit"}, - {"s3k5e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing laser"}, + {"s3k5e", false, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Releasing charge"}, {"s3k5f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Crusher stomp"}, {"s3k60", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Accelerating"}, {"s3k61", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drilling"}, From c9c9f95057e41dc1ee50a2993e6b9a23ecd497f5 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 26 Sep 2019 17:12:03 +0800 Subject: [PATCH 192/196] Lowered shockwave speed --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 974dd84fe..3ea794e66 100644 --- a/src/info.c +++ b/src/info.c @@ -5483,7 +5483,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPRK1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - 16*FRACUNIT, // speed + 12*FRACUNIT, // speed 48*FRACUNIT, // radius 8*FRACUNIT, // height 0, // display offset From d5e0ac1450d11b24a0ac7a65106a49988158c467 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 26 Sep 2019 18:57:25 +0800 Subject: [PATCH 193/196] Added A_Boss3ShockThink to dehacked.c --- src/dehacked.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dehacked.c b/src/dehacked.c index 7548a96aa..4bc119a19 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2325,6 +2325,7 @@ static actionpointer_t actionpointers[] = {{A_Boss1Spikeballs}, "A_BOSS1SPIKEBALLS"}, {{A_Boss3TakeDamage}, "A_BOSS3TAKEDAMAGE"}, {{A_Boss3Path}, "A_BOSS3PATH"}, + {{A_Boss3ShockThink}, "A_BOSS3SHOCKTHINK"}, {{A_LinedefExecute}, "A_LINEDEFEXECUTE"}, {{A_PlaySeeSound}, "A_PLAYSEESOUND"}, {{A_PlayAttackSound}, "A_PLAYATTACKSOUND"}, From ec1712064c9c45eafb2b6f3a1818b83b05bf1971 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 26 Sep 2019 17:06:29 +0100 Subject: [PATCH 194/196] * Add vwre vwre intro for Fang Clone Fighter battle. (Still skipped with presence of MTF_AMBUSH) * https://cdn.discordapp.com/attachments/428262628893261828/626792815451701259/srb20006.gif * Add fadeout instead of slapstick for Fang Clone Fighter death. * Allow placed Fang and Metal Sonic objects to be marked as Clone Fighters always through presence of MTF_EXTRA. --- src/dehacked.c | 17 +++++++ src/hardware/hw_light.c | 16 +++--- src/info.c | 106 ++++++++++++++++++++++++++++++++++++++-- src/info.h | 19 +++++++ src/p_enemy.c | 51 +++++++++++++++++-- src/p_mobj.c | 54 +++++++++++++++++++- src/sounds.c | 1 + src/sounds.h | 1 + 8 files changed, 248 insertions(+), 17 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index dcce68404..a6019e948 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4786,6 +4786,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Boss 5 "S_FANG_SETUP", + "S_FANG_INTRO0", "S_FANG_INTRO1", "S_FANG_INTRO2", "S_FANG_INTRO3", @@ -4798,6 +4799,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FANG_INTRO10", "S_FANG_INTRO11", "S_FANG_INTRO12", + "S_FANG_CLONE1", + "S_FANG_CLONE2", + "S_FANG_CLONE3", + "S_FANG_CLONE4", "S_FANG_IDLE0", "S_FANG_IDLE1", "S_FANG_IDLE2", @@ -4881,6 +4886,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ALART1", "S_ALART2", + "S_VWREF", + "S_VWREB", + + "S_PROJECTORLIGHT1", + "S_PROJECTORLIGHT2", + "S_PROJECTORLIGHT3", + "S_PROJECTORLIGHT4", + "S_PROJECTORLIGHT5", + "S_FBOMB1", "S_FBOMB2", "S_FBOMB_EXPL1", @@ -7291,6 +7305,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Boss 5 "MT_FANG", "MT_BROKENROBOT", + "MT_VWREF", + "MT_VWREB", + "MT_PROJECTORLIGHT", "MT_FBOMB", "MT_TNTDUST", // also used by barrel "MT_FSGNA", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 36ebbf133..8f1dbf2d2 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -204,13 +204,15 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_EGR1 // Boss 5 (Arid Canyon) - &lspr[NOLIGHT], //SPR_FANG // replaces EGGQ - &lspr[NOLIGHT], //SPR_BRKN - &lspr[NOLIGHT], //SPR_WHAT - &lspr[NOLIGHT], //SPR_FBOM - &lspr[NOLIGHT], //SPR_FSGN - &lspr[REDBALL_L], //SPR_BARX // bomb explosion (also used by barrel) - &lspr[NOLIGHT], //SPR_BARD // bomb dust (also used by barrel) + &lspr[NOLIGHT], // SPR_FANG // replaces EGGQ + &lspr[NOLIGHT], // SPR_BRKN + &lspr[NOLIGHT], // SPR_WHAT + &lspr[INVINCIBLE_L], // SPR_VWRE + &lspr[INVINCIBLE_L], // SPR_PROJ + &lspr[NOLIGHT], // SPR_FBOM + &lspr[NOLIGHT], // SPR_FSGN + &lspr[REDBALL_L], // SPR_BARX // bomb explosion (also used by barrel) + &lspr[NOLIGHT], // SPR_BARD // bomb dust (also used by barrel) // Boss 6 (Red Volcano) &lspr[NOLIGHT], // SPR_EEGR diff --git a/src/info.c b/src/info.c index 27c2a9c02..a7a165683 100644 --- a/src/info.c +++ b/src/info.c @@ -95,6 +95,8 @@ char sprnames[NUMSPRITES + 1][5] = "FANG", // replaces EGGQ "BRKN", // broken robot chunk "WHAT", // alart + "VWRE", + "PROJ", // projector light "FBOM", "FSGN", "BARX", // bomb explosion (also used by barrel) @@ -1350,9 +1352,10 @@ state_t states[NUMSTATES] = {SPR_EFIR, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_EGGROBOJET // Boss 5 - {SPR_NULL, 0, 2, {A_CheckFlags2}, MF2_AMBUSH, S_FANG_IDLE0, S_FANG_INTRO1}, // S_FANG_SETUP + {SPR_NULL, 0, 2, {A_CheckFlags2}, MF2_AMBUSH, S_FANG_IDLE0, S_FANG_INTRO0}, // S_FANG_SETUP - {SPR_NULL, 0, 2, {A_Boss5MakeJunk}, 0, 0, S_FANG_INTRO2}, // S_FANG_INTRO1 + {SPR_NULL, 0, 2, {NULL}, 0, 0, S_FANG_INTRO1}, // S_FANG_INTRO0 + {SPR_NULL, 0, 2, {A_Boss5MakeJunk}, -S_FANG_CLONE1, 0, S_FANG_INTRO2}, // S_FANG_INTRO1 {SPR_NULL, 0, 0, {A_Repeat}, 25, S_FANG_INTRO1, S_FANG_INTRO3}, // S_FANG_INTRO2 {SPR_NULL, 0, 0, {A_Boss5MakeJunk}, 0, 1, S_FANG_INTRO4}, // S_FANG_INTRO3 {SPR_FANG, 30, 1, {A_ZThrust}, 9, (1<<16)|1, S_FANG_INTRO5}, // S_FANG_INTRO4 @@ -1365,6 +1368,11 @@ state_t states[NUMSTATES] = {SPR_FANG, 26, 2, {A_Boss5MakeJunk}, S_BROKENROBOTD, 2, S_FANG_INTRO12}, // S_FANG_INTRO11 {SPR_FANG, 31|FF_ANIMATE, 50, {NULL}, 3, 4, S_FANG_IDLE1}, // S_FANG_INTRO12 + {SPR_FANG, 11, 2, {A_Boss5MakeJunk}, 0, -1, S_FANG_CLONE2}, // S_FANG_CLONE1 + {SPR_FANG, 11, 0, {A_Repeat}, 49, S_FANG_CLONE1, S_FANG_CLONE3}, // S_FANG_INTRO2 + {SPR_FANG, 12, 0, {A_SetObjectFlags}, MF_NOGRAVITY, 1, S_FANG_CLONE4}, // S_FANG_CLONE3 + {SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_IDLE0, 0, S_FANG_CLONE4}, // S_FANG_CLONE4 + {SPR_FANG, 0, 0, {A_SetObjectFlags}, MF_NOCLIPTHING, 1, S_FANG_IDLE1}, // S_FANG_IDLE0 {SPR_FANG, 2, 16, {A_Look}, 1, 0, S_FANG_IDLE2}, // S_FANG_IDLE1 {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE3}, // S_FANG_IDLE2 @@ -1440,8 +1448,8 @@ state_t states[NUMSTATES] = {SPR_FANG, 21, 0, {A_DoNPCPain}, 0, 0, S_FANG_DIE2}, // S_FANG_DIE1 {SPR_FANG, 21, 1, {A_Boss5CheckOnGround}, S_FANG_DIE3, 0, S_FANG_DIE2}, // S_FANG_DIE2 - {SPR_FANG, 22, 0, {A_Scream}, 0, 0, S_FANG_DIE4}, // S_FANG_DIE3 - {SPR_FANG, 22, 104, {NULL}, 0, 0, S_FANG_DIE5}, // S_FANG_DIE4 + {SPR_FANG, 22, 0, {A_Scream}, 0, 0, S_FANG_DIE4}, // S_FANG_DIE3 + {SPR_FANG, 22, -1, {A_SetFuse}, 70, 0, S_FANG_DIE5}, // S_FANG_DIE4 {SPR_FANG, 11, 0, {A_PlaySound}, sfx_jump, 0, S_FANG_DIE6}, // S_FANG_DIE5 {SPR_FANG, 11, 1, {A_ZThrust}, 6, (1<<16)|1, S_FANG_DIE7}, // S_FANG_DIE6 @@ -1466,6 +1474,15 @@ state_t states[NUMSTATES] = {SPR_WHAT, FF_ANIMATE|FF_FULLBRIGHT, 4, {NULL}, 1, 2, S_ALART2}, // S_ALART1 {SPR_WHAT, 2|FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 1, 2, S_NULL}, // S_ALART2 + {SPR_VWRE, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_VWREF + {SPR_VWRE, 1|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_VWREB + + {SPR_PROJ, FF_TRANS20|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PROJECTORLIGHT2}, // S_PROJECTORLIGHT1 + {SPR_PROJ, 1|FF_TRANS40|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_PROJECTORLIGHT3}, // S_PROJECTORLIGHT2 + {SPR_PROJ, 2|FF_TRANS20|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_PROJECTORLIGHT4}, // S_PROJECTORLIGHT3 + {SPR_PROJ, 3|FF_TRANS40|FF_FULLBRIGHT, 2, {A_Repeat}, 39, S_PROJECTORLIGHT2, S_PROJECTORLIGHT5}, // S_PROJECTORLIGHT4 + {SPR_PROJ, 4|FF_TRANS60|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_PROJECTORLIGHT5 + {SPR_FBOM, 0, 1, {A_GhostMe}, 0, 0, S_FBOMB2}, // S_FBOMB1 {SPR_FBOM, 1, 1, {A_GhostMe}, 0, 0, S_FBOMB1}, // S_FBOMB2 {SPR_BARX, 0|FF_FULLBRIGHT, 3, {A_SetObjectFlags}, MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP, 0, S_FBOMB_EXPL2}, // S_FBOMB_EXPL1 @@ -5706,6 +5723,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_VWREF + -1, // doomednum + S_VWREF, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 3, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 42*FRACUNIT, // radius + 12*FRACUNIT, // height + 1, // display offset + 1000, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_VWREB + -1, // doomednum + S_VWREB, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 3, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 42*FRACUNIT, // radius + 12*FRACUNIT, // height + -1, // display offset + 1000, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_PROJECTORLIGHT + -1, // doomednum + S_PROJECTORLIGHT1, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 3, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 42*FRACUNIT, // radius + 52*FRACUNIT, // height + -1, // display offset + 1000, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_FBOMB -1, // doomednum S_FBOMB1, // spawnstate diff --git a/src/info.h b/src/info.h index 771eb18d3..6d04f388c 100644 --- a/src/info.h +++ b/src/info.h @@ -342,6 +342,8 @@ typedef enum sprite SPR_FANG, // replaces EGGQ SPR_BRKN, SPR_WHAT, + SPR_VWRE, + SPR_PROJ, // projector light SPR_FBOM, SPR_FSGN, SPR_BARX, // bomb explosion (also used by barrel) @@ -1499,6 +1501,7 @@ typedef enum state // Boss 5 S_FANG_SETUP, + S_FANG_INTRO0, S_FANG_INTRO1, S_FANG_INTRO2, S_FANG_INTRO3, @@ -1511,6 +1514,10 @@ typedef enum state S_FANG_INTRO10, S_FANG_INTRO11, S_FANG_INTRO12, + S_FANG_CLONE1, + S_FANG_CLONE2, + S_FANG_CLONE3, + S_FANG_CLONE4, S_FANG_IDLE0, S_FANG_IDLE1, S_FANG_IDLE2, @@ -1594,6 +1601,15 @@ typedef enum state S_ALART1, S_ALART2, + S_VWREF, + S_VWREB, + + S_PROJECTORLIGHT1, + S_PROJECTORLIGHT2, + S_PROJECTORLIGHT3, + S_PROJECTORLIGHT4, + S_PROJECTORLIGHT5, + S_FBOMB1, S_FBOMB2, S_FBOMB_EXPL1, @@ -4026,6 +4042,9 @@ typedef enum mobj_type // Boss 5 MT_FANG, MT_BROKENROBOT, + MT_VWREF, + MT_VWREB, + MT_PROJECTORLIGHT, MT_FBOMB, MT_TNTDUST, // also used by barrel MT_FSGNA, diff --git a/src/p_enemy.c b/src/p_enemy.c index 6eec0e9b6..c35d903af 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4026,13 +4026,17 @@ bossjustdie: } case MT_FANG: { + if (mo->flags2 & MF2_SLIDEPUSH) + { + P_RemoveMobj(mo); + return; + } if (mo->tracer) { var1 = var2 = 0; A_Boss5Jump(mo); mo->momx = ((16 - 1)*mo->momx)/16; mo->momy = ((16 - 1)*mo->momy)/16; - if (!(mo->spawnpoint && mo->spawnpoint->options & MTF_EXTRA)) { const fixed_t time = FixedHypot(mo->tracer->x - mo->x, mo->tracer->y - mo->y)/FixedHypot(mo->momx, mo->momy); const fixed_t speed = 64*FRACUNIT; @@ -12980,8 +12984,8 @@ void A_Boss5MakeItRain(mobj_t *actor) // // Description: Make a mess. // -// var1 = state # to set on MT_BROKENROBOT (if 0 do nothing) -// var2 = mode (0 = make 1, & 1 make 8, & 2 alart mode) +// var1 = state # to set on MT_BROKENROBOT (if 0 do nothing, if -1 go to if colorized) +// var2 = mode (-1 = spin, 0 = make 1, & 1 make 8, & 2 alart mode) // void A_Boss5MakeJunk(mobj_t *actor) { @@ -12995,8 +12999,45 @@ void A_Boss5MakeJunk(mobj_t *actor) return; #endif - if (leveltime < 2) + if (locvar1 < 0 && (actor->flags2 & MF2_SLIDEPUSH)) // this entire action is a hack, don't judge me + { + INT32 curextravalue2 = actor->extravalue2; + P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_PROJECTORLIGHT); + actor->z += P_MobjFlip(actor)*actor->height; + actor->flags |= MF_NOGRAVITY; + S_StartSound(actor, sfx_vwre); + actor->extravalue2 = 49; + P_SetMobjState(actor, -locvar1); + actor->extravalue2 = curextravalue2; + actor->angle -= FixedAngle((49*45)<extravalue2)/50; + if (trans > 9) + trans = 9; + if (trans < 0) + trans = 0; + if (!(actor->extravalue2 & 1)) + { + if (actor->extravalue2 > 10) + { + mobj_t *front = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_VWREF); + broked = P_SpawnMobjFromMobj(front, 0, 0, 0, MT_VWREB); + front->z = broked->z = front->z - broked->height; + P_SetObjectMomZ(front, (4<momz = front->momz; + broked->fuse = front->fuse = (actor->height+(2*front->height))/front->momz; + } + if (!(actor->colorized = !actor->colorized)) + actor->frame |= FF_FULLBRIGHT; + } + actor->angle += ANGLE_45; + actor->frame = (actor->frame & ~FF_TRANSMASK)|(trans<angle = ang; P_InstaThrust(broked, ang, ((locvar2 & 2) ? 8 : 5)*actor->scale); P_SetObjectMomZ(broked, (((locvar2) ? 4 : 0) + P_RandomRange(2, 5))< 0) P_SetMobjState(broked, locvar1); if (!P_MobjWasRemoved(broked)) P_TeleportMove(broked, broked->x + broked->momx, broked->y + broked->momy, broked->z); diff --git a/src/p_mobj.c b/src/p_mobj.c index a459300a1..d9ddda26c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5063,6 +5063,24 @@ static void P_Boss5Thinker(mobj_t *mobj) { if (!mobj->health) { + if (mobj->fuse) + { + if (mobj->flags2 & MF2_SLIDEPUSH) + { + INT32 trans = 10-((10*mobj->fuse)/70); + if (trans > 9) + trans = 9; + if (trans < 0) + trans = 0; + mobj->frame = (mobj->frame & ~FF_TRANSMASK)|(trans<fuse & 1)) + { + mobj->colorized = !mobj->colorized; + mobj->frame ^= FF_FULLBRIGHT; + } + } + return; + } if (mobj->state == &states[mobj->info->xdeathstate]) mobj->momz -= (2*FRACUNIT)/3; else if (mobj->tracer && P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y) < 2*mobj->radius) @@ -7649,6 +7667,17 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->movedir) mobj->angle += mobj->movedir; break; + case MT_VWREF: + case MT_VWREB: + { + INT32 strength; + ++mobj->movedir; + mobj->frame &= ~FF_TRANSMASK; + strength = min(mobj->fuse, mobj->movedir)*3; + if (strength < 10) + mobj->frame |= ((10-strength)<<(FF_TRANSSHIFT)); + } + /* FALLTHRU */ default: if (mobj->fuse) { // Scenery object fuse! Very basic! @@ -9269,6 +9298,18 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s } P_RemoveMobj(mobj); return; + case MT_FANG: + if (mobj->flags2 & MF2_SLIDEPUSH) + { + var1 = 0; + var2 = 0; + A_BossDeath(mobj); + return; + } + P_SetMobjState(mobj, mobj->state->nextstate); + if (P_MobjWasRemoved(mobj)) + return; + break; case MT_METALSONIC_BATTLE: break; // don't remove case MT_SPIKE: @@ -9867,7 +9908,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } - if (sc != -1) + if (sc != -1 && !(mobj->flags2 & MF2_SLIDEPUSH)) { UINT8 i; for (i = 0; i < MAXPLAYERS; i++) @@ -9879,6 +9920,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { mobj->color = SKINCOLOR_SILVER; mobj->colorized = true; + mobj->flags2 |= MF2_SLIDEPUSH; break; } } @@ -11225,6 +11267,16 @@ You should think about modifying the deathmatch starts to take full advantage of else mobj->health = FixedMul(ss->sector->ceilingheight-ss->sector->floorheight, 3*(FRACUNIT/4))>>FRACBITS; break; + case MT_FANG: + case MT_METALSONIC_RACE: + case MT_METALSONIC_BATTLE: + if (mthing->options & MTF_EXTRA) + { + mobj->color = SKINCOLOR_SILVER; + mobj->colorized = true; + mobj->flags2 |= MF2_SLIDEPUSH; + } + break; case MT_BALLOON: if (mthing->angle > 0) mobj->color = ((mthing->angle-1) % (MAXSKINCOLORS-1))+1; diff --git a/src/sounds.c b/src/sounds.c index 1bfcf4cc8..cf37efd0a 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -213,6 +213,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"corkp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork fired"}, {"corkh", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork hit"}, {"alart", false, 200, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Caught red handed!"}, + {"vwre", false, 200, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Clone fighter!"}, {"bowl", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bowling"}, {"chuchu", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Train horn"}, {"bsnipe", false, 200, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Home-run smash"}, diff --git a/src/sounds.h b/src/sounds.h index d25a22619..e5568b59a 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -262,6 +262,7 @@ typedef enum sfx_corkp, sfx_corkh, sfx_alart, + sfx_vwre, sfx_bowl, sfx_chuchu, sfx_bsnipe, From 264c60fb70fd9130ce8d74ef125ed0b17917db8c Mon Sep 17 00:00:00 2001 From: lachwright Date: Fri, 27 Sep 2019 02:13:02 +0800 Subject: [PATCH 195/196] P_SetTarget fixes, added SPR_SHCK to hw_light.c --- src/hardware/hw_light.c | 1 + src/p_enemy.c | 6 +++--- src/p_mobj.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 1de20cad7..ecd741849 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -197,6 +197,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_EGGO &lspr[NOLIGHT], // SPR_SEBH &lspr[NOLIGHT], // SPR_FAKE + &lspr[NOLIGHT], // SPR_SHCK // Boss 4 (Castle Eggman) &lspr[NOLIGHT], // SPR_EGGP diff --git a/src/p_enemy.c b/src/p_enemy.c index dceaa6c46..f9467b2fb 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8106,7 +8106,7 @@ void A_Boss3ShockThink(mobj_t *actor) // Break the link if movements are too different if (FixedHypot(snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale) { - actor->hnext = NULL; + P_SetTarget(&actor->hnext, NULL); return; } @@ -8125,8 +8125,8 @@ void A_Boss3ShockThink(mobj_t *actor) P_SetTarget(&snew->target, actor->target); snew->fuse = actor->fuse; - actor->hnext = snew; - snew->hnext = snext; + P_SetTarget(&actor->hnext, snew); + P_SetTarget(&snew->hnext, snext); } } } diff --git a/src/p_mobj.c b/src/p_mobj.c index cdc6e4be1..45eda27d1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4627,8 +4627,8 @@ static void P_Boss3Thinker(mobj_t *mobj) else { if (i == numtospawn - 1) - shock->hnext = sfirst; - sprev->hnext = shock; + P_SetTarget(&shock->hnext, sfirst); + P_SetTarget(&sprev->hnext, shock); } P_Thrust(shock, ang, shock->info->speed); From bc128dffdcc5334b5b6f14faf195c9a1bf69ee1f Mon Sep 17 00:00:00 2001 From: sphere Date: Thu, 26 Sep 2019 23:15:52 +0200 Subject: [PATCH 196/196] Fix compiling errors, thanks to toaster. --- src/p_mobj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 45eda27d1..9da934616 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4607,7 +4607,7 @@ static void P_Boss3Thinker(mobj_t *mobj) { UINT8 i, numtospawn = 24; angle_t ang = 0, interval = FixedAngle((360 << FRACBITS) / numtospawn); - mobj_t *shock, *sfirst, *sprev; + mobj_t *shock, *sfirst, *sprev = NULL; mobj->movecount = mobj->health+1; mobj->movefactor = -512*FRACUNIT; @@ -4622,7 +4622,7 @@ static void P_Boss3Thinker(mobj_t *mobj) if (i % 2 == 0) P_SetMobjState(shock, shock->state->nextstate); - if (i == 0) + if (!sprev) sfirst = shock; else {

l)j}@K+?}=3h*Jh;?2_uiRlu8B-V@NuP5S| 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 e Date: Sun, 28 Jul 2019 17:06:01 -0400 Subject: [PATCH 036/196] Update mac icon --- src/sdl/macosx/Srb2mac.icns | Bin 83019 -> 173437 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/sdl/macosx/Srb2mac.icns b/src/sdl/macosx/Srb2mac.icns index 96cb8a36d991818eb03b7d80343518ab168bda7d..a3e37aab3ee846900a873610f7d8d66fd34bfda3 100644 GIT binary patch literal 173437 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 Date: Sun, 28 Jul 2019 18:19:00 -0400 Subject: [PATCH 037/196] Change it back to const char --- src/sdl/IMG_xpm.c | 22 +++++++++++----------- src/sdl/SDL_icon.xpm | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index 51cedfd6d..4c56ce363 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -34,7 +34,7 @@ * * Besides the standard API, also provides * - * SDL_Surface *IMG_ReadXPMFromArray(char **xpm) + * SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) * * that reads the image data from an XPM file included in the C source. * @@ -48,7 +48,7 @@ // SDLCALL terms removed from original SDL_image declarations int IMG_isXPM(SDL_RWops *src); SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src); -SDL_Surface *IMG_ReadXPMFromArray(char **xpm); +SDL_Surface *IMG_ReadXPMFromArray(const char **xpm); #define IMG_SetError SDL_SetError #define IMG_GetError SDL_GetError #endif @@ -192,7 +192,7 @@ static void free_colorhash(struct color_hash *hash) * convert colour spec to RGB (in 0xrrggbb format). * return 1 if successful. */ -static int color_to_rgb(char *spec, int speclen, Uint32 *rgb) +static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) { /* poor man's rgb.txt */ static struct { char *name; Uint32 rgb; } known[] = { @@ -933,7 +933,7 @@ static char *error; * If len > 0, it's assumed to be at least len chars (for efficiency). * Return NULL and set error upon EOF or parse error. */ -static char *get_next_line(char ***lines, SDL_RWops *src, int len) +static const char *get_next_line(const char ***lines, SDL_RWops *src, int len) { char *linebufnew; @@ -1005,7 +1005,7 @@ do { \ } while (0) /* read XPM from either array or RWops */ -static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src) +static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) { Sint64 start = 0; SDL_Surface *image = NULL; @@ -1017,8 +1017,8 @@ static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src) struct color_hash *colors = NULL; SDL_Color *im_colors = NULL; char *keystrings = NULL, *nextkey; - char *line; - char ***xpmlines = NULL; + const char *line; + const char ***xpmlines = NULL; int pixels_len; error = NULL; @@ -1085,7 +1085,7 @@ static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src) goto done; } for (index = 0; index < ncolors; ++index ) { - char *p; + const char *p; line = get_next_line(xpmlines, src, 0); if (!line) goto done; @@ -1095,7 +1095,7 @@ static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src) /* parse a colour definition */ for (;;) { char nametype; - char *colname; + const char *colname; Uint32 rgb, pixel; SKIPSPACE(p); @@ -1188,7 +1188,7 @@ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) return load_xpm(NULL, src); } -SDL_Surface *IMG_ReadXPMFromArray(char **xpm) +SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) { if (!xpm) { IMG_SetError("array is NULL"); @@ -1212,7 +1212,7 @@ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) return(NULL); } -SDL_Surface *IMG_ReadXPMFromArray(char **xpm) +SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) { return NULL; } diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm index c5c957468..2180d782c 100644 --- a/src/sdl/SDL_icon.xpm +++ b/src/sdl/SDL_icon.xpm @@ -1,5 +1,5 @@ /* XPM */ -static char * SDL_icon_xpm[] = { +static const char *SDL_icon_xpm[] = { "64 64 32 1", " c None", ". c #000271", From 1a643b1bd9f64dbaea280d261c179a9709a406a9 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Mon, 29 Jul 2019 19:08:55 -0400 Subject: [PATCH 038/196] SDL2: more const in xpm code --- src/sdl/IMG_xpm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index 4c56ce363..f156335fa 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -79,7 +79,7 @@ int IMG_isXPM(SDL_RWops *src) #define STARTING_HASH_SIZE 256 struct hash_entry { - char *key; + const char *key; Uint32 color; struct hash_entry *next; }; @@ -152,7 +152,7 @@ static struct color_hash *create_colorhash(int maxnum) } static int add_colorhash(struct color_hash *hash, - char *key, int cpp, Uint32 color) + const char *key, int cpp, Uint32 color) { int index = hash_key(key, cpp, hash->size); struct hash_entry *e = hash->next_free++; @@ -195,7 +195,7 @@ static void free_colorhash(struct color_hash *hash) static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) { /* poor man's rgb.txt */ - static struct { char *name; Uint32 rgb; } known[] = { + static struct { const char *name; Uint32 rgb; } known[] = { { "none", 0xFFFFFFFF }, { "black", 0x000000 }, { "white", 0xFFFFFF }, @@ -926,7 +926,7 @@ static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) static char *linebuf; static int buflen; -static char *error; +static const char *error; /* * Read next line from the source. From a8637d034e07afebd6a980604d5f8bb53cef2738 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Mon, 29 Jul 2019 19:22:14 -0400 Subject: [PATCH 039/196] SDL2: more consts --- src/sdl/IMG_xpm.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index f156335fa..43fb4ded2 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -88,8 +88,8 @@ struct color_hash { struct hash_entry **table; struct hash_entry *entries; /* array of all entries */ struct hash_entry *next_free; - int size; - int maxnum; + size_t size; + size_t maxnum; }; static int hash_key(const char *key, int cpp, int size) @@ -103,9 +103,9 @@ static int hash_key(const char *key, int cpp, int size) return hash & (size - 1); } -static struct color_hash *create_colorhash(int maxnum) +static struct color_hash *create_colorhash(size_t maxnum) { - int bytes, s; + size_t bytes, s; struct color_hash *hash; /* we know how many entries we need, so we can allocate @@ -164,7 +164,7 @@ static int add_colorhash(struct color_hash *hash, } /* fast lookup that works if cpp == 1 */ -#define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color) +#define QUICK_COLORHASH(hash, key) ((hash)->table[*(const Uint8 *)(key)]->color) static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp) { @@ -909,7 +909,7 @@ static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) *rgb = (Uint32)SDL_strtol(buf, NULL, 16); return 1; } else { - int i; + size_t i; for (i = 0; i < SDL_arraysize(known); i++) { if (SDL_strncasecmp(known[i].name, spec, speclen) == 0) { *rgb = known[i].rgb; @@ -1009,10 +1009,11 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) { Sint64 start = 0; SDL_Surface *image = NULL; - int index; + size_t index; int x, y; - int w, h, ncolors, cpp; - int indexed; + int w, h, cpp; + size_t ncolors; + size_t indexed; Uint8 *dst; struct color_hash *colors = NULL; SDL_Color *im_colors = NULL; @@ -1043,7 +1044,7 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) * Right now we don't use the hotspots but it should be handled * one day. */ - if (SDL_sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 + if (SDL_sscanf(line, "%d %d %lu %d", &w, &h, &ncolors, &cpp) != 4 || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) { error = "Invalid format description"; goto done; From c93fb440e543c7683f9998c01683ab999ab1478c Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 30 Jul 2019 20:56:03 -0400 Subject: [PATCH 040/196] use VERSIONSTRING --- src/win32/Srb2win.rc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index f133a4bcb..8e7fdccc9 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -66,8 +66,8 @@ END #include "../doomdef.h" // Needed for version string VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,2 - PRODUCTVERSION 2,2 + FILEVERSION 2,2,0,0 + PRODUCTVERSION 2,2,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -85,14 +85,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", "2.2\0" + VALUE "FileVersion", VERSIONSTRING VALUE "InternalName", "srb2\0" VALUE "LegalCopyright", "Copyright 1998-2019 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", "2.2\0" + VALUE "ProductVersion", VERSIONSTRING VALUE "SpecialBuild", "\0" END END From 46993268aecf7b239441ffdd0612d6c02be33ded Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 4 Aug 2019 20:02:38 -0400 Subject: [PATCH 041/196] * Added FORCERESETMUSIC level header * cv_resetmusicbyheader toggle to disable said override * Never reset music during time attack * Change cv_resetmusic default back to off --- src/dehacked.c | 14 ++++++++++++++ src/doomstat.h | 2 ++ src/g_game.c | 2 +- src/lua_maplib.c | 2 ++ src/m_menu.c | 13 +++++++++++-- src/p_setup.c | 5 +++-- src/s_sound.c | 7 +++++-- src/s_sound.h | 9 +++++++++ 8 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 2f28a74cf..92b0ee841 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1181,6 +1181,20 @@ static void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->muspostbosspos = (UINT32)get_number(word2); else if (fastcmp(word, "MUSICPOSTBOSSFADEIN")) mapheaderinfo[num-1]->muspostbossfadein = (UINT32)get_number(word2); + else if (fastcmp(word, "FORCERESETMUSIC")) + { + // This is a weird one because "FALSE"/"NO" could either apply to "leave to default preference" (cv_resetmusic) + // or "force off". Let's assume it means "force off", and let an unspecified value mean "default preference" + if (fastcmp(word2, "OFF") || word2[0] == 'F' || word2[0] == 'N') i = 0; + else if (fastcmp(word2, "ON") || word2[0] == 'T' || word2[0] == 'Y') i = 1; + else i = -1; // (fastcmp(word2, "DEFAULT")) + + if (i >= -1 && i <= 1) // -1 to force off, 1 to force on, 0 to honor default. + // This behavior can be disabled with cv_resetmusicbyheader + mapheaderinfo[num-1]->musforcereset = (SINT8)i; + else + deh_warning("Level header %d: invalid forceresetmusic option %d", num, i); + } else if (fastcmp(word, "FORCECHARACTER")) { strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); diff --git a/src/doomstat.h b/src/doomstat.h index b6c376d1c..a70a122a6 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -333,6 +333,8 @@ typedef struct UINT32 muspostbosspos; ///< Post-bossdeath position UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds. + SINT8 musforcereset; ///< Force resetmusic (-1 for default; 0 for force off; 1 for force on) + // Lua stuff. // (This is not ifdeffed so the map header structure can stay identical, just in case.) UINT8 numCustomOptions; ///< Internal. For Lua custom value support. diff --git a/src/g_game.c b/src/g_game.c index dad873fe7..3c623e7f1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2310,7 +2310,7 @@ void G_PlayerReborn(INT32 player) } // This is in S_Start, but this was not here previously. - // if (cv_resetmusic.value) + // if (RESETMUSIC) // S_StopMusic(); S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 74b259921..dbb69b7e2 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2017,6 +2017,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->muspostbosspos); else if (fastcmp(field,"muspostbossfadein")) lua_pushinteger(L, header->muspostbossfadein); + else if (fastcmp(field,"musforcereset")) + lua_pushinteger(L, header->musforcereset); else if (fastcmp(field,"forcecharacter")) lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) diff --git a/src/m_menu.c b/src/m_menu.c index a3e986fdf..57ce6a3ca 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1322,6 +1322,12 @@ static menuitem_t OP_SoundOptionsMenu[] = #define OPENMPT_MENUOFFSET 0 #endif +#ifdef HAVE_MIXERX +#define MIXERX_MENUOFFSET 81 +#else +#define MIXERX_MENUOFFSET 0 +#endif + static menuitem_t OP_SoundAdvancedMenu[] = { #ifdef HAVE_OPENMPT @@ -1333,12 +1339,15 @@ static menuitem_t OP_SoundAdvancedMenu[] = {IT_HEADER, NULL, "MIDI Settings", NULL, OPENMPT_MENUOFFSET+10}, {IT_STRING | IT_CVAR, NULL, "MIDI Player", &cv_midiplayer, OPENMPT_MENUOFFSET+22}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "FluidSynth Sound Font File", &cv_midisoundfontpath, OPENMPT_MENUOFFSET+34}, - {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+61} + {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+61}, #endif + + {IT_HEADER, NULL, "Miscellaneous", NULL, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+10}, + {IT_STRING | IT_CVAR, NULL, "Let Levels Force Reset Music", &cv_resetmusicbyheader, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+22}, }; #undef OPENMPT_MENUOFFSET - +#undef MIXERX_MENUOFFSET #endif static menuitem_t OP_DataOptionsMenu[] = diff --git a/src/p_setup.c b/src/p_setup.c index d0cd14b22..29f63f1bb 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -221,6 +221,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->muspostbosstrack = 0; mapheaderinfo[num]->muspostbosspos = 0; mapheaderinfo[num]->muspostbossfadein = 0; + mapheaderinfo[num]->musforcereset = -1; mapheaderinfo[num]->forcecharacter[0] = '\0'; mapheaderinfo[num]->weather = 0; mapheaderinfo[num]->skynum = 1; @@ -2692,7 +2693,7 @@ boolean P_SetupLevel(boolean skipprecip) S_StartSound(NULL, sfx_s3kaf); // Fade music! Time it to S3KAF: 0.25 seconds is snappy. - if (cv_resetmusic.value || + if (RESETMUSIC || strnicmp(S_MusicName(), (mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7)) S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE) @@ -2725,7 +2726,7 @@ boolean P_SetupLevel(boolean skipprecip) // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!titlemapinaction && (cv_resetmusic.value || + if (!titlemapinaction && (RESETMUSIC || strnicmp(S_MusicName(), (mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7))) S_FadeMusic(0, FixedMul( diff --git a/src/s_sound.c b/src/s_sound.c index 2c6faf041..bcb20833a 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -108,7 +108,9 @@ consvar_t cv_closedcaptioning = {"closedcaptioning", "Off", CV_SAVE|CV_CALL, CV_ consvar_t cv_numChannels = {"snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum, 0, NULL, NULL, 0, 0, NULL}; static consvar_t surround = {"surround", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_resetmusic = {"resetmusic", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, 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}; // 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}; @@ -275,6 +277,7 @@ void S_RegisterSoundStuff(void) CV_RegisterVar(&surround); CV_RegisterVar(&cv_samplerate); CV_RegisterVar(&cv_resetmusic); + CV_RegisterVar(&cv_resetmusicbyheader); CV_RegisterVar(&cv_gamesounds); CV_RegisterVar(&cv_gamedigimusic); CV_RegisterVar(&cv_gamemidimusic); @@ -2096,7 +2099,7 @@ void S_StartEx(boolean reset) mapmusposition = mapheaderinfo[gamemap-1]->muspos; } - if (cv_resetmusic.value || reset) + if (RESETMUSIC || reset) S_StopMusic(); S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); diff --git a/src/s_sound.h b/src/s_sound.h index e0737eff7..48128527c 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -31,7 +31,16 @@ openmpt_module *openmpt_mhandle; extern consvar_t stereoreverse; extern consvar_t cv_soundvolume, cv_closedcaptioning, cv_digmusicvolume, cv_midimusicvolume; extern consvar_t cv_numChannels; + extern consvar_t cv_resetmusic; +extern consvar_t cv_resetmusicbyheader; + +#define RESETMUSIC (!modeattacking && \ + (cv_resetmusicbyheader.value ? \ + (mapheaderinfo[gamemap-1]->musforcereset != -1 ? mapheaderinfo[gamemap-1]->musforcereset : cv_resetmusic.value) \ + : cv_resetmusic.value) \ + ) + extern consvar_t cv_gamedigimusic; extern consvar_t cv_gamemidimusic; extern consvar_t cv_gamesounds; From 598e9017b121ba0894bb97d3b50b3b4981bf0d14 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 13 Aug 2019 20:11:44 +0100 Subject: [PATCH 042/196] Fix P_PlayerCanDamage for CA_FLY and CA_BOUNCE to be less lenient in causing damage, by making them based off the top and bottom of the player object respectively rather than its vertical center. --- src/p_user.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index b74cafd21..1bf906061 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1051,6 +1051,8 @@ void P_ResetPlayer(player_t *player) // boolean P_PlayerCanDamage(player_t *player, mobj_t *thing) { + fixed_t bottomheight, topheight; + if (!player->mo || player->spectator || !thing || P_MobjWasRemoved(thing)) return false; @@ -1090,13 +1092,26 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing) return true; // From the top/bottom. - if (P_MobjFlip(player->mo)*(player->mo->z - (thing->z + thing->height/2)) > 0) + bottomheight = player->mo->z; + topheight = player->mo->z + player->mo->height; + + if (player->mo->eflags & MFE_VERTICALFLIP) { - if ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(player->mo)*player->mo->momz < 0)) + fixed_t swap = bottomheight; + bottomheight = topheight; + topheight = swap; + } + + if (P_MobjFlip(player->mo)*(bottomheight - (thing->z + thing->height/2)) > 0) + { + if ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) < 0)) + return true; + } + else if (P_MobjFlip(player->mo)*(topheight - (thing->z + thing->height/2)) < 0) + { + if (player->charability == CA_FLY && player->panim == PA_ABILITY && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0)) return true; } - else if (player->charability == CA_FLY && player->panim == PA_ABILITY) - return true; // Shield stomp. if (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY)) From 2dd5f1abe3212eb9d6f2b9ee8e6127900fc68f6e Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Tue, 20 Aug 2019 18:18:29 +0100 Subject: [PATCH 043/196] Fixes for lib_cvRegisterVar (the Lua version of CV_RegisterVar): * Make sure the consvar's properties are all initialised to zeros as defaults * Error if the consvar is not given a name * Error if the consvar has CV_CALL but no call function * Error if the consvar has CV_NOINIT but not CV_CALL --- src/lua_consolelib.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index dced4e43c..7766ba1c2 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -303,6 +303,8 @@ static int lib_cvRegisterVar(lua_State *L) #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("CV_RegisterVar") " (%s)", e); #define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1))) + memset(cvar, 0x00, sizeof(consvar_t)); // zero everything by default + lua_pushnil(L); while (lua_next(L, 1)) { // stack: cvar table, cvar userdata, key/index, value @@ -390,6 +392,13 @@ static int lib_cvRegisterVar(lua_State *L) #undef FIELDERROR #undef TYPEERROR + if (!cvar->name) + return luaL_error(L, M_GetText("Variable has no name!\n")); + if ((cvar->flags & CV_NOINIT) && !(cvar->flags & CV_CALL)) + return luaL_error(L, M_GetText("Variable %s has CV_NOINIT without CV_CALL\n"), cvar->name); + if ((cvar->flags & CV_CALL) && !cvar->func) + return luaL_error(L, M_GetText("Variable %s has CV_CALL without a function\n"), cvar->name); + // stack: cvar table, cvar userdata lua_getfield(L, LUA_REGISTRYINDEX, "CV_Vars"); I_Assert(lua_istable(L, 3)); From 6ef9ee5467cf8f67847999d0d530153b304ae562 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 20 Aug 2019 15:43:59 -0400 Subject: [PATCH 044/196] true not TRUE --- src/m_cheat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index 6fa3ea113..05b851fef 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -109,7 +109,7 @@ static UINT8 cheatf_devmode(void) G_SetGameModified(false); for (i = 0; i < MAXUNLOCKABLES; i++) unlockables[i].unlocked = true; - devparm = TRUE; + devparm = true; cv_debug |= 0x8000; // Refresh secrets menu existing. From 41d5ffa17f14aadbbbc34fbbff4ec8740ccc409d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 20 Aug 2019 20:21:53 -0300 Subject: [PATCH 045/196] Fix OpenGL renderer crash with -skipintro command line parameter. --- src/v_video.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index 2ec06a787..082d84915 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -856,8 +856,8 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ return; #ifdef HWRENDER - // Done - if (rendermode != render_soft && !con_startup) + //if (rendermode != render_soft && !con_startup) // Not this again + if (rendermode != render_soft) { HWR_DrawCroppedPatch((GLPatch_t*)patch,x,y,pscale,scrn,sx,sy,w,h); return; @@ -1174,7 +1174,8 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) return; #ifdef HWRENDER - if (rendermode != render_soft && !con_startup) + //if (rendermode != render_soft && !con_startup) // Not this again + if (rendermode != render_soft) { HWR_DrawFill(x, y, w, h, c); return; From fc1abdd7f8ac083f86c63722a80e7142b0f51299 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 22 Aug 2019 16:13:00 +0100 Subject: [PATCH 046/196] Autobrake polish! * Make it actually look like a brake, so people can tell it's on instead of just having high friction. * Fix it fucking with spring chains (caused by being unwittingly active for the first tic of spring collision). --- src/p_map.c | 3 +++ src/p_user.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/p_map.c b/src/p_map.c index cc9209ea8..15fa97c8f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -285,6 +285,9 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (spring->info->painchance != 2) { + if (object->player) + object->player->pflags &= ~PF_APPLYAUTOBRAKE; + if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA { object->momx = object->momy = 0; diff --git a/src/p_user.c b/src/p_user.c index fc1d97e49..74901c469 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11233,6 +11233,16 @@ void P_PlayerThink(player_t *player) if (!currentlyonground) acceleration /= 2; + // fake skidding! see P_SkidStuff for reference on conditionals + else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed/2, player->mo->scale)) + { + if (player->mo->state-states != S_PLAY_SKID) + P_SetPlayerMobjState(player->mo, S_PLAY_SKID); + player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; + + if (P_IsLocalPlayer(player)) // the sound happens way more frequently now, so give co-op players' ears a brake... + S_StartSound(player->mo, sfx_skid); + } if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... acceleration = FixedMul(acceleration<mo->movefactor)>>FRACBITS; From 1f2baf5b6bad77edf176c2a4e9115bb0c70cd0bb Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 22 Aug 2019 22:30:36 +0100 Subject: [PATCH 047/196] Make polyobjects agree with sector lighting/colormap like a FOF. Caution: has weird retry/reload bug. Ask sphere for sample map/coords. --- src/hardware/hw_main.c | 19 ++++++++++++------- src/r_bsp.c | 10 +++++----- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c600800fd..c6a8b16e5 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3333,6 +3333,7 @@ static void HWR_AddPolyObjectPlanes(void) { size_t i; sector_t *polyobjsector; + INT32 light = 0; // Polyobject Planes need their own function for drawing because they don't have extrasubsectors by themselves // It should be okay because polyobjects should always be convex anyway @@ -3351,19 +3352,22 @@ static void HWR_AddPolyObjectPlanes(void) && polyobjsector->floorheight >= gr_frontsector->floorheight && (viewz < polyobjsector->floorheight)) { + light = R_GetPlaneLight(gr_frontsector, polyobjsector->floorheight, true); if (po_ptrs[i]->translucency > 0) { FSurfaceInfo Surf; - FBITFIELD blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); + FBITFIELD blendmode; + memset(&Surf, 0x00, sizeof(Surf)); + blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], false, polyobjsector->floorheight, - polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL); + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), Surf.FlatColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } else { HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum); HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude, - polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, - polyobjsector, 255, NULL); + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, + polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } } @@ -3371,6 +3375,7 @@ static void HWR_AddPolyObjectPlanes(void) && polyobjsector->ceilingheight <= gr_frontsector->ceilingheight && (viewz > polyobjsector->ceilingheight)) { + light = R_GetPlaneLight(gr_frontsector, polyobjsector->ceilingheight, true); if (po_ptrs[i]->translucency > 0) { FSurfaceInfo Surf; @@ -3378,14 +3383,14 @@ static void HWR_AddPolyObjectPlanes(void) memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], true, polyobjsector->ceilingheight, - polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL); + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), Surf.FlatColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } else { HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum); HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude, - polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum, - polyobjsector, 255, NULL); + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, + polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } } } diff --git a/src/r_bsp.c b/src/r_bsp.c index d521d9f4d..23e751420 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -1088,9 +1088,9 @@ static void R_Subsector(size_t num) { light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight); ffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic, - polysec->lightlevel, polysec->floor_xoffs, polysec->floor_yoffs, + (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floor_xoffs, polysec->floor_yoffs, polysec->floorpic_angle-po->angle, - NULL, NULL, po + (light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po #ifdef ESLOPE , NULL // will ffloors be slopable eventually? #endif @@ -1115,10 +1115,10 @@ static void R_Subsector(size_t num) && polysec->ceilingheight <= ceilingcenterz && (viewz > polysec->ceilingheight)) { - light = R_GetPlaneLight(frontsector, polysec->ceilingheight, viewz < polysec->ceilingheight); + light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight); ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic, - polysec->lightlevel, polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle, - NULL, NULL, po + (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle, + (light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po #ifdef ESLOPE , NULL // will ffloors be slopable eventually? #endif From e5071bb6052c5202ce1d48ef8af6738bdf090e34 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 23 Aug 2019 11:00:05 -0700 Subject: [PATCH 048/196] Compare the file name only for real CL_SendRequestFile prepends the path for each file. --- src/d_netfil.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index deb04fbe1..d25b6a278 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -752,9 +752,12 @@ void Got_Filetxpak(void) { INT32 filenum = netbuffer->u.filetxpak.fileid; fileneeded_t *file = &fileneeded[filenum]; - char *filename = file->filename; + char *filename; static INT32 filetime = 0; + filename = va("%s", file->filename); + nameonly(filename); + if (!(strcmp(filename, "srb2.srb") && strcmp(filename, "srb2.wad") && strcmp(filename, "zones.dta") @@ -765,6 +768,8 @@ void Got_Filetxpak(void) )) I_Error("Tried to download \"%s\"", filename); + filename = file->filename; + if (filenum >= fileneedednum) { DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum)); From 4e256b73b2c6d8a47c3bb1f46cf14fad4115764a Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Aug 2019 18:25:27 +0100 Subject: [PATCH 049/196] Lua save-banks! * Array of 8 INT32's natively embedded into savedata (net and SP)! * Initialised to zero whenever a new save (or equivalent) is started, otherwise untouched by the base game. * Requires reservation to avoid clobber-conflicts. * Access via `reserveLuabanks()` - returns a read-write userdata. * Assign userdata to local variable or global rawset to use later. Mostly for future SUGOIlikes, but I'm sure someone could figure out an unrelated usage eventually. --- src/d_main.c | 1 + src/d_netcmd.c | 4 +++ src/doomstat.h | 4 +++ src/g_game.c | 25 ++++++++++++++++- src/lua_baselib.c | 15 ++++++++++ src/lua_infolib.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++- src/lua_libs.h | 2 ++ src/m_menu.c | 27 +++++++++++++++++- src/p_saveg.c | 51 ++++++++++++++++++++++++++++++---- 9 files changed, 191 insertions(+), 8 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index eaeae4b10..d99fb7494 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -716,6 +716,7 @@ void D_StartTitle(void) botskin = 0; cv_debug = 0; emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); lastmaploaded = 0; // In case someone exits out at the same time they start a time attack run, diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3e82fc60c..c1183ebbe 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1908,7 +1908,10 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) precache = false; if (resetplayer && !FLS) + { emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); + } if (modeattacking) { @@ -4103,6 +4106,7 @@ void Command_ExitGame_f(void) botskin = 0; cv_debug = 0; emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); if (dirmenu) closefilemenu(true); diff --git a/src/doomstat.h b/src/doomstat.h index b6c376d1c..1bf67de61 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -418,6 +418,10 @@ extern UINT16 emeralds; #define EMERALD7 64 #define ALL7EMERALDS(v) ((v & (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) == (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) +// yes, even in non HAVE_BLUA +#define NUM_LUABANKS 8 // please only make this number go up between versions, never down. you'll break saves otherwise. also, must fit in UINT8 +extern INT32 luabanks[NUM_LUABANKS]; + extern INT32 nummaprings; //keep track of spawned rings/coins /** Time attack information, currently a very small structure. diff --git a/src/g_game.c b/src/g_game.c index e70241269..ec86c3d51 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -172,6 +172,7 @@ static boolean retrying = false; UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage. UINT16 emeralds; +INT32 luabanks[NUM_LUABANKS]; // yes, even in non HAVE_BLUA UINT32 token; // Number of tokens collected in a level UINT32 tokenlist; // List of tokens collected boolean gottoken; // Did you get a token? Used for end of act @@ -3778,7 +3779,29 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives) // File end marker check CHECKPOS - if (READUINT8(save_p) != 0x1d) BADSAVE; + switch (READUINT8(save_p)) + { + case 0xb7: + { + UINT8 i, banksinuse; + CHECKPOS + banksinuse = READUINT8(save_p); + CHECKPOS + if (banksinuse > NUM_LUABANKS) + BADSAVE + for (i = 0; i < banksinuse; i++) + { + (void)READINT32(save_p); + CHECKPOS + } + if (READUINT8(save_p) != 0x1d) + BADSAVE + } + case 0x1d: + break; + default: + BADSAVE + } // done saved = FIL_WriteFile(backup, savebuffer, length); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 3c136a436..a69e8a188 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -182,6 +182,8 @@ static const struct { {META_CAMERA, "camera_t"}, {META_ACTION, "action"}, + + {META_LUABANKS, "luabanks[]"}, {NULL, NULL} }; @@ -228,6 +230,18 @@ static int lib_isPlayerAdmin(lua_State *L) return 1; } +static int lib_reserveLuabanks(lua_State *L) +{ + static boolean reserved = false; + if (!lua_lumploading) + return luaL_error(L, "luabanks[] cannot be reserved from within a hook or coroutine!"); + if (reserved) + return luaL_error(L, "luabanks[] has already been reserved! Only one savedata-enabled mod at a time may use this feature."); + reserved = true; + LUA_PushUserdata(L, &luabanks, META_LUABANKS); + return 1; +} + // M_RANDOM ////////////// @@ -2736,6 +2750,7 @@ static luaL_Reg lib[] = { {"chatprintf", lib_chatprintf}, {"userdataType", lib_userdataType}, {"IsPlayerAdmin", lib_isPlayerAdmin}, + {"reserveLuabanks", lib_reserveLuabanks}, // m_random {"P_RandomFixed",lib_pRandomFixed}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 77f37f8ec..8338fa5b9 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -18,6 +18,7 @@ #include "p_mobj.h" #include "p_local.h" #include "z_zone.h" +#include "doomstat.h" // luabanks[] #include "lua_script.h" #include "lua_libs.h" @@ -146,7 +147,7 @@ static int lib_getSpr2default(lua_State *L) return luaL_error(L, "spr2defaults[] invalid index"); if (i >= free_spr2) - return 0; + return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, 0, free_spr2-1); lua_pushinteger(L, spr2defaults[i]); return 1; @@ -1026,6 +1027,61 @@ static int sfxinfo_num(lua_State *L) return 1; } +////////////// +// LUABANKS // +////////////// + +static int lib_getluabanks(lua_State *L) +{ + UINT8 i; + + lua_remove(L, 1); // don't care about luabanks[] dummy userdata. + + if (lua_isnumber(L, 1)) + i = lua_tonumber(L, 1); + else + return luaL_error(L, "luabanks[] invalid index"); + + if (i >= NUM_LUABANKS) + luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS); + + lua_pushinteger(L, luabanks[i]); + return 1; +} + +static int lib_setluabanks(lua_State *L) +{ + UINT8 i; + INT32 j = 0; + + if (hud_running) + return luaL_error(L, "Do not alter luabanks[] in HUD rendering code!"); + + lua_remove(L, 1); // don't care about luabanks[] dummy userdata. + + if (lua_isnumber(L, 1)) + i = lua_tonumber(L, 1); + else + return luaL_error(L, "luabanks[] invalid index"); + + if (i >= NUM_LUABANKS) + luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1); + + if (lua_isnumber(L, 2)) + j = lua_tonumber(L, 2); + else + return luaL_error(L, "luabanks[] invalid set"); + + luabanks[i] = j; + return 0; +} + +static int lib_luabankslen(lua_State *L) +{ + lua_pushinteger(L, NUM_LUABANKS); + return 1; +} + ////////////////////////////// // // Now push all these functions into the Lua state! @@ -1147,6 +1203,18 @@ int LUA_InfoLib(lua_State *L) lua_pushvalue(L, -1); lua_setglobal(L, "S_sfx"); lua_setglobal(L, "sfxinfo"); + + luaL_newmetatable(L, META_LUABANKS); + lua_pushcfunction(L, lib_getluabanks); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_setluabanks); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, lib_luabankslen); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + return 0; } diff --git a/src/lua_libs.h b/src/lua_libs.h index 827c1d798..7609971ce 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -67,6 +67,8 @@ extern lua_State *gL; #define META_ACTION "ACTIONF_T*" +#define META_LUABANKS "LUABANKS[]*" + boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); diff --git a/src/m_menu.c b/src/m_menu.c index 128b15a76..96efb569a 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6885,6 +6885,7 @@ static void M_StartTutorial(INT32 choice) tutorialmode = true; // turn on tutorial mode emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); M_ClearMenus(true); gamecomplete = false; cursaveslot = 0; @@ -7293,7 +7294,29 @@ static void M_ReadSavegameInfo(UINT32 slot) // File end marker check CHECKPOS - if (READUINT8(save_p) != 0x1d) BADSAVE; + switch (READUINT8(save_p)) + { + case 0xb7: + { + UINT8 i, banksinuse; + CHECKPOS + banksinuse = READUINT8(save_p); + CHECKPOS + if (banksinuse > NUM_LUABANKS) + BADSAVE + for (i = 0; i < banksinuse; i++) + { + (void)READINT32(save_p); + CHECKPOS + } + if (READUINT8(save_p) != 0x1d) + BADSAVE + } + case 0x1d: + break; + default: + BADSAVE + } // done Z_Free(savebuffer); @@ -8495,6 +8518,7 @@ static void M_ChooseNightsAttack(INT32 choice) char nameofdemo[256]; (void)choice; emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); M_ClearMenus(true); modeattacking = ATTACKING_NIGHTS; @@ -8519,6 +8543,7 @@ static void M_ChooseTimeAttack(INT32 choice) char nameofdemo[256]; (void)choice; emeralds = 0; + memset(&luabanks, 0, sizeof(luabanks)); M_ClearMenus(true); modeattacking = ATTACKING_RECORD; diff --git a/src/p_saveg.c b/src/p_saveg.c index ea998b445..e03863bc2 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4109,12 +4109,54 @@ static inline boolean P_NetUnArchiveMisc(void) return true; } +static inline void P_ArchiveLuabanksAndConsistency(void) +{ + UINT8 i, banksinuse = NUM_LUABANKS; + + while (banksinuse && !luabanks[banksinuse-1]) + banksinuse--; // get the last used bank + + if (banksinuse) + { + WRITEUINT8(save_p, 0xb7); // luabanks marker + WRITEUINT8(save_p, banksinuse); + for (i = 0; i < banksinuse; i++) + WRITEINT32(save_p, luabanks[i]); + } + + WRITEUINT8(save_p, 0x1d); // consistency marker +} + +static inline boolean P_UnArchiveLuabanksAndConsistency(void) +{ + switch (READUINT8(save_p)) + { + case 0xb7: + { + UINT8 i, banksinuse = READUINT8(save_p); + if (banksinuse > NUM_LUABANKS) + return false; + for (i = 0; i < banksinuse; i++) + luabanks[i] = READINT32(save_p); + if (READUINT8(save_p) != 0x1d) + return false; + } + case 0x1d: + break; + default: + return false; + } + + return true; +} + void P_SaveGame(void) { P_ArchiveMisc(); P_ArchivePlayer(); - WRITEUINT8(save_p, 0x1d); // consistency marker + // yes, even in non HAVE_BLUA + P_ArchiveLuabanksAndConsistency(); } void P_SaveNetGame(void) @@ -4153,7 +4195,7 @@ void P_SaveNetGame(void) LUA_Archive(); #endif - WRITEUINT8(save_p, 0x1d); // consistency marker + P_ArchiveLuabanksAndConsistency(); } boolean P_LoadGame(INT16 mapoverride) @@ -4165,8 +4207,7 @@ boolean P_LoadGame(INT16 mapoverride) P_UnArchiveSPGame(mapoverride); P_UnArchivePlayer(); - // Savegame end marker - if (READUINT8(save_p) != 0x1d) + if (!P_UnArchiveLuabanksAndConsistency()) return false; // Only do this after confirming savegame is ok @@ -4207,5 +4248,5 @@ boolean P_LoadNetGame(void) // precipitation when loading a netgame save. Instead, precip has to be spawned here. // This is done in P_NetUnArchiveSpecials now. - return READUINT8(save_p) == 0x1d; + return P_UnArchiveLuabanksAndConsistency(); } From fbec4af086956df517ed631220d16778f9dfa425 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sat, 24 Aug 2019 17:17:55 -0400 Subject: [PATCH 050/196] Fallback graphic for firework display, if the character lacks one. --- src/f_finale.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index da042abeb..056b7f815 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1595,11 +1595,11 @@ void F_StartEnding(void) sprframe = &sprdef->spriteframes[4]; endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); } - else // eh, yknow what? too lazy to put MISSINGs here. eggman wins if you don't give your character an ending firework display. + else // Show a star if your character doesn't have an ending firework display. (Basically the MISSINGs for this) { - endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_LEVEL); - endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_LEVEL); - endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_LEVEL); + endfwrk[0] = W_CachePatchName("ENDFWRK3", PU_LEVEL); + endfwrk[1] = W_CachePatchName("ENDFWRK4", PU_LEVEL); + endfwrk[2] = W_CachePatchName("ENDFWRK5", PU_LEVEL); } endbrdr[0] = W_CachePatchName("ENDBRDR2", PU_LEVEL); From b746ef66ac5abb7e4574e56e875f114564f24c87 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Aug 2019 22:56:20 +0100 Subject: [PATCH 051/196] Fix range print for getter error (setter was caught ahead of time) --- src/lua_infolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 8338fa5b9..8ef0bafcf 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1043,7 +1043,7 @@ static int lib_getluabanks(lua_State *L) return luaL_error(L, "luabanks[] invalid index"); if (i >= NUM_LUABANKS) - luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS); + luaL_error(L, "luabanks[] index %d out of range (%d - %d)", i, 0, NUM_LUABANKS-1); lua_pushinteger(L, luabanks[i]); return 1; From db3a2e02a539e94db564159ca7b68f346e44ecfb Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 25 Aug 2019 22:24:08 +0100 Subject: [PATCH 052/196] Modify defaults for smpstage_start and smpstage_end to 60 and 66 respectively --- src/g_game.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index e70241269..576ea0c33 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3285,9 +3285,11 @@ void G_LoadGameSettings(void) { // defaults spstage_start = 1; - sstage_start = smpstage_start = 50; - sstage_end = smpstage_end = 56; // 7 special stages in vanilla SRB2 + sstage_start = 50; + sstage_end = 56; // 7 special stages in vanilla SRB2 sstage_end++; // plus one weirdo + smpstage_start = 60; + smpstage_end = 66; // 7 multiplayer special stages too // initialize free sfx slots for skin sounds S_InitRuntimeSounds(); From 89c4989987b18c6f8cc746816b41d5848eed5df3 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 26 Aug 2019 16:07:17 -0700 Subject: [PATCH 053/196] Show a negative number to indicate ammo penalty --- src/d_clisrv.c | 8 +++++++ src/d_clisrv.h | 3 +++ src/d_player.h | 4 ++++ src/dehacked.c | 4 ++++ src/g_game.c | 2 ++ src/g_game.h | 1 + src/lua_playerlib.c | 12 ++++++++++ src/p_saveg.c | 8 +++++++ src/p_user.c | 57 +++++++++++++++++++++++++++++++-------------- src/st_stuff.c | 27 +++++++++++---------- 10 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 556d86384..01e94485d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -513,6 +513,10 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->currentweapon = LONG(players[i].currentweapon); rsp->ringweapons = LONG(players[i].ringweapons); + rsp->ammoremoval = (UINT16)SHORT(players[i].ammoremoval); + rsp->ammoremovaltimer = (tic_t)LONG(players[i].ammoremovaltimer); + rsp->ammoremovalweapon = LONG(players[i].ammoremovalweapon); + for (j = 0; j < NUMPOWERS; ++j) rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]); @@ -644,6 +648,10 @@ static void resynch_read_player(resynch_pak *rsp) players[i].currentweapon = LONG(rsp->currentweapon); players[i].ringweapons = LONG(rsp->ringweapons); + players[i].ammoremoval = (UINT16)SHORT(rsp->ammoremoval); + players[i].ammoremovaltimer = (tic_t)LONG(rsp->ammoremovaltimer); + players[i].ammoremovalweapon = LONG(rsp->ammoremovalweapon); + for (j = 0; j < NUMPOWERS; ++j) players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a6783fb3d..a2f140f33 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -164,6 +164,9 @@ typedef struct angle_t aiming; INT32 currentweapon; INT32 ringweapons; + UINT16 ammoremoval; + tic_t ammoremovaltimer; + INT32 ammoremovalweapon; UINT16 powers[NUMPOWERS]; // Score is resynched in the confirm resync packet diff --git a/src/d_player.h b/src/d_player.h index c133af703..5860cf1de 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -333,6 +333,10 @@ typedef struct player_s INT32 currentweapon; // current weapon selected. INT32 ringweapons; // weapons currently obtained. + UINT16 ammoremoval; // amount of ammo removed for the current weapon. + tic_t ammoremovaltimer; // flashing counter for ammo used. + INT32 ammoremovalweapon; // weapon from which the ammo was removed. + // Power ups. invinc and invis are tic counters. UINT16 powers[NUMPOWERS]; diff --git a/src/dehacked.c b/src/dehacked.c index 04ac2ef4b..5db61a5b5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -3347,6 +3347,10 @@ static void readmaincfg(MYFILE *f) { gameovertics = get_number(word2); } + else if (fastcmp(word, "AMMOREMOVALTICS")) + { + ammoremovaltics = get_number(word2); + } else if (fastcmp(word, "INTROTOPLAY")) { introtoplay = (UINT8)get_number(word2); diff --git a/src/g_game.c b/src/g_game.c index 576ea0c33..d5faf6846 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -217,6 +217,8 @@ UINT16 nightslinktics = 2*TICRATE; INT32 gameovertics = 15*TICRATE; +UINT8 ammoremovaltics = 2*TICRATE; + UINT8 use1upSound = 0; UINT8 maxXtraLife = 2; // Max extra lives from rings diff --git a/src/g_game.h b/src/g_game.h index 4b63bc180..e161bc8ed 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -51,6 +51,7 @@ extern tic_t levelstarttic; // for modding? extern INT16 prevmap, nextmap; extern INT32 gameovertics; +extern UINT8 ammoremovaltics; extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) extern INT16 rw_maximums[NUM_WEAPONS]; extern INT32 pausedelay; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index b7bdaa1be..addd707e1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -136,6 +136,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->currentweapon); else if (fastcmp(field,"ringweapons")) lua_pushinteger(L, plr->ringweapons); + else if (fastcmp(field,"ammoremoval")) + lua_pushinteger(L, plr->ammoremoval); + else if (fastcmp(field,"ammoremovaltimer")) + lua_pushinteger(L, plr->ammoremovaltimer); + else if (fastcmp(field,"ammoremovalweapon")) + lua_pushinteger(L, plr->ammoremovalweapon); else if (fastcmp(field,"powers")) LUA_PushUserdata(L, plr->powers, META_POWERS); else if (fastcmp(field,"pflags")) @@ -428,6 +434,12 @@ static int player_set(lua_State *L) plr->currentweapon = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"ringweapons")) plr->ringweapons = (INT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"ammoremoval")) + plr->ammoremoval = (UINT16)luaL_checkinteger(L, 3); + else if (fastcmp(field,"ammoremovaltimer")) + plr->ammoremovaltimer = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"ammoremovalweapon")) + plr->ammoremovalweapon = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"powers")) return NOSET; else if (fastcmp(field,"pflags")) diff --git a/src/p_saveg.c b/src/p_saveg.c index ea998b445..7c073b151 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -125,6 +125,10 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].currentweapon); WRITEINT32(save_p, players[i].ringweapons); + WRITEUINT16(save_p, players[i].ammoremoval); + WRITEUINT32(save_p, players[i].ammoremovaltimer); + WRITEINT32(save_p, players[i].ammoremovaltimer); + for (j = 0; j < NUMPOWERS; j++) WRITEUINT16(save_p, players[i].powers[j]); @@ -329,6 +333,10 @@ static void P_NetUnArchivePlayers(void) players[i].currentweapon = READINT32(save_p); players[i].ringweapons = READINT32(save_p); + players[i].ammoremoval = READUINT16(save_p); + players[i].ammoremovaltimer = READUINT32(save_p); + players[i].ammoremovalweapon = READINT32(save_p); + for (j = 0; j < NUMPOWERS; j++) players[i].powers[j] = READUINT16(save_p); diff --git a/src/p_user.c b/src/p_user.c index 65f93a11c..09c8e36c0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3900,6 +3900,33 @@ static void P_SetWeaponDelay(player_t *player, INT32 delay) } } +// +// P_DrainWeaponAmmo +// +// Reduces rings and weapon ammo. Also penalizes the player +// for using weapon rings with no normal rings! >:V +// +static void P_DrainWeaponAmmo (player_t *player, INT32 power) +{ + player->powers[power]--; + + if (player->rings < 1) + { + player->ammoremovalweapon = player->currentweapon; + player->ammoremovaltimer = ammoremovaltics; + + if (player->powers[power] > 0) // can't take a ring that doesn't exist + { + player->powers[power]--; + player->ammoremoval = 2; + } + else + player->ammoremoval = 1; + } + else + player->rings--; +} + // // P_DoFiring() // @@ -3938,22 +3965,12 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) player->pflags |= PF_ATTACKDOWN; -#define TAKE_AMMO(player, power) \ - player->powers[power]--; \ - if (player->rings < 1) \ - { \ - if (player->powers[power] > 0) \ - player->powers[power]--; \ - } \ - else \ - player->rings--; - if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring. goto firenormal; //code repetition sucks. // Bounce ring else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering]) { - TAKE_AMMO(player, pw_bouncering); + P_DrainWeaponAmmo(player, pw_bouncering); P_SetWeaponDelay(player, TICRATE/4); mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING); @@ -3964,7 +3981,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // Rail ring else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring]) { - TAKE_AMMO(player, pw_railring); + P_DrainWeaponAmmo(player, pw_railring); P_SetWeaponDelay(player, (3*TICRATE)/2); mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW); @@ -3975,7 +3992,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // Automatic else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring]) { - TAKE_AMMO(player, pw_automaticring); + P_DrainWeaponAmmo(player, pw_automaticring); player->pflags &= ~PF_ATTACKDOWN; P_SetWeaponDelay(player, 2); @@ -3984,7 +4001,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // Explosion else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring]) { - TAKE_AMMO(player, pw_explosionring); + P_DrainWeaponAmmo(player, pw_explosionring); P_SetWeaponDelay(player, (3*TICRATE)/2); mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION); @@ -3992,7 +4009,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // Grenade else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering]) { - TAKE_AMMO(player, pw_grenadering); + P_DrainWeaponAmmo(player, pw_grenadering); P_SetWeaponDelay(player, TICRATE/3); mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION); @@ -4011,7 +4028,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) angle_t shotangle = player->mo->angle; angle_t oldaiming = player->aiming; - TAKE_AMMO(player, pw_scatterring); + P_DrainWeaponAmmo(player, pw_scatterring); P_SetWeaponDelay(player, (2*TICRATE)/3); // Center @@ -4073,8 +4090,6 @@ firenormal: } } - #undef TAKE_AMMO - if (mo) { if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING) @@ -11332,6 +11347,12 @@ void P_PlayerThink(player_t *player) // Counters, time dependent power ups. // Time Bonus & Ring Bonus count settings + if (player->ammoremovaltimer) + { + if (--player->ammoremovaltimer == 0) + player->ammoremoval = 0; + } + // Strength counts up to diminish fade. if (player->powers[pw_sneakers] && player->powers[pw_sneakers] < UINT16_MAX) player->powers[pw_sneakers]--; diff --git a/src/st_stuff.c b/src/st_stuff.c index aefb4c53c..142cd3e61 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1960,6 +1960,7 @@ static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, I static void ST_drawMatchHUD(void) { + char penaltystr[5]; const INT32 y = 176; // HUD_LIVES INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6; @@ -1986,18 +1987,20 @@ static void ST_drawMatchHUD(void) ST_drawWeaponSelect(offset, y); } - offset += 20; - ST_drawWeaponRing(pw_automaticring, RW_AUTO, WEP_AUTO, offset, y, autoring); - offset += 20; - ST_drawWeaponRing(pw_bouncering, RW_BOUNCE, WEP_BOUNCE, offset, y, bouncering); - offset += 20; - ST_drawWeaponRing(pw_scatterring, RW_SCATTER, WEP_SCATTER, offset, y, scatterring); - offset += 20; - ST_drawWeaponRing(pw_grenadering, RW_GRENADE, WEP_GRENADE, offset, y, grenadering); - offset += 20; - ST_drawWeaponRing(pw_explosionring, RW_EXPLODE, WEP_EXPLODE, offset, y, explosionring); - offset += 20; - ST_drawWeaponRing(pw_railring, RW_RAIL, WEP_RAIL, offset, y, railring); + ST_drawWeaponRing(pw_automaticring, RW_AUTO, WEP_AUTO, offset + 20, y, autoring); + ST_drawWeaponRing(pw_bouncering, RW_BOUNCE, WEP_BOUNCE, offset + 40, y, bouncering); + ST_drawWeaponRing(pw_scatterring, RW_SCATTER, WEP_SCATTER, offset + 60, y, scatterring); + ST_drawWeaponRing(pw_grenadering, RW_GRENADE, WEP_GRENADE, offset + 80, y, grenadering); + ST_drawWeaponRing(pw_explosionring, RW_EXPLODE, WEP_EXPLODE, offset + 100, y, explosionring); + ST_drawWeaponRing(pw_railring, RW_RAIL, WEP_RAIL, offset + 120, y, railring); + + if (stplyr->ammoremovaltimer && leveltime % 8 < 4) + { + sprintf(penaltystr, "-%d", stplyr->ammoremoval); + V_DrawString(offset + 8 + stplyr->ammoremovalweapon * 20, y, + V_REDMAP, penaltystr); + } + } } From 141631220028cba50baa5fb15dd8c6f25ccff86c Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 26 Aug 2019 20:38:32 -0400 Subject: [PATCH 054/196] Move some cvars out of D_ClientServerInit and save them Ported over from Kart --- src/d_clisrv.c | 15 +++++---------- src/d_clisrv.h | 1 + src/d_netcmd.c | 7 +++++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 556d86384..aaca4a84f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -152,7 +152,7 @@ ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; -static consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, 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}; @@ -2938,13 +2938,13 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) CL_RemovePlayer(pnum, kickreason); } -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 = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; +consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, 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}; static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; -consvar_t cv_resynchattempts = {"resynchattempts", "10", 0, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; -consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; +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 }; // max file size to send to a player (in kilobytes) static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; @@ -2985,11 +2985,6 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_KICK, Got_KickCmd); RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); #ifndef NONET - CV_RegisterVar(&cv_allownewplayer); - CV_RegisterVar(&cv_joinnextround); - CV_RegisterVar(&cv_showjoinaddress); - CV_RegisterVar(&cv_resynchattempts); - CV_RegisterVar(&cv_blamecfail); #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a6783fb3d..cfa58aad7 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -441,6 +441,7 @@ extern INT32 mapchangepending; // Points inside doomcom extern doomdata_t *netbuffer; +extern consvar_t cv_showjoinaddress; extern consvar_t cv_playbackspeed; #define BASEPACKETSIZE offsetof(doomdata_t, u) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3e82fc60c..63ad35355 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -556,9 +556,16 @@ void D_RegisterServerCommands(void) // d_clisrv CV_RegisterVar(&cv_maxplayers); + CV_RegisterVar(&cv_resynchattempts); CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); CV_RegisterVar(&cv_downloadspeed); +#ifndef NONET + CV_RegisterVar(&cv_allownewplayer); + CV_RegisterVar(&cv_joinnextround); + CV_RegisterVar(&cv_showjoinaddress); + CV_RegisterVar(&cv_blamecfail); +#endif COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); From b2712af2d07e0f4b68fcc9700e77b0837bece817 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 26 Aug 2019 21:49:12 -0400 Subject: [PATCH 055/196] Reorder the main multiplayer menu --- src/m_menu.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 128b15a76..63b372810 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -884,12 +884,12 @@ static menuitem_t MP_SplitServerMenu[] = static menuitem_t MP_MainMenu[] = { - {IT_HEADER, NULL, "Host a game", NULL, 0}, - {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 12}, - {IT_STRING|IT_CALL, NULL, "Splitscreen...", M_StartSplitServerMenu, 22}, - {IT_HEADER, NULL, "Join a game", NULL, 40}, - {IT_STRING|IT_CALL, NULL, "Server browser...", M_ConnectMenu, 52}, - {IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 62}, + {IT_HEADER, NULL, "Join a game", NULL, 0}, + {IT_STRING|IT_CALL, NULL, "Server browser...", M_ConnectMenu, 12}, + {IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 22}, + {IT_HEADER, NULL, "Host a game", NULL, 54}, + {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 66}, + {IT_STRING|IT_CALL, NULL, "Splitscreen...", M_StartSplitServerMenu, 76}, {IT_HEADER, NULL, "Player setup", NULL, 94}, {IT_STRING|IT_CALL, NULL, "Player 1...", M_SetupMultiPlayer, 106}, {IT_STRING|IT_CALL, NULL, "Player 2... ", M_SetupMultiPlayer2, 116}, @@ -9259,20 +9259,16 @@ static void M_DrawMPMainMenu(void) // use generic drawer for cursor, items and title M_DrawGenericMenu(); -#if MAXPLAYERS == 32 - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+12, - ((itemOn == 1) ? V_YELLOWMAP : 0), "(2-32 players)"); -#else -Update the maxplayers label... -#endif + V_DrawRightAlignedString(BASEVIDWIDTH-x, y+66, + ((itemOn == 4) ? V_YELLOWMAP : 0), va("(2-%d players)", MAXPLAYERS)); - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+22, - ((itemOn == 2) ? V_YELLOWMAP : 0), "(2 players)"); + V_DrawRightAlignedString(BASEVIDWIDTH-x, y+76, + ((itemOn == 5) ? V_YELLOWMAP : 0), "(2 players)"); V_DrawRightAlignedString(BASEVIDWIDTH-x, y+116, ((itemOn == 8) ? V_YELLOWMAP : 0), "(splitscreen)"); - y += 62; + y += 22; V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); @@ -9280,7 +9276,7 @@ Update the maxplayers label... V_DrawString(x+8,y+12, V_MONOSPACE, setupm_ip); // draw text cursor for name - if (itemOn == 5 //0 + if (itemOn == 2 //0 && skullAnimCounter < 4) //blink cursor V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_MONOSPACE),y+12,'_',false); } From a98862b3ed72e1f353c6d8d40d730f35d6fdb88e Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 26 Aug 2019 22:00:07 -0400 Subject: [PATCH 056/196] Allow letters to be used on ipv4 address field --- src/m_menu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 63b372810..586a85629 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9247,7 +9247,7 @@ static void M_StartServerMenu(INT32 choice) // CONNECT VIA IP // ============== -static char setupm_ip[16]; +static char setupm_ip[28]; // Draw the funky Connect IP menu. Tails 11-19-2002 // So much work for such a little thing! @@ -9273,12 +9273,12 @@ static void M_DrawMPMainMenu(void) V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); // draw name string - V_DrawString(x+8,y+12, V_MONOSPACE, setupm_ip); + V_DrawString(x+8,y+12, V_ALLOWLOWERCASE, setupm_ip); // draw text cursor for name if (itemOn == 2 //0 && skullAnimCounter < 4) //blink cursor - V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_MONOSPACE),y+12,'_',false); + V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',false); } // Tails 11-19-2002 @@ -9349,10 +9349,11 @@ static void M_HandleConnectIP(INT32 choice) default: l = strlen(setupm_ip); - if (l >= 16-1) + if (l >= 28-1) break; - if (choice == 46 || (choice >= 48 && choice <= 57)) // Rudimentary number and period enforcing + // Rudimentary number and period enforcing - also allows letters so hostnames can be used instead + if ((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z')) { S_StartSound(NULL,sfx_menu1); // Tails setupm_ip[l] = (char)choice; From b4d81266750d8389b060f8f506dcfecaa0404eea Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 27 Aug 2019 18:05:30 -0400 Subject: [PATCH 057/196] Rename offline mode to private mode --- src/m_menu.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 586a85629..926e74438 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -948,7 +948,7 @@ enum static menuitem_t MP_RoomMenu[] = { - {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, + {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, {IT_DISABLED, NULL, "", M_ChooseRoom, 27}, {IT_DISABLED, NULL, "", M_ChooseRoom, 36}, @@ -8872,7 +8872,7 @@ static void M_DrawConnectMenu(void) // Room name if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, - V_YELLOWMAP, (itemOn == mp_connect_room) ? "" : ""); else V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); @@ -9022,7 +9022,14 @@ static void M_ConnectMenu(INT32 choice) // first page of servers serverlistpage = 0; - M_SetupNextMenu(&MP_ConnectDef); + if (ms_RoomId < 0) + { + M_RoomMenu(0); // Select a room instead of staring at an empty list + // This prevents us from returning to the modified game alert. + currentMenu->prevMenu = &MP_MainDef; + } + else + M_SetupNextMenu(&MP_ConnectDef); itemOn = 0; M_Refresh(0); } @@ -9081,7 +9088,16 @@ static void M_ChooseRoom(INT32 choice) } serverlistpage = 0; - M_SetupNextMenu(currentMenu->prevMenu); + /* + We were on the Multiplayer menu? That means that we must have been trying to + view the server browser, but we hadn't selected a room yet. So we need to go + to the browser next, not back there. + */ + if (currentMenu->prevMenu == &MP_MainDef) + M_SetupNextMenu(&MP_ConnectDef); + else + M_SetupNextMenu(currentMenu->prevMenu); + if (currentMenu == &MP_ConnectDef) M_Refresh(0); } @@ -9140,7 +9156,7 @@ static void M_DrawServerMenu(void) M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true, false); if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, - V_YELLOWMAP, (itemOn == mp_server_room) ? "" : ""); else V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); From 7e47117afa7802e12e6716ac25098c1507e69223 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Wed, 28 Aug 2019 20:24:30 -0400 Subject: [PATCH 058/196] Add warning message when attempting to use the master server browser while `modifiedgame` is true. --- src/m_menu.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 926e74438..e29568536 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -265,6 +265,7 @@ static void M_ServerOptions(INT32 choice); #ifndef NONET static void M_StartServerMenu(INT32 choice); static void M_ConnectMenu(INT32 choice); +static void M_ConnectMenuModChecks(INT32 choice); static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); static void M_ChooseRoom(INT32 choice); @@ -885,7 +886,7 @@ static menuitem_t MP_SplitServerMenu[] = static menuitem_t MP_MainMenu[] = { {IT_HEADER, NULL, "Join a game", NULL, 0}, - {IT_STRING|IT_CALL, NULL, "Server browser...", M_ConnectMenu, 12}, + {IT_STRING|IT_CALL, NULL, "Server browser...", M_ConnectMenuModChecks, 12}, {IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 22}, {IT_HEADER, NULL, "Host a game", NULL, 54}, {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 66}, @@ -5603,7 +5604,7 @@ static boolean M_AddonsRefresh(void) { S_StartSound(NULL, sfx_lose); if (refreshdirmenu & REFRESHDIR_MAX) - message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you want to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nif you wish to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); else message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); } @@ -9034,6 +9035,20 @@ static void M_ConnectMenu(INT32 choice) M_Refresh(0); } +static void M_ConnectMenuModChecks(INT32 choice) +{ + (void)choice; + // okay never mind we want to COMMUNICATE to the player pre-emptively instead of letting them try and then get confused when it doesn't work + + 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); + return; + } + + M_ConnectMenu(-1); +} + static UINT32 roomIds[NUM_LIST_ROOMS]; static void M_RoomMenu(INT32 choice) From 1040a93a6e6e21abca01f4dcd771ab1e5047c719 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Wed, 28 Aug 2019 23:12:25 -0400 Subject: [PATCH 059/196] Ignore bots when looking for a player --- src/p_enemy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index e3f169784..18d37a1a7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -726,6 +726,9 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead + if (player->bot) + continue; // ignore bots + if (dist > 0 && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) continue; // Too far away From 8655b8f1f15f0b138d9c85af07a6f1b4a7a4dff1 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 29 Aug 2019 01:57:58 -0400 Subject: [PATCH 060/196] Add spawn object linedef special. Note that spawning a object within a random range does not fully work yet and crashes the game --- src/p_setup.c | 1 + src/p_spec.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index d0cd14b22..65335be3f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1480,6 +1480,7 @@ static void P_LoadRawSideDefs2(void *data) case 425: // Calls P_SetMobjState on calling mobj case 434: // Custom Power case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors + case 461: // Spawns an object on the map based on texture offsets { char process[8*3+1]; memset(process,0,8*3+1); diff --git a/src/p_spec.c b/src/p_spec.c index 37a1652f0..325f5ebe7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3953,6 +3953,27 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } break; + case 461: // Spawns an object on the map based on texture offsets + { + const mobjtype_t type = (mobjtype_t)(sides[line->sidenum[0]].toptexture); + + fixed_t x, y, z; + x = sides[line->sidenum[0]].textureoffset; + y = sides[line->sidenum[0]].rowoffset; + z = line->frontsector->floorheight; + + if (line->flags & ML_NOCLIMB) // If noclimb is set, spawn randomly within a range + { + x = P_RandomRange(sides[line->sidenum[0]].textureoffset, sides[line->sidenum[1]].textureoffset); + y = P_RandomRange(sides[line->sidenum[0]].rowoffset, sides[line->sidenum[1]].rowoffset); + z = P_RandomRange(line->frontsector->floorheight, line->frontsector->ceilingheight); + } + + CONS_Printf("mobjtype_t: %d\n", type); + P_SpawnMobj(x, y, z, type); + } + break; + #ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing From 656879b90bff5073c0e875235d58105b977d5eb2 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 29 Aug 2019 15:56:46 -0400 Subject: [PATCH 061/196] Is this thing working? --- src/p_user.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 35399c2b8..b93cf5b44 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11576,9 +11576,21 @@ void P_PlayerAfterThink(player_t *player) #endif if (splitscreen && player == &players[secondarydisplayplayer]) + { thiscam = &camera2; + if (P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam2_dist.value)*2)) + { + P_ResetCamera(player, thiscam); + } + } else if (player == &players[displayplayer]) + { thiscam = &camera; + if (P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam_dist.value)*2)) + { + P_ResetCamera(player, thiscam); + } + } if (player->playerstate == PST_DEAD) { From bcd0b044883a18eeeda61897bb01a2a731951264 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 29 Aug 2019 17:17:58 -0400 Subject: [PATCH 062/196] Forgot to account for z axis -- thanks James R --- src/p_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index b93cf5b44..2903e546f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11578,7 +11578,7 @@ void P_PlayerAfterThink(player_t *player) if (splitscreen && player == &players[secondarydisplayplayer]) { thiscam = &camera2; - if (P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam2_dist.value)*2)) + if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam2_dist.value)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+cv_cam2_dist.value)*2))) { P_ResetCamera(player, thiscam); } @@ -11586,7 +11586,7 @@ void P_PlayerAfterThink(player_t *player) else if (player == &players[displayplayer]) { thiscam = &camera; - if (P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam_dist.value)*2)) + if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam_dist.value)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+cv_cam_dist.value)*2))) { P_ResetCamera(player, thiscam); } From dadec1196b7df9903143b85651962b2fff34a07d Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Thu, 29 Aug 2019 19:27:52 -0400 Subject: [PATCH 063/196] Warn on implicit fallthrough --- src/Makefile.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index c69fcd57d..4658eb4ff 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -234,8 +234,7 @@ ifdef GCC61 WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare endif ifdef GCC71 - WFLAGS+=-Wno-error=implicit-fallthrough - WFLAGS+=-Wno-implicit-fallthrough + WFLAGS+=-Wimplicit-fallthrough=4 endif ifdef GCC81 WFLAGS+=-Wno-error=format-overflow From 10ea0f21ae3b61b2584b599adab57a5b65b29c82 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 29 Aug 2019 23:56:15 -0400 Subject: [PATCH 064/196] Fix spawning within random range --- src/p_spec.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 325f5ebe7..014a09845 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3964,13 +3964,24 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (line->flags & ML_NOCLIMB) // If noclimb is set, spawn randomly within a range { - x = P_RandomRange(sides[line->sidenum[0]].textureoffset, sides[line->sidenum[1]].textureoffset); - y = P_RandomRange(sides[line->sidenum[0]].rowoffset, sides[line->sidenum[1]].rowoffset); - z = P_RandomRange(line->frontsector->floorheight, line->frontsector->ceilingheight); + if (line->sidenum[1] != 0xffff) // Make sure the linedef has a back side + { + x = P_RandomRange(sides[line->sidenum[0]].textureoffset>>FRACBITS, sides[line->sidenum[1]].textureoffset>>FRACBITS)<sidenum[0]].rowoffset>>FRACBITS, sides[line->sidenum[1]].rowoffset>>FRACBITS)<frontsector->floorheight>>FRACBITS, line->frontsector->ceilingheight>>FRACBITS)<special); + break; + } } - CONS_Printf("mobjtype_t: %d\n", type); - P_SpawnMobj(x, y, z, type); + mobj_t *mobj = P_SpawnMobj(x, y, z, type); + if (mobj) + CONS_Debug(DBG_GAMELOGIC, "Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d)\n", line->special, mobj->type, mobj->x>>FRACBITS, mobj->y>>FRACBITS, mobj->z>>FRACBITS); //TODO: Convert mobj->type to a string somehow. + else + CONS_Alert(CONS_ERROR,"Linedef Type %d - Spawn Object: Object did not spawn!\n", line->special); } break; From 5c295d285b5c52a12e9cabc5bb23a33ec41ea15b Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 30 Aug 2019 00:36:10 -0400 Subject: [PATCH 065/196] Capitalize the 'if' --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index e29568536..e71877d53 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5604,7 +5604,7 @@ static boolean M_AddonsRefresh(void) { S_StartSound(NULL, sfx_lose); if (refreshdirmenu & REFRESHDIR_MAX) - message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nif you wish to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you wish to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); else message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); } From 8167a88becac105af29808d1f25f52f19095e00d Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 30 Aug 2019 00:38:58 -0400 Subject: [PATCH 066/196] Rename private mode to unlisted mode --- src/m_menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index e71877d53..5f5e987bd 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -949,7 +949,7 @@ enum static menuitem_t MP_RoomMenu[] = { - {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, + {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, {IT_DISABLED, NULL, "", M_ChooseRoom, 27}, {IT_DISABLED, NULL, "", M_ChooseRoom, 36}, @@ -8873,7 +8873,7 @@ static void M_DrawConnectMenu(void) // Room name if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, - V_YELLOWMAP, (itemOn == mp_connect_room) ? "" : ""); else V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); @@ -9171,7 +9171,7 @@ static void M_DrawServerMenu(void) M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true, false); if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, - V_YELLOWMAP, (itemOn == mp_server_room) ? "" : ""); else V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); From 6a9da63d7f2429492028d72fba08444c1060bd80 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 30 Aug 2019 19:19:54 +0100 Subject: [PATCH 067/196] Motor's new Eggman sprites, along with some other sweet bits of boss polish. What else can I say? Just play it. --- src/dehacked.c | 63 ++------ src/hardware/hw_light.c | 7 +- src/info.c | 333 ++++++++++++++-------------------------- src/info.h | 73 +++------ src/p_enemy.c | 234 ++++++++++++++++++++-------- src/p_inter.c | 6 + src/p_mobj.c | 114 ++++++++------ src/r_draw.c | 11 +- 8 files changed, 408 insertions(+), 433 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 04ac2ef4b..cf767a4b5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4585,10 +4585,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SONIC3KBOSSEXPLOSION6", "S_JETFUME1", - "S_JETFUME2", // Boss 1 "S_EGGMOBILE_STND", + "S_EGGMOBILE_ROFL", "S_EGGMOBILE_LATK1", "S_EGGMOBILE_LATK2", "S_EGGMOBILE_LATK3", @@ -4598,7 +4598,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE_LATK7", "S_EGGMOBILE_LATK8", "S_EGGMOBILE_LATK9", - "S_EGGMOBILE_LATK10", "S_EGGMOBILE_RATK1", "S_EGGMOBILE_RATK2", "S_EGGMOBILE_RATK3", @@ -4608,7 +4607,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE_RATK7", "S_EGGMOBILE_RATK8", "S_EGGMOBILE_RATK9", - "S_EGGMOBILE_RATK10", "S_EGGMOBILE_PANIC1", "S_EGGMOBILE_PANIC2", "S_EGGMOBILE_PANIC3", @@ -4616,6 +4614,14 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE_PANIC5", "S_EGGMOBILE_PANIC6", "S_EGGMOBILE_PANIC7", + "S_EGGMOBILE_PANIC8", + "S_EGGMOBILE_PANIC9", + "S_EGGMOBILE_PANIC10", + "S_EGGMOBILE_PANIC11", + "S_EGGMOBILE_PANIC12", + "S_EGGMOBILE_PANIC13", + "S_EGGMOBILE_PANIC14", + "S_EGGMOBILE_PANIC15", "S_EGGMOBILE_PAIN", "S_EGGMOBILE_PAIN2", "S_EGGMOBILE_DIE1", @@ -4626,6 +4632,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE_FLEE2", "S_EGGMOBILE_BALL", "S_EGGMOBILE_TARGET", + "S_BOSSEGLZ1", + "S_BOSSEGLZ2", // Boss 2 "S_EGGMOBILE2_STND", @@ -4657,11 +4665,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Boss 3 "S_EGGMOBILE3_STND", - "S_EGGMOBILE3_LAUGH1", - "S_EGGMOBILE3_LAUGH2", - "S_EGGMOBILE3_LAUGH3", - "S_EGGMOBILE3_LAUGH4", - "S_EGGMOBILE3_LAUGH5", + "S_EGGMOBILE3_SHOCK", "S_EGGMOBILE3_ATK1", "S_EGGMOBILE3_ATK2", "S_EGGMOBILE3_ATK3A", @@ -4670,21 +4674,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE3_ATK3D", "S_EGGMOBILE3_ATK4", "S_EGGMOBILE3_ATK5", - "S_EGGMOBILE3_LAUGH6", - "S_EGGMOBILE3_LAUGH7", - "S_EGGMOBILE3_LAUGH8", - "S_EGGMOBILE3_LAUGH9", - "S_EGGMOBILE3_LAUGH10", - "S_EGGMOBILE3_LAUGH11", - "S_EGGMOBILE3_LAUGH12", - "S_EGGMOBILE3_LAUGH13", - "S_EGGMOBILE3_LAUGH14", - "S_EGGMOBILE3_LAUGH15", - "S_EGGMOBILE3_LAUGH16", - "S_EGGMOBILE3_LAUGH17", - "S_EGGMOBILE3_LAUGH18", - "S_EGGMOBILE3_LAUGH19", - "S_EGGMOBILE3_LAUGH20", + "S_EGGMOBILE3_ROFL", "S_EGGMOBILE3_PAIN", "S_EGGMOBILE3_PAIN2", "S_EGGMOBILE3_DIE1", @@ -4694,15 +4684,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE3_FLEE1", "S_EGGMOBILE3_FLEE2", - // Boss 3 Propeller - "S_PROPELLER1", - "S_PROPELLER2", - "S_PROPELLER3", - "S_PROPELLER4", - "S_PROPELLER5", - "S_PROPELLER6", - "S_PROPELLER7", - // Boss 3 pinch "S_FAKEMOBILE_INIT", "S_FAKEMOBILE", @@ -4715,6 +4696,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FAKEMOBILE_DIE1", "S_FAKEMOBILE_DIE2", + "S_BOSSSEBH1", + "S_BOSSSEBH2", + // Boss 4 "S_EGGMOBILE4_STND", "S_EGGMOBILE4_LATK1", @@ -5118,16 +5102,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_MSSHIELD_F1", "S_MSSHIELD_F2", - "S_MSSHIELD_F3", - "S_MSSHIELD_F4", - "S_MSSHIELD_F5", - "S_MSSHIELD_F6", - "S_MSSHIELD_F7", - "S_MSSHIELD_F8", - "S_MSSHIELD_F9", - "S_MSSHIELD_F10", - "S_MSSHIELD_F11", - "S_MSSHIELD_F12", // Ring "S_RING", @@ -7211,6 +7185,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_EGGTRAP", "MT_BOSS3WAYPOINT", "MT_BOSS9GATHERPOINT", + "MT_BOSSJUNK", // Boss 1 "MT_EGGMOBILE", @@ -7222,15 +7197,11 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Boss 2 "MT_EGGMOBILE2", "MT_EGGMOBILE2_POGO", - "MT_BOSSTANK1", - "MT_BOSSTANK2", - "MT_BOSSSPIGOT", "MT_GOOP", "MT_GOOPTRAIL", // Boss 3 "MT_EGGMOBILE3", - "MT_PROPELLER", "MT_FAKEMOBILE", "MT_SHOCK", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index a52d72869..edfe328b8 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -186,17 +186,16 @@ light_t *t_lspr[NUMSPRITES] = // Boss 1, (Greenflower) &lspr[NOLIGHT], // SPR_EGGM + &lspr[NOLIGHT], // SPR_EGLZ // Boss 2, (Techno Hill) &lspr[NOLIGHT], // SPR_EGGN - &lspr[NOLIGHT], // SPR_TNKA - &lspr[NOLIGHT], // SPR_TNKB - &lspr[NOLIGHT], // SPR_SPNK + &lspr[NOLIGHT], // SPR_TANK &lspr[NOLIGHT], // SPR_GOOP // Boss 3 (Deep Sea) &lspr[NOLIGHT], // SPR_EGGO - &lspr[NOLIGHT], // SPR_PRPL + &lspr[NOLIGHT], // SPR_SEBH &lspr[NOLIGHT], // SPR_FAKE // Boss 4 (Castle Eggman) diff --git a/src/info.c b/src/info.c index 18f0e838a..5baf28943 100644 --- a/src/info.c +++ b/src/info.c @@ -73,18 +73,17 @@ char sprnames[NUMSPRITES + 1][5] = "JETF", // Boss jet fumes // Boss 1 (Greenflower) - "EGGM", + "EGGM", // Boss 1 + "EGLZ", // Boss 1 Junk // Boss 2 (Techno Hill) "EGGN", // Boss 2 - "TNKA", // Boss 2 Tank 1 - "TNKB", // Boss 2 Tank 2 - "SPNK", // Boss 2 Spigot + "TANK", // Boss 2 Junk "GOOP", // Boss 2 Goop // Boss 3 (Deep Sea) "EGGO", // Boss 3 - "PRPL", // Boss 3 Propeller + "SEBH", // Boss 3 Junk "FAKE", // Boss 3 Fakemobile // Boss 4 (Castle Eggman) @@ -1179,49 +1178,58 @@ state_t states[NUMSTATES] = {SPR_BOM3, FF_FULLBRIGHT|4, 3, {NULL}, 0, 0, S_SONIC3KBOSSEXPLOSION6}, // S_SONIC3KBOSSEXPLOSION5 {SPR_BOM3, FF_FULLBRIGHT|5, 4, {NULL}, 0, 0, S_NULL}, // S_SONIC3KBOSSEXPLOSION6 - {SPR_JETF, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_JETFUME2}, // S_JETFUME1 - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_JETFUME1}, // S_JETFUME2 + {SPR_JETF, FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 2, 1, S_NULL}, // S_JETFUME1 // Boss 1 {SPR_EGGM, 0, 1, {A_Boss1Chase}, 0, 0, S_EGGMOBILE_STND}, // S_EGGMOBILE_STND + {SPR_EGGM, FF_ANIMATE|17, 35, {A_FaceTarget}, 1, 2, S_EGGMOBILE_STND}, // S_EGGMOBILE_ROFL {SPR_EGGM, 1, 3, {A_FaceTarget}, 0, 0, S_EGGMOBILE_LATK2}, // S_EGGMOBILE_LATK1 {SPR_EGGM, 2, 15, {NULL}, 0, 0, S_EGGMOBILE_LATK3}, // S_EGGMOBILE_LATK2 - {SPR_EGGM, 3, 2, {A_FaceTarget}, 0, 0, S_EGGMOBILE_LATK4}, // S_EGGMOBILE_LATK3 - {SPR_EGGM, 4, 1, {NULL}, 0, 0, S_EGGMOBILE_LATK5}, // S_EGGMOBILE_LATK4 - {SPR_EGGM, 5, 1, {NULL}, 0, 0, S_EGGMOBILE_LATK6}, // S_EGGMOBILE_LATK5 - {SPR_EGGM, 6, 1, {NULL}, 0, 0, S_EGGMOBILE_LATK7}, // S_EGGMOBILE_LATK6 - {SPR_EGGM, 7, 1, {NULL}, 0, 0, S_EGGMOBILE_LATK8}, // S_EGGMOBILE_LATK7 - {SPR_EGGM, 8, 45, {A_Boss1Laser}, MT_LASER, 0, S_EGGMOBILE_LATK9}, // S_EGGMOBILE_LATK8 - {SPR_EGGM, 9, 10, {NULL}, 0, 0, S_EGGMOBILE_LATK10}, // S_EGGMOBILE_LATK9 - {SPR_EGGM, 10, 2, {NULL}, 0, 0, S_EGGMOBILE_STND}, // S_EGGMOBILE_LATK10 - {SPR_EGGM, 11, 3, {A_FaceTarget}, 0, 0, S_EGGMOBILE_RATK2}, // S_EGGMOBILE_RATK1 - {SPR_EGGM, 12, 15, {NULL}, 0, 0, S_EGGMOBILE_RATK3}, // S_EGGMOBILE_RATK2 - {SPR_EGGM, 13, 2, {A_FaceTarget}, 0, 0, S_EGGMOBILE_RATK4}, // S_EGGMOBILE_RATK3 - {SPR_EGGM, 14, 1, {NULL}, 0, 0, S_EGGMOBILE_RATK5}, // S_EGGMOBILE_RATK4 - {SPR_EGGM, 15, 1, {NULL}, 0, 0, S_EGGMOBILE_RATK6}, // S_EGGMOBILE_RATK5 - {SPR_EGGM, 16, 1, {NULL}, 0, 0, S_EGGMOBILE_RATK7}, // S_EGGMOBILE_RATK6 - {SPR_EGGM, 17, 1, {NULL}, 0, 0, S_EGGMOBILE_RATK8}, // S_EGGMOBILE_RATK7 - {SPR_EGGM, 18, 45, {A_Boss1Laser}, MT_LASER, 1, S_EGGMOBILE_RATK9}, // S_EGGMOBILE_RATK8 - {SPR_EGGM, 19, 10, {NULL}, 0, 0, S_EGGMOBILE_RATK10}, // S_EGGMOBILE_RATK9 - {SPR_EGGM, 20, 2, {NULL}, 0, 0, S_EGGMOBILE_STND}, // S_EGGMOBILE_RATK10 - {SPR_EGGM, 3, 12, {NULL}, 0, 0, S_EGGMOBILE_PANIC2}, // S_EGGMOBILE_PANIC1 - {SPR_EGGM, 4, 45, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC3}, // S_EGGMOBILE_PANIC2 - {SPR_EGGM, 3, 8, {NULL}, 0, 0, S_EGGMOBILE_PANIC4}, // S_EGGMOBILE_PANIC3 - {SPR_EGGM, 4, 45, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC5 }, // S_EGGMOBILE_PANIC4 - {SPR_EGGM, 3, 8, {NULL}, 0, 0, S_EGGMOBILE_PANIC6}, // S_EGGMOBILE_PANIC5 - {SPR_EGGM, 4, 45, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC7 }, // S_EGGMOBILE_PANIC6 - {SPR_EGGM, 0, 35, {NULL}, 0, 0, S_EGGMOBILE_STND }, // S_EGGMOBILE_PANIC7 - {SPR_EGGM, 21, 24, {A_Pain}, 0, 0, S_EGGMOBILE_PAIN2}, // S_EGGMOBILE_PAIN - {SPR_EGGM, 21, 16, {A_SkullAttack}, 1, 1, S_EGGMOBILE_STND}, // S_EGGMOBILE_PAIN2 - {SPR_EGGM, 22, 2, {A_Fall}, 0, 0, S_EGGMOBILE_DIE2}, // S_EGGMOBILE_DIE1 - {SPR_EGGM, 22, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_DIE3}, // S_EGGMOBILE_DIE2 - {SPR_EGGM, 22, 0, {A_Repeat}, 17, S_EGGMOBILE_DIE2, S_EGGMOBILE_DIE4}, // S_EGGMOBILE_DIE3 - {SPR_EGGM, 22, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_EGGMOBILE_DIE4 - {SPR_EGGM, 23, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_FLEE2}, // S_EGGMOBILE_FLEE1 - {SPR_EGGM, 24, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_FLEE1}, // S_EGGMOBILE_FLEE2 + {SPR_EGGM, 3, 2, {NULL}, 0, 0, S_EGGMOBILE_LATK4}, // S_EGGMOBILE_LATK3 + {SPR_EGGM, 4, 2, {A_FaceTarget}, 0, 0, S_EGGMOBILE_LATK5}, // S_EGGMOBILE_LATK4 + {SPR_EGGM, 6, 0, {A_PrepareRepeat}, 45, 0, S_EGGMOBILE_LATK6}, // S_EGGMOBILE_LATK5 + {SPR_EGGM, 5, 1, {A_Boss1Laser}, MT_LASER, 0, S_EGGMOBILE_LATK7}, // S_EGGMOBILE_LATK6 + {SPR_EGGM, 6, 1, {A_Boss1Laser}, MT_LASER, (1<<16), S_EGGMOBILE_LATK8}, // S_EGGMOBILE_LATK7 + {SPR_EGGM, 5, 0, {A_Repeat}, 45, S_EGGMOBILE_LATK6, S_EGGMOBILE_LATK9}, // S_EGGMOBILE_LATK8 + {SPR_EGGM, 8, 2, {NULL}, 0, 0, S_EGGMOBILE_ROFL}, // S_EGGMOBILE_LATK9 + {SPR_EGGM, 9, 3, {A_FaceTarget}, 0, 0, S_EGGMOBILE_RATK2}, // S_EGGMOBILE_RATK1 + {SPR_EGGM, 10, 15, {NULL}, 0, 0, S_EGGMOBILE_RATK3}, // S_EGGMOBILE_RATK2 + {SPR_EGGM, 11, 2, {NULL}, 0, 0, S_EGGMOBILE_RATK4}, // S_EGGMOBILE_RATK3 + {SPR_EGGM, 12, 2, {A_FaceTarget}, 0, 0, S_EGGMOBILE_RATK5}, // S_EGGMOBILE_RATK4 + {SPR_EGGM, 14, 0, {A_PrepareRepeat}, 45, 0, S_EGGMOBILE_RATK6}, // S_EGGMOBILE_RATK5 + {SPR_EGGM, 13, 1, {A_Boss1Laser}, MT_LASER, 1, S_EGGMOBILE_RATK7}, // S_EGGMOBILE_RATK6 + {SPR_EGGM, 14, 1, {A_Boss1Laser}, MT_LASER, 1|(1<<16), S_EGGMOBILE_RATK8}, // S_EGGMOBILE_RATK7 + {SPR_EGGM, 13, 0, {A_Repeat}, 45, S_EGGMOBILE_RATK6, S_EGGMOBILE_RATK9}, // S_EGGMOBILE_RATK8 + {SPR_EGGM, 16, 2, {NULL}, 0, 0, S_EGGMOBILE_ROFL}, // S_EGGMOBILE_RATK9 + {SPR_EGGM, 0, 0, {A_PrepareRepeat}, 45, 0, S_EGGMOBILE_PANIC2}, // S_EGGMOBILE_PANIC1 + {SPR_EGGM, FF_ANIMATE|1, 16, {A_FaceTarget}, 3, 4, S_EGGMOBILE_PANIC3}, // S_EGGMOBILE_PANIC2 + {SPR_EGGM, 7, 1, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC4}, // S_EGGMOBILE_PANIC3 + {SPR_EGGM, 6, 1, {A_Boss1Laser}, MT_LASER, 2|(1<<16), S_EGGMOBILE_PANIC5}, // S_EGGMOBILE_PANIC4 + {SPR_EGGM, 6, 0, {A_Repeat}, 45, S_EGGMOBILE_PANIC3, S_EGGMOBILE_PANIC6}, // S_EGGMOBILE_PANIC5 + {SPR_EGGM, 0, 0, {A_PrepareRepeat}, 45, 0, S_EGGMOBILE_PANIC7}, // S_EGGMOBILE_PANIC6 + {SPR_EGGM, FF_ANIMATE|9, 16, {A_FaceTarget}, 3, 4, S_EGGMOBILE_PANIC8}, // S_EGGMOBILE_PANIC7 + {SPR_EGGM, 15, 1, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC9}, // S_EGGMOBILE_PANIC8 + {SPR_EGGM, 14, 1, {A_Boss1Laser}, MT_LASER, 2|(1<<16), S_EGGMOBILE_PANIC10}, // S_EGGMOBILE_PANIC9 + {SPR_EGGM, 14, 0, {A_Repeat}, 45, S_EGGMOBILE_PANIC8, S_EGGMOBILE_PANIC11}, // S_EGGMOBILE_PANIC10 + {SPR_EGGM, 0, 0, {A_PrepareRepeat}, 45, 0, S_EGGMOBILE_PANIC12}, // S_EGGMOBILE_PANIC11 + {SPR_EGGM, FF_ANIMATE|1, 16, {A_FaceTarget}, 3, 4, S_EGGMOBILE_PANIC13}, // S_EGGMOBILE_PANIC12 + {SPR_EGGM, 7, 1, {A_Boss1Laser}, MT_LASER, 2, S_EGGMOBILE_PANIC14}, // S_EGGMOBILE_PANIC13 + {SPR_EGGM, 6, 1, {A_Boss1Laser}, MT_LASER, 2|(1<<16), S_EGGMOBILE_PANIC15}, // S_EGGMOBILE_PANIC14 + {SPR_EGGM, 6, 0, {A_Repeat}, 45, S_EGGMOBILE_PANIC13, S_EGGMOBILE_ROFL}, // S_EGGMOBILE_PANIC15 + {SPR_EGGM, 19, 24, {A_Pain}, 0, 0, S_EGGMOBILE_PAIN2}, // S_EGGMOBILE_PAIN + {SPR_EGGM, 19, 16, {A_SkullAttack}, 3, 1, S_EGGMOBILE_STND}, // S_EGGMOBILE_PAIN2 + {SPR_EGGM, 20, 2, {A_Fall}, 17, 0, S_EGGMOBILE_DIE2}, // S_EGGMOBILE_DIE1 + {SPR_EGGM, 20, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_DIE3}, // S_EGGMOBILE_DIE2 + {SPR_EGGM, 20, 0, {A_Repeat}, 17, S_EGGMOBILE_DIE2, S_EGGMOBILE_DIE4}, // S_EGGMOBILE_DIE3 + {SPR_EGGM, 20, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_EGGMOBILE_DIE4 + {SPR_EGGM, 21, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_FLEE2}, // S_EGGMOBILE_FLEE1 + {SPR_EGGM, 22, 2, {A_BossScream}, 0, 0, S_EGGMOBILE_FLEE1}, // S_EGGMOBILE_FLEE2 {SPR_UNID, 1, 1, {A_UnidusBall}, 2, 0, S_EGGMOBILE_BALL}, // S_EGGMOBILE_BALL {SPR_NULL, 0, 1, {A_FocusTarget}, 0, 0, S_EGGMOBILE_TARGET}, // S_EGGMOBILE_TARGET + {SPR_EGLZ, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSEGLZ1 + {SPR_EGLZ, 1, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSEGLZ2 + // Boss 2 {SPR_EGGN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGMOBILE2_STND {SPR_EGGN, 1, 4, {NULL}, 0, 0, S_EGGMOBILE2_POGO2}, // S_EGGMOBILE2_POGO1 @@ -1240,9 +1248,9 @@ state_t states[NUMSTATES] = {SPR_EGGN, 6, 2, {A_BossScream}, 0, 0, S_EGGMOBILE2_FLEE2}, // S_EGGMOBILE2_FLEE1 {SPR_EGGN, 7, 2, {A_BossScream}, 0, 0, S_EGGMOBILE2_FLEE1}, // S_EGGMOBILE2_FLEE2 - {SPR_TNKA, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSTANK1 - {SPR_TNKB, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSTANK2 - {SPR_SPNK, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSPIGOT + {SPR_TANK, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSTANK1 + {SPR_TANK, 1, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSTANK2 + {SPR_TANK, 2, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSPIGOT // Boss 2 Goop {SPR_GOOP, 0, 2, {A_SpawnObjectRelative}, 0, MT_GOOPTRAIL, S_GOOP2}, // S_GOOP1 @@ -1252,34 +1260,16 @@ state_t states[NUMSTATES] = // Boss 3 {SPR_EGGO, 0, 1, {NULL}, 0, 0, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_STND - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH2}, // S_EGGMOBILE3_LAUGH1 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH3}, // S_EGGMOBILE3_LAUGH2 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH4}, // S_EGGMOBILE3_LAUGH3 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH5}, // S_EGGMOBILE3_LAUGH4 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_ATK1}, // S_EGGMOBILE3_LAUGH5 - {SPR_EGGO, 1, 2, {NULL}, 0, 0, S_EGGMOBILE3_ATK2}, // S_EGGMOBILE3_ATK1 + {SPR_EGGO, FF_ANIMATE, 24, {NULL}, 1, 2, S_EGGMOBILE3_ATK2}, // S_EGGMOBILE3_SHOCK + {SPR_EGGO, 6|FF_ANIMATE, 24, {NULL}, 1, 2, S_EGGMOBILE3_ATK2}, // S_EGGMOBILE3_ATK1 {SPR_EGGO, 2, 2, {NULL}, 0, 0, S_EGGMOBILE3_ATK3A}, // S_EGGMOBILE3_ATK2 {SPR_EGGO, 3, 2, {A_BossFireShot}, MT_TORPEDO, 2, S_EGGMOBILE3_ATK3B}, // S_EGGMOBILE3_ATK3A {SPR_EGGO, 3, 2, {A_BossFireShot}, MT_TORPEDO, 4, S_EGGMOBILE3_ATK3C}, // S_EGGMOBILE3_ATK3B {SPR_EGGO, 3, 2, {A_BossFireShot}, MT_TORPEDO, 3, S_EGGMOBILE3_ATK3D}, // S_EGGMOBILE3_ATK3C {SPR_EGGO, 3, 2, {A_BossFireShot}, MT_TORPEDO, 5, S_EGGMOBILE3_ATK4}, // S_EGGMOBILE3_ATK3D {SPR_EGGO, 4, 2, {NULL}, 0, 0, S_EGGMOBILE3_ATK5}, // S_EGGMOBILE3_ATK4 - {SPR_EGGO, 5, 2, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH6}, // S_EGGMOBILE3_ATK5 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH7}, // S_EGGMOBILE3_LAUGH6 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH8}, // S_EGGMOBILE3_LAUGH7 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH9}, // S_EGGMOBILE3_LAUGH8 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH10}, // S_EGGMOBILE3_LAUGH9 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH11}, // S_EGGMOBILE3_LAUGH10 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH12}, // S_EGGMOBILE3_LAUGH11 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH13}, // S_EGGMOBILE3_LAUGH12 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH14}, // S_EGGMOBILE3_LAUGH13 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH15}, // S_EGGMOBILE3_LAUGH14 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH16}, // S_EGGMOBILE3_LAUGH15 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH17}, // S_EGGMOBILE3_LAUGH16 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH18}, // S_EGGMOBILE3_LAUGH17 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH19}, // S_EGGMOBILE3_LAUGH18 - {SPR_EGGO, 6, 4, {NULL}, 0, 0, S_EGGMOBILE3_LAUGH20}, // S_EGGMOBILE3_LAUGH19 - {SPR_EGGO, 7, 4, {NULL}, 0, 0, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_LAUGH20 + {SPR_EGGO, 5, 2, {NULL}, 0, 0, S_EGGMOBILE3_ROFL}, // S_EGGMOBILE3_ATK5 + {SPR_EGGO, 6|FF_ANIMATE, 60, {NULL}, 1, 2, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_ROFL {SPR_EGGO, 8, 1, {A_Boss3TakeDamage}, 0, 0, S_EGGMOBILE3_PAIN2}, // S_EGGMOBILE3_PAIN {SPR_EGGO, 8, 23, {A_Pain}, 0, 0, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_PAIN2 {SPR_EGGO, 9, 2, {A_Fall}, 0, 0, S_EGGMOBILE3_DIE2}, // S_EGGMOBILE3_DIE1 @@ -1289,17 +1279,8 @@ state_t states[NUMSTATES] = {SPR_EGGO, 10, 2, {A_BossScream}, 0, 0, S_EGGMOBILE3_FLEE2}, // S_EGGMOBILE3_FLEE1 {SPR_EGGO, 11, 2, {A_BossScream}, 0, 0, S_EGGMOBILE3_FLEE1}, // S_EGGMOBILE3_FLEE2 - // Boss 3 Propeller - {SPR_PRPL, 0, 1, {NULL}, 0, 0, S_PROPELLER2}, // S_PROPELLER1 - {SPR_PRPL, 1, 1, {NULL}, 0, 0, S_PROPELLER3}, // S_PROPELLER2 - {SPR_PRPL, 2, 1, {NULL}, 0, 0, S_PROPELLER4}, // S_PROPELLER3 - {SPR_PRPL, 3, 1, {NULL}, 0, 0, S_PROPELLER5}, // S_PROPELLER4 - {SPR_PRPL, 4, 1, {NULL}, 0, 0, S_PROPELLER6}, // S_PROPELLER5 - {SPR_PRPL, 5, 1, {NULL}, 0, 0, S_PROPELLER7}, // S_PROPELLER6 - {SPR_PRPL, 6, 1, {NULL}, 0, 0, S_PROPELLER1}, // S_PROPELLER7 - // Boss 3 Pinch - {SPR_FAKE, 0, 1, {A_BossJetFume}, 1, 0, S_FAKEMOBILE}, // S_FAKEMOBILE_INIT + {SPR_FAKE, 0, 1, {NULL}, 0, 0, S_FAKEMOBILE}, // S_FAKEMOBILE_INIT {SPR_FAKE, 0, 1, {A_Boss3Path}, 0, 0, S_FAKEMOBILE}, // S_FAKEMOBILE {SPR_FAKE, 0, 22, {NULL}, 0, 0, S_FAKEMOBILE_ATK2}, // S_FAKEMOBILE_ATK1 {SPR_FAKE, 0, 2, {NULL}, 0, 0, S_FAKEMOBILE_ATK3A}, // S_FAKEMOBILE_ATK2 @@ -1307,33 +1288,36 @@ state_t states[NUMSTATES] = {SPR_FAKE, 0, 2, {A_BossFireShot}, MT_TORPEDO2, 4, S_FAKEMOBILE_ATK3C}, // S_FAKEMOBILE_ATK3B {SPR_FAKE, 0, 2, {A_BossFireShot}, MT_TORPEDO2, 3, S_FAKEMOBILE_ATK3D}, // S_FAKEMOBILE_ATK3C {SPR_FAKE, 0, 2, {A_BossFireShot}, MT_TORPEDO2, 5, S_FAKEMOBILE}, // S_FAKEMOBILE_ATK3D - {SPR_FAKE, 0, 1, {NULL}, 0, 0, S_FAKEMOBILE_DIE2}, // S_FAKEMOBILE_DIE1 + {SPR_FAKE, 1, 1, {NULL}, 0, 0, S_FAKEMOBILE_DIE2}, // S_FAKEMOBILE_DIE1 {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FAKEMOBILE_DIE1}, // S_FAKEMOBILE_DIE2 + {SPR_SEBH, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH1 + {SPR_SEBH, 1, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH2 + // Boss 4 {SPR_EGGP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGMOBILE4_STND {SPR_EGGP, 1, 3, {NULL}, 0, 0, S_EGGMOBILE4_LATK2}, // S_EGGMOBILE4_LATK1 {SPR_EGGP, 2, 15, {NULL}, 0, 0, S_EGGMOBILE4_LATK3}, // S_EGGMOBILE4_LATK2 {SPR_EGGP, 3, 2, {NULL}, 0, 0, S_EGGMOBILE4_LATK4}, // S_EGGMOBILE4_LATK3 - {SPR_EGGP, 4, 4, {NULL}, 0, 0, S_EGGMOBILE4_LATK5}, // S_EGGMOBILE4_LATK4 - {SPR_EGGP, 4, 50, {A_Boss4Reverse}, sfx_mswing, 0, S_EGGMOBILE4_LATK6}, // S_EGGMOBILE4_LATK5 - {SPR_EGGP, 5, 2, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_LATK6 - {SPR_EGGP, 6, 3, {NULL}, 0, 0, S_EGGMOBILE4_RATK2}, // S_EGGMOBILE4_RATK1 - {SPR_EGGP, 7, 15, {NULL}, 0, 0, S_EGGMOBILE4_RATK3}, // S_EGGMOBILE4_RATK2 - {SPR_EGGP, 8, 2, {NULL}, 0, 0, S_EGGMOBILE4_RATK4}, // S_EGGMOBILE4_RATK3 - {SPR_EGGP, 9, 4, {NULL}, 0, 0, S_EGGMOBILE4_RATK5}, // S_EGGMOBILE4_RATK4 - {SPR_EGGP, 9,150, {A_Boss4SpeedUp}, sfx_mswing, 0, S_EGGMOBILE4_RATK6}, // S_EGGMOBILE4_RATK5 - {SPR_EGGP,10, 2, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_RATK6 - {SPR_EGGP, 0, 20, {A_Boss4Raise}, sfx_doord1, 0, S_EGGMOBILE4_RAISE2}, // S_EGGMOBILE4_RAISE1 - {SPR_EGGP,13|FF_ANIMATE, -1, {NULL}, 1, 10, S_NULL}, // S_EGGMOBILE4_RAISE2 - {SPR_EGGP,11, 0, {A_Boss4Reverse}, sfx_alarm, sfx_s3k60, S_EGGMOBILE4_PAIN2}, // S_EGGMOBILE4_PAIN1 - {SPR_EGGP,11, 24, {A_Pain}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_PAIN2 - {SPR_EGGP,12, 2, {A_Fall}, 0, 0, S_EGGMOBILE4_DIE2}, // S_EGGMOBILE4_DIE1 - {SPR_EGGP,12, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_DIE3}, // S_EGGMOBILE4_DIE2 - {SPR_EGGP,12, 0, {A_Repeat}, 17, S_EGGMOBILE4_DIE2, S_EGGMOBILE4_DIE4}, // S_EGGMOBILE4_DIE3 - {SPR_EGGP,12, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_EGGMOBILE4_DIE4 - {SPR_EGGP,13, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_FLEE2}, // S_EGGMOBILE4_FLEE1 - {SPR_EGGP,14, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_FLEE1}, // S_EGGMOBILE4_FLEE2 + {SPR_EGGP, 4, 2, {NULL}, 0, 0, S_EGGMOBILE4_LATK5}, // S_EGGMOBILE4_LATK4 + {SPR_EGGP, 5, 50, {A_Boss4Reverse}, sfx_mswing, 0, S_EGGMOBILE4_LATK6}, // S_EGGMOBILE4_LATK5 + {SPR_EGGP, 6, 2, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_LATK6 + {SPR_EGGP, 7, 3, {NULL}, 0, 0, S_EGGMOBILE4_RATK2}, // S_EGGMOBILE4_RATK1 + {SPR_EGGP, 8, 15, {NULL}, 0, 0, S_EGGMOBILE4_RATK3}, // S_EGGMOBILE4_RATK2 + {SPR_EGGP, 9, 2, {NULL}, 0, 0, S_EGGMOBILE4_RATK4}, // S_EGGMOBILE4_RATK3 + {SPR_EGGP,10, 2, {NULL}, 0, 0, S_EGGMOBILE4_RATK5}, // S_EGGMOBILE4_RATK4 + {SPR_EGGP,11,150, {A_Boss4SpeedUp}, sfx_mswing, 0, S_EGGMOBILE4_RATK6}, // S_EGGMOBILE4_RATK5 + {SPR_EGGP,12, 2, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_RATK6 + {SPR_EGGP,13, 20, {A_Boss4Raise}, sfx_doord1, 0, S_EGGMOBILE4_RAISE2}, // S_EGGMOBILE4_RAISE1 + {SPR_EGGP,15|FF_ANIMATE, -1, {NULL}, 1, 10, S_NULL}, // S_EGGMOBILE4_RAISE2 + {SPR_EGGP,13, 0, {A_Boss4Reverse}, sfx_alarm, sfx_s3k60, S_EGGMOBILE4_PAIN2}, // S_EGGMOBILE4_PAIN1 + {SPR_EGGP,13, 24, {A_Pain}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_PAIN2 + {SPR_EGGP,14, 2, {A_Fall}, 0, 0, S_EGGMOBILE4_DIE2}, // S_EGGMOBILE4_DIE1 + {SPR_EGGP,14, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_DIE3}, // S_EGGMOBILE4_DIE2 + {SPR_EGGP,14, 0, {A_Repeat}, 17, S_EGGMOBILE4_DIE2, S_EGGMOBILE4_DIE4}, // S_EGGMOBILE4_DIE3 + {SPR_EGGP,14, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_EGGMOBILE4_DIE4 + {SPR_EGGP,15, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_FLEE2}, // S_EGGMOBILE4_FLEE1 + {SPR_EGGP,16, 2, {A_BossScream}, 0, 0, S_EGGMOBILE4_FLEE1}, // S_EGGMOBILE4_FLEE2 {SPR_BMCE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGMOBILE4_MACE {SPR_BMCE, 0, 2, {A_BossScream}, 1, 0, S_EGGMOBILE4_MACE_DIE2}, // S_EGGMOBILE4_MACE_DIE1 {SPR_NULL, 0, 2, {A_BossScream}, 1, 0, S_EGGMOBILE4_MACE_DIE3}, // S_EGGMOBILE4_MACE_DIE2 @@ -1732,18 +1716,8 @@ state_t states[NUMSTATES] = {SPR_METL, 11, 1, {A_BossScream}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1 {SPR_METL, 11, 7, {NULL}, 0, 0, S_METALSONIC_FLEE1}, // S_METALSONIC_FLEE2 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 0, 1, {NULL}, 0, 0, S_MSSHIELD_F2}, // S_MSSHIELD_F1 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 1, 1, {NULL}, 0, 0, S_MSSHIELD_F3}, // S_MSSHIELD_F2 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 2, 1, {NULL}, 0, 0, S_MSSHIELD_F4}, // S_MSSHIELD_F3 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 3, 1, {NULL}, 0, 0, S_MSSHIELD_F5}, // S_MSSHIELD_F4 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 4, 1, {NULL}, 0, 0, S_MSSHIELD_F6}, // S_MSSHIELD_F5 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 5, 1, {NULL}, 0, 0, S_MSSHIELD_F7}, // S_MSSHIELD_F6 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 6, 1, {NULL}, 0, 0, S_MSSHIELD_F8}, // S_MSSHIELD_F7 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 7, 1, {NULL}, 0, 0, S_MSSHIELD_F9}, // S_MSSHIELD_F8 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 8, 1, {NULL}, 0, 0, S_MSSHIELD_F10}, // S_MSSHIELD_F9 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 9, 1, {NULL}, 0, 0, S_MSSHIELD_F11}, // S_MSSHIELD_F10 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|10, 1, {NULL}, 0, 0, S_MSSHIELD_F12}, // S_MSSHIELD_F11 - {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|11, 1, {NULL}, 0, 0, S_MSSHIELD_F1}, // S_MSSHIELD_F12 + {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|FF_ANIMATE, -1, {NULL}, 11, 1, S_NULL}, // S_MSSHIELD_F1 + {SPR_MSCF, FF_FULLBRIGHT|FF_ANIMATE|12, -1, {NULL}, 8, 2, S_NULL}, // S_MSSHIELD_F2 // Ring {SPR_RING, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_RING}, // S_RING @@ -5144,6 +5118,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BOSSJUNK + -1, // doomednum + S_BOSSEGLZ1, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 64*FRACUNIT, // height + 2, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + { // MT_EGGMOBILE 200, // doomednum S_EGGMOBILE_STND, // spawnstate @@ -5333,87 +5334,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_EGGMOBILE2_POGO5 // raisestate }, - { // MT_BOSSTANK1 - -1, // doomednum - S_BOSSTANK1, // spawnstate - 1, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 8*FRACUNIT, // radius - 64*FRACUNIT, // height - 0, // display offset - 100, // mass - 1, // damage - sfx_None, // activesound - MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT, // flags - S_NULL // raisestate - }, - - { // MT_BOSSTANK2 - -1, // doomednum - S_BOSSTANK2, // spawnstate - 1, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 8*FRACUNIT, // radius - 64*FRACUNIT, // height - 0, // display offset - 100, // mass - 1, // damage - sfx_None, // activesound - MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT, // flags - S_NULL // raisestate - }, - - { // MT_BOSSSPIGOT - -1, // doomednum - S_BOSSSPIGOT, // spawnstate - 1, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 8*FRACUNIT, // radius - 24*FRACUNIT, // height - 0, // display offset - 100, // mass - 1, // damage - sfx_None, // activesound - MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT, // flags - S_NULL // raisestate - }, - { // MT_GOOP -1, // doomednum S_GOOP1, // spawnstate @@ -5477,10 +5397,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // reactiontime sfx_None, // attacksound S_EGGMOBILE3_PAIN, // painstate - MT_PROPELLER, // painchance + MT_NULL, // painchance sfx_dmpain, // painsound S_NULL, // meleestate - S_EGGMOBILE3_LAUGH1,// missilestate + S_EGGMOBILE3_SHOCK, // missilestate S_EGGMOBILE3_DIE1, // deathstate S_EGGMOBILE3_FLEE1, // xdeathstate sfx_s3kb4, // deathsound @@ -5492,34 +5412,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 3, // damage sfx_telept, // activesound MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY|MF_BOSS|MF_NOCLIPHEIGHT, // flags - S_EGGMOBILE3_LAUGH20 // raisestate - }, - - { // MT_PROPELLER - -1, // doomednum - S_PROPELLER1, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 1, // speed - 4*FRACUNIT, // radius - 4*FRACUNIT, // height - 0, // display offset - 4, // mass - 0, // damage - sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags - S_NULL // raisestate + S_EGGMOBILE3_ROFL // raisestate }, { // MT_FAKEMOBILE @@ -5531,7 +5424,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate - MT_PROPELLER, // painchance + MT_NULL, // painchance sfx_s3k7b, // painsound S_NULL, // meleestate S_FAKEMOBILE_ATK1, // missilestate @@ -9211,7 +9104,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 20, // damage sfx_None, // activesound - MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index 593c1fb7c..a9d4bdde0 100644 --- a/src/info.h +++ b/src/info.h @@ -319,18 +319,17 @@ typedef enum sprite SPR_JETF, // Boss jet fumes // Boss 1 (Greenflower) - SPR_EGGM, + SPR_EGGM, // Boss 1 + SPR_EGLZ, // Boss 1 Junk // Boss 2 (Techno Hill) SPR_EGGN, // Boss 2 - SPR_TNKA, // Boss 2 Tank 1 - SPR_TNKB, // Boss 2 Tank 2 - SPR_SPNK, // Boss 2 Spigot + SPR_TANK, // Boss 2 Junk SPR_GOOP, // Boss 2 Goop // Boss 3 (Deep Sea) SPR_EGGO, // Boss 3 - SPR_PRPL, // Boss 3 Propeller + SPR_SEBH, // Boss 3 Junk SPR_FAKE, // Boss 3 Fakemobile // Boss 4 (Castle Eggman) @@ -1333,10 +1332,10 @@ typedef enum state S_SONIC3KBOSSEXPLOSION6, S_JETFUME1, - S_JETFUME2, // Boss 1 S_EGGMOBILE_STND, + S_EGGMOBILE_ROFL, S_EGGMOBILE_LATK1, S_EGGMOBILE_LATK2, S_EGGMOBILE_LATK3, @@ -1346,7 +1345,6 @@ typedef enum state S_EGGMOBILE_LATK7, S_EGGMOBILE_LATK8, S_EGGMOBILE_LATK9, - S_EGGMOBILE_LATK10, S_EGGMOBILE_RATK1, S_EGGMOBILE_RATK2, S_EGGMOBILE_RATK3, @@ -1356,7 +1354,6 @@ typedef enum state S_EGGMOBILE_RATK7, S_EGGMOBILE_RATK8, S_EGGMOBILE_RATK9, - S_EGGMOBILE_RATK10, S_EGGMOBILE_PANIC1, S_EGGMOBILE_PANIC2, S_EGGMOBILE_PANIC3, @@ -1364,6 +1361,14 @@ typedef enum state S_EGGMOBILE_PANIC5, S_EGGMOBILE_PANIC6, S_EGGMOBILE_PANIC7, + S_EGGMOBILE_PANIC8, + S_EGGMOBILE_PANIC9, + S_EGGMOBILE_PANIC10, + S_EGGMOBILE_PANIC11, + S_EGGMOBILE_PANIC12, + S_EGGMOBILE_PANIC13, + S_EGGMOBILE_PANIC14, + S_EGGMOBILE_PANIC15, S_EGGMOBILE_PAIN, S_EGGMOBILE_PAIN2, S_EGGMOBILE_DIE1, @@ -1375,6 +1380,9 @@ typedef enum state S_EGGMOBILE_BALL, S_EGGMOBILE_TARGET, + S_BOSSEGLZ1, + S_BOSSEGLZ2, + // Boss 2 S_EGGMOBILE2_STND, S_EGGMOBILE2_POGO1, @@ -1405,11 +1413,7 @@ typedef enum state // Boss 3 S_EGGMOBILE3_STND, - S_EGGMOBILE3_LAUGH1, - S_EGGMOBILE3_LAUGH2, - S_EGGMOBILE3_LAUGH3, - S_EGGMOBILE3_LAUGH4, - S_EGGMOBILE3_LAUGH5, + S_EGGMOBILE3_SHOCK, S_EGGMOBILE3_ATK1, S_EGGMOBILE3_ATK2, S_EGGMOBILE3_ATK3A, @@ -1418,21 +1422,7 @@ typedef enum state S_EGGMOBILE3_ATK3D, S_EGGMOBILE3_ATK4, S_EGGMOBILE3_ATK5, - S_EGGMOBILE3_LAUGH6, - S_EGGMOBILE3_LAUGH7, - S_EGGMOBILE3_LAUGH8, - S_EGGMOBILE3_LAUGH9, - S_EGGMOBILE3_LAUGH10, - S_EGGMOBILE3_LAUGH11, - S_EGGMOBILE3_LAUGH12, - S_EGGMOBILE3_LAUGH13, - S_EGGMOBILE3_LAUGH14, - S_EGGMOBILE3_LAUGH15, - S_EGGMOBILE3_LAUGH16, - S_EGGMOBILE3_LAUGH17, - S_EGGMOBILE3_LAUGH18, - S_EGGMOBILE3_LAUGH19, - S_EGGMOBILE3_LAUGH20, + S_EGGMOBILE3_ROFL, S_EGGMOBILE3_PAIN, S_EGGMOBILE3_PAIN2, S_EGGMOBILE3_DIE1, @@ -1442,15 +1432,6 @@ typedef enum state S_EGGMOBILE3_FLEE1, S_EGGMOBILE3_FLEE2, - // Boss 3 Propeller - S_PROPELLER1, - S_PROPELLER2, - S_PROPELLER3, - S_PROPELLER4, - S_PROPELLER5, - S_PROPELLER6, - S_PROPELLER7, - // Boss 3 Pinch S_FAKEMOBILE_INIT, S_FAKEMOBILE, @@ -1463,6 +1444,9 @@ typedef enum state S_FAKEMOBILE_DIE1, S_FAKEMOBILE_DIE2, + S_BOSSSEBH1, + S_BOSSSEBH2, + // Boss 4 S_EGGMOBILE4_STND, S_EGGMOBILE4_LATK1, @@ -1866,16 +1850,6 @@ typedef enum state S_MSSHIELD_F1, S_MSSHIELD_F2, - S_MSSHIELD_F3, - S_MSSHIELD_F4, - S_MSSHIELD_F5, - S_MSSHIELD_F6, - S_MSSHIELD_F7, - S_MSSHIELD_F8, - S_MSSHIELD_F9, - S_MSSHIELD_F10, - S_MSSHIELD_F11, - S_MSSHIELD_F12, // Ring S_RING, @@ -3981,6 +3955,7 @@ typedef enum mobj_type MT_EGGTRAP, MT_BOSS3WAYPOINT, MT_BOSS9GATHERPOINT, + MT_BOSSJUNK, // Boss 1 MT_EGGMOBILE, @@ -3992,15 +3967,11 @@ typedef enum mobj_type // Boss 2 MT_EGGMOBILE2, MT_EGGMOBILE2_POGO, - MT_BOSSTANK1, - MT_BOSSTANK2, - MT_BOSSSPIGOT, MT_GOOP, MT_GOOPTRAIL, // Boss 3 MT_EGGMOBILE3, - MT_PROPELLER, MT_FAKEMOBILE, MT_SHOCK, diff --git a/src/p_enemy.c b/src/p_enemy.c index e3f169784..d4ec3fb96 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3003,16 +3003,19 @@ void A_Boss7FireMissiles(mobj_t *actor) // 0 - Boss 1 Left side // 1 - Boss 1 Right side // 2 - Triple laser -// >3 - Boss 1 Middle +// 3 - Boss 1 Middle +// >=3 - Generic middle // void A_Boss1Laser(mobj_t *actor) { fixed_t x, y, z, floorz, speed; INT32 locvar1 = var1; - INT32 locvar2 = var2; + INT32 locvar2 = (var2 & 65535); + INT32 upperend = (var2>>16); INT32 i; angle_t angle; mobj_t *point; + tic_t dur; #ifdef HAVE_BLUA if (LUA_CallAction("A_Boss1Laser", actor)) @@ -3021,19 +3024,24 @@ void A_Boss1Laser(mobj_t *actor) if (!actor->target) return; + if ((upperend & 1) && (actor->extravalue2 > 1)) + actor->extravalue2--; + + dur = actor->extravalue2; + switch (locvar2) { case 0: - x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(44*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(44*FRACUNIT, actor->scale)); if (actor->eflags & MFE_VERTICALFLIP) z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; else z = actor->z + FixedMul(56*FRACUNIT, actor->scale); break; case 1: - x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(44*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(44*FRACUNIT, actor->scale)); if (actor->eflags & MFE_VERTICALFLIP) z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; else @@ -3048,6 +3056,11 @@ void A_Boss1Laser(mobj_t *actor) A_Boss1Laser(actor); return; break; + case 3: + x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(42*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(42*FRACUNIT, actor->scale)); + z = actor->z + actor->height/2; + break; default: x = actor->x; y = actor->y; @@ -3055,7 +3068,7 @@ void A_Boss1Laser(mobj_t *actor) break; } - if (!(actor->flags2 & MF2_FIRING) && actor->tics > 1) + if (!(actor->flags2 & MF2_FIRING) && dur > 1) { actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); if (mobjinfo[locvar1].seesound) @@ -3064,7 +3077,7 @@ void A_Boss1Laser(mobj_t *actor) { point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); point->angle = actor->angle; - point->fuse = actor->tics+1; + point->fuse = dur+1; P_SetTarget(&point->target, actor->target); P_SetTarget(&actor->target, point); } @@ -3073,9 +3086,9 @@ void A_Boss1Laser(mobj_t *actor) else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/ - if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) - angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); - else + /*if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) + angle = FixedAngle(FixedDiv(dur*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); + else*/ angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); point = P_SpawnMobj(x, y, z, locvar1); @@ -3109,7 +3122,7 @@ void A_Boss1Laser(mobj_t *actor) point->fuse = TICRATE; } - if (actor->tics > 1) + if (dur > 1) actor->flags2 |= MF2_FIRING; else actor->flags2 &= ~MF2_FIRING; @@ -3253,6 +3266,7 @@ void A_Boss4Raise(mobj_t *actor) // 0 - Fly at the player // 1 - Fly away from the player // 2 - Strafe in relation to the player +// 3 - Dynamic mode - don't get too close to walls // var2: // 0 - Fly horizontally and vertically // 1 - Fly horizontal-only (momz = 0) @@ -3283,16 +3297,83 @@ void A_SkullAttack(mobj_t *actor) S_StartSound(actor, actor->info->activesound); A_FaceTarget(actor); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + if (locvar1 == 1) actor->angle += ANGLE_180; else if (locvar1 == 2) actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90; + else if (locvar1 == 3) + { + statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate; + UINT32 oldflags = mobjinfo[MT_NULL].flags; + fixed_t oldradius = mobjinfo[MT_NULL].radius; + fixed_t oldheight = mobjinfo[MT_NULL].height; + mobj_t *check; + INT32 i, j, k; + boolean allow; + angle_t testang; + + mobjinfo[MT_NULL].spawnstate = S_INVISIBLE; + mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP; + mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius; + mobjinfo[MT_NULL].height = mobjinfo[actor->type].height; + + if (P_RandomChance(FRACUNIT/2)) // port priority 1? + { + i = 9; + j = 27; + } + else + { + i = 27; + j = 9; + } + +#define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\ + testang = actor->angle + ((i+(q))*ANG10);\ + allow = (P_TryMove(check,\ + P_ReturnThrustX(check, testang, dist + 2*actor->radius),\ + P_ReturnThrustY(check, testang, dist + 2*actor->radius),\ + true));\ + P_RemoveMobj(check);\ + if (allow)\ + break; + + if (P_RandomChance(FRACUNIT/2)) // port priority 2? + { + for (k = 0; k < 9; k++) + { + dostuff(i+k) + dostuff(i-k) + dostuff(j+k) + dostuff(j-k) + } + } + else + { + for (k = 0; k < 9; k++) + { + dostuff(i-k) + dostuff(i+k) + dostuff(j-k) + dostuff(j+k) + } + } + actor->angle = testang; + +#undef dostuff + + mobjinfo[MT_NULL].spawnstate = oldspawnstate; + mobjinfo[MT_NULL].flags = oldflags; + mobjinfo[MT_NULL].radius = oldradius; + mobjinfo[MT_NULL].height = oldheight; + } an = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(speed, FINECOSINE(an)); actor->momy = FixedMul(speed, FINESINE(an)); - dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); dist = dist / speed; if (dist < 1) @@ -3442,11 +3523,13 @@ void A_Pain(mobj_t *actor) // // Description: Changes a dying object's flags to reflect its having fallen to the ground. // -// var1 = unused +// var1 = value to set repeat to if nonzero // var2 = unused // void A_Fall(mobj_t *actor) { + INT32 locvar1 = var1; + #ifdef HAVE_BLUA if (LUA_CallAction("A_Fall", actor)) return; @@ -3459,6 +3542,9 @@ void A_Fall(mobj_t *actor) // So change this if corpse objects // are meant to be obstacles. + + if (locvar1) + actor->extravalue2 = locvar1; } #define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player @@ -3854,6 +3940,72 @@ bossjustdie: else if (P_MobjWasRemoved(mo)) return; #endif + + // Spawn your junk + switch (mo->type) + { + default: + break; + case MT_EGGMOBILE: // twin laser pods + { + mo2 = P_SpawnMobjFromMobj(mo, + P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSEGLZ1); + + mo2 = P_SpawnMobjFromMobj(mo, + P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSEGLZ2); + } + break; + case MT_EGGMOBILE2: // twin tanks + spigot + { + mo2 = P_SpawnMobjFromMobj(mo, + P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSTANK1); + + mo2 = P_SpawnMobjFromMobj(mo, + P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSTANK2); + + mo2 = P_SpawnMobjFromMobj(mo, 0, 0, + mobjinfo[MT_EGGMOBILE2].height + (32<angle = mo->angle; + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + mo2->momz += mo->momz; + P_SetMobjState(mo2, S_BOSSSPIGOT); + } + break; + case MT_EGGMOBILE3: + { + mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOSSJUNK); + mo2->angle = mo->angle; + P_SetMobjState(mo2, S_BOSSSEBH1); + } + break; + } + + // now do another switch case for escaping switch (mo->type) { case MT_BLACKEGGMAN: @@ -3951,7 +4103,7 @@ bossjustdie: mo->movedir = 0; mo->extravalue1 = 35; mo->flags2 |= MF2_BOSSFLEE; - mo->momz = 2*mo->scale; + mo->momz = P_MobjFlip(mo)*2*mo->scale; if (mo->target) { @@ -3969,50 +4121,6 @@ bossjustdie: break; } } - - if (mo->type == MT_EGGMOBILE2) - { - mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - - mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - - mo2 = P_SpawnMobj(mo->x, mo->y, - mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT); - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - return; - } } // Function: A_CustomPower @@ -8699,7 +8807,7 @@ void A_SetObjectFlags2(mobj_t *actor) // // var1: // 0 - Triple jet fume pattern -// 1 - Boss 3's propeller +// 1 - Unused (formerly Boss 3's propeller) // 2 - Metal Sonic jet fume // 3 - Boss 4 jet flame // var2 = unused @@ -8759,7 +8867,7 @@ void A_BossJetFume(mobj_t *actor) P_SetTarget(&actor->tracer, filler); } - else if (locvar1 == 1) // Boss 3 propeller + /*else if (locvar1 == 1) // Boss 3 propeller { fixed_t jetx, jety, jetz; @@ -8779,14 +8887,14 @@ void A_BossJetFume(mobj_t *actor) filler->angle = actor->angle - ANGLE_180; P_SetTarget(&actor->tracer, filler); - } + }*/ else if (locvar1 == 2) // Metal Sonic jet fumes { filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1); P_SetTarget(&filler->target, actor); filler->fuse = 59; P_SetTarget(&actor->tracer, filler); - filler->destscale = actor->scale/2; + filler->destscale = actor->scale/3; P_SetScale(filler, filler->destscale); if (actor->eflags & MFE_VERTICALFLIP) filler->flags2 |= MF2_OBJECTFLIP; diff --git a/src/p_inter.c b/src/p_inter.c index 0030e8e58..ae610d0fc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2594,6 +2594,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_EGGMOBILE3: { + mobj_t *mo2; thinker_t *th; UINT32 i = 0; // to check how many clones we've removed @@ -2614,6 +2615,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget mo->scalespeed = (mo->scale - mo->destscale)/(2*TICRATE); mo->momz = mo->info->speed; mo->angle = FixedAngle((P_RandomKey(36)*10)<angle = mo->angle; + P_SetMobjState(mo2, S_BOSSSEBH2); + if (++i == 2) // we've already removed 2 of these, let's stop now break; else diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ee90d250..e5edc99c4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4359,12 +4359,6 @@ static void P_Boss3Thinker(mobj_t *mobj) if (mobj->flags2 & MF2_FRET) mobj->movedir = 1; - if (!mobj->tracer) - { - var1 = 1; - A_BossJetFume(mobj); - } - if (mobj->health <= 0) return; /* @@ -4493,7 +4487,7 @@ static void P_Boss3Thinker(mobj_t *mobj) if (mobj->health <= mobj->info->damage) // pinch phase mobj->movecount--; // limited number of shots before diving again if (mobj->movecount) - P_SetMobjState(mobj, mobj->info->missilestate); + P_SetMobjState(mobj, mobj->info->missilestate+1); } } else if (mobj->threshold >= 0) // Traveling mode @@ -4592,6 +4586,15 @@ static void P_Boss3Thinker(mobj_t *mobj) ang += (ANGLE_MAX/64); } S_StartSound(mobj, sfx_fizzle); + + // look for a new target + P_BossTargetPlayer(mobj, false); + + if (mobj->target && mobj->target->player) + { + A_FaceTarget(mobj); + P_SetMobjState(mobj, mobj->info->missilestate); + } } else if (mobj->flags2 & (MF2_STRONGBOX|MF2_CLASSICPUSH)) // just hit the bottom of your tube { @@ -5527,8 +5530,7 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->tracer->destscale = FRACUNIT + (4*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2); P_SetScale(mobj->tracer, mobj->tracer->destscale); } - else - mobj->tracer->frame &= ~FF_TRANSMASK; // this causes a flicker but honestly i like it this way + P_TeleportMove(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2); mobj->tracer->momx = mobj->momx; mobj->tracer->momy = mobj->momy; @@ -5645,12 +5647,12 @@ static void P_Boss9Thinker(mobj_t *mobj) if (mobj->health > mobj->info->damage) { - P_SetScale(missile, FRACUNIT/2); + P_SetScale(missile, FRACUNIT/3); missile->color = SKINCOLOR_GOLD; // sonic cd electric power } else { - P_SetScale(missile, FRACUNIT/4); + P_SetScale(missile, FRACUNIT/5); missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power } missile->destscale = missile->scale*2; @@ -5940,9 +5942,7 @@ static void P_Boss9Thinker(mobj_t *mobj) P_SetTarget(&mobj->tracer, shield); P_SetTarget(&shield->target, mobj); shield->height -= 20*FRACUNIT; // different offset... - shield->color = SKINCOLOR_MAGENTA; - shield->colorized = true; - P_SetMobjState(shield, S_FIRS1); + P_SetMobjState(shield, S_MSSHIELD_F2); //P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); -- why does this happen twice? see case 2... } mobj->fuse = 4*TICRATE; @@ -7093,9 +7093,7 @@ void P_MobjThinker(mobj_t *mobj) switch (mobj->type) { - case MT_BOSSTANK1: - case MT_BOSSTANK2: - case MT_BOSSSPIGOT: + case MT_BOSSJUNK: mobj->flags2 ^= MF2_DONTDRAW; break; case MT_MACEPOINT: @@ -7681,12 +7679,22 @@ void P_MobjThinker(mobj_t *mobj) switch (mobj->type) { case MT_EGGMOBILE: - if (mobj->health < mobj->info->damage+1 && leveltime & 1 && mobj->health > 0) - P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SMOKE); + if (mobj->health < mobj->info->damage+1 && leveltime & 2) + { + fixed_t rad = mobj->radius>>FRACBITS; + fixed_t hei = mobj->height>>FRACBITS; + mobj_t *particle = P_SpawnMobjFromMobj(mobj, + P_RandomRange(rad, -rad)<momz += mobj->momz; + } if (mobj->flags2 & MF2_SKULLFLY) #if 1 P_SpawnGhostMobj(mobj); -#else +#else // all the way back from final demo... MT_THOK isn't even the same size anymore! { mobj_t *spawnmobj; spawnmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->painchance); @@ -7697,12 +7705,48 @@ void P_MobjThinker(mobj_t *mobj) P_Boss1Thinker(mobj); break; case MT_EGGMOBILE2: + if (mobj->health < mobj->info->damage+1 && leveltime & 2) + { + fixed_t rad = mobj->radius>>FRACBITS; + fixed_t hei = mobj->height>>FRACBITS; + mobj_t *particle = P_SpawnMobjFromMobj(mobj, + P_RandomRange(rad, -rad)<momz += mobj->momz; + } P_Boss2Thinker(mobj); break; case MT_EGGMOBILE3: + if (mobj->health < mobj->info->damage+1 && leveltime & 2) + { + fixed_t rad = mobj->radius>>FRACBITS; + fixed_t hei = mobj->height>>FRACBITS; + mobj_t *particle = P_SpawnMobjFromMobj(mobj, + P_RandomRange(rad, -rad)<momz += mobj->momz; + } P_Boss3Thinker(mobj); break; case MT_EGGMOBILE4: + if (mobj->health < mobj->info->damage+1 && leveltime & 2) + { + fixed_t rad = mobj->radius>>FRACBITS; + fixed_t hei = mobj->height>>FRACBITS; + mobj_t *particle = P_SpawnMobjFromMobj(mobj, + P_RandomRange(rad, -rad)<momz += mobj->momz; + } P_Boss4Thinker(mobj); break; case MT_FANG: @@ -8318,30 +8362,6 @@ void P_MobjThinker(mobj_t *mobj) mobj->fuse++; } break; - case MT_PROPELLER: - { - fixed_t jetx, jety; - - if (!mobj->target // if you have no target - || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now - { // then remove yourself as well! - P_RemoveMobj(mobj); - return; - } - - jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); - jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); - - P_UnsetThingPosition(mobj); - mobj->x = jetx; - mobj->y = jety; - mobj->z = mobj->target->z + FixedMul(17*FRACUNIT, mobj->target->scale); - mobj->angle = mobj->target->angle - ANGLE_180; - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_SetThingPosition(mobj); - } - break; case MT_JETFLAME: { if (!mobj->target // if you have no target @@ -9038,9 +9058,9 @@ void P_MobjThinker(mobj_t *mobj) { if (mobj->state->action.acp1 == (actionf_p1)A_Boss1Laser) { - var1 = mobj->state->var1; - var2 = mobj->state->var2; - mobj->state->action.acp1(mobj); + /*var1 = mobj->state->var1; + var2 = mobj->state->var2 & 65535; + mobj->state->action.acp1(mobj);*/ } else if (leveltime & 1) // Fire mode { diff --git a/src/r_draw.c b/src/r_draw.c index d8b720caf..396ed0344 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -557,9 +557,16 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U // White! if (skinnum == TC_BOSS) - dest_colormap[31] = 0; + { + for (i = 0; i < 16; i++) + dest_colormap[31-i] = i; + } else if (skinnum == TC_METALSONIC) - dest_colormap[159] = 0; + { + for (i = 0; i < 6; i++) + dest_colormap[Color_Index[SKINCOLOR_BLUE-1][12-i]] = Color_Index[SKINCOLOR_BLUE-1][i]; + dest_colormap[159] = dest_colormap[253] = dest_colormap[254] = 0; + } return; } else if (color == SKINCOLOR_NONE) From bbe8ef9ff72710934692186a11e13eea9cfc3107 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 31 Aug 2019 17:06:01 -0400 Subject: [PATCH 068/196] Merged orbital cam, made my reset code not run in NiGHTS, 2D mode, or when exiting levels. --- src/p_user.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 4eeb2a7db..97ab5894a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9570,7 +9570,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (checkdist < 128*FRACUNIT) checkdist = 128*FRACUNIT; - if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) + if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) //Sev here, I'm guessing this is where orbital cam lives { distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); @@ -9584,6 +9584,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); + if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE) && !(player->exiting)) //Which is why I'm slapping my cam reset code in here too + { + if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+camdist)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+camdist)*2))) + P_ResetCamera(player, thiscam); + } + #if 0 if (twodlevel || (mo->flags2 & MF2_TWOD)) { @@ -9934,6 +9940,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming); + } boolean P_SpectatorJoinGame(player_t *player) @@ -11591,21 +11598,9 @@ void P_PlayerAfterThink(player_t *player) #endif if (splitscreen && player == &players[secondarydisplayplayer]) - { thiscam = &camera2; - if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam2_dist.value)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+cv_cam2_dist.value)*2))) - { - P_ResetCamera(player, thiscam); - } - } else if (player == &players[displayplayer]) - { thiscam = &camera; - if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+cv_cam_dist.value)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+cv_cam_dist.value)*2))) - { - P_ResetCamera(player, thiscam); - } - } if (player->playerstate == PST_DEAD) { From fe99c64511c71553d9ac5b945439b9986fd73151 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Sep 2019 11:43:30 +0100 Subject: [PATCH 069/196] Give the Spectator Eggrobos the ability to move left and right relative to their angle, with initial direction depending on MTF_OBJECTSPECIAL/MTF_AMBUSH flag presence. (May need more tweaking before putting in CEZ3) --- src/p_mobj.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index e5edc99c4..1f42e3664 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8425,6 +8425,17 @@ void P_MobjThinker(mobj_t *mobj) } else { + + fixed_t basex = mobj->cusval, basey = mobj->cvmem; + + if (mobj->spawnpoint && mobj->spawnpoint->options & (MTF_AMBUSH|MTF_OBJECTSPECIAL)) + { + angle_t sideang = mobj->movedir + ((mobj->spawnpoint->options & MTF_AMBUSH) ? ANGLE_90 : -ANGLE_90); + fixed_t oscillate = FixedMul(FINESINE(((leveltime*ANG1)>>(ANGLETOFINESHIFT+2)) & FINEMASK), 250*mobj->scale); + basex += P_ReturnThrustX(mobj, sideang, oscillate); + basey += P_ReturnThrustY(mobj, sideang, oscillate); + } + mobj->z = mobj->threshold + FixedMul(FINESINE(((leveltime + mobj->movecount)*ANG2>>(ANGLETOFINESHIFT-2)) & FINEMASK), 8*mobj->scale); if (mobj->state != &states[mobj->info->meleestate]) { @@ -8453,8 +8464,8 @@ void P_MobjThinker(mobj_t *mobj) if (players[i].mo->z + players[i].mo->height < mobj->z - 8*mobj->scale) continue; compdist = P_AproxDistance( - players[i].mo->x + players[i].mo->momx - mobj->cusval, - players[i].mo->y + players[i].mo->momy - mobj->cvmem); + players[i].mo->x + players[i].mo->momx - basex, + players[i].mo->y + players[i].mo->momy - basey); if (compdist >= dist) continue; dist = compdist; @@ -8468,14 +8479,14 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y); if (P_AproxDistance( - mobj->x - mobj->cusval, - mobj->y - mobj->cvmem) + mobj->x - basex, + mobj->y - basey) < mobj->scale) S_StartSound(mobj, mobj->info->seesound); P_TeleportMove(mobj, - (15*(mobj->x>>4)) + (mobj->cusval>>4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS>>4), - (15*(mobj->y>>4)) + (mobj->cvmem>>4) + P_ReturnThrustY(mobj, mobj->angle, SPECTATORRADIUS>>4), + (15*(mobj->x>>4)) + (basex>>4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS>>4), + (15*(mobj->y>>4)) + (basey>>4) + P_ReturnThrustY(mobj, mobj->angle, SPECTATORRADIUS>>4), mobj->z); } else @@ -8498,18 +8509,12 @@ void P_MobjThinker(mobj_t *mobj) if (!didmove) { - if (P_AproxDistance( - mobj->x - mobj->cusval, - mobj->y - mobj->cvmem) - < mobj->scale) - P_TeleportMove(mobj, - mobj->cusval, - mobj->cvmem, - mobj->z); + if (P_AproxDistance(mobj->x - basex, mobj->y - basey) < mobj->scale) + P_TeleportMove(mobj, basex, basey, mobj->z); else P_TeleportMove(mobj, - (15*(mobj->x>>4)) + (mobj->cusval>>4), - (15*(mobj->y>>4)) + (mobj->cvmem>>4), + (15*(mobj->x>>4)) + (basex>>4), + (15*(mobj->y>>4)) + (basey>>4), mobj->z); } } From f07309707d2cae3c90202c0a4ec5932df3443407 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Sep 2019 15:55:23 +0100 Subject: [PATCH 070/196] Lots of death stuff. * Genesis-style love and attention to the death event. * Only visibly decrement lives/rings when you're respawning (or game over, see below). * Faster no-button-press respawn. * Game Over specific love. * Animation of Level Title font coming in from the sides. * https://cdn.discordapp.com/attachments/428262628893261828/617692325438554132/srb20067.gif * Change gameovertics to 10 seconds instead of 15. * Make the minimum time before you can force going to the Continue screen longer. * Accomodate death in MP special stages as a form of exit. * Don't have your rings or spheres reset when you die in a special stage, so that the stage isn't softlocked with the new harder limits. * Fix a bug with CoopLives_OnChange where changing to infinite lives didn't force a game-overed player to respawn. Also, two not-quite death things which nonetheless were relevant to change: * Fix quitting a special stage having some of the shared spheres/rings disappear into the aether. * Fix a warning during compilation for the Ring Penalty print. --- src/d_clisrv.c | 27 ++++++++++++++++++++++----- src/d_netcmd.c | 10 +--------- src/g_game.c | 20 ++++++++++++++++---- src/p_inter.c | 5 +---- src/p_mobj.c | 3 +-- src/p_user.c | 30 +++++++++++++----------------- src/st_stuff.c | 42 +++++++++++++++++++++++------------------- 7 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 01e94485d..2ce395769 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2415,7 +2415,7 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason) // the remaining players. if (G_IsSpecialStage(gamemap)) { - INT32 i, count, increment, spheres; + INT32 i, count, sincrement, spheres, rincrement, rings; for (i = 0, count = 0; i < MAXPLAYERS; i++) { @@ -2425,18 +2425,35 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason) count--; spheres = players[playernum].spheres; - increment = spheres/count; + rings = players[playernum].rings; + sincrement = spheres/count; + rincrement = rings/count; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && i != playernum) { - if (spheres < increment) + if (spheres < 2*sincrement) + { P_GivePlayerSpheres(&players[i], spheres); + spheres = 0; + } else - P_GivePlayerSpheres(&players[i], increment); + { + P_GivePlayerSpheres(&players[i], sincrement); + spheres -= sincrement; + } - spheres -= increment; + if (rings < 2*rincrement) + { + P_GivePlayerRings(&players[i], rings); + rings = 0; + } + else + { + P_GivePlayerRings(&players[i], rincrement); + rings -= rincrement; + } } } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3e82fc60c..ba28b19a2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2706,14 +2706,6 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } } - // Clear player score and rings if a spectator. - if (players[playernum].spectator) - { - players[playernum].score = players[playernum].rings = 0; - if (players[playernum].mo) - players[playernum].mo->health = 1; - } - // In tag, check to see if you still have a game. if (G_TagGametype()) P_CheckSurvivors(); @@ -3600,7 +3592,7 @@ static void CoopLives_OnChange(void) { case 0: CONS_Printf(M_GetText("Players can now respawn indefinitely.\n")); - return; + break; case 1: CONS_Printf(M_GetText("Lives are now per-player.\n")); return; diff --git a/src/g_game.c b/src/g_game.c index d5faf6846..1630f085b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -215,7 +215,7 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; UINT16 nightslinktics = 2*TICRATE; -INT32 gameovertics = 15*TICRATE; +INT32 gameovertics = 10*TICRATE; UINT8 ammoremovaltics = 2*TICRATE; @@ -2145,6 +2145,8 @@ void G_PlayerReborn(INT32 player) boolean outofcoop; INT16 bot; SINT8 pity; + INT16 rings; + INT16 spheres; score = players[player].score; lives = players[player].lives; @@ -2199,6 +2201,17 @@ void G_PlayerReborn(INT32 player) bot = players[player].bot; pity = players[player].pity; + if (!G_IsSpecialStage(gamemap)) + { + rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings); + spheres = 0; + } + else + { + rings = players[player].rings; + spheres = players[player].spheres; + } + p = &players[player]; memset(p, 0, sizeof (*p)); @@ -2252,6 +2265,8 @@ void G_PlayerReborn(INT32 player) if (bot) p->bot = 1; // reset to AI-controlled p->pity = pity; + p->rings = rings; + p->spheres = spheres; // Don't do anything immediately p->pflags |= PF_USEDOWN; @@ -2259,7 +2274,6 @@ void G_PlayerReborn(INT32 player) p->pflags |= PF_JUMPDOWN; p->playerstate = PST_LIVE; - p->rings = p->spheres = 0; // 0 rings p->panim = PA_IDLE; // standing animation //if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there @@ -2370,8 +2384,6 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost) P_SpawnPlayer(playernum); - players[playernum].rings = mapheaderinfo[gamemap-1]->startrings; - if (starpost) //Don't even bother with looking for a place to spawn. { P_MovePlayerToStarpost(playernum); diff --git a/src/p_inter.c b/src/p_inter.c index 0030e8e58..9025e2664 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3500,7 +3500,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return true; } - if (G_IsSpecialStage(gamemap)) + if (G_IsSpecialStage(gamemap) && !(damagetype & DMG_DEATHMASK)) { P_SpecialStageDamage(player, inflictor, source); return true; @@ -3524,10 +3524,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Instant-Death if (damagetype & DMG_DEATHMASK) - { P_KillPlayer(player, source, damage); - player->rings = player->spheres = 0; - } else if (metalrecording) { if (!inflictor) diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ee90d250..abf5de87d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10397,7 +10397,7 @@ void P_SpawnPlayer(INT32 playernum) && ((leveltime > 0 && ((G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) // late join special stage || (cv_coopstarposts.value == 2 && (p->jointime < 1 || p->outofcoop)))) // late join or die in new coop - || (((cv_cooplives.value == 1) || !P_GetLives(p)) && p->lives <= 0))); // game over and can't redistribute lives + || (!P_GetLives(p) && p->lives <= 0))); // game over and can't redistribute lives } else { @@ -10464,7 +10464,6 @@ void P_SpawnPlayer(INT32 playernum) P_SetupStateAnimation(mobj, mobj->state); mobj->health = 1; - p->rings = p->spheres = 0; p->playerstate = PST_LIVE; p->bonustime = false; diff --git a/src/p_user.c b/src/p_user.c index a69bd5b93..08579e48d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9027,19 +9027,22 @@ boolean P_GetLives(player_t *player) INT32 i, maxlivesplayer = -1, livescheck = 1; if (!(netgame || multiplayer) || (gametype != GT_COOP) - || (cv_cooplives.value == 1) || (player->lives == INFLIVES)) return true; - if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0) - return true; - if (cv_cooplives.value == 0) // infinite lives { - player->lives++; + if (player->lives < 1) + player->lives = 1; return true; } + if ((cv_cooplives.value == 2 || cv_cooplives.value == 1) && player->lives > 0) + return true; + + if (cv_cooplives.value == 1) + return false; + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) @@ -9146,7 +9149,7 @@ static void P_DeathThink(player_t *player) // continue logic if (!(netgame || multiplayer) && player->lives <= 0) { - if (player->deadtimer > TICRATE && (cmd->buttons & BT_USE || cmd->buttons & BT_JUMP) && player->continues > 0) + if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_USE || cmd->buttons & BT_JUMP) && player->continues > 0) G_UseContinue(); else if (player->deadtimer >= gameovertics) G_UseContinue(); // Even if we don't have one this handles ending the game @@ -9170,12 +9173,12 @@ static void P_DeathThink(player_t *player) // Force respawn if idle for more than 30 seconds in shooter modes. if (player->deadtimer > 30*TICRATE && !G_PlatformGametype()) player->playerstate = PST_REBORN; - else if ((player->lives > 0 || j != MAXPLAYERS) && !G_IsSpecialStage(gamemap)) // Don't allow "click to respawn" in special stages! + else if ((player->lives > 0 || j != MAXPLAYERS) && !(G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages! { if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) { P_ConsiderAllGone(); - if ((player->deadtimer > 5*TICRATE) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))) + if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))) { //player->spectator = true; player->outofcoop = true; @@ -9191,16 +9194,11 @@ static void P_DeathThink(player_t *player) player->playerstate = PST_REBORN; else switch(gametype) { case GT_COOP: - if (player->deadtimer > TICRATE) - player->playerstate = PST_REBORN; - break; case GT_COMPETITION: + case GT_RACE: if (player->deadtimer > TICRATE) player->playerstate = PST_REBORN; break; - case GT_RACE: - player->playerstate = PST_REBORN; - break; default: if (player->deadtimer > cv_respawntime.value*TICRATE) player->playerstate = PST_REBORN; @@ -9209,7 +9207,7 @@ static void P_DeathThink(player_t *player) } // Single player auto respawn - if (!(netgame || multiplayer) && player->deadtimer > 5*TICRATE) + if (!(netgame || multiplayer) && player->deadtimer > TICRATE<<1) player->playerstate = PST_REBORN; } } @@ -11011,8 +11009,6 @@ void P_PlayerThink(player_t *player) { if (gametype != GT_COOP) player->score = 0; - player->mo->health = 1; - player->rings = player->spheres = 0; } else if ((netgame || multiplayer) && player->lives <= 0 && gametype != GT_COOP) { diff --git a/src/st_stuff.c b/src/st_stuff.c index 142cd3e61..0db72efd1 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -66,8 +66,6 @@ patch_t *sboperiod; // Period for time centiseconds patch_t *livesback; // Lives icon background static patch_t *nrec_timer; // Timer for NiGHTS records static patch_t *sborings; -static patch_t *sboover; -static patch_t *timeover; static patch_t *stlivex; static patch_t *sboredrings; static patch_t *sboredtime; @@ -253,8 +251,6 @@ void ST_LoadGraphics(void) sbocolon = W_CachePatchName("STTCOLON", PU_HUDGFX); // Colon for time sboperiod = W_CachePatchName("STTPERIO", PU_HUDGFX); // Period for time centiseconds - sboover = W_CachePatchName("SBOOVER", PU_HUDGFX); - timeover = W_CachePatchName("TIMEOVER", PU_HUDGFX); stlivex = W_CachePatchName("STLIVEX", PU_HUDGFX); livesback = W_CachePatchName("STLIVEBK", PU_HUDGFX); nrec_timer = W_CachePatchName("NGRTIMER", PU_HUDGFX); // Timer for NiGHTS @@ -768,7 +764,12 @@ static inline void ST_drawRings(void) ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS)); - ringnum = ((objectplacing) ? op_currentdoomednum : max(stplyr->rings, 0)); + if (objectplacing) + ringnum = op_currentdoomednum; + else if (stplyr->rings < 0 || stplyr->spectator || stplyr->playerstate == PST_REBORN) + ringnum = 0; + else + ringnum = stplyr->rings; if (cv_timetic.value == 2) // Yes, even in modeattacking ST_DrawNumFromHud(HUD_RINGSNUMTICS, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS)); @@ -877,6 +878,8 @@ static void ST_drawLivesArea(void) '\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false); else { + if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1))) + livescount++; if (livescount > 99) livescount = 99; V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, @@ -1960,7 +1963,7 @@ static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, I static void ST_drawMatchHUD(void) { - char penaltystr[5]; + char penaltystr[7]; const INT32 y = 176; // HUD_LIVES INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6; @@ -2409,25 +2412,20 @@ static void ST_overlayDrawer(void) } } - // GAME OVER pic + // GAME OVER hud if ((gametype == GT_COOP) && (netgame || multiplayer) && (cv_cooplives.value == 0)) ; else if (G_GametypeUsesLives() && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer))) { - patch_t *p; - - if (countdown == 1) - p = timeover; - else - p = sboover; + INT32 i = MAXPLAYERS; + INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1)); if ((gametype == GT_COOP) && (netgame || multiplayer) && (cv_cooplives.value != 1)) { - INT32 i; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) @@ -2437,15 +2435,21 @@ static void ST_overlayDrawer(void) continue; if (players[i].lives > 0) - { - p = NULL; break; - } } } - if (p) - V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, BASEVIDHEIGHT/2 - (SHORT(p->height)/2), V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS), p); + if (i == MAXPLAYERS && deadtimer >= 0) + { + const char *first = (countdown == 1) ? "TIME" : "GAME"; + const char *second = "OVER"; + INT32 w1 = V_LevelNameWidth(first), w2 = (w1 + 16 + V_LevelNameWidth(second))>>1; + INT32 lvlttlx1 = min(6*deadtimer, BASEVIDWIDTH/2), lvlttlx2 = BASEVIDWIDTH - lvlttlx1; + UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS); + + V_DrawLevelTitle(lvlttlx1 - w2, (BASEVIDHEIGHT-16)>>1, flags, first); + V_DrawLevelTitle(lvlttlx2 + w1 + 16 - w2, (BASEVIDHEIGHT-16)>>1, flags, "OVER"); + } } if (G_GametypeHasTeams()) From f6d2b5109b8a16f91b21fd0dcf9c169a825321d2 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 3 Sep 2019 02:12:17 -0300 Subject: [PATCH 071/196] PRBoom sky dome --- src/hardware/hw_drv.h | 2 + src/hardware/hw_main.c | 182 +++++++++++++---------- src/hardware/hw_main.h | 1 + src/hardware/r_opengl/r_opengl.c | 243 +++++++++++++++++++++++++++++++ src/r_main.c | 1 + src/sdl/hwsym_sdl.c | 1 + src/sdl/i_video.c | 1 + src/v_video.c | 1 + src/win32/win_dll.c | 2 + 9 files changed, 361 insertions(+), 73 deletions(-) diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index e2fa90eb0..e0507bc70 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -47,6 +47,7 @@ EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal, RGBA_t *pgamma); EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl); EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color); EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags); +EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform); EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags); EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor); EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo); @@ -89,6 +90,7 @@ struct hwdriver_s FinishUpdate pfnFinishUpdate; Draw2DLine pfnDraw2DLine; DrawPolygon pfnDrawPolygon; + RenderSkyDome pfnRenderSkyDome; SetBlend pfnSetBlend; ClearBuffer pfnClearBuffer; SetTexture pfnSetTexture; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c600800fd..b326786f8 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5805,86 +5805,122 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) // ========================================================================== // // ========================================================================== -static void HWR_DrawSkyBackground(void) +static void HWR_DrawSkyBackground(player_t *player) { - FOutVector v[4]; - angle_t angle; - float dimensionmultiply; - float aspectratio; - float angleturn; - - HWR_GetTexture(texturetranslation[skytexture]); - aspectratio = (float)vid.width/(float)vid.height; - - //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 - // because it's called just after clearing the screen - // and thus, the near clipping plane is set to 3.99 - // Sryder: Just use the near clipping plane value then - - // 3--2 - // | /| - // |/ | - // 0--1 - v[0].x = v[3].x = -ZCLIP_PLANE-1; - v[1].x = v[2].x = ZCLIP_PLANE+1; - v[0].y = v[1].y = -ZCLIP_PLANE-1; - v[2].y = v[3].y = ZCLIP_PLANE+1; - - v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; - - // X - - // NOTE: This doesn't work right with texture widths greater than 1024 - // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly - // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture - - angle = (dup_viewangle + gr_xtoviewangle[0]); - - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); - - v[0].sow = v[3].sow = (-1.0f * angle) / ((ANGLE_90-1)*dimensionmultiply); // left - v[2].sow = v[1].sow = v[0].sow + (1.0f/dimensionmultiply); // right (or left + 1.0f) - // use +angle and -1.0f above instead if you wanted old backwards behavior - - // Y - angle = aimingangle; - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); - - if (splitscreen) + if (cv_grskydome.value) { - dimensionmultiply *= 2; - angle *= 2; - } + FTransform transform; + const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + postimg_t *type; - // Middle of the sky should always be at angle 0 - // need to keep correct aspect ratio with X - if (atransform.flip) - { - // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously - v[3].tow = v[2].tow = -(0.5f-(0.5f/dimensionmultiply)); // top - v[0].tow = v[1].tow = v[3].tow - (1.0f/dimensionmultiply); // bottom (or top - 1.0f) + if (splitscreen && player == &players[secondarydisplayplayer]) + type = &postimgtype2; + else + type = &postimgtype; + + memset(&transform, 0x00, sizeof(FTransform)); + + //04/01/2000: Hurdler: added for T&L + // It should replace all other gr_viewxxx when finished + transform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); + transform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); + + if (*type == postimg_flip) + transform.flip = true; + else + transform.flip = false; + + transform.scalex = 1; + transform.scaley = (float)vid.width/vid.height; + transform.scalez = 1; + transform.fovxangle = fpov; // Tails + transform.fovyangle = fpov; // Tails + transform.splitscreen = splitscreen; + + HWR_GetTexture(texturetranslation[skytexture]); + HWD.pfnRenderSkyDome(skytexture, textures[skytexture]->width, textures[skytexture]->height, transform); } else { - v[0].tow = v[1].tow = -(0.5f-(0.5f/dimensionmultiply)); // bottom - v[3].tow = v[2].tow = v[0].tow - (1.0f/dimensionmultiply); // top (or bottom - 1.0f) - } + FOutVector v[4]; + angle_t angle; + float dimensionmultiply; + float aspectratio; + float angleturn; - angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; + HWR_GetTexture(texturetranslation[skytexture]); + aspectratio = (float)vid.width/(float)vid.height; - if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa - { - angle = InvAngle(angle); - v[3].tow = v[2].tow += ((float) angle / angleturn); - v[0].tow = v[1].tow += ((float) angle / angleturn); - } - else - { - v[3].tow = v[2].tow -= ((float) angle / angleturn); - v[0].tow = v[1].tow -= ((float) angle / angleturn); - } + //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 + // because it's called just after clearing the screen + // and thus, the near clipping plane is set to 3.99 + // Sryder: Just use the near clipping plane value then - HWD.pfnDrawPolygon(NULL, v, 4, 0); + // 3--2 + // | /| + // |/ | + // 0--1 + v[0].x = v[3].x = -ZCLIP_PLANE-1; + v[1].x = v[2].x = ZCLIP_PLANE+1; + v[0].y = v[1].y = -ZCLIP_PLANE-1; + v[2].y = v[3].y = ZCLIP_PLANE+1; + + v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; + + // X + + // NOTE: This doesn't work right with texture widths greater than 1024 + // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly + // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture + + angle = (dup_viewangle + gr_xtoviewangle[0]); + + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); + + v[0].sow = v[3].sow = (-1.0f * angle) / ((ANGLE_90-1)*dimensionmultiply); // left + v[2].sow = v[1].sow = v[0].sow + (1.0f/dimensionmultiply); // right (or left + 1.0f) + // use +angle and -1.0f above instead if you wanted old backwards behavior + + // Y + angle = aimingangle; + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); + + if (splitscreen) + { + dimensionmultiply *= 2; + angle *= 2; + } + + // Middle of the sky should always be at angle 0 + // need to keep correct aspect ratio with X + if (atransform.flip) + { + // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously + v[3].tow = v[2].tow = -(0.5f-(0.5f/dimensionmultiply)); // top + v[0].tow = v[1].tow = v[3].tow - (1.0f/dimensionmultiply); // bottom (or top - 1.0f) + } + else + { + v[0].tow = v[1].tow = -(0.5f-(0.5f/dimensionmultiply)); // bottom + v[3].tow = v[2].tow = v[0].tow - (1.0f/dimensionmultiply); // top (or bottom - 1.0f) + } + + angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; + + if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa + { + angle = InvAngle(angle); + v[3].tow = v[2].tow += ((float) angle / angleturn); + v[0].tow = v[1].tow += ((float) angle / angleturn); + } + else + { + v[3].tow = v[2].tow -= ((float) angle / angleturn); + v[0].tow = v[1].tow -= ((float) angle / angleturn); + } + + HWD.pfnDrawPolygon(NULL, v, 4, 0); + } } @@ -6036,7 +6072,7 @@ if (0) } if (drawsky) - HWR_DrawSkyBackground(); + HWR_DrawSkyBackground(player); //Hurdler: it doesn't work in splitscreen mode drawsky = splitscreen; @@ -6253,7 +6289,7 @@ if (0) } if (!skybox && drawsky) // Don't draw the regular sky if there's a skybox - HWR_DrawSkyBackground(); + HWR_DrawSkyBackground(player); //Hurdler: it doesn't work in splitscreen mode drawsky = splitscreen; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index f8524990f..31e97cc13 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -98,6 +98,7 @@ extern consvar_t cv_voodoocompatibility; extern consvar_t cv_grfovchange; extern consvar_t cv_grsolvetjoin; extern consvar_t cv_grspritebillboarding; +extern consvar_t cv_grskydome; extern float gr_viewwidth, gr_viewheight, gr_baseviewwindowy; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index dfee19857..ae6ff7d09 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1427,6 +1427,249 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, Clamp2D(GL_TEXTURE_WRAP_T); } +// PRBoom sky dome +typedef struct vbo_vertex_s +{ + float x, y, z; + float u, v; + unsigned char r, g, b, a; +} vbo_vertex_t; + +typedef struct +{ + int mode; + int vertexcount; + int vertexindex; + int use_texture; +} GLSkyLoopDef; + +typedef struct +{ + int id; + int rows, columns; + int loopcount; + GLSkyLoopDef *loops; + vbo_vertex_t *data; +} GLSkyVBO; + +// The texture offset to be applied to the texture coordinates in SkyVertex(). +static int rows, columns; +static boolean yflip; +static int texw, texh; +static float yAdd; +static boolean foglayer; +static float delta = 0.0f; +static int gl_sky_detail = 16; +static INT32 lasttex = -1; + +static RGBA_t SkyColor; + +#define MAP_COEFF 128.0f +#define MAP_SCALE (MAP_COEFF*(float)FRACUNIT) + +static void SkyVertex(vbo_vertex_t *vbo, int r, int c) +{ + static fixed_t scale = 10000 << FRACBITS; + static angle_t maxSideAngle = ANGLE_180 / 3; + + angle_t topAngle = (angle_t)(c / (float)columns * ANGLE_MAX); + angle_t sideAngle = maxSideAngle * (rows - r) / rows; + fixed_t height = FINESINE(sideAngle>>ANGLETOFINESHIFT); + fixed_t realRadius = FixedMul(scale, FINECOSINE(sideAngle>>ANGLETOFINESHIFT)); + fixed_t x = FixedMul(realRadius, FINECOSINE(topAngle>>ANGLETOFINESHIFT)); + fixed_t y = (!yflip) ? FixedMul(scale, height) : FixedMul(scale, height) * -1; + fixed_t z = FixedMul(realRadius, FINESINE(topAngle>>ANGLETOFINESHIFT)); + float timesRepeat; + + timesRepeat = (short)(4 * (256.0f / texw)); + if (timesRepeat == 0.0f) + timesRepeat = 1.0f; + + if (!foglayer) + { + vbo->r = 255; + vbo->g = 255; + vbo->b = 255; + vbo->a = (r == 0 ? 0 : 255); + + // And the texture coordinates. + if (!yflip) // Flipped Y is for the lower hemisphere. + { + vbo->u = (-timesRepeat * c / (float)columns); + vbo->v = (r / (float)rows) * 1.f + yAdd; + } + else + { + vbo->u = (-timesRepeat * c / (float)columns); + vbo->v = ((rows-r)/(float)rows) * 1.f + yAdd; + } + + //if (SkyBox.wall.flag == GLDWF_SKYFLIP) + // vbo->u = -vbo->u; + } + + if (r != 4) + y += FRACUNIT * 300; + + // And finally the vertex. + vbo->x = (float)x/(float)MAP_SCALE; + vbo->y = (float)y/(float)MAP_SCALE + delta; + vbo->z = (float)z/(float)MAP_SCALE; +} + +GLSkyVBO sky_vbo; + +static void gld_BuildSky(int row_count, int col_count) +{ + int c, r; + vbo_vertex_t *vertex_p; + int vertex_count = 2 * row_count * (col_count * 2 + 2) + col_count * 2; + + GLSkyVBO *vbo = &sky_vbo; + + if ((vbo->columns != col_count) || (vbo->rows != row_count)) + { + free(vbo->loops); + free(vbo->data); + memset(vbo, 0, sizeof(&vbo)); + } + + if (!vbo->data) + { + memset(vbo, 0, sizeof(&vbo)); + vbo->loops = malloc((row_count * 2 + 2) * sizeof(vbo->loops[0])); + // create vertex array + vbo->data = malloc(vertex_count * sizeof(vbo->data[0])); + } + + vbo->columns = col_count; + vbo->rows = row_count; + + vertex_p = &vbo->data[0]; + vbo->loopcount = 0; + + memset(&SkyColor, 0xFF, sizeof(SkyColor)); + + for (yflip = 0; yflip < 2; yflip++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_FAN; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = col_count; + vbo->loops[vbo->loopcount].use_texture = false; + vbo->loopcount++; + + yAdd = 0.5f; + /*if (yflip == 0) + SkyColor = &sky->CeilingSkyColor[vbo_idx]; + else + SkyColor = &sky->FloorSkyColor[vbo_idx];*/ + + delta = 0.0f; + foglayer = true; + for (c = 0; c < col_count; c++) + { + SkyVertex(vertex_p, 1, c); + vertex_p->r = SkyColor.s.red; + vertex_p->g = SkyColor.s.green; + vertex_p->b = SkyColor.s.blue; + vertex_p->a = 255; + vertex_p++; + } + foglayer = false; + + delta = (yflip ? 5.0f : -5.0f) / MAP_COEFF; + + for (r = 0; r < row_count; r++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_STRIP; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = 2 * col_count + 2; + vbo->loops[vbo->loopcount].use_texture = true; + vbo->loopcount++; + + for (c = 0; c <= col_count; c++) + { + SkyVertex(vertex_p++, r + (yflip ? 1 : 0), (c ? c : 0)); + SkyVertex(vertex_p++, r + (yflip ? 0 : 1), (c ? c : 0)); + } + } + } +} + +static void RenderDomeForReal(INT32 skytexture) +{ + int i, j; + GLSkyVBO *vbo = &sky_vbo; + + //pglRotatef(-180.0f + sky->x_offset, 0.f, 1.f, 0.f); + pglRotatef(-180.0f, 0.f, 1.f, 0.f); + + rows = 4; + columns = 4 * gl_sky_detail; + + if (lasttex != skytexture) + { + lasttex = skytexture; + gld_BuildSky(rows, columns); + } + + pglScalef(1.0f, (float)texh / 230.0f, 1.0f); + + for (j = 0; j < 2; j++) + { + //gld_EnableTexture2D(GL_TEXTURE0_ARB, j != 0); + for (i = 0; i < vbo->loopcount; i++) + { + GLSkyLoopDef *loop = &vbo->loops[i]; + + if (j == 0 ? loop->use_texture : !loop->use_texture) + continue; + else + { + int k; + pglBegin(loop->mode); + for (k = loop->vertexindex; k < (loop->vertexindex + loop->vertexcount); k++) + { + vbo_vertex_t *v = &vbo->data[k]; + if (loop->use_texture) + pglTexCoord2f(v->u, v->v); + pglColor4f(v->r, v->g, v->b, v->a); + pglVertex3f(v->x, v->y, v->z); + } + pglEnd(); + } + } + } + + pglScalef(1.0f, 1.0f, 1.0f); + + // current color is undefined after glDrawArrays + pglColor4f(1.0f, 1.0f, 1.0f, 1.0f); +} + +EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform) +{ + GLint shading_mode = GL_FLAT; + pglGetIntegerv(GL_SHADE_MODEL, &shading_mode); + pglShadeModel(GL_SMOOTH); + + pglDepthMask(false); + pglDisable(GL_DEPTH_TEST); + pglDisable(GL_ALPHA_TEST); + + SetBlend(PF_Translucent|PF_Clip|PF_NoZClip|PF_NoDepthTest|PF_Modulated); + + texw = texture_width; + texh = texture_height; + SetTransform(&transform); + RenderDomeForReal(tex); + + pglEnable(GL_ALPHA_TEST); + pglEnable(GL_DEPTH_TEST); + pglDepthMask(true); + + pglShadeModel(shading_mode); +} // ========================================================================== // diff --git a/src/r_main.c b/src/r_main.c index db351e991..5135e57ce 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1215,6 +1215,7 @@ void R_RegisterEngineStuff(void) #endif CV_RegisterVar(&cv_grmd2); CV_RegisterVar(&cv_grspritebillboarding); + CV_RegisterVar(&cv_grskydome); #endif #ifdef HWRENDER diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 05ac6450e..103398405 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -79,6 +79,7 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(Init); GETFUNC(Draw2DLine); GETFUNC(DrawPolygon); + GETFUNC(RenderSkyDome); GETFUNC(SetBlend); GETFUNC(ClearBuffer); GETFUNC(SetTexture); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 5a4fd7a02..b526e6124 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1499,6 +1499,7 @@ void I_StartupGraphics(void) HWD.pfnFinishUpdate = NULL; HWD.pfnDraw2DLine = hwSym("Draw2DLine",NULL); HWD.pfnDrawPolygon = hwSym("DrawPolygon",NULL); + HWD.pfnRenderSkyDome = hwSym("RenderSkyDome",NULL); HWD.pfnSetBlend = hwSym("SetBlend",NULL); HWD.pfnClearBuffer = hwSym("ClearBuffer",NULL); HWD.pfnSetTexture = hwSym("SetTexture",NULL); diff --git a/src/v_video.c b/src/v_video.c index 2ec06a787..2dbb21bb3 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -112,6 +112,7 @@ static CV_PossibleValue_t CV_MD2[] = {{0, "Off"}, {1, "On"}, {2, "Old"}, {0, NUL // console variables in development consvar_t cv_grmd2 = {"gr_md2", "Off", CV_SAVE, CV_MD2, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grspritebillboarding = {"gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; #endif // local copy of the palette for V_GetColor() diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c index 71eda0437..ce007af25 100644 --- a/src/win32/win_dll.c +++ b/src/win32/win_dll.c @@ -102,6 +102,7 @@ static loadfunc_t hwdFuncTable[] = { {"FinishUpdate@4", &hwdriver.pfnFinishUpdate}, {"Draw2DLine@12", &hwdriver.pfnDraw2DLine}, {"DrawPolygon@16", &hwdriver.pfnDrawPolygon}, + {"RenderSkyDome@16", &hwdriver.pfnRenderDome}, {"SetBlend@4", &hwdriver.pfnSetBlend}, {"ClearBuffer@12", &hwdriver.pfnClearBuffer}, {"SetTexture@4", &hwdriver.pfnSetTexture}, @@ -133,6 +134,7 @@ static loadfunc_t hwdFuncTable[] = { {"FinishUpdate", &hwdriver.pfnFinishUpdate}, {"Draw2DLine", &hwdriver.pfnDraw2DLine}, {"DrawPolygon", &hwdriver.pfnDrawPolygon}, + {"RenderSkyDome", &hwdriver.pfnRenderDome}, {"SetBlend", &hwdriver.pfnSetBlend}, {"ClearBuffer", &hwdriver.pfnClearBuffer}, {"SetTexture", &hwdriver.pfnSetTexture}, From f0b4a609a819d7925e0aaba3f86a57dcb2fb9fd9 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 3 Sep 2019 23:44:04 -0300 Subject: [PATCH 072/196] Very tiny fix --- src/hardware/r_opengl/r_opengl.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index ae6ff7d09..a44556f1d 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1649,26 +1649,15 @@ static void RenderDomeForReal(INT32 skytexture) EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform) { - GLint shading_mode = GL_FLAT; - pglGetIntegerv(GL_SHADE_MODEL, &shading_mode); - pglShadeModel(GL_SMOOTH); - - pglDepthMask(false); - pglDisable(GL_DEPTH_TEST); - pglDisable(GL_ALPHA_TEST); - - SetBlend(PF_Translucent|PF_Clip|PF_NoZClip|PF_NoDepthTest|PF_Modulated); + SetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated); + SetTransform(&transform); texw = texture_width; texh = texture_height; - SetTransform(&transform); RenderDomeForReal(tex); - pglEnable(GL_ALPHA_TEST); - pglEnable(GL_DEPTH_TEST); - pglDepthMask(true); - - pglShadeModel(shading_mode); + // HWR_DrawSkyBackground left no blend flags after rendering the sky + SetBlend(0); } // ========================================================================== From 7177d76cfd64760a6a9e39df10b7f450e6a7f49a Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 4 Sep 2019 15:21:00 +0100 Subject: [PATCH 073/196] Seperate "Game" and "Over" assets, per Sev's request and design. https://cdn.discordapp.com/attachments/428262628893261828/618812279127015475/srb20069.gif --- src/st_stuff.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 0db72efd1..caa91120c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -66,6 +66,9 @@ patch_t *sboperiod; // Period for time centiseconds patch_t *livesback; // Lives icon background static patch_t *nrec_timer; // Timer for NiGHTS records static patch_t *sborings; +static patch_t *slidgame; +static patch_t *slidtime; +static patch_t *slidover; static patch_t *stlivex; static patch_t *sboredrings; static patch_t *sboredtime; @@ -251,6 +254,10 @@ void ST_LoadGraphics(void) sbocolon = W_CachePatchName("STTCOLON", PU_HUDGFX); // Colon for time sboperiod = W_CachePatchName("STTPERIO", PU_HUDGFX); // Period for time centiseconds + slidgame = W_CachePatchName("SLIDGAME", PU_HUDGFX); + slidtime = W_CachePatchName("SLIDTIME", PU_HUDGFX); + slidover = W_CachePatchName("SLIDOVER", PU_HUDGFX); + stlivex = W_CachePatchName("STLIVEX", PU_HUDGFX); livesback = W_CachePatchName("STLIVEBK", PU_HUDGFX); nrec_timer = W_CachePatchName("NGRTIMER", PU_HUDGFX); // Timer for NiGHTS @@ -2417,7 +2424,7 @@ static void ST_overlayDrawer(void) && (netgame || multiplayer) && (cv_cooplives.value == 0)) ; - else if (G_GametypeUsesLives() && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer))) + else if ((G_GametypeUsesLives() || gametype == GT_RACE) && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer))) { INT32 i = MAXPLAYERS; INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1)); @@ -2441,14 +2448,11 @@ static void ST_overlayDrawer(void) if (i == MAXPLAYERS && deadtimer >= 0) { - const char *first = (countdown == 1) ? "TIME" : "GAME"; - const char *second = "OVER"; - INT32 w1 = V_LevelNameWidth(first), w2 = (w1 + 16 + V_LevelNameWidth(second))>>1; - INT32 lvlttlx1 = min(6*deadtimer, BASEVIDWIDTH/2), lvlttlx2 = BASEVIDWIDTH - lvlttlx1; + INT32 lvlttlx = min(6*deadtimer, BASEVIDWIDTH/2); UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS); - V_DrawLevelTitle(lvlttlx1 - w2, (BASEVIDHEIGHT-16)>>1, flags, first); - V_DrawLevelTitle(lvlttlx2 + w1 + 16 - w2, (BASEVIDHEIGHT-16)>>1, flags, "OVER"); + V_DrawScaledPatch(lvlttlx - 8, BASEVIDHEIGHT/2, flags, (countdown == 1 ? slidtime : slidgame)); + V_DrawScaledPatch(BASEVIDWIDTH + 8 - lvlttlx, BASEVIDHEIGHT/2, flags, slidover); } } From 0986195d21ed346a2902b48c3026ca35313558a8 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 4 Sep 2019 15:51:14 +0100 Subject: [PATCH 074/196] Bunch of fixes for dying in MP special stages. * Don't allow the stage to be reloaded in G_DoReborn. * If you do ANY spawn after the very beginning moment, you're forced to be a spectator. * Have the "%d player%s remaining" Co-op exiting count visible at the same time as spectator controls. --- src/g_game.c | 2 +- src/p_mobj.c | 2 +- src/p_user.c | 2 +- src/st_stuff.c | 126 ++++++++++++++++++++++++------------------------- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1630f085b..1bbf8aa4a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2606,7 +2606,7 @@ void G_DoReborn(INT32 playernum) if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP)) resetlevel = true; - else if (gametype == GT_COOP && (netgame || multiplayer)) + else if (gametype == GT_COOP && (netgame || multiplayer) && !G_IsSpecialStage(gamemap)) { boolean notgameover = true; diff --git a/src/p_mobj.c b/src/p_mobj.c index abf5de87d..61c021cfb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10395,7 +10395,7 @@ void P_SpawnPlayer(INT32 playernum) p->spectator = p->outofcoop = (((multiplayer || netgame) && gametype == GT_COOP) // only question status in coop && ((leveltime > 0 - && ((G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) // late join special stage + && ((G_IsSpecialStage(gamemap)) // late join special stage || (cv_coopstarposts.value == 2 && (p->jointime < 1 || p->outofcoop)))) // late join or die in new coop || (!P_GetLives(p) && p->lives <= 0))); // game over and can't redistribute lives } diff --git a/src/p_user.c b/src/p_user.c index 08579e48d..5da17ee24 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9173,7 +9173,7 @@ static void P_DeathThink(player_t *player) // Force respawn if idle for more than 30 seconds in shooter modes. if (player->deadtimer > 30*TICRATE && !G_PlatformGametype()) player->playerstate = PST_REBORN; - else if ((player->lives > 0 || j != MAXPLAYERS) && !(G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages! + else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages! { if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) { diff --git a/src/st_stuff.c b/src/st_stuff.c index caa91120c..b59d18143 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2039,7 +2039,68 @@ static void ST_drawTextHUD(void) textHUDdraw(va("Lap:""\x82 %u/%d", stplyr->laps+1, cv_numlaps.value)) } - if (!stplyr->spectator && stplyr->exiting && cv_playersforexit.value && gametype == GT_COOP) + if (gametype != GT_COOP && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1))) + { + if (!splitscreen && !donef12) + { + textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) + donef12 = true; + } + } + else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text. + { + INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE; + + if (respawntime > 0 && !stplyr->spectator) + textHUDdraw(va(M_GetText("Respawn in %d..."), respawntime)) + else + textHUDdraw(M_GetText("\x82""JUMP:""\x80 Respawn")) + } + else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE)) + { + if (!splitscreen && !donef12) + { + textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) + donef12 = true; + } + + textHUDdraw(M_GetText("\x82""JUMP:""\x80 Rise")) + textHUDdraw(M_GetText("\x82""SPIN:""\x80 Lower")) + + if (G_IsSpecialStage(gamemap)) + textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) + else if (gametype == GT_COOP) + { + if (stplyr->lives <= 0 + && cv_cooplives.value == 2 + && (netgame || multiplayer)) + { + INT32 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + if (&players[i] == stplyr) + continue; + + if (players[i].lives > 1) + break; + } + + if (i != MAXPLAYERS) + textHUDdraw(M_GetText("You'll steal a life on respawn...")) + else + textHUDdraw(M_GetText("Wait to respawn...")) + } + else + textHUDdraw(M_GetText("Wait to respawn...")) + } + else + textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game")) + } + + if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting && cv_playersforexit.value) { INT32 i, total = 0, exiting = 0; @@ -2074,68 +2135,7 @@ static void ST_drawTextHUD(void) textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s"))) } } - else if (gametype != GT_COOP && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1))) - { - if (!splitscreen && !donef12) - { - textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) - donef12 = true; - } - } - else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) //Death overrides spectator text. - { - INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE; - - if (respawntime > 0 && !stplyr->spectator) - textHUDdraw(va(M_GetText("Respawn in %d..."), respawntime)) - else - textHUDdraw(M_GetText("\x82""JUMP:""\x80 Respawn")) - } - else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE)) - { - if (!splitscreen && !donef12) - { - textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view")) - donef12 = true; - } - - textHUDdraw(M_GetText("\x82""JUMP:""\x80 Rise")) - textHUDdraw(M_GetText("\x82""SPIN:""\x80 Lower")) - - if (G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) - textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) - else if (gametype == GT_COOP) - { - if (stplyr->lives <= 0 - && cv_cooplives.value == 2 - && (netgame || multiplayer)) - { - INT32 i; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (&players[i] == stplyr) - continue; - - if (players[i].lives > 1) - break; - } - - if (i != MAXPLAYERS) - textHUDdraw(M_GetText("You'll steal a life on respawn...")) - else - textHUDdraw(M_GetText("Wait to respawn...")) - } - else - textHUDdraw(M_GetText("Wait to respawn...")) - } - else - textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game")) - } - - if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator)) + else if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator)) { if (leveltime < hidetime * TICRATE) { From 5bc034bc3e1c4117838f9bf44d860501ff231561 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 4 Sep 2019 15:52:25 +0100 Subject: [PATCH 075/196] [slightly off-topic commit] don't allow MP Special Stages to be selectable via the MP level platter --- src/m_menu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/m_menu.c b/src/m_menu.c index 128b15a76..cb1fa03d7 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -4510,6 +4510,9 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt) if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) return false; + if (G_IsSpecialStage(mapnum+1)) + return false; + if (gt == GT_COOP && (mapheaderinfo[mapnum]->typeoflevel & TOL_COOP)) return true; From c6f3e4d53dd3bbd9b330e9930bf3a1f572d8d327 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 4 Sep 2019 13:59:09 -0300 Subject: [PATCH 076/196] Make sky dome look a bit better --- src/hardware/r_opengl/r_opengl.c | 33 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index a44556f1d..5b2dffc1a 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1456,7 +1456,7 @@ typedef struct static int rows, columns; static boolean yflip; static int texw, texh; -static float yAdd; +static float yMult, yAdd; static boolean foglayer; static float delta = 0.0f; static int gl_sky_detail = 16; @@ -1487,30 +1487,24 @@ static void SkyVertex(vbo_vertex_t *vbo, int r, int c) if (!foglayer) { + boolean flip = yflip; vbo->r = 255; vbo->g = 255; vbo->b = 255; vbo->a = (r == 0 ? 0 : 255); + // Flip Y coordinate anyway for the top part of the hemisphere + if (r <= 1) + flip = !flip; + // And the texture coordinates. - if (!yflip) // Flipped Y is for the lower hemisphere. - { - vbo->u = (-timesRepeat * c / (float)columns); - vbo->v = (r / (float)rows) * 1.f + yAdd; - } + vbo->u = (-timesRepeat * c / (float)columns); + if (!flip) // Flipped Y is for the lower hemisphere. + vbo->v = (r / (float)rows) * 1.f * yMult + yAdd; else - { - vbo->u = (-timesRepeat * c / (float)columns); - vbo->v = ((rows-r)/(float)rows) * 1.f + yAdd; - } - - //if (SkyBox.wall.flag == GLDWF_SKYFLIP) - // vbo->u = -vbo->u; + vbo->v = ((rows-r)/(float)rows) * 1.f * yMult + yAdd; } - if (r != 4) - y += FRACUNIT * 300; - // And finally the vertex. vbo->x = (float)x/(float)MAP_SCALE; vbo->y = (float)y/(float)MAP_SCALE + delta; @@ -1550,7 +1544,8 @@ static void gld_BuildSky(int row_count, int col_count) memset(&SkyColor, 0xFF, sizeof(SkyColor)); - for (yflip = 0; yflip < 2; yflip++) + // Why not? + for (yflip = false; yflip <= true; yflip++) { vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_FAN; vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; @@ -1559,6 +1554,7 @@ static void gld_BuildSky(int row_count, int col_count) vbo->loopcount++; yAdd = 0.5f; + yMult = 1.0f; /*if (yflip == 0) SkyColor = &sky->CeilingSkyColor[vbo_idx]; else @@ -1584,7 +1580,7 @@ static void gld_BuildSky(int row_count, int col_count) vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_STRIP; vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; vbo->loops[vbo->loopcount].vertexcount = 2 * col_count + 2; - vbo->loops[vbo->loopcount].use_texture = true; + vbo->loops[vbo->loopcount].use_texture = true; //(r > 1) ? true : false; vbo->loopcount++; for (c = 0; c <= col_count; c++) @@ -1617,7 +1613,6 @@ static void RenderDomeForReal(INT32 skytexture) for (j = 0; j < 2; j++) { - //gld_EnableTexture2D(GL_TEXTURE0_ARB, j != 0); for (i = 0; i < vbo->loopcount; i++) { GLSkyLoopDef *loop = &vbo->loops[i]; From 6aedca899f7410633050462b841dd9b2c96dc176 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 5 Sep 2019 17:42:36 -0300 Subject: [PATCH 077/196] 2.2 skies aren't flipped --- src/hardware/r_opengl/r_opengl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 5b2dffc1a..2fe6741e2 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1597,8 +1597,7 @@ static void RenderDomeForReal(INT32 skytexture) int i, j; GLSkyVBO *vbo = &sky_vbo; - //pglRotatef(-180.0f + sky->x_offset, 0.f, 1.f, 0.f); - pglRotatef(-180.0f, 0.f, 1.f, 0.f); + pglRotatef(270.f, 0.f, 1.f, 0.f); rows = 4; columns = 4 * gl_sky_detail; From bb2012a8d046702e445962b5f299c2e0c7102bcb Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 6 Sep 2019 19:01:46 +0100 Subject: [PATCH 078/196] Clean up an ungodly amount of shit relating to abilities. * Put everything in P_DoJumpStuff, instead of half in that function and half spread across the player thinker. * Have a proper if else cascade that first tries shield abilities, then super transformation, then random abilities like CA_TELEKINESIS. * Use this new arrangement to allow CA_TWINSPIN users to use their ability on spin if their secondary ability is CA2_MELEE (resolves #195). * Random bugfixing. Didn't keep track of what I'd caused while working on this and what was already there, but there was a lot of it. The only two abilities which have spin-button properties outside of the else block is CA_AIRDRILL and CA_FLY/CA_SWIM (which now also prevents you from swimming down in goowater). --- src/p_inter.c | 1 + src/p_mobj.c | 2 +- src/p_user.c | 432 ++++++++++++++++++++++++++------------------------ 3 files changed, 228 insertions(+), 207 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 0030e8e58..d3cfe1567 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1583,6 +1583,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Buenos Dias Mandy P_SetPlayerMobjState(toucher, S_PLAY_STUN); player->pflags &= ~PF_APPLYAUTOBRAKE; + P_ResetPlayer(player); player->drawangle = special->angle + ANGLE_180; P_InstaThrust(toucher, special->angle, FixedMul(3*special->info->speed, special->scale/2)); toucher->z += P_MobjFlip(toucher); diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ee90d250..64df86813 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2954,7 +2954,7 @@ static void P_PlayerZMovement(mobj_t *mo) } // Get up if you fell. if (mo->player->panim == PA_PAIN) - P_SetPlayerMobjState(mo, S_PLAY_STND); + P_SetPlayerMobjState(mo, S_PLAY_WALK); #ifdef ESLOPE if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { diff --git a/src/p_user.c b/src/p_user.c index a69bd5b93..0550c7f69 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4886,56 +4886,185 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range) // static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { - mobj_t *lockon = NULL; + mobj_t *lockonthok = NULL, *lockonshield = NULL, *visual = NULL; if (player->pflags & PF_JUMPSTASIS) return; - if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) && (lockon = P_LookForEnemies(player, true, false))) + if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) && (lockonthok = P_LookForEnemies(player, true, false))) { if (P_IsLocalPlayer(player)) // Only display it on your own view. { - mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockon); + visual = P_SpawnMobj(lockonthok->x, lockonthok->y, lockonthok->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker + P_SetTarget(&visual->target, lockonthok); } } - if (cmd->buttons & BT_USE && !(player->pflags & PF_JUMPDOWN) && !player->exiting && !P_PlayerInPain(player)) + ////////////////// + //SHIELD ACTIVES// + //& SUPER FLOAT!// + ////////////////// + + if ((player->pflags & PF_JUMPED) && !player->exiting && !P_PlayerInPain(player)) { - if (player->mo->tracer && player->powers[pw_carry] == CR_MACESPIN) - {} - else if (onground || player->climbing || (player->mo->tracer && player->powers[pw_carry])) - {} - else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND - && !(player->pflags & PF_JUMPED) - && !(player->pflags & PF_USEDOWN)) - P_DoJumpShield(player); - else if (!(player->pflags & PF_SLIDING) && ((gametype != GT_CTF) || (!player->gotflag))) + if (onground || player->climbing || player->powers[pw_carry]) + ; + else if (gametype == GT_CTF && player->gotflag) + ; + 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_USEDOWN) + && ((!(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_USE // Spin button effects + #ifdef HAVE_BLUA + && !LUAh_ShieldSpecial(player) + #endif + ) + { + // 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->pflags |= PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + S_StartSound(player->mo, sfx_s3k43); + } + else + { + player->pflags &= ~PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + S_StartSound(player->mo, sfx_s3k44); + } + player->secondjump = 0; + player->mo->momx = player->mo->momy = 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 ((cmd->buttons & BT_USE)) + { + if (!(player->pflags & PF_USEDOWN) && P_SuperReady(player)) + { + // If you can turn super and aren't already, + // and you don't have a shield, do it! + P_DoSuperTransformation(player, false); + } + else #ifdef HAVE_BLUA if (!LUAh_JumpSpinSpecial(player)) #endif switch (player->charability) { - case CA_TELEKINESIS: - if (player->pflags & PF_JUMPED) + case CA_THOK: + if (player->powers[pw_super]) // Super Sonic float { - if (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) + if ((player->speed > 5*player->mo->scale) // FixedMul(5<mo->scale), but scale is FRACUNIT-based + && (P_MobjFlip(player->mo)*player->mo->momz <= 0)) { - P_Telekinesis(player, - -FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player) - FixedMul(384*FRACUNIT, player->mo->scale)); + if (player->panim != PA_RUN && player->panim != PA_WALK) + { + if (player->speed >= FixedMul(player->runspeed, player->mo->scale)) + P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN); + else + P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT); + } + + player->mo->momz = 0; + player->pflags &= ~(PF_STARTJUMP|PF_SPINNING); } } break; - case CA_AIRDRILL: - if (player->pflags & PF_JUMPED) + case CA_TELEKINESIS: + if (!(player->pflags & (PF_THOKKED|PF_USEDOWN)) || (player->charflags & SF_MULTIABILITY)) { - if (player->pflags & PF_THOKKED) // speed up falling down - { - if (player->secondjump < 42) - player->secondjump ++; - } + P_Telekinesis(player, + -FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player) + FixedMul(384*FRACUNIT, player->mo->scale)); + } + break; + case CA_TWINSPIN: + if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_USEDOWN)) || player->charflags & SF_MULTIABILITY)) + { + player->pflags |= PF_THOKKED; + S_StartSound(player->mo, sfx_s3k42); + player->mo->frame = 0; + P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN); } break; default: @@ -4948,6 +5077,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { if (player->pflags & PF_JUMPED) { + if (cmd->buttons & BT_USE && player->secondjump < 42) // speed up falling down + player->secondjump++; + if (player->flyangle > 0 && player->pflags & PF_THOKKED) { player->flyangle--; @@ -4966,6 +5098,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } } + /////////////// + // CHARACTER // + // ABILITIES!// + /////////////// + if (cmd->buttons & BT_JUMP && !player->exiting && !P_PlayerInPain(player)) { #ifdef HAVE_BLUA @@ -5035,7 +5172,6 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale)); - player->drawangle = player->mo->angle; if (maptol & TOL_2D) { @@ -5050,11 +5186,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (player->charability == CA_HOMINGTHOK) { - P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon)); - if (lockon) + P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonthok)); + if (lockonthok) { P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y); + player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonthok->x, lockonthok->y); player->homing = 3*TICRATE; } else @@ -5066,6 +5202,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->pflags &= ~PF_NOJUMPDAMAGE; } + player->drawangle = player->mo->angle; + if (player->mo->info->attacksound && !player->spectator) S_StartSound(player->mo, player->mo->info->attacksound); // Play the THOK sound @@ -5212,6 +5350,60 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) P_DoJumpShield(player); } + // HOMING option. + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT // Sonic 3D Blast. + && player->pflags & PF_SHIELDABILITY) + { + if (player->homing && player->mo->tracer) + { + if (!P_HomingAttack(player->mo, player->mo->tracer)) + { + P_SetObjectMomZ(player->mo, 6*FRACUNIT, false); + if (player->mo->eflags & MFE_UNDERWATER) + player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3); + player->homing = 0; + } + } + + // If you're not jumping, then you obviously wouldn't be homing. + if (!(player->pflags & PF_JUMPED)) + player->homing = 0; + } + else if (player->charability == CA_HOMINGTHOK) // Sonic Adventure. + { + // If you've got a target, chase after it! + if (player->homing && player->mo->tracer) + { + P_SpawnThokMobj(player); + + // But if you don't, then stop homing. + if (!P_HomingAttack(player->mo, player->mo->tracer)) + { + if (player->mo->eflags & MFE_UNDERWATER) + P_SetObjectMomZ(player->mo, FixedDiv(457*FRACUNIT,72*FRACUNIT), false); + else + P_SetObjectMomZ(player->mo, 10*FRACUNIT, false); + + player->mo->momx = player->mo->momy = player->homing = 0; + + if (player->mo->tracer->flags2 & MF2_FRET) + P_InstaThrust(player->mo, player->mo->angle, -(player->speed>>3)); + + if (!(player->mo->tracer->flags & MF_BOSS)) + player->pflags &= ~PF_THOKKED; + + P_SetPlayerMobjState(player->mo, S_PLAY_SPRING); + player->pflags |= PF_NOJUMPDAMAGE; + } + } + + // If you're not jumping, then you obviously wouldn't be homing. + if (!(player->pflags & PF_JUMPED)) + player->homing = 0; + } + else + player->homing = 0; + if (cmd->buttons & BT_JUMP) { player->pflags |= PF_JUMPDOWN; @@ -5238,7 +5430,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->pflags &= ~PF_JUMPDOWN; // Repeat abilities, but not double jump! - if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP) + if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP && player->charability != CA_AIRDRILL) { if (player->charflags & SF_MULTIABILITY) { @@ -8000,7 +8192,7 @@ static void P_MovePlayer(player_t *player) S_StartSound(player->mo, sfx_putput); // Descend - if (cmd->buttons & BT_USE && !(player->pflags & PF_STASIS) && !player->exiting) + if (cmd->buttons & BT_USE && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER)) if (P_MobjFlip(player->mo)*player->mo->momz > -FixedMul(5*actionspd, player->mo->scale)) P_SetObjectMomZ(player->mo, -actionspd/2, true); @@ -8117,178 +8309,6 @@ static void P_MovePlayer(player_t *player) localangle2 = player->mo->angle; } - ////////////////// - //SHIELD ACTIVES// - //& SUPER FLOAT!// - ////////////////// - - if (player->pflags & PF_JUMPED && !player->exiting && player->mo->health) - { - mobj_t *lockon = NULL; - if (!player->powers[pw_super] && player->powers[pw_shield] == SH_ATTRACT && !(player->pflags & PF_THOKKED)) - { - if ((lockon = P_LookForEnemies(player, false, false))) - { - if (P_IsLocalPlayer(player)) // Only display it on your own view. - { - mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockon); - P_SetMobjStateNF(visual, visual->info->spawnstate+1); - } - } - } - if (cmd->buttons & BT_USE) // Spin button effects - { - if (player->powers[pw_super]) // Super can't use shield actives, only passives - { - if ((player->charability == CA_THOK) // Super Sonic float - && (player->speed > 5*player->mo->scale) // FixedMul(5<mo->scale), but scale is FRACUNIT-based - && (P_MobjFlip(player->mo)*player->mo->momz <= 0)) - { - if (player->panim != PA_RUN && player->panim != PA_WALK) - { - if (player->speed >= FixedMul(player->runspeed, player->mo->scale)) - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN); - else - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT); - } - - player->mo->momz = 0; - player->pflags &= ~(PF_STARTJUMP|PF_SPINNING); - } - } - else -#ifdef HAVE_BLUA - if (!LUAh_ShieldSpecial(player)) -#endif - { - if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously - && (!(player->powers[pw_shield] & SH_NOSTACK) || !(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped/turning super - { - // 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) - { - // Super! - case SH_NONE: - if (P_SuperReady(player)) - P_DoSuperTransformation(player, false); - break; - // 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, lockon)); - if (lockon) - { - player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->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: - player->pflags |= PF_THOKKED|PF_SHIELDABILITY; - player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - player->secondjump = 0; - player->mo->momx = player->mo->momy = 0; - P_SetObjectMomZ(player->mo, -24*FRACUNIT, false); - S_StartSound(player->mo, - ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) - ? sfx_s3k43 - : sfx_s3k44); - 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; - } - } - } - } - } - } - - // HOMING option. - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT // Sonic 3D Blast. - && player->pflags & PF_SHIELDABILITY) - { - if (player->homing && player->mo->tracer) - { - if (!P_HomingAttack(player->mo, player->mo->tracer)) - { - P_SetObjectMomZ(player->mo, 6*FRACUNIT, false); - if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3); - player->homing = 0; - } - } - - // If you're not jumping, then you obviously wouldn't be homing. - if (!(player->pflags & PF_JUMPED)) - player->homing = 0; - } - else if (player->charability == CA_HOMINGTHOK) // Sonic Adventure. - { - // If you've got a target, chase after it! - if (player->homing && player->mo->tracer) - { - P_SpawnThokMobj(player); - - // But if you don't, then stop homing. - if (!P_HomingAttack(player->mo, player->mo->tracer)) - { - if (player->mo->eflags & MFE_UNDERWATER) - P_SetObjectMomZ(player->mo, FixedDiv(457*FRACUNIT,72*FRACUNIT), false); - else - P_SetObjectMomZ(player->mo, 10*FRACUNIT, false); - - player->mo->momx = player->mo->momy = player->homing = 0; - - if (player->mo->tracer->flags2 & MF2_FRET) - P_InstaThrust(player->mo, player->mo->angle, -(player->speed>>3)); - - if (!(player->mo->tracer->flags & MF_BOSS)) - player->pflags &= ~PF_THOKKED; - - // P_SetPlayerMobjState(player->mo, S_PLAY_SPRING); -- Speed didn't like it, RIP - } - } - - // If you're not jumping, then you obviously wouldn't be homing. - if (!(player->pflags & PF_JUMPED)) - player->homing = 0; - } - else - player->homing = 0; - if (player->climbing == 1) P_DoClimbing(player); @@ -8332,7 +8352,7 @@ static void P_MovePlayer(player_t *player) // Less height while spinning. Good for spinning under things...? if ((player->mo->state == &states[player->mo->info->painstate]) - || ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE && player->charflags & SF_NOJUMPSPIN)) + || ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE)) || (player->pflags & PF_SPINNING) || player->powers[pw_tailsfly] || player->pflags & PF_GLIDING || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED)) From 2a85ed0bdd2a9505a03d41d04871af51d6de340b Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 6 Sep 2019 19:26:05 -0300 Subject: [PATCH 079/196] test --- src/hardware/hw_main.c | 4 ++-- src/r_plane.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 62e77ae77..0df79c415 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3700,7 +3700,7 @@ static void HWR_Subsector(size_t num) HWR_GetTextureFlat(levelflats[*rover->bottompic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, levelflats[*rover->bottompic].texturenum, - rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap); + rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } @@ -3766,7 +3766,7 @@ static void HWR_Subsector(size_t num) HWR_GetTextureFlat(levelflats[*rover->toppic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, levelflats[*rover->toppic].texturenum, - rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap); + rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } } diff --git a/src/r_plane.c b/src/r_plane.c index 645405b53..57c8079f1 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -1010,8 +1010,6 @@ void R_DrawSinglePlane(visplane_t *pl) if (ds_powersoftwo) { - - } if (hack) { /* From 02a7d8e0a2ec70f71e0a0ac5fd22c4faeece433a Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 6 Sep 2019 19:37:07 -0300 Subject: [PATCH 080/196] Fix tilted spans. --- src/r_draw8.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/r_draw8.c b/src/r_draw8.c index 1c4527a8e..77406f83c 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -726,8 +726,8 @@ void R_DrawTiltedSpan_8(void) if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -780,8 +780,8 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -813,8 +813,8 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -851,8 +851,8 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -929,8 +929,8 @@ void R_DrawTiltedTranslucentSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -983,8 +983,8 @@ void R_DrawTiltedTranslucentSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1016,8 +1016,8 @@ void R_DrawTiltedTranslucentSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1054,8 +1054,8 @@ void R_DrawTiltedTranslucentSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1132,8 +1132,8 @@ void R_DrawTiltedSplat_8(void) if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1190,8 +1190,8 @@ void R_DrawTiltedSplat_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1225,8 +1225,8 @@ void R_DrawTiltedSplat_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1266,8 +1266,8 @@ void R_DrawTiltedSplat_8(void) val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (!ds_powersoftwo) { - fixed_t x = ((u-viewx) >> FRACBITS); - fixed_t y = ((v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) From d38ba4d88ca9d00b254e4728dd997454994c6847 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 6 Sep 2019 19:41:29 -0300 Subject: [PATCH 081/196] define stuff idk --- src/hardware/hw_cache.c | 4 ++++ src/r_data.c | 7 +++++++ src/r_plane.c | 8 +++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 12e43c0e5..2b458c9d9 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -664,8 +664,10 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) { size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump); realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); +#ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); +#endif HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, @@ -892,8 +894,10 @@ static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); size_t lumplength = W_LumpLength(flatlumpnum); +#ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)patch, lumplength)) patch = R_PNGToPatch((UINT8 *)patch, lumplength); +#endif grMipmap->width = (UINT16)SHORT(patch->width); grMipmap->height = (UINT16)SHORT(patch->height); diff --git a/src/r_data.c b/src/r_data.c index d37b39315..8c6b4926a 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -376,8 +376,10 @@ static UINT8 *R_GenerateTexture(size_t texnum) lumpnum = patch->lump; lumplength = W_LumpLengthPwad(wadnum, lumpnum); realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); +#endif // Check the patch for holes. if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) @@ -468,8 +470,10 @@ static UINT8 *R_GenerateTexture(size_t texnum) lumpnum = patch->lump; lumplength = W_LumpLengthPwad(wadnum, lumpnum); realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); +#endif x1 = patch->originx; width = SHORT(realpatch->width); @@ -734,6 +738,8 @@ void R_LoadTextures(void) // 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; @@ -742,6 +748,7 @@ void R_LoadTextures(void) texture->height = height; } else +#endif { texture->width = SHORT(patchlump->width); texture->height = SHORT(patchlump->height); diff --git a/src/r_plane.c b/src/r_plane.c index 57c8079f1..92b3fe770 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -963,12 +963,14 @@ void R_DrawSinglePlane(visplane_t *pl) // Check if the flat is actually a wall texture. if (levelflat->texturenum != 0 && levelflat->texturenum != -1) flat = R_GetPatchFlat(levelflat, true, false); - // Maybe it's just a patch, then? - else if (R_CheckIfPatch(levelflat->lumpnum)) - flat = R_GetPatchFlat(levelflat, false, false); +#ifndef NO_PNG_LUMPS // Maybe it's a PNG?! else if (R_IsLumpPNG(ds_source, size)) flat = R_GetPatchFlat(levelflat, false, true); +#endif + // Maybe it's just a patch, then? + else if (R_CheckIfPatch(levelflat->lumpnum)) + flat = R_GetPatchFlat(levelflat, false, false); // It's a raw flat. else { From bc52bc83ccd2020d3ebb67f6d7a8e525857b4e11 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 6 Sep 2019 20:51:39 -0400 Subject: [PATCH 082/196] 2D toggle command --- src/d_netcmd.c | 1 + src/m_cheat.c | 12 ++++++++++++ src/m_cheat.h | 1 + 3 files changed, 14 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3e82fc60c..590543f00 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -849,6 +849,7 @@ void D_RegisterClientCommands(void) COM_AddCommand("rteleport", Command_RTeleport_f); COM_AddCommand("skynum", Command_Skynum_f); COM_AddCommand("weather", Command_Weather_f); + COM_AddCommand("toggletwod", Command_Toggletwod_f); #ifdef _DEBUG COM_AddCommand("causecfail", Command_CauseCfail_f); #endif diff --git a/src/m_cheat.c b/src/m_cheat.c index 29e8c8a02..da449b2f7 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -746,6 +746,18 @@ void Command_Weather_f(void) P_SwitchWeather(atoi(COM_Argv(1))); } +void Command_Toggletwod_f(void) +{ + player_t *p = &players[consoleplayer]; + + REQUIRE_DEVMODE; + REQUIRE_INLEVEL; + REQUIRE_SINGLEPLAYER; + + if (p->mo) + p->mo->flags2 ^= MF2_TWOD; +} + #ifdef _DEBUG // You never thought you needed this, did you? >=D // Yes, this has the specific purpose of completely screwing you up diff --git a/src/m_cheat.h b/src/m_cheat.h index aa9b0bfeb..f5d59120b 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -64,6 +64,7 @@ void Command_Teleport_f(void); void Command_RTeleport_f(void); void Command_Skynum_f(void); void Command_Weather_f(void); +void Command_Toggletwod_f(void); #ifdef _DEBUG void Command_CauseCfail_f(void); #endif From 99b4439b2ab2715d1ead8f28c5602de466a3f7a2 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 11:33:26 +0100 Subject: [PATCH 083/196] * Allow CA2_GUNSLINGER users to pop monitors with their ability (as long as their weapon type isn't a pre-existing weapon ring). * Correct the position of a carried player relative to Tails. --- src/p_inter.c | 3 ++- src/p_user.c | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index d3cfe1567..13277d425 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3419,7 +3419,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Make sure that boxes cannot be popped by enemies, red rings, etc. - if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) || (inflictor && !inflictor->player))) + if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) + || (inflictor && inflictor->type >= MT_REDRING && inflictor->type <= MT_GRENADERING))) return false; } diff --git a/src/p_user.c b/src/p_user.c index 0550c7f69..79866adec 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8863,7 +8863,7 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius) // Looks for something you can hit - Used for homing attack // If nonenemies is true, includes monitors and springs! // If bullet is true, you can look up and the distance is further, -// but your total angle span you can look is limited to compensate. +// but your total angle span you can look is limited to compensate. (Also, allows monitors.) // mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) { @@ -8873,6 +8873,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) const fixed_t maxdist = FixedMul((bullet ? RING_DIST*2 : RING_DIST), player->mo->scale); const angle_t span = (bullet ? ANG30 : ANGLE_90); fixed_t dist, closestdist = 0; + const mobjflag_t nonenemiesdisregard = (bullet ? 0 : MF_MONITOR)|MF_SPRING; for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) { @@ -8892,7 +8893,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) if (mo->flags2 & MF2_FRET) continue; - if (!nonenemies && mo->flags & (MF_MONITOR|MF_SPRING)) + if (!nonenemies && mo->flags & nonenemiesdisregard) continue; if (!bullet && mo->type == MT_DETON) // Don't be STUPID, Sonic! @@ -11772,7 +11773,7 @@ void P_PlayerAfterThink(player_t *player) { if ((tails->z + tails->height + player->mo->height + FixedMul(FRACUNIT, player->mo->scale)) <= tails->ceilingz && (tails->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame - player->mo->z = tails->z + tails->height + FixedMul(FRACUNIT, player->mo->scale); + player->mo->z = tails->z + tails->height + 12*player->mo->scale; else player->powers[pw_carry] = CR_NONE; } @@ -11780,7 +11781,7 @@ void P_PlayerAfterThink(player_t *player) { if ((tails->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale)) >= tails->floorz && !(tails->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame - player->mo->z = tails->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale); + player->mo->z = tails->z - player->mo->height - 12*player->mo->scale; else player->powers[pw_carry] = CR_NONE; } @@ -11789,7 +11790,7 @@ void P_PlayerAfterThink(player_t *player) player->powers[pw_carry] = CR_NONE; else { - P_TryMove(player->mo, tails->x, tails->y, true); + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); player->mo->momx = tails->momx; player->mo->momy = tails->momy; player->mo->momz = tails->momz; From 54b89eefbb700261a670875fcd77a63fa9e0838f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 11:48:58 +0100 Subject: [PATCH 084/196] * Fix jingle captions dying immediately (caused by an S_StopMusic call in P_PlayJingle; resolved by modifying order of operations). --- src/p_enemy.c | 2 +- src/p_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index e3f169784..0129000fc 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4149,9 +4149,9 @@ void A_Invincibility(mobj_t *actor) { if (mariomode) G_GhostAddColor(GHC_INVINCIBLE); + P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV); strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14); S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]); - P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV); } } diff --git a/src/p_user.c b/src/p_user.c index a69bd5b93..58ba00fc9 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1431,11 +1431,11 @@ void P_PlayLivesJingle(player_t *player) S_StartSound(NULL, sfx_marioa); else { + P_PlayJingle(player, JT_1UP); if (player) player->powers[pw_extralife] = extralifetics + 1; strlcpy(S_sfx[sfx_None].caption, "One-up", 7); S_StartCaption(sfx_None, -1, extralifetics+1); - P_PlayJingle(player, JT_1UP); } } From cf4b5a1b9d34ef72a83a71934ba6998800088053 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 11:49:13 +0100 Subject: [PATCH 085/196] * Comment out the notices that the two types of music are disabled (see issue #179; not a perfect fix, but good enough for now). --- src/s_sound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 1c6cd5ef1..3068db1df 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1783,12 +1783,12 @@ static lumpnum_t S_GetMusicLumpNum(const char *mname) return W_GetNumForName(va("d_%s", mname)); else if (S_DigMusicDisabled() && S_DigExists(mname)) { - CONS_Alert(CONS_NOTICE, "Digital music is disabled!\n"); + //CONS_Alert(CONS_NOTICE, "Digital music is disabled!\n"); return LUMPERROR; } else if (S_MIDIMusicDisabled() && S_MIDIExists(mname)) { - CONS_Alert(CONS_NOTICE, "MIDI music is disabled!\n"); + //CONS_Alert(CONS_NOTICE, "MIDI music is disabled!\n"); return LUMPERROR; } else From 5640aa955ab079aed717a56290e7488604da5a84 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 12:07:29 +0100 Subject: [PATCH 086/196] Fix the V_DrawCroppedPatch bleeding on the current charsel screen. --- src/v_video.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index 2ec06a787..edea7e051 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1045,9 +1045,15 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ prevdelta = topdelta; source = (const UINT8 *)(column) + 3; dest = desttop; - dest += FixedInt(FixedMul(topdelta< 0) + { + dest += FixedInt(FixedMul((topdelta-sy)<>FRACBITS) < column->length && (((ofs>>FRACBITS) - sy) + topdelta) < h; ofs += rowfrac) + for (; dest < deststop && (ofs>>FRACBITS) < column->length && (((ofs>>FRACBITS) - sy) + topdelta) < h; ofs += rowfrac) { if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION) *dest = patchdrawfunc(dest, source, ofs); From 92b77c987b0e600e19eda40feee08e44688372e9 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 12:10:50 +0100 Subject: [PATCH 087/196] Fix compilation warning for Match penalty string. (Egads, straight into master! Shock! Horror!) --- src/st_stuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 142cd3e61..a90661ef3 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1960,7 +1960,7 @@ static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, I static void ST_drawMatchHUD(void) { - char penaltystr[5]; + char penaltystr[7]; const INT32 y = 176; // HUD_LIVES INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6; From 5d85e82fa66ea58c939b7c048d05174819d03c97 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 20:08:05 +0100 Subject: [PATCH 088/196] Fix detection of GRADE_ constants in SOC. --- src/dehacked.c | 61 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 5db61a5b5..186d36fb2 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2957,6 +2957,17 @@ static void readunlockable(MYFILE *f, INT32 num) Z_Free(s); } +static const char NIGHTSGRADE_LIST[] = { + 'F', // GRADE_F + 'E', // GRADE_E + 'D', // GRADE_D + 'C', // GRADE_C + 'B', // GRADE_B + 'A', // GRADE_A + 'S', // GRADE_S + '\0' +}; + #define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0) static void readcondition(UINT8 set, UINT32 id, char *word2) { @@ -3058,7 +3069,21 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) PARAMCHECK(2); // one optional one ty = UC_NIGHTSSCORE + offset; - re = atoi(params[2 + !!(params[3])]); + i = (params[3] ? 3 : 2); + if (fastncmp("GRADE_",params[i],6)) + { + char *p = params[i]+6; + for (re = 0; NIGHTSGRADE_LIST[re]; re++) + if (*p == NIGHTSGRADE_LIST[re]) + break; + if (!NIGHTSGRADE_LIST[re]) + { + deh_warning("Invalid NiGHTS grade %s\n", params[i]); + return; + } + } + else + re = atoi(params[i]); // Convert to map number if it appears to be one if (params[1][0] >= 'A' && params[1][0] <= 'Z') @@ -8474,15 +8499,6 @@ struct { {"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED}, {"LF2_WIDEICON",LF2_WIDEICON}, - // NiGHTS grades - {"GRADE_F",GRADE_F}, - {"GRADE_E",GRADE_E}, - {"GRADE_D",GRADE_D}, - {"GRADE_C",GRADE_C}, - {"GRADE_B",GRADE_B}, - {"GRADE_A",GRADE_A}, - {"GRADE_S",GRADE_S}, - // Emeralds {"EMERALD1",EMERALD1}, {"EMERALD2",EMERALD2}, @@ -9304,6 +9320,19 @@ static fixed_t find_const(const char **rword) free(word); return 0; } + else if (fastncmp("GRADE_",word,6)) + { + char *p = word+6; + for (i = 0; NIGHTSGRADE_LIST[i]; i++) + if (*p == NIGHTSGRADE_LIST[i]) + { + free(word); + return i; + } + const_warning("NiGHTS grade",word); + free(word); + return 0; + } for (i = 0; INT_CONST[i].n; i++) if (fastcmp(word,INT_CONST[i].n)) { free(word); @@ -9752,6 +9781,18 @@ static inline int lib_getenum(lua_State *L) if (mathlib) return luaL_error(L, "skincolor '%s' could not be found.\n", word); return 0; } + else if (fastncmp("GRADE_",word,6)) + { + p = word+6; + for (i = 0; NIGHTSGRADE_LIST[i]; i++) + if (*p == NIGHTSGRADE_LIST[i]) + { + lua_pushinteger(L, i); + return 1; + } + if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word); + return 0; + } else if (fastncmp("MN_",word,3)) { p = word+3; for (i = 0; i < NUMMENUTYPES; i++) From f461b76bb0ed9fc96f0e4cb17fd5b990ba5694f4 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 7 Sep 2019 16:54:26 -0300 Subject: [PATCH 089/196] fix translucency --- src/doomdef.h | 4 ++- src/hardware/hw_cache.c | 14 +++++++++-- src/r_data.c | 54 ++++++++--------------------------------- src/r_plane.c | 2 -- 4 files changed, 25 insertions(+), 49 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 952bea318..4a0174369 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -616,6 +616,8 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// SRB2CB itself ported this from PrBoom+ #define NEWCLIP -//#define NO_PNG_LUMPS +#ifndef HAVE_PNG +#define NO_PNG_LUMPS +#endif #endif // __DOOMDEF__ diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 2b458c9d9..c9a75a4f3 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -892,8 +892,10 @@ lumpnum_t gr_patchflat; static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { + UINT8 *flat; patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); size_t lumplength = W_LumpLength(flatlumpnum); + #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)patch, lumplength)) patch = R_PNGToPatch((UINT8 *)patch, lumplength); @@ -902,7 +904,10 @@ static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) grMipmap->width = (UINT16)SHORT(patch->width); grMipmap->height = (UINT16)SHORT(patch->height); - R_PatchToFlat(patch, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); + flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); + memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); + + R_PatchToFlat(patch, flat); } static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) @@ -980,6 +985,8 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) { + UINT8 *flat; + // setup the texture info #ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; @@ -992,7 +999,10 @@ static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; - R_TextureToFlat(texturenum, Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data)); + flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); + memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); + + R_TextureToFlat(texturenum, flat); } void HWR_GetTextureFlat(INT32 texturenum) diff --git a/src/r_data.c b/src/r_data.c index 8c6b4926a..fb30005bf 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -376,9 +376,13 @@ static UINT8 *R_GenerateTexture(size_t texnum) lumpnum = patch->lump; lumplength = W_LumpLengthPwad(wadnum, lumpnum); realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + goto multipatch; + } #endif // Check the patch for holes. @@ -436,6 +440,9 @@ static UINT8 *R_GenerateTexture(size_t texnum) } // multi-patch textures (or 'composite') +#ifndef NO_PNG_LUMPS + multipatch: +#endif texture->holes = false; texture->flip = 0; blocksize = (texture->width * 4) + (texture->width * texture->height); @@ -2441,7 +2448,6 @@ boolean R_CheckIfPatch(lumpnum_t lump) { result = false; break; - } } } @@ -2475,8 +2481,7 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat) source = (UINT8 *)(column) + 3; for (ofs = 0; dest < deststop && ofs < column->length; ofs++) { - if (source[ofs] != TRANSPARENTPIXEL) - *dest = source[ofs]; + *dest = source[ofs]; dest += SHORT(patch->width); } column = (column_t *)((UINT8 *)column + column->length + 4); @@ -2637,7 +2642,8 @@ static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) for (x = 0; x < width; x++) { png_bytep px = &(row[x * 4]); - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); + if ((UINT8)px[3]) + flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); } } free(row_pointers); @@ -2645,34 +2651,6 @@ static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) return flat; } -// Get the alpha mask of the image. -static UINT8 *PNG_GetAlphaMask(UINT8 *png, size_t size) -{ - UINT8 *mask; - png_uint_32 x, y; - UINT16 width, height; - png_bytep *row_pointers = PNG_Read(png, &width, &height, size); - - if (!row_pointers) - return NULL; - - // Convert the image to 8bpp - mask = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(mask, 0, width * height); - for (y = 0; y < height; y++) - { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) - { - png_bytep px = &(row[x * 4]); - mask[((y * width) + x)] = (UINT8)px[3]; - } - } - free(row_pointers); - - return mask; -} - // Convert a PNG to a flat. UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) { @@ -2680,13 +2658,11 @@ UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) } // Convert a PNG to a patch. -// This is adapted from the "kartmaker" utility static unsigned char imgbuf[1<<26]; patch_t *R_PNGToPatch(UINT8 *png, size_t size) { UINT16 width, height; UINT8 *raw = PNG_RawConvert(png, &width, &height, size); - UINT8 *alphamask = PNG_GetAlphaMask(png, size); UINT32 x, y; UINT8 *img; @@ -2726,16 +2702,6 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) for (y = 0; y < height; y++) { UINT8 paletteIndex = raw[((y * width) + x)]; - UINT8 opaque = alphamask[((y * width) + x)]; // If 1, we have a pixel - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITE8(imgptr, 0); - startofspan = NULL; - continue; - } // Start new column if we need to if (!startofspan || spanSize == 255) diff --git a/src/r_plane.c b/src/r_plane.c index 92b3fe770..c28405726 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -754,7 +754,6 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole { patch = (patch_t *)ds_source; #ifndef NO_PNG_LUMPS -#ifdef HAVE_PNG if (ispng) { levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->lumpnum)); @@ -773,7 +772,6 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole } } else -#endif #endif { levelflat->width = ds_flatwidth = SHORT(patch->width); From d26ff197dc0bc204b3cd925778fd630ea16c5173 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 7 Sep 2019 22:11:33 +0100 Subject: [PATCH 090/196] * Store gravflip (resolves #206) and destscale in starposts. (Using the same field, taking advantage of the fact that object scale will always be positive!) * Update the function signature of P_MixUp to accomodate both it and drawangle instead of doing it outside of the function. * If the player is spawning from the start of the stage and it's from the ceiling, be in fall frames as requested (resolves #191). --- src/d_clisrv.c | 2 ++ src/d_clisrv.h | 1 + src/d_player.h | 1 + src/g_game.c | 8 +++++++- src/lua_playerlib.c | 4 ++++ src/m_cheat.c | 3 +++ src/p_enemy.c | 16 +++++++--------- src/p_inter.c | 12 ++++++++++++ src/p_local.h | 2 +- src/p_mobj.c | 33 +++++++++++++++++++++++---------- src/p_saveg.c | 2 ++ src/p_spec.c | 2 +- src/p_telept.c | 5 ++++- 13 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 01e94485d..78e07a397 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -579,6 +579,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->starpostnum = LONG(players[i].starpostnum); rsp->starposttime = (tic_t)LONG(players[i].starposttime); rsp->starpostangle = (angle_t)LONG(players[i].starpostangle); + rsp->starpostscale = (fixed_t)LONG(players[i].starpostscale); rsp->maxlink = LONG(players[i].maxlink); rsp->dashspeed = (fixed_t)LONG(players[i].dashspeed); @@ -714,6 +715,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].starpostnum = LONG(rsp->starpostnum); players[i].starposttime = (tic_t)LONG(rsp->starposttime); players[i].starpostangle = (angle_t)LONG(rsp->starpostangle); + players[i].starpostscale = (fixed_t)LONG(rsp->starpostscale); players[i].maxlink = LONG(rsp->maxlink); players[i].dashspeed = (fixed_t)LONG(rsp->dashspeed); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a2f140f33..3bfabfc03 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -228,6 +228,7 @@ typedef struct INT32 starpostnum; tic_t starposttime; angle_t starpostangle; + fixed_t starpostscale; INT32 maxlink; fixed_t dashspeed; diff --git a/src/d_player.h b/src/d_player.h index 5860cf1de..69080bd9d 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -441,6 +441,7 @@ typedef struct player_s INT32 starpostnum; // The number of the last starpost you hit tic_t starposttime; // Your time when you hit the starpost angle_t starpostangle; // Angle that the starpost is facing - you respawn facing this way + fixed_t starpostscale; // Scale of the player; if negative, player is gravflipped ///////////////// // NiGHTS Stuff// diff --git a/src/g_game.c b/src/g_game.c index d5faf6846..89a96f3d0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2083,6 +2083,7 @@ static inline void G_PlayerFinishLevel(INT32 player) p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility P_FlashPal(p, 0, 0); // Resets + p->starpostscale = 0; p->starpostangle = 0; p->starposttime = 0; p->starpostx = 0; @@ -2129,6 +2130,7 @@ void G_PlayerReborn(INT32 player) INT16 starpostz; INT32 starpostnum; INT32 starpostangle; + fixed_t starpostscale; fixed_t jumpfactor; fixed_t height; fixed_t spinheight; @@ -2184,6 +2186,7 @@ void G_PlayerReborn(INT32 player) starpostz = players[player].starpostz; starpostnum = players[player].starpostnum; starpostangle = players[player].starpostangle; + starpostscale = players[player].starpostscale; jumpfactor = players[player].jumpfactor; height = players[player].height; spinheight = players[player].spinheight; @@ -2239,6 +2242,7 @@ void G_PlayerReborn(INT32 player) p->starpostz = starpostz; p->starpostnum = starpostnum; p->starpostangle = starpostangle; + p->starpostscale = starpostscale; p->jumpfactor = jumpfactor; p->height = height; p->spinheight = spinheight; @@ -2657,6 +2661,7 @@ void G_DoReborn(INT32 playernum) { if (!playeringame[i]) continue; + players[i].starpostscale = 0; players[i].starpostangle = 0; players[i].starposttime = 0; players[i].starpostx = 0; @@ -2779,6 +2784,7 @@ void G_AddPlayer(INT32 playernum) if (!(cv_coopstarposts.value && (gametype == GT_COOP) && (p->starpostnum < players[i].starpostnum))) continue; + p->starpostscale = players[i].starpostscale; p->starposttime = players[i].starposttime; p->starpostx = players[i].starpostx; p->starposty = players[i].starposty; @@ -3866,7 +3872,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean for (i = 0; i < MAXPLAYERS; i++) { players[i].playerstate = PST_REBORN; - players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0; + players[i].starpostscale = players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0; players[i].starpostx = players[i].starposty = players[i].starpostz = 0; if (netgame || multiplayer) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index addd707e1..dd9959afb 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -262,6 +262,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->starposttime); else if (fastcmp(field,"starpostangle")) lua_pushangle(L, plr->starpostangle); + else if (fastcmp(field,"starpostscale")) + lua_pushfixed(L, plr->starpostscale); else if (fastcmp(field,"angle_pos")) lua_pushangle(L, plr->angle_pos); else if (fastcmp(field,"old_angle_pos")) @@ -570,6 +572,8 @@ static int player_set(lua_State *L) plr->starposttime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"starpostangle")) plr->starpostangle = luaL_checkangle(L, 3); + else if (fastcmp(field,"starpostscale")) + plr->starpostscale = luaL_checkfixed(L, 3); else if (fastcmp(field,"angle_pos")) plr->angle_pos = luaL_checkangle(L, 3); else if (fastcmp(field,"old_angle_pos")) diff --git a/src/m_cheat.c b/src/m_cheat.c index da449b2f7..ae2703ee2 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -831,6 +831,9 @@ void Command_Savecheckpoint_f(void) players[consoleplayer].starposty = players[consoleplayer].mo->y>>FRACBITS; players[consoleplayer].starpostz = players[consoleplayer].mo->floorz>>FRACBITS; players[consoleplayer].starpostangle = players[consoleplayer].mo->angle; + players[consoleplayer].starpostscale = players[consoleplayer].mo->destscale; + if (players[consoleplayer].mo->flags2 & MF2_OBJECTFLIP) + players[consoleplayer].starpostscale *= -1; CONS_Printf(M_GetText("Temporary checkpoint created at %d, %d, %d\n"), players[consoleplayer].starpostx, players[consoleplayer].starposty, players[consoleplayer].starpostz); } diff --git a/src/p_enemy.c b/src/p_enemy.c index b60e5e68b..6e49d165d 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -6358,6 +6358,7 @@ void A_MixUp(mobj_t *actor) INT32 starpostnum; tic_t starposttime; angle_t starpostangle; + fixed_t starpostscale; INT32 mflags2; @@ -6405,6 +6406,7 @@ void A_MixUp(mobj_t *actor) starposty = players[one].starposty; starpostz = players[one].starpostz; starpostangle = players[one].starpostangle; + starpostscale = players[one].starpostscale; starpostnum = players[one].starpostnum; starposttime = players[one].starposttime; @@ -6413,15 +6415,11 @@ void A_MixUp(mobj_t *actor) P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle, players[two].starpostx, players[two].starposty, players[two].starpostz, players[two].starpostnum, players[two].starposttime, players[two].starpostangle, - players[two].mo->flags2); - - players[one].drawangle = players[two].drawangle; + players[two].starpostscale, players[two].drawangle, players[two].mo->flags2); P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz, starpostnum, starposttime, starpostangle, - mflags2); - - players[two].drawangle = drawangle; + starpostscale, drawangle, mflags2); //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for... //but not all of it! So we need to make sure they aren't set wrong or anything. @@ -6448,6 +6446,7 @@ void A_MixUp(mobj_t *actor) INT32 starpostnum[MAXPLAYERS]; tic_t starposttime[MAXPLAYERS]; angle_t starpostangle[MAXPLAYERS]; + fixed_t starpostscale[MAXPLAYERS]; INT32 flags2[MAXPLAYERS]; @@ -6485,6 +6484,7 @@ void A_MixUp(mobj_t *actor) starpostnum[counter] = players[i].starpostnum; starposttime[counter] = players[i].starposttime; starpostangle[counter] = players[i].starpostangle; + starpostscale[counter] = players[i].starpostscale; flags2[counter] = players[i].mo->flags2; @@ -6525,9 +6525,7 @@ void A_MixUp(mobj_t *actor) P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom][0], spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2], starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom], - flags2[teleportfrom]); - - players[i].drawangle = anglepos[teleportfrom][1]; + starpostscale[teleportfrom], anglepos[teleportfrom][1], flags2[teleportfrom]); //...carry after. same reasoning. players[i].powers[pw_carry] = transcarry[teleportfrom]; diff --git a/src/p_inter.c b/src/p_inter.c index 0030e8e58..53481d6c5 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1427,6 +1427,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) players[i].starposty = player->mo->y>>FRACBITS; players[i].starpostz = special->z>>FRACBITS; players[i].starpostangle = special->angle; + players[i].starpostscale = player->mo->destscale; + if (special->flags2 & MF2_OBJECTFLIP) + { + players[i].starpostscale *= -1; + players[i].starpostz += (special->height - P_GetPlayerHeight(player))>>FRACBITS; + } players[i].starpostnum = special->health; if (cv_coopstarposts.value == 2 && (players[i].playerstate == PST_DEAD || players[i].spectator) && P_GetLives(&players[i])) @@ -1443,6 +1449,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->starposty = toucher->y>>FRACBITS; player->starpostz = special->z>>FRACBITS; player->starpostangle = special->angle; + player->starpostscale = player->mo->destscale; + if (special->flags2 & MF2_OBJECTFLIP) + { + player->starpostscale *= -1; + player->starpostz += (special->height - P_GetPlayerHeight(player))>>FRACBITS; + } player->starpostnum = special->health; S_StartSound(toucher, special->info->painsound); } diff --git a/src/p_local.h b/src/p_local.h index 3a0146e54..662eb691a 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -509,7 +509,7 @@ extern INT32 ceilmovesound; void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, INT16 starpostx, INT16 starposty, INT16 starpostz, INT32 starpostnum, tic_t starposttime, angle_t starpostangle, - INT32 flags2); + fixed_t starpostscale, angle_t drawangle, INT32 flags2); boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, boolean flash, boolean dontstopmove); boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state); boolean P_CheckMissileSpawn(mobj_t *th); diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ee90d250..130808712 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10521,10 +10521,6 @@ void P_AfterPlayerSpawn(INT32 playernum) else p->viewz = p->mo->z + p->viewheight; - if (p->powers[pw_carry] != CR_NIGHTSMODE) - P_SetPlayerMobjState(p->mo, S_PLAY_STND); - p->pflags &= ~PF_SPINNING; - if (playernum == consoleplayer) { // wake up the status bar @@ -10609,6 +10605,8 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing) mobj->eflags |= MFE_VERTICALFLIP; mobj->flags2 |= MF2_OBJECTFLIP; } + if (mthing->options & MTF_AMBUSH) + P_SetPlayerMobjState(mobj, S_PLAY_FALL); } else z = floor; @@ -10627,7 +10625,12 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing) P_SetThingPosition(mobj); mobj->z = z; - if (mobj->z == mobj->floorz) + if (mobj->flags2 & MF2_OBJECTFLIP) + { + if (mobj->z + mobj->height == mobj->ceilingz) + mobj->eflags |= MFE_ONGROUND; + } + else if (mobj->z == mobj->floorz) mobj->eflags |= MFE_ONGROUND; mobj->angle = angle; @@ -10663,16 +10666,26 @@ void P_MovePlayerToStarpost(INT32 playernum) sector->ceilingheight; z = p->starpostz << FRACBITS; - if (z < floor) - z = floor; - else if (z > ceiling - mobjinfo[MT_PLAYER].height) - z = ceiling - mobjinfo[MT_PLAYER].height; + + P_SetScale(mobj, (mobj->destscale = abs(p->starpostscale))); mobj->floorz = floor; mobj->ceilingz = ceiling; + if (z <= floor) + z = floor; + else if (z >= ceiling - mobj->height) + z = ceiling - mobj->height; + mobj->z = z; - if (mobj->z == mobj->floorz) + + if (p->starpostscale < 0) + { + mobj->flags2 |= MF2_OBJECTFLIP; + if (mobj->z + mobj->height == mobj->ceilingz) + mobj->eflags |= MFE_ONGROUND; + } + else if (mobj->z == mobj->floorz) mobj->eflags |= MFE_ONGROUND; mobj->angle = p->starpostangle; diff --git a/src/p_saveg.c b/src/p_saveg.c index 7c073b151..12f14e99d 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -189,6 +189,7 @@ static void P_NetArchivePlayers(void) WRITEINT16(save_p, players[i].starpostz); WRITEINT32(save_p, players[i].starpostnum); WRITEANGLE(save_p, players[i].starpostangle); + WRITEFIXED(save_p, players[i].starpostscale); WRITEANGLE(save_p, players[i].angle_pos); WRITEANGLE(save_p, players[i].old_angle_pos); @@ -397,6 +398,7 @@ static void P_NetUnArchivePlayers(void) players[i].starpostz = READINT16(save_p); players[i].starpostnum = READINT32(save_p); players[i].starpostangle = READANGLE(save_p); + players[i].starpostscale = READFIXED(save_p); players[i].angle_pos = READANGLE(save_p); players[i].old_angle_pos = READANGLE(save_p); diff --git a/src/p_spec.c b/src/p_spec.c index 37a1652f0..fbf896a2b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4942,7 +4942,7 @@ DoneSection2: CONS_Printf(M_GetText("%s started lap %u\n"), player_names[player-players], (UINT32)player->laps+1); // Reset starposts (checkpoints) info - player->starpostangle = player->starposttime = player->starpostnum = 0; + player->starpostscale = player->starpostangle = player->starposttime = player->starpostnum = 0; player->starpostx = player->starposty = player->starpostz = 0; P_ResetStarposts(); diff --git a/src/p_telept.c b/src/p_telept.c index e80dd0428..632b81e04 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -33,7 +33,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, INT16 starpostx, INT16 starposty, INT16 starpostz, INT32 starpostnum, tic_t starposttime, angle_t starpostangle, - INT32 flags2) + fixed_t starpostscale, angle_t drawangle, INT32 flags2) { const INT32 takeflags2 = MF2_TWOD|MF2_OBJECTFLIP; @@ -89,8 +89,11 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, thing->player->starpostz = starpostz; thing->player->starposttime = starposttime; thing->player->starpostangle = starpostangle; + thing->player->starpostscale = starpostscale; thing->player->starpostnum = starpostnum; + thing->player->drawangle = drawangle; + // Reset map starposts for the player's new info. P_ResetStarposts(); P_ClearStarPost(starpostnum); From ff293c3f6fa9e193480c769184e1591ab8c86f80 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 7 Sep 2019 18:20:49 -0300 Subject: [PATCH 091/196] fix non powers of two spans --- src/r_plane.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/r_plane.c b/src/r_plane.c index c28405726..a5b167015 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -1010,6 +1010,8 @@ void R_DrawSinglePlane(visplane_t *pl) if (ds_powersoftwo) { + // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red + fudge = ((1< Date: Sat, 7 Sep 2019 18:56:08 -0300 Subject: [PATCH 092/196] probably want to Z_Free this............... --- src/r_data.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index fb30005bf..5858117a5 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2511,7 +2511,7 @@ static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) { png_ioread *f = png_get_io_ptr(png_ptr); if (length > (f->bufsize - f->current_pos)) - png_error(png_ptr, "read error in read_data_memory (loadpng)"); + png_error(png_ptr, "PNG_IOReader: buffer overrun"); memcpy(data, f->buffer + f->current_pos, length); f->current_pos += length; } @@ -2573,11 +2573,10 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // png_source is array which have png data + // set our own read_function png_io.buffer = (png_bytep)png; png_io.bufsize = size; png_io.current_pos = 0; - // set our own read_function png_set_read_fn(png_ptr, &png_io, PNG_IOReader); #ifdef PNG_SET_USER_LIMITS_SUPPORTED @@ -2631,7 +2630,7 @@ static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) png_uint_32 width = *w, height = *h; if (!row_pointers) - return NULL; + I_Error("PNG_RawConvert: conversion failed"); // Convert the image to 8bpp flat = Z_Malloc(width * height, PU_LEVEL, NULL); @@ -2674,7 +2673,7 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) #define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);}) if (!raw) - return NULL; + I_Error("R_PNGToPatch: conversion failed"); // Write image size and offset WRITE16(imgptr, width); @@ -2762,6 +2761,9 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) size = imgptr-imgbuf; img = malloc(size); memcpy(img, imgbuf, size); + + Z_Free(raw); + return (patch_t *)img; } @@ -2809,11 +2811,10 @@ boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // png_source is array which have png data + // set our own read_function png_io.buffer = (png_bytep)png; png_io.bufsize = size; png_io.current_pos = 0; - // set our own read_function png_set_read_fn(png_ptr, &png_io, PNG_IOReader); #ifdef PNG_SET_USER_LIMITS_SUPPORTED From a67dd633ffd928b5af1f15899c1b474b58a7aa55 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 7 Sep 2019 19:02:50 -0300 Subject: [PATCH 093/196] if that function can't return NULL why should i do this? --- src/r_plane.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index a5b167015..de5bf9f00 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -758,18 +758,8 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole { levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->lumpnum)); levelflat->topoffset = levelflat->leftoffset = 0; - if (levelflat->flatpatch == NULL) - { - lumpnum_t redflr = W_CheckNumForName("REDFLR"); - levelflat->flatpatch = (UINT8 *)W_CacheLumpNum(redflr, PU_CACHE); - R_CheckFlatLength(W_LumpLength(redflr)); - R_CheckPowersOfTwo(); - } - else - { - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; } else #endif From 4adff344024825ccf0ed25419aca0d46030e5381 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 7 Sep 2019 22:43:29 -0300 Subject: [PATCH 094/196] opengl patch translucency --- src/hardware/hw_cache.c | 87 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 6bc2c712e..fb25dc58e 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -60,6 +60,47 @@ static const INT32 format2bpp[16] = 2, //14 GR_TEXFMT_AP_88 }; +static RGBA_t astblendpixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) +{ + RGBA_t output; + output.s.alpha = 0xFF; + + if (style == AST_TRANSLUCENT) + { + if (alpha == 0) + output.rgba = background.rgba; + else if (alpha == 0xFF) + output.rgba = foreground.rgba; + else if (alpha < 0xFF) + { + UINT8 beta = (0xFF - alpha); + output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF; + output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF; + output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF; + } + // write foreground pixel alpha + // if there's no pixel in here + if (!background.rgba) + output.s.alpha = foreground.s.alpha; + } + + return output; +} + +static UINT8 astblendpixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha) +{ + if ((style == AST_TRANSLUCENT) && (alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? + { + UINT8 *mytransmap; + if (alpha < 255/11) // Is the patch way too translucent? Don't render then. + return background; + // The equation's not exact but it works as intended. I'll call it a day for now. + mytransmap = transtables + ((8*(alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); + if (background != 0xFF) + return *(mytransmap + (background<<8) + foreground); + } + return background; +} // This code was originally placed directly in HWR_DrawPatchInCache. // It is now split from it for my sanity! (and the sanity of others) @@ -138,18 +179,37 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm // Alam: SRB2 uses Mingw, HUGS switch (bpp) { - case 2 : texelu16 = (UINT16)((alpha<<8) | texel); + case 2 : // uhhhhhhhh.......... + if ((originPatch != NULL) && originPatch->style) + texel = astblendpixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); + texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); + if ((originPatch != NULL) && originPatch->style) + { + RGBA_t rgbatexel; + rgbatexel.rgba = *(UINT32 *)dest; + colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; + if ((originPatch != NULL) && originPatch->style) + { + RGBA_t rgbatexel; + rgbatexel.rgba = *(UINT32 *)dest; + colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 - default: *dest = texel; + default: + if ((originPatch != NULL) && originPatch->style) + *dest = astblendpixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); + else + *dest = texel; break; } @@ -233,18 +293,37 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, // Alam: SRB2 uses Mingw, HUGS switch (bpp) { - case 2 : texelu16 = (UINT16)((alpha<<8) | texel); + case 2 : // uhhhhhhhh.......... + if ((originPatch != NULL) && originPatch->style) + texel = astblendpixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); + texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); + if ((originPatch != NULL) && originPatch->style) + { + RGBA_t rgbatexel; + rgbatexel.rgba = *(UINT32 *)dest; + colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; + if ((originPatch != NULL) && originPatch->style) + { + RGBA_t rgbatexel; + rgbatexel.rgba = *(UINT32 *)dest; + colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 - default: *dest = texel; + default: + if ((originPatch != NULL) && originPatch->style) + *dest = astblendpixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); + else + *dest = texel; break; } From b7c9f15225413018b526802eecedff95e3753fbf Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sat, 7 Sep 2019 22:00:38 -0400 Subject: [PATCH 095/196] Fix NiGHTS attack menu not drawing correct background --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 128b15a76..d9a6bfea6 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8479,8 +8479,8 @@ static void M_NightsAttack(INT32 choice) M_PatchSkinNameTable(); G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching - M_SetupNextMenu(&SP_NightsAttackDef); titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please + M_SetupNextMenu(&SP_NightsAttackDef); if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0]) CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]); else From 8ee0a9309b289a1a3d1a7260a44a43c776180686 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Sep 2019 01:29:09 -0400 Subject: [PATCH 096/196] Better directory structure --- src/d_main.c | 6 +++++- src/d_netcmd.c | 2 ++ src/m_menu.c | 51 ++++++++++++++++++++++++++++-------------------- src/m_menu.h | 3 +++ src/m_misc.c | 44 ++++++++++++++++++++++++++++------------- src/m_misc.h | 2 +- src/sdl/i_main.c | 36 ++++++++++++++++++++++++++++------ 7 files changed, 101 insertions(+), 43 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index eaeae4b10..c838cd2e3 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -129,6 +129,7 @@ char srb2home[256] = "."; char srb2path[256] = "."; boolean usehome = true; const char *pandf = "%s" PATHSEP "%s"; +static char addonsdir[MAX_WADPATH]; // // EVENT HANDLING @@ -1038,7 +1039,6 @@ void D_SRB2Main(void) // can't use sprintf since there is %u in savegamename strcatbf(savegamename, srb2home, PATHSEP); - I_mkdir(srb2home, 0700); #else snprintf(srb2home, sizeof srb2home, "%s", userhome); snprintf(downloaddir, sizeof downloaddir, "%s", userhome); @@ -1055,6 +1055,10 @@ void D_SRB2Main(void) configfile[sizeof configfile - 1] = '\0'; } + // Create addons dir + snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons"); + I_mkdir(addonsdir, 0755); + // rand() needs seeded regardless of password srand((unsigned int)time(NULL)); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 590543f00..42c76389d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -630,6 +630,8 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_screenshot_folder); CV_RegisterVar(&cv_screenshot_colorprofile); CV_RegisterVar(&cv_moviemode); + CV_RegisterVar(&cv_movie_option); + CV_RegisterVar(&cv_movie_folder); // PNG variables CV_RegisterVar(&cv_zlib_level); CV_RegisterVar(&cv_zlib_memory); diff --git a/src/m_menu.c b/src/m_menu.c index 128b15a76..93f5aa83f 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1353,36 +1353,39 @@ static menuitem_t OP_ScreenshotOptionsMenu[] = { {IT_HEADER, NULL, "General", NULL, 0}, {IT_STRING|IT_CVAR, NULL, "Use color profile", &cv_screenshot_colorprofile, 6}, - {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_screenshot_option, 11}, - {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_screenshot_folder, 16}, - {IT_HEADER, NULL, "Screenshots (F8)", NULL, 30}, - {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memory, 36}, - {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_level, 41}, - {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategy, 46}, - {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bits, 51}, + {IT_HEADER, NULL, "Screenshots (F8)", NULL, 16}, + {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_screenshot_option, 22}, + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_screenshot_folder, 27}, + {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memory, 42}, + {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_level, 47}, + {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategy, 52}, + {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bits, 57}, - {IT_HEADER, NULL, "Movie Mode (F9)", NULL, 60}, - {IT_STRING|IT_CVAR, NULL, "Capture Mode", &cv_moviemode, 66}, + {IT_HEADER, NULL, "Movie Mode (F9)", NULL, 64}, + {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_movie_option, 70}, + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_movie_folder, 75}, + {IT_STRING|IT_CVAR, NULL, "Capture Mode", &cv_moviemode, 90}, - {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 71}, - {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 76}, + {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 95}, + {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 100}, - {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 71}, - {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 76}, - {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategya, 81}, - {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bitsa, 86}, + {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 95}, + {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 100}, + {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategya, 105}, + {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bitsa, 110}, }; enum { op_screenshot_colorprofile = 1, - op_screenshot_folder = 3, - op_screenshot_capture = 10, - op_screenshot_gif_start = 11, - op_screenshot_gif_end = 12, - op_screenshot_apng_start = 13, - op_screenshot_apng_end = 16, + op_screenshot_folder = 4, + op_movie_folder = 11, + op_screenshot_capture = 12, + op_screenshot_gif_start = 13, + op_screenshot_gif_end = 14, + op_screenshot_apng_start = 15, + op_screenshot_apng_end = 18, }; static menuitem_t OP_EraseDataMenu[] = @@ -2200,6 +2203,12 @@ void Addons_option_Onchange(void) (cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); } +void Moviemode_option_Onchange(void) +{ + OP_ScreenshotOptionsMenu[op_movie_folder].status = + (cv_movie_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); +} + // ========================================================================== // END ORGANIZATION STUFF. // ========================================================================== diff --git a/src/m_menu.h b/src/m_menu.h index 347725e10..4292ea581 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -397,6 +397,9 @@ void Screenshot_option_Onchange(void); // Addons menu updating void Addons_option_Onchange(void); +// Moviemode menu updating +void Moviemode_option_Onchange(void); + // These defines make it a little easier to make menus #define DEFAULTMENUSTYLE(id, header, source, prev, x, y)\ {\ diff --git a/src/m_misc.c b/src/m_misc.c index 22e19df73..3ba50689c 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -108,6 +108,9 @@ consvar_t cv_screenshot_colorprofile = {"screenshot_colorprofile", "Yes", CV_SAV 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_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}; + static CV_PossibleValue_t zlib_mem_level_t[] = { {1, "(Min Memory) 1"}, {2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"}, @@ -1124,19 +1127,25 @@ static inline moviemode_t M_StartMovieGIF(const char *pathname) void M_StartMovie(void) { #if NUMSCREENS > 2 - const char *pathname = "."; + char pathname[MAX_WADPATH]; if (moviemode) return; - if (cv_screenshot_option.value == 0) - pathname = usehome ? srb2home : srb2path; - else if (cv_screenshot_option.value == 1) - pathname = srb2home; - else if (cv_screenshot_option.value == 2) - pathname = srb2path; - else if (cv_screenshot_option.value == 3 && *cv_screenshot_folder.string != '\0') - pathname = cv_screenshot_folder.string; + if (cv_movie_option.value == 0) + strcpy(pathname, usehome ? srb2home : srb2path); + else if (cv_movie_option.value == 1) + strcpy(pathname, srb2home); + else if (cv_movie_option.value == 2) + strcpy(pathname, srb2path); + else if (cv_movie_option.value == 3 && *cv_movie_folder.string != '\0') + strcpy(pathname, cv_screenshot_folder.string); + + if (cv_movie_option.value != 3) + { + strcat(pathname, PATHSEP"movies"PATHSEP); + I_mkdir(pathname, 0755); + } if (rendermode == render_none) I_Error("Can't make a movie without a render system\n"); @@ -1474,7 +1483,8 @@ void M_ScreenShot(void) void M_DoScreenShot(void) { #if NUMSCREENS > 2 - const char *freename = NULL, *pathname = "."; + const char *freename = NULL; + char pathname[MAX_WADPATH]; boolean ret = false; UINT8 *linear = NULL; @@ -1486,13 +1496,19 @@ void M_DoScreenShot(void) return; if (cv_screenshot_option.value == 0) - pathname = usehome ? srb2home : srb2path; + strcpy(pathname, usehome ? srb2home : srb2path); else if (cv_screenshot_option.value == 1) - pathname = srb2home; + strcpy(pathname, srb2home); else if (cv_screenshot_option.value == 2) - pathname = srb2path; + strcpy(pathname, srb2path); else if (cv_screenshot_option.value == 3 && *cv_screenshot_folder.string != '\0') - pathname = cv_screenshot_folder.string; + strcpy(pathname, cv_screenshot_folder.string); + + if (cv_screenshot_option.value != 3) + { + strcat(pathname, PATHSEP"screenshots"PATHSEP); + I_mkdir(pathname, 0755); + } #ifdef USE_PNG freename = Newsnapshotfile(pathname,"png"); diff --git a/src/m_misc.h b/src/m_misc.h index 6ac92dbcc..7038e3e48 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -30,7 +30,7 @@ typedef enum { extern moviemode_t moviemode; extern consvar_t cv_screenshot_option, cv_screenshot_folder, cv_screenshot_colorprofile; -extern consvar_t cv_moviemode; +extern consvar_t cv_moviemode, cv_movie_folder, cv_movie_option; extern consvar_t cv_zlib_memory, cv_zlib_level, cv_zlib_strategy, cv_zlib_window_bits; extern consvar_t cv_zlib_memorya, cv_zlib_levela, cv_zlib_strategya, cv_zlib_window_bitsa; extern consvar_t cv_apng_delay; diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c index f54f0d7c5..2a67e88fe 100644 --- a/src/sdl/i_main.c +++ b/src/sdl/i_main.c @@ -26,6 +26,8 @@ #include #endif +#include "time.h" // For log timestamps + #ifdef HAVE_SDL #ifdef HAVE_TTF @@ -114,6 +116,7 @@ int main(int argc, char **argv) #endif { const char *logdir = NULL; + char logfile[MAX_WADPATH]; myargc = argc; myargv = argv; /// \todo pull out path to exe from this string @@ -125,15 +128,35 @@ int main(int argc, char **argv) #endif #endif - logdir = D_Home(); - #ifdef LOGMESSAGES + if (!M_CheckParm("-nolog")) + { + logdir = D_Home(); + + time_t my_time; + struct tm * timeinfo; + char buf[26]; + my_time = time(NULL); + timeinfo = localtime(&my_time); + + strftime(buf, 26, "%Y-%m-%d %H-%M-%S", timeinfo); + strcpy(logfile, va("log-%s.txt", buf)); + #ifdef DEFAULTDIR - if (logdir) - logstream = fopen(va("%s/"DEFAULTDIR"/log.txt",logdir), "wt"); - else + if (logdir) + { + // Create dirs here because D_SRB2Main() is too late. + I_mkdir(va("%s%s"DEFAULTDIR, logdir, PATHSEP), 0755); + I_mkdir(va("%s%s"DEFAULTDIR"%slogs",logdir, PATHSEP, PATHSEP), 0755); + logstream = fopen(va("%s%s"DEFAULTDIR"%slogs%s%s",logdir, PATHSEP, PATHSEP, PATHSEP, logfile), "wt"); + } + else #endif - logstream = fopen("./log.txt", "wt"); + { + I_mkdir("."PATHSEP"logs"PATHSEP, 0755); + logstream = fopen(va("."PATHSEP"logs"PATHSEP"%s", logfile), "wt"); + } + } #endif //I_OutputMsg("I_StartupSystem() ...\n"); @@ -161,6 +184,7 @@ int main(int argc, char **argv) CONS_Printf("Setting up SRB2...\n"); D_SRB2Main(); CONS_Printf("Entering main game loop...\n"); + CONS_Printf("%s\n", logfile); // never return D_SRB2Loop(); From ff1fa3f92babee4290ebadc1578e8525c9213008 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 8 Sep 2019 13:21:00 -0300 Subject: [PATCH 097/196] Implement all the other alpha blend styles --- src/hardware/hw_cache.c | 85 +++++-------------------- src/r_data.c | 133 ++++++++++++++++++++++++++++++++++------ src/r_data.h | 7 ++- 3 files changed, 138 insertions(+), 87 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index fb25dc58e..5eb6d9104 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -60,48 +60,6 @@ static const INT32 format2bpp[16] = 2, //14 GR_TEXFMT_AP_88 }; -static RGBA_t astblendpixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) -{ - RGBA_t output; - output.s.alpha = 0xFF; - - if (style == AST_TRANSLUCENT) - { - if (alpha == 0) - output.rgba = background.rgba; - else if (alpha == 0xFF) - output.rgba = foreground.rgba; - else if (alpha < 0xFF) - { - UINT8 beta = (0xFF - alpha); - output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF; - output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF; - output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF; - } - // write foreground pixel alpha - // if there's no pixel in here - if (!background.rgba) - output.s.alpha = foreground.s.alpha; - } - - return output; -} - -static UINT8 astblendpixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha) -{ - if ((style == AST_TRANSLUCENT) && (alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? - { - UINT8 *mytransmap; - if (alpha < 255/11) // Is the patch way too translucent? Don't render then. - return background; - // The equation's not exact but it works as intended. I'll call it a day for now. - mytransmap = transtables + ((8*(alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); - if (background != 0xFF) - return *(mytransmap + (background<<8) + foreground); - } - return background; -} - // This code was originally placed directly in HWR_DrawPatchInCache. // It is now split from it for my sanity! (and the sanity of others) // -- Monster Iestyn (13/02/19) @@ -180,34 +138,34 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm switch (bpp) { case 2 : // uhhhhhhhh.......... - if ((originPatch != NULL) && originPatch->style) - texel = astblendpixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) + texel = ASTBlendPixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); - if ((originPatch != NULL) && originPatch->style) + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; - if ((originPatch != NULL) && originPatch->style) + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 default: - if ((originPatch != NULL) && originPatch->style) - *dest = astblendpixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) + *dest = ASTBlendPixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); else *dest = texel; break; @@ -294,34 +252,34 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, switch (bpp) { case 2 : // uhhhhhhhh.......... - if ((originPatch != NULL) && originPatch->style) - texel = astblendpixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) + texel = ASTBlendPixel_8bpp(*(dest+1), texel, originPatch->style, originPatch->alpha); texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); - if ((originPatch != NULL) && originPatch->style) + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; - if ((originPatch != NULL) && originPatch->style) + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; - colortemp = astblendpixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); + colortemp = ASTBlendPixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 default: - if ((originPatch != NULL) && originPatch->style) - *dest = astblendpixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); + if ((originPatch != NULL) && (originPatch->style != AST_COPY)) + *dest = ASTBlendPixel_8bpp(*dest, texel, originPatch->style, originPatch->alpha); else *dest = texel; break; @@ -410,16 +368,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, if (texture->width <= 0 || texture->height <= 0) return; - /*if ((patch->style == AST_TRANSLUCENT) && (patch->alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? - { - if (patch->alpha < 255/11) // Is the patch way too translucent? Don't render then. - continue; - ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawTransFlippedColumnInCache : HWR_DrawTransColumnInCache; - } - else*/ - { - ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache; - } + ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache; x1 = patch->originx; width = SHORT(realpatch->width); diff --git a/src/r_data.c b/src/r_data.c index 6889bddde..335a390d0 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -220,15 +220,110 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, tex } } +RGBA_t ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) +{ + RGBA_t output; + if (style == AST_TRANSLUCENT) + { + if (alpha == 0) + output.rgba = background.rgba; + else if (alpha == 0xFF) + output.rgba = foreground.rgba; + else if (alpha < 0xFF) + { + UINT8 beta = (0xFF - alpha); + output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF; + output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF; + output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF; + } + // write foreground pixel alpha + // if there's no pixel in here + if (!background.rgba) + output.s.alpha = foreground.s.alpha; + } +#define clamp(c) max(min(c, 0xFF), 0x00); + else + { + float falpha = ((float)alpha / 256.0f); + float fr = ((float)foreground.s.red * falpha); + float fg = ((float)foreground.s.green * falpha); + float fb = ((float)foreground.s.blue * falpha); + if (style == AST_ADD) + { + output.s.red = clamp((int)(background.s.red + fr)); + output.s.green = clamp((int)(background.s.green + fg)); + output.s.blue = clamp((int)(background.s.blue + fb)); + } + else if (style == AST_SUBTRACT) + { + output.s.red = clamp((int)(background.s.red - fr)); + output.s.green = clamp((int)(background.s.green - fg)); + output.s.blue = clamp((int)(background.s.blue - fb)); + } + else if (style == AST_REVERSESUBTRACT) + { + output.s.red = clamp((int)((-background.s.red) + fr)); + output.s.green = clamp((int)((-background.s.green) + fg)); + output.s.blue = clamp((int)((-background.s.blue) + fb)); + } + else if (style == AST_MODULATE) + { + fr = ((float)foreground.s.red / 256.0f); + fg = ((float)foreground.s.green / 256.0f); + fb = ((float)foreground.s.blue / 256.0f); + output.s.red = clamp((int)(background.s.red * fr)); + output.s.green = clamp((int)(background.s.green * fg)); + output.s.blue = clamp((int)(background.s.blue * fb)); + } + // just copy the pixel + else if (style == AST_COPY) + return background; + } +#undef clamp + // unimplemented blend modes return the background pixel + output = background; + output.s.alpha = 0xFF; + return output; +} + +UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha) +{ + if ((style == AST_TRANSLUCENT) && (alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? + { + UINT8 *mytransmap; + if (alpha < 255/11) // Is the patch way too translucent? Don't render then. + return background; + // The equation's not exact but it works as intended. I'll call it a day for now. + mytransmap = transtables + ((8*(alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); + if (background != 0xFF) + return *(mytransmap + (background<<8) + foreground); + } + // just copy the pixel + else if (style == AST_COPY) + return background; + // use ASTBlendPixel for all other blend modes + // and find the nearest colour in the palette + else if (style != AST_TRANSLUCENT) + { + RGBA_t texel; + RGBA_t bg = V_GetColor(background); + RGBA_t fg = V_GetColor(foreground); + texel = ASTBlendPixel(bg, fg, style, alpha); + return NearestColor(texel.s.red, texel.s.green, texel.s.blue); + } + // fallback if all above fails, somehow + // return the background pixel + return background; +} + // -// R_DrawTransColumnInCache +// 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_DrawTransColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) { INT32 count, position; UINT8 *source, *dest; - UINT8 *mytransmap = transtables + ((8*(originPatch->alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); // The equation's not exact but it works as intended. I'll call it a day for now. INT32 topdelta, prevdelta = -1; INT32 originy = originPatch->originy; @@ -258,7 +353,8 @@ static inline void R_DrawTransColumnInCache(column_t *patch, UINT8 *cache, texpa if (count > 0) { for (; dest < cache + position + count; source++, dest++) - if (*dest != 0xFF) *dest = *(mytransmap + ((*dest)<<8) + (*source)); + if (*dest != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); @@ -269,11 +365,10 @@ static inline void R_DrawTransColumnInCache(column_t *patch, UINT8 *cache, texpa // R_DrawTransColumnInCache // Similar to the one above except that the column is inverted. // -static inline void R_DrawTransFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) { INT32 count, position; UINT8 *source, *dest; - UINT8 *mytransmap = transtables + ((8*(originPatch->alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); // The equation's not exact but it works as intended. I'll call it a day for now. INT32 topdelta, prevdelta = -1; INT32 originy = originPatch->originy; @@ -302,7 +397,8 @@ static inline void R_DrawTransFlippedColumnInCache(column_t *patch, UINT8 *cache if (count > 0) { for (; dest < cache + position + count; --source, dest++) - if (*dest != 0xFF) *dest = *(mytransmap + ((*dest)<<8) + (*source)); + if (*dest != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); } patch = (column_t *)((UINT8 *)patch + patch->length + 4); @@ -422,16 +518,10 @@ static UINT8 *R_GenerateTexture(size_t texnum) for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. - if ((patch->style == AST_TRANSLUCENT) && (patch->alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency? - { - if (patch->alpha < 255/11) // Is the patch way too translucent? Don't render then. - continue; - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawTransFlippedColumnInCache : R_DrawTransColumnInCache; - } + if (patch->style != AST_COPY) + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; else - { ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; - } realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); x1 = patch->originx; @@ -848,8 +938,16 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) { Z_Free(texturesToken); texturesToken = M_GetToken(NULL); - if(stricmp(texturesToken, "TRANSLUCENT")==0) + 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; @@ -1615,7 +1713,6 @@ extracolormap_t *R_ColormapForName(char *name) // static double deltas[256][3], map[256][3]; -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); static int RoundUp(double number); lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) @@ -2027,7 +2124,7 @@ extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *ex // Thanks to quake2 source! // utils3/qdata/images.c -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; diff --git a/src/r_data.h b/src/r_data.h index b6b0a16a1..af1c9114a 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -22,7 +22,12 @@ #endif // Possible alpha types for a patch. -enum patchalphastyle {AST_COPY, AST_TRANSLUCENT}; // , AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; +enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; + +RGBA_t ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha); +UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha); + +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); // moved here for r_sky.c (texpatch_t is used) From 2e90c4093429c91155761817e6425ffffcbcb018 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 6 Sep 2019 14:57:13 +0100 Subject: [PATCH 098/196] Fade time before Game Over track. Notably, can no longer use P_PlayJingle because of pre-fade. --- src/p_inter.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 9025e2664..6d1f54d5f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2469,8 +2469,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget else if (P_IsLocalPlayer(target->player)) gameovermus = true; - if (gameovermus) - P_PlayJingle(target->player, JT_GOVER); // Yousa dead now, Okieday? Tails 03-14-2000 + if (gameovermus) // Yousa dead now, Okieday? Tails 03-14-2000 + S_ChangeMusicEx("_gover", 0, 0, 0, (2*MUSICRATE) - (MUSICRATE/25), 0); // 1.96 seconds + //P_PlayJingle(target->player, JT_GOVER); // can't be used because incompatible with track fadeout if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers) { From 2c898841e978bf5fbf85c3120db4ed5948187de8 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 8 Sep 2019 18:03:51 +0100 Subject: [PATCH 099/196] Extend gameovertics again by two seconds. --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 1bbf8aa4a..ded4e6ae7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -215,7 +215,7 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; UINT16 nightslinktics = 2*TICRATE; -INT32 gameovertics = 10*TICRATE; +INT32 gameovertics = 12*TICRATE; UINT8 ammoremovaltics = 2*TICRATE; From 11e3f5ec964112a8799b08a76eb3eef994baee4f Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 8 Sep 2019 21:21:36 +0100 Subject: [PATCH 100/196] Change flipped starpostz to define the top of the player object, for cases where scale on contact with starpost is different to spawn scale. --- src/m_cheat.c | 3 +++ src/p_inter.c | 4 ++-- src/p_mobj.c | 26 ++++++++++++++------------ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index ae2703ee2..bb757839a 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -833,7 +833,10 @@ void Command_Savecheckpoint_f(void) players[consoleplayer].starpostangle = players[consoleplayer].mo->angle; players[consoleplayer].starpostscale = players[consoleplayer].mo->destscale; if (players[consoleplayer].mo->flags2 & MF2_OBJECTFLIP) + { players[consoleplayer].starpostscale *= -1; + players[consoleplayer].starpostz += players[consoleplayer].mo->height; + } CONS_Printf(M_GetText("Temporary checkpoint created at %d, %d, %d\n"), players[consoleplayer].starpostx, players[consoleplayer].starposty, players[consoleplayer].starpostz); } diff --git a/src/p_inter.c b/src/p_inter.c index 53481d6c5..cc9250e42 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1431,7 +1431,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->flags2 & MF2_OBJECTFLIP) { players[i].starpostscale *= -1; - players[i].starpostz += (special->height - P_GetPlayerHeight(player))>>FRACBITS; + players[i].starpostz += special->height>>FRACBITS; } players[i].starpostnum = special->health; @@ -1453,7 +1453,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->flags2 & MF2_OBJECTFLIP) { player->starpostscale *= -1; - player->starpostz += (special->height - P_GetPlayerHeight(player))>>FRACBITS; + player->starpostz += special->height>>FRACBITS; } player->starpostnum = special->health; S_StartSound(toucher, special->info->painsound); diff --git a/src/p_mobj.c b/src/p_mobj.c index 130808712..5843b9f43 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10669,24 +10669,26 @@ void P_MovePlayerToStarpost(INT32 playernum) P_SetScale(mobj, (mobj->destscale = abs(p->starpostscale))); - mobj->floorz = floor; - mobj->ceilingz = ceiling; - - if (z <= floor) - z = floor; - else if (z >= ceiling - mobj->height) - z = ceiling - mobj->height; - - mobj->z = z; - if (p->starpostscale < 0) { mobj->flags2 |= MF2_OBJECTFLIP; - if (mobj->z + mobj->height == mobj->ceilingz) + if (z >= ceiling) + { mobj->eflags |= MFE_ONGROUND; + z = ceiling; + } + z -= mobj->height; } - else if (mobj->z == mobj->floorz) + else if (z <= floor) + { mobj->eflags |= MFE_ONGROUND; + z = floor; + } + + mobj->floorz = floor; + mobj->ceilingz = ceiling; + + mobj->z = z; mobj->angle = p->starpostangle; From ba50a03d9c13328124769006fd99d7dec2e6c939 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 8 Sep 2019 21:28:02 +0100 Subject: [PATCH 101/196] Update SPR2_XTRA references for adjusted player.dta content. --- src/f_finale.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 056b7f815..e44add4d1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1584,15 +1584,15 @@ void F_StartEnding(void) UINT8 skinnum = players[consoleplayer].skin; spritedef_t *sprdef; spriteframe_t *sprframe; - if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 5) + if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 7) { sprdef = &skins[skinnum].sprites[SPR2_XTRA]; // character head, skin specific - sprframe = &sprdef->spriteframes[2]; - endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); - sprframe = &sprdef->spriteframes[3]; - endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); sprframe = &sprdef->spriteframes[4]; + endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); + sprframe = &sprdef->spriteframes[5]; + endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); + sprframe = &sprdef->spriteframes[6]; endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); } else // Show a star if your character doesn't have an ending firework display. (Basically the MISSINGs for this) From cc80cd77c531d23f88ae5f7ee417a453172fdc09 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Sep 2019 16:54:40 -0400 Subject: [PATCH 102/196] Fix this mistake --- src/m_misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_misc.c b/src/m_misc.c index 3ba50689c..aaaf30d67 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1139,7 +1139,7 @@ void M_StartMovie(void) else if (cv_movie_option.value == 2) strcpy(pathname, srb2path); else if (cv_movie_option.value == 3 && *cv_movie_folder.string != '\0') - strcpy(pathname, cv_screenshot_folder.string); + strcpy(pathname, cv_movie_folder.string); if (cv_movie_option.value != 3) { From 1bf78a242341c2a770690bf16b8968567d0906e6 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Sep 2019 17:14:47 -0400 Subject: [PATCH 103/196] Move mobj_t declaration to top of the block --- src/p_spec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_spec.c b/src/p_spec.c index 014a09845..74bea7266 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3956,6 +3956,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 461: // Spawns an object on the map based on texture offsets { const mobjtype_t type = (mobjtype_t)(sides[line->sidenum[0]].toptexture); + mobj_t *mobj; fixed_t x, y, z; x = sides[line->sidenum[0]].textureoffset; @@ -3977,7 +3978,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } } - mobj_t *mobj = P_SpawnMobj(x, y, z, type); + mobj = P_SpawnMobj(x, y, z, type); if (mobj) CONS_Debug(DBG_GAMELOGIC, "Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d)\n", line->special, mobj->type, mobj->x>>FRACBITS, mobj->y>>FRACBITS, mobj->z>>FRACBITS); //TODO: Convert mobj->type to a string somehow. else From 555b4e176686f971e407df16a566c1341a71851a Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Sep 2019 19:39:23 -0400 Subject: [PATCH 104/196] Fix function being redefined --- src/sdl/i_system.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 57edb1c35..78363c85e 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1035,7 +1035,7 @@ void I_GetJoystickEvents(void) */ -static int joy_open2(int joyindex) +static int joy_open(int joyindex) { SDL_Joystick *newdev = NULL; int num_joy = 0; @@ -1069,48 +1069,48 @@ static int joy_open2(int joyindex) // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed // 5. Plug Controller B -> Index 0 opened // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B - if (JoyInfo2.dev) + if (JoyInfo.dev) { - if (JoyInfo2.dev == newdev // same device, nothing to do - || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device + if (JoyInfo.dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device return JoyInfo.axises; // Else, we're changing devices, so send neutral joy events - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n"); - I_ShutdownJoystick2(); + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n"); + I_ShutdownJoystick(); } - JoyInfo2.dev = newdev; + JoyInfo.dev = newdev; - if (JoyInfo2.dev == NULL) + if (JoyInfo.dev == NULL) { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError()); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError()); return -1; } else { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); - JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); - if (JoyInfo2.axises > JOYAXISSET*2) - JoyInfo2.axises = JOYAXISSET*2; -/* if (joyaxes<2) + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev)); + JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); + if (JoyInfo.axises > JOYAXISSET*2) + JoyInfo.axises = JOYAXISSET*2; + /* if (joyaxes<2) { I_OutputMsg("Not enought axes?\n"); return 0; }*/ - JoyInfo2.buttons = SDL_JoystickNumButtons(JoyInfo2.dev); - if (JoyInfo2.buttons > JOYBUTTONS) - JoyInfo2.buttons = JOYBUTTONS; + JoyInfo.buttons = SDL_JoystickNumButtons(JoyInfo.dev); + if (JoyInfo.buttons > JOYBUTTONS) + JoyInfo.buttons = JOYBUTTONS; - JoyInfo2.hats = SDL_JoystickNumHats(JoyInfo2.dev); - if (JoyInfo2.hats > JOYHATS) - JoyInfo2.hats = JOYHATS; + JoyInfo.hats = SDL_JoystickNumHats(JoyInfo.dev); + if (JoyInfo.hats > JOYHATS) + JoyInfo.hats = JOYHATS; - JoyInfo2.balls = SDL_JoystickNumBalls(JoyInfo2.dev); + JoyInfo.balls = SDL_JoystickNumBalls(JoyInfo.dev); - //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo2.dev), "pad"); + //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo.dev), "pad"); - return JoyInfo2.axises; + return JoyInfo.axises; } } From e5d9d3499f7691d5707519477a88f0a1f6561bfb Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 8 Sep 2019 21:07:16 +0100 Subject: [PATCH 105/196] DON'T PUSH ALONE - first dregs of continue screen --- src/info.c | 12 ++++++++++++ src/info.h | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 18f0e838a..50f74b4d1 100644 --- a/src/info.c +++ b/src/info.c @@ -577,8 +577,14 @@ char spr2names[NUMPLAYERSPRITES][5] = "TALA", "TALB", + "CNT1", + "CNT2", + "CNT3", + "CNT4", + "SIGN", "LIFE", + "XTRA", }; playersprite_t free_spr2 = SPR2_FIRSTFREESLOT; @@ -674,8 +680,14 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_TAL9, // SPR2_TALA, SPR2_TAL0, // SPR2_TALB, + 0, // SPR2_CNT1, + SPR2_FALL, // SPR2_CNT2, + SPR2_SPNG, // SPR2_CNT3, + SPR2_CNT1, // SPR2_CNT4, + 0, // SPR2_SIGN, 0, // SPR2_LIFE, + 0, // SPR2_XTRA (should never be referenced) }; diff --git a/src/info.h b/src/info.h index 593c1fb7c..e1adbe070 100644 --- a/src/info.h +++ b/src/info.h @@ -833,9 +833,15 @@ typedef enum playersprite SPR2_TALA, SPR2_TALB, + SPR2_CNT1, // continue disappointment + SPR2_CNT2, // continue lift + SPR2_CNT3, // continue spin + SPR2_CNT4, // continue "soooooooniiic!" tugging + SPR2_SIGN, // end sign head SPR2_LIFE, // life monitor icon - SPR2_XTRA, // stuff that isn't in-game - keep this last in the list + + SPR2_XTRA, // stuff that isn't in-map - "would this ever need an md2 or variable length animation?" SPR2_FIRSTFREESLOT, SPR2_LASTFREESLOT = 0x7f, From 0a69190848f196103840c5ba255a198144fcb439 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 9 Sep 2019 15:39:10 -0300 Subject: [PATCH 106/196] Missing arguments --- src/hardware/hw_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 07ae7ed2b..4a075d376 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3400,7 +3400,7 @@ static void HWR_AddPolyObjectPlanes(void) FBITFIELD blendmode; memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); - HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], false, polyobjsector->floorheight, + HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, po_ptrs[i], false, polyobjsector->floorheight, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), Surf.FlatColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } else @@ -3408,7 +3408,7 @@ static void HWR_AddPolyObjectPlanes(void) HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum); HWR_GetTextureFlat(levelflats[polyobjsector->floorpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude, - (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } } @@ -3424,7 +3424,7 @@ static void HWR_AddPolyObjectPlanes(void) FBITFIELD blendmode; memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); - HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], true, polyobjsector->ceilingheight, + HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, po_ptrs[i], true, polyobjsector->ceilingheight, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), Surf.FlatColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } else @@ -3432,7 +3432,7 @@ static void HWR_AddPolyObjectPlanes(void) HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum); HWR_GetTextureFlat(levelflats[polyobjsector->ceilingpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude, - (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, + (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, levelflats[polyobjsector->floorpic].texturenum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); } } From 043bb86acdf81ef0ecbd388aed57fe0fa29c8747 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Mon, 9 Sep 2019 18:40:21 +0000 Subject: [PATCH 107/196] Revert "Merge branch 'flats-png_port' into 'master'" This reverts merge request !322 --- src/doomdef.h | 4 - src/hardware/hw_cache.c | 114 +---- src/hardware/hw_glide.h | 2 - src/hardware/hw_glob.h | 3 - src/hardware/hw_light.c | 2 - src/hardware/hw_main.c | 207 ++++----- src/hardware/hw_md2.c | 4 - src/p_setup.c | 10 - src/p_setup.h | 7 - src/p_spec.c | 30 +- src/r_data.c | 643 ++-------------------------- src/r_data.h | 27 +- src/r_draw.c | 2 - src/r_draw.h | 13 +- src/r_draw8.c | 913 +++++++++++----------------------------- src/r_plane.c | 450 +++++++++----------- src/r_plane.h | 2 - src/screen.c | 2 +- src/w_wad.c | 2 + 19 files changed, 582 insertions(+), 1855 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 4a0174369..6e7db2143 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -616,8 +616,4 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// SRB2CB itself ported this from PrBoom+ #define NEWCLIP -#ifndef HAVE_PNG -#define NO_PNG_LUMPS -#endif - #endif // __DOOMDEF__ diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index c9a75a4f3..6bc2c712e 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -30,7 +30,6 @@ #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" -#include "../p_setup.h" //Hurdler: 25/04/2000: used for new colormap code in hardware mode //static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized) @@ -421,7 +420,6 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, GrTexInfo *grInfo) { -#ifdef GLIDE_API_COMPATIBILITY // Build the full textures from patches. static const GrLOD_t gr_lods[9] = { @@ -458,9 +456,6 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, INT32 j,k; INT32 max,min; -#else - (void)grInfo; -#endif // find a power of 2 width/height if (cv_grrounddown.value) @@ -516,7 +511,6 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, } else { -#ifdef GLIDE_API_COMPATIBILITY //size up to nearest power of 2 blockwidth = 1; while (blockwidth < originalwidth) @@ -534,14 +528,9 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockheight > 2048) blockheight = 2048; //I_Error("3D GenerateTexture : too big"); -#else - blockwidth = originalwidth; - blockheight = originalheight; -#endif } // do the boring LOD stuff.. blech! -#ifdef GLIDE_API_COMPATIBILITY if (blockwidth >= blockheight) { max = blockwidth; @@ -573,7 +562,6 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockwidth < blockheight) j += 4; grInfo->aspectRatioLog2 = gr_aspects[j].aspect; -#endif blocksize = blockwidth * blockheight; @@ -662,12 +650,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { - size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump); realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); -#endif HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, @@ -773,13 +756,11 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm static size_t gr_numtextures; static GLTexture_t *gr_textures; // for ALL Doom textures -static GLTexture_t *gr_textures2; void HWR_InitTextureCache(void) { gr_numtextures = 0; gr_textures = NULL; - gr_textures2 = NULL; } @@ -818,10 +799,7 @@ void HWR_FreeTextureCache(void) // texturecache info, we can free it if (gr_textures) free(gr_textures); - if (gr_textures2) - free(gr_textures2); gr_textures = NULL; - gr_textures2 = NULL; gr_numtextures = 0; } @@ -839,9 +817,6 @@ void HWR_PrepLevelCache(size_t pnumtextures) gr_textures = calloc(pnumtextures, sizeof (*gr_textures)); if (gr_textures == NULL) I_Error("3D can't alloc gr_textures"); - gr_textures2 = calloc(pnumtextures, sizeof (*gr_textures2)); - if (gr_textures2 == NULL) - I_Error("3D can't alloc gr_textures2"); } void HWR_SetPalette(RGBA_t *palette) @@ -872,7 +847,7 @@ GLTexture_t *HWR_GetTexture(INT32 tex) GLTexture_t *grtex; #ifdef PARANOIA if ((unsigned)tex >= gr_numtextures) - I_Error("HWR_GetTexture: tex >= numtextures\n"); + I_Error(" HWR_GetTexture: tex >= numtextures\n"); #endif grtex = &gr_textures[tex]; @@ -887,39 +862,15 @@ GLTexture_t *HWR_GetTexture(INT32 tex) return grtex; } -// HWR_RenderPlane and HWR_RenderPolyObjectPlane need this to get the flat dimensions from a patch. -lumpnum_t gr_patchflat; - -static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) -{ - UINT8 *flat; - patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); - size_t lumplength = W_LumpLength(flatlumpnum); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)patch, lumplength)) - patch = R_PNGToPatch((UINT8 *)patch, lumplength); -#endif - - grMipmap->width = (UINT16)SHORT(patch->width); - grMipmap->height = (UINT16)SHORT(patch->height); - - flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); - memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); - - R_PatchToFlat(patch, flat); -} static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { size_t size, pflatsize; // setup the texture info -#ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; -#endif grMipmap->grInfo.format = GR_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; @@ -949,20 +900,15 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) pflatsize = 64; break; } + grMipmap->width = (UINT16)pflatsize; + grMipmap->height = (UINT16)pflatsize; - if (R_CheckIfPatch(flatlumpnum)) - HWR_LoadPatchFlat(grMipmap, flatlumpnum); - else - { - grMipmap->width = (UINT16)pflatsize; - grMipmap->height = (UINT16)pflatsize; - - // the flat raw data needn't be converted with palettized textures - W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), - PU_HWRCACHE, &grMipmap->grInfo.data)); - } + // the flat raw data needn't be converted with palettized textures + W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), + PU_HWRCACHE, &grMipmap->grInfo.data)); } + // Download a Doom 'flat' to the hardware cache and make it ready for use void HWR_GetFlat(lumpnum_t flatlumpnum) { @@ -977,52 +923,6 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) // The system-memory data can be purged now. Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED); - - gr_patchflat = 0; - if (R_CheckIfPatch(flatlumpnum)) - gr_patchflat = flatlumpnum; -} - -static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) -{ - UINT8 *flat; - - // setup the texture info -#ifdef GLIDE_API_COMPATIBILITY - grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; - grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; - grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; -#endif - grMipmap->grInfo.format = GR_TEXFMT_P_8; - grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; - - grMipmap->width = (UINT16)textures[texturenum]->width; - grMipmap->height = (UINT16)textures[texturenum]->height; - - flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); - memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); - - R_TextureToFlat(texturenum, flat); -} - -void HWR_GetTextureFlat(INT32 texturenum) -{ - GLTexture_t *grtex; -#ifdef PARANOIA - if ((unsigned)texturenum >= gr_numtextures) - I_Error("HWR_GetTextureFlat: texturenum >= numtextures\n"); -#endif - if (texturenum == 0 || texturenum == -1) - return; - grtex = &gr_textures2[texturenum]; - - if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded) - HWR_LoadTextureFlat(&grtex->mipmap, texturenum); - - HWD.pfnSetTexture(&grtex->mipmap); - - // The system-memory data can be purged now. - Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED); } // diff --git a/src/hardware/hw_glide.h b/src/hardware/hw_glide.h index bf91229ef..2625d5864 100644 --- a/src/hardware/hw_glide.h +++ b/src/hardware/hw_glide.h @@ -59,11 +59,9 @@ typedef FxI32 GrTextureFormat_t; typedef struct { -#ifdef GLIDE_API_COMPATIBILITY GrLOD_t smallLodLog2; GrLOD_t largeLodLog2; GrAspectRatio_t aspectRatioLog2; -#endif GrTextureFormat_t format; void *data; } GrTexInfo; diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index c7b06edfd..9656e54e9 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -101,7 +101,6 @@ void HWR_FreeTextureCache(void); void HWR_FreeExtraSubsectors(void); void HWR_GetFlat(lumpnum_t flatlumpnum); -void HWR_GetTextureFlat(INT32 texturenum); GLTexture_t *HWR_GetTexture(INT32 tex); void HWR_GetPatch(GLPatch_t *gpatch); void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap); @@ -115,8 +114,6 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum); // -------- // hw_draw.c // -------- -extern lumpnum_t gr_patchflat; - extern float gr_patch_scalex; extern float gr_patch_scaley; diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 1de20cad7..edfe328b8 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -1225,11 +1225,9 @@ static void HWR_SetLight(void) lightmappatch.height = 128; lightmappatch.mipmap.width = 128; lightmappatch.mipmap.height = 128; -#ifdef GLIDE_API_COMPATIBILITY lightmappatch.mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; -#endif lightmappatch.mipmap.flags = 0; //TF_WRAPXY; // DEBUG: view the overdraw ! } HWD.pfnSetTexture(&lightmappatch.mipmap); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 07ae7ed2b..c6a8b16e5 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -70,9 +70,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); #endif #ifdef SORTING -void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); #else static void HWR_Add3DWater(lumpnum_t lumpnum, extrasubsector_t *xsub, fixed_t fixedheight, @@ -522,7 +522,7 @@ static UINT8 HWR_FogBlockAlpha(INT32 light, UINT32 color) // Let's see if this c // HWR_RenderPlane : Render a floor or ceiling convex polygon // -----------------+ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, - FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) + FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) { polyvertex_t * pv; float height; //constant y for all points on the convex flat polygon @@ -530,9 +530,8 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is INT32 nrPlaneVerts; //verts original define of convex flat polygon INT32 i; float flatxref,flatyref; - float fflatwidth, fflatheight; + float fflatsize; INT32 flatflag; - boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; @@ -541,7 +540,6 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is #ifdef ESLOPE pslope_t *slope = NULL; #endif - patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -582,10 +580,9 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is if (nrPlaneVerts < 3) //not even a triangle ? return; - // This check is so inconsistent between functions, it hurts. - if (nrPlaneVerts > INT16_MAX) // FIXME: exceeds plVerts size + if (nrPlaneVerts > (INT32)UINT16_MAX) // FIXME: exceeds plVerts size { - CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, INT16_MAX); + CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, UINT16_MAX); return; } @@ -602,47 +599,38 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is switch (len) { case 4194304: // 2048x2048 lump - fflatwidth = fflatheight = 2048.0f; + fflatsize = 2048.0f; + flatflag = 2047; break; case 1048576: // 1024x1024 lump - fflatwidth = fflatheight = 1024.0f; + fflatsize = 1024.0f; + flatflag = 1023; break; case 262144:// 512x512 lump - fflatwidth = fflatheight = 512.0f; + fflatsize = 512.0f; + flatflag = 511; break; case 65536: // 256x256 lump - fflatwidth = fflatheight = 256.0f; + fflatsize = 256.0f; + flatflag = 255; break; case 16384: // 128x128 lump - fflatwidth = fflatheight = 128.0f; + fflatsize = 128.0f; + flatflag = 127; break; case 1024: // 32x32 lump - fflatwidth = fflatheight = 32.0f; + fflatsize = 32.0f; + flatflag = 31; break; default: // 64x64 lump - fflatwidth = fflatheight = 64.0f; + fflatsize = 64.0f; + flatflag = 63; break; } - flatflag = ((INT32)fflatwidth)-1; - - if (texturenum != 0 && texturenum != -1) - { - fflatwidth = textures[texturenum]->width; - fflatheight = textures[texturenum]->height; - } - else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? - { - patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); - fflatwidth = SHORT(patch->width); - fflatheight = SHORT(patch->height); - } - else - texflat = false; - // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatwidth); - flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatheight); + flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatsize); + flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatsize); // transform v3d = planeVerts; @@ -651,14 +639,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; angle = FOFsector->floorpic_angle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; angle = FOFsector->ceilingpic_angle; } } @@ -666,14 +654,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; angle = gr_frontsector->floorpic_angle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; angle = gr_frontsector->ceilingpic_angle; } } @@ -692,24 +680,17 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is for (i = 0; i < nrPlaneVerts; i++,v3d++,pv++) { // Hurdler: add scrolling texture on floor/ceiling - if (texflat) - { - v3d->sow = (float)(pv->x / fflatwidth) + scrollx; - v3d->tow = -(float)(pv->y / fflatheight) + scrolly; - } - else - { - v3d->sow = (float)((pv->x / fflatwidth) - flatxref + scrollx); - v3d->tow = (float)(flatyref - (pv->y / fflatheight) + scrolly); - } + v3d->sow = (float)((pv->x / fflatsize) - flatxref + scrollx); + v3d->tow = (float)(-(pv->y / fflatsize) + flatyref + scrolly); + + //v3d->sow = (float)(pv->x / fflatsize); + //v3d->tow = (float)(pv->y / fflatsize); // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); - if (texflat) - tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle)))); } @@ -3183,23 +3164,21 @@ static inline void HWR_AddPolyObjectSegs(void) #ifdef POLYOBJECTS_PLANES static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, - FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, + FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { float height; //constant y for all points on the convex flat polygon FOutVector *v3d; INT32 i; float flatxref,flatyref; - float fflatwidth, fflatheight; + float fflatsize; INT32 flatflag; - boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; fixed_t tempxsow, tempytow; size_t nrPlaneVerts; - patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -3230,47 +3209,38 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, switch (len) { case 4194304: // 2048x2048 lump - fflatwidth = fflatheight = 2048.0f; + fflatsize = 2048.0f; + flatflag = 2047; break; case 1048576: // 1024x1024 lump - fflatwidth = fflatheight = 1024.0f; + fflatsize = 1024.0f; + flatflag = 1023; break; case 262144:// 512x512 lump - fflatwidth = fflatheight = 512.0f; + fflatsize = 512.0f; + flatflag = 511; break; case 65536: // 256x256 lump - fflatwidth = fflatheight = 256.0f; + fflatsize = 256.0f; + flatflag = 255; break; case 16384: // 128x128 lump - fflatwidth = fflatheight = 128.0f; + fflatsize = 128.0f; + flatflag = 127; break; case 1024: // 32x32 lump - fflatwidth = fflatheight = 32.0f; + fflatsize = 32.0f; + flatflag = 31; break; default: // 64x64 lump - fflatwidth = fflatheight = 64.0f; + fflatsize = 64.0f; + flatflag = 63; break; } - flatflag = ((INT32)fflatwidth)-1; - - if (texturenum != 0 && texturenum != -1) - { - fflatwidth = textures[texturenum]->width; - fflatheight = textures[texturenum]->height; - } - else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? - { - patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); - fflatwidth = SHORT(patch->width); - fflatheight = SHORT(patch->height); - } - else - texflat = false; - // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)((polysector->origVerts[0].x & (~flatflag)) / fflatwidth); - flatyref = (float)((polysector->origVerts[0].y & (~flatflag)) / fflatheight); + flatxref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].x) & (~flatflag)) / fflatsize); + flatyref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].y) & (~flatflag)) / fflatsize); // transform v3d = planeVerts; @@ -3279,14 +3249,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; angle = FOFsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; angle = FOFsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3294,14 +3264,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; angle = gr_frontsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; angle = gr_frontsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3324,26 +3294,15 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++) { - // Go from the polysector's original vertex locations - // Means the flat is offset based on the original vertex locations - if (texflat) - { - v3d->sow = (float)(FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) + scrollx; - v3d->tow = -(float)(FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly; - } - else - { - v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); - v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); - } + // Hurdler: add scrolling texture on floor/ceiling + v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatsize) - flatxref + scrollx); // Go from the polysector's original vertex locations + v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatsize) + scrolly); // Means the flat is offset based on the original vertex locations // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); - if (texflat) - tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle)))); } @@ -3406,7 +3365,6 @@ static void HWR_AddPolyObjectPlanes(void) else { HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum); - HWR_GetTextureFlat(levelflats[polyobjsector->floorpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); @@ -3430,7 +3388,6 @@ static void HWR_AddPolyObjectPlanes(void) else { HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum); - HWR_GetTextureFlat(levelflats[polyobjsector->ceilingpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); @@ -3584,12 +3541,11 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->floorpic].lumpnum); - HWR_GetTextureFlat(levelflats[gr_frontsector->floorpic].texturenum); HWR_RenderPlane(gr_frontsector, &extrasubsectors[num], false, // Hack to make things continue to work around slopes. locFloorHeight == cullFloorHeight ? locFloorHeight : gr_frontsector->floorheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, levelflats[gr_frontsector->floorpic].texturenum, NULL, 255, false, floorcolormap); + PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, NULL, 255, false, floorcolormap); } } else @@ -3607,12 +3563,11 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->ceilingpic].lumpnum); - HWR_GetTextureFlat(levelflats[gr_frontsector->ceilingpic].texturenum); HWR_RenderPlane(NULL, &extrasubsectors[num], true, // Hack to make things continue to work around slopes. locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gr_frontsector->ceilingheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum, levelflats[gr_frontsector->ceilingpic].texturenum, NULL, 255, false, ceilingcolormap); + PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum,NULL, 255, false, ceilingcolormap); } } else @@ -3671,7 +3626,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, 0, + HWR_AddTransparentFloor(0, &extrasubsectors[num], false, *rover->bottomheight, @@ -3690,7 +3645,6 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->bottompic].lumpnum, - levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], false, *rover->bottomheight, @@ -3702,9 +3656,8 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->bottompic].lumpnum); - HWR_GetTextureFlat(levelflats[*rover->bottompic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, levelflats[*rover->bottompic].texturenum, + HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } @@ -3736,7 +3689,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, 0, + HWR_AddTransparentFloor(0, &extrasubsectors[num], true, *rover->topheight, @@ -3755,7 +3708,6 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->toppic].lumpnum, - levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], true, *rover->topheight, @@ -3768,9 +3720,8 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->toppic].lumpnum); - HWR_GetTextureFlat(levelflats[*rover->toppic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, levelflats[*rover->toppic].texturenum, + HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } @@ -5099,7 +5050,6 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; - INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5118,7 +5068,6 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; - INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5149,7 +5098,7 @@ static INT32 drawcount = 0; #define MAX_TRANSPARENTFLOOR 512 // This will likely turn into a copy of HWR_Add3DWater and replace it. -void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) { static size_t allocedplanes = 0; @@ -5168,7 +5117,6 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector planeinfo[numplanes].fixedheight = fixedheight; planeinfo[numplanes].lightlevel = lightlevel; planeinfo[numplanes].lumpnum = lumpnum; - planeinfo[numplanes].texturenum = texturenum; planeinfo[numplanes].xsub = xsub; planeinfo[numplanes].alpha = alpha; planeinfo[numplanes].FOFSector = FOFSector; @@ -5182,7 +5130,7 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector // Adding this for now until I can create extrasubsector info for polyobjects // When that happens it'll just be done through HWR_AddTransparentFloor and HWR_RenderPlane -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap) { static size_t allocedpolyplanes = 0; @@ -5201,7 +5149,6 @@ void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, poly polyplaneinfo[numpolyplanes].fixedheight = fixedheight; polyplaneinfo[numpolyplanes].lightlevel = lightlevel; polyplaneinfo[numpolyplanes].lumpnum = lumpnum; - polyplaneinfo[numpolyplanes].texturenum = texturenum; polyplaneinfo[numpolyplanes].polysector = polysector; polyplaneinfo[numpolyplanes].alpha = alpha; polyplaneinfo[numpolyplanes].FOFSector = FOFSector; @@ -5363,12 +5310,9 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].plane->blend & PF_NoTexture)) - { HWR_GetFlat(sortnode[sortindex[i]].plane->lumpnum); - HWR_GetTextureFlat(sortnode[sortindex[i]].plane->texturenum); - } HWR_RenderPlane(NULL, sortnode[sortindex[i]].plane->xsub, sortnode[sortindex[i]].plane->isceiling, sortnode[sortindex[i]].plane->fixedheight, sortnode[sortindex[i]].plane->blend, sortnode[sortindex[i]].plane->lightlevel, - sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->texturenum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); + sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); } else if (sortnode[sortindex[i]].polyplane) { @@ -5376,12 +5320,9 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].polyplane->blend & PF_NoTexture)) - { HWR_GetFlat(sortnode[sortindex[i]].polyplane->lumpnum); - HWR_GetTextureFlat(sortnode[sortindex[i]].polyplane->texturenum); - } HWR_RenderPolyObjectPlane(sortnode[sortindex[i]].polyplane->polysector, sortnode[sortindex[i]].polyplane->isceiling, sortnode[sortindex[i]].polyplane->fixedheight, sortnode[sortindex[i]].polyplane->blend, sortnode[sortindex[i]].polyplane->lightlevel, - sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->texturenum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); + sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); } else if (sortnode[sortindex[i]].wall) { diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 7b6367cf3..d4728315a 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -747,12 +747,10 @@ static void md2_loadTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; -#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; -#endif } HWD.pfnSetTexture(&grpatch->mipmap); HWR_UnlockCachedPatch(grpatch); @@ -800,12 +798,10 @@ static void md2_loadBlendTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; -#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; -#endif } HWD.pfnSetTexture(&grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary HWR_UnlockCachedPatch(grpatch); diff --git a/src/p_setup.c b/src/p_setup.c index 60e036a87..65335be3f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -573,11 +573,6 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); - levelflat->texturenum = R_CheckTextureNumForName(flatname); - levelflat->lasttexturenum = levelflat->texturenum; - - levelflat->baselumpnum = LUMPERROR; - levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); @@ -622,11 +617,6 @@ INT32 P_AddLevelFlatRuntime(const char *flatname) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); - levelflat->texturenum = R_CheckTextureNumForName(flatname); - levelflat->lasttexturenum = levelflat->texturenum; - - levelflat->baselumpnum = LUMPERROR; - levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); diff --git a/src/p_setup.h b/src/p_setup.h index 7e3a149eb..7e8a5d7e6 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -37,19 +37,12 @@ typedef struct { char name[9]; // resource name from wad lumpnum_t lumpnum; // lump number of the flat - INT32 texturenum, lasttexturenum; // texture number of the flat - UINT16 width, height; - fixed_t topoffset, leftoffset; // for flat animation lumpnum_t baselumpnum; - INT32 basetexturenum; INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; - - // for patchflats - UINT8 *flatpatch; } levelflat_t; extern size_t numlevelflats; diff --git a/src/p_spec.c b/src/p_spec.c index 256ca3453..7742554cd 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -205,8 +205,8 @@ void P_InitPicAnims(void) if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR) continue; - lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname); - lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname); + lastanim->picnum = R_FlatNumForName(animdefs[i].endname); + lastanim->basepic = R_FlatNumForName(animdefs[i].startname); } lastanim->istexture = animdefs[i].istexture; @@ -464,19 +464,7 @@ static inline void P_FindAnimatedFlat(INT32 animnum) for (i = 0; i < numlevelflats; i++, foundflats++) { // is that levelflat from the flat anim sequence ? - if ((anims[animnum].istexture) && (foundflats->texturenum != 0 && foundflats->texturenum != -1) - && ((UINT16)foundflats->texturenum >= startflatnum && (UINT16)foundflats->texturenum <= endflatnum)) - { - foundflats->basetexturenum = startflatnum; - foundflats->animseq = foundflats->texturenum - startflatnum; - foundflats->numpics = endflatnum - startflatnum + 1; - foundflats->speed = anims[animnum].speed; - - CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n", - atoi(sizeu1(i)), foundflats->name, foundflats->animseq, - foundflats->numpics,foundflats->speed); - } - else if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) + if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) { foundflats->baselumpnum = startflatnum; foundflats->animseq = foundflats->lumpnum - startflatnum; @@ -500,7 +488,10 @@ void P_SetupLevelFlatAnims(void) // the original game flat anim sequences for (i = 0; anims[i].istexture != -1; i++) - P_FindAnimatedFlat(i); + { + if (!anims[i].istexture) + P_FindAnimatedFlat(i); + } } // @@ -5678,12 +5669,9 @@ void P_UpdateSpecials(void) { if (foundflats->speed) // it is an animated flat { - // update the levelflat texture number - if (foundflats->basetexturenum != -1) - foundflats->texturenum = foundflats->basetexturenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); // update the levelflat lump number - else if (foundflats->baselumpnum != LUMPERROR) - foundflats->lumpnum = foundflats->baselumpnum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); + foundflats->lumpnum = foundflats->baselumpnum + + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); } } } diff --git a/src/r_data.c b/src/r_data.c index 5858117a5..6889bddde 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -40,28 +40,6 @@ #include #endif -#ifdef HAVE_PNG - -#ifndef _MSC_VER -#ifndef _LARGEFILE64_SOURCE -#define _LARGEFILE64_SOURCE -#endif -#endif - -#ifndef _LFS64_LARGEFILE -#define _LFS64_LARGEFILE -#endif - -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 0 -#endif - -#include "png.h" -#ifndef PNG_READ_SUPPORTED -#undef HAVE_PNG -#endif -#endif - // // Texture definition. // Each texture is composed of one or more patches, @@ -120,11 +98,12 @@ 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; +// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask +INT32 *texturewidthmask; + fixed_t *textureheight; // needed for texture pegging INT32 *texturetranslation; @@ -336,7 +315,7 @@ static inline void R_DrawTransFlippedColumnInCache(column_t *patch, UINT8 *cache // 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. +// been simplified for the sake of highcolor, dynamic ligthing, & speed. // // This is not optimised, but it's supposed to be executed only once // per level, when enough memory is available. @@ -353,10 +332,6 @@ static UINT8 *R_GenerateTexture(size_t texnum) column_t *patchcol; UINT32 *colofs; - UINT16 wadnum; - lumpnum_t lumpnum; - size_t lumplength; - I_Assert(texnum <= (size_t)numtextures); texture = textures[texnum]; I_Assert(texture != NULL); @@ -371,19 +346,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) { boolean holey = false; patch = texture->patches; - - wadnum = patch->wad; - lumpnum = patch->lump; - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - { - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); - goto multipatch; - } -#endif + realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); // Check the patch for holes. if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) @@ -413,7 +376,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) { texture->holes = true; texture->flip = patch->flip; - blocksize = lumplength; + blocksize = W_LumpLengthPwad(patch->wad, patch->lump); block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function &texturecache[texnum]); M_Memcpy(block, realpatch, blocksize); @@ -440,9 +403,6 @@ static UINT8 *R_GenerateTexture(size_t texnum) } // multi-patch textures (or 'composite') -#ifndef NO_PNG_LUMPS - multipatch: -#endif texture->holes = false; texture->flip = 0; blocksize = (texture->width * 4) + (texture->width * texture->height); @@ -473,15 +433,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; } - wadnum = patch->wad; - lumpnum = patch->lump; - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); -#endif - + realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); x1 = patch->originx; width = SHORT(realpatch->width); height = SHORT(realpatch->height); @@ -557,14 +509,10 @@ void R_CheckTextureCache(INT32 tex) 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); + col &= texturewidthmask[tex]; data = texturecache[tex]; + if (!data) data = R_GenerateTexture(tex); @@ -602,7 +550,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); #define TX_END "TX_END" void R_LoadTextures(void) { - INT32 i, w; + INT32 i, k, w; UINT16 j; UINT16 texstart, texend, texturesLumpPos; patch_t *patchlump; @@ -619,7 +567,6 @@ void R_LoadTextures(void) } Z_Free(texturetranslation); Z_Free(textures); - Z_Free(texflats); } // Load patches and textures. @@ -680,16 +627,15 @@ void R_LoadTextures(void) // 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)); + texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); + // Allocate texture width mask table. + texturewidthmask = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); + // Allocate texture height mask 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); @@ -727,39 +673,20 @@ void R_LoadTextures(void) // 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; - size_t lumplength; - if (wadfiles[w]->type == RET_PK3) { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + if (W_IsLumpFolder((UINT16)w, texstart + j)) // Check if lump is a folder continue; // If it is then SKIP IT } - - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE); //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); - } + M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name)); + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); texture->patchcount = 1; texture->holes = false; texture->flip = 0; @@ -774,7 +701,11 @@ void R_LoadTextures(void) Z_Unlock(patchlump); - texturewidth[i] = texture->width; + k = 1; + while (k << 1 <= texture->width) + k <<= 1; + + texturewidthmask[i] = k - 1; textureheight[i] = texture->height << FRACBITS; i++; } @@ -1166,7 +1097,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + if (stricmp(texturesToken, "WALLTEXTURE")==0) { numTexturesInLump++; Z_Free(texturesToken); @@ -1174,7 +1105,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) } else { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } @@ -1215,21 +1146,21 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + if (stricmp(texturesToken, "WALLTEXTURE")==0) { Z_Free(texturesToken); // Get the new texture newTexture = R_ParseTexture(true); // Store the new texture textures[*texindex] = newTexture; - texturewidth[*texindex] = newTexture->width; + texturewidthmask[*texindex] = newTexture->width - 1; 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); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } @@ -1336,41 +1267,6 @@ lumpnum_t R_GetFlatNumForName(const char *name) lump = LUMPERROR; } - // Detect textures - if (lump == LUMPERROR) - { - // Scan wad files backwards so patched textures take preference. - for (i = numwadfiles - 1; i >= 0; i--) - { - switch (wadfiles[i]->type) - { - case RET_WAD: - if ((start = W_CheckNumForNamePwad("TX_START", (UINT16)i, 0)) == INT16_MAX) - continue; - if ((end = W_CheckNumForNamePwad("TX_END", (UINT16)i, start)) == INT16_MAX) - continue; - break; - case RET_PK3: - if ((start = W_CheckNumForFolderStartPK3("Textures/", i, 0)) == INT16_MAX) - continue; - if ((end = W_CheckNumForFolderEndPK3("Textures/", 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; - } - } - if (lump == LUMPERROR) { if (strcmp(name, SKYFLATNAME)) @@ -1719,6 +1615,7 @@ extracolormap_t *R_ColormapForName(char *name) // static double deltas[256][3], map[256][3]; +static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); static int RoundUp(double number); lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) @@ -2130,7 +2027,7 @@ extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *ex // Thanks to quake2 source! // utils3/qdata/images.c -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; @@ -2409,479 +2306,3 @@ void R_PrecacheLevel(void) "texturememory: %s k\n" "spritememory: %s k\n", sizeu1(flatmemory>>10), sizeu2(texturememory>>10), sizeu3(spritememory>>10)); } - -// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350 -boolean R_CheckIfPatch(lumpnum_t lump) -{ - size_t size; - INT16 width, height; - patch_t *patch; - boolean result; - - size = W_LumpLength(lump); - - // 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)); - - if (result) - { - // The dimensions seem like they might be valid for a patch, so - // check the column directory for extra security. All columns - // must begin after the column directory, and none of them must - // point past the end of the patch. - INT16 x; - - for (x = 0; x < width; x++) - { - UINT32 ofs = LONG(patch->columnofs[x]); - - // Need one byte for an empty column (but there's patches that don't know that!) - if (ofs < (UINT32)width * 4 + 8 || ofs >= (UINT32)size) - { - result = false; - break; - } - } - } - - return result; -} - -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); - } - } -} - -#ifndef NO_PNG_LUMPS -boolean R_IsLumpPNG(UINT8 *d, size_t s) -{ - if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ - return false; - // Check for PNG file signature using memcmp - // As it may be faster on CPUs with slow unaligned memory access - // Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature - return (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0); -} - -#ifdef HAVE_PNG -typedef struct { - png_bytep buffer; - png_uint_32 bufsize; - png_uint_32 current_pos; -} png_ioread; - -static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) -{ - png_ioread *f = png_get_io_ptr(png_ptr); - if (length > (f->bufsize - f->current_pos)) - png_error(png_ptr, "PNG_IOReader: buffer overrun"); - memcpy(data, f->buffer + f->current_pos, length); - f->current_pos += length; -} - -static void PNG_error(png_structp PNG, png_const_charp pngtext) -{ - CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext); - //I_Error("libpng error at %p: %s", PNG, pngtext); -} - -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(UINT8 *png, UINT16 *w, UINT16 *h, 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; -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - jmp_buf jmpbuf; -#endif -#endif - - png_ioread png_io; - png_bytep *row_pointers; - - 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; - } - - 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; - } - -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) -#else - 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; - } -#ifdef USE_FAR_KEYWORD - png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); -#endif - - // set our own read_function - png_io.buffer = (png_bytep)png; - png_io.bufsize = size; - png_io.current_pos = 0; - png_set_read_fn(png_ptr, &png_io, PNG_IOReader); - -#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, &width, &height, &bit_depth, &color_type, - NULL, NULL, NULL); - - if (bit_depth == 16) - png_set_strip_16(png_ptr); - - 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); - - if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png_ptr); - else if (color_type != PNG_COLOR_TYPE_RGB_ALPHA && color_type != PNG_COLOR_TYPE_GRAY_ALPHA) - { -#if PNG_LIBPNG_VER < 10207 - png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); -#else - png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); -#endif - } - - png_read_update_info(png_ptr, png_info_ptr); - - // Read the image - row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); - for (y = 0; y < height; y++) - row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, png_info_ptr)); - png_read_image(png_ptr, row_pointers); - png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - - *w = (INT32)width; - *h = (INT32)height; - return row_pointers; -} - -// Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) -{ - UINT8 *flat; - png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, size); - png_uint_32 width = *w, height = *h; - - if (!row_pointers) - I_Error("PNG_RawConvert: conversion failed"); - - // Convert the image to 8bpp - flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); - 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; -} - -// Convert a PNG to a flat. -UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) -{ - return PNG_RawConvert(png, &levelflat->width, &levelflat->height, size); -} - -// Convert a PNG to a patch. -static unsigned char imgbuf[1<<26]; -patch_t *R_PNGToPatch(UINT8 *png, size_t size) -{ - UINT16 width, height; - UINT8 *raw = PNG_RawConvert(png, &width, &height, size); - - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - - #define WRITE8(buf, a) ({*buf = (a); buf++;}) - #define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;}) - #define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);}) - - if (!raw) - I_Error("R_PNGToPatch: conversion failed"); - - // Write image size and offset - WRITE16(imgptr, width); - WRITE16(imgptr, height); - // no offsets - WRITE16(imgptr, 0); - WRITE16(imgptr, 0); - - // 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; - - //printf("%d ", x); - // Write column pointer (@TODO may be wrong) - WRITE32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT8 paletteIndex = raw[((y * width) + x)]; - - // 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) - WRITE8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITE8(imgptr, 254); - WRITE8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITE8(imgptr, 254); - WRITE8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITE8(imgptr, writeY);///@TODO calculate starting y pos - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITE8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITE8(imgptr, 0); - - WRITE8(imgptr, 0xFF); - } - - #undef WRITE8 - #undef WRITE16 - #undef WRITE32 - - size = imgptr-imgbuf; - img = malloc(size); - memcpy(img, imgbuf, size); - - Z_Free(raw); - - return (patch_t *)img; -} - -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) -{ - png_structp png_ptr; - png_infop png_info_ptr; - png_uint_32 w, h; - int bit_depth, color_type; -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - jmp_buf jmpbuf; -#endif -#endif - - png_ioread png_io; - - 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; - } - - 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; - } - -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) -#else - 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; - } -#ifdef USE_FAR_KEYWORD - png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); -#endif - - // set our own read_function - png_io.buffer = (png_bytep)png; - png_io.bufsize = size; - png_io.current_pos = 0; - png_set_read_fn(png_ptr, &png_io, PNG_IOReader); - -#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); - - // okay done. stop. - png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - - *width = (INT32)w; - *height = (INT32)h; - return true; -} -#endif -#endif - -void R_TextureToFlat(size_t tex, UINT8 *flat) -{ - texture_t *texture = textures[tex]; - - fixed_t col, ofs; - column_t *column; - UINT8 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = flat; - deststop = desttop + (texture->width * texture->height); - - for (col = 0; col < texture->width; col++, desttop++) - { - column = (column_t *)R_GetColumn(tex, col); - if (!texture->holes) - { - dest = desttop; - source = (UINT8 *)(column); - for (ofs = 0; dest < deststop && ofs < texture->height; ofs++) - { - if (source[ofs] != TRANSPARENTPIXEL) - *dest = source[ofs]; - dest += texture->width; - } - } - else - { - INT32 topdelta, prevdelta = -1; - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - - dest = desttop + (topdelta * texture->width); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - if (source[ofs] != TRANSPARENTPIXEL) - *dest = source[ofs]; - dest += texture->width; - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } - } -} diff --git a/src/r_data.h b/src/r_data.h index b29bf4557..b6b0a16a1 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -16,7 +16,6 @@ #include "r_defs.h" #include "r_state.h" -#include "p_setup.h" // levelflats #ifdef __GNUG__ #pragma interface @@ -56,17 +55,12 @@ typedef struct 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; +// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask +extern INT32 *texturewidthmask; + extern fixed_t *textureheight; // needed for texture pegging extern INT16 color8to16[256]; // remap color index to highcolor @@ -94,6 +88,7 @@ void R_PrecacheLevel(void); // Floor/ceiling opaque texture tiles, // lookup by name. For animation? lumpnum_t R_GetFlatNumForName(const char *name); +#define R_FlatNumForName(x) R_GetFlatNumForName(x) // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. @@ -153,20 +148,6 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); #define R_PutRgbaRGB(r, g, b) (R_PutRgbaR(r) + R_PutRgbaG(g) + R_PutRgbaB(b)) #define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a)) -boolean R_CheckIfPatch(lumpnum_t lump); -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); - -void R_PatchToFlat(patch_t *patch, UINT8 *flat); -void R_TextureToFlat(size_t tex, UINT8 *flat); - -#ifndef NO_PNG_LUMPS -boolean R_IsLumpPNG(UINT8 *d, size_t s); - -UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size); -patch_t *R_PNGToPatch(UINT8 *png, size_t size); -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); -#endif - extern INT32 numtextures; #endif diff --git a/src/r_draw.c b/src/r_draw.c index 1754403c4..396ed0344 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -99,8 +99,6 @@ INT32 dc_numlights = 0, dc_maxlights, dc_texheight; INT32 ds_y, ds_x1, ds_x2; lighttable_t *ds_colormap; fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; -UINT16 ds_flatwidth, ds_flatheight; -boolean ds_powersoftwo; UINT8 *ds_source; // start of a 64*64 tile image UINT8 *ds_transmap; // one of the translucency tables diff --git a/src/r_draw.h b/src/r_draw.h index 3c1429722..82498eb11 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -57,9 +57,7 @@ extern INT32 dc_texheight; extern INT32 ds_y, ds_x1, ds_x2; extern lighttable_t *ds_colormap; extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; -extern UINT16 ds_flatwidth, ds_flatheight; -extern boolean ds_powersoftwo; -extern UINT8 *ds_source; +extern UINT8 *ds_source; // start of a 64*64 tile image extern UINT8 *ds_transmap; #ifdef ESLOPE @@ -130,8 +128,6 @@ void R_FillBackScreen(void); void R_DrawViewBorder(void); #endif -#define TRANSPARENTPIXEL 255 - // ----------------- // 8bpp DRAWING CODE // ----------------- @@ -173,13 +169,6 @@ void R_DrawFogSpan_8(void); void R_DrawFogColumn_8(void); void R_DrawColumnShadowed_8(void); -#ifndef NOWATER -void R_DrawTranslucentWaterSpan_8(void); - -extern INT32 ds_bgofs; -extern INT32 ds_waterofs; -#endif - // ------------------ // 16bpp DRAWING CODE // ------------------ diff --git a/src/r_draw8.c b/src/r_draw8.c index 77406f83c..8a2d37fb3 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -105,6 +105,8 @@ void R_DrawColumn_8(void) } } +#define TRANSPARENTPIXEL 255 + void R_Draw2sMultiPatchColumn_8(void) { INT32 count; @@ -541,19 +543,16 @@ void R_DrawTranslatedColumn_8(void) */ void R_DrawSpan_8 (void) { - fixed_t xposition; - fixed_t yposition; - fixed_t xstep, ystep; + UINT32 xposition; + UINT32 yposition; + UINT32 xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count = (ds_x2 - ds_x1 + 1); - - xposition = ds_xfrac; yposition = ds_yfrac; - xstep = ds_xstep; ystep = ds_ystep; + size_t count; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -562,88 +561,62 @@ void R_DrawSpan_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - if (ds_powersoftwo) - { - xposition <<= nflatshiftup; yposition <<= nflatshiftup; - xstep <<= nflatshiftup; ystep <<= nflatshiftup; - } + xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; + xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; if (dest+8 > deststop) return; - if (!ds_powersoftwo) + while (count >= 8) { - while (count-- && dest <= deststop) - { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; - // 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); + dest[1] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; - x %= ds_flatwidth; - y %= ds_flatheight; + dest[2] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; - *dest++ = colormap[source[((y * ds_flatwidth) + x)]]; - xposition += xstep; - yposition += ystep; - } + dest[3] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[4] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[5] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[6] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[7] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; } - else + while (count-- && dest <= deststop) { - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[1] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[2] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[3] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[4] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[5] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[6] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[7] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count-- && dest <= deststop) - { - *dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - } + *dest++ = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; } } @@ -724,24 +697,7 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = colormap[source[((y * ds_flatwidth) + x)]]; - } - else - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; iz += ds_sz.x; uz += ds_su.x; @@ -778,24 +734,7 @@ void R_DrawTiltedSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = colormap[source[((y * ds_flatwidth) + x)]]; - } - else - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -811,24 +750,7 @@ void R_DrawTiltedSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = colormap[source[((y * ds_flatwidth) + x)]]; - } - else - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; } else { @@ -849,24 +771,7 @@ void R_DrawTiltedSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = colormap[source[((y * ds_flatwidth) + x)]]; - } - else - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -927,24 +832,7 @@ void R_DrawTiltedTranslucentSpan_8(void) v = (INT64)(vz*z) + viewy; colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); - } - else - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; iz += ds_sz.x; uz += ds_su.x; @@ -981,24 +869,7 @@ void R_DrawTiltedTranslucentSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); - } - else - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -1014,24 +885,7 @@ void R_DrawTiltedTranslucentSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); - } - else - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); } else { @@ -1052,24 +906,7 @@ void R_DrawTiltedTranslucentSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); - } - else - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -1130,28 +967,9 @@ void R_DrawTiltedSplat_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - val = source[((y * ds_flatwidth) + x)]; - } - else - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; - + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; - dest++; iz += ds_sz.x; uz += ds_su.x; @@ -1188,24 +1006,7 @@ void R_DrawTiltedSplat_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - val = source[((y * ds_flatwidth) + x)]; - } - else - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1223,24 +1024,7 @@ void R_DrawTiltedSplat_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - val = source[((y * ds_flatwidth) + x)]; - } - else - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; } @@ -1264,24 +1048,6 @@ void R_DrawTiltedSplat_8(void) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; - if (!ds_powersoftwo) - { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> 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; - - val = source[((y * ds_flatwidth) + x)]; - } - else - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1299,21 +1065,17 @@ void R_DrawTiltedSplat_8(void) */ void R_DrawSplat_8 (void) { - fixed_t xposition; - fixed_t yposition; - fixed_t xstep, ystep; + UINT32 xposition; + UINT32 yposition; + UINT32 xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; - const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count = (ds_x2 - ds_x1 + 1); + size_t count; UINT32 val; - xposition = ds_xfrac; yposition = ds_yfrac; - xstep = ds_xstep; ystep = ds_ystep; - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the // texture with two shifts, an OR and one AND. (see below) @@ -1321,125 +1083,99 @@ void R_DrawSplat_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - if (ds_powersoftwo) - { - xposition <<= nflatshiftup; yposition <<= nflatshiftup; - xstep <<= nflatshiftup; ystep <<= nflatshiftup; - } + xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; + xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; - if (!ds_powersoftwo) + while (count >= 8) { - while (count-- && dest <= deststop) - { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + // + // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[0] = colormap[val]; + xposition += xstep; + yposition += ystep; - // 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); + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[1] = colormap[val]; + xposition += xstep; + yposition += ystep; - x %= ds_flatwidth; - y %= ds_flatheight; + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[2] = colormap[val]; + xposition += xstep; + yposition += ystep; - val = source[((y * ds_flatwidth) + x)]; - if (val != TRANSPARENTPIXEL) - *dest = colormap[val]; - dest++; - xposition += xstep; - yposition += ystep; - } + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[3] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[4] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[5] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[6] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[7] = colormap[val]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; } - else + while (count--) { - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - // - // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[0] = colormap[val]; - xposition += xstep; - yposition += ystep; + val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + *dest = colormap[val]; - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[1] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[2] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[3] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[4] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[5] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[6] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[7] = colormap[val]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count-- && dest <= deststop) - { - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - *dest = colormap[val]; - dest++; - xposition += xstep; - yposition += ystep; - } + dest++; + xposition += xstep; + yposition += ystep; } } @@ -1448,20 +1184,16 @@ void R_DrawSplat_8 (void) */ void R_DrawTranslucentSplat_8 (void) { - fixed_t xposition; - fixed_t yposition; - fixed_t xstep, ystep; + UINT32 xposition; + UINT32 yposition; + UINT32 xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; - const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count = (ds_x2 - ds_x1 + 1); - UINT32 val; - - xposition = ds_xfrac; yposition = ds_yfrac; - xstep = ds_xstep; ystep = ds_ystep; + size_t count; + UINT8 val; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -1470,107 +1202,79 @@ void R_DrawTranslucentSplat_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - if (ds_powersoftwo) - { - xposition <<= nflatshiftup; yposition <<= nflatshiftup; - xstep <<= nflatshiftup; ystep <<= nflatshiftup; - } + xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; + xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; - if (!ds_powersoftwo) + while (count >= 8) { - while (count-- && dest <= deststop) - { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; - // 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); + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; - x %= ds_flatwidth; - y %= ds_flatheight; + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; - val = source[((y * ds_flatwidth) + x)]; - if (val != TRANSPARENTPIXEL) - *dest = *(ds_transmap + (colormap[val] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; - } + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; } - else + while (count--) { - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; + val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + *dest = *(ds_transmap + (colormap[val] << 8) + *dest); - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count-- && dest <= deststop) - { - val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - *dest = *(ds_transmap + (colormap[val] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; - } + dest++; + xposition += xstep; + yposition += ystep; } } @@ -1579,20 +1283,15 @@ void R_DrawTranslucentSplat_8 (void) */ void R_DrawTranslucentSpan_8 (void) { - fixed_t xposition; - fixed_t yposition; - fixed_t xstep, ystep; + UINT32 xposition; + UINT32 yposition; + UINT32 xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; - const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count = (ds_x2 - ds_x1 + 1); - UINT32 val; - - xposition = ds_xfrac; yposition = ds_yfrac; - xstep = ds_xstep; ystep = ds_ystep; + size_t count; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -1601,161 +1300,63 @@ void R_DrawTranslucentSpan_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - if (ds_powersoftwo) - { - xposition <<= nflatshiftup; yposition <<= nflatshiftup; - xstep <<= nflatshiftup; ystep <<= nflatshiftup; - } + xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; + xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; - if (!ds_powersoftwo) + while (count >= 8) { - while (count-- && dest <= deststop) - { - fixed_t x = (xposition >> FRACBITS); - fixed_t y = (yposition >> FRACBITS); + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; - // 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); + dest[1] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; - x %= ds_flatwidth; - y %= ds_flatheight; + dest[2] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; - val = ((y * ds_flatwidth) + x); - *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; - } + dest[3] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + dest[4] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + dest[5] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + dest[6] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + dest[7] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; } - else + while (count--) { - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; - - dest[1] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; - - dest[2] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; - - dest[3] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - dest[4] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - dest[5] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - dest[6] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - dest[7] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count-- && dest <= deststop) - { - val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; - } + *dest = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; } } -#ifndef NOWATER -void R_DrawTranslucentWaterSpan_8(void) -{ - fixed_t xposition; - fixed_t yposition; - fixed_t xstep, ystep; - - UINT8 *source; - UINT8 *colormap; - UINT8 *dest; - UINT8 *dsrc; - const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - - size_t count = (ds_x2 - ds_x1 + 1); - - xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs); - xstep = ds_xstep; ystep = ds_ystep; - - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - if (ds_powersoftwo) - { - xposition <<= nflatshiftup; yposition <<= nflatshiftup; - xstep <<= nflatshiftup; ystep <<= nflatshiftup; - } - - source = ds_source; - colormap = ds_colormap; - dest = ylookup[ds_y] + columnofs[ds_x1]; - dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; - - if (!ds_powersoftwo) - { - 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; - - *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - } - } - else - { - while (count-- && dest <= deststop) - { - *dest++ = colormap[*(ds_transmap + (source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - } - } -} -#endif - /** \brief The R_DrawFogSpan_8 function Draws the actual span with fogging. */ diff --git a/src/r_plane.c b/src/r_plane.c index de5bf9f00..2f6f97240 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -127,13 +127,91 @@ void R_InitPlanes(void) // viewheight #ifndef NOWATER -INT32 ds_bgofs; -INT32 ds_waterofs; - +static INT32 bgofs; static INT32 wtofs=0; +static INT32 waterofs; static boolean itswater; #endif +#ifndef NOWATER +static void R_DrawTranslucentWaterSpan_8(void) +{ + UINT32 xposition; + UINT32 yposition; + UINT32 xstep, ystep; + + UINT8 *source; + UINT8 *colormap; + UINT8 *dest; + UINT8 *dsrc; + + size_t count; + + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? + xposition = ds_xfrac << nflatshiftup; yposition = (ds_yfrac + waterofs) << nflatshiftup; + xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + + source = ds_source; + colormap = ds_colormap; + dest = ylookup[ds_y] + columnofs[ds_x1]; + dsrc = screens[1] + (ds_y+bgofs)*vid.width + ds_x1; + count = ds_x2 - ds_x1 + 1; + + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } + while (count--) + { + *dest++ = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + } +} +#endif + void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; @@ -180,17 +258,17 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { const INT32 yay = (wtofs + (distance>>9) ) & 8191; // ripples da water texture - ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; + bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; angle = (angle + 2048) & 8191; // 90 degrees - ds_xfrac += FixedMul(FINECOSINE(angle), (ds_bgofs<=viewheight) - ds_bgofs = viewheight-y-1; - if (y+ds_bgofs<0) - ds_bgofs = -y; + if (y+bgofs>=viewheight) + bgofs = viewheight-y-1; + if (y+bgofs<0) + bgofs = -y; } #endif @@ -602,7 +680,7 @@ void R_DrawPlanes(void) } } #ifndef NOWATER - ds_waterofs = (leveltime & 1)*16384; + waterofs = (leveltime & 1)*16384; wtofs = leveltime * 140; #endif } @@ -650,156 +728,13 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -boolean R_CheckPowersOfTwo(void) -{ - return (ds_powersoftwo = ((!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))) && (ds_flatwidth == ds_flatheight))); -} - -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; - } -} - -static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) -{ - UINT8 *flat; - textureflat_t *texflat = &texflats[levelflat->texturenum]; - patch_t *patch = NULL; - boolean texturechanged = (leveltexture ? (levelflat->texturenum != levelflat->lasttexturenum) : false); - - // 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) - { - if (leveltexture) - { - texture_t *texture = textures[levelflat->texturenum]; - texflat->width = ds_flatwidth = texture->width; - texflat->height = ds_flatheight = texture->height; - - texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); - memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); - R_TextureToFlat(levelflat->texturenum, texflat->flat); - flat = texflat->flat; - - levelflat->flatpatch = flat; - levelflat->width = ds_flatwidth; - levelflat->height = ds_flatheight; - } - else - { - patch = (patch_t *)ds_source; -#ifndef NO_PNG_LUMPS - if (ispng) - { - levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->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 = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); - memset(levelflat->flatpatch, TRANSPARENTPIXEL, 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->lasttexturenum = levelflat->texturenum; - return flat; -} - void R_DrawSinglePlane(visplane_t *pl) { - UINT8 *flat; INT32 light = 0; INT32 x; INT32 stop, angle; size_t size; ffloor_t *rover; - levelflat_t *levelflat; if (!(pl->minx <= pl->maxx)) return; @@ -939,43 +874,64 @@ void R_DrawSinglePlane(visplane_t *pl) viewangle = pl->viewangle+pl->plangle; } + currentplane = pl; + + ds_source = (UINT8 *) + W_CacheLumpNum(levelflats[pl->picnum].lumpnum, + PU_STATIC); // Stay here until Z_ChangeTag + + size = W_LumpLength(levelflats[pl->picnum].lumpnum); + + switch (size) + { + case 4194304: // 2048x2048 lump + nflatmask = 0x3FF800; + nflatxshift = 21; + nflatyshift = 10; + nflatshiftup = 5; + break; + case 1048576: // 1024x1024 lump + nflatmask = 0xFFC00; + nflatxshift = 22; + nflatyshift = 12; + nflatshiftup = 6; + break; + case 262144:// 512x512 lump' + nflatmask = 0x3FE00; + nflatxshift = 23; + nflatyshift = 14; + nflatshiftup = 7; + break; + case 65536: // 256x256 lump + nflatmask = 0xFF00; + nflatxshift = 24; + nflatyshift = 16; + nflatshiftup = 8; + break; + case 16384: // 128x128 lump + nflatmask = 0x3F80; + nflatxshift = 25; + nflatyshift = 18; + nflatshiftup = 9; + break; + case 1024: // 32x32 lump + nflatmask = 0x3E0; + nflatxshift = 27; + nflatyshift = 22; + nflatshiftup = 11; + break; + default: // 64x64 lump + nflatmask = 0xFC0; + nflatxshift = 26; + nflatyshift = 20; + nflatshiftup = 10; + break; + } + xoffs = pl->xoffs; yoffs = pl->yoffs; planeheight = abs(pl->height - pl->viewz); - currentplane = pl; - levelflat = &levelflats[pl->picnum]; - size = W_LumpLength(levelflat->lumpnum); - ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag - - // Check if the flat is actually a wall texture. - if (levelflat->texturenum != 0 && levelflat->texturenum != -1) - flat = R_GetPatchFlat(levelflat, true, false); -#ifndef NO_PNG_LUMPS - // Maybe it's a PNG?! - else if (R_IsLumpPNG(ds_source, size)) - flat = R_GetPatchFlat(levelflat, false, true); -#endif - // Maybe it's just a patch, then? - else if (R_CheckIfPatch(levelflat->lumpnum)) - flat = R_GetPatchFlat(levelflat, false, false); - // It's a raw flat. - else - { - R_CheckFlatLength(size); - flat = ds_source; - } - - Z_ChangeTag(ds_source, PU_CACHE); - ds_source = flat; - - if (ds_source == NULL) - return; - - // Check if the flat has dimensions that are powers-of-two numbers. - if (R_CheckPowersOfTwo()) - R_CheckFlatLength(ds_flatwidth * ds_flatheight); - if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; @@ -989,63 +945,60 @@ void R_DrawSinglePlane(visplane_t *pl) floatv3_t p, m, n; float ang; float vx, vy, vz; - float fudge = 0; // compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly // use this as a temp var to store P_GetZAt's return value each time fixed_t temp; + // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red + const float fudge = ((1<plangle & (ANGLE_90-1)); yoffs *= 1; - if (ds_powersoftwo) + if (hack) { - // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red - fudge = ((1<>ANGLETOFINESHIFT); - const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); + /* + Essentially: We can't & the components along the regular axes when the plane is rotated. + This is because the distance on each regular axis in order to loop is different. + We rotate them, & the components, add them together, & them again, and then rotate them back. + These three seperate & operations are done per axis in order to prevent overflows. + toast 10/04/17 + */ + const fixed_t cosinecomponent = FINECOSINE(hack>>ANGLETOFINESHIFT); + const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); - const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); + const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); - fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); - fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); + fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); + fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); - temp = ox & modmask; - oy &= modmask; - ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction - oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); + temp = ox & modmask; + oy &= modmask; + ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction + oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); - temp = xoffs; - xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); - yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); + temp = xoffs; + xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); + yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); - temp = xoffs & modmask; - yoffs &= modmask; - xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto - yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); + temp = xoffs & modmask; + yoffs &= modmask; + xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto + yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); - xoffs -= (pl->slope->o.x - ox); - yoffs += (pl->slope->o.y + oy); - } - else - { - xoffs &= ((1 << (32-nflatshiftup))-1); - yoffs &= ((1 << (32-nflatshiftup))-1); - xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - } - xoffs = (fixed_t)(xoffs*fudge); - yoffs = (fixed_t)(yoffs/fudge); + xoffs -= (pl->slope->o.x - ox); + yoffs += (pl->slope->o.y + oy); } + else + { + xoffs &= ((1 << (32-nflatshiftup))-1); + yoffs &= ((1 << (32-nflatshiftup))-1); + xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + } + + xoffs = (fixed_t)(xoffs*fudge); + yoffs = (fixed_t)(yoffs/fudge); vx = FIXED_TO_FLOAT(pl->viewx+xoffs); vy = FIXED_TO_FLOAT(pl->viewy-yoffs); @@ -1080,16 +1033,13 @@ void R_DrawSinglePlane(visplane_t *pl) temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang))); n.y = FIXED_TO_FLOAT(temp) - zeroheight; - if (ds_powersoftwo) - { - m.x /= fudge; - m.y /= fudge; - m.z /= fudge; + m.x /= fudge; + m.y /= fudge; + m.z /= fudge; - n.x *= fudge; - n.y *= fudge; - n.z *= fudge; - } + n.x *= fudge; + n.y *= fudge; + n.z *= fudge; // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. #define CROSS(d, v1, v2) \ @@ -1106,26 +1056,14 @@ void R_DrawSinglePlane(visplane_t *pl) ds_sz.z *= focallengthf; // Premultiply the texture vectors with the scale factors - if (ds_powersoftwo) - { #define SFMULT 65536.f*(1< Date: Mon, 9 Sep 2019 15:02:13 -0400 Subject: [PATCH 108/196] Be clear on what FALLTHRU we really want --- src/d_main.c | 2 +- src/g_game.c | 2 +- src/sdl/i_system.c | 2 +- src/win32/win_sys.c | 2 +- src/win32/win_vid.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index eaeae4b10..9fa506bee 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -287,7 +287,7 @@ static void D_Display(void) F_TitleScreenDrawer(); break; } - // Intentional fall-through + /* FALLTHRU */ case GS_LEVEL: if (!gametic) break; diff --git a/src/g_game.c b/src/g_game.c index 89a96f3d0..2b7a1c981 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4583,7 +4583,7 @@ void G_GhostTicker(void) default: case GHC_RETURNSKIN: g->mo->skin = g->oldmo.skin; - // fallthru + /* FALLTHRU */ case GHC_NORMAL: // Go back to skin color g->mo->color = g->oldmo.color; break; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 98166a1ce..c4fb1c0fc 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -530,7 +530,7 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) break; case VK_RETURN: entering_con_command = false; - // Fall through. + /* FALLTHRU */ default: event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char } diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index d10f73b58..93b3ff523 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -327,7 +327,7 @@ static inline VOID I_GetConsoleEvents(VOID) break; case VK_RETURN: entering_con_command = false; - // Fall through. + /* FALLTHRU */ default: ev.data1 = MapVirtualKey(input.Event.KeyEvent.wVirtualKeyCode,2); // convert in to char } diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index e2f32fa61..11c7a6744 100644 --- a/src/win32/win_vid.c +++ b/src/win32/win_vid.c @@ -322,9 +322,9 @@ static inline boolean I_SkipFrame(void) case GS_LEVEL: if (!paused) return false; - /* FALLTHRU */ //case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible #ifndef CLIENT_LOADINGSCREEN + /* FALLTHRU */ case GS_WAITINGPLAYERS: #endif return skip; // Skip odd frames From 470ac5fed29d7e18eae67713d06810d01c52eea2 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Mon, 9 Sep 2019 15:05:17 -0400 Subject: [PATCH 109/196] Revert "Revert "Merge branch 'flats-png_port' into 'master'"" This reverts commit 043bb86acdf81ef0ecbd388aed57fe0fa29c8747. --- src/doomdef.h | 4 + src/hardware/hw_cache.c | 114 ++++- src/hardware/hw_glide.h | 2 + src/hardware/hw_glob.h | 3 + src/hardware/hw_light.c | 2 + src/hardware/hw_main.c | 207 +++++---- src/hardware/hw_md2.c | 4 + src/p_setup.c | 10 + src/p_setup.h | 7 + src/p_spec.c | 30 +- src/r_data.c | 643 ++++++++++++++++++++++++++-- src/r_data.h | 27 +- src/r_draw.c | 2 + src/r_draw.h | 13 +- src/r_draw8.c | 913 +++++++++++++++++++++++++++++----------- src/r_plane.c | 450 +++++++++++--------- src/r_plane.h | 2 + src/screen.c | 2 +- src/w_wad.c | 2 - 19 files changed, 1855 insertions(+), 582 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 6e7db2143..4a0174369 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -616,4 +616,8 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// SRB2CB itself ported this from PrBoom+ #define NEWCLIP +#ifndef HAVE_PNG +#define NO_PNG_LUMPS +#endif + #endif // __DOOMDEF__ diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 6bc2c712e..c9a75a4f3 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -30,6 +30,7 @@ #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" +#include "../p_setup.h" //Hurdler: 25/04/2000: used for new colormap code in hardware mode //static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized) @@ -420,6 +421,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, GrTexInfo *grInfo) { +#ifdef GLIDE_API_COMPATIBILITY // Build the full textures from patches. static const GrLOD_t gr_lods[9] = { @@ -456,6 +458,9 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, INT32 j,k; INT32 max,min; +#else + (void)grInfo; +#endif // find a power of 2 width/height if (cv_grrounddown.value) @@ -511,6 +516,7 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, } else { +#ifdef GLIDE_API_COMPATIBILITY //size up to nearest power of 2 blockwidth = 1; while (blockwidth < originalwidth) @@ -528,9 +534,14 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockheight > 2048) blockheight = 2048; //I_Error("3D GenerateTexture : too big"); +#else + blockwidth = originalwidth; + blockheight = originalheight; +#endif } // do the boring LOD stuff.. blech! +#ifdef GLIDE_API_COMPATIBILITY if (blockwidth >= blockheight) { max = blockwidth; @@ -562,6 +573,7 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockwidth < blockheight) j += 4; grInfo->aspectRatioLog2 = gr_aspects[j].aspect; +#endif blocksize = blockwidth * blockheight; @@ -650,7 +662,12 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { + size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump); realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); +#ifndef NO_PNG_LUMPS + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); +#endif HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, @@ -756,11 +773,13 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm static size_t gr_numtextures; static GLTexture_t *gr_textures; // for ALL Doom textures +static GLTexture_t *gr_textures2; void HWR_InitTextureCache(void) { gr_numtextures = 0; gr_textures = NULL; + gr_textures2 = NULL; } @@ -799,7 +818,10 @@ void HWR_FreeTextureCache(void) // texturecache info, we can free it if (gr_textures) free(gr_textures); + if (gr_textures2) + free(gr_textures2); gr_textures = NULL; + gr_textures2 = NULL; gr_numtextures = 0; } @@ -817,6 +839,9 @@ void HWR_PrepLevelCache(size_t pnumtextures) gr_textures = calloc(pnumtextures, sizeof (*gr_textures)); if (gr_textures == NULL) I_Error("3D can't alloc gr_textures"); + gr_textures2 = calloc(pnumtextures, sizeof (*gr_textures2)); + if (gr_textures2 == NULL) + I_Error("3D can't alloc gr_textures2"); } void HWR_SetPalette(RGBA_t *palette) @@ -847,7 +872,7 @@ GLTexture_t *HWR_GetTexture(INT32 tex) GLTexture_t *grtex; #ifdef PARANOIA if ((unsigned)tex >= gr_numtextures) - I_Error(" HWR_GetTexture: tex >= numtextures\n"); + I_Error("HWR_GetTexture: tex >= numtextures\n"); #endif grtex = &gr_textures[tex]; @@ -862,15 +887,39 @@ GLTexture_t *HWR_GetTexture(INT32 tex) return grtex; } +// HWR_RenderPlane and HWR_RenderPolyObjectPlane need this to get the flat dimensions from a patch. +lumpnum_t gr_patchflat; + +static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) +{ + UINT8 *flat; + patch_t *patch = (patch_t *)W_CacheLumpNum(flatlumpnum, PU_STATIC); + size_t lumplength = W_LumpLength(flatlumpnum); + +#ifndef NO_PNG_LUMPS + if (R_IsLumpPNG((UINT8 *)patch, lumplength)) + patch = R_PNGToPatch((UINT8 *)patch, lumplength); +#endif + + grMipmap->width = (UINT16)SHORT(patch->width); + grMipmap->height = (UINT16)SHORT(patch->height); + + flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); + memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); + + R_PatchToFlat(patch, flat); +} static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { size_t size, pflatsize; // setup the texture info +#ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif grMipmap->grInfo.format = GR_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; @@ -900,15 +949,20 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) pflatsize = 64; break; } - grMipmap->width = (UINT16)pflatsize; - grMipmap->height = (UINT16)pflatsize; - // the flat raw data needn't be converted with palettized textures - W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), - PU_HWRCACHE, &grMipmap->grInfo.data)); + if (R_CheckIfPatch(flatlumpnum)) + HWR_LoadPatchFlat(grMipmap, flatlumpnum); + else + { + grMipmap->width = (UINT16)pflatsize; + grMipmap->height = (UINT16)pflatsize; + + // the flat raw data needn't be converted with palettized textures + W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), + PU_HWRCACHE, &grMipmap->grInfo.data)); + } } - // Download a Doom 'flat' to the hardware cache and make it ready for use void HWR_GetFlat(lumpnum_t flatlumpnum) { @@ -923,6 +977,52 @@ void HWR_GetFlat(lumpnum_t flatlumpnum) // The system-memory data can be purged now. Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED); + + gr_patchflat = 0; + if (R_CheckIfPatch(flatlumpnum)) + gr_patchflat = flatlumpnum; +} + +static void HWR_LoadTextureFlat(GLMipmap_t *grMipmap, INT32 texturenum) +{ + UINT8 *flat; + + // setup the texture info +#ifdef GLIDE_API_COMPATIBILITY + grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; + grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif + grMipmap->grInfo.format = GR_TEXFMT_P_8; + grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; + + grMipmap->width = (UINT16)textures[texturenum]->width; + grMipmap->height = (UINT16)textures[texturenum]->height; + + flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); + memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); + + R_TextureToFlat(texturenum, flat); +} + +void HWR_GetTextureFlat(INT32 texturenum) +{ + GLTexture_t *grtex; +#ifdef PARANOIA + if ((unsigned)texturenum >= gr_numtextures) + I_Error("HWR_GetTextureFlat: texturenum >= numtextures\n"); +#endif + if (texturenum == 0 || texturenum == -1) + return; + grtex = &gr_textures2[texturenum]; + + if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded) + HWR_LoadTextureFlat(&grtex->mipmap, texturenum); + + HWD.pfnSetTexture(&grtex->mipmap); + + // The system-memory data can be purged now. + Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED); } // diff --git a/src/hardware/hw_glide.h b/src/hardware/hw_glide.h index 2625d5864..bf91229ef 100644 --- a/src/hardware/hw_glide.h +++ b/src/hardware/hw_glide.h @@ -59,9 +59,11 @@ typedef FxI32 GrTextureFormat_t; typedef struct { +#ifdef GLIDE_API_COMPATIBILITY GrLOD_t smallLodLog2; GrLOD_t largeLodLog2; GrAspectRatio_t aspectRatioLog2; +#endif GrTextureFormat_t format; void *data; } GrTexInfo; diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index 9656e54e9..c7b06edfd 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -101,6 +101,7 @@ void HWR_FreeTextureCache(void); void HWR_FreeExtraSubsectors(void); void HWR_GetFlat(lumpnum_t flatlumpnum); +void HWR_GetTextureFlat(INT32 texturenum); GLTexture_t *HWR_GetTexture(INT32 tex); void HWR_GetPatch(GLPatch_t *gpatch); void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap); @@ -114,6 +115,8 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum); // -------- // hw_draw.c // -------- +extern lumpnum_t gr_patchflat; + extern float gr_patch_scalex; extern float gr_patch_scaley; diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index edfe328b8..1de20cad7 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -1225,9 +1225,11 @@ static void HWR_SetLight(void) lightmappatch.height = 128; lightmappatch.mipmap.width = 128; lightmappatch.mipmap.height = 128; +#ifdef GLIDE_API_COMPATIBILITY lightmappatch.mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_128; lightmappatch.mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif lightmappatch.mipmap.flags = 0; //TF_WRAPXY; // DEBUG: view the overdraw ! } HWD.pfnSetTexture(&lightmappatch.mipmap); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c6a8b16e5..07ae7ed2b 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -70,9 +70,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); #endif #ifdef SORTING -void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); #else static void HWR_Add3DWater(lumpnum_t lumpnum, extrasubsector_t *xsub, fixed_t fixedheight, @@ -522,7 +522,7 @@ static UINT8 HWR_FogBlockAlpha(INT32 light, UINT32 color) // Let's see if this c // HWR_RenderPlane : Render a floor or ceiling convex polygon // -----------------+ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, - FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) + FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, UINT8 alpha, boolean fogplane, extracolormap_t *planecolormap) { polyvertex_t * pv; float height; //constant y for all points on the convex flat polygon @@ -530,8 +530,9 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is INT32 nrPlaneVerts; //verts original define of convex flat polygon INT32 i; float flatxref,flatyref; - float fflatsize; + float fflatwidth, fflatheight; INT32 flatflag; + boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; @@ -540,6 +541,7 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is #ifdef ESLOPE pslope_t *slope = NULL; #endif + patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -580,9 +582,10 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is if (nrPlaneVerts < 3) //not even a triangle ? return; - if (nrPlaneVerts > (INT32)UINT16_MAX) // FIXME: exceeds plVerts size + // This check is so inconsistent between functions, it hurts. + if (nrPlaneVerts > INT16_MAX) // FIXME: exceeds plVerts size { - CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, UINT16_MAX); + CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, INT16_MAX); return; } @@ -599,38 +602,47 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is switch (len) { case 4194304: // 2048x2048 lump - fflatsize = 2048.0f; - flatflag = 2047; + fflatwidth = fflatheight = 2048.0f; break; case 1048576: // 1024x1024 lump - fflatsize = 1024.0f; - flatflag = 1023; + fflatwidth = fflatheight = 1024.0f; break; case 262144:// 512x512 lump - fflatsize = 512.0f; - flatflag = 511; + fflatwidth = fflatheight = 512.0f; break; case 65536: // 256x256 lump - fflatsize = 256.0f; - flatflag = 255; + fflatwidth = fflatheight = 256.0f; break; case 16384: // 128x128 lump - fflatsize = 128.0f; - flatflag = 127; + fflatwidth = fflatheight = 128.0f; break; case 1024: // 32x32 lump - fflatsize = 32.0f; - flatflag = 31; + fflatwidth = fflatheight = 32.0f; break; default: // 64x64 lump - fflatsize = 64.0f; - flatflag = 63; + fflatwidth = fflatheight = 64.0f; break; } + flatflag = ((INT32)fflatwidth)-1; + + if (texturenum != 0 && texturenum != -1) + { + fflatwidth = textures[texturenum]->width; + fflatheight = textures[texturenum]->height; + } + else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = SHORT(patch->width); + fflatheight = SHORT(patch->height); + } + else + texflat = false; + // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatsize); - flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatsize); + flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatwidth); + flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatheight); // transform v3d = planeVerts; @@ -639,14 +651,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; angle = FOFsector->floorpic_angle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; angle = FOFsector->ceilingpic_angle; } } @@ -654,14 +666,14 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; angle = gr_frontsector->floorpic_angle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; angle = gr_frontsector->ceilingpic_angle; } } @@ -680,17 +692,24 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is for (i = 0; i < nrPlaneVerts; i++,v3d++,pv++) { // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((pv->x / fflatsize) - flatxref + scrollx); - v3d->tow = (float)(-(pv->y / fflatsize) + flatyref + scrolly); - - //v3d->sow = (float)(pv->x / fflatsize); - //v3d->tow = (float)(pv->y / fflatsize); + if (texflat) + { + v3d->sow = (float)(pv->x / fflatwidth) + scrollx; + v3d->tow = -(float)(pv->y / fflatheight) + scrolly; + } + else + { + v3d->sow = (float)((pv->x / fflatwidth) - flatxref + scrollx); + v3d->tow = (float)(flatyref - (pv->y / fflatheight) + scrolly); + } // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); + if (texflat) + tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle)))); } @@ -3164,21 +3183,23 @@ static inline void HWR_AddPolyObjectSegs(void) #ifdef POLYOBJECTS_PLANES static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, - FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, + FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, INT32 texturenum, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { float height; //constant y for all points on the convex flat polygon FOutVector *v3d; INT32 i; float flatxref,flatyref; - float fflatsize; + float fflatwidth, fflatheight; INT32 flatflag; + boolean texflat = true; size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; fixed_t tempxsow, tempytow; size_t nrPlaneVerts; + patch_t *patch; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; @@ -3209,38 +3230,47 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, switch (len) { case 4194304: // 2048x2048 lump - fflatsize = 2048.0f; - flatflag = 2047; + fflatwidth = fflatheight = 2048.0f; break; case 1048576: // 1024x1024 lump - fflatsize = 1024.0f; - flatflag = 1023; + fflatwidth = fflatheight = 1024.0f; break; case 262144:// 512x512 lump - fflatsize = 512.0f; - flatflag = 511; + fflatwidth = fflatheight = 512.0f; break; case 65536: // 256x256 lump - fflatsize = 256.0f; - flatflag = 255; + fflatwidth = fflatheight = 256.0f; break; case 16384: // 128x128 lump - fflatsize = 128.0f; - flatflag = 127; + fflatwidth = fflatheight = 128.0f; break; case 1024: // 32x32 lump - fflatsize = 32.0f; - flatflag = 31; + fflatwidth = fflatheight = 32.0f; break; default: // 64x64 lump - fflatsize = 64.0f; - flatflag = 63; + fflatwidth = fflatheight = 64.0f; break; } + flatflag = ((INT32)fflatwidth)-1; + + if (texturenum != 0 && texturenum != -1) + { + fflatwidth = textures[texturenum]->width; + fflatheight = textures[texturenum]->height; + } + else if (gr_patchflat && R_CheckIfPatch(gr_patchflat)) // Just in case? + { + patch = (patch_t *)W_CacheLumpNum(gr_patchflat, PU_STATIC); + fflatwidth = SHORT(patch->width); + fflatheight = SHORT(patch->height); + } + else + texflat = false; + // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].x) & (~flatflag)) / fflatsize); - flatyref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].y) & (~flatflag)) / fflatsize); + flatxref = (float)((polysector->origVerts[0].x & (~flatflag)) / fflatwidth); + flatyref = (float)((polysector->origVerts[0].y & (~flatflag)) / fflatheight); // transform v3d = planeVerts; @@ -3249,14 +3279,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight; angle = FOFsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight; angle = FOFsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3264,14 +3294,14 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatheight; angle = gr_frontsector->floorpic_angle>>ANGLETOFINESHIFT; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize; - scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize; + scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatwidth; + scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatheight; angle = gr_frontsector->ceilingpic_angle>>ANGLETOFINESHIFT; } } @@ -3294,15 +3324,26 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++) { - // Hurdler: add scrolling texture on floor/ceiling - v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatsize) - flatxref + scrollx); // Go from the polysector's original vertex locations - v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatsize) + scrolly); // Means the flat is offset based on the original vertex locations + // Go from the polysector's original vertex locations + // Means the flat is offset based on the original vertex locations + if (texflat) + { + v3d->sow = (float)(FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) + scrollx; + v3d->tow = -(float)(FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly; + } + else + { + v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); + v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); + } // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle { tempxsow = FLOAT_TO_FIXED(v3d->sow); tempytow = FLOAT_TO_FIXED(v3d->tow); + if (texflat) + tempytow = -tempytow; v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); v3d->tow = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle)))); } @@ -3365,6 +3406,7 @@ static void HWR_AddPolyObjectPlanes(void) else { HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum); + HWR_GetTextureFlat(levelflats[polyobjsector->floorpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); @@ -3388,6 +3430,7 @@ static void HWR_AddPolyObjectPlanes(void) else { HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum); + HWR_GetTextureFlat(levelflats[polyobjsector->ceilingpic].texturenum); HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude, (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum, polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap)); @@ -3541,11 +3584,12 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->floorpic].lumpnum); + HWR_GetTextureFlat(levelflats[gr_frontsector->floorpic].texturenum); HWR_RenderPlane(gr_frontsector, &extrasubsectors[num], false, // Hack to make things continue to work around slopes. locFloorHeight == cullFloorHeight ? locFloorHeight : gr_frontsector->floorheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, NULL, 255, false, floorcolormap); + PF_Occlude, floorlightlevel, levelflats[gr_frontsector->floorpic].lumpnum, levelflats[gr_frontsector->floorpic].texturenum, NULL, 255, false, floorcolormap); } } else @@ -3563,11 +3607,12 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetFlat(levelflats[gr_frontsector->ceilingpic].lumpnum); + HWR_GetTextureFlat(levelflats[gr_frontsector->ceilingpic].texturenum); HWR_RenderPlane(NULL, &extrasubsectors[num], true, // Hack to make things continue to work around slopes. locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gr_frontsector->ceilingheight, // We now return you to your regularly scheduled rendering. - PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum,NULL, 255, false, ceilingcolormap); + PF_Occlude, ceilinglightlevel, levelflats[gr_frontsector->ceilingpic].lumpnum, levelflats[gr_frontsector->ceilingpic].texturenum, NULL, 255, false, ceilingcolormap); } } else @@ -3626,7 +3671,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, + HWR_AddTransparentFloor(0, 0, &extrasubsectors[num], false, *rover->bottomheight, @@ -3645,6 +3690,7 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->bottompic].lumpnum, + levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], false, *rover->bottomheight, @@ -3656,8 +3702,9 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->bottompic].lumpnum); + HWR_GetTextureFlat(levelflats[*rover->bottompic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, + HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum, levelflats[*rover->bottompic].texturenum, rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } @@ -3689,7 +3736,7 @@ static void HWR_Subsector(size_t num) else alpha = HWR_FogBlockAlpha(*gr_frontsector->lightlist[light].lightlevel, NORMALFOG); - HWR_AddTransparentFloor(0, + HWR_AddTransparentFloor(0, 0, &extrasubsectors[num], true, *rover->topheight, @@ -3708,6 +3755,7 @@ static void HWR_Subsector(size_t num) rover->alpha-1, rover->master->frontsector); #else HWR_AddTransparentFloor(levelflats[*rover->toppic].lumpnum, + levelflats[*rover->bottompic].texturenum, &extrasubsectors[num], true, *rover->topheight, @@ -3720,8 +3768,9 @@ static void HWR_Subsector(size_t num) else { HWR_GetFlat(levelflats[*rover->toppic].lumpnum); + HWR_GetTextureFlat(levelflats[*rover->toppic].texturenum); light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); - HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, + HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum, levelflats[*rover->toppic].texturenum, rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap); } } @@ -5050,6 +5099,7 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; + INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5068,6 +5118,7 @@ typedef struct fixed_t fixedheight; INT32 lightlevel; lumpnum_t lumpnum; + INT32 texturenum; INT32 alpha; sector_t *FOFSector; FBITFIELD blend; @@ -5098,7 +5149,7 @@ static INT32 drawcount = 0; #define MAX_TRANSPARENTFLOOR 512 // This will likely turn into a copy of HWR_Add3DWater and replace it. -void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean isceiling, +void HWR_AddTransparentFloor(lumpnum_t lumpnum, INT32 texturenum, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) { static size_t allocedplanes = 0; @@ -5117,6 +5168,7 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean planeinfo[numplanes].fixedheight = fixedheight; planeinfo[numplanes].lightlevel = lightlevel; planeinfo[numplanes].lumpnum = lumpnum; + planeinfo[numplanes].texturenum = texturenum; planeinfo[numplanes].xsub = xsub; planeinfo[numplanes].alpha = alpha; planeinfo[numplanes].FOFSector = FOFSector; @@ -5130,7 +5182,7 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, boolean // Adding this for now until I can create extrasubsector info for polyobjects // When that happens it'll just be done through HWR_AddTransparentFloor and HWR_RenderPlane -void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, boolean isceiling, +void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, INT32 texturenum, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap) { static size_t allocedpolyplanes = 0; @@ -5149,6 +5201,7 @@ void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, polyplaneinfo[numpolyplanes].fixedheight = fixedheight; polyplaneinfo[numpolyplanes].lightlevel = lightlevel; polyplaneinfo[numpolyplanes].lumpnum = lumpnum; + polyplaneinfo[numpolyplanes].texturenum = texturenum; polyplaneinfo[numpolyplanes].polysector = polysector; polyplaneinfo[numpolyplanes].alpha = alpha; polyplaneinfo[numpolyplanes].FOFSector = FOFSector; @@ -5310,9 +5363,12 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].plane->blend & PF_NoTexture)) + { HWR_GetFlat(sortnode[sortindex[i]].plane->lumpnum); + HWR_GetTextureFlat(sortnode[sortindex[i]].plane->texturenum); + } HWR_RenderPlane(NULL, sortnode[sortindex[i]].plane->xsub, sortnode[sortindex[i]].plane->isceiling, sortnode[sortindex[i]].plane->fixedheight, sortnode[sortindex[i]].plane->blend, sortnode[sortindex[i]].plane->lightlevel, - sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); + sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->texturenum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap); } else if (sortnode[sortindex[i]].polyplane) { @@ -5320,9 +5376,12 @@ static void HWR_CreateDrawNodes(void) gr_frontsector = NULL; if (!(sortnode[sortindex[i]].polyplane->blend & PF_NoTexture)) + { HWR_GetFlat(sortnode[sortindex[i]].polyplane->lumpnum); + HWR_GetTextureFlat(sortnode[sortindex[i]].polyplane->texturenum); + } HWR_RenderPolyObjectPlane(sortnode[sortindex[i]].polyplane->polysector, sortnode[sortindex[i]].polyplane->isceiling, sortnode[sortindex[i]].polyplane->fixedheight, sortnode[sortindex[i]].polyplane->blend, sortnode[sortindex[i]].polyplane->lightlevel, - sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); + sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->texturenum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap); } else if (sortnode[sortindex[i]].wall) { diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index d4728315a..7b6367cf3 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -747,10 +747,12 @@ static void md2_loadTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; +#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif } HWD.pfnSetTexture(&grpatch->mipmap); HWR_UnlockCachedPatch(grpatch); @@ -798,10 +800,12 @@ static void md2_loadBlendTexture(md2_t *model) grpatch->mipmap.width = (UINT16)w; grpatch->mipmap.height = (UINT16)h; +#ifdef GLIDE_API_COMPATIBILITY // not correct! grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256; grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif } HWD.pfnSetTexture(&grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary HWR_UnlockCachedPatch(grpatch); diff --git a/src/p_setup.c b/src/p_setup.c index 65335be3f..60e036a87 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -573,6 +573,11 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); + levelflat->texturenum = R_CheckTextureNumForName(flatname); + levelflat->lasttexturenum = levelflat->texturenum; + + levelflat->baselumpnum = LUMPERROR; + levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); @@ -617,6 +622,11 @@ INT32 P_AddLevelFlatRuntime(const char *flatname) // store the flat lump number levelflat->lumpnum = R_GetFlatNumForName(flatname); + levelflat->texturenum = R_CheckTextureNumForName(flatname); + levelflat->lasttexturenum = levelflat->texturenum; + + levelflat->baselumpnum = LUMPERROR; + levelflat->basetexturenum = -1; #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); diff --git a/src/p_setup.h b/src/p_setup.h index 7e8a5d7e6..7e3a149eb 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -37,12 +37,19 @@ typedef struct { char name[9]; // resource name from wad lumpnum_t lumpnum; // lump number of the flat + INT32 texturenum, lasttexturenum; // texture number of the flat + UINT16 width, height; + fixed_t topoffset, leftoffset; // for flat animation lumpnum_t baselumpnum; + INT32 basetexturenum; INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; + + // for patchflats + UINT8 *flatpatch; } levelflat_t; extern size_t numlevelflats; diff --git a/src/p_spec.c b/src/p_spec.c index 7742554cd..256ca3453 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -205,8 +205,8 @@ void P_InitPicAnims(void) if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR) continue; - lastanim->picnum = R_FlatNumForName(animdefs[i].endname); - lastanim->basepic = R_FlatNumForName(animdefs[i].startname); + lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname); + lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname); } lastanim->istexture = animdefs[i].istexture; @@ -464,7 +464,19 @@ static inline void P_FindAnimatedFlat(INT32 animnum) for (i = 0; i < numlevelflats; i++, foundflats++) { // is that levelflat from the flat anim sequence ? - if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) + if ((anims[animnum].istexture) && (foundflats->texturenum != 0 && foundflats->texturenum != -1) + && ((UINT16)foundflats->texturenum >= startflatnum && (UINT16)foundflats->texturenum <= endflatnum)) + { + foundflats->basetexturenum = startflatnum; + foundflats->animseq = foundflats->texturenum - startflatnum; + foundflats->numpics = endflatnum - startflatnum + 1; + foundflats->speed = anims[animnum].speed; + + CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n", + atoi(sizeu1(i)), foundflats->name, foundflats->animseq, + foundflats->numpics,foundflats->speed); + } + else if (foundflats->lumpnum >= startflatnum && foundflats->lumpnum <= endflatnum) { foundflats->baselumpnum = startflatnum; foundflats->animseq = foundflats->lumpnum - startflatnum; @@ -488,10 +500,7 @@ void P_SetupLevelFlatAnims(void) // the original game flat anim sequences for (i = 0; anims[i].istexture != -1; i++) - { - if (!anims[i].istexture) - P_FindAnimatedFlat(i); - } + P_FindAnimatedFlat(i); } // @@ -5669,9 +5678,12 @@ void P_UpdateSpecials(void) { if (foundflats->speed) // it is an animated flat { + // update the levelflat texture number + if (foundflats->basetexturenum != -1) + foundflats->texturenum = foundflats->basetexturenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); // update the levelflat lump number - foundflats->lumpnum = foundflats->baselumpnum + - ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); + else if (foundflats->baselumpnum != LUMPERROR) + foundflats->lumpnum = foundflats->baselumpnum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); } } } diff --git a/src/r_data.c b/src/r_data.c index 6889bddde..5858117a5 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -40,6 +40,28 @@ #include #endif +#ifdef HAVE_PNG + +#ifndef _MSC_VER +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#endif + +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE +#endif + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif + +#include "png.h" +#ifndef PNG_READ_SUPPORTED +#undef HAVE_PNG +#endif +#endif + // // Texture definition. // Each texture is composed of one or more patches, @@ -98,12 +120,11 @@ 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 -// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask -INT32 *texturewidthmask; - +INT32 *texturewidth; fixed_t *textureheight; // needed for texture pegging INT32 *texturetranslation; @@ -315,7 +336,7 @@ static inline void R_DrawTransFlippedColumnInCache(column_t *patch, UINT8 *cache // 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, dynamic ligthing, & speed. +// 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. @@ -332,6 +353,10 @@ static UINT8 *R_GenerateTexture(size_t texnum) column_t *patchcol; UINT32 *colofs; + UINT16 wadnum; + lumpnum_t lumpnum; + size_t lumplength; + I_Assert(texnum <= (size_t)numtextures); texture = textures[texnum]; I_Assert(texture != NULL); @@ -346,7 +371,19 @@ static UINT8 *R_GenerateTexture(size_t texnum) { boolean holey = false; patch = texture->patches; - realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); + + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + +#ifndef NO_PNG_LUMPS + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + goto multipatch; + } +#endif // Check the patch for holes. if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) @@ -376,7 +413,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) { texture->holes = true; texture->flip = patch->flip; - blocksize = W_LumpLengthPwad(patch->wad, patch->lump); + blocksize = lumplength; block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function &texturecache[texnum]); M_Memcpy(block, realpatch, blocksize); @@ -403,6 +440,9 @@ static UINT8 *R_GenerateTexture(size_t texnum) } // multi-patch textures (or 'composite') +#ifndef NO_PNG_LUMPS + multipatch: +#endif texture->holes = false; texture->flip = 0; blocksize = (texture->width * 4) + (texture->width * texture->height); @@ -433,7 +473,15 @@ static UINT8 *R_GenerateTexture(size_t texnum) ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; } - realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS + if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); +#endif + x1 = patch->originx; width = SHORT(realpatch->width); height = SHORT(realpatch->height); @@ -509,10 +557,14 @@ void R_CheckTextureCache(INT32 tex) 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); - col &= texturewidthmask[tex]; data = texturecache[tex]; - if (!data) data = R_GenerateTexture(tex); @@ -550,7 +602,7 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); #define TX_END "TX_END" void R_LoadTextures(void) { - INT32 i, k, w; + INT32 i, w; UINT16 j; UINT16 texstart, texend, texturesLumpPos; patch_t *patchlump; @@ -567,6 +619,7 @@ void R_LoadTextures(void) } Z_Free(texturetranslation); Z_Free(textures); + Z_Free(texflats); } // Load patches and textures. @@ -627,15 +680,16 @@ void R_LoadTextures(void) // 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 mask table. - texturewidthmask = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height mask table. - textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); + 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); @@ -673,20 +727,39 @@ void R_LoadTextures(void) // 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; + size_t lumplength; + if (wadfiles[w]->type == RET_PK3) { - if (W_IsLumpFolder((UINT16)w, texstart + j)) // Check if lump is a folder + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder continue; // If it is then SKIP IT } - patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE); + + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); //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((UINT16)w, texstart + j), sizeof(texture->name)); - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); + 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->patchcount = 1; texture->holes = false; texture->flip = 0; @@ -701,11 +774,7 @@ void R_LoadTextures(void) Z_Unlock(patchlump); - k = 1; - while (k << 1 <= texture->width) - k <<= 1; - - texturewidthmask[i] = k - 1; + texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; i++; } @@ -1097,7 +1166,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE")==0) + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) { numTexturesInLump++; Z_Free(texturesToken); @@ -1105,7 +1174,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) } else { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\", got \"%s\"",texturesToken); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } @@ -1146,21 +1215,21 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) texturesToken = M_GetToken(texturesText); while (texturesToken != NULL) { - if (stricmp(texturesToken, "WALLTEXTURE")==0) + 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; - texturewidthmask[*texindex] = newTexture->width - 1; + 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\", got \"%s\"",texturesToken); + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); } texturesToken = M_GetToken(NULL); } @@ -1267,6 +1336,41 @@ lumpnum_t R_GetFlatNumForName(const char *name) lump = LUMPERROR; } + // Detect textures + if (lump == LUMPERROR) + { + // Scan wad files backwards so patched textures take preference. + for (i = numwadfiles - 1; i >= 0; i--) + { + switch (wadfiles[i]->type) + { + case RET_WAD: + if ((start = W_CheckNumForNamePwad("TX_START", (UINT16)i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForNamePwad("TX_END", (UINT16)i, start)) == INT16_MAX) + continue; + break; + case RET_PK3: + if ((start = W_CheckNumForFolderStartPK3("Textures/", i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForFolderEndPK3("Textures/", 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; + } + } + if (lump == LUMPERROR) { if (strcmp(name, SKYFLATNAME)) @@ -1615,7 +1719,6 @@ extracolormap_t *R_ColormapForName(char *name) // static double deltas[256][3], map[256][3]; -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); static int RoundUp(double number); lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) @@ -2027,7 +2130,7 @@ extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *ex // Thanks to quake2 source! // utils3/qdata/images.c -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; @@ -2306,3 +2409,479 @@ void R_PrecacheLevel(void) "texturememory: %s k\n" "spritememory: %s k\n", sizeu1(flatmemory>>10), sizeu2(texturememory>>10), sizeu3(spritememory>>10)); } + +// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350 +boolean R_CheckIfPatch(lumpnum_t lump) +{ + size_t size; + INT16 width, height; + patch_t *patch; + boolean result; + + size = W_LumpLength(lump); + + // 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)); + + if (result) + { + // The dimensions seem like they might be valid for a patch, so + // check the column directory for extra security. All columns + // must begin after the column directory, and none of them must + // point past the end of the patch. + INT16 x; + + for (x = 0; x < width; x++) + { + UINT32 ofs = LONG(patch->columnofs[x]); + + // Need one byte for an empty column (but there's patches that don't know that!) + if (ofs < (UINT32)width * 4 + 8 || ofs >= (UINT32)size) + { + result = false; + break; + } + } + } + + return result; +} + +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); + } + } +} + +#ifndef NO_PNG_LUMPS +boolean R_IsLumpPNG(UINT8 *d, size_t s) +{ + if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ + return false; + // Check for PNG file signature using memcmp + // As it may be faster on CPUs with slow unaligned memory access + // Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature + return (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0); +} + +#ifdef HAVE_PNG +typedef struct { + png_bytep buffer; + png_uint_32 bufsize; + png_uint_32 current_pos; +} png_ioread; + +static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_ioread *f = png_get_io_ptr(png_ptr); + if (length > (f->bufsize - f->current_pos)) + png_error(png_ptr, "PNG_IOReader: buffer overrun"); + memcpy(data, f->buffer + f->current_pos, length); + f->current_pos += length; +} + +static void PNG_error(png_structp PNG, png_const_charp pngtext) +{ + CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext); + //I_Error("libpng error at %p: %s", PNG, pngtext); +} + +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(UINT8 *png, UINT16 *w, UINT16 *h, 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; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + png_ioread png_io; + png_bytep *row_pointers; + + 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; + } + + 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; + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + 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; + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // set our own read_function + png_io.buffer = (png_bytep)png; + png_io.bufsize = size; + png_io.current_pos = 0; + png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + +#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, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + 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); + + if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + else if (color_type != PNG_COLOR_TYPE_RGB_ALPHA && color_type != PNG_COLOR_TYPE_GRAY_ALPHA) + { +#if PNG_LIBPNG_VER < 10207 + png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); +#else + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); +#endif + } + + png_read_update_info(png_ptr, png_info_ptr); + + // Read the image + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + for (y = 0; y < height; y++) + row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, png_info_ptr)); + png_read_image(png_ptr, row_pointers); + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + + *w = (INT32)width; + *h = (INT32)height; + return row_pointers; +} + +// Convert a PNG to a raw image. +static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) +{ + UINT8 *flat; + png_uint_32 x, y; + png_bytep *row_pointers = PNG_Read(png, w, h, size); + png_uint_32 width = *w, height = *h; + + if (!row_pointers) + I_Error("PNG_RawConvert: conversion failed"); + + // Convert the image to 8bpp + flat = Z_Malloc(width * height, PU_LEVEL, NULL); + memset(flat, TRANSPARENTPIXEL, width * height); + 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; +} + +// Convert a PNG to a flat. +UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) +{ + return PNG_RawConvert(png, &levelflat->width, &levelflat->height, size); +} + +// Convert a PNG to a patch. +static unsigned char imgbuf[1<<26]; +patch_t *R_PNGToPatch(UINT8 *png, size_t size) +{ + UINT16 width, height; + UINT8 *raw = PNG_RawConvert(png, &width, &height, size); + + UINT32 x, y; + UINT8 *img; + UINT8 *imgptr = imgbuf; + UINT8 *colpointers, *startofspan; + + #define WRITE8(buf, a) ({*buf = (a); buf++;}) + #define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;}) + #define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);}) + + if (!raw) + I_Error("R_PNGToPatch: conversion failed"); + + // Write image size and offset + WRITE16(imgptr, width); + WRITE16(imgptr, height); + // no offsets + WRITE16(imgptr, 0); + WRITE16(imgptr, 0); + + // 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; + + //printf("%d ", x); + // Write column pointer (@TODO may be wrong) + WRITE32(colpointers, imgptr - imgbuf); + + // Write pixels + for (y = 0; y < height; y++) + { + UINT8 paletteIndex = raw[((y * width) + x)]; + + // 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) + WRITE8(imgptr, 0); + + if (y > 254) + { + // Make sure we're aligned to 254 + if (lastStartY < 254) + { + WRITE8(imgptr, 254); + WRITE8(imgptr, 0); + imgptr += 2; + lastStartY = 254; + } + + // Write stopgap empty spans if needed + writeY = y - lastStartY; + + while (writeY > 254) + { + WRITE8(imgptr, 254); + WRITE8(imgptr, 0); + imgptr += 2; + writeY -= 254; + } + } + + startofspan = imgptr; + WRITE8(imgptr, writeY);///@TODO calculate starting y pos + imgptr += 2; + spanSize = 0; + + lastStartY = y; + } + + // Write the pixel + WRITE8(imgptr, paletteIndex); + spanSize++; + startofspan[1] = spanSize; + } + + if (startofspan) + WRITE8(imgptr, 0); + + WRITE8(imgptr, 0xFF); + } + + #undef WRITE8 + #undef WRITE16 + #undef WRITE32 + + size = imgptr-imgbuf; + img = malloc(size); + memcpy(img, imgbuf, size); + + Z_Free(raw); + + return (patch_t *)img; +} + +boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) +{ + png_structp png_ptr; + png_infop png_info_ptr; + png_uint_32 w, h; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + png_ioread png_io; + + 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; + } + + 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; + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + 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; + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // set our own read_function + png_io.buffer = (png_bytep)png; + png_io.bufsize = size; + png_io.current_pos = 0; + png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + +#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); + + // okay done. stop. + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + + *width = (INT32)w; + *height = (INT32)h; + return true; +} +#endif +#endif + +void R_TextureToFlat(size_t tex, UINT8 *flat) +{ + texture_t *texture = textures[tex]; + + fixed_t col, ofs; + column_t *column; + UINT8 *desttop, *dest, *deststop; + UINT8 *source; + + desttop = flat; + deststop = desttop + (texture->width * texture->height); + + for (col = 0; col < texture->width; col++, desttop++) + { + column = (column_t *)R_GetColumn(tex, col); + if (!texture->holes) + { + dest = desttop; + source = (UINT8 *)(column); + for (ofs = 0; dest < deststop && ofs < texture->height; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += texture->width; + } + } + else + { + INT32 topdelta, prevdelta = -1; + while (column->topdelta != 0xff) + { + topdelta = column->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + + dest = desttop + (topdelta * texture->width); + source = (UINT8 *)(column) + 3; + for (ofs = 0; dest < deststop && ofs < column->length; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += texture->width; + } + column = (column_t *)((UINT8 *)column + column->length + 4); + } + } + } +} diff --git a/src/r_data.h b/src/r_data.h index b6b0a16a1..b29bf4557 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -16,6 +16,7 @@ #include "r_defs.h" #include "r_state.h" +#include "p_setup.h" // levelflats #ifdef __GNUG__ #pragma interface @@ -55,12 +56,17 @@ typedef struct 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; -// texture width is a power of 2, so it can easily repeat along sidedefs using a simple mask -extern INT32 *texturewidthmask; - +extern INT32 *texturewidth; extern fixed_t *textureheight; // needed for texture pegging extern INT16 color8to16[256]; // remap color index to highcolor @@ -88,7 +94,6 @@ void R_PrecacheLevel(void); // Floor/ceiling opaque texture tiles, // lookup by name. For animation? lumpnum_t R_GetFlatNumForName(const char *name); -#define R_FlatNumForName(x) R_GetFlatNumForName(x) // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. @@ -148,6 +153,20 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); #define R_PutRgbaRGB(r, g, b) (R_PutRgbaR(r) + R_PutRgbaG(g) + R_PutRgbaB(b)) #define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a)) +boolean R_CheckIfPatch(lumpnum_t lump); +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); + +void R_PatchToFlat(patch_t *patch, UINT8 *flat); +void R_TextureToFlat(size_t tex, UINT8 *flat); + +#ifndef NO_PNG_LUMPS +boolean R_IsLumpPNG(UINT8 *d, size_t s); + +UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size); +patch_t *R_PNGToPatch(UINT8 *png, size_t size); +boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); +#endif + extern INT32 numtextures; #endif diff --git a/src/r_draw.c b/src/r_draw.c index 396ed0344..1754403c4 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -99,6 +99,8 @@ INT32 dc_numlights = 0, dc_maxlights, dc_texheight; INT32 ds_y, ds_x1, ds_x2; lighttable_t *ds_colormap; fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; +UINT16 ds_flatwidth, ds_flatheight; +boolean ds_powersoftwo; UINT8 *ds_source; // start of a 64*64 tile image UINT8 *ds_transmap; // one of the translucency tables diff --git a/src/r_draw.h b/src/r_draw.h index 82498eb11..3c1429722 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -57,7 +57,9 @@ extern INT32 dc_texheight; extern INT32 ds_y, ds_x1, ds_x2; extern lighttable_t *ds_colormap; extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; -extern UINT8 *ds_source; // start of a 64*64 tile image +extern UINT16 ds_flatwidth, ds_flatheight; +extern boolean ds_powersoftwo; +extern UINT8 *ds_source; extern UINT8 *ds_transmap; #ifdef ESLOPE @@ -128,6 +130,8 @@ void R_FillBackScreen(void); void R_DrawViewBorder(void); #endif +#define TRANSPARENTPIXEL 255 + // ----------------- // 8bpp DRAWING CODE // ----------------- @@ -169,6 +173,13 @@ void R_DrawFogSpan_8(void); void R_DrawFogColumn_8(void); void R_DrawColumnShadowed_8(void); +#ifndef NOWATER +void R_DrawTranslucentWaterSpan_8(void); + +extern INT32 ds_bgofs; +extern INT32 ds_waterofs; +#endif + // ------------------ // 16bpp DRAWING CODE // ------------------ diff --git a/src/r_draw8.c b/src/r_draw8.c index 8a2d37fb3..77406f83c 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -105,8 +105,6 @@ void R_DrawColumn_8(void) } } -#define TRANSPARENTPIXEL 255 - void R_Draw2sMultiPatchColumn_8(void) { INT32 count; @@ -543,16 +541,19 @@ void R_DrawTranslatedColumn_8(void) */ void R_DrawSpan_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + size_t count = (ds_x2 - ds_x1 + 1); + + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -561,62 +562,88 @@ void R_DrawSpan_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; if (dest+8 > deststop) return; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - dest[1] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + // 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); - dest[2] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + x %= ds_flatwidth; + y %= ds_flatheight; - dest[3] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[4] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[5] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[6] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest[7] = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + *dest++ = colormap[source[((y * ds_flatwidth) + x)]]; + xposition += xstep; + yposition += ystep; + } } - while (count-- && dest <= deststop) + else { - *dest++ = colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]]; - xposition += xstep; - yposition += ystep; + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[1] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[2] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[3] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[4] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[5] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[6] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest[7] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } + while (count-- && dest <= deststop) + { + *dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]]; + xposition += xstep; + yposition += ystep; + } } } @@ -697,7 +724,24 @@ void R_DrawTiltedSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; iz += ds_sz.x; uz += ds_su.x; @@ -734,7 +778,24 @@ void R_DrawTiltedSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -750,7 +811,24 @@ void R_DrawTiltedSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; } else { @@ -771,7 +849,24 @@ void R_DrawTiltedSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = colormap[source[((y * ds_flatwidth) + x)]]; + } + else + *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; u += stepu; v += stepv; @@ -832,7 +927,24 @@ void R_DrawTiltedTranslucentSpan_8(void) v = (INT64)(vz*z) + viewy; colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; iz += ds_sz.x; uz += ds_su.x; @@ -869,7 +981,24 @@ void R_DrawTiltedTranslucentSpan_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -885,7 +1014,24 @@ void R_DrawTiltedTranslucentSpan_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); } else { @@ -906,7 +1052,24 @@ void R_DrawTiltedTranslucentSpan_8(void) for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); + } + else + *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; u += stepu; v += stepv; @@ -967,9 +1130,28 @@ void R_DrawTiltedSplat_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) *dest = colormap[val]; + dest++; iz += ds_sz.x; uz += ds_su.x; @@ -1006,7 +1188,24 @@ void R_DrawTiltedSplat_8(void) for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1024,7 +1223,24 @@ void R_DrawTiltedSplat_8(void) u = (INT64)(startu); v = (INT64)(startv); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); - val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; } @@ -1048,6 +1264,24 @@ void R_DrawTiltedSplat_8(void) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; + if (!ds_powersoftwo) + { + fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); + fixed_t y = (((fixed_t)v-viewy) >> 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; + + val = source[((y * ds_flatwidth) + x)]; + } + else + val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; if (val != TRANSPARENTPIXEL) *dest = colormap[val]; dest++; @@ -1065,17 +1299,21 @@ void R_DrawTiltedSplat_8(void) */ void R_DrawSplat_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + size_t count = (ds_x2 - ds_x1 + 1); UINT32 val; + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the // texture with two shifts, an OR and one AND. (see below) @@ -1083,99 +1321,125 @@ void R_DrawSplat_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - // - // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[0] = colormap[val]; - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[1] = colormap[val]; - xposition += xstep; - yposition += ystep; + // 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); - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[2] = colormap[val]; - xposition += xstep; - yposition += ystep; + x %= ds_flatwidth; + y %= ds_flatheight; - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[3] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[4] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[5] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[6] = colormap[val]; - xposition += xstep; - yposition += ystep; - - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - dest[7] = colormap[val]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = source[((y * ds_flatwidth) + x)]; + if (val != TRANSPARENTPIXEL) + *dest = colormap[val]; + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - val = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift); - val &= 4194303; - val = source[val]; - if (val != TRANSPARENTPIXEL) - *dest = colormap[val]; + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + // + // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[0] = colormap[val]; + xposition += xstep; + yposition += ystep; - dest++; - xposition += xstep; - yposition += ystep; + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[1] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[2] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[3] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[4] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[5] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[6] = colormap[val]; + xposition += xstep; + yposition += ystep; + + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + val &= 4194303; + val = source[val]; + if (val != TRANSPARENTPIXEL) + dest[7] = colormap[val]; + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } + while (count-- && dest <= deststop) + { + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + *dest = colormap[val]; + dest++; + xposition += xstep; + yposition += ystep; + } } } @@ -1184,16 +1448,20 @@ void R_DrawSplat_8 (void) */ void R_DrawTranslucentSplat_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; - UINT8 val; + size_t count = (ds_x2 - ds_x1 + 1); + UINT32 val; + + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -1202,79 +1470,107 @@ void R_DrawTranslucentSplat_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; + // 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); - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; + x %= ds_flatwidth; + y %= ds_flatheight; - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = source[((y * ds_flatwidth) + x)]; + if (val != TRANSPARENTPIXEL) + *dest = *(ds_transmap + (colormap[val] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]; - if (val != TRANSPARENTPIXEL) - *dest = *(ds_transmap + (colormap[val] << 8) + *dest); + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; - dest++; - xposition += xstep; - yposition += ystep; + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } + while (count-- && dest <= deststop) + { + val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]; + if (val != TRANSPARENTPIXEL) + *dest = *(ds_transmap + (colormap[val] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } } @@ -1283,15 +1579,20 @@ void R_DrawTranslucentSplat_8 (void) */ void R_DrawTranslucentSpan_8 (void) { - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; UINT8 *source; UINT8 *colormap; UINT8 *dest; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; - size_t count; + size_t count = (ds_x2 - ds_x1 + 1); + UINT32 val; + + xposition = ds_xfrac; yposition = ds_yfrac; + xstep = ds_xstep; ystep = ds_ystep; // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest // can be used for the fraction part. This allows calculation of the memory address in the @@ -1300,63 +1601,161 @@ void R_DrawTranslucentSpan_8 (void) // bit per power of two (obviously) // Ok, because I was able to eliminate the variable spot below, this function is now FASTER // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = ds_yfrac << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; - count = ds_x2 - ds_x1 + 1; - while (count >= 8) + if (!ds_powersoftwo) { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[0]); - xposition += xstep; - yposition += ystep; + while (count-- && dest <= deststop) + { + fixed_t x = (xposition >> FRACBITS); + fixed_t y = (yposition >> FRACBITS); - dest[1] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[1]); - xposition += xstep; - yposition += ystep; + // 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); - dest[2] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[2]); - xposition += xstep; - yposition += ystep; + x %= ds_flatwidth; + y %= ds_flatheight; - dest[3] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[3]); - xposition += xstep; - yposition += ystep; - - dest[4] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[4]); - xposition += xstep; - yposition += ystep; - - dest[5] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[5]); - xposition += xstep; - yposition += ystep; - - dest[6] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[6]); - xposition += xstep; - yposition += ystep; - - dest[7] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[7]); - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; + val = ((y * ds_flatwidth) + x); + *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } - while (count--) + else { - *dest = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + *dest); - dest++; - xposition += xstep; - yposition += ystep; + while (count >= 8) + { + // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't + // have the uber complicated math to calculate it now, so that was a memory write we didn't + // need! + dest[0] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[0]); + xposition += xstep; + yposition += ystep; + + dest[1] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[1]); + xposition += xstep; + yposition += ystep; + + dest[2] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[2]); + xposition += xstep; + yposition += ystep; + + dest[3] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[3]); + xposition += xstep; + yposition += ystep; + + dest[4] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[4]); + xposition += xstep; + yposition += ystep; + + dest[5] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[5]); + xposition += xstep; + yposition += ystep; + + dest[6] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[6]); + xposition += xstep; + yposition += ystep; + + dest[7] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[7]); + xposition += xstep; + yposition += ystep; + + dest += 8; + count -= 8; + } + while (count-- && dest <= deststop) + { + val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); + *dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest); + dest++; + xposition += xstep; + yposition += ystep; + } } } +#ifndef NOWATER +void R_DrawTranslucentWaterSpan_8(void) +{ + fixed_t xposition; + fixed_t yposition; + fixed_t xstep, ystep; + + UINT8 *source; + UINT8 *colormap; + UINT8 *dest; + UINT8 *dsrc; + const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; + + size_t count = (ds_x2 - ds_x1 + 1); + + xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs); + xstep = ds_xstep; ystep = ds_ystep; + + // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest + // can be used for the fraction part. This allows calculation of the memory address in the + // texture with two shifts, an OR and one AND. (see below) + // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one + // bit per power of two (obviously) + // Ok, because I was able to eliminate the variable spot below, this function is now FASTER + // than the original span renderer. Whodathunkit? + if (ds_powersoftwo) + { + xposition <<= nflatshiftup; yposition <<= nflatshiftup; + xstep <<= nflatshiftup; ystep <<= nflatshiftup; + } + + source = ds_source; + colormap = ds_colormap; + dest = ylookup[ds_y] + columnofs[ds_x1]; + dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; + + if (!ds_powersoftwo) + { + 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; + + *dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + } + } + else + { + while (count-- && dest <= deststop) + { + *dest++ = colormap[*(ds_transmap + (source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)] << 8) + *dsrc++)]; + xposition += xstep; + yposition += ystep; + } + } +} +#endif + /** \brief The R_DrawFogSpan_8 function Draws the actual span with fogging. */ diff --git a/src/r_plane.c b/src/r_plane.c index 2f6f97240..de5bf9f00 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -127,91 +127,13 @@ void R_InitPlanes(void) // viewheight #ifndef NOWATER -static INT32 bgofs; +INT32 ds_bgofs; +INT32 ds_waterofs; + static INT32 wtofs=0; -static INT32 waterofs; static boolean itswater; #endif -#ifndef NOWATER -static void R_DrawTranslucentWaterSpan_8(void) -{ - UINT32 xposition; - UINT32 yposition; - UINT32 xstep, ystep; - - UINT8 *source; - UINT8 *colormap; - UINT8 *dest; - UINT8 *dsrc; - - size_t count; - - // SoM: we only need 6 bits for the integer part (0 thru 63) so the rest - // can be used for the fraction part. This allows calculation of the memory address in the - // texture with two shifts, an OR and one AND. (see below) - // for texture sizes > 64 the amount of precision we can allow will decrease, but only by one - // bit per power of two (obviously) - // Ok, because I was able to eliminate the variable spot below, this function is now FASTER - // than the original span renderer. Whodathunkit? - xposition = ds_xfrac << nflatshiftup; yposition = (ds_yfrac + waterofs) << nflatshiftup; - xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup; - - source = ds_source; - colormap = ds_colormap; - dest = ylookup[ds_y] + columnofs[ds_x1]; - dsrc = screens[1] + (ds_y+bgofs)*vid.width + ds_x1; - count = ds_x2 - ds_x1 + 1; - - while (count >= 8) - { - // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't - // have the uber complicated math to calculate it now, so that was a memory write we didn't - // need! - dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - - dest += 8; - count -= 8; - } - while (count--) - { - *dest++ = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)]; - xposition += xstep; - yposition += ystep; - } -} -#endif - void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; @@ -258,17 +180,17 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { const INT32 yay = (wtofs + (distance>>9) ) & 8191; // ripples da water texture - bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; + ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; angle = (angle + 2048) & 8191; // 90 degrees - ds_xfrac += FixedMul(FINECOSINE(angle), (bgofs<=viewheight) - bgofs = viewheight-y-1; - if (y+bgofs<0) - bgofs = -y; + if (y+ds_bgofs>=viewheight) + ds_bgofs = viewheight-y-1; + if (y+ds_bgofs<0) + ds_bgofs = -y; } #endif @@ -680,7 +602,7 @@ void R_DrawPlanes(void) } } #ifndef NOWATER - waterofs = (leveltime & 1)*16384; + ds_waterofs = (leveltime & 1)*16384; wtofs = leveltime * 140; #endif } @@ -728,13 +650,156 @@ static void R_DrawSkyPlane(visplane_t *pl) } } +boolean R_CheckPowersOfTwo(void) +{ + return (ds_powersoftwo = ((!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))) && (ds_flatwidth == ds_flatheight))); +} + +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; + } +} + +static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) +{ + UINT8 *flat; + textureflat_t *texflat = &texflats[levelflat->texturenum]; + patch_t *patch = NULL; + boolean texturechanged = (leveltexture ? (levelflat->texturenum != levelflat->lasttexturenum) : false); + + // 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) + { + if (leveltexture) + { + texture_t *texture = textures[levelflat->texturenum]; + texflat->width = ds_flatwidth = texture->width; + texflat->height = ds_flatheight = texture->height; + + texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight); + R_TextureToFlat(levelflat->texturenum, texflat->flat); + flat = texflat->flat; + + levelflat->flatpatch = flat; + levelflat->width = ds_flatwidth; + levelflat->height = ds_flatheight; + } + else + { + patch = (patch_t *)ds_source; +#ifndef NO_PNG_LUMPS + if (ispng) + { + levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->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 = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL); + memset(levelflat->flatpatch, TRANSPARENTPIXEL, 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->lasttexturenum = levelflat->texturenum; + return flat; +} + void R_DrawSinglePlane(visplane_t *pl) { + UINT8 *flat; INT32 light = 0; INT32 x; INT32 stop, angle; size_t size; ffloor_t *rover; + levelflat_t *levelflat; if (!(pl->minx <= pl->maxx)) return; @@ -874,64 +939,43 @@ void R_DrawSinglePlane(visplane_t *pl) viewangle = pl->viewangle+pl->plangle; } - currentplane = pl; - - ds_source = (UINT8 *) - W_CacheLumpNum(levelflats[pl->picnum].lumpnum, - PU_STATIC); // Stay here until Z_ChangeTag - - size = W_LumpLength(levelflats[pl->picnum].lumpnum); - - switch (size) - { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - break; - case 262144:// 512x512 lump' - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - break; - } - xoffs = pl->xoffs; yoffs = pl->yoffs; planeheight = abs(pl->height - pl->viewz); + currentplane = pl; + levelflat = &levelflats[pl->picnum]; + size = W_LumpLength(levelflat->lumpnum); + ds_source = (UINT8 *)W_CacheLumpNum(levelflat->lumpnum, PU_STATIC); // Stay here until Z_ChangeTag + + // Check if the flat is actually a wall texture. + if (levelflat->texturenum != 0 && levelflat->texturenum != -1) + flat = R_GetPatchFlat(levelflat, true, false); +#ifndef NO_PNG_LUMPS + // Maybe it's a PNG?! + else if (R_IsLumpPNG(ds_source, size)) + flat = R_GetPatchFlat(levelflat, false, true); +#endif + // Maybe it's just a patch, then? + else if (R_CheckIfPatch(levelflat->lumpnum)) + flat = R_GetPatchFlat(levelflat, false, false); + // It's a raw flat. + else + { + R_CheckFlatLength(size); + flat = ds_source; + } + + Z_ChangeTag(ds_source, PU_CACHE); + ds_source = flat; + + if (ds_source == NULL) + return; + + // Check if the flat has dimensions that are powers-of-two numbers. + if (R_CheckPowersOfTwo()) + R_CheckFlatLength(ds_flatwidth * ds_flatheight); + if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; @@ -945,60 +989,63 @@ void R_DrawSinglePlane(visplane_t *pl) floatv3_t p, m, n; float ang; float vx, vy, vz; + float fudge = 0; // compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly // use this as a temp var to store P_GetZAt's return value each time fixed_t temp; - // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red - const float fudge = ((1<plangle & (ANGLE_90-1)); yoffs *= 1; - if (hack) + if (ds_powersoftwo) { - /* - Essentially: We can't & the components along the regular axes when the plane is rotated. - This is because the distance on each regular axis in order to loop is different. - We rotate them, & the components, add them together, & them again, and then rotate them back. - These three seperate & operations are done per axis in order to prevent overflows. - toast 10/04/17 - */ - const fixed_t cosinecomponent = FINECOSINE(hack>>ANGLETOFINESHIFT); - const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); + // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red + fudge = ((1<>ANGLETOFINESHIFT); + const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); - const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); + const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); - fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); - fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); + fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); + fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); - temp = ox & modmask; - oy &= modmask; - ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction - oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); + temp = ox & modmask; + oy &= modmask; + ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction + oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); - temp = xoffs; - xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); - yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); + temp = xoffs; + xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); + yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); - temp = xoffs & modmask; - yoffs &= modmask; - xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto - yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); + temp = xoffs & modmask; + yoffs &= modmask; + xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto + yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); - xoffs -= (pl->slope->o.x - ox); - yoffs += (pl->slope->o.y + oy); + xoffs -= (pl->slope->o.x - ox); + yoffs += (pl->slope->o.y + oy); + } + else + { + xoffs &= ((1 << (32-nflatshiftup))-1); + yoffs &= ((1 << (32-nflatshiftup))-1); + xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); + } + xoffs = (fixed_t)(xoffs*fudge); + yoffs = (fixed_t)(yoffs/fudge); } - else - { - xoffs &= ((1 << (32-nflatshiftup))-1); - yoffs &= ((1 << (32-nflatshiftup))-1); - xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - } - - xoffs = (fixed_t)(xoffs*fudge); - yoffs = (fixed_t)(yoffs/fudge); vx = FIXED_TO_FLOAT(pl->viewx+xoffs); vy = FIXED_TO_FLOAT(pl->viewy-yoffs); @@ -1033,13 +1080,16 @@ void R_DrawSinglePlane(visplane_t *pl) temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang))); n.y = FIXED_TO_FLOAT(temp) - zeroheight; - m.x /= fudge; - m.y /= fudge; - m.z /= fudge; + if (ds_powersoftwo) + { + m.x /= fudge; + m.y /= fudge; + m.z /= fudge; - n.x *= fudge; - n.y *= fudge; - n.z *= fudge; + n.x *= fudge; + n.y *= fudge; + n.z *= fudge; + } // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. #define CROSS(d, v1, v2) \ @@ -1056,14 +1106,26 @@ void R_DrawSinglePlane(visplane_t *pl) ds_sz.z *= focallengthf; // Premultiply the texture vectors with the scale factors + if (ds_powersoftwo) + { #define SFMULT 65536.f*(1< Date: Tue, 10 Sep 2019 14:44:04 +0100 Subject: [PATCH 110/196] Update SPR2 defaulting for SPR2_CNT1. --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 04a9121b8..3bee08529 100644 --- a/src/info.c +++ b/src/info.c @@ -679,7 +679,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_TAL9, // SPR2_TALA, SPR2_TAL0, // SPR2_TALB, - 0, // SPR2_CNT1, + SPR2_WAIT, // SPR2_CNT1, SPR2_FALL, // SPR2_CNT2, SPR2_SPNG, // SPR2_CNT3, SPR2_CNT1, // SPR2_CNT4, From 25e975e781a60559df7e9cd743e9f1e502dcc4f7 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 10 Sep 2019 15:06:20 +0100 Subject: [PATCH 111/196] Add the new s1, s2, cd, 3db and kc sounds to sounds.c and sounds.h! These have been in srb2.pk3 for a while, but they were inaccessible because of not having in-game references. --- src/sounds.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/sounds.h | 301 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 619 insertions(+) diff --git a/src/sounds.c b/src/sounds.c index 11ba1e0bc..43225a615 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -35,6 +35,23 @@ sfxinfo_t S_sfx[NUMSFX] = // name, singularity, priority, pitch, volume, data, length, skinsound, usefulness, lumpnum, caption {"none" , false, 0, 0, -1, NULL, 0, -1, -1, LUMPERROR, "///////////////////////////////"}, // maximum length + // A HUMBLE REQUEST FROM YOUR FRIENDLY NEIGHBORHOOD toaster! + // + // If you see a caption that's just "" (shows the lumpname in-game), + // and you intend to use the sound associated with it in a mod, + // PLEASE give it a caption through SOC or Lua. + // + // If the first character of the caption is '/', no caption will be + // produced; only do this for "unimportant" sounds that aren't used + // to indicate gameplay. + // + // (to whomstever updates the sounds list wiki page for 2.2, please + // either copy this comment across, or make sure its desire is + // codified in the initial paragraph of the page.) + // + // Closed Captioning may be a niche feature, but it's an important one. + // Thank you! ^u^ + // Skin Sounds {"altdi1", false, 192, 16, -1, NULL, 0, SKSPLDET1, -1, LUMPERROR, "Dying"}, {"altdi2", false, 192, 16, -1, NULL, 0, SKSPLDET2, -1, LUMPERROR, "Dying"}, @@ -290,6 +307,139 @@ sfxinfo_t S_sfx[NUMSFX] = {"brakrl", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Rocket launch"}, {"brakrx", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Rocket explosion"}, + // Sonic 1 sounds + {"s1a0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1a9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1aa", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ab", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ac", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ad", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ae", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1af", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1b9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ba", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1bb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1bc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1bd", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1be", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1bf", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1c9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ca", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1cb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1cc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1cd", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1ce", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s1cf", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + + // Sonic 2 sounds + {"s220", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s221", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s222", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s223", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s224", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s225", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s226", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s227", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s228", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s229", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s22f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s230", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s231", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s232", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s233", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s234", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s235", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s236", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s237", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s238", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s239", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s23f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s240", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s241", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s242", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s243", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s244", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s245", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s246", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s247", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s248", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s249", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s24f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s250", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s251", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s252", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s253", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s254", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s255", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s256", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s257", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s258", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s259", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s25f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s260", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s261", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s262", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s263", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s264", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s265", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s266", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s267", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s268", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s269", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s26f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s270", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // S3&K sounds {"s3k33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // stereo in original game, identical to latter {"s3k34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // mono in original game, identical to previous @@ -492,6 +642,174 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3kdbs", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Running on water"}, {"s3kdbl", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Running on water"}, // ditto + // 3D Blast sounds (the "missing" ones are direct copies of S3K's, no minor differences what-so-ever) + {"3db06", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Collection"}, + {"3db09", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Peep"}, + {"3db14", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Chirp"}, + {"3db16", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + + // Sonic CD sounds + {"cdfm00", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Skid"}, + {"cdfm01", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm02", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Jump"}, + {"cdfm03", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Dying"}, + {"cdfm04", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ring loss"}, + {"cdfm05", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, + {"cdfm06", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pop"}, + {"cdfm07", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Shield"}, + {"cdfm08", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spring"}, + {"cdfm09", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm10", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm11", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm12", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm13", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm14", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm15", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm16", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm17", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm18", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm19", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm20", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm21", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm22", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm23", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm24", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm25", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm26", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm27", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm28", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm29", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bubble gasp"}, + {"cdfm30", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Starpost"}, + {"cdfm31", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Warp"}, + {"cdfm32", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm35", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm36", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm37", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm38", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drowning"}, + {"cdfm39", false, 128, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm40", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm42", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm43", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm44", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Extra time"}, + {"cdfm45", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm46", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm47", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aquaphobia"}, + {"cdfm50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm51", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm52", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm53", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm54", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm55", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm56", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Warp"}, + {"cdfm57", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm58", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm59", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm60", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm61", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm62", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm63", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm64", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm65", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm66", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm67", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm68", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm69", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm70", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm71", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm72", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm73", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm74", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm75", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm76", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm77", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm78", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm79", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdpcm0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Future."}, + {"cdpcm1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Past."}, + {"cdpcm2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "All right!"}, + {"cdpcm3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "I'm outta here..."}, + {"cdpcm4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Yes!"}, + {"cdpcm5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Yeah!"}, + {"cdpcm6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Giggles"}, + {"cdpcm7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Eep!"}, + {"cdpcm8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdpcm9", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Bumper"}, + + // Knuckles Chaotix sounds + {"kc2a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc2b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc2c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc2d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc2e", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc2f", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc30", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc31", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc32", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc35", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc36", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc37", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc38", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc39", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc3f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc40", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc42", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Confirm"}, + {"kc43", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc44", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc45", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc46", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc47", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Select"}, + {"kc49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc51", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc52", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc53", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc54", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc55", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc56", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc57", false, 128, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Sheer terror"}, + {"kc58", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc59", false, 128, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Shrink"}, + {"kc5a", false, 128, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Grow"}, + {"kc5b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc5c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc5d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc5e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc5f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc60", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc61", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc62", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc63", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc64", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Terrifying rumble"}, + {"kc65", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc66", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc67", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc68", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc69", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc6b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ascending"}, + {"kc6c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc6d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc6e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // skin sounds free slots to add sounds at run time (Boris HACK!!!) // initialized to NULL }; diff --git a/src/sounds.h b/src/sounds.h index 20f89d9fb..674f51d79 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -356,6 +356,139 @@ typedef enum sfx_brakrl, // Rocket launcher sfx_brakrx, // Rocket explodes + // S1 sounds + sfx_s1a0, + sfx_s1a1, + sfx_s1a2, + sfx_s1a3, + sfx_s1a4, + sfx_s1a5, + sfx_s1a6, + sfx_s1a7, + sfx_s1a8, + sfx_s1a9, + sfx_s1aa, + sfx_s1ab, + sfx_s1ac, + sfx_s1ad, + sfx_s1ae, + sfx_s1af, + sfx_s1b0, + sfx_s1b1, + sfx_s1b2, + sfx_s1b3, + sfx_s1b4, + sfx_s1b5, + sfx_s1b6, + sfx_s1b7, + sfx_s1b8, + sfx_s1b9, + sfx_s1ba, + sfx_s1bb, + sfx_s1bc, + sfx_s1bd, + sfx_s1be, + sfx_s1bf, + sfx_s1c0, + sfx_s1c1, + sfx_s1c2, + sfx_s1c3, + sfx_s1c4, + sfx_s1c5, + sfx_s1c6, + sfx_s1c7, + sfx_s1c8, + sfx_s1c9, + sfx_s1ca, + sfx_s1cb, + sfx_s1cc, + sfx_s1cd, + sfx_s1ce, + sfx_s1cf, + + // S2 sounds + sfx_s220, + sfx_s221, + sfx_s222, + sfx_s223, + sfx_s224, + sfx_s225, + sfx_s226, + sfx_s227, + sfx_s228, + sfx_s229, + sfx_s22a, + sfx_s22b, + sfx_s22c, + sfx_s22d, + sfx_s22e, + sfx_s22f, + sfx_s230, + sfx_s231, + sfx_s232, + sfx_s233, + sfx_s234, + sfx_s235, + sfx_s236, + sfx_s237, + sfx_s238, + sfx_s239, + sfx_s23a, + sfx_s23b, + sfx_s23c, + sfx_s23d, + sfx_s23e, + sfx_s23f, + sfx_s240, + sfx_s241, + sfx_s242, + sfx_s243, + sfx_s244, + sfx_s245, + sfx_s246, + sfx_s247, + sfx_s248, + sfx_s249, + sfx_s24a, + sfx_s24b, + sfx_s24c, + sfx_s24d, + sfx_s24e, + sfx_s24f, + sfx_s250, + sfx_s251, + sfx_s252, + sfx_s253, + sfx_s254, + sfx_s255, + sfx_s256, + sfx_s257, + sfx_s258, + sfx_s259, + sfx_s25a, + sfx_s25b, + sfx_s25c, + sfx_s25d, + sfx_s25e, + sfx_s25f, + sfx_s260, + sfx_s261, + sfx_s262, + sfx_s263, + sfx_s264, + sfx_s265, + sfx_s266, + sfx_s267, + sfx_s268, + sfx_s269, + sfx_s26a, + sfx_s26b, + sfx_s26c, + sfx_s26d, + sfx_s26e, + sfx_s26f, + sfx_s270, + // S3&K sounds sfx_s3k33, sfx_s3k34, @@ -558,6 +691,174 @@ typedef enum sfx_s3kdbs, sfx_s3kdbl, + // 3DB sounds + sfx_3db06, + sfx_3db09, + sfx_3db14, + sfx_3db16, + + // SCD sounds + sfx_cdfm00, + sfx_cdfm01, + sfx_cdfm02, + sfx_cdfm03, + sfx_cdfm04, + sfx_cdfm05, + sfx_cdfm06, + sfx_cdfm07, + sfx_cdfm08, + sfx_cdfm09, + sfx_cdfm10, + sfx_cdfm11, + sfx_cdfm12, + sfx_cdfm13, + sfx_cdfm14, + sfx_cdfm15, + sfx_cdfm16, + sfx_cdfm17, + sfx_cdfm18, + sfx_cdfm19, + sfx_cdfm20, + sfx_cdfm21, + sfx_cdfm22, + sfx_cdfm23, + sfx_cdfm24, + sfx_cdfm25, + sfx_cdfm26, + sfx_cdfm27, + sfx_cdfm28, + sfx_cdfm29, + sfx_cdfm30, + sfx_cdfm31, + sfx_cdfm32, + sfx_cdfm33, + sfx_cdfm34, + sfx_cdfm35, + sfx_cdfm36, + sfx_cdfm37, + sfx_cdfm38, + sfx_cdfm39, + sfx_cdfm40, + sfx_cdfm41, + sfx_cdfm42, + sfx_cdfm43, + sfx_cdfm44, + sfx_cdfm45, + sfx_cdfm46, + sfx_cdfm47, + sfx_cdfm48, + sfx_cdfm49, + sfx_cdfm50, + sfx_cdfm51, + sfx_cdfm52, + sfx_cdfm53, + sfx_cdfm54, + sfx_cdfm55, + sfx_cdfm56, + sfx_cdfm57, + sfx_cdfm58, + sfx_cdfm59, + sfx_cdfm60, + sfx_cdfm61, + sfx_cdfm62, + sfx_cdfm63, + sfx_cdfm64, + sfx_cdfm65, + sfx_cdfm66, + sfx_cdfm67, + sfx_cdfm68, + sfx_cdfm69, + sfx_cdfm70, + sfx_cdfm71, + sfx_cdfm72, + sfx_cdfm73, + sfx_cdfm74, + sfx_cdfm75, + sfx_cdfm76, + sfx_cdfm77, + sfx_cdfm78, + sfx_cdfm79, + sfx_cdpcm0, + sfx_cdpcm1, + sfx_cdpcm2, + sfx_cdpcm3, + sfx_cdpcm4, + sfx_cdpcm5, + sfx_cdpcm6, + sfx_cdpcm7, + sfx_cdpcm8, + sfx_cdpcm9, + + // KC sounds + sfx_kc2a, + sfx_kc2b, + sfx_kc2c, + sfx_kc2d, + sfx_kc2e, + sfx_kc2f, + sfx_kc30, + sfx_kc31, + sfx_kc32, + sfx_kc33, + sfx_kc34, + sfx_kc35, + sfx_kc36, + sfx_kc37, + sfx_kc38, + sfx_kc39, + sfx_kc3a, + sfx_kc3b, + sfx_kc3c, + sfx_kc3d, + sfx_kc3e, + sfx_kc3f, + sfx_kc40, + sfx_kc41, + sfx_kc42, + sfx_kc43, + sfx_kc44, + sfx_kc45, + sfx_kc46, + sfx_kc47, + sfx_kc48, + sfx_kc49, + sfx_kc4a, + sfx_kc4b, + sfx_kc4c, + sfx_kc4d, + sfx_kc4e, + sfx_kc4f, + sfx_kc50, + sfx_kc51, + sfx_kc52, + sfx_kc53, + sfx_kc54, + sfx_kc55, + sfx_kc56, + sfx_kc57, + sfx_kc58, + sfx_kc59, + sfx_kc5a, + sfx_kc5b, + sfx_kc5c, + sfx_kc5d, + sfx_kc5e, + sfx_kc5f, + sfx_kc60, + sfx_kc61, + sfx_kc62, + sfx_kc63, + sfx_kc64, + sfx_kc65, + sfx_kc66, + sfx_kc67, + sfx_kc68, + sfx_kc69, + sfx_kc6b, + sfx_kc6c, + sfx_kc6d, + sfx_kc6e, + // free slots for S_AddSoundFx() at run-time -------------------- sfx_freeslot0, // From 4be108fa76c36aa9c8934a6925bd3168cf7b887b Mon Sep 17 00:00:00 2001 From: James Date: Tue, 10 Sep 2019 10:11:03 -0400 Subject: [PATCH 112/196] Lach has blessed us with a remade cam_adjust. --- src/p_local.h | 4 ++-- src/p_user.c | 37 +++++++++++++++++++++++++++++++------ src/r_main.c | 2 ++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index c956310ef..bbece506b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -115,10 +115,10 @@ typedef struct camera_s extern camera_t camera, camera2; extern consvar_t cv_cam_dist, cv_cam_still, cv_cam_height; -extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed, cv_cam_orbit; +extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed, cv_cam_orbit, cv_cam_adjust; extern consvar_t cv_cam2_dist, cv_cam2_still, cv_cam2_height; -extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed, cv_cam2_orbit; +extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed, cv_cam2_orbit, cv_cam2_adjust; extern fixed_t t_cam_dist, t_cam_height, t_cam_rotate; extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate; diff --git a/src/p_user.c b/src/p_user.c index 97ab5894a..53e9704ff 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9295,6 +9295,7 @@ consvar_t cv_cam_speed = {"cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NUL 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_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cam_adjust = {"cam_adjust", "Off", CV_SAVE|CV_SHOWMODIF, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_dist = {"cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cam2_height = {"cam2_height", "25", CV_FLOAT|CV_SAVE, 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}; @@ -9302,6 +9303,7 @@ consvar_t cv_cam2_speed = {"cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, N 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_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_cam2_adjust = {"cam2_adjust", "Off", CV_SAVE|CV_SHOWMODIF, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; fixed_t t_cam_dist = -42; fixed_t t_cam_height = -42; @@ -9570,6 +9572,35 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (checkdist < 128*FRACUNIT) checkdist = 128*FRACUNIT; + if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) // This block here is like 90% Lach's work, thanks bud + { + if (!cameranoclip && !resetcalled) + { + if ((P_AproxDistance(mo->x-thiscam->x-thiscam->momx, mo->y-thiscam->y-thiscam->momy) > FixedDiv(mo->momz+dist, camspeed)) || (abs(thiscam->z - player->mo->z) > (FixedDiv(mo->momz+dist, camspeed)))) //If the camera is too far away, + { + P_ResetCamera(player, thiscam); //Reset the camera + return true; + } + } + if ((thiscam == &camera && cv_cam_adjust.value) || (thiscam == &camera2 && cv_cam2_adjust.value)) + { + if (!(mo->eflags & MFE_JUSTHITFLOOR) && (P_IsObjectOnGround(mo)) // Check that player is grounded + && thiscam->ceilingz - thiscam->floorz >= P_GetPlayerHeight(player)) // Check that camera's sector is large enough for the player to fit into, at least + { + if (mo->eflags & MFE_VERTICALFLIP) // if player is upside-down + { + //z = min(z, thiscam->ceilingz); // solution 1: change new z coordinate to be at LEAST its ground height + z += min(thiscam->ceilingz - mo->z, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player + } + else // player is not upside-down + { + //z = max(z, thiscam->floorz); // solution 1: change new z coordinate to be at LEAST its ground height + z += max(thiscam->floorz - mo->z - mo->height, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player + } + } + } + } + if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) //Sev here, I'm guessing this is where orbital cam lives { distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); @@ -9584,12 +9615,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); - if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE) && !(player->exiting)) //Which is why I'm slapping my cam reset code in here too - { - if ((P_AproxDistance(player->mo->x-thiscam->x-thiscam->momx, player->mo->y-thiscam->y-thiscam->momy) > ((player->speed+camdist)*2)) || (abs(thiscam->z - player->mo->z) > ((player->speed+camdist)*2))) - P_ResetCamera(player, thiscam); - } - #if 0 if (twodlevel || (mo->flags2 & MF2_TWOD)) { diff --git a/src/r_main.c b/src/r_main.c index eabfa0384..9cce41288 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1183,6 +1183,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_cam_rotate); CV_RegisterVar(&cv_cam_rotspeed); CV_RegisterVar(&cv_cam_orbit); + CV_RegisterVar(&cv_cam_adjust); CV_RegisterVar(&cv_cam2_dist); CV_RegisterVar(&cv_cam2_still); @@ -1191,6 +1192,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_cam2_rotate); CV_RegisterVar(&cv_cam2_rotspeed); CV_RegisterVar(&cv_cam2_orbit); + CV_RegisterVar(&cv_cam2_adjust); CV_RegisterVar(&cv_showhud); CV_RegisterVar(&cv_translucenthud); From f1cc17ea02abdb2a2a44801c922695c7ff9400f5 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 10 Sep 2019 17:25:21 -0300 Subject: [PATCH 113/196] Enable usage of R_DrawSpan_8_MMX if drawing a flat with powers-of-two dimensions --- src/r_plane.c | 12 +++++++++++- src/screen.c | 5 +++-- src/screen.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index de5bf9f00..51a69336e 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -652,7 +652,13 @@ static void R_DrawSkyPlane(visplane_t *pl) boolean R_CheckPowersOfTwo(void) { - return (ds_powersoftwo = ((!((ds_flatwidth & (ds_flatwidth - 1)) || (ds_flatheight & (ds_flatheight - 1)))) && (ds_flatwidth == ds_flatheight))); + if (ds_flatwidth & (ds_flatwidth - 1)) + ds_powersoftwo = false; + else if (ds_flatheight & (ds_flatheight - 1)) + ds_powersoftwo = false; + else if (ds_flatwidth == ds_flatheight) + ds_powersoftwo = true; + return ds_powersoftwo; } void R_CheckFlatLength(size_t size) @@ -974,7 +980,11 @@ void R_DrawSinglePlane(visplane_t *pl) // Check if the flat has dimensions that are powers-of-two numbers. if (R_CheckPowersOfTwo()) + { R_CheckFlatLength(ds_flatwidth * ds_flatheight); + if (spanfunc == basespanfunc) + spanfunc = mmxspanfunc; + } if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; diff --git a/src/screen.c b/src/screen.c index 4bace5239..547036a60 100644 --- a/src/screen.c +++ b/src/screen.c @@ -49,6 +49,7 @@ void (*fuzzcolfunc)(void); // standard fuzzy effect column drawer void (*transcolfunc)(void); // translation column drawer void (*shadecolfunc)(void); // smokie test.. void (*spanfunc)(void); // span drawer, use a 64x64 tile +void (*mmxspanfunc)(void); // span drawer in MMX assembly void (*splatfunc)(void); // span drawer w/ transparency void (*basespanfunc)(void); // default span func for color mode void (*transtransfunc)(void); // translucent translated column drawer @@ -112,7 +113,7 @@ void SCR_SetMode(void) // if (true)//vid.bpp == 1) //Always run in 8bpp. todo: remove all 16bpp code? { - spanfunc = basespanfunc = R_DrawSpan_8; + spanfunc = basespanfunc = mmxspanfunc = R_DrawSpan_8; splatfunc = R_DrawSplat_8; transcolfunc = R_DrawTranslatedColumn_8; transtransfunc = R_DrawTranslatedTranslucentColumn_8; @@ -133,7 +134,7 @@ void SCR_SetMode(void) //fuzzcolfunc = R_DrawTranslucentColumn_8_ASM; walldrawerfunc = R_DrawWallColumn_8_MMX; twosmultipatchfunc = R_Draw2sMultiPatchColumn_8_MMX; - //spanfunc = basespanfunc = R_DrawSpan_8_MMX; + mmxspanfunc = R_DrawSpan_8_MMX; } else { diff --git a/src/screen.h b/src/screen.h index 7aa6fdb63..3554b5520 100644 --- a/src/screen.h +++ b/src/screen.h @@ -123,6 +123,7 @@ extern void (*transcolfunc)(void); extern void (*shadecolfunc)(void); extern void (*spanfunc)(void); extern void (*basespanfunc)(void); +extern void (*mmxspanfunc)(void); extern void (*splatfunc)(void); extern void (*transtransfunc)(void); extern void (*twosmultipatchfunc)(void); From ed5e8c486cfceba32b548ba5b5591c2b46e70b7f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 00:50:51 +0100 Subject: [PATCH 114/196] Continue screen! With aaaaaall the new assets to go with it. * https://cdn.discordapp.com/attachments/428262628893261828/621129794045870108/srb20077.gif * https://cdn.discordapp.com/attachments/428262628893261828/621129785124585502/srb20078.gif * I will be accepting no further questions at this time. --- src/f_finale.c | 250 +++++++++++++++++++++++++++++++++++++++++++------ src/r_things.c | 4 + src/r_things.h | 2 + src/st_stuff.c | 2 +- src/st_stuff.h | 1 + src/v_video.c | 14 +-- src/y_inter.c | 2 +- 7 files changed, 238 insertions(+), 37 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index e44add4d1..ee22ecb2f 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2465,6 +2465,11 @@ void F_TitleDemoTicker(void) // ========== // CONTINUE // ========== + +static skin_t *contskins[2]; +static UINT8 cont_spr2[2][6]; +static UINT8 *contcolormaps[2]; + void F_StartContinue(void) { I_Assert(!netgame && !multiplayer); @@ -2488,7 +2493,44 @@ void F_StartContinue(void) S_ChangeMusicInternal("_conti", false); S_StopSounds(); - timetonext = TICRATE*11; + contskins[0] = &skins[players[consoleplayer].skin]; + cont_spr2[0][0] = P_GetSkinSprite2(contskins[0], SPR2_CNT1, NULL); + cont_spr2[0][2] = contskins[0]->contangle & 7; + contcolormaps[0] = R_GetTranslationColormap(players[consoleplayer].skin, players[consoleplayer].skincolor, GTC_CACHE); + cont_spr2[0][4] = contskins[0]->sprites[cont_spr2[0][0]].numframes; + cont_spr2[0][5] = max(1, contskins[0]->contspeed); + + if (botskin) + { + INT32 secondplaya; + + if (secondarydisplayplayer != consoleplayer) + secondplaya = secondarydisplayplayer; + else // HACK + secondplaya = 1; + + contskins[1] = &skins[players[secondplaya].skin]; + cont_spr2[1][0] = P_GetSkinSprite2(contskins[1], SPR2_CNT4, NULL); + cont_spr2[1][2] = (contskins[1]->contangle >> 3) & 7; + contcolormaps[1] = R_GetTranslationColormap(players[secondplaya].skin, players[secondplaya].skincolor, GTC_CACHE); + cont_spr2[1][4] = contskins[1]->sprites[cont_spr2[1][0]].numframes; + if (cont_spr2[1][0] == SPR2_CNT4) + cont_spr2[1][5] = 4; // sorry, this one is hardcoded + else + cont_spr2[1][5] = max(1, contskins[1]->contspeed); + } + else + { + contskins[1] = NULL; + contcolormaps[1] = NULL; + cont_spr2[1][0] = cont_spr2[1][2] = cont_spr2[1][4] = cont_spr2[1][5] = 0; + } + + cont_spr2[0][1] = cont_spr2[0][3] =\ + cont_spr2[1][1] = cont_spr2[1][3] = 0; + + timetonext = (11*TICRATE)+11; + continuetime = 0; } // @@ -2497,47 +2539,198 @@ void F_StartContinue(void) // void F_ContinueDrawer(void) { - patch_t *contsonic; - INT32 i, x = (BASEVIDWIDTH/2) + 4, ncontinues = players[consoleplayer].continues; - if (ncontinues > 20) - ncontinues = 20; + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *patch; + INT32 i, x = (BASEVIDWIDTH>>1), ncontinues = players[consoleplayer].continues; + char numbuf[9] = "CONTNUM*"; + tic_t timeleft = (timetonext/TICRATE); + INT32 offsx = 0, offsy = 0, lift[2] = {0, 0}; - if (imcontinuing) - contsonic = W_CachePatchName("CONT2", PU_CACHE); - else - contsonic = W_CachePatchName("CONT1", PU_CACHE); + if (continuetime >= 3*TICRATE) + { + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); + return; + } V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - V_DrawCenteredString(BASEVIDWIDTH/2, 100, 0, "CONTINUE?"); - // Draw a Sonic! - V_DrawScaledPatch((BASEVIDWIDTH - SHORT(contsonic->width))/2, 32, 0, contsonic); + if (timetonext >= (11*TICRATE)+10) + return; - // Draw the continue markers! Show continues minus one. - x -= ncontinues * 6; - for (i = 0; i < ncontinues; ++i) - V_DrawContinueIcon(x + (i*12), 140, 0, players[consoleplayer].skin, players[consoleplayer].skincolor); + V_DrawLevelTitle(x - (V_LevelNameWidth("CONTINUE")>>1), 16, 0, "CONTINUE"); - V_DrawCenteredString(BASEVIDWIDTH/2, 168, 0, va("\x82*\x80" " %02d " "\x82*\x80", timetonext/TICRATE)); + // Two stars... + patch = W_CachePatchName("CONTSTAR", PU_CACHE); + V_DrawScaledPatch(x-32, 160, 0, patch); + V_DrawScaledPatch(x+32, 160, 0, patch); + + // Time left! + if (timeleft > 9) + { + numbuf[7] = '1'; + V_DrawScaledPatch(x - 10, 160, 0, W_CachePatchName(numbuf, PU_CACHE)); + numbuf[7] = '0'; + V_DrawScaledPatch(x + 10, 160, 0, W_CachePatchName(numbuf, PU_CACHE)); + } + else + { + numbuf[7] = '0'+timeleft; + V_DrawScaledPatch(x, 160, 0, W_CachePatchName(numbuf, PU_CACHE)); + } + + // Draw the continue markers! Show continues. + if (ncontinues > 10) + { + if (!(continuetime & 1) || continuetime > 17) + V_DrawContinueIcon(x, 68, 0, players[consoleplayer].skin, players[consoleplayer].skincolor); + V_DrawScaledPatch(x+12, 68-2, 0, stlivex); + V_DrawRightAlignedString(x+36, 69-5, 0, + va("%d",(imcontinuing ? ncontinues-1 : ncontinues))); + } + else + { + x += (ncontinues/2) * 30; + if (!(ncontinues & 1)) + x -= 15; + for (i = 0; i < ncontinues; ++i) + { + if (i == (ncontinues/2) && ((continuetime & 1) || continuetime > 17)) + continue; + V_DrawContinueIcon(x - (i*30), 68, 0, players[consoleplayer].skin, players[consoleplayer].skincolor); + } + x = BASEVIDWIDTH>>1; + } + + // Spotlight + V_DrawScaledPatch(x, 140, 0, W_CachePatchName("CONTSPOT", PU_CACHE)); + + // warping laser + if (continuetime) + { + INT32 w = min(continuetime, 28), brightness = (continuetime>>1) & 7; + if (brightness > 3) + brightness = 8-brightness; + V_DrawFadeFill(x-w, 0, w<<1, 140, 0, 0, (3+brightness)); + } + + if (contskins[1]) + { + if (continuetime > 15) + { + angle_t work = FixedAngle((10*(continuetime-15))<>ANGLETOFINESHIFT; + offsy = FINESINE(work)<<1; + offsx = (27*FINECOSINE(work))>>1; + } + else + offsx = 27<<(FRACBITS-1); + lift[1] = continuetime-10; + if (lift[1] < 0) + lift[1] = 0; + else if (lift[1] > TICRATE+5) + lift[1] = TICRATE+5; + } + + lift[0] = continuetime-5; + if (lift[0] < 0) + lift[0] = 0; + else if (lift[0] > TICRATE+5) + lift[0] = TICRATE+5; + +#define drawchar(dx, dy, n) {\ + sprdef = &contskins[n]->sprites[cont_spr2[n][0]];\ + sprframe = &sprdef->spriteframes[cont_spr2[n][1]];\ + patch = W_CachePatchNum(sprframe->lumppat[cont_spr2[n][2]], PU_CACHE);\ + V_DrawFixedPatch((dx), (dy), FRACUNIT, (sprframe->flip & (1<= 0) + drawchar((BASEVIDWIDTH<<(FRACBITS-1))-offsx, ((140-lift[0])< (11*TICRATE)) + V_DrawFadeScreen(31, timetonext-(11*TICRATE)); + if (continuetime > ((3*TICRATE) - 10)) + V_DrawFadeScreen(0, (continuetime - ((3*TICRATE) - 10))); } void F_ContinueTicker(void) { if (!imcontinuing) { - // note the setup to prevent 2x reloading - if (timetonext >= 0) - timetonext--; - if (timetonext == 0) - Command_ExitGame_f(); + if (timetonext > 0) + { + if (!(--timetonext)) + { + Command_ExitGame_f(); + return; + } + } } else { - // note the setup to prevent 2x reloading - if (continuetime >= 0) - continuetime--; - if (continuetime == 0) + if (++continuetime == 3*TICRATE) + { G_Continue(); + return; + } + + if (continuetime > 5 && ((continuetime & 1) || continuetime > TICRATE) && (++cont_spr2[0][2]) >= 8) + cont_spr2[0][2] = 0; + + if (continuetime > 10 && (!(continuetime & 1) || continuetime > TICRATE+5) && (++cont_spr2[1][2]) >= 8) + cont_spr2[1][2] = 0; + + if (continuetime == (3*TICRATE)-10) + S_StartSound(NULL, sfx_cdfm56); // or 31 + else if (continuetime == 5) + { + cont_spr2[0][0] = P_GetSkinSprite2(contskins[0], SPR2_CNT2, NULL); + cont_spr2[0][4] = contskins[0]->sprites[cont_spr2[0][0]].numframes; + cont_spr2[0][1] = cont_spr2[0][3] = 0; + cont_spr2[0][5] = 2; + } + else if (continuetime == TICRATE) + { + cont_spr2[0][0] = P_GetSkinSprite2(contskins[0], SPR2_CNT3, NULL); + cont_spr2[0][4] = contskins[0]->sprites[cont_spr2[0][0]].numframes; + cont_spr2[0][1] = cont_spr2[0][3] = 0; + } + else if (contskins[1]) + { + if (continuetime == 10) + { + cont_spr2[1][0] = P_GetSkinSprite2(contskins[1], SPR2_CNT2, NULL); + cont_spr2[1][4] = contskins[1]->sprites[cont_spr2[1][0]].numframes; + cont_spr2[1][1] = cont_spr2[1][3] = 0; + cont_spr2[1][5] = 2; + } + else if (continuetime == TICRATE+5) + { + cont_spr2[1][0] = P_GetSkinSprite2(contskins[1], SPR2_CNT3, NULL); + cont_spr2[1][4] = contskins[1]->sprites[cont_spr2[1][0]].numframes; + cont_spr2[1][1] = cont_spr2[1][3] = 0; + } + } + } + + if ((++cont_spr2[0][3]) >= cont_spr2[0][5]) + { + cont_spr2[0][3] = 0; + if (++cont_spr2[0][1] >= cont_spr2[0][4]) + cont_spr2[0][1] = 0; + } + + if (contskins[1] && (++cont_spr2[1][3]) >= cont_spr2[1][5]) + { + cont_spr2[1][3] = 0; + if (++cont_spr2[1][1] >= cont_spr2[1][4]) + cont_spr2[1][1] = 0; } } @@ -2568,8 +2761,9 @@ boolean F_ContinueResponder(event_t *event) keypressed = true; imcontinuing = true; - continuetime = TICRATE; - S_StartSound(NULL, sfx_itemup); + S_StartSound(NULL, sfx_kc6b); + I_FadeSong(0, MUSICRATE, &S_StopMusic); + return true; } diff --git a/src/r_things.c b/src/r_things.c index 92f2b9460..bf4b3ca40 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2603,6 +2603,8 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->followitem = 0; skin->highresscale = FRACUNIT; + skin->contspeed = 17; + skin->contangle = 0; skin->availability = 0; @@ -2907,6 +2909,8 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETINT(thrustfactor) GETINT(accelstart) GETINT(acceleration) + GETINT(contspeed) + GETINT(contangle) #undef GETINT #define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) skin->field = R_GetColorByName(value); diff --git a/src/r_things.h b/src/r_things.h index 9c3d16ab0..0fa9c4b94 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -122,6 +122,8 @@ typedef struct UINT8 prefoppositecolor; // if 0 use tables instead fixed_t highresscale; // scale of highres, default is 0.5 + UINT8 contspeed; // continue screen animation speed + UINT8 contangle; // initial angle on continue screen // specific sounds per skin sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table diff --git a/src/st_stuff.c b/src/st_stuff.c index b59d18143..c4f1327c0 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -64,12 +64,12 @@ patch_t *sbotime; // Time logo patch_t *sbocolon; // Colon for time patch_t *sboperiod; // Period for time centiseconds patch_t *livesback; // Lives icon background +patch_t *stlivex; static patch_t *nrec_timer; // Timer for NiGHTS records static patch_t *sborings; static patch_t *slidgame; static patch_t *slidtime; static patch_t *slidover; -static patch_t *stlivex; static patch_t *sboredrings; static patch_t *sboredtime; static patch_t *getall; // Special Stage HUD diff --git a/src/st_stuff.h b/src/st_stuff.h index 40574f46c..aaf01ca15 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -70,6 +70,7 @@ extern patch_t *sboperiod; extern patch_t *faceprefix[MAXSKINS]; // face status patches extern patch_t *superprefix[MAXSKINS]; // super face status patches extern patch_t *livesback; +extern patch_t *stlivex; extern patch_t *ngradeletters[7]; /** HUD location information (don't move this comment) diff --git a/src/v_video.c b/src/v_video.c index 082d84915..3b5a81170 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1064,17 +1064,17 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ // void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor) { - if (skinnum < 0 || skinnum >= numskins || (skins[skinnum].flags & SF_HIRES)) - V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE)); - else + if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes >= 4) { - spriteframe_t *sprframe = &skins[skinnum].sprites[SPR2_WAIT].spriteframes[0]; - patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spriteframe_t *sprframe = &sprdef->spriteframes[3]; + patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); const UINT8 *colormap = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE); - // No variant for translucency - V_DrawTinyMappedPatch(x, y, flags, patch, colormap); + V_DrawMappedPatch(x, y, flags, patch, colormap); } + else + V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE)); } // diff --git a/src/y_inter.c b/src/y_inter.c index 975902ab0..1cb1b9cd8 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -429,7 +429,7 @@ void Y_IntermissionDrawer(void) { if ((data.spec.continues & 0x80) && i == continues-1 && (endtic < 0 || intertic%20 < 10)) break; - V_DrawContinueIcon(246 + xoffset5 - (i*12), 162+yoffset, 0, *data.spec.playerchar, *data.spec.playercolor); + V_DrawContinueIcon(246 + xoffset5 - (i*20), 162+yoffset, 0, *data.spec.playerchar, *data.spec.playercolor); } } } From ab1472bfc196f88af496e6d9300b3f670a3b136c Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 13:30:24 +0100 Subject: [PATCH 115/196] Tweak gameovertics slightly to avoid having no sound for too long. --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 3e3e8d157..5589089b3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -215,7 +215,7 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; UINT16 nightslinktics = 2*TICRATE; -INT32 gameovertics = 12*TICRATE; +INT32 gameovertics = 11*TICRATE; UINT8 ammoremovaltics = 2*TICRATE; From fe7e374e3bf5e4983c288e662253a4cbc162d9e3 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 13:32:12 +0100 Subject: [PATCH 116/196] Fix the Save Select platter drawing for the new player signpost art. This might LOOK like an unrelated change, but it needs a new player.dta, so might as well keep that all localised! --- src/m_menu.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 92d0fc791..b7c869539 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7055,7 +7055,7 @@ static void M_DrawLoadGameData(void) } } - y -= 13; + y -= 4; // character heads, lives, and continues { @@ -7064,7 +7064,7 @@ static void M_DrawLoadGameData(void) patch_t *patch; UINT8 *colormap = NULL; - INT32 tempx = (x+40)<highresscale, 0, patch, colormap); Z_Free(colormap); - tempx -= (15<spriteframes[0]; patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); - if ((calc = SHORT(patch->topoffset) - 42) > 0) - tempy += ((4+calc)<highresscale, flip, patch, colormap); skipsign: - y += 25; + y += 16; tempx = x + 10; if (savegameinfo[savetodraw].lives != INFLIVES From 10a0b38ab1b41731505f1025949cc493fca4ea61 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 14:24:49 +0100 Subject: [PATCH 117/196] Fix an issue where completing a stage normally would not correctly set startrings for the next one (as opposed to warping directly or dying inside it). --- src/p_setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 65335be3f..bb96cf89f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2242,7 +2242,7 @@ static void P_LevelInitStuff(void) } // obliteration station... - players[i].rings = players[i].spheres =\ + players[i].spheres =\ players[i].xtralife = players[i].deadtimer =\ players[i].numboxes = players[i].totalring =\ players[i].laps = players[i].aiming =\ @@ -2267,6 +2267,7 @@ static void P_LevelInitStuff(void) // aha, the first evidence this shouldn't be a memset! players[i].drillmeter = 40*20; + players[i].rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings); P_ResetPlayer(&players[i]); // hit these too From 737690c8b87815ccb88d57caad4af72641c6b648 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 11 Sep 2019 10:45:59 -0400 Subject: [PATCH 118/196] Added cam_adjust, de-ghettoified the camera in general --- src/p_mobj.c | 15 ++++++++++++++- src/p_user.c | 18 +++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ee90d250..e7a757c4d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3760,7 +3760,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (thiscam->momx || thiscam->momy) { - if (!P_TryCameraMove(thiscam->x + thiscam->momx, thiscam->y + thiscam->momy, thiscam)) + if (!P_TryCameraMove(thiscam->x + thiscam->momx, thiscam->y + thiscam->momy, thiscam)) // Thanks for the greatly improved camera, Lach -- Sev { // Never fails for 2D mode. mobj_t dummy; dummy.thinker.function.acp1 = (actionf_p1)P_MobjThinker; @@ -3770,9 +3770,22 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled dummy.z = thiscam->z; dummy.height = thiscam->height; if (!resetcalled && !(player->pflags & PF_NOCLIP) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead. + { P_ResetCamera(player, thiscam); + resetcalled = true; + } else + { + fixed_t camspeed = P_AproxDistance(thiscam->momx, thiscam->momy); + P_SlideCameraMove(thiscam); + + if (!resetcalled && P_AproxDistance(thiscam->momx, thiscam->momy) == camspeed) + { + P_ResetCamera(player, thiscam); + resetcalled = true; + } + } if (resetcalled) // Okay this means the camera is fully reset. return true; } diff --git a/src/p_user.c b/src/p_user.c index 53e9704ff..13450bc9f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9357,7 +9357,7 @@ void P_ResetCamera(player_t *player, camera_t *thiscam) boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled) { angle_t angle = 0, focusangle = 0, focusaiming = 0; - fixed_t x, y, z, dist, distxy, distz, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; + fixed_t x, y, z, dist, distxy, distz, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight, slopez = 0; INT32 camrotate; boolean camstill, cameranoclip; mobj_t *mo; @@ -9574,14 +9574,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) // This block here is like 90% Lach's work, thanks bud { - if (!cameranoclip && !resetcalled) - { - if ((P_AproxDistance(mo->x-thiscam->x-thiscam->momx, mo->y-thiscam->y-thiscam->momy) > FixedDiv(mo->momz+dist, camspeed)) || (abs(thiscam->z - player->mo->z) > (FixedDiv(mo->momz+dist, camspeed)))) //If the camera is too far away, - { - P_ResetCamera(player, thiscam); //Reset the camera - return true; - } - } if ((thiscam == &camera && cv_cam_adjust.value) || (thiscam == &camera2 && cv_cam2_adjust.value)) { if (!(mo->eflags & MFE_JUSTHITFLOOR) && (P_IsObjectOnGround(mo)) // Check that player is grounded @@ -9590,12 +9582,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (mo->eflags & MFE_VERTICALFLIP) // if player is upside-down { //z = min(z, thiscam->ceilingz); // solution 1: change new z coordinate to be at LEAST its ground height - z += min(thiscam->ceilingz - mo->z, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player + slopez += min(thiscam->ceilingz - mo->z, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player } else // player is not upside-down { //z = max(z, thiscam->floorz); // solution 1: change new z coordinate to be at LEAST its ground height - z += max(thiscam->floorz - mo->z - mo->height, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player + slopez += max(thiscam->floorz - mo->z - mo->height, 0); // solution 2: change new z coordinate by the difference between camera's ground and top of player } } } @@ -9604,12 +9596,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if ((thiscam == &camera && cv_cam_orbit.value) || (thiscam == &camera2 && cv_cam2_orbit.value)) //Sev here, I'm guessing this is where orbital cam lives { distxy = FixedMul(dist, FINECOSINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); - distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)); + distz = -FixedMul(dist, FINESINE((focusaiming>>ANGLETOFINESHIFT) & FINEMASK)) + slopez; } else { distxy = dist; - distz = 0; + distz = slopez; } x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); From 9aed6374cd9d6f54f835b2329b56efaf15ec0352 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 17:32:25 +0100 Subject: [PATCH 119/196] Forgot to put these in Lua. (Not gonna make a fifth exe today just for this one change, though.) --- src/lua_skinlib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index a28f6a359..7f68905aa 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -52,6 +52,8 @@ enum skin { skin_supercolor, skin_prefoppositecolor, skin_highresscale, + skin_contspeed, + skin_contangle, skin_soundsid, skin_availability }; @@ -88,6 +90,8 @@ static const char *const skin_opt[] = { "supercolor", "prefoppositecolor", "highresscale", + "contspeed", + "contangle", "soundsid", "availability", NULL}; @@ -199,6 +203,12 @@ static int skin_get(lua_State *L) case skin_highresscale: lua_pushinteger(L, skin->highresscale); break; + case skin_contspeed: + lua_pushinteger(L, skin->contspeed); + break; + case skin_contangle: + lua_pushinteger(L, skin->contangle); + break; case skin_soundsid: LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); break; From 323c7fa064d7851a3cb9cd3b9e2a9acaf6479acb Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 11 Sep 2019 14:22:56 -0400 Subject: [PATCH 120/196] Increase maximum number of Luabanks to 16 on Steel's suggestion. (Using the Web IDE 'cuz I'm tired, so no new exe; luckily I made the code flexible to constant replacement!) --- src/doomstat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doomstat.h b/src/doomstat.h index 1bf67de61..c12a6d007 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -419,7 +419,7 @@ extern UINT16 emeralds; #define ALL7EMERALDS(v) ((v & (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) == (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) // yes, even in non HAVE_BLUA -#define NUM_LUABANKS 8 // please only make this number go up between versions, never down. you'll break saves otherwise. also, must fit in UINT8 +#define NUM_LUABANKS 16 // please only make this number go up between versions, never down. you'll break saves otherwise. also, must fit in UINT8 extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins From aa91627ba28fbaabe8c12da9d510c1ce8f851d84 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 11 Sep 2019 16:59:28 -0300 Subject: [PATCH 121/196] Allow PNG graphics to be used as patches Also allows them to be used as sprites. --- src/hardware/hw_cache.c | 11 +++++- src/r_data.c | 71 ++++++++++++++++++++++++++++------- src/r_data.h | 2 +- src/r_things.c | 17 ++++++++- src/w_wad.c | 83 +++++++++++++++++++++++++++++++++++++---- src/w_wad.h | 9 ++--- 6 files changed, 160 insertions(+), 33 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 77ef26506..2574bc011 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -694,7 +694,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false); #endif HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, @@ -724,6 +724,13 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm { INT32 newwidth, newheight; +#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((UINT8 *)patch, len)) + patch = R_PNGToPatch((UINT8 *)patch, len, NULL, true); +#endif + // don't do it twice (like a cache) if (grMipmap->width == 0) { @@ -926,7 +933,7 @@ static void HWR_LoadPatchFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)patch, lumplength)) - patch = R_PNGToPatch((UINT8 *)patch, lumplength); + patch = R_PNGToPatch((UINT8 *)patch, lumplength, NULL, false); #endif grMipmap->width = (UINT16)SHORT(patch->width); diff --git a/src/r_data.c b/src/r_data.c index 38dc28980..a080fa4c3 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -476,7 +476,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) { - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false); goto multipatch; } #endif @@ -569,7 +569,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); #ifndef NO_PNG_LUMPS if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength); + realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false); #endif x1 = patch->originx; @@ -2625,7 +2625,7 @@ 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(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) +static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -2683,8 +2683,7 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) png_read_info(png_ptr, png_info_ptr); - png_get_IHDR(png_ptr, png_info_ptr, &width, &height, &bit_depth, &color_type, - NULL, NULL, NULL); + png_get_IHDR(png_ptr, png_info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (bit_depth == 16) png_set_strip_16(png_ptr); @@ -2712,6 +2711,38 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) for (y = 0; y < height; y++) row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, png_info_ptr)); png_read_image(png_ptr, row_pointers); + + // Read grAB chunk + if (topoffset || leftoffset) + { + UINT8 *header = png; + while (size--) + { + if (!memcmp(header, "grAb", 4)) + { + // grAb stores numbers as big-endian. + #ifdef SRB2_BIG_ENDIAN + #define ENDIANESS(x) (x) + #else + #define ENDIANESS(x) ((x>>24)&0xff)|((x<<8)&0xff0000)|((x>>8)&0xff00)|((x<<24)&0xff000000) + #endif + // skip name + header += 4; + // read left offset + if (leftoffset != NULL) + *leftoffset = (INT16)ENDIANESS(*(INT32 *)header); + // read top offset + header += 4; + if (topoffset != NULL) + *topoffset = (INT16)ENDIANESS(*(INT32 *)header); + #undef ENDIANESS + break; + } + header++; + } + } + + // bye png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); *w = (INT32)width; @@ -2720,11 +2751,11 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) } // Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) +static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { UINT8 *flat; png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, size); + png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); png_uint_32 width = *w, height = *h; if (!row_pointers) @@ -2751,15 +2782,16 @@ static UINT8 *PNG_RawConvert(UINT8 *png, UINT16 *w, UINT16 *h, size_t size) // Convert a PNG to a flat. UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) { - return PNG_RawConvert(png, &levelflat->width, &levelflat->height, size); + return PNG_RawConvert(png, &levelflat->width, &levelflat->height, NULL, NULL, size); } // Convert a PNG to a patch. static unsigned char imgbuf[1<<26]; -patch_t *R_PNGToPatch(UINT8 *png, size_t size) +patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean translucency) { UINT16 width, height; - UINT8 *raw = PNG_RawConvert(png, &width, &height, size); + INT16 topoffset = 0, leftoffset = 0; + UINT8 *raw = PNG_RawConvert(png, &width, &height, &topoffset, &leftoffset, size); UINT32 x, y; UINT8 *img; @@ -2776,9 +2808,8 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) // Write image size and offset WRITE16(imgptr, width); WRITE16(imgptr, height); - // no offsets - WRITE16(imgptr, 0); - WRITE16(imgptr, 0); + WRITE16(imgptr, leftoffset); + WRITE16(imgptr, topoffset); // Leave placeholder to column pointers colpointers = imgptr; @@ -2799,6 +2830,16 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) for (y = 0; y < height; y++) { UINT8 paletteIndex = raw[((y * width) + x)]; + boolean opaque = translucency ? (paletteIndex != TRANSPARENTPIXEL) : true; + + // End span if we have a transparent pixel + if (!opaque) + { + if (startofspan) + WRITE8(imgptr, 0); + startofspan = NULL; + continue; + } // Start new column if we need to if (!startofspan || spanSize == 255) @@ -2857,11 +2898,13 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size) #undef WRITE32 size = imgptr-imgbuf; - img = malloc(size); + img = Z_Malloc(size, PU_STATIC, NULL); memcpy(img, imgbuf, size); Z_Free(raw); + if (destsize != NULL) + *destsize = size; return (patch_t *)img; } diff --git a/src/r_data.h b/src/r_data.h index 91301100b..38b7ba0ce 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -168,7 +168,7 @@ void R_TextureToFlat(size_t tex, UINT8 *flat); boolean R_IsLumpPNG(UINT8 *d, size_t s); UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size); -patch_t *R_PNGToPatch(UINT8 *png, size_t size); +patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean transparency); boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); #endif diff --git a/src/r_things.c b/src/r_things.c index 92f2b9460..5940e2189 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -254,6 +254,19 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, // 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)) + { + png = R_PNGToPatch((UINT8 *)png, len, NULL, true); + M_Memcpy(&patch, png, sizeof(INT16)*4); + } + Z_Free(png); + } +#endif spritecachedinfo[numspritelumps].width = SHORT(patch.width)<patch, PU_CACHE); + patch_t *patch = W_CachePatchNum(vis->patch, PU_CACHE); fixed_t this_scale = vis->mobj->scale; INT32 x1, x2; INT64 overflow_test; @@ -870,7 +883,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis) INT64 overflow_test; //Fab : R_InitSprites now sets a wad lump number - patch = W_CacheLumpNum(vis->patch, PU_CACHE); + patch = W_CachePatchNum(vis->patch, PU_CACHE); if (!patch) return; diff --git a/src/w_wad.c b/src/w_wad.c index 2fda8674c..9688de328 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -789,6 +789,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile) // set up caching // Z_Calloc(numlumps * sizeof (*wadfile->lumpcache), PU_STATIC, &wadfile->lumpcache); + Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache); #ifdef HWRENDER // allocates GLPatch info structures and store them in a tree @@ -1457,6 +1458,38 @@ boolean W_IsLumpCached(lumpnum_t lumpnum, void *ptr) return W_IsLumpCachedPWAD(WADFILENUM(lumpnum),LUMPNUM(lumpnum), ptr); } +// +// W_IsPatchCached +// +// If a patch is already cached return true, otherwise +// return false. +// +// no outside code uses the PWAD form, for now +static inline boolean W_IsPatchCachedPWAD(UINT16 wad, UINT16 lump, void *ptr) +{ + void *lcache; + + if (!TestValidLump(wad, lump)) + return false; + + lcache = wadfiles[wad]->patchcache[lump]; + + if (ptr) + { + if (ptr == lcache) + return true; + } + else if (lcache) + return true; + + return false; +} + +boolean W_IsPatchCached(lumpnum_t lumpnum, void *ptr) +{ + return W_IsPatchCachedPWAD(WADFILENUM(lumpnum),LUMPNUM(lumpnum), ptr); +} + // ========================================================================== // W_CacheLumpName // ========================================================================== @@ -1480,18 +1513,53 @@ void *W_CacheLumpName(const char *name, INT32 tag) // Cache a patch into heap memory, convert the patch format as necessary // -// Software-only compile cache the data without conversion -#ifdef HWRENDER -static inline void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) +void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) { +#ifdef HWRENDER GLPatch_t *grPatch; - - if (rendermode == render_soft || rendermode == render_none) - return W_CacheLumpNumPwad(wad, lump, tag); +#endif if (!TestValidLump(wad, lump)) return NULL; +#ifdef HWRENDER + // Software-only compile cache the data without conversion + if (rendermode == render_soft || rendermode == render_none) + { +#endif + lumpcache_t *lumpcache = wadfiles[wad]->patchcache; + if (!lumpcache[lump]) + { + size_t len = W_LumpLengthPwad(wad, lump); + void *ptr, *lumpdata, *srcdata = NULL; + + ptr = Z_Malloc(len, tag, &lumpcache[lump]); + lumpdata = Z_Malloc(len, tag, NULL); + + // read the lump in full + W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); + +#ifndef NO_PNG_LUMPS + // lump is a png so convert it + if (R_IsLumpPNG((UINT8 *)lumpdata, len)) + { + size_t newlen; + srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen, true); + ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]); + M_Memcpy(ptr, srcdata, newlen); + Z_Free(srcdata); + } + else // just copy it into the patch cache +#endif + M_Memcpy(ptr, lumpdata, len); + } + else + Z_ChangeTag(lumpcache[lump], tag); + + return lumpcache[lump]; +#ifdef HWRENDER + } + grPatch = HWR_GetCachedGLPatchPwad(wad, lump); if (grPatch->mipmap.grInfo.data) @@ -1515,6 +1583,7 @@ static inline void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) // return GLPatch_t, which can be casted to (patch_t) with valid patch header info return (void *)grPatch; +#endif } void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) @@ -1522,8 +1591,6 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) return W_CachePatchNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag); } -#endif // HWRENDER - void W_UnlockCachedPatch(void *patch) { // The hardware code does its own memory management, as its patches diff --git a/src/w_wad.h b/src/w_wad.h index 651738850..91d4e733e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -102,6 +102,7 @@ typedef struct wadfile_s restype_t type; lumpinfo_t *lumpinfo; lumpcache_t *lumpcache; + lumpcache_t *patchcache; #ifdef HWRENDER aatree_t *hwrcache; // patches are cached in renderer's native format #endif @@ -167,17 +168,13 @@ void *W_CacheLumpNum(lumpnum_t lump, INT32 tag); void *W_CacheLumpNumForce(lumpnum_t lumpnum, INT32 tag); boolean W_IsLumpCached(lumpnum_t lump, void *ptr); +boolean W_IsPatchCached(lumpnum_t lump, void *ptr); void *W_CacheLumpName(const char *name, INT32 tag); void *W_CachePatchName(const char *name, INT32 tag); -#ifdef HWRENDER -//void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); // return a patch_t +void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); // return a patch_t void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); // return a patch_t -#else -//#define W_CachePatchNumPwad(wad, lump, tag) W_CacheLumpNumPwad(wad, lump, tag) -#define W_CachePatchNum(lumpnum, tag) W_CacheLumpNum(lumpnum, tag) -#endif void W_UnlockCachedPatch(void *patch); From ed551f2536f50ea97ea8ae2a41a488359350dcfa Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 11 Sep 2019 17:03:50 -0300 Subject: [PATCH 122/196] Transparency, not translucency --- src/r_data.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index a080fa4c3..71c909b88 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -2720,7 +2720,7 @@ static png_bytep *PNG_Read(UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, I { if (!memcmp(header, "grAb", 4)) { - // grAb stores numbers as big-endian. + // The grAb chunk stores offsets as big-endian numbers. #ifdef SRB2_BIG_ENDIAN #define ENDIANESS(x) (x) #else @@ -2787,7 +2787,7 @@ UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size) // Convert a PNG to a patch. static unsigned char imgbuf[1<<26]; -patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean translucency) +patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean transparency) { UINT16 width, height; INT16 topoffset = 0, leftoffset = 0; @@ -2830,7 +2830,7 @@ patch_t *R_PNGToPatch(UINT8 *png, size_t size, size_t *destsize, boolean translu for (y = 0; y < height; y++) { UINT8 paletteIndex = raw[((y * width) + x)]; - boolean opaque = translucency ? (paletteIndex != TRANSPARENTPIXEL) : true; + boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true; // End span if we have a transparent pixel if (!opaque) From a5ff609873b44227430ee2399d268e31abac45bb Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 11 Sep 2019 20:18:04 -0300 Subject: [PATCH 123/196] Read user chunks --- libs/libpng-src/pngconf.h | 1 - libs/libpng-src/projects/libpng32.a | Bin 512072 -> 197156 bytes libs/libpng-src/projects/libpng64.a | Bin 424760 -> 251556 bytes src/r_data.c | 91 ++++++++++++++++++---------- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/libs/libpng-src/pngconf.h b/libs/libpng-src/pngconf.h index 23f8313c3..5cb126197 100644 --- a/libs/libpng-src/pngconf.h +++ b/libs/libpng-src/pngconf.h @@ -62,7 +62,6 @@ #define PNG_NO_READ_iTXt #define PNG_NO_READ_APNG -#define PNG_NO_READ_UNKNOWN_CHUNKS #define PNG_NO_READ_USER_TRANSFORM #define PNG_READ_BGR_SUPPORTED #define PNG_NO_READ_SWAP_ALPHA diff --git a/libs/libpng-src/projects/libpng32.a b/libs/libpng-src/projects/libpng32.a index 74cb54e8c24dd3a924ff491086fb92d670298a83..5d2917502b9a01f0d75ca824d495d141b7ad49c7 100644 GIT binary patch literal 197156 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%0DwX+pN_53`}4 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 AqW}N^ literal 512072 zcmd?S3w&Kg)jm9X?-K~6&hEidmG}RWKmVy7^}qcKrv6#|TmQB# zR>A+xzt|1ODf{Wjw9+KpHh1>uo94Du^Kx?N+tp1S9o@|axAvw|8uq3rr@7apG&glL zXFHlQDTmaX;(dEpYj-ZzbIb z)0AbfIYH-QL}$dE1)0S~^mVP3gvL*Yd9Jm4>WMwi>lhHFavvrsn2UI!%ep z-Po1wTpAYG+1b~a&i3?l_ZqEgs+OWUJ-yv6)|9%bn&&NE*x1_MmufLJl?I$YFS%IX zkFJVs+K6R-a?S;sd49t=NsSgQxS&C!rDvaMXe>(8md#l>N29j+=bOCkr=Pw^)7me% z&}iL##u>)RdQP9SK&PfH>X~;@=z??S8!2fNZ1@(RIai}hLntpZf1%MTv-q6zb?Rzc zT2u1{ri|7IwS;x+X@afOnWoU9a^K!%Tv+Z_jZR*bpWfM&Uakuj_Lg){V?<()h-@YO zV^q_SM$5U)MOtJ--bL-gyU2pPbF7FvTCy>;AX6iI;x1}I=1fNx=AE^zq~T602G?zj zTA684tMV?B>N71H-ZdsnYcbWfj-=Wa;+@m3xQkkfXByLn#azo%sh+S`Ha54R ze>&Fd?Oy5JHFtORpvy|fT$*idMbA&SuXbd$ca2i1Dc#xF+R?Pk5yD=aX@i)y?v56R zo=rnsM?2IsM8+IP9B-kH6Qd2qVQ)s9=}L7t6!h$_rVNaxTZCC6f8^chP(j*ge$$v(JXd(F=khM{K)JIXCwJE4co+E? z?+{$Ii#Y}(t*4`Dl}j4W6(xU^u(G`+(-zs-@!qyn{xISs^)+_4w!#O;&9~}bCAOrX zBK03Pt4_7fKT6e>_H<8oI@Q>1M)y(4OFL5N@NVM2=vU0!yKE_k&}F^Q2(dDgbxij! zdkiy=>L4yDilU^l+ps5%rD6Ng>W9nKBG8MpN;B^nkCM-=@HAqobD5XFO#!-fPyh{k3^h{BK| zL~+9qqM^$WqUaHF*lB|yMDqwkh=!k#VCM~n5KR>fA#a9e=2OyQT^Ni^3w3ucZSP9a zwKSmxQ%E)~+B((S(bR1Cgx1=Q@uoN8HAJ@WD5A_XA{s}hrOQmK^QNb%d3k%6nT3ah z^vWjFkx5CnwYNH>vDK$hDwmfTdUb?TOJ;3qX=!Zg=xH;PX_7FWaf000-DRfCsHdjc zYE!331*Bs#+unn*I1&z`tp7RqJE|CqxrJAx7|#;(DKx(mEFC0 zBBLp+&JuZ|zK;egeILz(^!+GzF-#ITA}50Jb%J#Tw^j%_|Xg-;gR7)gA%aHYo z%h;QWR%gv{Y&3CC8U`8j0~yflTbm2xIcnv-d6^V!Oqw$og0<->g85HxJ7 zZ^LjHQAUOGmiA0r*vZM1>g&O1AJw&|sUwv^597m-WlJNv=F_vgipE4^6*+37beZ;z zm%ss^yrc9G{?UcFQ%L8r1%rm}Xm+${V&&cbY*R2!gwBnaC*9Mgxw(*-?c%7V>mawz zsEPaLws7_v3K}(BfZx!&_cVaW+@#=EE{?h+el$Y03;kGg{ zldsEgT{dBXw~yjAV}(*56cqDj$eLbmarO!P-+_g&Ec=) zeMHe!uhfd-5E_hMt}Iihi((Ho;O>mMTzjve)=`v^@%rk!;~MGWI`OG}ur$ z(6DE4$;5#ryZf)&tyb;3wP82YHZ<%WT(W2V+Bj_V(?>+w$b|l@iV9ckx3!_D5CVoL;Z|jFyK4m3@EOoyU#9BY znS%{QLBquU-GwXm-%S3K8zv4k6m3Ki4+l#o4mL~-8VdVᴓ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 z zY3WVxZ<0AI&W6`;luH%MwUSxm*^U#!B5Hj$r@6?wtEF%(_iBi&yz9_6NG3+IazrWJ zJVdA_f5+&Ed=XEwidR}kC8(o+k+a4ktnwuFu{0XXhfxN0gCsYgqiG=MZ}j%`-^jn; zLY$C)8Emn{^6#(soF@N%^x8k2f3pdy^KVApH7XM(NG@KjGmXrkNywJG8Aq-pnLOTx z?{zBE?3|R-#~hTC+#FA^^1N@Fk+I1#h>+$)W#j=mb22a+9Uhp6#iB9aPsqPm8vEzp zhJ^gs}j<<*$-HDGEkJ$Mi{g6fgdGZrDs-eihz|l*#&@kxr`3@er z%0w=*wh0wS`X)qOHA!Y-ix76zO(}f7C6ld5X;!EquMX&2GS6!Agsi;A;!q&3i52TP zp>mF5g5eSK^1OvSaF}e|C->{7L~a^ncHA?FrSLaoDSW-VoNo433 zWQN*Vo}o&lEX+xDO73k++t>M_?4-^R>nD{)=WszqD%BBPN`7+0+LoR;-k<6&3$viX zFy!9%Anmx@&8X&exR-@rC_B8ljFD;Z{j@+s$K6S&*;G36C&e>()E%hgn-nPHi-jD% z9(lv;Xi6ZLZz_gL*m^=0^wY>O2d8f|Hqd4R4K^^>24?yrx61~DzvvC4bFv8aO;^BY z`%bVW35@BuJ6U8(4vg%$+anO~r*+)T@#DzUl)%uAyHf>94P#7)RMbW%NwL_FO=Qokrpr9&XG2OErB}8`EgWvPPbCi0p{g3m9sll zEN2Wj6)-6>G*y~fnaspC#JK9f#Miya=iW(2B+cujNY>@L?d-n=Sg!T1-=0=K{f$a_ z4^;QaSucv}CWK-pFhKEDz{K)a)z7X#(Excs6_5*(4sHqEpxxd(`%&EMI8EF0Cklt) zCkZZX&%cN>w%5tnE}(rcA4EgWruPcjMDO4D6yr6Z@e#8&f4}j0=oQ`5R|&Pw%k>_o z0V?MxeR=FM49Q|yI#x^c`ES|yw!PU$;xbEFxLZ-g3Ql&Sri51q2Ca;5pO&GMW$q*8 zMeB|GQ5>=ePI-Xzde$&s{!TSsdea4^J%xcK z*822gLQ!jR)$oQQS92?Q?py0g6$aC_Ke2I!CvM%?V)# zh>;*bVyv<_+bJ4KvSlv%FdSk6@=ATI3+b=%U&e{h(S-P;pJ|z%Ou!A0NwOwh1)@C! zi0iu^e{drtHrM4ier#ptd6Aa8Q8FDllWStVlQNh@iUpisdjc83jp_QNWbkB~tZxMJ zE-;^DhOH79JD6{e(gdwit4C=^UsC;|9$;Lc1_>9^6kjBxg-c_T4%(8-7$4B`GlZ5U zIxBCa3|RGLGZ}TsMHqcQY9zwhnW%@5KOhE&2Jw&E*{x5QQWHCU)a&TgdRd}4ZI%M2UMvJ1dQ#Epf^a6dx*`SP^;&dejC8M{s8wfKP$i&}8#Cbo~GPW^pX*Lk)rRwx`MHM3Ss4Dr=KGRNm5-uRSSUS`D=-*jFzej z-BY>~mqfSe>bg$ye~2>Dn4=*QmTK)6EgGkF1kF;3+CY5L!&D3ty&9LGSEB4O)ftdG z2}Q^9&~nu}1)r~!F>HbY{kK-2Hm(Rc0y;XWRW!aMK{4y_C_rICh?r8{sCi}Mqg6wM z+jGqZ-L~kEGGDGp2r8|P_KA5jD_jYUdh9wP>*=u4R}4Ga zhWLZexF}v%%z9|#QDM_{qpYTvM;={_*ZSa6A>isBYFghWbUY+y@Y*hLB9`*iO2fOuN}HTsQaI5%#V)WVuvU#f1u)K@*hIg(jssK9 zaXQsz(W)s~)Tl}y9#eRSDmqho1*gB(sZ_0@-X#di66+q(4LduQAJLjpf`wo2fT3KzXx6W^T3SFU~}G)P<}wj#yPvG7~8XnJtZ ztO+UY<#ef&-M+{TV2>3oy4DOvXE9++X}=PWnMr1(^tlN|i!L#PJ)n=LwC92@Nj4)D z&k6Yu6E3hzY4^m=i!CJjSOaXY_Y|1Qe5u^yq@#F6Nh{NWkupKZmpQ=YtQIga;4bqv zabCvCs(bBXlZnD8sp5YY)$vEvj|y#HYmjC`A#@9}CzmXsH%#pB^>r~#)$^OgypZ~F zQ@^;|rE zrNsd=_lth_#^Oz%C~ns09G-BS;ZJ=<2aV3pk%^h>`@ZGR2VM&vO*ew_q+C~S9*@&6 zcQNqfsK>JiKimopW3uJp-0lCJX#8>BWhduP49^>RvgfBTO*8Jhk;7N;S?x1^yWR|T zrdw02J<`SY*mb1|2>W@Ky;F|j3rdJi`!(rbGH|6=lG&8{8uyhlKb!* zX7Izb;74h`@CPc%vd-)aA2$CagSkIRP`SfqbRJTWTs`}~hLQpk&ED>gzy($~YOEUn z%sm|Ow({TKdfW=Xf8nsLeZk%Ct-CYcD-MlgLYQk6ebhEc#wcu@k|_txr;1_o4DxZx zWn7FoH8=EzVFM}Y=zP%Muv_kIvM9DB@{ktZicE)68)F2F59GmviZWht?;Wl z|HwWQvGV(gWhuF=(+Wm;j<5zuW-7^If(6$mO9n1~PBotWHPc>y(#q+m<+&e+KMD5) zcTu^H9NrpS@1dTFQHO5P#qeHV;dZUZ=AS&Ge$RHuS1INZ1&Lz*NJA{dD8;-erc=)t zoIA{qnp=;P?^-jBt-CFynwH$FF6xPAvKf9`tEPFXZVDeOzrSw6$=1!SM(3NxwbP7D7-JP}ZNpN!u7lzIr;)OscB*U} zpq+-JomOP%)kVh>Gj4BOJ4yD-UtKm|OtysfVZWoDrsQBh_0;JU!4lOoE}0L>tO24q z>M4>@Ajv+_&p(lc(OGJdN45KQ!y0{r;|^V6X+4*{uiDL{Zd*Z9+2|DoO$zQzRSo9X z2yYEkQrQpJN~(197*4q~(fRKRND53*8X9W}hMP}ccb!(zW1^z81jp%TlFh9oExB}uk3c_XHV^Wc+B2sM;gn?AK`!lhHg6E68* zbecumjo{C@ge*rf)2*%vc`%>c<+*fn*mH@G1B3~1b^<&s0rn=qBNO0)1enCFEwV$F8bOGoFBpSGLMD|&STz4-sj0RKM` z-s&1}g~ydpmM2Om!V}R)xn-O%<{dEJUCi<5lsR-r`k4R*Z5&kjo~1^<|0WHW;bDaDn(t&vINqR<+IUPh|M~uHX3Wp>pH5EV)c5D0g?O zqqK5-VTeQ;ekW4WXGLal9Q$qm;dewFFid3hW z%XUhyJ(tne;o3S!;VWTp|I`S>C!a78#u@9PV1wenXp z#I?e!tqapt*Y*~JT&rlU(fJbuFx?0AGJuei=GPTDlL^R&nOk@HGJ2#Z6!IHl4Z1L7H4WY-$FXMbIO}54SDIY@UQic>_cD1P9pO~atnyC4HI2^m|pY<@eyD6 zHN`9cEpzJ;EBuyqq08J#fE`wd-9^upR?&7lz@C4KyY*E#BLYKgjtE^tS;{?H8b{J( zE22-UpGD)|`5?t?x<3=8^%cEh+*`_^CqV~QJ(v0&ZKWZIm+$6J%*}fuH#1l3r(Oe8 zi(3CRGB`3y1~wdd*^%^T+A_3@k-XGY^BEbgi;@jT3~JrK_EWIuWGrVZm>$~O>m#CH zl7Fu`>SfTYpC0Rx*bZ46i0RDVEJHMzKrt# z1tTR?mx20K)MIq!F;8)`)DhDzpA4!09Zg#O3D)lRBuOo!+o(35)E{Nstt*u)x{P-ZmIZelDGRfWy|Juo z3IpBlvivpfZP$m(FE|q35Zrwv*vB}x?g+Nmx>zEe-N#DA>K@KQ#$H|u?o2B>Vgx5a zAl-jwM71Bs-GST9;PIruEqs$%F$f;_1iryHC2#>>4sDcS-?1z_!4-SLXl<}N{rav6 z!>9yEm4Uf(s87=|+At0XoD&Q5@bCBWVUcw_=xkN}Sh zb{E^uqKz#-Vygx#n2b@PV~N~Z|DM`4J%e!*Z%EP&Yjjk2+!nTO|e8~3v5fOVr%(+^tWOUsA~uhh?!k?ohS`2z2HQ28@RIIezy!VqUY0U zIgZUt2Gn`5{k*3}yBfU}&7@Em1b$BjfwBE0Mt%&q&EN)>c=snq55rG}Au`5GkFMmh zSKQ^L9NAB@J)e+!87qCpAAKk1PYPQXoQz)IFK~_WkJFz8Gwc&M;eSc)#a(Go$6Yt~ z^LrG@&BL#mhc~Gs;c7rB%gHjzWW_G}Q{pZBIJndOXPP*d8VVE}4>7#o;l_LQt|V*# zvLM|pBZcUjq$0+QTyUwTKh`Ti9NeXPgy?>RwYm%|JY-^5;L;NueLWGq;EJzY2Q{;cb?!mHsI|%d7-!SL$`5*GSVFB=L z-X;Lv94uxqjlH1%Ig1f~r*iotgMTR-JacoG`Q0z|?KD=k46?drdV=fI7-X5NPb8Pk zeVz_drsOMrAoV12eP3B*akel2eLqduy=B8?guLH8e+YfPT)+;xJN6PUdR(*D= z)LW6(1Khtofw7rSxDlG;^+g&Pn}0;PU`=jCDzvK3)?=T7TFDshRYC(XuX|KO^vazY z^2j)g`fE!44V-_-XquBPQTnr$2|2Yz%mv&N?;AOIAka|dJvKYI&MiW`fdE6ZnGGw? z3E6VzIahgF#);#2PSBo4lSlatRer{aHAW66M`OWqm6zs8uy+ZTYX{RTvtq%HCNLbO zB=IO8B-~@S6!0H*^3o_1?!94Dp!9ilU+XJF_QkPau<84M}5o>4xk0q-i^k$#fh0Q_x%OVxoQzAEKmlbU`BQ1T!#)BKC z6mdcG!RM{?-Q4}6+67NiQ+5F`nPj!(JTX5|$W8M7TQF|v@kQEblbOh`{>UTy?V^`i zZuyR41lfd@g1Q&EqrQf_=9}GoHu2g15`P@=s>S!jI&PM8)H2weU0!sct($co7J-8w z9I(P$r$q7?>AqwYz0P~HXpXV!N;23jJAc<6 zND8b{W2{nyRX0Jvr@|^N#wt}<{W`{qo^L^Vj1_7ItM6)`)^u29#8{;lt!uvnHW{$V zjIqfmdcD0yq{)QEpcsoxShRmj5;ch6S+U@Q2tFQM7QqL{f@cx@5(z$7sh`f5!Q^8* zr_pa4QoQM7)76kSM9Rw_xyM}=89J`aeN-k0wKVusB6Zp2ky|N`y_`|Zj@-<>q6gPc z;e_jh-Dbvmf6+nIWZjga_r*$umGO?*`H9i_nP|#!G^Gb1{1Q#p{uku4rLb>xj|WY- zSoFqNNLSD+P|hlkRByq-CMZkd@7%#eCoIcYBa5v&u;l} z57=lr3|;jaKJ|MA`aqzA!bD6poD?ezCqEY*l;(8Qcpzcd@*yS#jOx7texyLf;ceUm zMw%1zjg#vDsy_mC@(@t{$@xah4ir}hX!s$7`S)$nG}{bvf7!v$gUe#uavzyZ26U zN2j>=SVix%ty?M?D=^(onOA2T-}vs}}XtfDBVX*I>|(he6>KiEFEV`xd1C??0E zkn6ye9*H8wj>02R$W2zfZjb}(i&G?uR40lQ6-9b1id2b$dK1V?bD~I7<{>9Y0K;B>{iY$rZf>;!TB?{``^F!8lWMG5LQjqsNknp=IWi?pB1$EqR29*^Dk3>JKQoPp(uk=2 zb|lOIGcDb6Riu*)g11)@Gf!T(WYBSE5OMqUM9LGclQUg@I`VhhuOULI z#9%_ujW=gWh;xJMh7e$|%RiXRXwM{y7CQ0fA)M!4}D)VPbs5~+= z+h4S13I#ir%o(nNE*j^=b1oo@#jqWWJ?rnIMd85HjR7X%!u zOz>o7(je8XmKJwthJf#j-vn-k8mdVXi!*hq#gz!=Z01w_F1YG9_|$)dv*{Snv8_P$ zm=0>wsRUZJ4JKlVI-pW&eSh7T=hlNgwRAz}6l5shcdha<$f8fVLn z5s+a2!DxC}0*I9$+Fi@o|02#~D#S5D>>7cyNg291B6K3wv7OKzds(>bRt(>faM+X) zvp#rZ=tjr1f4c#%*WDC%SR&2ekIIn1n7BBf=j&f zB^G`to&A_`IwmpoyeXUx5dPX`8Vq=SCM427^}H&OqG(HPZ-K{b7$JrM+>OV!xO+OBM5i74+AmYpdHvtPuJBCkam z$pj@M_&)G(#ZxHuQ}!54GL824`k?`I}bvjWD^H8(wb~zG$vKIl|26 zFv{V=4Lk|4+R8t?`sj$ljrkkRqGQGb+gKnsi`Ezqyb9oFURmtA%zT<2$mOTk4*upK z>nn2B@It&xja6fF_zXRh&!}v6Kk^Tn(yhHA@k&Jb2;W=yh6qUHAat~w{*71*H|1}l z(|TYt4RO(W{20+oDI0^|6m28M|~s)dXUwc09$#~?jNp@yoV_E^1`;A1gC z&AFufC#X&%7oz~IB;Yea`-pBUpP`I=PtX^|rfEuLln2aY6@ehRP7uv3OM)CFNSxOw z6~9MD+$R{W+NqF`$BhR~XzS59tR98RKJ@e@JvIE@ypRvYiDfn&CVa zq|#YOo0qMeGqPivg4fdQ0z9njVwqei4Jp?|RoNAIEX|(7w;@}riIF25U>ndJWTg#e zN=ve}mVgnKD8-Cyr6ooV%$1U4YbAj~ctcU1t&~JUXdThfuxdtx{@~sYwQTkcR3!R5 zW@x2>c?$iffhmhDaMNDRIFrM@s|8w`?Vf}?hlZ0~)-AMM8>I21)!HO&B@Gt`fCb__ zb^bwV(AK-91?!a-%q%*1PYVs^JqwFnMZ&OL?#oD18N~W3-21nXuS*O0*+14oe)l?3 zQMADd?=p?4A4iLDt>jg)@P3>pdP0WG?>W2Q!D?-JxHC^~%^t%0Y!zX~QlrMYo@5DFPKo5XeY__xhh%V!!l7CqaSX+wnnPKu%py>hBEvBOhibyy zJ>PcrL#y1-=MKZW!JWiHic4y5z)htLxTMa`b9;Qe0xz@V1~=8O!6nO;Gu){@-jYv+ zw%na&^4vVM4dfHTZS+3m7a?UGYe#hv%8A%W@kSQ5cl`h}%4EAENaK6^ee zd9o%tR;J^*?DK!_%l}9n`Tm^P)>`>HggKnUYvD~I75jG**uT@CJ&U!^8{d`sWwJ&$ z^$U&-L@&h^n;(G;_ZU;(i{7blE~*f(Q1SlEE_CfZQ88YKi|HDx++UFOFMtI?*Z87q ze9<+&=o(*iO+*)+(7FbPxZX(m@Y0HhkJiakv=E=T;`PjopC7>^;1)d|2{c3WZH6le zrd94_7CT+CR!HPyz*rXJJ9I>9mWL`Ejcd|@5mkp1`$yQv%e`W2u2N zXu0z8%}x6zxiF@y_BgDcmmV-rtsFBWX-wc_k==< zVN`+T-oLr8#5G}zDK^VwRPQO|Gq%8eVDpg@muHM@JV@~fIcLmdp|Hd?%0_1i8eLsn0WE5Dv)@}3*DCcEye6YkTVKI z$Cehd&Z<}~9g_sMp-?GX0jsN;Q^OcR1PVp%8VWq_Pd1+{aRtVBpq*DJ+E-qXg4I_t zYNBi2m=uYvFjW{AuzGsj+a-x&pIKKJWN#)dH{=z3Amu_FAaf+>;t zZq0w}m@-z=mYXzk(Fb=W)bDs#o6kgT{>mxg7cnOpEoMxh>XSGQQL;sAX@qG$qkolc z?S%Fy@xIuyeNU1sh0F@QWu$5QLX;r_Figj}5xUG>tBd^)Np);=oABhGq#ELgbS`C=JQ86o2J5%(6KTdevnrBTLYr;n#pLfy$S4ShzUvBTTk zoZFqJAiC6xphaYev3xcR95Fj5WnFghm*@`s<1OfF+kaonqA+ zAGa5^-2Gj#Y_RfwEV4y^&1tr$#1XW`&q@$U_eu~frzqj zOG#JY*@}{`XI1!#k-Q-jFP|q7?l|0AxJAckMm*mv5r&5kYksCYAS*^J;g_uV1vS5@ z@N0OiVq#b4VI%1T zE?t@)jCNUP-bbmMZ@iiJ^>@g`tEvUDTl>z} zKy^K%DjSwKrTSW3T~ZUf)H|dTyJmD2N1D=Bf1GT}qn5t?B@ydv3<09(onFc?{HWAK zU7@J|@|q}woX^e(VqK zI#C|ECBq+SOD{8id!XF?Zh2&4arkf<=OgdyLU3MT()J0ux_*LCvdHLX!|uMLND%BA z3q^7G8P)g&^^?7vAt@%7wkokTIuw69{y+v6^^N6+K*{}(9)1&&z+INNz#zHtfU+Ij zag5R9Lo6V!I#?e04y&B)$nsFY+{q%Y*o*;{)Ych1<~1Jp zo$!-ncKdk|Jt3vh971%dH)C}n#!57-di(Gb(%MEVXhod<0bL#@BayffA!+C%ix_w4 zo+xhqK<=FLytUYM;ZUE1(dG}rPe}enHwxR8R%9}}v`0B}cCbGmpfz>6ZVM8ZIoQVky{Ri-R|@8(UG*}Pz~$P+CV>52am`Te-9Sl@hj zdKg?*?@bQB5gmbtdy6Dcgt4=K%G|fuz;^d+$5>cTv;!J@zOPPG{#iy3(GO|M+#a{f zb1TEOA?dCmzt1eln3LX|R*~B6*_5=y@q?I5)_-XpSK655f}}a_W_M<)D|1?ELCPFY zb8u@tl`_p;;Lb{OWvxjsNSl+|oKoTGPTCaXzhwabGc#Rhx@%oo1%u{fHfL0% zcc*Uh>_|G`=K0*zlB5Fn9KuO_EwT90Q(Wn{dJ2-~BsIG$@KfP)P7ObhPTcn5RlG6Nbv33zNB=tAb@30=Tq8oMu5%ZsxQ5nwT!#BxS7Hj>gVS7t-IuuHsU`k_ z@#JKlqH={sF`DL;wZ<{@!k zc$&DsOr~4TbKP=euxrMiEZ6nqT?u(tMBWvUcbAfP7m#;fBk#^8?{eI^r{;kwt8-3Y zR%_z2Bc|&-`S8!npR~hLWs3O?^Op;}l3!^8^MJeg{gA(rz|s^852F7i&l^ml%T@fdQR|fW7?wnvVZr z!Sj1nw#)Sxa52AYIrRE0@FsqTpY3wV`lOfNJE^H30(bCx2?BoxT)^*^OOP2^ckr8& zk8HqRe%+&x4LFTke*@WoH}SjqD&zy+!S81b z0sa8&DuVVJmrJ^8o_umGzZThmSMqy;KWWHa-*maY$KO8ShxuJz=5ieo8h*EYn|R2- z9sI5fAT#hLe$TvYZEH)_5DSx=PyS&#!6m*0i)VwTRzW3Pd;yj{iugim8*lZH=wT0^4NbN6 zb7QoU2>wlN3V3T8o7!sYybEglaqn)`%eU>lbK*Gki*{yB8-zFaNaV-&#^O!%4C@zBI|ob6}DakntqPk#>?# zm)Udu)228viz6{+JAl%zn5wD(@!VEHv^?>s<48N|FV9Cs`}gv3(c)__ZkRX9yXX%6 z+Y)oPD0il-Hs-F4`QI9I-x~9`6QU|QP@>TH%N)j>JOhC#v!sC~5+7N_7Jb&O&Krg^i|6s0WQr zjZ11;noe!|sLTl=Dq5PPYI|Gjm()mu2 z^6G!I9j(pMv`Swi&6Ia;_1rq5p!;b>3s0Ln%EN9v#gUjh3!MZjAD!Sh=&h@6_1;=N z&n~V6pFmR+T}flDNS1(Zskyzap6X2x(&Sy(HrMV^PAi%;rB1=Ab~QoA;*}`g;zDdU zS#hK-oUO%))ltPTQ;JNt)d>wUawqU;qzkNWXlPnQ-m50Ax}mwQnq01KY^-TGt@O8E zKgn{W7e`{oo=zAQAf7B0MCW)sS#hMDEaW_`cYWi$n#Eq#G@Mqvy0Yo6-ZKPS5=j}(Jg8Us>gn#Sk)9OlVVq(Q{`ot<9ot<$|)%4c7 zrj~#`{EOwnqB>~HpLe!)VQ`LAmjBhjXS>42{?aN06Z9y(*gd#W^G;lTQU5tbjTdwwCPYDIvxGXB|#JP1VX1JUZ`=b z4(`l1?1_k79i;zDBtGr@#m`7{mihpbl7^TT zIWxqzR;DeQv_5g3re;v_ae#EW)$^*G1NH4PfM}4(j?A&hm-A=3Tnlbuu1n9m5_sYq z=9A7;O>)R5)6v8^ot|$fHTNwJw9u>dpWKdi^+yX6p?30XTGJvm5>S15b>ki0Tki;{ zE?#w+YVJje=xXc)Le{oAe~qB%>Ff(4M3$zQhB;eVsXW*Uq|>n-NayY$2PdZeHEseceVT5%162a)+-P-hiyhp*IJo}$YPQml_})wgE`z#{G`(LqIw=e(&I(c5nwB+z=X1&F^X;Ewdj;%RHS+!P<8g zkdCb$s7$BB@$GVaf92pF1=8vGql0_aq1))-HaWPR4o)s3>)1{J>9i%EZObwQNT;pD z!8HTvcIZx^$x6Nlfpm&~2BcF|agI%Q6Oc~Z0w5h*tAkq%q^18p(DjPne*)<^);e@A z0qL-xIKH1dzUi!{Yu}52bZjFX+&6$UzX^`-w;bQO4(>Jwmwv9zckWTMMKsvq27u#Wn0_m`R zAWe5OkfwXWpP5Kst`=9bBygy$Gb^-43MV{n}+V zZWNHlRXMnY4(=f!o!(yp={VkS=z1Kwp_kkIE(X&4raHLU4ld~69&&JR0DVj4!A>9@ z+aU)!0;F@JaFp$Py#rMOX{qi7(y4q9NJ}v2>+DP^&KCh`&Le>|zbhP^BlL1ElNfLC5#ZE9}@V z0MfC|1JZH)+<_he((*k8r1|Z1eD?rpSw3@cCxCQ_CkpI%=fQTGlBE?$bNRIc{T7J- z>2-HHP!Evia@fK3Ik>B?v_s5rpgJI($|eW55=iIie*o!Jz5+B|#r77E4)L*rI}TK& zd`FM9>570f-4q8m3rN!~2b!TmJOrfE_J{**1VU`mwh2h5=#Yau0;KcL^9`FW3rN#_ z4M_8w==fgm_&j_+R`-Ok%`8<*igYaD2^ z18sAlqd+Jq>CJ7oQ+^we&Z|d(bY4B{;Qj)nbM2xvTEESr=B0V*4E6L#BdR)m0)=)ZbI!bU^jxW=#7~N3EcbJ3Abs(?f%g`|vLbz(q3?5>f=^A4=1dTyb z-a1~%9UY?Bfh@<@@8HTENJb($>^h3w@Cl2mm2l}Ps`)ddHm;*iT`2NAcJ?TKtI=fnYsP_E5p=71r9XU@s+dBBatjwoyO8ovEyqwzJ3RilUkZ{ zg@c>nK$VVfm4ln(K=T~mItSO_Kyubga}GGT#SSFvr`or}!F4)N$nm}3!L4+lhaBG@ zI=G)W(8G@JFCE;k9q2K~_i+dJ2M2o6@wHRraoH*IsMBkbr}T)i5FQL3>Kyl|^K9DJ zP7yub5ZhNy-D%&)97s;!Y1|(iXtX*KL?rw>+2?ZAx;eVl(iW(17(G{G*==uZ=1^0B zK8(|LF0U&Y8BX`l0O{pV(I|oo`4&Sh7#d&xNv|L|8^=hS+uaW%^OpntFx6m$K|E9= zh~j=2IstW7F;3G3W{xK@)WZ@gH4!7Nf}CEAhw26+XBgucsp)c(F^<_GG~{6%Behpf zHO4W3>TF{ilMO~rIL0wvFmlc@jwt{mryb*%VlZ;%F^;JaKFpotm^onN{9_!`3`R~t z#xWgWKn1|#PpuWzhx)#sJPPwks4&-iKN z*?Rk2w2;GD^BQVqtAoY~ZaDy^0@?nYt4(yv+0;b090g5ut23Ml{N!+AqQ4y9OLWWe zw?wx(be0fao%>2~tAkbvZgpHL!L1HACAj5WPa=OgwUX$TLn!^+a>OJdyqsuAbjJ=U zC=|yB=ia8Xbao@>j}%ghB!;U4tZC7tO$%?WZ>*W!(zHmq=QXLa<6t?*6Xe8qJgh#l z;7}@lx@i03S_7z(LM5o$?Ai4Tn`h6ST|F1S`lefF&*v1jYxeAgt+U&mu}`ewoW?<7 zowh~QEsY#9QZmHVM#Zd}bIA`5WXx@oL-4avE}bShgOZ?TO1EYw^5TSKL(^P^QXz;h zu5jXTv`rkMTC?ZZFRsxlEhtC3#38CR&>!v6;m~G9pyJW-I#D`0r&!twCk{v3#Nnu# zI2=6_hofZTa5PN$*y(XH97PPC3{iJ$c4vvoxlXH1Q>qUa#83Rm4~dn!h9+MA@u zvYZZ1IYsf5H&q@T5S8O>N@J@Wz*Q&TG)u~i{C8zie#(1v(-mI%SD?Q7qhbBMYV25r zEF3$gXpDm9WX@Y5eKf1QW9gL?JYkwQHmfd_^VQRZyHbQV~CyVLt}lD>3=l8gePRgwcx7dFey^ ztl%g9D|XE(zJjzT{`2Cfi}AAYq@&o+gS%-#^~R%Hy?>1RoQ|i|h1f+yw1;Q?G8y=# z+x}Ab?Mj(*DWZ&yr<4_kHuqXh zob;rW!PV_39;#uj2`ge= zbWRGK?d$Ar%Z^Fgp0TcxR z1oaM)hxpo7VizH}14CDNBqgX z)3EBtiwK>{+wbX}hE3#`+7}ZtrFR-;{MNQNSdog<-f3dIuYFF$pVm7KlYK9>-)Ker z>AllGkyNGo^%Xp;snZW`_Sq#QJL<*b~EDd>pjDK zN=AOGqmm>nw|FrALlyG0KPv|0B|T#Qs9ww;-6YnJd}86}N-^+rfiit044AUJVxe&c zSCf3j>Ys6_;Ag}b__*!9LhLV=W8NPl%;}1w*N?q?p+FZ+Fo5_EHG9uAd#3}=9M+rN zTY+~;E(!AX=Jej+j?u+vlt0WD{a190+?^h6*Eg%HjJzS?&w%ztOsdn75ypyTR4ytO zH|`e+w0chp2JNU>+cDmZQ5=@sFpi__-(xcHBC#8IeYUbMy4H(9S1inmW!ze<*Y4kG z23rtc6~7A=fboq3<~Ylccc5Zn%6h#$)P006=d1-S`W2)FVJ z;YWh&23Ca613$)vp2x)HRi2~qJRwe= zF|hJR;Ca#%^9Fw;?|C%O;|s5y8W}pXG=hyZDZir4#)^x$3vNY9a8FC}6|FW_oB<-j zgBa8@v*-ohi4=E9DsHM5{s*}wZlO6Ks8<+-e_tU=GC^S75{gYAB9pUlW5W^--Qq48 zf_sSK@VMrHs!hvQ9Db@fh~m*Gz~Q@!0}6-|3WvKDhqG~?tvEDl4#PkUQygy59EO7! zt~iu~2(HP+ovV^L0XL5GqgMhHc1M?BBE%QDf$I7c3g|L7?-h$~sAQ6{A{!E3013=C zvE?Rjb{$I!@MtJ@+^*p330%lGC2$ts)Br6_lB(~$L&XMg(pUIVDSC|fq3xZKnZ%!m zzf}Ix`4e0ge?z1xhj!7n=sn8Xi6{_SWoArBGw$03Ox5zcH&1W}hEKp|*fdmsg2!0? z5)H5(;Ja3lIV^-2l{TvcHA}o(B(@;|2Hr&a98GJo~Z}LqE zT*@~!kb{1mLERTiV7WO~Xl9?WDoNZJGxNBu5bv!1?-TgxKNvKv5)1mO{S#}D9=EZo z5P`ob3fq?0a*^!}c(-F`{_k+D%} zzGOUe<4xb5?ho!8Y2=Tk!QuT<3WP^7GkoDS=CTiD3^^ewFnL01;Hu7UWBF*ZzT=Ke zN=MA8h)!W>-%JngI36<7jghbz2@8Ml`H^C+NyZ~W^ASNjfk8dna0}SB-dwh$gCgp< zBZH3Ts0>Ax!+mJB0JN>YK|+&f(`ZAnC4CSxcw!(KgCWlm2-C-y+vA%Q7{>zvNn~F# zwjfd|y>yKFpCEHfz94T7fOGHi5B`^WR!dA&~0vsm*oLdRFfPl9V zu!Ml61YAPEJNx{M1v=kAdZGq7$Nr%awj39g#3_@d-2~z$R0v|M96&-U^j#x zL%0{hPay1pa6kBc5FUW=V+cPL!cQPP2;qJR4?%bk!q31Tg79+)KZCGW2tS7qvz{%z z5Ry|14nuep{4ofRLHO0OLsGT;GP>VMk*u=y=AZ(f`vogp=2>rMCP&|)p#0f;QZr+ku!fG)7v4xu&BH!7_f3wBE%6r}Fz)^3P=+1H z4P(WR0kDHw7G_-6lp_5suNX3x?O`mtGj(!g>F^SN_zQpjy5O#29(APv826<^EA$Ni znvUSE%jQ$*E$3l7faU_aV}`}IZ%!xP5*(PBzx{Or)xM?!ZO5YgcAkB?{S86>g+FZ5 z-@X;ccGzx*ZSA}KZqy;RZ{wFIkec@LFcQ}E>oyxXml-+Jc=6$g(dk9kiBu%Ofa9JH zM%~50ei@(6d6-Y-pgIE`9Kk`Q=%=BZ7-dHs~DN^bVoda}Ra&f2$JKA)!50RI&fTgLCZzoxyNn+=6 zR$6E_rn4CsERseA@=Pl-QG$s5)Y3trNsPA%db9SOH6+sL3C$Msm(z{VO_;}y8Y`~V zex+wbzLOG~B#n0InXt$5ETaV}KcPN5GBGuDli2FL?Hssc0voQfS0UuXBi~LVgc!)4 z^|jD!M&Stb5y~j!=Oa)$LBxb{>4?xILkYA|`_8&B(wGsNosHl0i$XW$D1jc+ex;X0 zzLOc6bcV!mDeTWw0zIHTr(YJCI4E?}P>JDkxSxeU?d?K3I`Zu-sL#e{))k@I=O9e` zEkZU1K?XxArjARm3{5&0LE1}&?5arPkkIVmfYYxI-SjntXun*D#s}9Mp-H*I^%_{7 zXBK_heg+<`-z*L-Iv@0`>#Wf15wr?44%xAm!5v+Tjmg;jU#ffOZgx+oYRUe{0;(>i zLswi(_u^MJiXP#g8M)&m51HSS>USTcrkC-SX>d=nyptZEql!f~x^_DMy<`9oos@JJ zKy)1csv%FH33p1s&o?!20S)0T^e5fP{@^-~6{@7wzKgD7F|Bsn$>LD63<5Ao?hq71 zaLX5=%99e1-nWwe;4a6XfcUq;pjilf5EMfYkAVmzAbofxG2BHAi}7zuV9+cCJ_w2- zxaA}fNC1c6pAr!x(?;j(q(3I>FcRw}YC4{__mMR!QGP&_^gOqG7^>`%D0dOnJ_39M zAstWK9tb}X!YG9FJh$u(RUQz+eGq;M;l~it@w9ycA=cVg`3^ux&vVPCp~}yN@F0Y} z5Pk+B9Z%cm5FQc2UI^)VZaEUFlhDygX zEPR6}x&pOQ-Nz3yV%>-A*XZtp7vpa3D|2ruFX}e#&0(OyYqfMAX+RcF>_~g3dJkUN z;VoSCLPPjnKabq`BaK+~`j|%=*146@=yOms%s``PACjp+DB9B+LeXTV1kMv!z&9me z@zu{iE^*UH-kPHyYEZ4OYL!nIIpguDDiQA)B{{l9l;*pZRf=3j3fr^EJdQ2tAmf2?t@-Or;7w*NfkU#0wa=s0S3Vl3bG*Zg(-!e8P)n83eWg>O*d ziCa35+P@e%St`8r$jV?Gf-Y5r^Gb5cc@@bYS*ohncO^ZN#gOnq;8~)J(?#E|FA!Y-n48TQN5+5Kx8b_e3$O_d z=)o~dXs9JJ#<~49A)&gck()Cps{IUa-gv5IOsKRT4Qk@KO&K1|;jOK@PmG+ijhse3 zM!XaqiS?JTAIaY-L&fHY!OnSH;sU5#8H-R26)PUrk<>=Nk@WGa7ZU%_W1e}d%Ys9H6qm9WIDWRpR*jGtAu!bgvzCDt8$D&j*$ z9+BWB#qEQiIIWPBk{w<41~1o1$a5h~R+X|xVGGb_?00eHHug;7(h9xLXkfF;>hWOiz( zR9>qqVR@mHRa&asXFLN9_0Y&9s$eTq=LKiMTn`nu$>ZBl4~r~HCqS0Sb1v+uFUa$> z3b`N`c`^vX<8&qGg-Z2E@jrCPnchfKCIOhiw_E^wJycw#0+!?<&!AB084|7D zGf#Q8jEqd8>b4F=o_v_gNYPl~*IY|RMHUSvz}X_t*J00a5qU0CAr}-N&k%wPgKc4` zR0fR3iXl4W%(0Osg8;*k=Nqt>QKPZqQ{J|QcpML4hDviqoFce0aHKJNUHL7UP#n7R ze3n734NV^rT@8+km`+9HC2JWPevju1r2bEiRK1<;r)uYK@CWy&`+4r0XEoiy-SooG zP>ub;K6fC%Hz{xxPZFD<0?gpM%}||e{!}LULX|8XEKW8*mqmigV+3b#vY2VI4Emax`!_RWt6=V5LHb64 zrt&EC3~ClLy_P{uGvS&ES3$T6!qpM3jx~a{Y$wR0LB=ZHUFc?{*6k%ABeliv6Obi> z<_`$SK&^r$g9?@i>R2+UlO=-6T@W%-Tf7HCmI#`o5He7!V9B6@C4xGZ4C-WwpmHCC zjMNshX0Vtgg60DdGEl4d6hf8=>R2+UlO=-6Ll81jTg;llVwMP+KZlTkTE!6vzj|g! zqSQz&x{ddMR;u-#bL2ft#$$!O)X(5bQ{b*=mhSo5~Bc7S~rC?c|=OPJ^sshln zSz~xCVLeMwqgwp*^&&}m(YtNGissX4IreB(`X`~(vYg>(F5^sc=*ng#PmYXYVQNqE zlzwxY^PIWO9Xsw;9124!O`G12pQz8YG|DNB_)dx`i2xYHHYm z(SD4;WrRIbrZi~d5(s5x(&7-v5M2EA)Q81UW;PPQAs`?GOJqs|10I%U@|5VWr#>Yz zC6c+(OgPBoX^F#tfG}v0Sr-gu_Qge9!_d2%Ci*lY`g!rC^BC_MXj#{Ip&SMWD$L9e zLX|SJDcMQLy%MENY1E<`Q8Be?*$p8TW@a>0DKndry%6pf!X5~zF-v4h!_=llrZiNT znfpVPGP5c96vEGhP^L7psCEcKrZz1yrJ=&i>2bz#w$uC0Y4mS>V`@dsi? z@;y~|cq*v{w?}FaPbFQ=mlros=X>eZ8cNXcrT3xC> z%*ioV&vOfU^gT&h)@sd}> z>mHC_GQnJa6*JRlD(xLhlTv!Rk!zP>9M9y^&nBzF3k_!oO%;&FP8OhmD7yPq(b3>B zk)Xk2d7cK3$5LqU&Ls=1ds*A^lt|8!ElglyMaO8EW)6JSy~)TVHH(645bUTD6^=?| z+4z9q$3neSxG-zeu0*2$o5uU{4pPDmB?56wgPJ$Z-*B0YW{J%*;;&TR`kp zPs45r?4A~OOWaCosOvl>M6P2e4^v7+0>~eMe1T#qvI>W#zh{hJ%rs^~3v7PR64ByV zVKuY}I|Qcf7gl6PiDZYwCTU(Ge4Z9Q(h!n6L`$ojkVED2c;7WrRI_*vr9AvjVV|1e zzf8KQm2P=E#1*0imC61eRjKeQ)Xlh0_bQYj@hxHR$TR-AB*md>i93`lZ+#PgUNvHC`&FwS=WaYrH7nl8jK*c#kk=E*2P1 zr_`{7p=iT+#-!E~11j=r16>l5U~OBZDKC1j?Z2Ym=kZEi-~7w=$ypuQ#rbto;4G_) zxXLm*j;06B>NuKhbe`~_mdl3fcILj$GW~?qwmq!11a|r&mF$7bTFN1&TE6hx z94Rq*=h6)BWeNTRmf)>3isSp|ObTS$T*fBR>S6$Os^bRsw|OFtI7r|9d1FK%jQeL> zp|WoA@oJxk@-9(6RqAQoD)GPJdGXJ-Lk?H|o;(6>6c5=dgylnda(rFhlkpPdWqCa2 z0A;HpJ19ViLmnV^XyikAA1UMwuL)Al%PUGTQV-=eL3Z1euV~7@GCe8h-C5QQlyJFh zrrn<>$MIFr-|L_x^ZvYt?V!KXLD{muKX0W?`6Erq_Wb>M9X926H6>f|kL5Mnlr5T) zZTH9W=Gc_;H6`2akL6X^lruG@^hsIB^;ll9O+HnVv&a4u^;{pxTO+X7CdktSJRjUd zd5Bjw7?n(~N|_uYA=HaFVgxQbY?coDiX`O#6#}v?0`Cx53Psk>D33P;xk)4M&)cSu zWp68nUh(SIUJvC-y@dWG;cI+EifVSLHQB$&0=_5xOnH>`dYO4z zJRT4k`Y(EmcXE<1GM)AylNB5v>y<11z&^aZeNNkw%1NxGP|5$t-oM93SzQa<_)PAB zz!MZL(x?%~nh6vWMa?MIOq{_Njmk)Vn?X}lld+poW zd#`;<374g*EiAvlizvp@R?B#!{-)$`8M8py(X-fCFvs$gIbm6UZ}b-Wr!q|Vvi*SV zFS1tvme~^i9Qe|2kIfF3u=4=VVjU-QyOQ$}CoNnuFkE)N66Z4QHAuJ)CQj5!*`P3> z#F=5mnS;<-qqM#k5n_?-!Lw4H!EFd9W3Y%?+0fQR2L(J!KC|*$;@4Icb?I zT=D}I=bx2lSstPD;o3J`k{2$+5Qd~r<0TA-5pFo*WQEIy!FRY4XSU^8AY7Tx-@=@< ztN`yTRh$!aoFgRsHSlFPK6XU7ZIShgPBniSb82)n_yJ47OP%bsVAd(Mi^uSs zq0xMkLYKmHFp)BTU8)QLOrFY4vd~W)C&Fb63QJ`4Cv$RaTx#{YM23rC!xJDHD@*^! zEFyx&`AmRM$;oh;B7p+xlR@@Fw2hO;G|7P!KJ;2krFrA z%sdv3;kVxAaa!Q961Y#Cq2gC2D2ZgO6;{&BBnCSE5@Rl77Ee*EIVvs0j!oLwSXo#p zlbkKtA1>L;kK{ZVTqYh9WNu!@TFBUa;Sy0xW0{-F3<4?uDHO_Mf=ochcJYxpMH$!< znW`)CN={H0AQj0Z92*{_21$)mGPT!9aTFk!s>7E91YWa>gSHr;IDDnaw?uu8uF+zr zUcV<`yu3sPdac{3+&`g20<5ov#_&xFT}HGjgQZr=9JqvujZ6&7B;IcJS|EZd1;mTf zl&K9Crz{!>AS$!+X>1RXN>F%gSGc4*Ob|$do{x>TW^G!F*KU=kQa8(}wPj@K9t1c5 zQe+~S}Tu^AA=TA;1vt>dFB;s?6uiTFl1a@TS78H+} z)G%-1^M6{rZu>#!fJ+L!w^cqc|H*&-^OE&DzDhc0$dx5yCpZ4&XTSNcr5}8DDEZt= zue#~>DNU_U{q~h*8+Lx3GSD@`H?Atw_VeHUW%-Anf0KINWmo&}sBUimr+@qJ6&rVb zn|A&Wt{HV_P0NC(f4^wurrn3rzjyhyH{UgN=A-}ohrh1c{Kb)sL3u@i@zZ97pLyZc z)m!!)&AhmPaI*Dt^I{3UD^0whtftZSb6@PPjC7` z&F^<#)3WGrpR%96J$S;ue4KyZf9$>P!PmR`kAD2Ui|+l^*5Nb$xc^5F|Ghi;rq*TW z-u~2vOQ-yH=T%Lwe4T#tqpQAm*FSH*eCi**xOV1WkMzAIviAFTKie_%{uev1f9Q?l z#;s4hfAPd$Z@Z%YQIBft27bD{aB3l|T4>&F=G`zH`$xi&_r%e|+?N!~ZyA z|3$yLck7S-{&2T*zIVwb|5~|Yz>~MFFZ}ZZUuBx5Yux`{8@u2aKmN$`+U#Sg?fw;) z{af{}^Zx0MjaUD7^SAw?|dc)u5o=k4NY1yT}owD=Xr*7YH)hkV3 z_l?}LcIb=ucYgobyF0FbgI`fF}#I5gN@pAovi+??F z+l_BMa_aQ^Or=RA{`;=`NUvcd+=T>4aj<*ef?92887)xz{)&dJp-;|2thHg1j$&9i;mY+Tre z9c`4~c>s+T$R#sI_R4*Rb zoVcF4n)8>_>u1y}9H%`sD((_lRNOW7&5e^A8UzkiPOhtV)X%8y0G_%6UL4CI9BM>M@RJ z+xBXFCtr zK(!4$;BX8ft@2N5Uoq$W&UPN~^A<&7e-vFI7f>Yp%LYy%Po`P?5QCdGgkNC-mDXni zp=&&ij=ktbK{MzYFK;rB7bs7%mZ`_`BH2%FyloEXb>{Chcg>Qe88wi^_q)sH_5$y# zXe(Z$HhIvgU!TFu?Vb#x8ocgxlMUm)SkO+j--(0rd$ct+b#i6e4OFj~c1%sc|C;xtwZSKcTcX20s%-;Ai2 zovoP8cyKwulZKZBcsDVp)I1tcFGQ*w=vV891ZAszgclrNs}k2EpO= zsf0iT1{8MrG(`U5S_~T`F*o|mRetjwo{7CvE(74D?+K39?lj|3xlw@^LdEiI9t=#$ z`U&SxPVL}W*HKQC95LGa(>hkRmf@XZ*@JwM=Ul5{%ah$k`zbskKUcO(Ui5D754YAg zl?BsjJbEE6CYNo>Z7hli7hm|da#FT9tR6w-z-YKt4z+rXmzS?>9YX+zsa}*e)w8wX zR(W%l(=iJ&QXG!Odt`BHwa<9@U5x#%x=6@%r`O!;HIMV?_B*sQukp6mSZltx8kmU0 zWFL;m305h>kZU44XkB;v!d0&O1Lh%r;peO(xaxH^^-)8o_{<8|)Eb{z<(e*)cnaBd zHxl*J6=9s~&fZl)isH_AebBAO`)WS|=sIatm(vhdwUwea4_Vdr?u2S-U5q}n(igyC*&0B+;O+LsBr4CuqF2kZ9vN9ABiBGQQY=^sqd+p77RQQ& z$|zP+u5LZrWwh17yY*RIhrjNIv2?^@L= zwFco%y)K{FoA~M)mqCpS<@zGeN@8M9t2`~{eaq%AS$#9wn-#*UjYR1XJ5vM5xDwzT zY`S+zEIGCxYc+|n%R`9cIUWn*NxnQo95(JX#oDHyl<4?kjcT-Wz43lj=HA%CwA2<$??}^5u^CC%Mtg(c*^P7;5;)yT^1eIUu@Rw(s|pmS)*B=MOG}*&d@D< zd31R_p^ZiTDX&qyHr>VMAW`6>PlZ`1l=xAAMT%o0Y#&Wip@Sw|XmSi%w!QD0(7GemR#9|gJh zm=;V@afHvEN8{pGxQ0j?az&s^&K@3eEX1Qv=pYEtCA3(rWt>GI z1dP?wG!_!KSoJRgAq7f0y0sJ%KSHu|#FZ;0d-hz2b;2a!%8G^FJM0!hFUE*+W6)eh zbGTA^jp6cwDZf|MRC&jNyw-7ezNqz-=6>^DQ^N(L?M3PjL&iv?ftplo{L@N?j$Y%R zmiaE(Dm~i2Lhhr13^%)^VKDOhD4}gI)c(vz;~1%=m6kCag40{Z@ZK#$UTkkqk#_gS z8Tt7>9dnUlrt=e{{_=p%Pp>);%A$z5wDmJ%9?!+MZ$~(DN$ZY-^NuwbkA8rQy=!do z9EPThys{Yg{O(gXQgt6442CXOZe#P#@6xz5!Nmtw#-c62tl+qcIK9m<*xb^U|9@)#CDdcC?(^d0||C#_?I+^0z+t0OO-QKs*jb@4J) z{G72|rQqiglcV>nq_->9p*xd5=?0`EkBtG>o2<7h_8`UD(|_9Y73nJE1zgot$c&&` z#k*9i+OkOaHq}^WIW-HtcX{tj zXtU!3O#aLb<>vCzRU!aQ@q^I0Y=KdM1Ln$7b47rOPab+?hsuUyg@-M_gD@Q*ZyVlw zy%QOcIih2d0_HJAQEIL#HB(#!H`-DOOQmC5a-EBFs#oVyuKu<)%@=jv#CeGMBQI$g z><=4qY)Pm}**=7C=yLD8V;9fdT5cYEJx}FMY|nFSFIJOE9fx;idT!@xzK+sJj%##u z&d^idc{k>AzMD2=EE-yBeq227B<{}%U);9OXuBT|f8jnxBb^r@ua{#ig&nd6E|C)g zqR{0MrkUl7j!=~X(Ur2(5hNx{bMf|E8X4${vn^ef;v$HQS(hd7 zi#?#Kvo*dkkKrx4RFwe<4gT)qI?q*j(V9h_{=54yYOU6&jBQi>w>h!V;nSrn6VMArNh zen)%Nqu47nD2YCH4^+MS6M7Oh6}!YzL8}-lF9_0lu5j$RR@$XWPeIK}qcqqUCkBU0 z=iTTC(X+*?-|~Qr_BWGk=ZemA^mv(hqrxr$OY0hHDD5ECvM8>+M4`|(Ix!;XekC%_ zCApYjMi5;!w70GbqW=oYwe91u*gg(wy=66pL|1wH=b_6*;34Jac0DjqRJ~nV-bf^| zaql(O(A!BTr~(9#jNH)<8D=JSRk}X(YVY&E6azn-aV1-eOU?Jl)@$VYJhJsV$DZvn z)>iG^XqzpCB-wWy69U(*8f}xwXeK~1Cu3+ij-PnU7M6y;G63T-Vu{K%o_LawrIDwf zhq5&CxQyTn*-yGCAa3(l#r%{q+;vP2IvWQd#2u@jr4)mD^xDzTlU@k!$={z$J5hiGM zE0TLad~8clHc@`+9k@TTn8zS{7MM?TJCc5S0cAJvOtn2 z^>PZog&zdX9V(DuJTzr;$c) zwQ^8ECs-$3YFZbG=>0m)^+Z zQhD0fG;jB8d*tS`)tlwicNO>P^+uUhwNyRnluXDAOxb{pRGMIP*e0tYDZ+TA(N(Cl zSKU$7i7KS)j;hn;=F_Uqd#M8p89#f9LgEV0WxXpGt`Ih^Z{j{6~VgcVMVZDPIAnNP`ILma1<{58ExJ{Xzf zDwab3fim&Z4V0RD;-a1A5>in3%AhZxvc*{RlF}aNMTzWH5`iT#?yKF}Pm$C4Rz~ke zA$<#rtr>yHJ+#KZtE7uw+CXT8G1}f1K2A8VJn}~=BXzd&ThZ&6Nvd;Il0|;Sx3^2~qls5Nf8_U4 z+f}AZYF90YTeftj#+#sG6T5&8P3!y9k`WiS)U(fMKSl=`E29dKv#~t{Vf>;R`&$@& z3irhfeiT2n9=bSoHzbK}1O4ucJf;dEV6J*yu17?x1*54>TFoBs7oz^9%(PI&=$nQA zi$3}@r-0)6sEnwUC?f!T(Me9knMZUB6O=>jpOK#&(@9iu^mfHQB1FREQu0&V!{z26@@lEZKG%qN-@TSPO^q zkSczfgqTuJ>)8dROGy#w9$Lq6c0}(^`jG0R8G-lo%f%=!%U+o{{+|D37?FID-?^kl zMs)M=VffFW=J{}E^J{L3JSpQjMnQgamCt;i6x?2;lXaJ){exy~v8(&G>Fd?nD+u9v zMU2qRnLdxQJged9cuF`fkEz5QLpj7%(RQV<}u@aD09t2hVCjOYF6cA<~&Vj5>TBl@}^=c{A4B=LL=NRv%&Gk zYB6Z}T$A8f!_=(B7Y?~*1u54_KD5DXEE-k9tbufnN36lX6}q(QT(`r|e6d&mB&dYa z3R3uj7)($V>I{TW&k?l+CEgm*oOQ18DnP5ACDQFk>GbY)7SdtoNn^psp=}morT7nO zw$62}hHt#Qlp<|Cnr=KUmbWE)l8u*Favn#? zulNVd)`JkPl$ne&@{1w&J!oIp-aKdC8|pey?5~TV(WZsa4-U5$;Yw=0(A&B!y>%%D z*3RT5dzsBYyBxgv)M~z+{dzeAkyn&bjYLG>nXiZ*444t=k{D5P5!Ny#SNf9u$qb5P z_n?1ziMR*CxO}-rdqm_bZoZ4_w#@O+V)c;49VwIOSZ6$4(yOg>q{QT)QADl&L~p}H z7$3vX3yMGUF_rFEU&yq6WTC=lcbm;FL$Sk}rOD?(Zm)N>+Vo>=dWWJ2V)A=lw*I{^m*2pvcsdYTnrXz`u2M|*%I4A}!_9D* zX_t&7L?;yqshkffmKrW$m71R9xBc@|D(-g&YBaZ(INl%h{BtKO#o%esl^aaf{ zO)(w%|2NZA^0ehHKU$|vHfo6}czodp40fc+_?=6AL&wo~al6~lplcS!P4AI|J*Bhy z$XD^wSz|o<3kkt_mq(@1y(Zhe!V#%>oq3G6=rv;5YUtI52l>MOa#q2`pF5GMi1Qc# z1tJmAidgxKHW$QH-DhPXTdJ_?0G^dvib;11C(qI!xt{g&4j%_txObJS02ez8Kj_sO z)OG?(pS>mJD@JyL^|3qiVE$M zCkYb;#b`ilsQ}`V)T61!6V;MZW6>>r*?QuQB#(+zwPlZr_}U8Qv1ZgZ7vFiuSYwtE z$UHg!@F^H`?cz?9bBE8f-B{2KFLRsM+~zkwD>c`*ev#zmMzq#1n94urdY*Xb?=w2D zwbJXC5&A#FjOhS}fZX1&TP)2iJ~AMdCod@5Qs+lr&r+lHcAP$QJyW5z?1Kn;)`UK5 zJ)Y6{W-xL=X|$GG7#qlxW964^_t&e0cWN4WJcqE1|o(2 z=snzgb3k%8U^YAco}tpeGu`Le-xO7g$g*jY(&H*ezDc3Ue3L`vd{aV2d{aZ0@=Xi% z4@QO+2F<#w9e>Z35aze7D>Ft%a@?cMiXZ-8Iz8P+>p?~xlwnqoO&-td*vcX)?lO}% zUVU7Vljz7!bPP&#Y{3nm$NfI_h-&^g&)uiz2rD`0LeJp6_<-p=!>BE{irm80;m98IORG+pJBZ8htc>`OYbo_3j) z?_ATmBh9I5pb`FWRpYV?q0*_LLZ+_W+nafWC5ol3v6Rkt?Al}%Uu>i*Tdn_m zkqi9h1tqkp6F1SOPQ>a^N+rf$=M;9=++^~N-3_A0o*ds_rOh^x`8lWuG@dzMR19OM zK=jYW3VKq0Y8Ijjt@$WhOa>*6I>ZVXA9d&f4H6eeo)SjPidF_A&x#T*Co9hjMAS$+ zXg(_gYR1*#LtyGyE+dXu8s}hid0E32Ys4b_2k&IU9ch*EP%sjCo*zay5=qc(m0=OP zmt?#n7lgKtrW$lEV8R%itcH}uj??U~M-kd^hqy}GJx9)HqJSItKIWQ-2Y?>w0q6z{ zpl`Vbc*(U`)BMFcZ0df~wOwHrmGw0i{aJPa`67)efyjJGB#VXVfyk4RQ16msXfc+z z^}v*K+%B2Q_BRJzXU5v^lq**YZy@K`IwszPs!a@r5>(WdTO~Z zW0%o(otGj%)!$f92;eul8R@`b{!YCHA7tH^LSJ=Ru?(*L#(|C+AFcviUQ}{`g-n)W z2K3Y?4wTgo_10B|uC*TX4$lq8)ny zwn^}961={^R&d!u|d*BIT00^GIKekrAaU;S=!$UKE# z(dr-EHWee#yezTMG?!F~DVsN>W5p&ZgP+B9XFCRNj<*)*vMG(XTZX__Y8rb$bn z(R)T_q`Ny+lJ8G9Mt{mcA$kwD!f}_?PAZ$R=-~|Sn>v5)$wYsf79ts&R!l{p@=o_c#6Q^i-2;aWiH!JSj*Y>>!-z@FhFYcRV``(Bz z^L1OYe)xD>SM+xtql{ZFq34>>-_M0w*l|AfyFX#f@rd8J^%I}-qqsh+b8!pQ3MbVh zzudWp+T>?P&4Lgc!@SWMoc{aZEy)0%dtbopY~9H|AUOg6--~?t0;Y-!GX_L;N{Ry-rHaRv@T6TQ>G{~rl z1@TGF^2n=Nk>0EoY0C|wNM~{nWsV`%*(e~|iDAMC7(k}u0 z=32Ed&5$*PJtL^f9jMAB1Gu9iC67DiQm()@&UJk6Ht+MB@2e3v$Njs?qr(OUolE@` zXj?9&$N+6F%Sv}~ZwE)r*BcAuen+anJx*`*)+CPRGI&~JEcll4hIBf&T0g?oDEBcw z;sUg7V4=Mu*&m(A8UL|75kgB_qiM?#t3EZ%O9pa}+FvwsZYptcJIaGxO-E$E(hTlN zBQlQjPvDkEA`5HU>FGqqHCFd=Q{Aza7NT#?AT;;MHD;Jw3Ewgq+&!5@E_Xc=cgt-c z%DIoAzHgXo31~3IAm;WZl)CFt!VH%%S%m2qp1?yRAGKU4VfuMnSM?8<^&?+evPe=n zC-Tes%lt3;Fik0IWV!RGtO0pFhm8fY^zV({%VOT_?DFWeZ1>l}Xn;b)X8P39peM%9 zGJodB<(}2j6_$Ix1f99j+eXDaaIIB@y8(RW*M+CtUo$oGa*eAuI+J@`S8~ncZe9kN zaj=x0Q5rIx09J?($dgiavCAJc#N?}0L&>3Pd{RPwzNsM>U(Wu^>+e(rmq$9xV3Cs^ zsQkamZKs@FY6~@yODk>nrI8U+Q>x#S*KvFZVNG@^-ddBdr zD7wu`OR#8+&{cRQfesZ_##2&JB#1gs1K&AC4_N7_D{2U|9+)QyX}^~CZElgpxjgmK z&Qh;)SI(o{r9^XCU$ss3;P6AD7Cr5F%`7I=5M2*EP}V`zp5 z@n?JpG@EAFlp#W-BLr;`A!IC$S8WDDWQq_O2tlH{TOL6GTIB70M1V{Lpj~cisg=lS zm-qLT$omjEt#g0NSR(FA#90z?Un1se$*nDNxy9@(BJC%UW)UfuvL0@c6a2Nb(`Ef7 z!hS?ZYu(x+`&AzR6>`xpHZc80zm`VAqFZGQoE>{8i;<=M9uA}23^l$r+P@l#aWTbJ zY~AP0f`wAd!RUR?AS)Qffyi58XAVX!4gD2OmbZA&(02_e_iPE!=DpEc(b6l+JsVk0 zD4^YEZY}k!76nl5`B2n>KXZexZ7a|0TtmY@iSC{O;Ijw8`_z_wJqEsEj5P*Uz5KuXCq!>&gMv{7&j<&S0u|J$K*N@1joEZ@@Ke8_*;f)jqI+w)VgW(h@tiF)U?- zS-%wg)MYZdTud!a8HDrbV&Sn;aJ(0laxqj}#34i0`c=a5eV|F(7=6p=vr1T~adG`7 z$Z8qHS8jx=Ydx;6rF^QkOK`@yu&{>VGDx2E4xj2>64|Fhz>`7MO8_cm++HgK_?`H5 zER&B6khgsdv@OP`lbc&BC15A_sMg9Ty8b;r-JJq$6&BmU%V@q!2J&4U;5!8BU3WCzmkry?YQ&^#-i_27ta?~fpwkcr^3$b-upK- z-ZU0<`B;4{a&vR$NLO(iJ7akHBe2WM0LSOtp?bsr)?Ds(%J5ufT*79XuyM;cW255a zV_e4GUG~d;;4?R4lXevu0fvanlVpgv!&p#naY=$p(syu4I)h8H#l>xL!9X)3`8&8I zpTQ-?;9b7Wc;L^w9 za-+qCQ-(A8d~jHDgTarM&;5!GKLjgc`RZBa&>KV%GeH@6nI z^!Kjo^trcs!;SrAAG!HGrWr8VuV&fqPUlC$S9XgAoN~>U`Ch4WOR4iy)l1IA9O;kN z$c-y%&gFG~6Et_Lhk@LeNw3R%i!n83CiZ|4hAVs4pY)ntZnv`Y!$&cWudJH`elc$BSxVvO7#`8iqakCzi7UnV+VrlLQU^g%Ja(msP5bpKINd|mL z$_QahYM9*FoIj_x2}bW$Hy#dh!YP4=Tsbd1)*p9oCGA^pf`eCa@Fj5g4IBb&6ev*1 z;k6*A?B|e>ir=^k9nM4W9Wi))mwS`h6=$UQz{X1991?>)$yx5%X|xr>QWABclbZrH zm%&cw{`KANL*}NN!s72R>IF&JS5{UgpC z1|pd2{Nm=y`l9|je%9tOr!S639WbS$%P#S%Z(FdD&#xwf3m!lI;tt$1L z;Ae$D^FTTCucwv-JSSyPlKGXl?R}%IjL6lj(F?$oL+@v1))?iLrzI@IL?0QzWz-BZ zH4A)h%+ws?%pla~ty;!VowcaQQ)*npATJgvQlDeoM-xhPS*JMkViRCyCl zGtbGFjFJJ#(X}NocgMX)sL;dk4&iMa{TAO5!{fe0jQ}nyY673QDIRsuWTbY|Xj@ML zMZOe8nvyS3EQU$SPzgFNFIzmT@|9#Cj$#rqR3$2ymn;6E{9Kh_Jc2_NW=>v#gcz4! zppuHmoS_9mT9>CDa;V5JR*A%;Zm7Zp^TvpOMgEw0@`6Lh2#-+SB>Ab!pJXL0G*ltx zy7rig3IDZzik=Yz4VTzE42LbL#;@QDuU;Y>-Be!uwAX9|? zDoA;9v@s<=z}4iXOhlL-93e9a;7^HiMSVVVkgyjk0Q6h6M|CPur}^?HF%7B25tS!z7CM5*8x~EmeqyC{YtUN zcgtclI+)&~WuT>UUY1#mQaP9gQ91LB_F?BBoyde5VT;`aRL*O%MW-}+tF}gM%!eS_ zh_*pFc)mh24e&=RMcXV5c#dmrvp1kj9g9mn$Hh|R%{=OFTguxqbZy?&6HImf1VoQ$ zDV+T&vLk~lRHDJ=Tfdg}4{zVP?N4!%GP%7W(!o%$qS{++DX-$g~nryyRD@u4Ei zqg!`Tk?}d-^1+f-pMnoZ74O2Dh0h6u6mHC_c|IZkPV8KFF{#GqGs_2)Ry~#ZPVTY1 zi^`18YRiXPGwajwNyCTQjL#n}pLBev&39t-x{KP3kE|~fk6)R%eEFj?c%LeR#EZ@9 zAv~&?Rr6j#ehrsbPGwRZYpE8o`miOON_{7{R^COW-qCU^*y+mR<;$PZ*T>6V!oedP zuzJ-~t8230K&_6o$c>M8DAN}lxS{ecW&i327W-HJXl!7&a&C07^H$D{1f5G5+W4-= z2xcrWSl{%~q2;m8p6!jc@SJUJc4>50w$IH4qcLsv5~o(q_b-vk z^)M@*tGL7Wixg^6eO9*8rss%x>%ixNNzpnSjGC6RDD;U^ui5HJojKWppSD~+pA)8D zUcmbZpmr;$+V}W$$$YZ%J@j_{heF2E3(xX-jY^^D657g5K$A`g#WDHp7GJ@2eZZ%7 zx8M!{HGXlifU+o6yHdjKt}(in!%h~Fx;_(%?H2>qZx+w}Le?#x!{V)$op_6-aTl}p zu5W})rtSxL0fiR=A=oUg4hTM1v8Yw6lj5;O7^|pxIGXsE^XYmQBtj8nG0=g1e7a?- zuH^XwSJwf>%5bATBoy=CZk{w@^$l*w0R4xo=SjK_@ODZ!m+y9oU;XENy1-a+h}>#@ zO$lCeQh1#}tOK&rcHmucekSqqpo+>kBm*Up3I|q1N4g-im_c+va`%9gMAsLR3C9JJ z1v|yMh771b2!|T-rE$09L=BSH@TA}Y$@0!^66P4PiV>s+-h3rjI>lmg!z6~D@Y5V5 zV=!MBHQxXnkXL0SPdmjz!aK-()T*TpW7J3)95^nVBp&QEm@353P%D{PkI{g9r!1x5 zDkbS1i}9?#OvMN(3y1YrTaFIfCbS*G17G1K^`KJZs8!WWsuYZkO5rOxfS*yxVo)tf z>$qB(l;F-gi?!QC5J(S*FjKqvv`Dd6;s>BagHmNg$7L#@(IUdvbRj9rm{?Y1&64s5 zS}xLmEMn{xoK(4%QeOP|sefA#D;wUUALOQ2sXY^0J_@<3zd+ZItrzj&(l;AOiyF0{}*OYJwNe)RRtq67dtzLbyf z+9r#&ozhUZ?Nr)fB(Cbjb@3U+K<-Mb|}Y&5hr4nQxj)eQws{4zTINT;(-C z^ttygId!qu+-P=tmz=z~aHVsdyPHi&g`3T#OS&%hxepYsbFX7?^hBTUvB`U-@kA28 zFmw7M16^tf&gZ6cT9Kcea>}0_kO>*QDYAH3O6ICS4ZzF#8U}y!4NIL0Dgt*fXFZxafeg5AZMx5 zxlt_1k4EXiVzN*qqRMz$QKI^t?mbY_tIRo9CcHkD{di~lT&Le8QRgAYbo_;ztPJUv4Dosn8Bd&dKJ3{8_C%HdNaP8yh9&WMA|qYS z&0>n4PK>UEOz^Us-o2fDGyIwx{qE0*i0qKuKSA!36_Wj5BxV5)D+?aKobj}Wfcw10 z=mRl1%cb^v%d~vKAhwg};Z$V445QTKj`!y|4}pO>m&^Dm5k(M){6a>CYDTy55=5uh z(yOT)RQ$VG3Yms+^M5@z`?ECrZ{(s}=O3!!n-sc|6csbnDRG)#QLfv0iHI5O4wA%1 zNU!-06Z&0#&lko|ZibOpZDMA>QBL-#{G4;xiAIoRqry4o`rRw^((WF=^Gii9ZvQkZ0eHDE4{{8|-}`9*-!YBFFh=1E{VE5qE9 zIr@0!=wlf$|6YdG)#7mS`)owgrlScG$%Xg{6N-Z+hl_@nLC8PxsK>;r+3|#e)kvt@bzjT`1jg(ve;wh zI-{&xS>6w^p2b4(0?S{nqL`Z$otavTsSf}IXXMV_Seq_j@?e-MK1i)EdMm|Pm8jo6_<~tF8p^>E%BP1F8=A!*%`LK ztUVhGR^rbSHgYR^bw+e{X7RfH81u+0e|QSBPAGn-IoyXWc5Ws`)yKPT9~zv!C5+Ya zm)Q=X@59PEmx<`?zP3N3_G2y6#1Gr4%tu-Kiic(Feyk-Zepp23GAo_k&-P;kf2>9B zm%k5N=iGj+OE6VhQ8GVW(<1kbvxLo1-n%a5bFcA+Z|Psdd6qkw@MWD#pH=Goz#7h{ zryv~GR)+KUW&5L}Pw_-mb};&5_GIiR_3V+^AJv!#3pW)TPczXwNi~*w9q_zoJiXML zx!HfoN$8}G2RxVvpI(`H#M_2dWi<0lZa`wx?gR3hJj!=K2G|!Lts7#F~S=EBA+L0!s z*IH6jxe}m5CJ7x&p<;+R^{9-GQZAsCu3}Q0!UJo|7=`RK+GA=j=K(z{J{pjD+2T^? z(tvXdrn5Ys^SECOT34Z+cn}WNnjdX)8jC#6y%=6Md&5P_L^Du&A?l8_*Z?MnK$?lW(9KcvKg+-g&QWM!R(T1J8 z2O`p?3A?mt6RR?}r6sbXdBg5exAjS6$09iFOp9G# zVMqI)LR-JBZz4O?z-~3&A1`}T9?W8C9FnX=R%(g71i4uihfP10$sy^N$OWyiwS{@q zq6XPLHWq(xf4_5+cYU{y(P6ll?JmWbsj**`N5X35Tdkj8&s#%uf)2wh1k$4zEJpH_ zlys1o`~O{=j^^SX=~A8H=0Vz6T^_w-K+v;Fnzi(ILC*&IPthgIe3_pHJljNLpjEJ0 zXTD!73ge{e3dMNzHIUzYTlIp>O0B(4dr=E|kLV4uKJJf}&?j!EPh32}(if3bO;*betz0B8`;W90eHD{~>=$}J;CaFbonSoZzoL^1VAT}yD;3+)1WC~Mz2)|01 zQ(-)yd)|-dHZdja1YfP@KC3`6PlC716t^7*>iia{lQ~_b%!g_}{cZ#~|6k)rCeYZ%`eJChzw8_)-;OiOXzRNKM7u)9XY}%F~+e~F0OGud{Fio!C zKRTLn(XG+C3mrixGwG&JWx*TuEeqc4HNA}02vkc^asKsQwG<{*fi<*6o1^R;3$vxn zy@9ngzY&lXCL&UMD-WP_0!*KZsDEReDx5F>E+@NCL452F-@$0MgJnJFS&r*qQ={rz zUnQ6<3#ed8Dwse2cC69&$oT@Ew!9@<$@FQbEYhql<@wk%NscOZQeAc++ z8dW>4lz3bWzV2Z^s){S+%(%Xn7*}dMuGHw#!;CZIlBHoguFQB`nbEnT(WJaG z&l*=oVqAUVaWU$-r>_-PpEKi<^Zj;QedBTUjm{O7Cvo*XYh1E<)y~7LcwCH>?&)X6 zm34YtE%)2;^oz$MN>d`~*DDhC-KshmYblb&RH=Uf45Q`jG%D|Jm^lMgcd^8)R+ME{ zWwKdTYJ1OmfXtGD&VjC=Q&vWuo8+Kco}U@=f~C9*E7L!J;TrR}wI_F3srjk2q&zQ1 z0;BCgm60KC-$Y;Qo|;4rXS(j=a5y)0nR{eAi21hH{8HxZGLiM08~o;Of8knh;eMa< zUD_e1PyFuP916JDSNN{a{jMyYJmJEEE3@_|z7GJ;U?j!m{*-w3dBb^L^QhmwhbxbK z<}y}QWgE4uJ{K|x@~U0?vF&8L8lN$l8^NZl6qgq>0_);8heu@S;C>dE&E+yBRcq>m z|BBU#a?bq*ujeoV^G3PaNz{%^2+wiy_4So+FdNIjR+(|Sx1g!~?vLQ3leW`exXkZd z?RW3wdR#&I%$>eM&dD!P$pc-8=W4H~!+2t+^bc&md*WjN*bX~ii@6Umzr8s;EL(|r z(BH=X*ky>hRTVX2mMJmInBtN1B3mDkYFvi%X6y|8{pP32dhoH|T#10EiN;N7i1wi^ z8f9TwQxFYLtQQ@RFgjRfJiZid?DH(s7KQz>S8%HRremBdQ(WUYzZ1krAuGT0-eY&Kg&gL+YoS)z}jX?v-&CYUa zLhDY>bjW#0R>SN2NMFE{*2anfzpX9n`-x0&~@)F_Z+}ZLPN3oe-f6CB-qF6*D_Vk z6>>Gz5Q83q8`I=*xILsxI?_qXnvbhejD&1(L+rgmC=&>k@*3IPhUJ{J(z|wvgvyBB zC6fVV73FXTk&;u8GJZb_>9!S;!P`Y^DSP7xB34?SGFO6OpFdWDYH$;wh1qG>#1}xKEbxz zDJ3pz0PM7*;H0K>?~p=juaw~jQGe(+8SXTF%<-1bWjxi*m5yp7<0nDqMm#!F1S#7B zAO1qd(QT90(ljtTcwbjr>9}Z}TZ}fjyE(gIj6ULZ9`P~{T`&?V^p_vM4StL;?9f<7 zlX(Pt-4?HNn_5gBq&X#_B)wcf!NHc}F!33qxA~ph)L{JZ+Y(oKvY#Wv(F(TEx>vUz z6N)8%=PI>^{UH>@Gd+dvcYa9d+nI}T%ElOd((A;K#5(t%6qn{y4jlTi5T&`lYVAhW z_q@*cmF{1t{O?HlCIDI=4N``T{@kc{Z51)#}-^M7-#B8`gmWBXKY_|;h9nGSR#d^S{p?W3kg3Z7<*@wpyc2PB8!_hwAvI$TMW@&L6=y zP0ls4O@;glIK48sb@=kjSfw6Kms91+#+I7nD9O)B;#7>xY@9M&$)*b$=@^wiGHFzJ zWV&G0HXG^Jzz$2ILrc=M?2iGP12zZR9H*uQ3*TYz9R}ZFy|8v{8f(5+?m1|*UAgVl zDHyq6ay4lE=8UoKancW^PNZw4T0I5JTXR($Op`Ei)LAW%Fp;QoRjxARZi?7%aa{; zE=}Qg?q>Szck}doPM%z|MUoYVk=ozcwZ6;i<~Wu1g=<~!2)@PK;&*o@5L79GoVWy^ zc-NnhF@?<^35nn0?{l77e+nfQ_l|u`!brfJe33+LkE?JyZ7C`g?@+!NbL#TM*jnLE z@;YrHD)Knsx2Pgte5&%qMpNNV_Bw4DD)Pj>GbcYqGD>VS74DQC5i0V)hw{=O3FT|w zRG;(k`fe18KU|TQDw!iQn~MB26*kTJv6Vd)d4idfCv%v(d@<8hxUtV1h-ZIA9uEVr zz-ps&e?)$OX(m)T?w$y8@`9dsjdr;>Id%=ufQdT9^lI})_i$^oUeYy`aW_r;CxGJfwHs4ykY5wSogS21S!JE>UXcD zAjF&wtwLuNt&4fxYy9CXg%v`f&VssA#SqnUa#c|@_s0^QUQWR$u_O3D%VMc2o_Md>!JdhS^yZyzOXd?G3v!*KE8m6VJa;~;O8>Mpu+8XY^Uc83@}NRMY8 z@VmcVa-12!0kvS*;V)#S)9rWe^%trIO?QW^X?oq={=&U}_g+6+{-4;Y<^&szCpHQo z>O$?N^}D$Tdq7NTfX{=kjF1`Z59At&yIGUkJ1PuF__ptTe(Dq+R?8YJS;!_*2K;QO zWn$oGLq5xFY!_Bbi^mbk?>r<5gtb>tKJ%2X@QB~sp1ThfAz&IicL>7oF zg`h7Okz0~KVhDSU#T`d5mrg4<#CdZ#c)8l&oJ*$_ z8bUW1x;%-9=Hw>Ri4949p!{>9p#Gq}&{?yIeZ0ak+F_ z{L+M;W`P66+iB1^G7}eaWbU3FX8FEe6LoQtrSsK5OAytx8M21>^zA2$A`KE?) z`KE<1l#j8rHoE|cXkZb!b)(kNXzg&0g7L_~Lpzj$4rj$ZvhdIj#3aNpHH|@+1>nEY~K>@TN5Ka+sz9sK596vkHH6`_lt-*vaukgKq>1jD4{LBaL%7+(`u>3rkd$qA-IBGD-e~1l!tTXoNddAmcDpoq#N^f~|vb52hK@5lobBOywj8&bp zGr5(}>yBX^l5+;m80%uqAH^1OlNs|GKYKV3D3+t{vot@7-*6P`zf)S{mhuGDu+4J2 z<_?==&~oX4_7$#)tz4|eo7`2rCN>-kOwimA>p;RdDd+-;ukdr3LfnHfub86|vCelD zF80zkevL)zW1VvQnGE-1FMmV&TK`oxp&nl~UyQ%WsOUl7*k@Li9z1B58GG9F` zlyDg&&ySE^ZaTS?7si{psLYgShC&70P?&hxW299AdREQMxc)^UXpzgxUtob(?lzGa zc+}!zZp>tDr961UtG+WTSd}aT*Zs`CzA<>jhBy@ugEQY<{PW|#xM3EiN)N4c%*fu+%4GoiF$U} z$!4XL$@(fiXk{MiaB_Qm>R4xivrn3%&)Bqr)JZ7~$zzfhJJXUKX_J!*oRgqo%{u+c z1Y=9rAd zY3ow9Cmn?5Y~jxB<2cuOuH$rSC0C;*m!cVPCXK{dVx^;Bx}%?Sprc1>;`^WENKEtO ziyV`W_ILbvPd~?)Em@A6SNC=N=+!=st6#`;40|TSacMZ+ap9~q$DnDc$a(RZGDtpW zq&hNw)AMdKDD_UO{O0m^8Gjl474Rpo<}t~26!RD0Pnz}^{+{O#Gr0PdJHm5-C-EzD z{5-!m zqA(rrYV(m)w#9PE2mZy&DrV3-dti)oZY_x7NJmemCfdV;A6)(RK0fDL;gR| z6R$UcC_z3YZ|;=(I#Lmu-B6v|)KER8wx+ha3ZewMDfQDE8mpU{YU^j5P8UxKPhf_s z&(5VMKkT5DgbyinS&y5HBhr>;;P6+f5#wYm~N~I2QdT#)K1^iX<7v}GI+JXFw zXWoqF>66L4`WjspO{6^6stuZM>WunEUOvrjtbU-mx+x@?GqbTaRGnLyD=$;!PMOv` z<9-Qc*Nt3Wk+k`%{E26w6yo&i(6suhTynFfu5zl1uasAO91m7E*5}q$&zMTpzQ20* z%=$)E6K-t|iEv~?edFxhnbRns+=j;bDb-CPR1aaT)jli?VpTR)PLX<1Fzd2{E61zE z@ZpK!+Byl^!~?9=O(bi2eG6q?(^x+}ft{nSy0YpYAahjDf+NW%rdrbUzj2*#wiL$Y zwNgI4nJk`Mom-gu!`y2K3Nx*`cIvc{6y|9GE^o?}8=-Tn z>Lu9p%FvW)iiD?XbqVrnq|V{9@e@r|X~jI1W}sT@w8|M0$(bDDB_*%Q>VHyYrC*Rg z!Nu`_HXQel(>#J-;-Bc>#J|(uB4_B|>)&_1MJ{pwZ~vr^B72dqm2aVfKDD8JKvybU zH{YuiBu9cYoeN0gd^Rp(L(c-~urJwkvIAU)EhMuv)C|O_DkP9=EH&=;KsxMeKst`M zZQl+XC-)2J5E*3l2*o7_NP6WI$2uU*`D-AQC%E&_HyU>#(6tKZvT?&~+$%Qj4It^I zQylAU+($NUzl}R=<8DNM={Rl%(s4A|xQA@qP4w@Y?iL_TcbAQuXyck}+(S0*Q5*M! zjr*;Qd(p=I)yBPLnI$i@vr|l9TEy2A&I!_ycbSbn0=~6gu`(|avaU+3rh;kd( zX5;?J#x1vT8*N;-jWhbh`TYn;^Sjl?HQTtSfbvy&ylvw;fV6zG`o_aP2Bf9`HIVjQ zVfzm07pJ=hNYj-9X}TKQcczW|g^hdNhF06Y9|CEqJ^|7>dBCPSY15T4Uejr-0MhZ+ z1L=4lvwhb9c~mMpfHdb_K)STjxwY&%<$F1h_PrlS`yR7#&g?kudqA4*VjDLe=z7Iv zs_k2E`!29?5gWI`#&y`ZO9#X`{}4z^umDI)zYeHS#qk-CPVcusI=$zd6Zg#p(zqfU zH_FD<+PD@VE%QMj9mlwH<2l|AG*ZR(OQ0JR^dgWBvE26EYWtpx0{oGpyAnv}&>SGm z`L94)`nQ3uRCJ#LU8SHd+c)F9IBo=x4&epT@s75kCLkS$38drw8<3WHGmw_~Gawz- z!8s1?I~Yj&{uoHtus;Lo*p>rn`8ES-`M$Az<$#RFeGf=;z8Xk}9cBBr*|?wCxL*P3 z5PNOkqqc9#Am$$`Hwu9?zgvKGi1|QT`X_B@5s=PQBI z-UQOAeBbunZu@>^a?M+ZRm-M;&jge={j@* zNJ}vE;&^%de27hFL;07-ad!ggu#G^L^lsbNhJqvFxCu72#UICIjfz8MHguN_O|qe1 z+t3ypYQ8xhw$+9nv!N-$xNq^Tap=L@<4|Nm9D2%z+L?vw{EGtV6#d@D{n5r90J=$) zOd2B(UD9`uLnR8w$C1llGW~rVqRVsqK;g1%9O}=aLkU>ERA&oE;agCy?d!5}c{Vi6 z_ARh+WV01k+J=r-+E6Zk$&O+h@&RezfQ<{<&=}iyoQ)fALlw5~BpX*{L(^a*EE~$UeU+6)`3|yub8O#S8z-Z6%|-65&^4!k79hA3#|RrLvLSh1 zM*E6YN<(6l)lk5OV4z0aP65;?-02vOwWC_O9ggl(4#!ky!;Gmjn?tpA!>3qiN7IZ3 zrtF~_aXaCftQo|;?cWlhT>gZHNco2%P_vdEj4&%sIR9%rUrMl5?Lj>-Avn1|!zJ9!xzLvGMg_+Q5jVuLmPDHnI2hVE#+^)EMg%NgVt3SghH#NlrFx$9m2iBGbVuKO(iF1KhT_cVB&g^?W7pzO)l|Kjc9?lUm=oq?Hk2Ii+{ zV19lE=GSLn{__mXUwdJ&Bw{Q&UBYF(XwV50YZ|L9MG;TMhF&z7)TT7dPUKTnjY%$K z1#-kgZS56G=sVBAq(~J||0EQgF+3$=1U7onU!|O2jk%I#_F1eP~&is|G~<- zy80<+^-)O^nx@K@>WR(hx`|g^>97%}yA?sbB~5TDV`q=AJjJYhr%bD!a{t7*L9>TI-semRF0ZL3 zIOV0e1g9A35}Yat6C3MiCg5r?Z8uG`kyZ6pN|Xt+4x4B%TCuEGSJ~7$(XgnRsIBe^ z#8~Mm^d7+*tHp?Z*7Ey~lI>M5&Qc)~P^umXdAal*TDaJ{C-- zG)qmlF%Q}`%Bpo%Qz#_`S58!cw0TwWu56rvX;@*WtE5bv2`a(uu!4`Zv!r@PQ*&eW zL@A$%wFoO~IdNH}mJ469Z(_XeU?r^@UVlZd`d6TT5@3zFX2b}EyRy)8ZIOZ|^{HBb zNrZngA81ifiX&0y3u;~g>t?j!)kTWHF&K@T>liaBQSWP?Grz@-F^dy*|CtmTGf(Ie z^tNE1r_DddWbmDvG!0#}1AfKB6Z#49k#W?>Ycx&#TkI$(PV6fLFC)V$CM4s1n##kE z&|NV?yW`(%fBJc9T*E?>J>y*0aQ6Zv z`ejJPfk$U5xEHOETSVZ~oZcD{9x2=-P$&z9d;sA2sX`Vle6E#cP5)GErM}In>}@gE~Uyxr6LS?Czp(_`r1=^-80dZ2|GTnVW60&Dl8)7-ze z13d?1RT{SBy$%1kNNt@sT34r-uecCV*QL|sS*E#!Hsk`5y+QU{oC^udW0tjwg=oXV4^(`C=!?>NI)~`; zwA)L9aPnm8uc*}jpRWAJb>%Rl?Mbp4t%*YX1BDwf_r@yb(#c01DeyyvMbeo}c+9pc`^Os8*TaSOn&)y{H;m#N6 z@6J}fvF{Vy+AEi!ObeT@M5;_ns!-nqRy~i`TGjU>rJfk0*!(SZ{0<@si0T#7hk&Tw z-4=-IT`M4Z5!Jih0#Uu+S|F_w*dM~kDTJ=i!gfcPWwVag+AD-r3ewzC)r@4Q3 zn)~Ocx!X>2hyEYtz6Ct0>Rfv!nLxPc4vLDmI%;f*A|;5L2-?{&qcbu=u7U;S9-$&c zm{e%J1Sf&nj)PciYm4VER?jIOJx4v-fMPWP_}Xee1f{dRII1w07pv?aaY;<^k=@#qG>J+nIgs%zt-U7dyRw zfjJ!ir0luj$jvCWIF5_r9N{=py(OQpOR;QS)@9^Qdldg<5>7=H)L<+YUd?uR4E`9U zMXWb5hLQ2rKn}xrxm$qe&4y!CGY)Hk>O#;<;BW830VpEq)zvWASNFyO_RY4jOKa9} zxv^7n*$%k663n{120a*`7tZbu}?VlbUkv6B zClGVab$qx5*r@Far|7FrcJAdvuUxk!Yy{s?swNlX5e$p#vO#rg9`4Dju206(!a@5$+})#QXl#zEtOJ*J?|=NX?AB?P`#;sF#-F#=jMedve(Wc$roM?zs+=VJ%~t zD$3X08Vy-n9l!cE1Fqn%Z%~=yO@G36DRt5V%mzD3X*PzL_*!vFZ+*ha ze`GSVFobj*pAx;Gg@m1hx5>+M7qPuDadjrP7jF(4PrQ!H3R&{Fm+%a@L2k6bgkx47 zT#j4!0URSo{|FXRW#U?DRaDE$z(LWV$>e}C5WCVzw4i!br z_F%aJS2eS|4mKcSTTiBS4|6rO-_-^?vZKjmn-Ir}x-GGDl(svP-~u&plIjs6ZM!%g z$S|;V80|F^ixhO`O`7RWdPwm&sy=F+Kb%T3&!Pa7lar7DF|*6j&LKN%q@OUzD7?({1 zr}fqNuFmm;zUr6K!^J`8>NU!N&scl~9Xt*P7;lq&Xz3VSZU13-;U@`XUf8PWB~S}t zYm7f?g?dM=ihh#EVJoyEoW!5XHbzQ(3>X#oU%z7GU8B)J1O^)ruzTaeagG7j8{=D1 z(EN!Q4k}xz6|jmXD*)pY>K#sw@vAW6xA#JyGm+Vu#A-S+s&vazSoZ=PzwflcWxf-P z+D~oYgt%B}v4Qh=@}P9p?=U7xuW+u;0vp16MUyfngw_^@F6)Z36ZD?CekB6$rCQ}w zfiy)Zgpt^~FG;etsrZOHSxIr6vZG{qeXqS!=0xhG`LNz7tuCWQSt1A#VQO`LMK;#3 z3Gw)`12cIaN!uB-RN{-vLmvR!&IZYN$+6Ii3hUQZunAjB85qKRg6^z^H8jDExq2Lf zUf37_L&V1Sf2HPc#*#)@#rGdEYKcw7vS9wHm|edjRN4?l7BaAkG)j9}DS z#x~trIu1`~V9X?0yCo0`gK&5Wt-v@d0doBHviF{eSAv#Ornsg#ym2AT{Lv`<(gwHR zTCrq7vrYLs22kl#@n z!y)DujBg&rWZH7JFT9&};U{d5nct#a;pC}d3kO^_hU>OhpOPMg`jhb!Mz2}BIaZAG zDBTOoy646I8r~SE9`c9sXDjS9AvogF4gXO^c#QYsF|{8aHwEw*g3DlW#KI`SDH<4u zPQqhUl~FRGcsQu#H4tW$Oe-e)VUCgsyj%stV=Kg8tWi>1yhF_Q6f6Gy#U7-GH`5l$`;lTl)3>ng z31kY;&uK{g6qjke3tia=9?To#D}?|V|9F4+IW|Pn7?m7Y*!U!@&~kqfehhvvqQZq) zcw)?iXS8@Yo}-I7(wb0QW%RfJ#3RKu5DMaPNutqX2%wwtYvJ0pY!-xQ{z?4rHygcTXKvZ%b2-RPoip$ z98F348n%|#z)_UoKVKMk;EC=m0FG)!c;pHkX=3aa$v7E~jFROHLIg^4G6Ui<&gzM( z8LNT^)(b}`!^x*v&(XTbsLQ_HIBY#V99;WaW7PV7F!7RPNYY-VE0R#cI16TVN_$z$ zQYW!4I^tV@0|uJ`%~+C=h6czv+cZCHZ3dVJFelBqFUblBG+kn;UUKA)=RXE)JZk$cQ-Edzz=_!o<`--!Zt?V zSbRB}LYFUYIXJqnv;o(yCTi&@q_l>Y*!4#4D4bpR&Lo-V`clVjfKaDXq{1-wPmpBL zquqgKC-+?^xx4?y__^FE!`G%yyrfP#c6!2?VD*NwK`R)?mXw^wi+@rxex2Q(qs+Tu z0uk$!{v1diOS7k60S}tz_$NpLR)F57e*D0mM#a7hky|N}c~AY2!DH|7H;B$cxazOO z8aVHc8QBRp3|!e$W*0|h-nymAcc#5}t{;SDnOUNr zk;1qnm_-mG zPXiH5%5*J+uJ#!{tvkLo?37Q(^Fy_Uucn7s!ygz!3!#^*w3v$l&Rr`kd2ukCWe`LVb-)zM&GrYjk?pB z)ZBR?GNBAMoYLx<;S48-9F-jFsN^s`GdvNVIQ2UvYhnnnJ9}bSQTJ(#IK~eL5!I-a z7iNu*Oj1>}Ke7<>J37*HM_r^}H=|sxW|Z4w=jP5R?cpyv3v+^beWdLJXHaW%)*+iP zq2zq>HI$^pCVdw=86JZ5a~8?jB+?&EEt9#Wr52*wO(==o24z+g%I!vNADCr;?BraU z(3{y4O3n}Mq_HWWIU<_1_#T zHnK&guMoeO)ZSscs~Znan(+Dd*=Drp==PhV)&}X;E`>&*6TsIn5EY!%a*v)g5?s$P zQzix)v(^@J^o^(*wQCS!SeBzWI0Kazi{S~>1U!8)Ag%0|)BpQ%H<`tzF?nJ%*(q!t zz%_l4vZ1i$T$wYr=N{J~ci#bak?q~??2!8kxppAoa>7{&NOEcUUJ1eF)N8PELK zwRrksz&IyM7zwKO19LW`winWgnfc<%USza zTvu+A0&MA_Oijv^uS{E&=}X*WK~+BcWi|5O zD0!xslS!lG`C`sYaD`KCF~^%A?kS$6i2IAH6!BnjjUq7HSH$zhD?ngOEnUTySb>x} zXL;S!pkJBQ^1sv7Vzk$fjlMmqv)6oGM(%oY899$|8FyqGtFn#rvyCzfv;#`Uxqh{G zJ-$Cr!-iLDZOuoYS<4j9Z`3Uy?~R%d-?x(F)qE5%>Xv|q_$45;=EE12h4h5$;Nsar z=%m2jT#%dr7tJ5nwHZquf=v*&!-Pur4oek(3MaT<;X|}-s?9n9DKX_^gOfEB9uZ_h zUW*4xBb@;*2Vg^h%K_3E;Bqd(6G&%QCwul6Aw>}F7Ghff_E2qEup|D@WT$_LI7E|= zF%5{*(B$ZgRoIxzWO$`T5>0$s$ng62vZMaA&AU1|`VznGE}n7M&;ItOhI9uy$PQsu;3|STj)v?!$%t6Clu_s4<`t(%6?&v9Q?q#oM%u=be}ko&n%`Es zH=62mn1w1Av2r61+DwScDpHXt_p^?q1G%5|9{2&3ADe@xFE$d-g4o4)3fQC+=?m@> z6&b3irSoQ(i2XmConC=(^n59rd{_ds-i;(j4{(Y$p5gUVMT>cyirDDVUYWdg+~K~< zeyV80$u+iL@Gfz3ONL?!qS}vi*}hl^rdw!4Zgq~p0Vq^gmMtihEuJV_ zJW;lIs6RRd;({!vw%UH2 zoZDlI;IcN>uN7M@!z!9<-Gs0j9|)Hmh$c^uCZ`s<7QNLPHyzx>rTlqSm{=laT(KiF zUW1Y(A*;%8a8nUYYgUkrH1 zr$g>>({PwnuWa;~S7qQP+J2A^Uy>qNI^B8?Nf{VUuiirv2Zrj)jUJLn8Uv7E^gzNa zl`u;Zh6VBlq`>H*l4Yr6S&}SFiaDylfS;3j^M-X$wtlL_q;ymd+^*xTUCaqXa@mt^ zhte&#>y2yYDu*p@htgN4$H7L%HyGVy=CH3}Us}|9Nrt4dCWKE%oPe;pN|V1-isF2k z(mn;cBI#Ev9~IV)mL?2-x!&4|Wb)_iaL!aed#E@t>VT=t!AEL3}RY;x#d#kSiF&b!x_j#XEhF za+b09S+Mu6^BQ$4guutT#X=Msb+d)QaOy)8`rdUIL-8!t-gOu_brYh-s9O%A=3`jZ zk+X;+s{lT!!eFlubCj#{3!Je5~$}YP0QV=F4 z6v&EUsab%cMfxkObl7T*l%yl^bYTQ_J%X90DO1g52sFZ}<^nwPV$<-=eVr&|??@diNVS*~U59#!1=65!uGU*~W`t#1*?)^NF*>iJDKG zsA!o_bXK&?Ck}06j7IHX7Ja5@nNNJ6XqivEqiC5=yaF2GR6k$>OoVeQ++1}4r^TJgqkIp|nuI2@MCouQmI^{zZs6C8Nxfuq^`b#hr zhPExArOg8SlelD%Q6R9Zo&$XYKZz)nNI&P2mEP_))WN{@W5?yJcIHvGIdxJ%CKI;6 zi`+71yA9)Ds9b8UE|TpDFgqt9%F&8kXw$5|bgC8w8iNb+IU|)rjkk z^2H)K)>pUXj=rvBhm-fxk+t^fRG*!Y1ei#*+Xq`lu$%El-?vzqKmnc=PvLlWHbTrvf;N>o(U%#2h9Pe z5-Xjv?)X;E15y-#>*h{J!jV@z@Wf1>$U|biONc<_pEqMp^(^SXbf9uMY3qx0j|NWE z%-NN5{5bkpJrz2{gMlIi>f(+)fu<;wd5L_DK@H~dgcgO^au9BZVu(fc%|w;2sk!4n~>+1Jg;X=GoTiNMYq1q;M)CGEj7CN_Ax=fXQY>p6GESR(g*kUgQbcc#%FELz8%y|CK?K^mZ>O=I&+iG0$`d_B-b+1Q`V~&o=vb(vIH)p@vbiy%DR3;4t-ZoI3yC`?Mee};c=@K@k%pdIK(H+bLD z149MSHF$rDgJ#Gc&mO#c0!wBRXg}Tu@vj2yIR&_V_|GRF?_2S|4Rj6OKgXZtw*v1y z&<&3M_&(!o@Id?Vei()OB|s_pibZ#OJve)Wxv!d7c5q zV}U-;ZcvvB#WT%iiU*44`GC`kPV!u#zNe`usDZ+}7Stf2CWE?Ms9Qi?A=E-pKM*Pj zDj?MBpiH4Y2Q^rzPCf_iq*zlIfjU=g2ZFjxsOvxlg_;T~B-9*G%H6G?l)J^Ct`yt* zKwTx&ziM~?q22ufR9I{`f{F*gEKo|`utKMdCWBI?a0@6^ z3JXE0yuS~WDvyUisZw|fl*;=?Pyva{8=!(h?F1DP>T^(63FSr2Q#siKR9JXtgHkzE z3QFaM2`VDCBS5JXO$Md%#R4@IqQng@7*zN`>($sOyDyWs%2ogHR(ushHM)nkc*n zKur?rRZz`QP|EL6P%8G*G;bCt6@%Y_nj}7+0;Nh}op$$%cK4>{{Ymqtp5%lvAC!vU zouE|gmuTCEwe1?sdkd6`!RMOS?PP~{B`6j9k(#;@lnP@uC>6%-+V);;yIk{r4@!m6 zsCnBpZ@1=Mj$TZKHW-u&V=|~Qk}pqd+m|#I?&bLSPfh(^Q=36uBYr;trD8zjGu00D z2Q@%!uK}fO?*=toc=e!ETwc(&hc&O@G{?uiphk#~$3dw&_%SF|2YshI)H#|e(^LS| zXz}|qP%4%6psp3(N1(bP0lu??xT~ke(8g#bDGhKZ2N7O5Y8my@ipj7>sq^UWY zxZJ1=zZYvNqN(dOb&ICzH1#V@ zJ*BDjn);Kb`hC|4Bc`dOrhcobXEoKNsgE^P^gTyPv8Kv271h)vO~o{IkEVX3sa2XH z^0z9P;hMTpQ+H_U=bCz4Q|mPKfu{Cps#}R8`7BLcq^YYkHBnP@HFdY99@5l*fVxri zYUl+{TNeYRV*f9i>ea{NnIZ1{pe6}5MDvDf-gr$-ywIs{H-Z{3KIVd&Ce+=arVDlc z_c0q1Y9Oc|2{i`PO+wuaYL-wx0d=!bi$STnbw8-t!g~bN9HE{8RVCCqP*7>~IIn=Z zOsK7(ZV~E3P;-Sk3`&J|c0Z@(zYNqov5kU?2{m2YUXky}d*L{z#{aO$<3VO3wBMi| zSAE$iv}-E8_kmJzJ|DGPwGl0FeWv*ML{ob-^><;zUUK^&>8dD%MnQP4&?f zzhWzQ12k2pDF_x@h2KYa`7)P8l>^mX+ai&+J0wyN{qsF&iftZ}W>bD`i-y7GL5Xb& zN@Dv(mfE}lnkv(_gETLosi3wEYhF}S!?i8*Sfyi}rY30HNt(yov7W+x}7Wnl$yMw%w|Ef6~+rZ3{*8Ovc}bzp4RBhgcVs4xteN#UIB~bg#xzimDb~ zhq!lK>&d|0iNAa0Nh$sgG0AAn9OJxQ+=zST2xspZ&a2>X4xizCL|+&OlanXD4$ITy zK@~?>g?OqMldW9feh8b@?HF$O8G*m<_#aKqFxyRM57H&BxdVf%37PoZ1`gL0GMszB z;mSgW^J{RpzL4Rpq?;_xAHd;SL&nV(aJb@-;k*wH*BvsPPbueYH|HyGxCW7N(*@bW zm52=IG;p{ck>T_QhpQ494!0(AZ9;L}XhsMCImR^$?(fe0+(<_7+@U78QH+?Q`B@T_ zEH{D?^*7(U0#2a>uAdvd2ucMvy>leFk&8Nso4?>$?vkR&YZ+PmI^&=3rOO;qp6Rw5 z>RUc1ARYXO{mvg07;Z$b#O!|X6Bd7%tisKAgl_*>YP+F^EAn?Xhx)8Yep2I)p(yf? zHpk=f+nilFoc%eRZc42o#qo7|4(Ea#&cGZ_D2Fp9hci8gQ=P-9%i;Vyhx4yFoZshg z*5z=xo}_+GO5e}ne3rv$%i$QRNp;#%~kE(oS)@}mIHG*SLJX< z=5Quvarg-!7k_b1mYc~_<^W&N`8jdEE6WXS@5$l(GKcd-4(H_@&h{M6M>(9o=Wz1b z2+7Zh;VC(s^Kv--b2xz<&WIe&^c>EQb2xWraXLnt{6&@5qFsdmegeyD7&LVRZ3agu!Y|Y!%}^Qn~2Y7g8gTI3agycw+g6}efwpY zz?M|J{uC+f1xL zx0yJAS!Tcoy3K?jbeo9_=r$8n&~4V>takVW4HSDojRGN+NUNR>&`D{_w1`uM!#i;* z@n3}jNLEQGE|!`-51+Jg-4b>RsC3}7_>`HmQKM~4S8>=5(p7lb(66>LqPc3XPB7WP zuXatg8w<&1=)DxVqwxeg&Bj0V*yDi+>$s#rR~EaLIF-sJHwSZ7tFlhGd^*?ibVOBY zJ5gX$G0~~Oc0@upATOJ)i5dl#k(xfg3iZMz6D@FT;>~krtLElLILUys(nN$w?R?&h znUyhZ!VO|K&AWB7(m)DF>Sk6=l&kTyBgM1Phv|s-q)m*WjBlKx9Zk7Wbps3#MLT6d zY`Q9MS95^u!4+c~6xRQBsIa92$}aKCkEpPmQ2==rV@OQn@XJMo-2rYNOgDPj<4n#S z{QnE6uw0?&u4e-<;1}os6}A`n&*!+yMTISfyEox(t}7iKp~Cv%E;9>(0Y8STPl59k zi1h~cSjJAq0RT?wp2J^#84s6lsK&*)a=sf||L9k?Ka-KBDRZhjoLuOKzc6+}`oe8H zmcltFPvVNZ`grKvk3%Qi-8c&SCBvzL1=yp7eU1z=Y6Kg?@n?H6WMI8eQtU#U$`=NB zkk%Wo7W_$bc&j4fAFtv;_yYg8#)!Xpyl_s{dq5!f_4@@C zIVSs^c#)vgPfD^Q(pP!Y&zzkh1-PxXzZY|U^_`dR;rP$OpL0-V!Q1#3;s15J63&Jj z&Y*Em1D^ElldpJ+dP?)^L17sMwtvvP!Jt_1K3Vrx)XOj{cOYbMgj(z3@KddIaqU2@ zmnt4(sVK%n*;3OL4+|_d<=3_yuY{pxm_Ief?b)XR4199+EPL>%J0o3*bSEGj^q1|tXNFs@j`!AY*ArZNk#q$9 zOi%DUTF0;J2?|{;sQ-VVCs={J`=&j?<7naCKeq7hlBXTZeLdVwai!yEJps4jyMIUN z34kirt|zb>zP2xL@EOn(u-SI^1Z-%&T~ELsfIR^vNlzdQ@Y?kRlgJXBlB4znW91&# z4!Qzq&oh02srJQU*XFnC1w7nSd5qRy*MOrn{>N(h!$=-~Y`tdT&x~Zd#rCNP|8L&% zUjo;vy`QS7W4HW4xcmR6)0jGPoG);DSVyP``G*Un7f zYv%!%_P@AXnt!(b9D1Ubl&SqC9KE>vc-pNv2lE6rG8fY1@w|ZlKc)3=ck2VUmCj<5 z?fP}psm8Sdtv`nwKPsq5^}HyQ?1~er zcWf0rm^59;q=kaajXs`b0jAAW{ow7+&3muviZfcfMbi3!I=Qp1B00Dyk{nqWD%}jc zHK1AfVXtOB6e6A>2q%Er(m%x`+kJ-1%*(}67Yqd-c6P|-fZ7_q$mP0H(l zfe#@T!!iDtyyaJ}J;cddXcj~~k+F1TKXUAjdIQNVg}aMkwyb+EYqI3zBPcQ$&F zdq&;;(1rA~c+?y!ja^i82xzxKC=Emd?RGxVZmC1*7pXEXVC1^D>F+dgIi?qY*{;hA zmHm)#wdCi4@4|xQ>DsZb^R){x2FyUOjabCVsSp5LqxOfGMLDw_ zqkd3dvuw}++`s^?I@k;71Fs9iRx&M6+g9C-Gw1pE4=uuDlpl}ratu8H>L$R~qOBJU zTNsB-!ec1lYZaR9vKkbR#11{=$Eg&1rs+b6|PTU>)T1jHNgCMpblz%0kVjgkzO z?GCZeVA<{w^CQI#^8RKRwh;%rR)96yW7p!qKQxGVl`a(9qa)Y~%qyDb(k(!NoL20| z^O%6P=fc4w#WjpBPT<3U1NCAULa!RsmN>Q=)HVQv2DK%Qtp>H71j8f6JCroy*gBxL z%T@?)x#Vw;ha1H7Sd+YxD!_OROz=`G(6=BQ%-i@5gbur(b#ddjMTdRzn7;l&bXeZe2aaMGL9{0q7OmgWzb<=t)0CfFDS4^?az=;81n*I z9)y_+w4wD2V|Wry-3FAv)T6!Fyrs?~h^j(_j3zI^O$WmjSf!`>Ds@W?1~}z#sQ@om z`fXH!^)*Hvp{*a~#KWjt1#+biZ9Lx?hI31wqs9&#<|Pgs=7lyw>#fDW>Lk!*#Z=!t z72X|Y+YYqfd*eIc8Mu01-~}^~GKa#AQNJvV7lw7qdFVZv+yv`H{2W?6u{M}k?gwSb z%?62OXM;|}&&3mvt&6syJONP(ubXf+0{Z)`dmsR}k{a%;86+kOZ2#U8g^vuE5G~2ciIVfCy#Hjl@yEcB-$QhZ90 zpI`k;eFep@=^)|AL*^_0JM%Fy1g_9eVV$nj`JFYsaB!D7D+Mqp!WAt7I0h=-lILLt z-f06~Kr2A?TZ%FOjIU8U9t1`usmEB(PVF1CujA@>8f&N&3#;EJ@UfTGZygA``aJ-W z)vtJAF6X1B9a%o!l`~2>*^Owc`Cdu+4ZlEeU_8wi?|jFED`}^blE$XFRG#uQS z0GnLQilPxY#=wBOD!7>negnNWzkBBBhu|ICO56Gv(4?t8ovT05y8SXlwZ7-*m$Jt) z0nu<4#_|MUA-W(& z(7qhR7)Hy_YG1a%#wo?x%Mf?RP)ESxbZ;gyGy1Ars=u2n^mGM5m%NXKDAn;<_E78% zozp7;yLNR1==|fT@fB0M zCn~&+X4_6jV`}$7&QiPnu`aLzqF-#^AM&QA#Ij6(7)->oD&XR~k)Zb720AgSjeR>( zgco_fi%YNAK1EU^l^*HJm{0=Rb&fyAXruZL7v!X?V_cBaF{;XiD~vKt!RGXjp`9!9 zVykQvc7n5{MmS2moRN{{^REcssSg=tz6+k6Y))6hApN5X$AxB(n=&Cs+UfLw4s@Hn zV#i9|4>m{XFOQ>Isqi}NFL!;7K0mANo%X{9rUsI9S4K!ai8;G8Y>lW+JMHu0kKn&` zn`)n&{tx}vLG)j*(CS4*t`@llWtH|RzxqFP`||H}@P8b8w2G?#`vCptA@rX~Sf_78 zcB=H*@x%S86ikyjg4HwT$zU zcJOvakAWKDZhR3S?s{Q@oeOX0V9o)H?j9R^w#Q8Ym=v%b!0d)p!IVL=t3gm;C?q?h zha}4deYb|GgZ=XYQVZ{rLNFdIu0h#EEu_nuH;xc@0JU2I$8f?#0o!f|UbuQe(qu`R zaRP&j!$S79#~yt!u}%t3~0yEAK`kc@N702;5U&MI|S z!O69DCYtZ|*eO&Jc1c=UvN7X;-L@t6w{Ws6gICu}*%F)~gsc5YE-ON48A)*(`NJhi zHkFP^>s3Q8qSX2yt}5oFVf4XKE@E#wy#nsp zq5BQElDJ+(?}J&zrM8mzk^@##heX@~E!_8YZB zU=2U4yO?=$%`g+ib0NreU4zqiAc}Op0heW|D7Idu+r#YOaq;R0Xl@k(S$qxK3#6RY z$+Q~G_zr)lwjtKl2|iZ0GGNqpS5c5m!z2LSkptF)Y_;QS*oYvSOO(mZ$*tg_oz*!x zN_BCFCY^7~?_E^bULWZ4};^DO4DD5KVuieVo zlHxmFOi1CRZ(vL?=W}U2^HdhtSM;u&kr-@3dvJ zr*f&Ypk*HymwWN@tov3%uKNp&LesYGeb#-?9LwHo-M2c&UXSn8Hs{8M~s!L7vWq`kn@&bbOWtqhXb7K0e52>WtOk6jmKJn0JY(qFHkb3mZFKiMo zncv>=n3?=#1L99O=JTo$XGt$nocS%RbbBQA1V?OwCTJ$_<)|W>?6a+IbM+{*=0!EE zj+lEF+h1|dg(ow)xD3uX&;2{R?p=owT@0~A#w#K=&<>{4t(W1u!D5DZ&D8F-72cf~RXgR`kZK8+7Vy;1P%xxxq=_hI3D+ATUVvf3D@2o>{LC!VUtw{co^*UtT z%lkVjc;EQ}J=AtmW7J+lH+Jmp?2(lynfV&9mYoWVosH?W@ZrpNH|zQCG;mR=!%#;j zndx6<@&i^}N^`6nSvQM|AOcqFY3=PpF8kFeoA&mnx$F}i*nf`6tJ8lDciE>m!@?=M zGUiJUFkB66yY;HFWNqqyH5m9_ z7P9`x1^{2<`|KTpeGat`gCDiAKk*J(>5#R<`L<~$m+)ISDtlLR@1hAHz17n&>3kjk zm$1d)?Vz--QJI%drl)$+zl)wV)-=1ru{t-cNq|2^7g5Zf=Jtun4 z^K|dmZBo~&F2f5OIyL!re8Xtc6J4d|)S+Fw{xDaNJb{6B`3-nUFAHv@c`boUMLa7vk z##S%S$W96YX%gN)Ku?Wo<_X|E4Tr!dfUd&(YHSgk1$sE%&tXG)40HqD@e>sC;8MJ| zU@OWB@OR)n41qpQ_jq3nyrdnV19}D$w^#_ygS!@5}K&19SlI>1QKdpsVn{>l~oUfnJLD1NaXIeeL8~Gl4BSdDe7n zS;aYpTdHTy#kN)Ag4)b!3c;1oE%}2fELCiW72iN7#mTDKa!zY9PE-H~)H7_3pw9xBQ+cDCISTmJ1t(iM7pEh% zn|~PL)s3|JZ4hPuL1fshcy@&Bs`8XaJbND#+Vz;ot{p+LQg8?8u9Sna{Km+x9gE3D zcFpJqPnr1%x;uM1PK*hdD-_EH9nLtz;VtS1kc}sf9T^!Nniv^&ZD{1^iKE60AAZfq z(V-wqhrqAc8j5p#)95(M{gshuG&J&?yB}^289HjLIsDi@GV!7NW4q0c&!}+Z%40`= zG}?yl_|L+BDgMvkAI5)Bcbu&OK1oXvdN;iL;r}LR-r#ly==EqjdgK2%{!8(X;XeWY zVJEVF$%$1#%LP_6DwovZo2SQS%$erz=NU42_V<7TJZ+kPOcm=C9Zas|Q_el|XrTNu z?o5;#(Q`X?oN+gNGQv2DD<@V{tLMdlvTTb|k@9%{hBog3;J^*Se+B6C@Fu$468wLL zKY!QEp0&VFXydulfoTo+=UZpao`zG({y~@fFY14jUpGDk!bYJfU&ruw#pV9~7hiHz ze?xMDqW(>p&MJf`;6NAJq>D@YA2l31$IIxggUI#tqg_AC)-%X%Y>|Y0^B>!34 zexYroCiw9-qgJDn)eIM`>m!{fl^_-07`}Ns&HF<04r$(rCpdmj2c`U8q^}yjVn3j#6Xz+QRGj0Wl#c``CGURCdl1wB$)S~+_oU_#@m2XH;_Co$ z_krfMYF<8iM&+&>DCN#P89l4`7zaw#GGetV>c^U@)71T%dRSBcr72>yD!=z)EMFm# z9|EOnUZbWy&{Q`xsLIE5P%3`anz|j7is`GM0wOOTy`pkg1xoQwIo+YAf>O3Ofl_`K zYu>$@_aZ3e_XBO)s%?LUnS_$^ASfkemF7LKdCi))RrAipEJOM24NCd_H&DYQ_6?wv zw!fp@jYfZ`H0K=jeo9{^fl~Ui8I;oY(SAo?=70)|yp^C-XitJtp{>)rS2XVf&1==X z{Ieau-9Rb7=W5;snm0uAhJqUCVGFVa)HOnV0BX2Uv2z@G3qUD%tzHMmaw__nLE`S= z0*~i>p#soEmA2zhmdeK%_#Gj(czBRCIPHvX#-<8E6$=lInax9UU{gJ{Efmb=p)V9xP z-fB%fuWcJNZ@s2AY1=<)UX!NY)V5nS?@yZAp>6-7c`cgyMBDDsyuWK|zqb8K^SG{{ zN|37x#o*%vzUFn;6xSJ)ZBNbXr75mFC|mCDq&%NzzNT)|RE?%;HI>lRJ(^mosrxnc zfTkYQ)Cx^KqN$ad;=UFYmnSv#jHXs=>Um8yXllKt_;FK7snJxerV^UEM^j5Rb-$(_ z&=fyiD0wkW&DYdznyS%Mt)>#1LWaw_6vX1kmPWN8iYkP;kMQvDjKtsT=@1b?mLRfE z#!jFY{CL8(BeX}_-GYBV#|;@WQ{ehq=A`Uz!R0DjhVv9STx-j4UZhtfA;Wox9Q-h} zL+DHgQ>uROD!(u7cLwE?I?2_#jGXhp;hJ5B!-F(jxyx{PRFCU-8O~^MxQdtIOaq5& zc^S@JaJZtE;c(8wb-fISM}D}vm*Mcs8`tz&eRBqg-TM-H5Jl6_eu=82H{>Kn3Tqjzoz4>0| zdiKwaQ2>>P8$aBHvmB{Y#EONYhnwHSX>pbt8ty?#`Rk1T(a=J9V;z6gx1*tjK4!at zQo&7kKJm}~t+Cxe&EST$JomT3c0&zU0vv%~sP7N=v>NVfagzEU|Hw(1!-TnP~%Jz+U%e>BU@(puD43cTwp>3f0?L39lrtYA8*o|GgX(U3J8!DZY6Q4 zhaJlp@Ju!4U=G%yEd;CJZdC#_p|a4Be<~1%tpNgE$cS1JFc)=rR1Ym90~?5sFN7~X zI!d5}$ESqKO1Z?qG4sooi&5!hud9TK(=nG3{iZk_m-H*UQ~~y7P?~mqq~_loO1eif zY)PB8DZIW1cBpo>FJAut1(c>87_WS_9p8+1sc_H1E8v+Fa3i$PW*pr8={@Dfc?LYW zC{01Q8}L593w@wmarJ=i+T(Qi!Joea57#dMJP+bvV5>j5&-KS63NV9l(G(w`)IKZj zbHJXj8ai_C`z(zg1i}SJgLhmlXPh{Njvp+j9&5(;=f@`BaWdY%*e!S#7MgIz3Q! z^#maRTWPy4wD_qUTIAvN$TZUfGkRUSeJwMvuZ7d|a8e!qjaYw-SZ^^I147p8c=dw- zqjoAp#YJcU`=)BA!++)5reD%1R~z5U$na|8Dm?RIKg2U1TatOTaX&l@VyEHR z35No)Mq7d1Hw?l|_yBs*;EiDa4b6#Q0c6mu?6vjt%Gw7k#|Cdj@*c;xSD96WAx-%% zUuV=ZIuYAp6bBx|sTq0U)cpKtD&R}_AVY-^N{sHLgM+(*Kw?&>JSPNr>SQKv^X5B} zAfa3z*I891rlWuJDu=>f-TH*1(zT39+1%T;kiVIX2idTVh$xr-67_~Kht_| zPugMA{Rvi+S^mzQ-Qv5w+{*YlHo_$*6!+sX$+f7F5$r%?szP;bcT5kZu4yaX+}b^g zo!yc0&9Tu@>kS0#>l!NGf7@=fR0DALc=&JJ@-svW-llW^*cKsYrZ z67{yyKazJ*Wyp$Mj1sLMpBG9@xHu0os~>kRiVs@Q&&Wnqx+oYxSv+6dPgJP(bi@n- z-~gC3{U8Qe4;${sk3$8~yv=ZLv|Sgp)&{MOx;@!Jw{6Cf8l2{4d(wvXBnCQl%@KZ^ zh5yF9u=R4dWH*kn8nyQc>!24$8_-C{Cg7PLyAn@nOAbm~au99FMfifnyry_F(WiGf z{*tJ*Z7xyf`yKo9mH9!(e1S4Q%wnhifGsavo;GSX0iJ~APm6G>8tN9xi>Ah) zHL3Q&;rCf+%IE6`Tc6&jrj(4?qvf{9pfA6pRG1QNIP zMRTHrp`ke+&51IE64CR~oV2+dP8nQs2XGhsO;)Yz@*2|lYEy5c zQNJgFeT)sQ`RsPW=Dw|bkF#Px0jd8Yn2Z&V3qo7UpxyqE^@X`_d$4?TaX4f||7Ix(FR`v@5p{Y-(bi4I#FnJp&>s{+_``!(f!;=wx#Vmh4l_WY`}siY}rZ3&jFwcbIa zZfz^s-FV+<#Wd>%r0u-u=On4d|oTVlLt0aU@ldkC>{F!rqPE>X}CU zOmEP72LX-AFZs;0_BXbjWtQ#_S}&Cx4qC65ykpdl$kS}I1UB!Nyc6`kk#`1>S z3twPsjFUH}3>}!k2pWef9|7AA$f)m8@|krQp_CrBL$D4u9z3h`osxH~gB)#mcL%Na zN)B6}H6A{z^s|!Bya&tI-d+&2HdsyJ)B+rv8jA?OWuoepyk+gCqunLDt+xc&Emo&Gd^(gBuq z6eXQ9+vwrmy8WmRsS!ueD;rDZ!;qc^Bo}EDWn9d*GXUKbP7rztARzL%Rh}&+b`nGL zG0XS>ebyG_97h)P@FH{36g(SqLnn0U$`5+JNc<4GzJH?v#SbB0A7w3wAHwV=t|(t@ zGRy_BAK=+3b_P|Q5~HbWyn)1^E0`Jv5Bf584Ya!|(Y-52(Bxm4h#m)ljp)Yi5z8wR z-Mg@k(Gm{3(awn`%*Q)%gS$fI?|H|c!N8SJxD%NwK)CJ7z-^gbX_wx@_@j$tj8MDs zBdM9bD-#n88C@$W6OPx>sz_az2iqqsP?J zI}J|EGNjj}EeyxQpc0I5J5zFjTe9@2j2We(A;?JW06D5&I*;T3zfVTDp_Ziz8mtsXO`gkYH;AJ;y%U_UL$9wCZetl%P(&=Y9lZM z9gk%wa$2+*`cEZGz)eU*WRN?v7%rQ2wOFhdB(i_~rkd0$eAytf}qvIqe}K&2o%%W>4sS2R8@wPT z^sIa$BkLF@g29>H_=fzDbtH&XhmCuBL1x%mhXMYCD8^BK>PWa1m3KoS`=CIqs3LhX z0eC)5Q#;{QoVA`DQ3rJ$hr^9p?n{g$Pv$|nUE$0+ zN15u#)VC^9eYR92ulF$K!Nh!(?vN+VF##espl+|RWIS{;ijF?a*-rdJv}b#uA{F+K z85+^qENiOv560Z3&`e+?tj5x8eV70`FzS02n&mCkhM73U;R{dtx`?bnEQe4~d9iDq zF#yW^4h+X8z+v)2mq;OWIE2HXaFj_`ysA3BY3WzsX93o`6_ea;QN$SVeL1|KV`liOhUI(9ilZo6Rw%m12F=#f+vMq5yNqwYD>1um+v z)x{{$cj{nl->b0uJEzAOV$=uEWXmw9EBcDdkr&U1VqtQIE>3wKoEUT}nzM1nlGm{! zSCKpc709Sv2^x{T8cn^*YK$;}FZw!oKtTOl`Co5;1sQpDmtmidviD_MHlAYlW4Lf9jP6Je=w|GCK9Wp{W7mquzs2FWRY9eJg|3`PA3E zVB#lv@KeOK#(o@q)SZrYApHcQf-!fWN#TXT;(!O~mFV7^#>hprC)Q5XF?12=Tx3w< z3un%h4=C^)I0edyw&2c`Ro<=vTv@rdpw@c@iO|>*X%@kCVA51 zpdpUY?@32o#(*b%8T*mcN#hVf=OlctJV8y;=YX@%Rrd-e7c%Y8&-<7eSioU_X*Dg} z<>`S4E!W6@XRl6CYC?R(4 zu>xy-#2~JMQU5686HPwLB`sssLPjf^nsqavzz(A_UXRHAiW^rzS9`7ZDy^q@%_~f| zWA^m-N^2owjs%ROKb1JsIviCAV~XnTq5~)W08aqO^m!;3Nu<Css zfLNo=2`BGUsd_b<>K=)vev*Gp>Ws+Hl&>6<6Xfu86 z8B;`{mz`iXe-%i;E-?jb`UfNNjro{=pmy|uM^Je&^d$Y^ZLaP8>b&M7I9c6%1l|LQ z{$}%mU;=iDs~zufdm1LYw)2q)?GQpcB%z&#pcAl4jC5S5*CStyRg(Xe1AoPgH|lzz z;fI0wZPXEfFQafMDi&Y*uX2jG2A(;DxD@|u7$V*i*}zWBM+~h4Fpl-XIJSWCv>HLq z=>+DDoe?WIhxu0Y$ComSp~N*_L=Ub)iHUh6(q>{}e)D0ZHZis!k{a6uc_r3keaNTo z%}30{oP(jnjfc#{tzQ}S=iB+~MP}uRH*p?FoR7>(Ou;N}ZeFx}zfm_7s*x^00@S(i znrE*(C1z_a34#nz}rJv<;ow^LOY*+IHDlWULb1MBP%#C@O8e2Q`I8LbWf0 z*GdjvdGHjnzQ8Imiq%}hmA`1x;vP6OLb2lG14jK`{M#z9KwOa;khcDd%qX7wwE*;t40?eZ#>bQs?Do`OQZfzUM&1=H|pQSzzzO-lr&pS z#uI-w_ulAD)#fWVZ+fxVu?=pKnO6wiq#leO^WCYR0&Zp9b5zL*kT(*dLIX^~jO^d(T0f zgC&P#QTuq{WF9h8wVmAwN}PjumeYx~KV-d+6z!doNe~i^m@p-wlGl+E%)$0f$x4a1 z$g`pA1IR_Ab`FaCTLgaD*9Lwegw6~!8Nefnl-!bG#9?i?~81mg4TU( z@r}CaShP$36syT*;`>l!ZzwS*FMT__B6FmkL06<2^3A%*H=D}zDez>=x1CylZv4pS z{WDrmR#3dCp>|t}mo%{GM6h$nsCyUV8t8BXTwp!vL!b9>rS&LHc)g%ryh#qG=kQAZ zJDg{_c*N~d260s?_8(|9_cZSCncfYEnF<(h2^nuY#@S(C&($Df)k6$!NUF~xSgiWk zm;MO~$W9L|rRNG_acfs$u;gH+b)QPoeJV-!iCpWxtR!_(Vt+-kP_V`i;Bz}#E0wBW zsZ{++rRrCbsyDIjMBEMC{SH2J@A{%xIl{LtgA{;YpaDOEf~rI(6EnDClou;B7X_8w3Rin-YDKfSgfLhX!1Vxdl2Uo<<*!3j+_FwKm5_& z_5RrL!KB4)dO_=M?z`T*-WTf%sXzw0LyQ3l3}e>8XPVHV_%0u!FbUmp5-~wQhiQWq zQ={$x7{TPPI50%l?ePRW8wQ{4U5(J~#V~@|33?^4e1Nnz9u5Af%2#7+nQ zaP>);J1yJ^PhMCWg3F2V-rgy=-r$79;ByFZ`#NmZSBQoX;c%_N-Pf%Q#QI_!hLjJ= zt1bqL&{hT4^VyrPMqV-9 zhnVj44@6@yzF<$P)bK9?W@U$F>JgZoYr6ZVjfR-_4Z3EAlt^eghmjsk+V zQ6K42bD;CBmj;ub8hk0ftmXj9;VDF{<^Wp$ABm#Whr4bFbSb(E6&>pnn}Uhq z-GWxwH=dZFwd7(cD~!d{Fygr`WWBuajc97ZX>DfX?mX}2h<8hTXD5uHWB(PzN46UFY!PTFUV)JVpz1F1Y1 zX@{93^_q;&9I3pT+x8bJhY~GEkx+bU0R}~`cvp9d;FEBv*by0LR2!^Q*W{^`4Q-3J zb+VmohWFFN`ymeRf{7otA#K_IGk!5BJ8~?)hmY;|t7G{+bZox|U4EmfA0A-(>pZA2 z>0e;nLk)uVK#4F>dRg>m-y4yAZ$OFiv8ANK`z{oTv*X$saE%=&(UO-%g$^qvfeO8* zRcNJFp}QOvnj|W8MYalgMTM|l&s*oYrVSNBk)76jDE4ukhI+A@97z1+Nc@mvsg%y`6N=+l=KB2V zSU#aPNB4Qq<&#RoRM&N-(oDuUMbEF4_B5*IC%zMFfNX!>*xk4@&rzFFxQ9MLbD&F( z@^sVf4>so6?azN{<+%#AYY16!yMZ2zmB ztrRwFp+KvIo4V&l<$uXrBaTys8l=OP%6i|Bi~Rf$GRimP%Ou~ zBi~Rh$9l6?t%GQf(;T-+^Se2Hx{PU2vS^Gqr=zHD(tq0KI9$8UMeXcE?d+d`V7L1( z+nMLIbKkR_yhq!)Kf9g#P0(?-{NKrpI7rQu_>XR9pPxx6uU4>Ky@5ffO@<-BCU+nw zZ1QIr2VEot&sYKa!=*3$BwlhGFQzKNswMV`6h^E!2B9zK1Qw&3>$)V%G4~^IIdR(2 zcMZp&;m>QJj-Fu2+a^cB9?V6HfOID#5U6CpXO^tP&X5POL^`3kh{L5N7oZ=N&(qxb z77KvilkC5v9e3Dyu@Ww+_F|{8q#NABsjvDZu+FG!xbrxCal^qS%y_sSR70%%URaMY z7V$_^%|Wcz6v9i*L7+_DgUA8m?jUwD)YgEu4wdeeg%ox(SQv@T;F?HZq=n;;>%0;H zd@szyx4$B+5Eh2KTTNqCLo;?|VOnfrW;No7wWo9o(sn9*U@y(SzhQDfF90(Ode>om zj{Pv(lhON&nhzQE8%=ClfJgD9gMlXsw|%KFwz|BSSc|x>t$ZIqkeR0luLG})8zX%P z5hP(Av?EOBVM;6A;zZ$qGWq;~QMjKxbRRZb0&32D;Vrp+5N?n=9NG7g3==eOM z-Df*JjPC{U4L;@iE4sE+Qk1CckQ9?i5yVrYH?^#{XjyM)8CFb(W3_M{qEG}~lBxXL zFbwxnk%A@7r1P+|sHa#po6oV0g1Ofk4!?=1$PoyYr3fEIsW11g00SDr4}em{Ni2Q4 zaPVp{P^}|QZE}H+YB3XJ5W8ETV#gbcp5xN3wIAr+xWnhO%@x+m7{mK;iOz)$P#?U{ zY{EN~8rK$S{J3W1p-yHVe@Ywz#{%=(pH^O#%vuX6`K_3_xuSGoO?17g?c=;Tz zo9N|jdU*-|FX08Y+u^#$+&y1}96#{9zcQ$N9jG*IZ|``)6Mu)nyKz4}LVJ4rWKRXq z6l>o?oEhpleBkcC_}Tyk4&IT8G-KjKT!P5No{?h;B%%kH5{j9!$Bus9e9kCMAcn@;r_`05&8FR$ro+>SPT1zM=tbq{y3UAsp&fG= zX!VHlbzNcV+Af)>7czd9@)hZVsCRXu-bJE5TUnW1Q#W*TqTZFU8>7UT-4ONenW%S@ zsFx{UBf6)C9p^;7JEMM<@-^bP_=e*%Q9llOzR0KiEI2;U^jZ94BzD2^={?e;c~Dh* zvZ|u~p<))GtE$9@=-Mr?P5WVTsxoTv8|d?v!i)_Vc9-xbyOlH8t=#)}Si(*^hSujZ z>q~6@twXHocS0m5nz#bY`g8mv%mMgC8miD^a57q3WJ*t_c4RdlG!s3-@gta;+z&UP zFuVO7LIs7|eV3xJ#|LKY@xilG>^nhgYv~sGvdPxWAgKf;~(>5F~VkiDp3HTKW1=^|^%L;*l zA>4K^B0FVlD6SL5`9xf2ZKK3>rZ~4C3K8$k$g`LiJ9-+h>Z$TmGA5D=VDBTkT$MTH zTaCJ6&NS|UQUsGD5Z~gW=k_4oFJh;$r}-cjA$INQa*(TCwKL$xOwDbJA3^)yh;Aih zHDN<*A+`z?g{NYF4cq_EKE;1a6ki6h1dpccJaz{WtW{PTvnLeeJM(SDg+RWK!kc$1 znzQXuYeS_md3&_vwMwIEyC?n;*5MvP*lZNbqZZuE*@wmw&D$h2e{ITVjhu{?P-7-+(ej$cOg<(nw)mfU5KiE47aH8{>%}kpG30&pw-Nr1(BFRg zL)#6uvHkC8K;dNjI<%75Jh{%O?Tf@X>p=kLFT-&M|NpB9BK-O6(d<|{xwIW$s3(1C z^a$+uf0bJZdr!0+UnaI;=pMA1;~(rXu~z_Bov`yfXP>m&r>1@?bMmvBL+n)!8jHHX z0&UDK-qa9$YdiaGGjR*9?OAIq+Jn#WXz5M#DdX{dYrlDR6S{}I5Tq@76L#>lAU`$F z*W7mYJ7_TT&GNq)i`FO$b}B;(n)jdGgxMPJl)bM zHKem0g-#NMo0T6EJ<9k(9fi&kg%Qfrg2L3iE_M{Ktp`ykQx+yVlp$U1D0Go1d`F49 zu50S2-E*Dw&|RYN8tj-0$HmtjZ%5%c zX7-}rD^DYi=NG=Sx1jGlKK&4Q=p)X-1gCU+`WBRt{XK3$m!eqL%>D+;o%oScLlh#pX^)MjeL)DoI1QrZ$tYfd-^ zPeOtLZy_QA!2pp$2qzLERWyk`$Moqq)9H-0b9?7)=i}JU(CKtSTWuwv;H_4QB8V68 zf_Xr>2!v~r|M$1|b8-P}JDvA$%gOURd+)W^UVH7e*IsMwy_afUVBvM#7q8*hAFt#$ zJ^nR*1M%_vX2dV%myHYS$p^rkl1L&4R~Dk2z!qKtsh82bj847$p={+&6;_lObCfGEIi_mBDOX z+V}T-=x=rBuJldeU{-?ZJy{xH#nNH1GzNb6i^$DECk;P5a8Dn-loe_JH1WTRhuEK$ zJd7_dkK&7i;Wa%hyS~f%izg)At*Ge(Q2-k;*=q5{J8m#UU=F!-I??wI)K>S z-4*$VraJ{OR;=au2VyM;GGki~#6oL(V#L2=Z^jfzzL?pslCV$ycnL0CHuQDL?5_C3 z`V4&EJv_u9co{BW?J_Gnj;(`*+#)}I^^?j)H#M9f1dQ*;wM-L)(iRuT^9=S`Ey@Td z7ON}j1MJ2M2#5x+0Fa=aDdqgltMGk&H9WC&LIboTm)K$&gKkAc-SLAP{Y_Lg(v;kq{(-{dlv6k@#nk zP0el#!l7W?^PwtN$v)bX4hEb#ge$PJnYbXnX>K1QxqNIN5yx|TOIto9^q6nN`OO~NSCW`L^xJBETRrq! zT;JkDzb(exaDHGGjC1ZQ2Bu1peO;vhdJ(jqV)m3Dgop(ymPLfh@n}-9fyuMe^2_CW8myA8h zUb`w4_v$!d>PUf~J@z={$O4&IukdZFG@ZGv%n8(ENK)LEZ>MOAYix$?$$vM?g<`a` zw<>(y<-U(gecSpw^j3^VwnH!Ughd5ca2qyCnNBGT*Um8x_&0yaPl>LnDsT#|5jOC?_=_|mr>e;c8`xHt~@h6)X_}wdjUQ^e0D0OiCRe13OZwr zfjP8tKK~DJD=pjFbBDzom{sdH|mfCOC>kvBNpbU zcs2==7iAd~52|Q@6oki81ja4M*=EG*y%~H*tj^(iDP3}lx?~7h)UY;f=6u4RYa|4@ z?`CJ`)By=`GwQ!_i zzsPT994RRYRM}g)`57w+>do*Oj|{kJ5uU`srLvYJ)Xh;Q5N5dMUdh?A!;rJN{#&5)S&+b%Cp3`GpRV{`*^Sj{~il9JK~E> zzukp;@LDXnsb52Qyp&C#)1AD3r}NMd_E+TZL1L8WA45AxAEX+@o23@SEBN)t^CWY` zb_8M8pJuS*l#Q_gquQ6Kl4dYAl9+oslDGv~-^^g&kHK#*lg_R#<| zsvSmkOrUjWEw}KzG)o20T&6Q9s;Nl2$9&Mfg@3gH3ZS^0g&(yyK2Un>P2}xkzY2Im z!^qnd<-d-?ZtUx3Y>Y5cbqaeKwv`@x8LF0)=kEbye{9Qs-9u`uDWm8JWBS~p(wX8Ki>YMBJ*AP;Dt)g_9VkqFDlB;=nQW6w1{I#FWP=*@cy*Ty68BR9 zKG6cdTgl~x?^uJXWt6=}qEhjt^ql1AUr^&`kytruBHGk+6tfZu(ofU2OGp?)LLHKD z>BT&h2(gQ36{_4VC}a(&Rd*w5j2uNe?QM*}(PHQop^92rTNog)lrGZec}?PKJrBhMn`nFv%g z`jMrDuf*^5I*YN`#aL_#F`|Yisx%}^7>bpwt%@0z4Ozf9#N-T#MP`;_Fjg`^V;Y!j z9ABh;O$}64Z)PeI*bdCHpgi9nNr-;MgCLZ^NA&y-N+6dgNzS?ekbuw(>BkW=3@pA%B@hN( z98|Fv@)-<@AzKM#tCupb@-cFjX`M?)f&vRC1E5&|jN+njHBtp=41hqe?ovVi6cDAs z6knvcDj<#qq6S#?RCM zj3K+rMEXTEypD!9(eP#{Z&sXY_gs-%q)#)!)+MQiw+%zX_*H7@(?4gnvIk_)0{%t{ zhFPF<%i@D(Ljab5tZ;WH#`dqmQ<3&j(N<#Kk1}F0YO((Q40&0le}%-DbCaK$Fz~1` zl)hmGHBym)UQGfyB^CKQ7{C6~EpBS~a=Z+^VTN#%8N!8(RIi8OeKCgNeRXUZ4T%L9 z6ilvWQ1D(Xo+_ikG2j2L;9iU_U77#08p~zK+itiCoXxlxlc^C5g*4%9&WI zhb5}S7_!0?k5&RXVu<8w>J(p>(5=Ozl)%u6A)Bi+5>*n-GU32ZB#_0zFj7Q zryS=rVVOVXy3M{bA_se+F!4C(3Fiv?YO$$-_*@gYW5d4wA{y#Ik?JCfd>_nFEK>RT zHqCHKIMi`rDlt)+o~Tq39+yP1y!s_}II7OG@HAnAO;{#{ zFu5dn$0REG|HVRBtn?`!riqo1S+UW=bXcPDLZymrkT%7ml*90pQZo{jp+x2R${>b2 zPl;k5rmENoWe6lHhbvXsFozXu@7pwyVVF|=iOS4GWri|n@CB48k`AGhiT2YzCHF~V zO?lZ02(yj3a~G#ENl0)^hny+Dkkiv)Mowq{GG9ueSui#K1L*GR%jbbx9MZ; zOVLiOm|`%d!AIOr+67JassUnCGa?4@yO9f$91?-W0S1C6IgBKcH&UTQsx=L=Xuey%*i0OP!NNI%878+SE8O25mtJYlGu}G_zH4=Fe8b zuyKNtgI4Yx3B+7lnag_#lT0ysVfFcmDzo4|j|45aOPF+uG2B(3m#ETgRmOWvhPx7S z6tnWK&bHF2jL9cv^+XaRoq|?6X(17lbdvf~Ea`+gBk5GedrZ>F$S+AJ=?pVREtK&01mS3ei--sF!jXh%M-`%i6e0U0K@T z5j-C5d_OB1JQ@k^$Rt7y;J6zbRz>exnPLug%I4ST*~k5sfz+JrxCPuTZ{a@`VI`)0)VZs$Hp6u@o< zP-%Noy;V{3Zdv>2-R4M@(7Hegm>Ha01qK^?1KrwWyY*v?x~1*kU&EU@+3+3S)y+4t zEg-_-XkI`2Ht!oVd3)nE>X(Yb(-`G!8F`4H!$kg7tLxKC@Tz@jD&D%!@TozTshlrG zCU1{#2(1{pe=gZ^akv4{Hs?F7AuHi^i}*!(k95F zVbRGShuVHggQJsAhT49rmWS3JgEtt7YqwqR6sUQ}cs?5S?T2AQrd8oY)oE9oF&OXB z$-SYrcX%dKxA>O7uSI#Sj3j146y%tC7jL$^S1wn32hBkbIN}OXAI8-+EfP8QMmC>nog+0JNnWu;1q7 zsrgu3yn*Fa=YgiKk^EcKPoqf}8$$6-G-CVUMevX5Em!CB5fv1W9U@89#C`+}t89)q zGdP9s24_hnn=#&j{MwpdPPAY0HTS;_wIYV`e;-;$fakiMLIx`1*G2NKeGRS-`+gts zZH2KpC?K4;DUaaB*?E%q^>PhXonpN8hCBCYI;_NjSk)5^zNLgd#vW&-G4x&!2VX~) z(5rXD8nsb9E#ZmrMv&*a?$A08)`>Kt_%FHZvnRh2ZBDs(>+3cM3;t~~80<1^ zKP9Ul3#@FH0)84u+@N~u83CJdWvpc29f;q^7tyn75S^6?WOfUgVvu>)(s_3@c-SEG zLa5bcO0iu!@T4$TBVXZ$2Jt7tD?+Tq!@$c`6&la=S{tbs-&3tk>CxaDNBAezb##-CBYAmTTs zgY8{?;mL1?+N3W`X6}Ad+?u!wsuJpSxN(bnom8}b9uMPS7{cSl+B)5)uxnt6$LE?! z)fb)o3J2`ORF0;gH*^uFcD-&D2=r(XNaC4OOU zM)m7~tq$^@le+Re;Xy_Vfe9@gua^ zi08pt0Ti%W!RAP^E7b8cI>DqB5C#Oh!#;F|jyJcSDzDjjLwkX;&W_Robc4fIH$YyZ z8!&--y1}vO?Ky`G=TddSlt^-4ZRb9Gg_@Bx85wO06Es-5D`8&Qn=M7&R|i|Bd!K1M z?0eCGLEV8W3!%i}P}}cWo4Ummvt#2S7=iw=FSGFiTK&zw{|!{L@8!w8kgrh#;`X3B ziYOy?;JYW_o5A_hfIavr{FH?|;c#IQd+}86AuzUnaKBL^dK{h%n1B zhUG}IJTIFt$JKe+oFt+b_?x)L`aXp4(s*9Bj{nRK`<^$Ukl9}DB`QZDL!<`a`O|D)2*cYw8w!t-CR4K3+n&sS|Ev@~LJ@O8-!LKo{_9xU%e%l43E z*-QGt`?P)n4~yUDXWno5>D^mP(E1^M=DnyKzvpT35q;aO*B6wqM?cEE>@ZKguky3_ zC_l?wJ?#gd-|{ez(9PcGm20Qoe#_?#y!A4FhjzY5TBnM9the`gS}6DgS(5)bCG1tq zW%v{LhC*#dh(9&!JwzJ9`hP#0CK{xNq0fdga`%D!1szylzo~sduBE%+K>Svu)nn z)Jjb0!e^<0$j6sMZEF}6#NCFIFSP7B$PnMoS(BmG;RuHEnrBVda`v-!?ImzwRpUvk z_J@5hM46z!BY9j=v*iXSduGI`WNY6R0wfh)&p(^#<632SXsS z2jg6j?j8audU z!tLY3Yfk$)stB>EEfJ%(r4xo^fH%IZ`Q5Cr`wdlc_})SF0-74L+ek^ z*E~Qh0Qh2je?KeC(Hk5y)YtfZ%uDi(Ub6Lg?2+>Jf=yGHd~Zk6qeXMTuFL?@L@t@I zc{clPrkqWhb?;%;1&4Gy^Xd##*{jVNDv;flq#VPzTa! z>-&3<-VC>H54Dz|#hMNb^PmpY?l4Y?Els{+x-aI;en6}BHeJRBDeyd^KBIbnz!to> zp*J&m>#dWd3Tw&V$`ctHP^i}(!1PqkVS3^ijxegI*;EeOuHg-f3Yy%ysag6U=Hex1 zN|IxOyYR7a1kf}5>AK!|@(XEX=jfWwu8|R+`NiRariD$VqGls{m3nP;l(;zJ@S4@N z`><8xzuJJh`LsVK-_Lu%cU!Vy;7iSULTwBG!jN$>kpKlOyw`%utGrM zMd=mJ>^wiKo`|ojw&gf2YhiPZIgTic;^eF3j6B;m7CZ+jle_g~tY$-u1L(jRi$TF= zHRvEp;etD%$+|kUD6B*T5M!_?Z~uvDIBlGCjyCQxZ9H9C^GZc~{8f>sXlgWhI-0mA z#lDx9Vz;PpYD^nje>j5X$ul6PHdwDWShtN3XAT(+lQw}-(VQsf0VP)F30xOU9LRG| zGrxs77@f9l39a4hYAc~w8T;dxFY=gV4K^4tNrJS9b7vnG#_Va%>;=RMPpaNG|KY92 z=^0F7H9Ps!9@rGC*-6Xunj*;$aO3)FI}Z>(aLx4gA{o>QmL zxqXMIeI?81b#5fNBa*)xM*c9I{2hOKr#OHAHG|8JPIshFKZ$~pM0p8^?Z)V=$U{`S z`XRBV@3N1A>Vh-r+47Dy`_9`J- z`EftL@yF9mYinChFz)A2v{sYrPkA6U@HjuS9~YvWS-d0FPm%mXG%op)wk6Y5p>@aU zqF(+Ti8w$1s>Sl45o7vg<;)?@b-t2BP94#RP^kP-KQ%u(xaPwbdo|xe%~bp-Bz$zB z-X9wNu=V<=ZCyN1eREvh5l+6OMd%c#)l{AARIlhKQBa*I$0F?9uHh5+DwT;N?CVCF z_tN5rRBu$1dJj+i6;s1YhB0l$`7fEyq_tkXRCpqJh!(%ZA!H%L3};tcA@AT4aYN)G zm{5~8dtRBeLv1sqUn49YBCIN}%$>{Y!x~BNvU=2U`dl+(22;5c*^s3fX%vhNv<4J_ z1{8q7O^_l9zO($c9qE^=9cJok=78ciF^nS=127il=Vwqf;$zsa!}!M3MZ2Q;oIuwX z@paQ2R_wdLYY&g)S^GiQ`FW0Mo!HIt+%?koL+h3!5U@o%Km5AsZ^Oy&syhsK;u09i zk*NtLPqJo0uKm|LKQW^Q^MPRv{U+ukVT`5&4_=t^CMl{WmXrhk9@{E|%fGux$^i#fA0X&x=IgK!jrouV`RW@cNi zNm3`fhC1#Ju<_qeYPx#t-i&bmGyJ_mWQJ_e;#J@8ra0MOS6-ilEA+zr81)?qt%U{G zou+-qLu)&s#>JNV-|5`T7}am$ONko?)9Xn~FU}}a4*e!p_!Bpx03u$Y--l7>Cm9yV zAUf3q5>sg1Em<*VnxRwcKL!j#5FJWq8qgQ*077Edy^AQOp}Tn99CrTRNTcN2 zod-tx^piZLD~b|@6z*q@uh@2cKyt*VUy~_4u?n?=9;3DU&)M2}7%?*3t3X@mXJG^? zeIm6TNTb79UCboBt(*IXJPQrRhlX`FrXM8Wj+z$B@E?CdbJ>xWM?Uj9p3>G2}f*o@@*n9l8tw{7i*%s zl!=Ltjz;H9{!RB`ImS#9F07=$&p8+tu2M%W9X141GinxJ@0DwK&Uq;{Gis{YPboV5 z0ux{QpLX;Mn`)Y2)lubZWFlcD*3qE;^c|+gDrWo!P0;x38TT2I?wx#(G2vJ9oiVeD zxU*~M0X8Xdf+)jkIm4`}PzRO6-0!^z(K&aJ&9Uv?y-;~v+|VmITv zSg(1L=k1PhV9*64Ji|0|jR8}SQ}ndXBJ&j<6AaF5&+pVJqn%L$gql`mZT%n|B;4D; zSjr{59*jsK{HN3LU=s4HAspGcl~V>d(7)S4J0)nR1WiZ)ZF$z#J>j76!*!`eDO~1z zN~B_qptDT{MckTvdh79)T|hdRg2ef(I??oOND*gYUt3=z5ha|h`J@LclH6^8B{%oO z$yNKfTVOYUNRC|gHeK!E6g;M9hy6hq-Or8H{{SQBdLd-mX4ruirHfo`TO@6CUJGp0 zzc_c(2yZoh)34-Ot$HFK!oNm{rCr@^jx!2B8Dh(}&gr&cL&lfCt_p&0W@9!h{7?iJ zee2a9gB4P0rCcG2PUZEN}LHX3aed{7K_2ZGOX z+H33)CVDPKl0-y_{~#KCTScAQPa`iqBAZi=NS`S6FS(1*_h|B{Ih;0 z7&*-a3@!_1kJ&bEJ)S%f$#v=%YT9I#_T`K$*T6o^2Y0e*Y%A?XddM$& zJlB$YBeA04Cp5~~02h8r4^LfpU5ef^ebH2}&Lf@sGg+Kp2aAVV1BkogP{*cla%03P zXrr9=-vy$wBU;x1@?@nCjo1&A~bFmbNv9J2doO6Y!A>L zNOdUK2^@9I4ndlfP7q0^s@%E@;0b?NyY?WM;kDlgb4WTqd5A;u<@itT3+qhe6ZwZE z{^^;}x>#2DvW?u|MDxu|*ul*?n+CAo1NKMHz<#}p-0n;sQk~n0cXsCVwsogi!3Zbn ziWIWp?sGC%bLZ{asd z8JbWo3hvMn)mBE}N@6UPR1cCNqiG#-T{7Wsr9Ok5dr$VaG za9{uVw;f|^qt~iohlm%Nifp`G^95Axixr-1eA}%9hXMbqbP26^jmgSre(14meaU~? zIP%KcbKaH(@GhBe*3f0}I=92f7Z6%|8U^s$G``Xl>`)`aKJy1U!IC@6+yCAeeCQsX zR9*8C#;bi4pZ(1=x`3XMX;;;%x5A0Z>T7A<7#)ZosyS8Oo`Xfj+62UicZJs7At!$q zHNO#c{{AmiGxeCIhWVvlYNEahv4b@?-OxV!O(C6gkEt|$Xr)aW2X&k;t|EF2l(iS_ zRD+idYVhruRLNB>79uFTJJtDNuC0ETS;pv>Sp*xFIO*+asuWhr!>9QH_FQAx!JHd# z3-&_%&b?U)f_z!gL=r1xYgvV@<(`!4b&pmwGlnG;l3N zZS!Hh6oODq8deL4rJ2AOCm6Q4t({_QV__^{U+YXa!T)#Q)(s~=a!CciZpf!jVb>c};Sk!y<+ z`9Nl|80yIT5=(H^)9mLha4q!lyYG^f`3!yI;*pm5ZtOzed&*n1J#Hjbq?AQlk%DWr zD|0jcTp!aV=d;oUF&)8iH^2V)SAjj&OmCV2Dw4Qxuf@LE`g*Q4Nqs2+YZQG+vzpNP zjMuf2AKuJFVd6nu$}OT?HLXIc7{Mxo{5h9vM&hA>cGQU{@Qc`Wm5RlZ9H4WpmU(wy zVE8aEow9g- zv@Kb1A#C#sQ;xt-qG399ViI@%B4(&6lhT<87F(JfvL0oAfS?dbz68=NS4`A#0Z1%X z<)gopm;rlK2F>g>0VxEia}l^LfHp(@r$smxpGWOPwu#qb)|`8Il{8~OX}z|b0mTH! z2+O1g{jS2nQ|sq*$~T1FGtt(rQ0wiidJKyxDpc}q-TP9RZf6_!dMn_PZ<|%<3wE(A z6$N+s{iplVxStj^3u6f5bOTScWMXPiCJ&{=J*%daNYmKi3Q$RuXjV!p^^Z}=HHl_S zG$9>h*1f@y2&PSq67e~B83 zqRC^a3eT*`$|J?iO)8q2n1oKs{aE!3)qZ_Mm9)Kmo|Ttu@-Fh1V*fVBO!-*cL4t@# zu%13RF2p1%ApxuONjzB+oP}64AIDj%k&YgMXCkIq!4vCgkwo5bE~pe^N2Jj9*Yz9F zvUP3aiR8TI0eT-x=@!xa&8f=h@hhUqo?52(Wuo`la|o3!7>+(??&cm^TYi6SEB~L? zhV~j(F!P}9ZF`UbBk@(j_rMoQIDp7)Gh;}q&T_WGY+%nNTWO_e>&PXyk(FQQ(g;*& zzb2B@Ed_M}tj>!A?n@{Lk@h;)CW|ve>!t^4lhXq3-|b838}yKMQ!s3g;-(O%Xcoau zA**Qe87jma?t2@MG8SV{mX-JCS6TJwEH{PQ{DDwkm-l$>)MQ?rdyrRYN{`A0UhUq@ zj4|l-*SsQCiq*Gu#wCrKZ`^q-D^5z0Qdll(`aM4P&3D21VREkua%ET@_^yHUm(M5;|XK9OplyX%jne-$hSm7l?OY-FwRqE7L-cdFijct#K%;wJ@jF3w?v z@=Qg0U~|mV2c*ze(a@?a<1&SC`>mh!a7t~)Px@s`!y&%l^->QMrr&i#`=os|LT=JC z9h;ZG(=SgYBfq3AMsmh{4@zLN3k9%yEQh3m z<95cx81+7a-gi7D%9CB(utMrVQYQ?g>YMaeC8O%C-9>WlpyYm4Og`(@S3Z&|cEHt{ z-q3b2h@gn==MA+3KvoCi!Pxkjlj@{|MVS-v07z45{gEv>!<1~j2q8sZuTLJz??fbo z)^6#(GtK!MPXpTIdpYrW0Sl1UeJAX-_Rs+K+B! z6X~7SF)T1c9nYpzaox@HcK+`$HVA{-ESzG2HI!w_^gWvxJ7l8Nu@R3Nm5;qew3$gl zH|_kCrL-VF%3p}m#hs@qzi2eSt2?iZd$wvrYwykKzw}3Zn3tLXBf^cd#4fZ@viS=X ztTH=NP$Ky!SS7FzMd5&J@vl0q85TzXfoMr0+fH&lLj2NVVPm4rMKr`oo|)cCWrG}x zjO<7JbF@W>f6b~LIFjB@RmuDU;@-4!MvZ5o{t?v*PKJd8>TjjysYrgWMg6Fqp9WAr z;yb8WiFrcKT#pF<;Xb#B4+HOh_inw1I*fvhN_i{u7$e$fn5dL7Uezg#o4Ksd8Om}G@*tgZ zmEWCe_3>iXvYt-59rYDf!|ro!6#$+yuNHtDl*XH=F)@oV!BE|-HMi!I7bqVCfjPE3 z5u4Y}rTh)NQf!7lL9n&PBqj|WG@=&y-D#wnc;$|n0p{onIVG5;-={@~mP?F#Ez)?? zQtD2>+k~>D{Av?n;%_ikFeshio$1Y^tJA#{iT(CJk`0E9d?~2sgl) zbuIf6y7R6&LmyVqOWu`o4ZE|9wzGz+L{FJnbhEpWdUuLNN;4A`emCQgp(p1VS|WPp zw*Pn&y7$lLIFkK1=e#(MaA7fwFzG!b;+JX$x|;wTb`koBCoWtBSBxPIL{_n4{5n<6 zqHfHNp^jM@910|M32;;o@1?^M)8$&jq%bwZeMdTk(a+cnzEHj)Ad~`c9IH zKM@Pl)KP;yc`5}cdGhYfrZTzmmTFJ9TzPl1OWe5f?$vfSx$@?WRQ;KWTSmE$_1C8+ z$On9HaFAn^I_t>6=Q(WsoioDh0`U2Q}*2>+>0q^{D(!`@#UtDc6{7v>??QTcc+&**%LXwCmlcTQ7%Hp_N_CU z$0NQ<%_&2=P5x2yVze9oYyXe#T-U)KMAqGA}T%P1#=Ni_L3&$jkg}w#j^fm${i&R_7N{HG2=? zJeIUJXIm4(lm2V)$w)wdF0${89}FUNVioV|EP^WJ1(x_S5gn@#U#!RrNw({SWRE); z$V@E%kJgaxPR1Rm6*VLM3Xp|MhWp(yhwZmA?_0reH{UKF2W9iQEqoJ0At9MTh7G)F z{-_=N>UW>v)tK=w@!HvpC^Eq#T%xHKo4AyY<(#V3AXBRp%Z3a$&hQuWA{0#c+l(yE zj&M6zBEaJ_f(iCcQkZqe9G{zKmnRCcYLYc`9&v@WKKFSt^QMriYupTMjO>zms5UvN z%)Rw>D;)rCqOv6F7nJ$j>-erkn{lGL-`Z_*?Ne9Lb@!keGmR0Ka4i;`wk6V(5gdMF zK(!sm`*Nne2EV(IRB3P9UqR>B$Xb9si_v5T(`*KlJ+f=8eS#gOaqtcG?n(PZc=WgI zQ$~E2Iks!Oby?K;2Dv>Nbuh&&+M%F9D>-QZqH;K0+w4P3MSR>uP#DWT>&WJbj z>yLj?C%q|>B>8No8z?-{I2u2Ca)%i{^N1XIFB069@L#>&-Uy&R^rAc>$^ER{aVUqP zg-07dnN-ZNU<~xj0WH5E#fRa z-R})I5etlA#T|s!6=MkMRzOUsa8u)DhE7B)Y-RJ18@`N#A^S-c4esGB>@=Df3+u}l zR)$(cQUVEgvq$uB;WK9-bv7txs&KLaD25H#6&itNZ7+&(?n9QFprYv2MksFlodTCk z0K{(;_h2l=JE-5pD?MNy{P9U(BP%sqiIcmCSIUeQz9F8u&f^)z(bheY;K797UJdya zvedSq)36ynY}*3PaPMg(7$=-OJS02_*o%RII*zm3L>v8rm2^uN+tk_BsE8#~I}2%Z zdm`6xhz^kW4~Hz>iXp-S5-IEnwch`RVH@J809QW{N0lCBXRJZ%r~vkmewh#<*PuWu zr>Qr^I3`eJaM;r3RM--UmOT7beONyf~p%c3eS~hOU02-@C9yM`G z#Bva~bih+i6y6mHnkXh`HJd~GBbPi_sdy?Ix%8(YiZ&{v|Vg>S(!Zn9G?y(36pw z37NhmAtp*W%W5=Qv$hF%pREI5dr;{RW?1j@cgjucAg|Fklo#rvD=`1* ztLA0}ZHTe>3KIG%#bd0#8r4y13Furx1xR?MYq}gCbE+wY3W*Ax#kQ){)W}Iv`o^?j z=YRx5}#Y)h1LIXPEoA$3m%AB7zs(YbES zV3W6WWSXIx)r=MRux=bCjN7K8pSu6N1GI79KBB)MstUDHr+;oidAfC*Nox*}#8M5D zHG2REnnaN*A}P!~%JOL3S{kG z&GL2Vejj(li-UB`CWGfw0phHUq6!kTAhru?~6u>F(|L)fVzP4O)Ounbwy?tEl-+8xyRA% zp;6U&xnfm$ba3wFUsOw$X{v88|0-<8PgEzltn(vJ>TZ9(kKw} z2kd&N$qWF>zso2$ zxcl9>DXqAQ<{>7H3%44A8l$?#iA)+s*97FT-y0rS4u@(YS5$;TJ!H~g!>{TXL+Zz)-kq~X8mb)7x~>ElL+6X)&mk(^L_49 z40Hq0wmohk&QG;{frY5H3sLK3_S#Nzj#rTMaqMVchcc>&582 z(Ad}>AyyWzY7*5*RvD73TfJfGv&nLJ6=4{}{Uebw$U8WNPl|4(XchvJ*vDg9oNi^6 z2${_~4zPAD<$fCbj1h3ssN6;jspd_yr2CI7XMv&)!A9B20vMz{i|*a_t-r^Ay5FSs z_xL~a{)s)_LLz;uCESoO_Bk)>``KaaQ~3^o&I)6nh3%_a%OcYi3S(b|{BAa@b`!>a zo&gAwmWfUnQP|PXL@w{5@Fy_Q^CS<@<^rs|1GM>Cx8((B6BzE+;&p>IMT1NlH}Yu^ zSKDQ?`wiw3GWPT3%@(76zKmLoK8Y`{u-W^m9B+4mhjEt zn{L)Mp~+`CH)SrERR-I~gmu&Aw_ZZkWs9}FN{ti(v)7zL`?&(%2tBAx|GZ;B=zw#0 zf^l`@hAG?nw~E*xIOv;vaX4GtnXOGtjhw=4<~!*`Y};u*DN>x)s5LN)3Hhwz=pw{AFFtuMm?IWqV?qg~{VrwrF?8EX^->MJM zC&}_*R(Z`f;2)XRUf@z^q)|%>?x#s&M=wFQ?$xHz(whB=lH)hD=MleELRiMSg4KjR zGi9sE1}!yNOR`DDmRM{Mx5P4yZaWXXY0ynQwGXq4KE9t08rg^mzdnlt{OC^I=M#GH zujwe>>CO-EM)Ab?e^;X%@B2-(b!X!*DMZ%G=vAJOSYt6pw(X4nAJ87|tmv~B#BSLi z;k1$D@7ODG|L%iOoCtodT}t+L9@jBLMB;~oy>xZWe#S+CoX$DpFrR7@&%yBeeTpzg zFXF_|(1TG>Z|jcF0shEn@|8%EJ7~?4@Ba4;;60&eayRRTaOVj?zfaNK;o#BIn&&EN zj)obBSyQNCLdypSi`Rb?2(q-8tiWQf77jhQmsSnL&$2RBjNp2IH2Fd_`6Bhj`oj0W zt4fH+4sj{27AMKycAgBdm%#2j;H0lea5n|md>gCzxV*+?!%8UJd0Hl!q6wT{HfdZq z_#xYyK_I+-KN0V2#AxbOG%{%;&31vexwkS_!|u|Kf~+t|MuKo{eP4t#*}k=a*1IL( zSxl5>TOkh`OX?2iHmrOR!lYTxn{Bt>0a0y)jaQjXu;awy=#y}`+4*1EUeqbwZ>0_F z?t?)T*xdjpnxUYDg_p6Q6HZ)kJId!fu%>dFac6R}To}m6NMMIGaiQ?EvMKbru2)<6 zTEEjH`E6BN3dq+mQ)9f&jBySa>?HM+#?K0kGU&&+(GXVv`26FgHiK?snBR*em~U-|;lgM5uVfd{SMK9&SM9Xa{&R$HVng z*UOgIsVERHzFx?hZZH`waZNrW1=q!On1h7hz<4&)ogmCj|6W7=l3)!-L}A2`&S^Bn zJ&K&jyT^(!3^_)GnGQPW5%+}Y4v4ER;6fxxuA?56j)+~Tp~sVhNI}Wi`RXA zL)@jwHxtz~_amBHciL>@oG;=!#P|>R(=2_j$guQXPMUKMb3x=jA~_059`or`enTkD z2bqXg=+WG3TVsX2h!(M}(Z&QxK0`RzV6-sRWH+f=Y;f4H$zV1J$P|*-6itc_1B@Cg z+{=-pii=Op2O2HfkXPL;+8Anj#5x8cMjKW|0x=1lNvIQ&?jqJ5Dt>b7adv_vcNv-; zi`B3$w6j5XKiO1454Ra7s%1e@i^;P$3#sKzaVKbTV78#?G#+MyQ_L`$4LDshQF8Kz z_R)uEwsxURsvgR7H?#+2WGN^29p)_O*sSO5}LC*Mo*w& zM38snU_eKfNIN#BdBJSZ>1cVA=^nw%8OKc9V+1!$#fZhv^))b+{Rl1>biAU~ingAA z1c;L~X;vQvUN>XqAO*8YZ#bSP!?*~lV8YE*%^w?djk1xbJ7Q6$4150?G*tw~36Y*e zNp}j{;~uu`+q1}>Od)qVg&aMnJ^%%3Hkh{EA_9NsxFzs|{N+BCtEK9FDw2FPs{g;j z!IbIuzrP+jGviHQddWt5VbczG_A-f`@^j2zl!-35ud|00$39J&r8R_oi89GoJKtyL z)zPWWq<;qh5Z+xM3BJmKg?|2&*Ssp!m@4;%J5K^=8}nW!yU)U%Jpn+Kcvo}qrJ`oP zXfTd>2@~X=37lfdFvseze9<6Jrz!s~>_)cHx#9JD1DskYa~SAn0~v2~A9o;sOFKk~ z)(aM)GaO~1P{fv&qv;q&;jA*Jd^m^prEcU>w3beA#!P9S{O0zp^!QDD4e0VkZsPhp zznO7*P{NJnPDcTZQ6~x-i&l#C`k=|k$A`d}M2X&wam%0!vw@UJSNFiKT-jvK9eaxUu zwrU0{hZECp8iDkk$X_6IMS6oSx1L+{BvTA}YP~^^cvTJuwpb*%Hb&>?+^BK8%>?im zE#L(nJr*t_IxNM)KkR4WH7OQe48x|!Z!|1iWLWr$6bl>uO9)B&N$NpmmZ?^p2n%tj zIbatK*1Pyo=K~vlq<~4MAq8-7oiYsf3QVG{4f=3__o0aXZSFN3JW{BetrR7iP_2R> z@d)6YT$)bM8%r*hGnOEb6XMqCR_GjcOI>fG`UpvMNTO^&9H#I-sZIc2(beKDmN1knXKM*R0A)=d7=ID79MIXyaqJku-$2 z(3}o6fdvX1z|bBp>06^K{r-s$=6;Jk2%R+&E?;C}8s1bA%;IMRE|6+Y2zxHndJFm; z0`MQ`Hv%Yb8gH+Jd!DzbQ`7ew`k%WdUnz5nauLbwA>KoZt)J7IlZUTel2`Rjc~W!o zISGwV)12~er18Eai`dXIR(3{6Q7C@>cW3>H_E*w|xERsw98)>3%E>l~Q_2#0! zet4tv6Y{IynWmD0HwH7rVvVjSc#bY6ZNW&w&u7p{^EBJs&gX*Lcwll3bB`P6B5+i= znR$@k{_y6Ktjw%Ht{U$CjK#vkYfd1OALGf~vlj99x%+q-(9hJGSX);=Ne zY_LLKAYMaCM!bYy-Oe8BxIDb|G~I`JEthE|)OtOg-qJ}2v7`!he8Kb4MG{w<)E*YH zN^RNi3w2y;9B{`;P#M13q06J2ZV%Le`23ts8;*L zl{BPe#INHwGyWN`_d?hWP*CvovxI5sBn-Ss-Qw{Ymp**nr2f!L<)nWIZd~KViLaWZ zMFU9#{Yk;AOzL$*QqKgt7lVf?JqOtT7)Uw?*tUVxn0&)sEOrdPIEw>zn0Tn4%}pyk zZX&D!;|Hhf(kFodZn_q3;?i>LIz%DDO@T8ZSZ?x4*NU4ysWja5M@oyEKB+X^^hZjI zoBB&LOyDN`z1phgZr7qh`(wf^_mNM-l5ZiO_NQ}uY|OXKu+x8&C3bA9*vTmv6H9LF zXQ#A&c2eYtWv72To1NwvcKW(uCtdh*1(ZQHt;sd#__qE7a~8Tq%&9`V`uf(s;>5LU zF1JZbyd(xN<*r0>MG{{~C0;%x(E`0ZmH0lEI{~HSnLvL&kaRZCyM`p50d&kjVodQV zKVWGCbQr-m2yOAfeoro4VdT<19w#vy*7U9UJgbMEVPd5HMl5}H>#4*Yeax1APR_WN_kHnc zTx!RJ$_f5mOsIMHopU~okI@z`+#!o#iPhhbY1Ax>Ilhg}2djZ<<1_8#3>CZlDJ^7n z7H4i@JE>=`E4Z7vdG$D8HS1i@x&A?Ob3lGXE?brUuw(+7h9>l;0hFY*fI$BgZ`Mw# zzcCq2;^{-V@Moouac2!$uh`yncggCf@nk0*_sgmA<-M;!=kp%R%%ga~btvV1-J|8Q znXugR2lD>6`D*e;2J*7Aki6#Wg#%xIiHP$cJZv6K2HncxeJvQM;--PTr|3x!*tZAr zUNivVwt=tzN!~%;6UFZjzHcYe&LP{+qONA?%DsP~%hUrw<3Q1QLYQXcKsn7pr|*kP z%u%&Jmd0uhsaJ|_jazjk{J_ivCf;RW(t?%$nu1DL83&Q=OFs*`)t@cEYZ)^F z$fmP1@B`M}KOc;xdoy_l^ix$9`j&hFJTYybCghGMCgi_H4)O%KwnN*&pZrD)nqBxQAu-7l;OZP*O)1?ge5RT&G$$1 z>&iGpM+C8_>7*ByK%H=2Ir~X-gX&T5{`=#0@f>+{!F?Gq+9Ls`dJP zry_~_jtc*_Vmjbq!-eiPhPCA%LO-M2>k@XBa|*<7qsiBfZ9_bc?+bHz6fw@FzSqoU zCeIL1$NiS2$sVTcnD10+{+pG6A+ea&^V*hVg=y02m?oxv2dW2Kk5=cCFq!`iEg}lY1G`??&Q^0Y~td2{M{l3DtVP7ui z8*K8!)m@^?TnMDw*5ytP5Ae1agR$?hH$0gD=yHZ9 zn|sFhbsq5hx?{fATK2Hi#{ly=W;86|rN_7eE$>RMCb!+aIn~|YG~HdK-ae$>ew(A; zfOu-EQ}Ao@q+y$m1ouUP9A^9hhVi$fEY;0B7inmI7|Dso`SN;w<1oFS=$;$>5XvkK=7^3 zcQb>B*?^K-yNd}_9|z7TP=J8}sScMolm6QyRq$|x0w!qbt!Sc=DQ+b=^%>KF;UFOk z2MrL5!YMja(zzRt!T_niNfo`upa8zz6;5N1THQ7rVdDM=Y!qp)3SeDxR6ByANpoJk z9N|rwMy@dJUSbrL*8gJ6gq>NO8{HZHHN~~Ss&FPfQl5V(*5Y!DsLTB6Iiupt>bRkN z@jBy-BJ_N3baHcVsP%+^XcK0~SSDQVjP2skt+`Tjhy&sfFDkC%;wA2r=BRs9VumR( zhJ@j%5@BvZ<$We6^PBr*3DZo>)wy!E#37Umb9R3Rd(CVeKvW4r;A|ZPk8+K1h0c6? zj)uD*^Fut1hnQ1`b*_r@%;!pkS9ASOl}zL_x#^~|Cz|YvO#UWKd5M)(B!L!Q!AVWG z_mn#|r%6jpV|jIRU*bj?d2hzVePd5_@;zj~&t#v1i?l*8!in-c$&DKk-?yJM7nZ5O zDZm`3&eO#I3eoy~cJ;mHjg3iNq; zDm?ibqawJM@G_vEQQ_R6yTh7%BkKQk>Vb9jfvL{u--;uQ_SJ{DNP_FSrp!n?akCu< zlBZB&{Ze8tptWxsDe*U;s@X#)AshB!zTnpLcVLf}-RPxSc;nxSPHtj;)p}O03HqOr zWC9h4WbT+t*yMcnDRT2R7nJj%5~X#f^s%|v#^$RFV?_Q+cdA5pnu+ej9c}-r9-KhS z$YP`ZI@r0R9;@VarrZZ}s7 z8GXjYJ323tm?3!FD#__da))xiTw;clr<<6aof$xxA&`+g^rP^}3MY~$pbPSVI`4$J z5aKV9&g?cY?Qfe2w$aH|r`Z~9x@Qr#jjx-hdaeYVWS%fBw2e1UlQ~l7X%rgwGrUG8 z&(5RVT$8dSkAhOV!;?`PLTzQ{kt@7HZP%H{qCDI2SM&C8c=8GoR-&I3PyQyY_}=>P zvF+}{etoTYawF;EY&s@#n?9yL-IiZs(@&<-XZNQ|JF#n=)Z#}vO55k;IJk}<^g;I_ zv$$*(bD@z-CbCnR2ErU> z2epmiV>{h$1l*Y`AooXv^xE7%QnH4|M)E$wiwF7}W?5+}yaRN-TE5RXv3*uTi^G%E zXOqp7`s_;cq&~|tPwKM`;bX767x9{kS1|*!hc@&N$b-CS5_Y4s;g%ai9pNmleJ^6M zq>~(quw^Rf>-?hh#^GyL>9Pae_jubJmVeMo>%YScVm4&qDroF7*F0|zXdG7eUA?|M z`IgyYHH{*y9*|_3XUqJZ$J0Ac5m7w6^WBW#c0#byBf)2h=D{(M#y%OwDLtV>A1fMalbiZoM^b^n_(8!#em^8G3Gn8V$k2H}AUH)K;dK;do=NnuDIo`s}hl5`$m z8Iwl!iJ>i`IpiPIA_YPyz^jbw-l|Xkk<2*7N)+2w#>1JkIty~2x!Jxb)p?u3S?Qa? z!x7r0-gPlLeFMwJ76SONLF`2fnt_F5Bk;3-2E+UeLdAb(xdvu^*W;QUun@e1U&wDc zhq#p4ezD~_N{P`ZX^QNyrP2qLQeXZFr3RIt19iKr;cEs17XrxIosd(j!e=;JqepZ85*;@tOPv;zj%tZo#h! zw=mHa%r$|lnZPts;Eplaa6BHRH`EQK|?1C*R+YT(XHh( znenQRo9akDO`w#)jV#!~O9rwrAy>=Qp92+32))o;YMP*^1cSQl5e8?usULQSQR%OI ztO3PJwaFR7bsG$YI$@%hUESIDumYj)|L~s;>3(_8$gw1MZXTsjcZ-a8#mGQu7$h<^hrceZHB+WK50I6iS1 z8H?BS4O@9lq`gIEc3zO%!?5S^#Par=n7epFkbW5>#ikY&$lBC$x&B0wEjj$9r0@#g zzl*GCY@y|w#g6*s^5Kho=zh95eqMLW)nsG>SjEEju{Dh=yPwWgv`za|atPP9?~u^_ zG{L>{BRp0dYWpYs%?-6R>Mx#T-I4Sd>5KIjNBMR9r5YdAT)^XyYTD97w5Qi_Hy5k4 z*!@q7su&tF6ChGTua*b-w?%NQn4tOOgv_rD`P3*Udz@Ly{goo!xnVTxA3QHAdJNL69WyLCpV zp$`YQExhL0H8F?wiAJtm;B@Sew%<5}I*SF~(vMNN%QQ8`A=}Lx)O1mc+P|xzB3bFM zcK1-YoYHH|7$kJ%eBsm4K9+1;m>F7Yz{qr=fHtoVPKS&*r8KDC$C@^crNg!|qBM#_ z-`yN5+!hXfcVi^$ZO(+Jig3$`Q0T$=JVlewUd;`U%QDtO1foF$Q9OR!X3LfEuj_iu zQm6S~tn&a)-pxJ}{_t55%j{6z3l(hm!&CMNaU!!K-vaS(@S72z%5P@;(!-%;F&{sb zqkc7#3|#HVeUUr4@N=OqkW|X-O8qXV9L4wT{M$v^8=+CN2`?c|h@>SZL%GS&WdSU7 zL!*}TXRr`%>W3icuMdW>z(81HG5~%jP!_%)8l?ypTSd9aFxzAhe&Dh2h0v(-{tOm^ ztwIn~#XJ2FN_C<2XQ0ktY`N5IWVutFi-~)J#oE+sF+W^F!;5|Tv!HksS&PXcz^d7_ zew5OdRGBO{RgtB-($;IB>{a_Oqq_1YL&K&~R5RC>7lkeMS6lc7#u8IaJy}FUs3Ng#1Bo4`3{C1&Wy+K5lLhf9tu6E!OkmI3=XBx5mtr23$>hrW%yVEYnHJ{F7}5D zp9{6T&sXN$OjZKy_^W4k`wO8KdAtcB%ZfTB9Ae!R2y^>q%M-RdxBGD$N+c1>D10f@ zq9wIz%BCiEWL0IVroXZuOU|bz0t<_WQPYb1sAVLza1;i|6coO&VktFTKn=sGAxI4? zZU<1=XrPY(`tZV+R+J0!r+}Od$-nC_54QbRBu+bGh$ahyOiC3<-6?p zp^p0_rf58G?yn}&d*u?KkOHug_T(9=FO7_64pJ|Yuu>8=bF~sz z+eDVJmA-@(Udnlsa^7Uq_SJjyt02_V^kxp(Ds$gXr!gWNpF@(-Hwzy-N)N~ni zlzaM|dOT>aRrq!s+lGI~y3e?Zm)YT@IiL5RuuYUFpDWMb!d?I!%gs7BVoa}>vH-v= zuWM_LZ4W1W#@!XiM!-^(MKWZ$#(E=s$2jP}&?L)i=_P~NC9*Z!JAIDr;f!MLq-vCJ8Gq)0?I zLu4HjliVcd(m_*gI1x6{UbZhpYul;T4Q9MZJCK<8=3aoP5Zy#x*=}Qd*%{Ch*F*MH zjsdb)iR_;OS)9MEkWj*Hle(bPcL)aNX1lG&wh?WBa|yy5=hAw1@?edlQ_7RNq$csc^PZW3G++M3G>*WJT6V=2wo@0x;u8MBZ9|qDtVN~r}R_ghj9!I;_N?GCi-WT z$s8zS`JZNZNa91nh4csU9MoSGcvM8;bne(GY7^DeZN0%wx?$&_v)pg{&UkRd<^9Z< z9!_?8CDe7{;0fnmf7sm2gN-Mgm_j$EN`cx$Jh2KRH7s7b zFn3vFJa<`bZo|B#3-8QrSUCTV__8||Hq2X z`Ik0^_rG8z&fjeQjpW~U-pBAahkv>J%j4fT{uS_V0{@Pa{>XgtEKN&$ad}$W4|%?` zJ}vDh{QW8aey)FwX=zXLcRl}}=HIjYdy#)H^Y0b@{SZ8EUb=WGZJW1bar2!ELK8ZNxuieeg2nMgP_yCAcw@s-3uykT_?;`h zHo=3J`Xdwc&ECtX)JN~H=4n6wm@3SFTDc7Qj|_ch{O{&7{__QVX#v{i;{@G|iM|Pf zY5t82rj6W?Jz>P$;q}4Fto4~M2X^@ndTFUVga4+=tK8U6DChU5`6u}5eHRZ;yEqX@ z8`YGamW_5c?61=&_~-In@P-dd8$Ka8H>-YFW#;<8%l=(_^ZC+z5XSdOu*0L%!d=0% z$=kEiu6lM@T0Uo3UH(``+Fv{ppq_N<@l%g4ZJ6((Plj=>db2XqvL+0hn^~Vx8CdUs z*_WM_c4o_bwoP+=!!y!``$nhPme3IX%eQ~hKs`;%F3dS;x%?H8NAj${#rzx1v;O|( zBE#Bg>-qZ{|5R=le~$VysCprFTE8U>K4vs8e!1-0VXX^9^xPR%Lqabl9X z1Rb1OfUDTWb-?COoVYpo)N$D(azg6FCVrHD-~DIq%u4G+dRph6Gc(_O|Gjta-230h z-1+B@VVJ;2aD02QVesSKD>xonfjG$H_=7K@E&vbWxN<$}5O58SEw!jOz;PV^;|YWV zzJlW>c*w>5#c{m#he!u-F^+@(jQR=8t-|&EQv5R4fe?uK40Tvjb(J z1#`DHw=I|-m|tA7q`0&g$ult7X3WIqGXmJNW$_u0VlNz1I8Skhcs+%_osB=|DfC+) z;kFVsI;auLJb=w-9MC*My8f-)7%b?5?Z=TbG;hv)6#y;J)+S8 zEqz>b2Q_+1OAl-A8I6u;>2sPprqT0SI;go9HF`-)U(wtdjm~N51O^AfS+~r(R=%mQK_mCTYapLc#_#7u2XwOWBi1 z%-(zFdP*)40VTN1BZDD3`YnK z^K&p9CpZjkuQ*z87?uvl3=YGU#W`|tm}OuRx4M}cFdRiVexhJFmT;IYU^t?1mukao-f!131&i8SUs&Nv!zLR|vGF^R+cmVQwA9A+$v zjpGxCxfu*cDGoD>MT@bDFd?&Xo5>`=Av)*=$1ovvZtp^T7P3wlIFtT_>N`7a|GcM+ zemb0p)V+Hy;*!(sVCop!=J>%t2l1?9veH~A?)Vu4iPuvJKdaOH@T7u?g~N~te)#w` z+HvYuoUQX4M={N544;>pIvTA?XNNM={IoW?QNRs zG&`E((UL_Yh`N^e<~q&EW1v25+!n8EYtX0hx|T-MY~&L$o+~vizSKibSku~kyU=iX>XV_id(?P4b;0$S^M$DQiR>4Io; zTT{E@@l4#+7O#tKan3q7H#eE~X>4>-@H;v9Txy(d+2sZUwH(kfO+rjWE7sRtW*7wy*sY}M|Pf2@A_eV zkYW%$hh|4H%ZliF&$QFwZvr;nkL+diF1=hXIM}hEzsopck&=xur0v`z7JkM((z(lz zVy}lOP50<7J2sISd!^r}=W>;q^jvb6?lif}OL{Jr7)vSL6fj?4_|+Xy2CpA3JccdGS!`0$UlO=0;||2;avH1jYt>;^>@RFB4gdjRLA(Kw%gGk z-}wA7UY~W{G;joB^*&P7D(jtK$>6C0jx-%X+DE1M;pxlfSQS9NfjgBZE;rUhdM+17 zlBemfX!mPoVe+hs>Jk$YfsIZyH0(h<{4*td1Hp`;7X35#_7+NASz$_y{7(g<-$CFZUfa^~2<@=W?NS zL-Y53pY34s3bbc}k=)6Xl0gK{u~L!z2U18zp>4ygZcEM*a$jewlP6(!BYD`5t5!Pa z{rw28S_m~~{AJ7;bMBW%@?*Tlm}Qvdt+o6xLO9N_(UoK47?Ubgi;YjSY}E#_`4Iti zX9AAXu~EMm>2_>WffG3X1-3$9CgR)J*baP2ULmY%Y;Et@j`QoavGj8@HlLcsWKsHz z?_)njp>x>tYChwA>_y5RO{8A|5-BAT3&;6_?gE;O&1cXENzk7I-J-Zhf!I<>^?_v~ z+BIT9%mwzUGBYC_b3@QXEoH6<$8-n^XlYP$%q5XBk0fT+bm7?D2`balJ2Y3WQKgnf zG*_)rjh3#}+&YamXlcgYifw5OUR>^ay%oLQime=*$FQFWcrUeS%$+zJ)QtVCLA`Q9 z-3>Xl%VE9(hC1djTfk7$gc;peQUlpaJMPo#D_M5wZggK+3lG!}$4O5n=h9aeI6@cN zVc}1D%CknLmM`YK^Ylj2E{lgOAHrC}T309(dMwTgs2oFq8S#|2AF>fWa17&X^wSvF zcEzk`hen*VZ+BCpM$WFGwKIs86hvd28)NcNA_=gF5!aPrbv`sm-d_Hmqvl1$C5r>f zvFGQA%`oo1L0T@>f43%4b`VPR6m;n$R(LqJCVz;YpEb17u5`RGT&A9X8~B6p_a2)O zIJRP$dj5X!ldOB7Ke|jkKfN5rZW{804!5rEcV5X7b#Cvd%g08=htc|ig|-)U9EbfG z_$#vF!>B=}Z{EU^G=Bk{y*VnrnsTn+4NI*%vXC8pHHYw8KMWfu|DIe#)VJ=1o)4G! zAL7UdOYaXbt+3wM&yOdfA?`nfzo+>3XV)21|KEE_A3W>R)uwt@pEC^A`mR;zbgoKvwH+#*)T}5&2sF=n76