From ace03bde0bab0bf84e0e1efe4e42cfe0e37dc32d Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 1 Apr 2021 10:37:18 +0300 Subject: [PATCH 01/11] - updated continuous integration settings * Install compilers for Linux targets explicitly * Specify compilers for Linux target by executable names instead of full paths * Switch to latest Clang target to 11 * Use ZMusic 1.1.6 --- .github/workflows/continuous_integration.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 3c603963d..4cd5b7486 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -38,30 +38,30 @@ jobs: - { name: "Linux GCC 7", os: ubuntu-20.04, - extra_options: "-DCMAKE_C_COMPILER=/usr/bin/gcc-7 -DCMAKE_CXX_COMPILER=/usr/bin/g++-7", - deps_cmdline: "sudo apt update && sudo apt install libsdl2-dev libvpx-dev libgtk2.0-dev gcc-7 g++-7", + extra_options: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7", + deps_cmdline: "sudo apt update && sudo apt install g++-7 libsdl2-dev libvpx-dev libgtk2.0-dev", build_type: "RelWithDebInfo" } - { name: "Linux GCC 10", os: ubuntu-20.04, - extra_options: "-DCMAKE_C_COMPILER=/usr/bin/gcc-10 -DCMAKE_CXX_COMPILER=/usr/bin/g++-10", - deps_cmdline: "sudo apt update && sudo apt install libsdl2-dev libvpx-dev libgtk-3-dev", + extra_options: "-DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10", + deps_cmdline: "sudo apt update && sudo apt install g++-10 libsdl2-dev libvpx-dev libgtk-3-dev", build_type: "MinSizeRel" } - { name: "Linux Clang 6", os: ubuntu-20.04, - extra_options: "-DCMAKE_C_COMPILER=/usr/bin/clang-6.0 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-6.0 \ + extra_options: "-DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 \ -DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", deps_cmdline: "sudo apt update && sudo apt install clang-6.0 libsdl2-dev libvpx-dev libopenal-dev libfluidsynth-dev libmpg123-dev libsndfile1-dev", build_type: "Debug" } - { - name: "Linux Clang 10", + name: "Linux Clang 11", os: ubuntu-20.04, - extra_options: "-DCMAKE_C_COMPILER=/usr/bin/clang-10 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10", - deps_cmdline: "sudo apt update && sudo apt install libsdl2-dev libvpx-dev", + extra_options: "-DCMAKE_C_COMPILER=clang-11 -DCMAKE_CXX_COMPILER=clang++-11", + deps_cmdline: "sudo apt update && sudo apt install clang-11 libsdl2-dev libvpx-dev", build_type: "Release" } @@ -79,7 +79,7 @@ jobs: cd build git clone https://github.com/coelckers/ZMusic.git cd ZMusic - git checkout 1.1.4 + git checkout 1.1.6 cd .. cmake -B zmusic_build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DCMAKE_INSTALL_PREFIX=`pwd`/zmusic_install ${{ matrix.config.extra_options }} ZMusic cmake --build zmusic_build --target install --parallel 3 From dff6b2a7320f9c22c76bb7df1d27657cdcb257e3 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 1 Apr 2021 12:01:45 +0300 Subject: [PATCH 02/11] - moved macOS extra options to proper CI target --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 4cd5b7486..e080bf629 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -25,13 +25,13 @@ jobs: - { name: "macOS", os: macos-10.15, - extra_options: "-DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", deps_cmdline: "brew install libvpx", build_type: "Release" } - { name: "macOS", os: macos-10.15, + extra_options: "-DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", deps_cmdline: "brew install libvpx fluidsynth mpg123 libsndfile", build_type: "Debug" } From 4ffe0044832410f8c8650001f344abe1cc637cec Mon Sep 17 00:00:00 2001 From: Mitchell Richters Date: Fri, 2 Apr 2021 22:46:43 +1100 Subject: [PATCH 03/11] - Further work within `PlayerAngle` and `PlayerHorizon` structs following 39fe9efaffc6652993fe2a4708ced8d770d2122a and bf2d8078a4418eed5386faf904ca075d2af587d5. * Reduce code repetition. * Clamp incoming horizon when using `settarget()` methods. * Eliminate double calculation that was occurring in `processhelpers()`. --- source/core/gameinput.h | 208 +++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 110 deletions(-) diff --git a/source/core/gameinput.h b/source/core/gameinput.h index 652cac326..40a6b70f3 100644 --- a/source/core/gameinput.h +++ b/source/core/gameinput.h @@ -3,6 +3,7 @@ #include "m_fixed.h" #include "binaryangle.h" #include "gamecvars.h" +#include "gamestruct.h" #include "packet.h" int getincangle(int a, int na); @@ -12,8 +13,7 @@ lookangle getincanglebam(binangle a, binangle na); struct PlayerHorizon { - fixedhoriz horiz, ohoriz, horizoff, ohorizoff, target; - double adjustment; + fixedhoriz horiz, ohoriz, horizoff, ohorizoff; void backup() { @@ -29,14 +29,7 @@ struct PlayerHorizon void addadjustment(double value) { - if (!SyncInput()) - { - adjustment += value * FRACUNIT; - } - else - { - horiz += q16horiz(FloatToFixed(value)); - } + __addadjustment(q16horiz(FloatToFixed(value))); } void resetadjustment() @@ -46,53 +39,35 @@ struct PlayerHorizon void settarget(int value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = buildhoriz(value); - if (target.asq16() == 0) target = q16horiz(1); - } - else - { - horiz = buildhoriz(value); - if (backup) ohoriz = horiz; - } + __settarget(buildhoriz(clamp(value, FixedToInt(gi->playerHorizMin()), FixedToInt(gi->playerHorizMax()))), backup); } void settarget(double value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = buildfhoriz(value); - if (target.asq16() == 0) target = q16horiz(1); - } - else - { - horiz = buildfhoriz(value); - if (backup) ohoriz = horiz; - } + __settarget(buildfhoriz(clamp(value, FixedToFloat(gi->playerHorizMin()), FixedToFloat(gi->playerHorizMax()))), backup); } void settarget(fixedhoriz value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = value; - if (target.asq16() == 0) target = q16horiz(1); - } - else - { - horiz = value; - if (backup) ohoriz = horiz; - } + __settarget(q16horiz(clamp(value.asq16(), gi->playerHorizMin(), gi->playerHorizMax())), backup); + } + + bool targetset() + { + return target.asq16(); } void processhelpers(double const scaleAdjust) { - if (target.asq16()) + if (targetset()) { - horiz += q16horiz(xs_CRoundToInt(scaleAdjust * (target - horiz).asq16())); + auto delta = (target - horiz).asq16(); - if (abs((horiz - target).asq16()) < FRACUNIT) + if (abs(delta) > FRACUNIT) + { + horiz += q16horiz(xs_CRoundToInt(scaleAdjust * delta)); + } + else { horiz = target; target = q16horiz(0); @@ -121,13 +96,42 @@ struct PlayerHorizon fixed_t const curr = sum().asq16(); return q16horiz(prev + xs_CRoundToInt(ratio * (curr - prev))); } + +private: + fixedhoriz target; + double adjustment; + + void __addadjustment(fixedhoriz value) + { + if (!SyncInput()) + { + adjustment += value.asq16(); + } + else + { + horiz += value; + } + } + + void __settarget(fixedhoriz value, bool backup = false) + { + if (!SyncInput() && !backup) + { + target = value; + if (!targetset()) target = q16horiz(1); + } + else + { + horiz = value; + if (backup) ohoriz = horiz; + } + } }; struct PlayerAngle { - binangle ang, oang, target; + binangle ang, oang; lookangle look_ang, olook_ang, rotscrnang, orotscrnang, spin; - double adjustment; void backup() { @@ -145,50 +149,22 @@ struct PlayerAngle void addadjustment(int value) { - if (!SyncInput()) - { - adjustment += BAngToBAM(value); - } - else - { - ang += buildang(value); - } + __addadjustment(buildang(value)); } void addadjustment(double value) { - if (!SyncInput()) - { - adjustment += value * BAMUNIT; - } - else - { - ang += bamang(xs_CRoundToUInt(value * BAMUNIT)); - } + __addadjustment(buildfang(value)); } void addadjustment(lookangle value) { - if (!SyncInput()) - { - adjustment += value.asbam(); - } - else - { - ang += bamang(value.asbam()); - } + __addadjustment(bamang(value.asbam())); } void addadjustment(binangle value) { - if (!SyncInput()) - { - adjustment += value.asbam(); - } - else - { - ang += value; - } + __addadjustment(value); } void resetadjustment() @@ -198,53 +174,35 @@ struct PlayerAngle void settarget(int value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = buildang(value & 2047); - if (target.asbam() == 0) target = bamang(1); - } - else - { - ang = buildang(value & 2047); - if (backup) oang = ang; - } + __settarget(buildang(value & 2047), backup); } void settarget(double value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = buildfang(fmod(value, 2048)); - if (target.asbam() == 0) target = bamang(1); - } - else - { - ang = buildfang(fmod(value, 2048)); - if (backup) oang = ang; - } + __settarget(buildfang(fmod(value, 2048)), backup); } void settarget(binangle value, bool backup = false) { - if (!SyncInput() && !backup) - { - target = value; - if (target.asbam() == 0) target = bamang(1); - } - else - { - ang = value; - if (backup) oang = ang; - } + __settarget(value, backup); + } + + bool targetset() + { + return target.asbam(); } void processhelpers(double const scaleAdjust) { - if (target.asbam()) + if (targetset()) { - ang += bamang(xs_CRoundToUInt(scaleAdjust * getincanglebam(ang, target).asbam())); + auto delta = getincanglebam(ang, target).asbam(); - if (getincanglebam(ang, target).asbam() < BAMUNIT) + if (delta > BAMUNIT) + { + ang += bamang(xs_CRoundToUInt(scaleAdjust * delta)); + } + else { ang = target; target = bamang(0); @@ -291,6 +249,36 @@ struct PlayerAngle { return (!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).asbam() * (0.5 / BAMUNIT); // Used within draw code for weapon and crosshair when looking left/right. } + +private: + binangle target; + double adjustment; + + void __addadjustment(binangle value) + { + if (!SyncInput()) + { + adjustment += value.asbam(); + } + else + { + ang += value; + } + } + + void __settarget(binangle value, bool backup = false) + { + if (!SyncInput() && !backup) + { + target = value; + if (!targetset()) target = bamang(1); + } + else + { + ang = value; + if (backup) oang = ang; + } + } }; class FSerializer; From 9c01bde44efbbe209268196a192615d4a95a8621 Mon Sep 17 00:00:00 2001 From: Mitchell Richters Date: Fri, 2 Apr 2021 22:41:47 +1100 Subject: [PATCH 04/11] - gameinput.cpp: Block player input within `sethorizon()` and `applylook()` if target for each has been set by the ticker. * Stops players having the ability to provide input and fight the system trying to set an input. --- source/core/gameinput.cpp | 145 +++++++++++++++------------- source/core/gameinput.h | 2 +- source/games/blood/src/controls.cpp | 2 +- source/games/blood/src/player.cpp | 2 +- source/games/duke/src/input.cpp | 2 +- source/games/duke/src/player_d.cpp | 2 +- source/games/duke/src/player_r.cpp | 2 +- source/games/exhumed/src/input.cpp | 2 +- source/games/exhumed/src/player.cpp | 2 +- source/games/sw/src/player.cpp | 2 +- 10 files changed, 87 insertions(+), 76 deletions(-) diff --git a/source/core/gameinput.cpp b/source/core/gameinput.cpp index a36edaa7a..3172631d9 100644 --- a/source/core/gameinput.cpp +++ b/source/core/gameinput.cpp @@ -271,59 +271,63 @@ void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlIn // //--------------------------------------------------------------------------- -void sethorizon(fixedhoriz* horiz, float const horz, ESyncBits* actions, double const scaleAdjust) +void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust) { - // Store current horizon as true pitch. - double pitch = horiz->aspitch(); - - if (horz) + // Process only if no targeted horizon set. + if (!horizon->targetset()) { - *actions &= ~SB_CENTERVIEW; - pitch += horz; - } + // Store current horizon as true pitch. + double pitch = horizon->horiz.aspitch(); - // this is the locked type - if (*actions & (SB_AIM_UP|SB_AIM_DOWN)) - { - *actions &= ~SB_CENTERVIEW; - double const amount = HorizToPitch(250. / GameTicRate); - - if (*actions & SB_AIM_DOWN) - pitch -= scaleAdjust * amount; - - if (*actions & SB_AIM_UP) - pitch += scaleAdjust * amount; - } - - // this is the unlocked type - if (*actions & (SB_LOOK_UP|SB_LOOK_DOWN)) - { - *actions |= SB_CENTERVIEW; - double const amount = HorizToPitch(500. / GameTicRate); - - if (*actions & SB_LOOK_DOWN) - pitch -= scaleAdjust * amount; - - if (*actions & SB_LOOK_UP) - pitch += scaleAdjust * amount; - } - - // clamp before converting back to horizon - *horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax())); - - // return to center if conditions met. - if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN))) - { - if (abs(horiz->asq16()) > FloatToFixed(0.25)) + if (horz) { - // move horiz back to 0 - *horiz -= q16horiz(xs_CRoundToInt(scaleAdjust * horiz->asq16() * (10. / GameTicRate))); - } - else - { - // not looking anymore because horiz is back at 0 - *horiz = q16horiz(0); *actions &= ~SB_CENTERVIEW; + pitch += horz; + } + + // this is the locked type + if (*actions & (SB_AIM_UP|SB_AIM_DOWN)) + { + *actions &= ~SB_CENTERVIEW; + double const amount = HorizToPitch(250. / GameTicRate); + + if (*actions & SB_AIM_DOWN) + pitch -= scaleAdjust * amount; + + if (*actions & SB_AIM_UP) + pitch += scaleAdjust * amount; + } + + // this is the unlocked type + if (*actions & (SB_LOOK_UP|SB_LOOK_DOWN)) + { + *actions |= SB_CENTERVIEW; + double const amount = HorizToPitch(500. / GameTicRate); + + if (*actions & SB_LOOK_DOWN) + pitch -= scaleAdjust * amount; + + if (*actions & SB_LOOK_UP) + pitch += scaleAdjust * amount; + } + + // clamp before converting back to horizon + horizon->horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax())); + + // return to center if conditions met. + if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN))) + { + if (abs(horizon->horiz.asq16()) > FloatToFixed(0.25)) + { + // move horiz back to 0 + horizon->horiz -= q16horiz(xs_CRoundToInt(scaleAdjust * horizon->horiz.asq16() * (10. / GameTicRate))); + } + else + { + // not looking anymore because horiz is back at 0 + horizon->horiz = q16horiz(0); + *actions &= ~SB_CENTERVIEW; + } } } } @@ -358,34 +362,41 @@ void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double angle->rotscrnang -= bamlook(xs_CRoundToInt(scaleAdjust * (720. / GameTicRate) * BAMUNIT)); } - if (*actions & SB_TURNAROUND) + if (!angle->targetset()) { - if (angle->spin.asbam() == 0) + if (*actions & SB_TURNAROUND) { - // currently not spinning, so start a spin - angle->spin = buildlook(-1024); + if (angle->spin.asbam() == 0) + { + // currently not spinning, so start a spin + angle->spin = buildlook(-1024); + } + *actions &= ~SB_TURNAROUND; } - *actions &= ~SB_TURNAROUND; - } - if (angle->spin.asbam() < 0) - { - // return spin to 0 - lookangle add = bamlook(xs_CRoundToUInt(scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate) * BAMUNIT)); - angle->spin += add; - if (angle->spin.asbam() > 0) + if (angle->spin.asbam() < 0) { - // Don't overshoot our target. With variable factor this is possible. - add -= angle->spin; + // return spin to 0 + lookangle add = bamlook(xs_CRoundToUInt(scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate) * BAMUNIT)); + angle->spin += add; + if (angle->spin.asbam() > 0) + { + // Don't overshoot our target. With variable factor this is possible. + add -= angle->spin; + angle->spin = bamlook(0); + } + angle->ang += bamang(add.asbam()); + } + + if (avel) + { + // add player's input + angle->ang += degang(avel); angle->spin = bamlook(0); } - angle->ang += bamang(add.asbam()); } - - if (avel) + else { - // add player's input - angle->ang += degang(avel); angle->spin = bamlook(0); } } diff --git a/source/core/gameinput.h b/source/core/gameinput.h index 40a6b70f3..2fdc94b9a 100644 --- a/source/core/gameinput.h +++ b/source/core/gameinput.h @@ -290,6 +290,6 @@ void updateTurnHeldAmt(double const scaleAdjust); bool const isTurboTurnTime(); void resetTurnHeldAmt(); void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlInfo* const hidInput, double const scaleAdjust, int const drink_amt = 0, bool const allowstrafe = true, double const turnscale = 1); -void sethorizon(fixedhoriz* horiz, float const horz, ESyncBits* actions, double const scaleAdjust = 1); +void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust = 1); void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double const scaleAdjust = 1); void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust = 1, bool const climbing = false); diff --git a/source/games/blood/src/controls.cpp b/source/games/blood/src/controls.cpp index 4b7de23eb..9da34ba1e 100644 --- a/source/games/blood/src/controls.cpp +++ b/source/games/blood/src/controls.cpp @@ -59,7 +59,7 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) if (gView->pXSprite->health != 0) { applylook(&pPlayer->angle, input.avel, &pPlayer->input.actions, scaleAdjust); - sethorizon(&pPlayer->horizon.horiz, input.horz, &pPlayer->input.actions, scaleAdjust); + sethorizon(&pPlayer->horizon, input.horz, &pPlayer->input.actions, scaleAdjust); doslopetilting(pPlayer, scaleAdjust); } diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index 98fe8e48a..cda8a9355 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -1548,7 +1548,7 @@ void ProcessInput(PLAYER *pPlayer) if (SyncInput()) { - sethorizon(&pPlayer->horizon.horiz, pInput->horz, &pInput->actions); + sethorizon(&pPlayer->horizon, pInput->horz, &pInput->actions); doslopetilting(pPlayer); } diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp index d8cbaa751..c05c1fa83 100644 --- a/source/games/duke/src/input.cpp +++ b/source/games/duke/src/input.cpp @@ -842,7 +842,7 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) doslopetilting(p, scaleAdjust); applylook(&p->angle, p->adjustavel(input.avel), &p->sync.actions, scaleAdjust); p->apply_seasick(scaleAdjust); - sethorizon(&p->horizon.horiz, input.horz, &p->sync.actions, scaleAdjust); + sethorizon(&p->horizon, input.horz, &p->sync.actions, scaleAdjust); } p->angle.processhelpers(scaleAdjust); diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp index 16de421c8..3d5832434 100644 --- a/source/games/duke/src/player_d.cpp +++ b/source/games/duke/src/player_d.cpp @@ -3139,7 +3139,7 @@ HORIZONLY: if (SyncInput()) { - sethorizon(&p->horizon.horiz, PlayerHorizon(snum), &p->sync.actions); + sethorizon(&p->horizon, PlayerHorizon(snum), &p->sync.actions); } p->checkhardlanding(); diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp index 9747b4e40..bd43f3bb4 100644 --- a/source/games/duke/src/player_r.cpp +++ b/source/games/duke/src/player_r.cpp @@ -4001,7 +4001,7 @@ HORIZONLY: if (SyncInput()) { - sethorizon(&p->horizon.horiz, PlayerHorizon(snum), &p->sync.actions); + sethorizon(&p->horizon, PlayerHorizon(snum), &p->sync.actions); } p->checkhardlanding(); diff --git a/source/games/exhumed/src/input.cpp b/source/games/exhumed/src/input.cpp index e2af84093..9f67a2773 100644 --- a/source/games/exhumed/src/input.cpp +++ b/source/games/exhumed/src/input.cpp @@ -125,7 +125,7 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) if (!nFreeze) { applylook(&pPlayer->angle, input.avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); - sethorizon(&pPlayer->horizon.horiz, input.horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); + sethorizon(&pPlayer->horizon, input.horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); } pPlayer->angle.processhelpers(scaleAdjust); diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 1bef13e45..322fb6ecf 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -2656,7 +2656,7 @@ loc_1BD2E: if (SyncInput()) { Player* pPlayer = &PlayerList[nPlayer]; - sethorizon(&pPlayer->horizon.horiz, sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions); + sethorizon(&pPlayer->horizon, sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions); } } else // else, player's health is less than 0 diff --git a/source/games/sw/src/player.cpp b/source/games/sw/src/player.cpp index 78b614554..351df2422 100644 --- a/source/games/sw/src/player.cpp +++ b/source/games/sw/src/player.cpp @@ -1669,7 +1669,7 @@ DoPlayerHorizon(PLAYERp pp, float const horz, double const scaleAdjust) { bool const canslopetilt = !TEST(pp->Flags, PF_FLYING|PF_SWIMMING|PF_DIVING|PF_CLIMBING|PF_JUMPING|PF_FALLING) && TEST(sector[pp->cursectnum].floorstat, FLOOR_STAT_SLOPE); calcviewpitch(pp->pos.vec2, &pp->horizon.horizoff, pp->angle.ang, pp->input.actions & SB_AIMMODE, canslopetilt, pp->cursectnum, scaleAdjust, TEST(pp->Flags, PF_CLIMBING)); - sethorizon(&pp->horizon.horiz, horz, &pp->input.actions, scaleAdjust); + sethorizon(&pp->horizon, horz, &pp->input.actions, scaleAdjust); } void From e3c2757f1c69661146415a88921767f6810d2c88 Mon Sep 17 00:00:00 2001 From: Mitchell Richters Date: Fri, 2 Apr 2021 23:23:24 +1100 Subject: [PATCH 05/11] - sethorizon(): Ensure `SB_CENTERVIEW` bit is always cleared if horizon target is set. --- source/core/gameinput.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/core/gameinput.cpp b/source/core/gameinput.cpp index 3172631d9..bbd8b0968 100644 --- a/source/core/gameinput.cpp +++ b/source/core/gameinput.cpp @@ -330,6 +330,10 @@ void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, do } } } + else + { + *actions &= ~SB_CENTERVIEW; + } } //--------------------------------------------------------------------------- From 952bccbf5fa05b05e86f7506f3848b6ad8ac4a70 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Apr 2021 21:40:16 +0200 Subject: [PATCH 06/11] - added a check for game-side-defined voxels when discarding one-sided wall sprites viewed from the back. --- source/build/src/polymost.cpp | 2 ++ source/core/gamestruct.h | 1 + source/games/sw/src/game.cpp | 5 +++++ source/games/sw/src/game.h | 1 + 4 files changed, 9 insertions(+) diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index 2bf705d50..c21e2417e 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -22,6 +22,7 @@ Ken Silverman's official web site: http://www.advsys.net/ken #include "texturemanager.h" #include "hw_renderstate.h" #include "printf.h" +#include "gamestruct.h" int checkTranslucentReplacement(FTextureID picnum, int pal); @@ -2201,6 +2202,7 @@ void polymost_scansector(int32_t sectnum) { if ((spr->cstat&(64+48))!=(64+16) || (r_voxels && tiletovox[spr->picnum] >= 0 && voxmodels[tiletovox[spr->picnum]]) || + (r_voxels && gi->Voxelize(spr->picnum)) || DMulScale(bcos(spr->ang), -s.x, bsin(spr->ang), -s.y, 6) > 0) if (renderAddTsprite(z, sectnum)) break; diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index 66359440e..664c878d3 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -101,6 +101,7 @@ struct GameInterface virtual int chaseCamX(binangle ang) { return 0; } virtual int chaseCamY(binangle ang) { return 0; } virtual int chaseCamZ(fixedhoriz horiz) { return 0; } + virtual bool Voxelize(int sprnum) { return false; } virtual FString statFPS() { diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index ec39e50a8..50583459a 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -801,4 +801,9 @@ void GameInterface::FreeLevelData() ::GameInterface::FreeLevelData(); } +bool GameInterface::Voxelize(int sprnum) +{ + return (aVoxelArray[sprnum].Voxel >= 0); +} + END_SW_NS diff --git a/source/games/sw/src/game.h b/source/games/sw/src/game.h index 0b1a5f583..799aa2065 100644 --- a/source/games/sw/src/game.h +++ b/source/games/sw/src/game.h @@ -2255,6 +2255,7 @@ struct GameInterface : ::GameInterface int chaseCamX(binangle ang) { return -ang.bcos(-3); } int chaseCamY(binangle ang) { return -ang.bsin(-3); } int chaseCamZ(fixedhoriz horiz) { return horiz.asq16() >> 8; } + bool Voxelize(int sprnum); GameStats getStats() override; From ebac57fb4f526021de31854b2f8a2f9f6a075960 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Apr 2021 09:25:41 +0200 Subject: [PATCH 07/11] - fixed music in Blood's menu This is only supposed to play for the fade-in of the background but needs to stop when the menu appears. Fixes #291 --- source/games/blood/src/credits.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/games/blood/src/credits.cpp b/source/games/blood/src/credits.cpp index 9d0b44025..ea78f4b7b 100644 --- a/source/games/blood/src/credits.cpp +++ b/source/games/blood/src/credits.cpp @@ -88,6 +88,7 @@ void playlogos() jobs[job++] = { Create(2518, DScreenJob::fadein) }; RunScreenJob(jobs, job, [](bool) { + Mus_Stop(); gameaction = ga_mainmenu; }, true, true); } From a298a3d3df745bb21e4036596b06fe0d7b7a2660 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Apr 2021 09:35:05 +0200 Subject: [PATCH 08/11] - disabled 'isRunning' in Blood entirely. After discovering yet another misbehaving place in the game I think it can be safely concluded that this is better left off unconditionally. The only remaining place where this still gets checked is in nnexts, but I wouldn't expect this to work either. Fixes #292 --- source/games/blood/src/player.cpp | 3 ++- source/games/blood/src/sbar.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index cda8a9355..28ae4aec8 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -1327,7 +1327,8 @@ void ProcessInput(PLAYER *pPlayer) else if (pPlayer->restTime >= 0) pPlayer->restTime += 4; - pPlayer->isRunning = !!(pInput->actions & SB_RUN) && pPlayer->restTime <= 10; + // This was just too broken. Every single place in the game depending on 'isRunning' will misbehave if this is set because originally it never worked as intended. + pPlayer->isRunning = false;// !!(pInput->actions& SB_RUN) && pPlayer->restTime <= 10; WeaponProcess(pPlayer); if (pXSprite->health == 0) diff --git a/source/games/blood/src/sbar.cpp b/source/games/blood/src/sbar.cpp index 2d8104c30..fe6f16a07 100644 --- a/source/games/blood/src/sbar.cpp +++ b/source/games/blood/src/sbar.cpp @@ -623,8 +623,8 @@ private: else DrawStatSprite(nTile, x, y, 40, 5); } - DrawStatMaskedSprite(2202, 118.5, 185.5, pPlayer->isRunning ? 16 : 40); - DrawStatMaskedSprite(2202, 201.5, 185.5, pPlayer->isRunning ? 16 : 40); + DrawStatMaskedSprite(2202, 118.5, 185.5, /*pPlayer->isRunning ? 16 :*/ 40); + DrawStatMaskedSprite(2202, 201.5, 185.5, /*pPlayer->isRunning ? 16 :*/ 40); if (pPlayer->throwPower) { TileHGauge(2260, 124, 175.5, pPlayer->throwPower, 65536); From 97a0cb2a1060559a614fa5baa2ab3a77871c243c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Apr 2021 10:33:29 +0200 Subject: [PATCH 09/11] - handle voxel rotation in the backend to enable it for all games. This was previously only present in the Blood module and missed in Exhumed from upstream so the entire option was rather pointless. Fixes #290 --- source/build/include/buildtypes.h | 7 ++++++- source/build/include/mdsprite.h | 2 +- source/build/include/polymost.h | 1 + source/build/src/mdsprite.cpp | 2 +- source/build/src/polymost.cpp | 7 +++++-- source/build/src/voxmodel.cpp | 11 +++++++++-- source/core/gamecontrol.h | 1 + source/games/blood/src/animatesprite.cpp | 6 +++--- source/games/blood/src/view.cpp | 1 + source/games/duke/src/render.cpp | 1 + source/games/exhumed/src/view.cpp | 1 + source/games/sw/src/draw.cpp | 1 + 12 files changed, 31 insertions(+), 10 deletions(-) diff --git a/source/build/include/buildtypes.h b/source/build/include/buildtypes.h index 1ac86d561..d526315a0 100644 --- a/source/build/include/buildtypes.h +++ b/source/build/include/buildtypes.h @@ -149,6 +149,10 @@ enum CSTAT_SPRITE_RESERVED5 = 1u<<14u, // used by Duke 3D (Polymer), Shadow Warrior, Blood CSTAT_SPRITE_INVISIBLE = 1u<<15u, + + // Raze extensions, using the higher bits to avoid conflitcs with the reserved and undocumented bits above. + CSTAT_SPRITE_MDLROTATE = 1u<<16u, // Only for tsprites: rotate if this is a model or voxel. + }; enum { @@ -180,6 +184,7 @@ enum CSTAT_WALL_RESERVED1 = 1u<<13u, CSTAT_WALL_RESERVED2 = 1u<<14u, // used by Shadow Warrior, Blood CSTAT_WALL_RESERVED3 = 1u<<15u, // used by Shadow Warrior, Blood + }; #endif @@ -202,7 +207,7 @@ struct spritetype }; vec3_t opos; }; - uint16_t cstat; + uint32_t cstat; int16_t picnum; int8_t shade; uint8_t pal, clipdist, blend; diff --git a/source/build/include/mdsprite.h b/source/build/include/mdsprite.h index 1c4882ab3..05cf0b10e 100644 --- a/source/build/include/mdsprite.h +++ b/source/build/include/mdsprite.h @@ -210,7 +210,7 @@ EXTERN voxmodel_t *voxmodels[MAXVOXELS]; void voxfree(voxmodel_t *m); voxmodel_t *voxload(int lumpnum); -int32_t polymost_voxdraw(voxmodel_t *m, tspriteptr_t const tspr); +int32_t polymost_voxdraw(voxmodel_t *m, tspriteptr_t const tspr, bool rotate); int md3postload_polymer(md3model_t* m); //int32_t md_thinoutmodel(int32_t modelid, uint8_t *usedframebitmap); diff --git a/source/build/include/polymost.h b/source/build/include/polymost.h index 41bbc7fbf..ee9a14849 100644 --- a/source/build/include/polymost.h +++ b/source/build/include/polymost.h @@ -11,6 +11,7 @@ extern float gtang; extern double gxyaspect; extern float grhalfxdown10x; extern float gcosang, gsinang, gcosang2, gsinang2; +extern int pm_smoothratio; extern void Polymost_prepare_loadboard(void); diff --git a/source/build/src/mdsprite.cpp b/source/build/src/mdsprite.cpp index 1ce3b78f2..dcd16b5c6 100644 --- a/source/build/src/mdsprite.cpp +++ b/source/build/src/mdsprite.cpp @@ -1648,7 +1648,7 @@ int32_t polymost_mddraw(tspriteptr_t tspr) mdmodel_t *const vm = models[tile2model[Ptile2tile(tspr->picnum, (tspr->owner >= MAXSPRITES) ? tspr->pal : sprite[tspr->owner].pal)].modelid]; if (vm->mdnum == 1) - return polymost_voxdraw((voxmodel_t *)vm,tspr); + return polymost_voxdraw((voxmodel_t *)vm,tspr, false); // can't access rotating info anymore else if (vm->mdnum == 3) return polymost_md3draw((md3model_t *)vm,tspr); return 0; diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index c21e2417e..496edb870 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -36,6 +36,7 @@ CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBAL { if (screen) screen->SetTextureFilterMode(); } +int pm_smoothratio; //{ "r_yshearing", "enable/disable y-shearing", (void*)&r_yshearing, CVAR_BOOL, 0, 1 }, disabled because not fully functional @@ -3034,13 +3035,15 @@ void polymost_drawsprite(int32_t snum) { if ((tspr->cstat & 48) != 48 && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]]) { - if (polymost_voxdraw(voxmodels[tiletovox[tspr->picnum]], tspr)) return; + int num = tiletovox[tspr->picnum]; + if (polymost_voxdraw(voxmodels[num], tspr, voxrotate[num>>3] & (1<<(num&7)))) return; break; // else, render as flat sprite } if ((tspr->cstat & 48) == 48 && tspr->picnum < MAXVOXELS && voxmodels[tspr->picnum]) { - polymost_voxdraw(voxmodels[tspr->picnum], tspr); + int num = tspr->picnum; + polymost_voxdraw(voxmodels[tspr->picnum], tspr, voxrotate[num >> 3] & (1 << (num & 7))); return; } } diff --git a/source/build/src/voxmodel.cpp b/source/build/src/voxmodel.cpp index 79db18c36..2e3b37e60 100644 --- a/source/build/src/voxmodel.cpp +++ b/source/build/src/voxmodel.cpp @@ -12,6 +12,7 @@ #include "hw_renderstate.h" #include "texturemanager.h" #include "voxels.h" +#include "gamecontrol.h" #include "glbackend/gl_models.h" #include "palette.h" @@ -50,8 +51,7 @@ voxmodel_t *voxload(int lumpnum) } //Draw voxel model as perfect cubes -// Note: This is a hopeless mess that totally forfeits any chance of using a vertex buffer with its messy coordinate adjustments. :( -int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr) +int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate) { float f, g, k0, zoff; @@ -65,6 +65,13 @@ int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr) //updateanimation((md2model *)m,tspr); + if ((tspr->cstat & CSTAT_SPRITE_MDLROTATE) || rotate) + { + int myclock = (PlayClock << 3) + MulScale(4 << 3, pm_smoothratio, 16); + tspr->ang = (tspr->ang + myclock) & 2047; // will be applied in md3_vox_calcmat_common. + } + + vec3f_t m0 = { m->scale, m->scale, m->scale }; vec3f_t a0 = { 0, 0, m->zadd*m->scale }; diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index 66ae96da2..85242b2f7 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -14,6 +14,7 @@ extern FString currentGame; extern FString LumpFilter; +extern int PlayClock; class FArgs; extern bool GUICapture; extern bool AppActive; diff --git a/source/games/blood/src/animatesprite.cpp b/source/games/blood/src/animatesprite.cpp index dfe6f84f3..fb9e477a9 100644 --- a/source/games/blood/src/animatesprite.cpp +++ b/source/games/blood/src/animatesprite.cpp @@ -574,8 +574,8 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t int const nVoxel = tiletovox[pTSprite->picnum]; - if (nVoxel != -1 && ((voxrotate[nVoxel>>3]&pow2char[nVoxel&7]) != 0 || (picanm[nRootTile].extra&7) == 7)) - pTSprite->ang = (pTSprite->ang+myclock)&2047; + if (nVoxel != -1 && (picanm[nRootTile].extra & 7) == 7) + pTSprite->cstat |= CSTAT_SPRITE_MDLROTATE; // per-sprite rotation setting. } if ((pTSprite->cstat&48) != 48 && hw_models && !(spriteext[nSprite].flags&SPREXT_NOTMD)) @@ -590,7 +590,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t pTSprite->xoffset += tileLeftOffset(nAnimTile); if ((picanm[nRootTile].extra&7) == 7) - pTSprite->ang = (pTSprite->ang+myclock)&2047; + pTSprite->cstat |= CSTAT_SPRITE_MDLROTATE; // per-sprite rotation setting. } } diff --git a/source/games/blood/src/view.cpp b/source/games/blood/src/view.cpp index 6575d1298..938156aa7 100644 --- a/source/games/blood/src/view.cpp +++ b/source/games/blood/src/view.cpp @@ -467,6 +467,7 @@ void viewDrawScreen(bool sceneonly) gInterpolate = I_GetTimeFrac() * MaxSmoothRatio; } else gInterpolate = MaxSmoothRatio; + pm_smoothratio = (int)gInterpolate; if (cl_interpolate) { diff --git a/source/games/duke/src/render.cpp b/source/games/duke/src/render.cpp index 321e2bc31..42bcc0c07 100644 --- a/source/games/duke/src/render.cpp +++ b/source/games/duke/src/render.cpp @@ -474,6 +474,7 @@ void displayrooms(int snum, double smoothratio) int tiltcs = 0; // JBF 20030807 p = &ps[snum]; + pm_smoothratio = (int)smoothratio; if (automapMode == am_full || p->cursectnum == -1) return; diff --git a/source/games/exhumed/src/view.cpp b/source/games/exhumed/src/view.cpp index bd17491b4..8d3e7cf20 100644 --- a/source/games/exhumed/src/view.cpp +++ b/source/games/exhumed/src/view.cpp @@ -207,6 +207,7 @@ void DrawView(double smoothRatio, bool sceneonly) zbob = bsin(2 * bobangle, -3); DoInterpolations(smoothRatio / 65536.); + pm_smoothratio = (int)smoothRatio; int nPlayerSprite = PlayerList[nLocalPlayer].nSprite; int nPlayerOldCstat = sprite[nPlayerSprite].cstat; diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index 6763fd752..915788055 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -1492,6 +1492,7 @@ drawscreen(PLAYERp pp, double smoothratio) PreDraw(); PreUpdatePanel(smoothratio); + pm_smoothratio = (int)smoothratio; if (!ScreenSavePic) { From 6ebbf1288dc6ca5ef6b42d38c4191c2359fd3b0c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Apr 2021 13:01:32 +0200 Subject: [PATCH 10/11] - Duke/RR: Don't delete master switch sprites. The sound system may play sounds on them after their deletion - this resulted in undefined behavior. To ensure properly defined behavior the sprite needs to be retained at least as long as the sound controller may still need it - which cannot be reliably determined so it has to be kept around forever. This would be easier if the sound controller code had proper start and stop events instead of inferring what to do from secondary information. Fixes #288. --- source/games/duke/src/actors.cpp | 9 +++++++-- source/games/duke/src/constants.h | 1 + source/games/duke/src/sounds.cpp | 4 ---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp index a1c737886..04d9e311e 100644 --- a/source/games/duke/src/actors.cpp +++ b/source/games/duke/src/actors.cpp @@ -1002,7 +1002,7 @@ void movemasterswitch(DDukeActor *actor, int spectype1, int spectype2) break; } } - else if (sj->statnum == 6) + else if (sj->statnum == STAT_STANDABLE) { if (sj->picnum == spectype1 || sj->picnum == spectype2) // SEENINE and OOZFILTER { @@ -1010,7 +1010,12 @@ void movemasterswitch(DDukeActor *actor, int spectype1, int spectype2) } } } - deletesprite(actor); + // we cannot delete this because it may be used as a sound source. + // This originally depended on undefined behavior as the deleted sprite was still used for the sound + // with no checking if it got reused in the mean time. + spri->picnum = 0; // give it a picnum without any behavior attached, just in case + spri->cstat |= CSTAT_SPRITE_INVISIBLE; + changespritestat(actor->GetIndex(), STAT_REMOVED); } } } diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h index 9251b1042..d88db1693 100644 --- a/source/games/duke/src/constants.h +++ b/source/games/duke/src/constants.h @@ -256,6 +256,7 @@ enum STAT_DESTRUCT = 100, STAT_BOWLING = 105, + STAT_REMOVED = MAXSTATUS-2, STAT_NETALLOC = MAXSTATUS-1 }; diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp index b4066e57d..79970b348 100644 --- a/source/games/duke/src/sounds.cpp +++ b/source/games/duke/src/sounds.cpp @@ -445,10 +445,6 @@ void GameInterface::UpdateSounds(void) int S_PlaySound3D(int sndnum, DDukeActor* actor, const vec3_t* pos, int channel, EChanFlags flags) { - if (sndnum == GENERIC_AMBIENCE1 || sndnum == DUMPSTER_MOVE) - { - int a = 0; - } auto const pl = &ps[myconnectindex]; if (!soundEngine->isValidSoundId(sndnum+1) || !SoundEnabled() || actor == nullptr || !playrunning() || (pl->timebeforeexit > 0 && pl->timebeforeexit <= REALGAMETICSPERSEC * 3)) return -1; From 9828cd712952330c48d3c0a1b4f153e80df198b2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Apr 2021 13:02:26 +0200 Subject: [PATCH 11/11] - this better have a null check... --- source/games/duke/src/actors_d.cpp | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/source/games/duke/src/actors_d.cpp b/source/games/duke/src/actors_d.cpp index 6feb2aa15..3ceaf8ed2 100644 --- a/source/games/duke/src/actors_d.cpp +++ b/source/games/duke/src/actors_d.cpp @@ -2556,26 +2556,29 @@ static void greenslime(DDukeActor *actor) { auto s5 = actor->temp_actor; makeitfall(actor); - s5->s.xvel = 0; - - int l = s5->s.ang; - - s->z = s5->s.z; - s->x = s5->s.x + bcos(l, -11); - s->y = s5->s.y + bsin(l, -11); - - s->picnum = GREENSLIME + 2 + (global_random & 1); - - if (s->yrepeat < 64) s->yrepeat += 2; - else + if (s5) { - if (s->xrepeat < 32) s->xrepeat += 4; + s5->s.xvel = 0; + + int l = s5->s.ang; + + s->z = s5->s.z; + s->x = s5->s.x + bcos(l, -11); + s->y = s5->s.y + bsin(l, -11); + + s->picnum = GREENSLIME + 2 + (global_random & 1); + + if (s->yrepeat < 64) s->yrepeat += 2; else { - t[0] = -1; - x = ldist(actor, s5); - if (x < 768) { - s5->s.xrepeat = 0; + if (s->xrepeat < 32) s->xrepeat += 4; + else + { + t[0] = -1; + x = ldist(actor, s5); + if (x < 768) { + s5->s.xrepeat = 0; + } } } }