From e2cb3910185f941e48f2530dafbacdec34385009 Mon Sep 17 00:00:00 2001
From: FireMario211 <17692105+FireMario211@users.noreply.github.com>
Date: Tue, 16 Apr 2024 22:50:34 -0400
Subject: [PATCH] v1.6.0 update! gonna publish tomorrow,
 https://github.com/FireMario211/Prism-Menu/issues/7

---
 .github/workflows/build-mac.yml | 47 ++++++++++++++++++++
 .github/workflows/build.yml     |  3 --
 build-mac.sh                    |  7 +++
 changelog.md                    |  1 +
 mod.json                        |  2 +-
 src/Hacks/Global.cpp            |  2 +-
 src/Hacks/Quartz.cpp            | 79 ++++++++++++++++++++++++++++++---
 src/hacks.hpp                   |  2 +-
 8 files changed, 130 insertions(+), 13 deletions(-)
 create mode 100644 .github/workflows/build-mac.yml
 create mode 100644 build-mac.sh

diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml
new file mode 100644
index 0000000..8170957
--- /dev/null
+++ b/.github/workflows/build-mac.yml
@@ -0,0 +1,47 @@
+# because for WHATEVER REASON, loadFromCheckpoint is either tail call optimized or idk
+
+name: Build Geode Mod (Mac OS)
+
+on:
+  workflow_dispatch:
+  pull_request:
+  push:
+    branches:
+      - "main"
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        config:
+        - name: macOS
+          os: macos-latest
+        
+    name: ${{ matrix.config.name }}
+    runs-on: ${{ matrix.config.os }}
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+          
+      - name: Build the mod
+        uses: geode-sdk/build-geode-mod@main
+        with:
+          build-config: 'Release'
+          combine: true
+          target: ${{ matrix.config.target }}
+
+  package:
+    name: Package builds
+    runs-on: ubuntu-latest
+    needs: ['build']
+    steps:
+      - uses: geode-sdk/build-geode-mod/combine@main
+        id: build
+
+      - uses: actions/upload-artifact@v4
+        with:
+          name: Build Output
+          path: ${{ steps.build.outputs.build-output }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 035ae4f..be5de3f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,9 +16,6 @@ jobs:
         - name: Windows
           os: windows-latest
 
-        - name: macOS
-          os: macos-latest
-
         - name: Android32
           os: ubuntu-latest
           target: Android32
diff --git a/build-mac.sh b/build-mac.sh
new file mode 100644
index 0000000..ce68229
--- /dev/null
+++ b/build-mac.sh
@@ -0,0 +1,7 @@
+export GEODE_BINDINGS_REPO_PATH=~/bindingsneverchange
+#cmake -B buildrwdi -DCMAKE_BUILD_TYPE=RelWithDebInfo
+#cmake --build buildrwdi --config RelWithDebInfo
+#cmake -B buildrel -DCMAKE_BUILD_TYPE=Release
+#cmake --build buildrel --config Release
+#cmake -B builddeb -DCMAKE_BUILD_TYPE=Debug
+#cmake --build builddeb --config Debug
diff --git a/changelog.md b/changelog.md
index 3568fb0..453d27e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,5 @@
 # v1.6.0
+    - Added Quartz Bot (A replay bot)
     - Added TPS Bypass (for all platforms)
     - Allowed Noclip Accuracy to no longer require Anticheat Bypass to be enabled.
     - Added No Death Effect.
diff --git a/mod.json b/mod.json
index ff5cab7..019ae2c 100644
--- a/mod.json
+++ b/mod.json
@@ -1,6 +1,6 @@
 {
 	"geode": "2.0.0-beta.24",
-	"version": "v1.5.1",
+	"version": "v1.6.0",
 	"gd": {
 		"win": "2.204",
 		"mac": "2.200",
diff --git a/src/Hacks/Global.cpp b/src/Hacks/Global.cpp
index 024cb36..4a65a9b 100644
--- a/src/Hacks/Global.cpp
+++ b/src/Hacks/Global.cpp
@@ -34,8 +34,8 @@ int syncCooldown = 0;
 class $modify(CCScheduler) {
     void update(float dt) {
         HackItem* speedhack = Hacks::getHack("Speedhack");
-        speedhack->value.floatValue = std::max(speedhack->value.floatValue, 0.01f);
         if (speedhack == nullptr) return CCScheduler::update(dt);
+        speedhack->value.floatValue = std::max(speedhack->value.floatValue, 0.01f);
         if (speedhack->value.floatValue == 1.0F && !current_macro.isEnabled) return CCScheduler::update(dt);
         float speedHackValue = stof(Utils::setPrecision(speedhack->value.floatValue, 3));
         dt *= speedHackValue; // def not copied!
diff --git a/src/Hacks/Quartz.cpp b/src/Hacks/Quartz.cpp
index 5b986f6..b62f1d3 100644
--- a/src/Hacks/Quartz.cpp
+++ b/src/Hacks/Quartz.cpp
@@ -401,6 +401,10 @@ void UpdatePlayerType(PlayerObject* player, PlayerFrame frame, MacroType type) {
 
 bool STOPTIME = false;
 
+bool stillHolding1 = false;
+bool stillHolding2 = false;
+bool hadHeld = false;
+
 class $modify(QuartzPlayLayer, PlayLayer) {
     float bot_frame_dt = 0.F;
     int bot_frame = 0;
@@ -484,7 +488,13 @@ class $modify(QuartzPlayLayer, PlayLayer) {
                         current_macro.levelInfo.id = m_level->m_levelID;
                         current_macro.ldm = m_level->m_lowDetailModeToggled;
                     } else if (Hacks::isHackEnabled("Playback")) {
-                        m_fields->lastInputFrame = current_macro.inputs[current_macro.inputs.size() - 1].frame; 
+                        if (current_macro.inputs.size() == 0) {
+                            current_macro.isEnabled = false;
+                            m_fields->started = false;
+                            FLAlertLayer::create("Error", "You are attempting to <cy>playback a macro</c> that <cr>has no inputs!</c>\nConsider <cy>recording the macro</c> to play it back!", "OK")->show();
+                        } else {
+                            m_fields->lastInputFrame = current_macro.inputs[current_macro.inputs.size() - 1].frame; 
+                        }
                     }
                     Hacks::setTPS(current_macro.framerate);
                 } else {
@@ -864,6 +874,8 @@ class $modify(QuartzPlayLayer, PlayLayer) {
         } else {
             Hacks::setTPS();
         }
+        /*stillHolding1 = false;
+        stillHolding2 = false;*/
         PlayLayer::resetLevel();
     }
     void postUpdate(float dt) {
@@ -915,6 +927,45 @@ class $modify(QuartzPlayLayer, PlayLayer) {
                     }
                 }
                 m_fields->bot_frame = lastFrame;
+                if ((stillHolding1 || stillHolding2) && !hadHeld) {
+                    bool player2Vis = m_player2 != nullptr && m_player2->isRunning();
+                    QuartzInput input1;
+                    QuartzInput input2;
+                    bool hasReld1 = false;
+                    bool hasReld2 = false;
+                    if (stillHolding1 && !player2Vis) {
+                        input1 = QuartzInput(lastFrame + 1, 1, false, false);
+                        hasReld1 = true;
+                    } else if (stillHolding1 && !stillHolding2 && player2Vis) {
+                        input2 = QuartzInput(lastFrame + 1, 1, true, false);
+                        //hadHeld = false;
+                        hasReld2 = true;
+                    } else if (stillHolding1 && stillHolding2 && player2Vis) {
+                        input1 = QuartzInput(lastFrame + 1, 1, false, false);
+                        input2 = QuartzInput(lastFrame + 1, 1, true, false);
+                        hasReld1 = true;
+                        hasReld2 = true;
+                    }
+                    if (hasReld1 || hasReld2) {
+                        if (Hacks::isHackEnabled("Frame Fix")) {
+                            if (hasReld1) {
+                                PlayerFrame frameFixed = PlayerToFrame(m_player1, lastFrame + 1, false, false, 1);
+                                frameFixed.fix = true;
+                                frameFixed.type = MacroType::QUARTZ;
+                                input1.frameFix = frameFixed;
+                            }
+                            if (hasReld2) {
+                                PlayerFrame frameFixed = PlayerToFrame(m_player2, lastFrame + 1, true, false, 1);
+                                frameFixed.fix = true;
+                                frameFixed.type = MacroType::QUARTZ;
+                                input2.frameFix = frameFixed;
+                            }
+                        }
+                        if (hasReld1) current_macro.inputs.push_back(input1);
+                        if (hasReld2) current_macro.inputs.push_back(input2);
+                        log::debug("Fix Release Input at frame {}", lastFrame);
+                    }
+                }
             }
         }
 		PlayLayer::loadFromCheckpoint(p0);
@@ -930,6 +981,10 @@ class $modify(GJBaseGameLayer) {
         float currentFPS = 1.F / realDt;
         float macroFPS = current_macro.framerate;
         if (PlayLayer::get() != nullptr) {
+            /*if (lastFrameChanged != m_gameState.m_unk1f8) {
+                lastFrameChanged = m_gameState.m_unk1f8;
+                std::cout << stillHolding << " " << m_gameState.m_unk1f8 << std::endl;
+            }*/
             auto playLayer = static_cast<QuartzPlayLayer*>(QuartzPlayLayer::get());
             if (!playLayer->m_fields->started || !playLayer->m_fields->playLayerPostUpdate) return GJBaseGameLayer::update(realDt);
             playLayer->m_fields->replay = Hacks::isHackEnabled("Playback") || Hacks::isHackEnabled("Record");
@@ -1141,6 +1196,8 @@ class $modify(GJBaseGameLayer) {
     }
     void handleButton(bool push, int button, bool player1) {
         //if (STOPTIME) return;
+        if (player1) stillHolding1 = push;
+        if (!player1) stillHolding2 = push;
         GJBaseGameLayer::handleButton(push,button,player1);
         if (PlayLayer::get() != nullptr && Hacks::isHackEnabled("Record")) {
             auto playLayer = static_cast<QuartzPlayLayer*>(QuartzPlayLayer::get());
@@ -1158,17 +1215,25 @@ class $modify(GJBaseGameLayer) {
                     input.frameFix = frameFixed;
                 }
             }
-            if (push) {
-                std::cout << "[HOLD] ";
-            } else {
-                std::cout << "[RELEASE] ";
-            }
-            std::cout << playLayer->m_fields->bot_frame_dt << "|" << playLayer->m_fields->bot_frame << "|1" << std::endl;
+            // geode needs to stop logging this in the .txt files
+            log::debug("[{}] Frame {}", (push) ? "HOLD" : "RELEASE", playLayer->m_fields->bot_frame);
             current_macro.inputs.push_back(input);
         }
     }
 };
 
+// robert, why do you handle the handleButton func when the player holds, but dont handle it when the player doesnt? you make no sense!
+class $modify(PlayerObject) {
+    void pushButton(PlayerButton p0) {
+        hadHeld = true;
+        PlayerObject::pushButton(p0);
+    }
+    void releaseButton(PlayerButton p0) {
+        hadHeld = false;
+        PlayerObject::releaseButton(p0);
+    }
+};
+
 // the "watermark" (fig method)
 class $modify(EndLevelLayer) {
     void customSetup() {
diff --git a/src/hacks.hpp b/src/hacks.hpp
index 1f72e18..88b4127 100644
--- a/src/hacks.hpp
+++ b/src/hacks.hpp
@@ -416,10 +416,10 @@ class Hacks {
         }
     }
     static void setPitch(float pitch) {
+#ifndef GEODE_IS_MACOS // TODO: figure out why it turns to creepypasta when tuning down speedhack
         if (!Hacks::isHackEnabled("Speedhack Audio")) {
             pitch = 1.0F;
         }
-#ifndef GEODE_IS_MACOS // TODO: figure out why it turns to creepypasta when tuning down speedhack
         auto fmod = FMODAudioEngine::sharedEngine();
         FMOD_RESULT result; // ensuring
         FMOD::ChannelGroup* master_group = nullptr;