diff --git a/.github/workflows/multi-platform.yml b/.github/workflows/multi-platform.yml index 6a5086b..0f608c3 100644 --- a/.github/workflows/multi-platform.yml +++ b/.github/workflows/multi-platform.yml @@ -15,6 +15,10 @@ jobs: - name: Windows os: windows-latest + - name: Android64 + os: ubuntu-latest + target: Android64 + name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} diff --git a/docs/css/style.css b/docs/css/style.css deleted file mode 100644 index 35e6948..0000000 --- a/docs/css/style.css +++ /dev/null @@ -1,316 +0,0 @@ - -@import url('https://fonts.cdnfonts.com/css/google-sans'); - -:root { - --background: rgb(27, 27, 29); - --foreground: rgb(27, 27, 29); - --foreground-light: rgb(27, 27, 29); - --bg-accent: rgb(27, 27, 29); -} - -* { - margin: 0; - padding: 0; - font-family: 'Product Sans', sans-serif; - color: var(--foreground); - box-sizing: border-box; - transition: all 200ms; -} - -img { - vertical-align: bottom; -} - -html { - scroll-behavior: smooth; -} - -body { - background-color: var(--background); -} - -.kbd { - padding: 0 8px; - color: var(--foreground); - background-color: var(--bg-accent); - border-radius: 8px; -} - -.wrapper { - width: 80vw; - margin: 0 auto; -} - -.header { - display: flex; - justify-content: space-between; - align-items: center; - margin: 16px auto; - padding: 0 16px; -} - -.links { - display: flex; - gap: 16px; - list-style: none; -} - -.links a { - text-decoration: none; - padding: 8px 16px; - color: var(--foreground); - background-color: var(--bg-accent); - border-radius: 32px; - font-size: 24px; -} - -.links a:hover { - color: var(--background); - background-color: var(--foreground); -} - -.logo { - color: var(--background); - font-size: 32px; - background-color: var(--foreground); - padding: 8px 16px; - border-radius: 32px; -} - -.hero { - display: flex; - height: 80vh; - flex-direction: column; - justify-content: center; - background-color: var(--bg-accent); - padding: 32px; - border-radius: 25px; -} - -.hero > h1 { - color: var(--foreground); - font-size: 64px; -} - -.hero > p { - color: var(--foreground); - font-size: 32px; -} - -.overview { - margin: 16px auto; -} - -.overview h2 { - /* display: inline-block; */ - color: var(--background); - font-size: 32px; - background-color: var(--foreground); - padding: 8px 16px; - margin-bottom: 16px; - border-radius: 32px; -} - -.overview-info p { - color: var(--foreground); - font-size: 32px; - margin-bottom: 16px; - border-radius: 32px; - text-wrap: balance; -} - -.overview-info ul { - color: var(--foreground); - font-size: 24px; - margin-bottom: 16px; - border-radius: 32px; - padding-left: 32px; -} - -.overview-split { - display: flex; - justify-content: space-between; - align-items: center; -} - -.overview-image { - padding: 8px; - background-color: var(--foreground); - border-radius: 24px; -} - -.overview-image:hover { - background-color: var(--bg-accent); -} - -.overview-image img { - margin-bottom: 0; - width: 600px; - border-radius: 16px; -} - -.install { - margin: 16px auto; -} - -.install h2 { - /* display: inline-block; */ - color: var(--background); - font-size: 32px; - background-color: var(--foreground); - padding: 8px 16px; - margin-bottom: 16px; - border-radius: 32px; -} - -.install-info { - color: var(--foreground); - font-size: 32px; - margin-left: 32px; - margin-bottom: 16px; - border-radius: 32px; - text-wrap: balance; -} - -.install-info ol { - color: var(--foreground); - font-size: 24px; - margin-bottom: 16px; - border-radius: 32px; - padding-left: 32px; -} - -.install-split { - display: flex; - justify-content: space-between; - align-items: center; -} - -.install-image { - padding: 8px; - background-color: var(--foreground); - border-radius: 24px; -} - -.install-image:hover { - background-color: var(--bg-accent); -} -.install-image img { - margin-bottom: 0; - width: 600px; - border-radius: 16px; -} - -.footer { - display: flex; - flex-direction: column; - align-items: center; - margin-top: 48px; - margin-bottom: 16px; -} - -.footer * { - margin-bottom: 8px; - color: var(--foreground-light); -} - -.footer ul { - display: flex; - list-style: none; -} - -.footer ul li { - margin-right: 8px; - margin-left: 8px; -} - -.footer h3 { - font-size: 24px; -} - -.footer p { - font-size: 16px; -} - -#theme-switch svg { - transform: translate(0, 3px); -} - -#theme-switch * { - color: var(--foreground); -} - -#theme-switch:hover * { - color: var(--background); -} - -@media (max-width: 1210px) { - .menu-link { - display: none; - } -} - -@media (max-width: 1100px) { - body { - width: auto; - padding: 4px; - } - - .hero { - height: 50vh; - } - - header > .wrapper { - padding-left: 0; - padding-right: 0; - } - - .hero > h1 { - font-size: 32px; - } - - .hero > p { - font-size: 24px; - } - - .overview > h2 { - padding: 8px 24px; - } - - .overview-split { - flex-direction: column; - } - - .overview-image img { - width: 80vw; - height: auto; - } - - .install > h2 { - padding: 8px 24px; - } - - .install-split { - flex-direction: column-reverse; - } - - .install-image img { - width: 80vw; - height: auto; - } - - .install-info { - margin-left: 0; - margin-top: 16px; - } - - .footer { - margin-top: 48px; - } - - .footer ul { - flex-direction: column; - align-items: center; - margin-bottom: 16px; - } -} diff --git a/docs/img/install.png b/docs/img/install.png deleted file mode 100644 index af9e2a1..0000000 Binary files a/docs/img/install.png and /dev/null differ diff --git a/docs/img/screenshot.png b/docs/img/screenshot.png deleted file mode 100644 index 0f6aaf2..0000000 Binary files a/docs/img/screenshot.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 3271ba5..0000000 --- a/docs/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - GDH - Official website - - -
-
- -
-
- -
-
-

GDH is a next-level mod menu.

-

GDH is a mod menu made to be both easy to use & have a lot of features

-
- -
-

Feature Overview

-
- -

GDH has a lot of features, here are the main ones:

-
    -
  • Basic Hacks
  • -
  • Bot called ReplayEngine
  • -
  • TPS Unlocker
  • -
  • Labels
  • -
  • Keybinds
  • -
  • Hitboxes
  • -
  • And more!
  • -
-
- - Screenshot - -
-
- -
-

Install Guide

-
- - Screenshot - - -

GDH is really easy to install, here's the guide:

-
    -
  1. Ensure Geode is installed
  2. -
  3. Open Download tab in Geode menu
  4. -
  5. Find GDH, install it and restart GD
  6. -
  7. Done! Press Tab to open integrated menu!
  8. -
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/docs/js/script.js b/docs/js/script.js deleted file mode 100644 index b1abcc7..0000000 --- a/docs/js/script.js +++ /dev/null @@ -1,22 +0,0 @@ - -var theme = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)').matches : true; - -var bulb = ''; -var noBulb = ''; - -function setTheme(dark) { - document.documentElement.style.setProperty("--background", dark ? "rgb(27, 27, 29)" : "rgb(229, 255, 240)"); - document.documentElement.style.setProperty("--foreground", dark ? "rgb(198, 243, 221)" : "rgb(27, 32, 30)"); - document.documentElement.style.setProperty("--foreground-light", dark ? "rgb(100, 100, 100)" : "rgb(130, 130, 130)"); - document.documentElement.style.setProperty("--bg-accent", dark ? "rgb(73, 110, 84)" : "rgb(178, 223, 191)"); - document.getElementById("theme-switch").innerHTML = dark ? noBulb : bulb; -} - -function switchTheme() { - theme = !theme; - setTheme(theme); -} - -window.onload = () => { - setTheme(theme); -} diff --git a/src/gui.cpp b/src/gui.cpp index eca2b83..e81c9f0 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -4,6 +4,7 @@ #include "config.hpp" #include #include "hacks.hpp" +#include "memory.hpp" std::chrono::steady_clock::time_point animationStartTime; bool isAnimating = false; @@ -55,6 +56,17 @@ void Gui::animateAlpha() std::vector stretchedWindows; void Gui::Render() { + #ifdef GEODE_IS_ANDROID + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1); + ImGui::Begin("toggle gui"); + if (ImGui::Button("toggle")) { + Toggle(); + } + ImGui::End(); + ImGui::PopStyleVar(); + #endif + + if (isAnimating) { animateAlpha(); } @@ -110,6 +122,8 @@ void Gui::Render() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::Combo("##Menu scale", &m_index_scale, items, IM_ARRAYSIZE(items))) { m_scale = float(atof(items[m_index_scale])) / 100.0f; + config.set("gui_scale", m_scale); + config.set("gui_index_scale", m_index_scale); m_needRescale = true; ImGuiCocos::get().reload(); } @@ -140,8 +154,46 @@ void Gui::Render() { ApplyGuiColors(!inverted); } } - else if (windowName == "Variables") { - ImGui::Button("ff"); + else if (windowName == "Framerate") { + bool tps_enabled = config.get("tps_enabled", false); + float tps_value = config.get("tps_value", 240.f); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (35 + 5) * m_scale); + if (ImGui::DragFloat("##tps_value", &tps_value, 1, 1, FLT_MAX, "%0.f TPS")) + config.set("tps_value", tps_value); + + ImGui::SameLine(); + if (ImGuiH::Checkbox("##tps_enabled", &tps_enabled, m_scale)) + config.set("tps_enabled", tps_enabled); + + bool speedhack_enabled = config.get("speedhack_enabled", false); + float speedhack_value = config.get("speedhack_value", 1.f); + + bool speedhackAudio_enabled = config.get("speedhackAudio_enabled", false); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (35 + 5) * m_scale); + if (ImGui::DragFloat("##speedhack_value", &speedhack_value, 0.01f, 0, FLT_MAX, "Speed: %.2fx")) + config.set("speedhack_value", speedhack_value); + + ImGui::SameLine(); + if (ImGuiH::Checkbox("##speedhack_enabled", &speedhack_enabled, m_scale)) + config.set("speedhack_enabled", speedhack_enabled); + + if (ImGuiH::Checkbox("Speedhack Audio", &speedhackAudio_enabled, m_scale)) + config.set("speedhackAudio_enabled", speedhackAudio_enabled); + } + else if (windowName == "Variables") { + static uintptr_t address = geode::base::get(); + ImGui::Text("%s", fmt::format("{:x} ({:x})", address, address - geode::base::get()).c_str()); + + if (ImGui::Button("scan")) + address = memory::PatternScan(address+1, 0, "00 60 6A 48"); + + if (ImGui::Button("scan2")) + address = memory::PatternScan(address+1, 0, "80 67 6A 48"); + + if (ImGui::Button("reset")) + address = geode::base::get(); } else { for (auto& hck : win.hacks) { @@ -160,14 +212,30 @@ void Gui::Render() { config.set(hck.config, enabled); if (!hck.game_var.empty()) GameManager::get()->setGameVariable(hck.game_var.c_str(), enabled); - hck.handlerFunc(enabled); + if (hck.handlerFunc) hck.handlerFunc(enabled); } - - ImGui::PopStyleColor(); if (ImGui::IsItemHovered() && !hck.desc.empty()) { ImGui::SetTooltip("%s", hck.desc.c_str()); } + + if (hck.handlerCustomWindow) { + ImGui::SameLine(); + if (ImGui::ArrowButton(fmt::format("{} Settings", hck.name).c_str(), ImGuiDir_Right)) { + ImGui::OpenPopup(fmt::format("{} Settings", hck.name).c_str()); + } + + if (ImGui::BeginPopupModal(fmt::format("{} Settings", hck.name).c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + hck.handlerCustomWindow(); + + if (ImGui::Button("Close", {400 * m_scale, NULL})) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + + ImGui::PopStyleColor(); } } @@ -180,6 +248,10 @@ void Gui::Init() { auto &config = Config::get(); stretchedWindows.clear(); + + m_scale = config.get("gui_scale", 1.f); + m_index_scale = config.get("gui_index_scale", 7); + ApplyGuiColors(config.get("gui_inverted", false)); ApplyColor(themes[config.get("gui_color_index", 0)]); ApplyStyle(m_scale); diff --git a/src/hacks.cpp b/src/hacks.cpp index b6b2621..03ac695 100644 --- a/src/hacks.cpp +++ b/src/hacks.cpp @@ -1,11 +1,13 @@ #include "hacks.hpp" #include "config.hpp" +#include "gui.hpp" +#include void Hacks::Init() { m_windows = { {"Core", 10, 10, 200, 230, { - {"Free Window Resize", "Allows free window resizing", "free_win_resize"}, + {"Free Window Resize", "Allows free window resizing", "free_win_resize"}, // + {"Noclip", "The player will be invincible to obstacles", "noclip"}, // + {"Unlock Items", "The following elements will be unlocked:\n- Icons + Colors\n- Practice Music Sync\n- Music Unlocker", "unlock_items"}, // + {"No Respawn Blink", "Upon respawning, the cube will not produce an unpleasant flicker", "no_respawn_blink"}, // + @@ -32,15 +34,14 @@ void Hacks::Init() { {"Auto Pickup Coins", "Collects all coins in the level", "auto_pickup_coins"}, // + {"Auto Practice Mode", "Auto-enables practice mode", "auto_practice_mode"}, // + {"Auto Song Download", "Automatic downloading of song when you enter an online level", "auto_song_download"}, // + - {"Allow Low Volume", "Removes the limit on minimum volume percentage", "allow_low_volume"}, // + - {"Anticheat Bypass", "Disables level kicking at level completion", "anticheat_bypass"}, + {"Allow Low Volume", "Removes the limit on minimum volume percentage", "allow_low_volume"}, // + {"Coins In Practice", "The ability to collect coins in practice", "coins_in_practice"}, {"Confirm Exit", "Warning before level exit", "confim_exit", "0167"}, // + {"Fast Chest Open", "Removes the delay for opening chests", "fast_chest_open"}, {"Force Dont Enter", "Disables effects when objects enter the viewable play area", "force_dont_enter"}, {"Force Dont Fade", "Disables effects when objects leave the viewable play area", "force_dont_fade"}, {"Random Seed", "Changes the seed game so that the random trigger is not triggered randomly", "random_seed"}, - {"Respawn Time", "Changes respawn time on death", "respawn_time"}, + {"Respawn Time", "Changes respawn time on death", "respawn_time"}, // + {"Restart Level", "Reload the level", "restart_level"}, {"Practice Mode", "Enter practice mode", "practice_mode"}, {"Ignore ESC", "Prevents exiting the level", "ignore_esc"}, // + @@ -48,12 +49,13 @@ void Hacks::Init() { {"Jump Hack", "Removes the barrier to jump gravity", "jump_hack"}, // + {"Show Percentage", "Show percentages in level progress", "show_percentage", "0040"}, // + {"Smart Startpos", "Restores correct gameplay without startpos settings", "smart_startpos"}, - {"Startpos Switcher", "The ability to switch between starting positions using the keys that you setted in keybinds", "startpos_switcher"}, - {"Reset Camera", "When switching between starting positions, the camera may move, so this feature fixes that unpleasant switch", "reset_cameara"}, + {"Startpos Switcher", "The ability to switch between starting positions using the keys that you setted in keybinds", "startpos_switcher"}, // + {"RGB Icons", "LGBT icons, yes :3", "rgb_icons"}, {"Solid Wave Trail", "Disables wave blending", "solid_wave_trail"}, {"Show Triggers", "Displaying triggers on the PlayLayer", "show_triggers"}, {"Show Hitboxes", "Visualizes hitbox levels", "show_hitboxes"}, + {"Show Total Attempts", "", "show_total_attempts"}, // + + {"Stop triggers on death", "Stops move/rotation triggers on death so you can see what killed you", "stop_triggers_on_death"}, // + {"All Modes Platformer", "Removes the limit on all modes in the platformer", "all_modes_platformer"}, {"Force Platformer", "Enables platformer mode in all levels", "force_platformer"}, {"Hide Attempts", "Hides the attempt count in-game", "hide_attempts", "0135"}, // + @@ -66,32 +68,31 @@ void Hacks::Init() { {"No Shaders", "Disabling shaders in levels", "no_shaders"}, // + {"No Particles", "Disables resuming the particle system", "no_particles"}, // + {"No Short Numbers", "All numbers are displayed in full\n(For example, \"1.5M\" becomes \"1500000\")", "no_short_numbers"}, // + - {"No BG Flash", "Removes the unpleasant flicker when triggering the portal", "no_bg_flash"}, + {"No BG Flash", "Removes the unpleasant flicker when triggering the portal", "no_bg_flash"}, // + {"No Glow", "Disables glow on objects", "no_glow"}, // + {"No Mirror", "Disables level mirroring", "no_mirror_portal"}, // + {"No New Best Popup", "Disable the new best popup", "no_new_best_popup"}, // + {"No Portal Lighting", "Disables lightning when entering mini/large portal", "no_portal_lighting"}, - {"No Pulse", "Disables pulsation of falls, orbs, etc", "no_pulse"}, + {"No Pulse", "Disables pulsation of falls, orbs, etc", "no_pulse"}, // + + {"Pulse Size", "Changes pulsation of falls, orbs, etc", "pulse_size"}, // + {"No Robot Fire", "Hides robot boost fire", "no_robot_fire"}, // + {"No Spider Dash", "Disables spider dash trail when teleporting", "no_spider_dash"}, // + - {"No Trail", "Removes the trail located near the player", "no_trail"}, - {"Always Trail", "Displays the trail near the player at any location", "always_trail"}, - {"No Wave Trail", "Disables the trail on the wave", "no_wave_trail"}, - {"Wave Trail Size", "Resizes the trail", "wave_trail_size"}, - {"No Wave Pulse", "Disables the pulsation of the trail on the wave", "no_wave_pulse"}, - {"Wave Trail Fix", "Corrects distortion in the wave (an attempt by RobTop to make a non-glitchy wave, but again a fail)", "wave_trail_fix"} + {"No Trail", "Removes the trail located near the player", "no_trail"}, // + + {"Always Trail", "Displays the trail near the player at any location", "always_trail"}, // + + {"Wave Trail Size", "Resizes the wave trail", "wave_trail_size"}, // + + {"Wave Trail On Death", "", "wave_trail_on_death"} // + } }, {"Creator", 450, 10, 220, 250, { {"Copy Hack", "Copy any online level without a password", "copy_hack"}, // + {"Custom Object Bypass", "Removes the limit restricted to 1000 objects", "custom_object_bypass"}, - {"Default Song Bypass", "Removes restrictions on secret official songs", "default_song_bypass"}, - {"Scale Snap Bypass", "Removes the slider snapping when stretched from 0.97 to 1.03", "scale_snap_bypass"}, - {"Verify Hack", "Publish a level without verification", "verify_hack"}, - {"Smooth Editor Trail", "Makes the wave smoother in the editor", "smooth_editor_trail"}, + {"Default Song Bypass", "Removes restrictions on secret official songs", "default_song_bypass"}, // + + {"Editor Extension", "", "editor_extension"}, + {"Verify Hack", "Publish a level without verification", "verify_hack"}, // + + {"Smooth Editor Trail", "Makes the wave smoother in the editor", "smooth_editor_trail"}, // + {"Level Edit", "Edit any online level", "level_edit"}, // + - {"No (C) Mark", "Removes copyright on copied levels", "no_c_mark"} + {"No (C) Mark", "Removes copyright on copied levels", "no_c_mark"} // + } }, {"Framerate", 450, 270, 220, 130}, @@ -102,6 +103,107 @@ void Hacks::Init() { }; auto &config = Config::get(); + + #ifdef GEODE_IS_WINDOWS + SetHandlerByConfig("free_win_resize", [this](bool enabled) { + static auto result1 = geode::Mod::get()->patch((void*)(geode::base::getCocos() + 0xd6eca), {0x90, 0x90, 0x90, 0x90, 0x90}); + static auto patch1 = result1.isErr() ? nullptr : result1.unwrap(); + + static auto result2 = geode::Mod::get()->patch((void*)(geode::base::getCocos() + 0xd5089), {0x90, 0x90, 0x90, 0x90, 0x90, 0x90}); + static auto patch2 = result1.isErr() ? nullptr : result2.unwrap(); + + static auto result3 = geode::Mod::get()->patch((void*)(geode::base::getCocos() + 0xd6567), {0x48, 0xe9}); + static auto patch3 = result1.isErr() ? nullptr : result3.unwrap(); + + if (enabled) { + if (patch1) (void) patch1->enable(); + if (patch2) (void) patch1->enable(); + if (patch3) (void) patch1->enable(); + } else { + if (patch1) (void) patch1->disable(); + if (patch2) (void) patch1->disable(); + if (patch3) (void) patch1->disable(); + } + }); + + #endif + + SetHandlerByConfig("editor_extension", [this](bool enabled) { + uintptr_t address1; //Pattern: 00 60 6A 48 (2x) + uintptr_t address2; //Pattern: 80 67 6A 48 (1x) + + #ifdef GEODE_IS_WINDOWS + address1 = geode::base::get() + 0x607ca0; + address2 = geode::base::get() + 0x607ca4; + #elif defined(GEODE_IS_ANDROID64) + address1 = geode::base::get() + 0x65d740; + address2 = geode::base::get() + 0x67790c; + #endif + + + static auto result1 = geode::Mod::get()->patch((void*)(address1), {0x00, 0x60, 0xEA, 0x4B}); + static auto patch1 = result1.isErr() ? nullptr : result1.unwrap(); + + static auto result2 = geode::Mod::get()->patch((void*)(address2), {0x00, 0x60, 0xEA, 0x4B}); + static auto patch2 = result1.isErr() ? nullptr : result2.unwrap(); + + if (enabled) { + if (patch1) (void) patch1->enable(); + if (patch2) (void) patch2->enable(); + } else { + if (patch1) (void) patch1->disable(); + if (patch2) (void) patch2->disable(); + } + }); + + + SetHandlerByConfig("hide_pause_menu", [this](bool enabled) { + auto pl = PlayLayer::get(); + if (pl && pl->m_isPaused && pauseLayer != nullptr) + pauseLayer->setVisible(!enabled); + }); + + SetCustomWindowHandlerByConfig("wave_trail_size", [this, &config]() { + float value = config.get("wave_trail_size_value", 1.f); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::DragFloat("##waveTrailSize", &value, 0.01f, 0.0f, FLT_MAX, "Wave Trail Size: %.2f")) + config.set("wave_trail_size_value", value); + }); + + SetCustomWindowHandlerByConfig("respawn_time", [this, &config]() { + float value = config.get("respawn_time_value", 1.f); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::DragFloat("##respawn_time_value", &value, 0.01f, 0.0f, FLT_MAX, "Respawn Time: %.2f")) + config.set("respawn_time_value", value); + }); + + SetCustomWindowHandlerByConfig("pulse_size", [this, &config]() { + auto &gui = Gui::get(); + + float value = config.get("pulse_size_value", 0.5f); + bool multiply = config.get("pulse_multiply", false); + + if (ImGuiH::Checkbox("Multiply pulsation", &multiply, gui.m_scale)) { + config.set("pulse_multiply", multiply); + } + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::DragFloat("##pulse_size_value", &value, 0.01f, 0.0f, FLT_MAX, "Pulse Size: %.2f")) + config.set("pulse_size_value", value); + }); + + SetCustomWindowHandlerByConfig("startpos_switcher", [this, &config]() { + auto &gui = Gui::get(); + + bool reset_camera = config.get("startos_switcher::reset_camera", true); + + if (ImGuiH::Checkbox("Reset Camera", &reset_camera, gui.m_scale)) { + config.set("startos_switcher::reset_camera", reset_camera); + } + }); + for (auto& win : m_windows) { win.orig_x = win.x; win.orig_y = win.y; @@ -109,15 +211,8 @@ void Hacks::Init() { win.orig_h = win.h; for (auto& hck : win.hacks) { - if (!hck.game_var.empty()) { - config.set(hck.config, GameManager::get()->getGameVariable(hck.game_var.c_str())); - } + if (!hck.game_var.empty()) config.set(hck.config, GameManager::get()->getGameVariable(hck.game_var.c_str())); + if (config.get(hck.config, false) && hck.handlerFunc) hck.handlerFunc(true); } } - - SetHandlerByConfig("hide_pause_menu", [this](bool enabled) { - auto pl = PlayLayer::get(); - if (pl && pl->m_isPaused && pauseLayer != nullptr) - pauseLayer->setVisible(!enabled); - }); } \ No newline at end of file diff --git a/src/hacks.hpp b/src/hacks.hpp index ceeb5f9..ee8b74c 100644 --- a/src/hacks.hpp +++ b/src/hacks.hpp @@ -8,9 +8,12 @@ struct hack { std::string config; std::string game_var; - std::function handlerFunc = [](bool) {}; + std::function handlerFunc = nullptr; void setHandler(std::function func) { handlerFunc = func; } + std::function handlerCustomWindow = nullptr; + void setCustomWindowHandler(std::function func) { handlerCustomWindow = func; } + int keybind = 0; }; @@ -54,4 +57,18 @@ class Hacks { } return false; } + + bool SetCustomWindowHandlerByConfig(const std::string& configName, std::function handler) { + for (auto& window : m_windows) { + auto it = std::find_if(window.hacks.begin(), window.hacks.end(), [&](const hack& h) { + return h.config == configName; + }); + + if (it != window.hacks.end()) { + it->setCustomWindowHandler(handler); + return true; + } + } + return false; + } }; \ No newline at end of file diff --git a/src/hooks.cpp b/src/hooks.cpp index a17f3c0..10f8962 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -1,31 +1,89 @@ +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "hacks.hpp" #include "config.hpp" +std::vector startPositions; +int selectedStartpos = -1; + +void switchStartPos(int incBy, bool direction = true) { + auto &config = Config::get(); + auto pl = PlayLayer::get(); + + if (!pl || startPositions.empty()) return; + + selectedStartpos += incBy; + + if (selectedStartpos < -1) + selectedStartpos = startPositions.size() - 1; + + if (selectedStartpos >= startPositions.size()) + selectedStartpos = -1; + + if (direction) { + StartPosObject* obj = selectedStartpos == -1 ? nullptr : startPositions[selectedStartpos]; + + pl->m_currentCheckpoint = nullptr; + pl->setStartPosObject(obj); + pl->resetLevel(); + + if (config.get("startos_switcher::reset_camera", true)) + pl->resetCamera(); + + pl->startMusic(); + + return; + } +} + +class $modify(cocos2d::CCScheduler) { + void update(float dt) { + auto &config = Config::get(); + + if (config.get("speedhack_enabled", false)) + dt *= config.get("speedhack_value", 1.f); + + return CCScheduler::update(dt); + } +}; + class $modify(PlayLayer) { struct Fields { GameObject* anticheat_obj = nullptr; std::vector coinsObjects; + cocos2d::CCMenu* startposSwitcherUI; + + ~Fields() { + startPositions.clear(); + selectedStartpos = -1; + } }; bool init(GJGameLevel* level, bool useReplay, bool dontCreateObjects) { @@ -35,9 +93,59 @@ class $modify(PlayLayer) { if (config.get("auto_practice_mode", false)) togglePracticeMode(true); + if (config.get("startpos_switcher", false) && !startPositions.empty()) { + auto win_size = cocos2d::CCDirector::sharedDirector()->getWinSize(); + + auto label = cocos2d::CCLabelBMFont::create(fmt::format("{}/{}", selectedStartpos+1, startPositions.size()).c_str(), "bigFont.fnt"); + label->setScale(0.5f); + label->setPosition(win_size.width/2, 20.f); + label->setOpacity(100); + label->setID("startposSwitcherLabels"_spr); + + auto left_arrow = cocos2d::CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"); + left_arrow->setScale(0.5f); + auto left_arrowClick = geode::cocos::CCMenuItemExt::createSpriteExtra(left_arrow, [this, label](CCMenuItemSpriteExtra* sender) { + switchStartPos(-1); + label->setCString(fmt::format("{}/{}", selectedStartpos+1, startPositions.size()).c_str()); + }); + left_arrowClick->setPosition(win_size.width/2 - 50, cocos2d::CCDirector::sharedDirector()->getScreenBottom() + left_arrowClick->getScaledContentHeight()); + left_arrowClick->setOpacity(100); + left_arrowClick->setID("startposSwitcherLeftArrowClick"_spr); + + auto right_arrow = cocos2d::CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"); + right_arrow->setScale(0.5f); + right_arrow->setFlipX(true); + auto right_arrowClick = geode::cocos::CCMenuItemExt::createSpriteExtra(right_arrow, [this, label](CCMenuItemSpriteExtra* sender) { + switchStartPos(1); + label->setCString(fmt::format("{}/{}", selectedStartpos+1, startPositions.size()).c_str()); + }); + right_arrowClick->setPosition(win_size.width/2 + 50, cocos2d::CCDirector::sharedDirector()->getScreenBottom() + right_arrowClick->getScaledContentHeight()); + right_arrowClick->setOpacity(100); + right_arrowClick->setID("startpos_switcher_rightArrowClick"_spr); + + + m_fields->startposSwitcherUI = cocos2d::CCMenu::create(); + m_fields->startposSwitcherUI->setID("startposSwitcherUI"_spr); + m_fields->startposSwitcherUI->setPosition(0, 0); + m_fields->startposSwitcherUI->setZOrder(999); + + m_fields->startposSwitcherUI->addChild(left_arrowClick); + m_fields->startposSwitcherUI->addChild(right_arrowClick); + m_fields->startposSwitcherUI->addChild(label); + + m_uiLayer->addChild(m_fields->startposSwitcherUI); + } + return true; } + void togglePracticeMode(bool practiceMode) { + if (m_fields->startposSwitcherUI) { + m_fields->startposSwitcherUI->setVisible(!practiceMode); + } + PlayLayer::togglePracticeMode(practiceMode); + } + void addObject(GameObject* obj) { auto& config = Config::get(); @@ -60,6 +168,9 @@ class $modify(PlayLayer) { if (obj->m_objectID == 1329 || obj->m_objectID == 142) { m_fields->coinsObjects.push_back(obj); } + else if (obj->m_objectID == 31) { + startPositions.push_back(static_cast(obj)); + } } void destroyPlayer(PlayerObject* player, GameObject* obj) { @@ -80,6 +191,19 @@ class $modify(PlayLayer) { PlayLayer::destroyPlayer(player, obj); m_isTestMode = testmode; } + + if (config.get("respawn_time", false)) { + if (auto* respawnSequence = this->getActionByTag(0x10)) { + this->stopAction(respawnSequence); + auto* newSequence = cocos2d::CCSequence::create( + cocos2d::CCDelayTime::create(config.get("respawn_time_value", 1.f)), + cocos2d::CCCallFunc::create(this, callfunc_selector(PlayLayer::delayedResetLevel)), + nullptr + ); + newSequence->setTag(0x10); + this->runAction(newSequence); + } + } } @@ -88,9 +212,11 @@ class $modify(PlayLayer) { PlayLayer::resetLevel(); - if (config.get("no_do_not_flip", false) && m_attemptLabel) { + if (config.get("no_do_not_flip", false) && m_attemptLabel) m_attemptLabel->setScaleY(1); - } + + if (config.get("show_total_attempts", false) && m_attemptLabel) + m_attemptLabel->setCString(fmt::format("Attempt {}", static_cast(m_level->m_attempts)).c_str()); if (config.get("safe_mode", false)) m_level->m_attempts = m_level->m_attempts - 1; @@ -102,6 +228,14 @@ class $modify(PlayLayer) { pickupItem(static_cast(coin)); } } + + if (config.get("instant_complete", false)) { + //bypass anticheat + m_timePlayed = 10.0; + m_bestAttemptTime = 10.0; + + PlayLayer::playEndAnimationToPos({0, 0}); + } } void levelComplete() { @@ -120,16 +254,40 @@ class $modify(PlayLayer) { if (Config::get().get("no_new_best_popup", false)) return; PlayLayer::showNewBest(p0, p1, p2, p3, p4, p5); } + + void updateVisibility(float dt) { + auto& config = Config::get(); + + if (!config.get("pulse_size", false) && config.get("no_pulse", false)) + m_audioEffectsLayer->m_notAudioScale = 0.5; + + if (config.get("pulse_size", false)) { + float value = config.get("pulse_size_value", 0.5f); + if (config.get("pulse_multiply", false)) + m_audioEffectsLayer->m_notAudioScale *= value; + else + m_audioEffectsLayer->m_notAudioScale = value; + } + + PlayLayer::updateVisibility(dt); + } }; class $modify(GJBaseGameLayer) { + static void onModify(auto& self) { + (void) self.setHookPriority("GJBaseGameLayer::update", 0x99999); + } + void update(float dt) { auto& config = Config::get(); - GJBaseGameLayer::update(dt); + if (config.get("stop_triggers_on_death", false) && m_player1->m_isDead || m_player2->m_isDead) + return; if (config.get("jump_hack", false)) m_player1->m_isOnGround = true; + + GJBaseGameLayer::update(dt); } bool canBeActivatedByPlayer(PlayerObject *p0, EffectGameObject *p1) { @@ -145,6 +303,34 @@ class $modify(GJBaseGameLayer) { GJBaseGameLayer::updateZoom(p0, p1, p2, p3, p4, p5); } + + + void teleportPlayer(TeleportPortalObject *p0, PlayerObject *p1) { + auto& config = Config::get(); + bool hasNoEffects = p0->m_hasNoEffects; + + if (config.get("no_bg_flash", false)) + p0->m_hasNoEffects = true; + + GJBaseGameLayer::teleportPlayer(p0, p1); + p0->m_hasNoEffects = hasNoEffects; + } + + void lightningFlash(cocos2d::CCPoint from, cocos2d::CCPoint to, cocos2d::ccColor3B color, float lineWidth, float duration, int displacement, bool flash, float opacity) { + auto& config = Config::get(); + auto gm = GameManager::get(); + + auto performanceMode = gm->m_performanceMode; + + if (config.get("no_portal_lighting", false)) + gm->m_performanceMode = true; + + if (config.get("no_bg_flash", false)) + flash = false; + + GJBaseGameLayer::lightningFlash(from, to, color, lineWidth, duration, displacement, flash, opacity); + gm->m_performanceMode = performanceMode; + } }; class $modify(CameraTriggerGameObject) { @@ -156,6 +342,7 @@ class $modify(CameraTriggerGameObject) { } }; + class $modify(GameStatsManager) { bool isItemUnlocked(UnlockType p0, int p1) { if (GameStatsManager::isItemUnlocked(p0, p1)) @@ -166,9 +353,8 @@ class $modify(GameStatsManager) { } }; - class $modify(PlayerObject) { - virtual void update(float dt) + void update(float dt) { PlayerObject::update(dt); @@ -194,11 +380,15 @@ class $modify(PlayerObject) { PlayerObject::incrementJumps(); } - void playSpiderDashEffect(cocos2d::CCPoint from, cocos2d::CCPoint to) - { + void playSpiderDashEffect(cocos2d::CCPoint from, cocos2d::CCPoint to) { if (!Config::get().get("no_spider_dash", false)) PlayerObject::playSpiderDashEffect(from, to); } + + void fadeOutStreak2(float p0) { + if (!Config::get().get("wave_trail_on_death", false)) + PlayerObject::fadeOutStreak2(p0); + } }; class $modify(CCTextInputNode){ @@ -274,7 +464,7 @@ class $modify(GJScaleControl) { if (m_sliderXY && m_sliderXY->m_touchLogic->m_activateThumb) { m_sliderXY->getThumb()->setPositionX(convertToNodeSpace(touch->getLocation()).x); m_sliderXY->updateBar(); - float value = m_sliderXY->getThumb()->getValue(); + float value = scaleFromValue(m_sliderXY->getThumb()->getValue()); updateLabelXY(value); sliderChanged(m_sliderXY->getThumb()); if (EditorUI::get()) @@ -514,4 +704,101 @@ class $modify(GameToolbox) { gd::string str = fmt::format("{}", value); return str; } +}; + +class $modify(HardStreak) { + void updateStroke(float p0) { + auto& config = Config::get(); + + if (config.get("wave_trail_size", false)) + m_pulseSize = config.get("wave_trail_size_value", 1.f); + + HardStreak::updateStroke(p0); + } +}; + +class $modify(cocos2d::CCMotionStreak) { + void update(float delta) { + auto& config = Config::get(); + if (config.get("always_trail", false)) + m_bStroke = true; + + if (config.get("no_trail", false)) + m_bStroke = false; + + CCMotionStreak::update(delta); + } +}; + + +class $modify(SongSelectNode) { + void audioNext(cocos2d::CCObject* p0) { + if (Config::get().get("default_song_bypass", false)) { + m_selectedSongID++; + getLevelSettings()->m_level->m_audioTrack = m_selectedSongID; + + return SongSelectNode::updateAudioLabel(); + } + + SongSelectNode::audioNext(p0); + } + + void audioPrevious(cocos2d::CCObject* p0) { + if (Config::get().get("default_song_bypass", false)) { + m_selectedSongID--; + getLevelSettings()->m_level->m_audioTrack = m_selectedSongID; + + return SongSelectNode::updateAudioLabel(); + } + + SongSelectNode::audioPrevious(p0); + } +}; + +class $modify(MoreSearchLayer) { + void audioPrevious(cocos2d::CCObject* sender) { + if (!Config::get().get("default_song_bypass", false)) + return audioPrevious(sender); + + int song = std::max(1, GameLevelManager::get()->getIntForKey("song_filter")); + MoreSearchLayer::selectSong(--song); + } + + void audioNext(cocos2d::CCObject* sender) { + if (!Config::get().get("default_song_bypass", false)) + return audioNext(sender); + + int song = std::max(1, GameLevelManager::get()->getIntForKey("song_filter")); + MoreSearchLayer::selectSong(++song); + } + + void selectSong(int songID) { + if (!Config::get().get("default_song_bypass", false)) + return selectSong(songID); + + GameLevelManager::get()->setIntForKey(songID, "song_filter"); + updateAudioLabel(); + } +}; + +class $modify(EditLevelLayer) { + bool init(GJGameLevel *gjgl) { + if (Config::get().get("verify_hack", false)) + gjgl->m_isVerified = true; + return EditLevelLayer::init(gjgl); + } + + void onShare(CCObject* sender) { + if (Config::get().get("no_c_mark", false)) m_level->m_originalLevel = 0; + + EditLevelLayer::onShare(sender); + } +}; + +class $modify(LevelEditorLayer) { + void postUpdate(float dt) { + if (Config::get().get("smooth_editor_trail", false)) + m_trailTimer = 0.1f; + LevelEditorLayer::postUpdate(dt); + } }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b4fd27a..5fb7764 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,7 @@ class $modify(MenuLayer) { } }; +#ifdef GEODE_IS_WINDOWS class $modify(cocos2d::CCEGLView) { void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { CCEGLView::onGLFWKeyCallback(window, key, scancode, action, mods); @@ -49,4 +50,5 @@ class $modify(cocos2d::CCEGLView) { } } } -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..bb35aec --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,46 @@ +#include "memory.hpp" + +struct PatternByte { + bool isWildcard; + uint8_t value; +}; + +uintptr_t memory::PatternScan(uintptr_t base, uintptr_t scanSize, const std::string signature) { + std::vector patternData; + + for (size_t i = 0; i < signature.size(); ++i) { + if (signature[i] == ' ') { + continue; + } + + if (signature[i] == '?') { + patternData.push_back({ true, 0 }); + } + else { + std::string byteStr = signature.substr(i, 2); + patternData.push_back({ false, static_cast(std::stoul(byteStr, nullptr, 16)) }); + i++; + } + } + + for (uintptr_t i = base; /*i < base + scanSize*/; ++i) { + bool found = true; + + for (size_t j = 0; j < patternData.size(); ++j) { + if (patternData[j].isWildcard) { + continue; + } + + if (patternData[j].value != *reinterpret_cast(i + j)) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return 0; +} \ No newline at end of file diff --git a/src/memory.hpp b/src/memory.hpp new file mode 100644 index 0000000..d87f17e --- /dev/null +++ b/src/memory.hpp @@ -0,0 +1,9 @@ +#pragma once +#include +#include +#include +#include + +namespace memory { + uintptr_t PatternScan(uintptr_t base, uintptr_t scanSize, const std::string signature); +} \ No newline at end of file