diff --git a/.github/workflows/multi-platform.yml b/.github/workflows/multi-platform.yml
new file mode 100644
index 0000000..1cbfbca
--- /dev/null
+++ b/.github/workflows/multi-platform.yml
@@ -0,0 +1,55 @@
+name: Build Geode Mod
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - "**"
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - name: Windows
+ os: windows-latest
+
+ - name: macOS
+ os: macos-latest
+
+ - name: Android32
+ os: ubuntu-latest
+ target: Android32
+
+ - name: Android64
+ os: ubuntu-latest
+ target: Android64
+
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.os }}
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build the mod
+ uses: geode-sdk/build-geode-mod@main
+ with:
+ bindings: geode-sdk/bindings
+ bindings-ref: main
+ 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/.gitignore b/.gitignore
new file mode 100644
index 0000000..2604c20
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,64 @@
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Macos be like
+**/.DS_Store
+
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# Ignore build folders
+**/build
+# Ignore platform specific build folders
+build-*/
+
+# Workspace files are user-specific
+*.sublime-workspace
+
+# ILY vscode
+**/.vscode
+
+# Local History for Visual Studio Code
+.history/
+
+# clangd
+.cache/
+
+# Visual Studio
+.vs/
+
+# CLion
+.idea/
+/cmake-build-*/
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..fb92d2f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.21)
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+
+project(SettingsPlus VERSION 1.0.0)
+
+add_library(${PROJECT_NAME} SHARED
+ src/main.cpp
+ src/SettingsLayer.cpp
+ # Add any extra C++ source files here
+)
+
+if (NOT DEFINED ENV{GEODE_SDK})
+ message(FATAL_ERROR "Unable to find Geode SDK! Please define GEODE_SDK environment variable to point to Geode")
+else()
+ message(STATUS "Found Geode: $ENV{GEODE_SDK}")
+endif()
+
+add_subdirectory($ENV{GEODE_SDK} ${CMAKE_CURRENT_BINARY_DIR}/geode)
+
+CPMAddPackage("gh:camila314/uibuilder#c662f54")
+target_link_libraries(${PROJECT_NAME} UIBuilder)
+
+setup_geode_mod(${PROJECT_NAME})
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..df8fb37
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# Settings+
+
+An enhanced settings experience
+
+
+
+## Build instructions
+For more info, see [The Geode docs](https://docs.geode-sdk.org/getting-started/create-mod#build)
+```sh
+# Assuming you have the Geode CLI set up already
+geode build
+```
+
+# Credits
+* [WylieMaster's GD Docs page on Game Variables](https://wyliemaster.github.io/gddocs/#/resources/client/gamesave/gv)
\ No newline at end of file
diff --git a/about.md b/about.md
new file mode 100644
index 0000000..1955b17
--- /dev/null
+++ b/about.md
@@ -0,0 +1,12 @@
+# Settings+
+
+An enhanced options modal for Geometry Dash.
+
+### Features
+- Easily selectable categories
+- Searching
+- Options that aren't available in the base GD settings
+
+HUGE thank you and shoutout to the [GD Docs](https://wyliemaster.github.io/gddocs/#), specifically the page about [game variables](https://wyliemaster.github.io/gddocs/#/resources/client/gamesave/gv). This mod would've been absolute pain to program without these resources.
+
+And a quick thank you to hiimjustin000, for help in reverse engineering.
diff --git a/about.md.old b/about.md.old
new file mode 100644
index 0000000..370ff0f
--- /dev/null
+++ b/about.md.old
@@ -0,0 +1,74 @@
+# Template Mod
+
+Edit about.md to change this
+
+- Gameplay
+ - gv_0010 Flip 2P controls
+ - gv_0011 Always Limit Controls
+ - gv_0024 Show Cursor In-Game
+ - gv_0026 Auto Retry
+ - gv_0028 Disable Thumbstick
+ - gv_0040 Show Percent
+ - gv_0026 Default Mini Icon
+ - gv_0052 Fast Reset
+ - gv_0074 Restart Button
+ - gv_0109 Extra Info (?)
+ - gv_0113 Flip Plat. Controls
+ - gv_0126 Decimal Percent
+ - gv_0130 Orb Labels
+ - gv_0134 Hide Attempts
+ - gv_0153 Explode Player on Death
+ - gv_0163 Quick Keys
+ - gv_0167 Confirm Exit
+ - gv_0174 Hide Playtest Text
+- Practice
+ - gv_0027 Auto Checkpoints
+ - gv_0067 High Start Position Accuracy
+ - gv_0068 Quick Checkpoint Mode
+ - gv_0071 Hide Practice Button
+ - gv_0100 Practice Death Effect
+ - gv_0135 Hide Attempts in Practice
+- Perf
+ - gv_0014 Disable Explosion Shake
+ - gv_0023 Smooth Fix
+ - gv_0042 Increase Max Levels
+ - gv_0056 Disable Object Alert
+ - gv_0065 Move Optimization
+ - gv_0066 High Capacity Mode
+ - gv_0081 Disable Shake Effect
+ - gv_0082 Disable High Object Alert
+ - gv_0093 Increase Local Levels Per Page
+ - gv_0101 Force Smooth Fix
+ - gv_0102 Smooth Fix in the Editor
+ - gv_0108 Auto LDM
+ - gv_0126 Save Gauntlet Levels
+ - gv_0128 Lock Cursor In-Game
+ - gv_0136 Extra LDM
+ - gv_0140 Disable Orb Scale
+ - gv_0141 Disable Trigger Orb Scale
+ - gv_0155 Disable Shader Anti-Aliasing
+- Audio
+ - gv_0010 Load Songs into Memory
+ - gv_0022 Higher Audio Quality
+ - gv_0033 Change Song Path
+ - gv_0018 No Song Limit
+ - gv_0125 Normal Music in Editor
+ - gv_0142 Reduce Audio Quality
+ - gv_0159 Audio Fix 01
+- Misc
+ - gv_0095 Do Not...
+ - gv_0015 Flip Pause Button
+ - gv_0061 Switch Spider Teleport Color
+ - gv_0062 Switch Dash Fire Color
+ - gv_0096 Switch Wave Trail Color
+ - gv_0072 Disable Gravity Effect
+ - gv_0073 New Completed Filter
+ - gv_0075 Disable Comments
+ - gv_0076 Disable Account Comments
+ - gv_0077 Featured Levels Only
+ - gv_0083 Disable Song Alert
+ - gv_0084 Manual Level Order
+ - gv_0090 Autoload Comments
+ - gv_0094 More Comments Mode
+ - gv_0099 Show Leaderboard Percent
+ - gv_0168 Fast Menu
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..2f16a49
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,2 @@
+# 1.0.0
+- Initial Release
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..b3ca451
Binary files /dev/null and b/logo.png differ
diff --git a/mod.json b/mod.json
new file mode 100644
index 0000000..fb3515a
--- /dev/null
+++ b/mod.json
@@ -0,0 +1,24 @@
+{
+ "geode": "3.2.0",
+ "gd": {
+ "win": "2.206",
+ "android": "2.206",
+ "mac": "2.206",
+ "ios": "2.206"
+ },
+ "id": "techstudent10.settings_plus",
+ "name": "Settings+",
+ "version": "v1.0.0",
+ "developer": "TechStudent10",
+ "description": "A better Settings experience",
+ "repository": "https://github.com/TechStudent10/SettingsPlus",
+ "issues": {
+ "info": "Please report issues and bugs to the Issues page on GitHub",
+ "url": "https://github.com/TechStudent10/SettingsPlus/issues"
+ },
+ "tags": [
+ "enhancement",
+ "interface",
+ "utility"
+ ]
+}
diff --git a/script.py b/script.py
new file mode 100644
index 0000000..48fcc95
--- /dev/null
+++ b/script.py
@@ -0,0 +1,14 @@
+# Converts the stuff I put in about.md into the macro for the mod
+
+text_input = """- gv_0027 Auto Checkpoints
+- gv_0067 High Start Position Accuracy
+- gv_0068 Quick Checkpoint Mode
+- gv_0071 Hide Practice Button
+- gv_0100 Practice Death Effect
+- gv_0135 Hide Attempts in Practice"""
+
+for line in text_input.split("\n"):
+ line = line[2:] # Remove the -
+ gv = line.split(" ")[0].replace("gv_", "") # Retrive Game Variable
+ name = line.replace("gv_" + gv + " ", "") # Retrieve Name
+ print(f"SETTING(\"{name}\", \"{gv}\")")
diff --git a/script2.py b/script2.py
new file mode 100644
index 0000000..b278308
--- /dev/null
+++ b/script2.py
@@ -0,0 +1,156 @@
+str_input = """21:33:26 INFO [Main] [Settings+]: layer_0:
+21:33:26 INFO [Main] [Settings+]: page_0:7
+21:33:26 INFO [Main] [Settings+]: object_0:
+21:33:26 INFO [Main] [Settings+]: info_26:Restarts level upon death automatically.
+21:33:26 INFO [Main] [Settings+]: info_52:Restarts in 0.5 s instead of 1.0 s upon death.
+21:33:26 INFO [Main] [Settings+]: info_128:Locks and hides cursor during gameplay.
+21:33:26 INFO [Main] [Settings+]: info_10:Flips which side controls which player during 2-player mode.
+21:33:26 INFO [Main] [Settings+]: info_11:Limits player 1 controls to one side even when dual mode is inactive.
+21:33:26 INFO [Main] [Settings+]: info_28:Disables mouse movement when using a controller thumbstick.
+21:33:26 INFO [Main] [Settings+]: info_163:Enables some quick temporary bindings until full customization later. Use 'R' for reset, 'CTRL + R' for full reset, and 'P' to toggle hitboxes in Practice mode.
+21:33:26 INFO [Main] [Settings+]: layer_1:
+21:33:26 INFO [Main] [Settings+]: page_1:10
+21:33:26 INFO [Main] [Settings+]: object_1:
+21:33:26 INFO [Main] [Settings+]: info_24:Shows cursor and pause button during gameplay.
+21:33:26 INFO [Main] [Settings+]: info_135:Hides the attempt counter when playing levels.
+21:33:26 INFO [Main] [Settings+]: info_15:Flips the location of the pause button.
+21:33:26 INFO [Main] [Settings+]: info_129:Disables extra indicators on portals.
+21:33:26 INFO [Main] [Settings+]: info_130:Enables extra indicators on orbs.
+21:33:26 INFO [Main] [Settings+]: info_140:Disables the scaling effect on all orbs.
+21:33:26 INFO [Main] [Settings+]: info_141:Disables the scaling effect on only trigger orbs.
+21:33:26 INFO [Main] [Settings+]: info_172:Disables shake effects.
+21:33:26 INFO [Main] [Settings+]: info_14:Disables the shake effect that happens upon death.
+21:33:26 INFO [Main] [Settings+]: info_72:Disables the effect that happens upon changing gravity.
+21:33:26 INFO [Main] [Settings+]: layer_2:
+21:33:26 INFO [Main] [Settings+]: page_2:5
+21:33:26 INFO [Main] [Settings+]: object_2:
+21:33:26 INFO [Main] [Settings+]: info_60:Sets player icon in mini mode to default.
+21:33:26 INFO [Main] [Settings+]: info_61:Toggles between main and secondary color for the teleport effect in spider mode.
+21:33:26 INFO [Main] [Settings+]: info_62:Toggles between main and secondary color for the fire effect from dash orbs.
+21:33:26 INFO [Main] [Settings+]: info_96:Toggles between main and secondary color for the trail in wave mode.
+21:33:26 INFO [Main] [Settings+]: info_174:Hides text in the top left when using start positions or ignore damage.
+21:33:26 INFO [Main] [Settings+]: layer_3:
+21:33:26 INFO [Main] [Settings+]: page_3:8
+21:33:26 INFO [Main] [Settings+]: object_3:
+21:33:26 INFO [Main] [Settings+]: info_71:Hides the checkpoint buttons shown in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_134:Hides the attempt counter when playing levels in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_27:Places checkpoints automatically in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_68:Tries to place checkpoints more often in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_100:Shows death effects in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_125:Plays normal music in sync to editor levels in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_166:Shows hitboxes while in practice mode.
+21:33:26 INFO [Main] [Settings+]: info_171:Disables the player's hitbox in practice mode (if hitboxes are shown).
+21:33:26 INFO [Main] [Settings+]: layer_4:
+21:33:26 INFO [Main] [Settings+]: page_4:8
+21:33:26 INFO [Main] [Settings+]: object_4:
+21:33:26 INFO [Main] [Settings+]: info_66:Increases draw capacity for batch nodes at level start. Can improve performance on some levels, but may cause issues on low-end devices.
+21:33:26 INFO [Main] [Settings+]: info_108:Enables low detail mode on levels that support it automatically.
+21:33:26 INFO [Main] [Settings+]: info_82:Removes the alert shown when starting levels with a high object count.
+21:33:26 INFO [Main] [Settings+]: info_136:Removes glow and enter effects while in low detail mode. Levels without LDM show LDM Lite.
+21:33:26 INFO [Main] [Settings+]: info_42:Increases maximum locally saved levels from 10 to 100. This refers to level data, not statistics. Enabling this can make your save file considerably larger, so keeping the option off is recommended for quicker saving.
+21:33:26 INFO [Main] [Settings+]: info_119:Saves level statistics as usual, but levels need to be redownloaded every time you restart the game. Makes saving and loading faster.
+21:33:26 INFO [Main] [Settings+]: info_127:Saves gauntlet levels locally so they do not have to be redownloaded. Increases save time but helpful if you have poor connection.
+21:33:26 INFO [Main] [Settings+]: info_155:Disables anti-aliasing on shader effects.
+21:33:26 INFO [Main] [Settings+]: layer_5:
+21:33:26 INFO [Main] [Settings+]: page_5:5
+21:33:26 INFO [Main] [Settings+]: object_5:
+21:33:26 INFO [Main] [Settings+]: info_33:Saves custom songs in a different directory. May fix custom songs not working.
+21:33:26 INFO [Main] [Settings+]: info_83:Removes the alert shown when starting levels without the song downloaded.
+21:33:26 INFO [Main] [Settings+]: info_18:Stops automatic deletion of custom songs. This is done by default to save space.
+21:33:26 INFO [Main] [Settings+]: info_142:Lowers audio sampling rate from 44100 Hz to 24000 Hz. Requires restarting to take effect.
+21:33:26 INFO [Main] [Settings+]: info_159:Increases the audio buffer size, which may fix certain issues. Do not enable if audio is working fine. Causes a slight more audio delay. Requires restarting to take effect.
+21:33:26 INFO [Main] [Settings+]: layer_6:
+21:33:26 INFO [Main] [Settings+]: page_6:10
+21:33:26 INFO [Main] [Settings+]: object_6:
+21:33:26 INFO [Main] [Settings+]: info_94:Shows more comments per page. Why not?
+21:33:26 INFO [Main] [Settings+]: info_90:Loads comments automatically.
+21:33:26 INFO [Main] [Settings+]: info_73:Makes completed levels filter based only on percentage from update 2.1. Useful to rebeat levels for Mana Orbs.
+21:33:26 INFO [Main] [Settings+]: info_93:Increases created and saved levels per page from 10 to 20.
+21:33:26 INFO [Main] [Settings+]: info_84:Places new levels last in the saved levels list. Useful if you want to manually move levels to the top.
+21:33:26 INFO [Main] [Settings+]: info_126:Shows decimals in level progress.
+21:33:26 INFO [Main] [Settings+]: info_99:Toggles viewing the leaderboard percentage you have on levels. To upload your level progress to the level leaderboard, you need to replay levels completed before 2.11.
+21:33:26 INFO [Main] [Settings+]: info_95:Does not do anything... Well, nothing useful.
+21:33:26 INFO [Main] [Settings+]: info_167:Adds an extra confirmation window when exiting levels.
+21:33:26 INFO [Main] [Settings+]: info_168:Makes transitions between menu pages faster.""".replace("21:33:26 INFO [Main] [Settings+]: ", "")
+
+gvs = []
+
+for line in str_input.split("\n"):
+ if not line.startswith("info_"):
+ continue
+
+ line_split = line.replace("info_", "").split(":")
+ gv = line_split[0].zfill(4)
+ desc = line_split[1]
+ gvs.append(gv)
+ print(f"""if (gv == \"{gv}\") ret = \"{desc}\";""")
+
+str_input2 = """21:47:34 INFO [Main] [Settings+]: 0026
+21:47:34 INFO [Main] [Settings+]: 0010
+21:47:34 INFO [Main] [Settings+]: 0011
+21:47:34 INFO [Main] [Settings+]: 0024
+21:47:34 INFO [Main] [Settings+]: 0028
+21:47:34 INFO [Main] [Settings+]: 0040
+21:47:34 INFO [Main] [Settings+]: 0052
+21:47:34 INFO [Main] [Settings+]: 0074
+21:47:34 INFO [Main] [Settings+]: 0109
+21:47:34 INFO [Main] [Settings+]: 0113
+21:47:34 INFO [Main] [Settings+]: 0126
+21:47:34 INFO [Main] [Settings+]: 0130
+21:47:34 INFO [Main] [Settings+]: 0134
+21:47:34 INFO [Main] [Settings+]: 0163
+21:47:34 INFO [Main] [Settings+]: 0167
+21:47:34 INFO [Main] [Settings+]: 0174
+21:47:34 INFO [Main] [Settings+]: 0015
+21:47:34 INFO [Main] [Settings+]: 0153
+21:47:34 INFO [Main] [Settings+]: 0026
+21:47:34 INFO [Main] [Settings+]: 0061
+21:47:34 INFO [Main] [Settings+]: 0062
+21:47:34 INFO [Main] [Settings+]: 0096
+21:47:34 INFO [Main] [Settings+]: 0010
+21:47:34 INFO [Main] [Settings+]: 0022
+21:47:34 INFO [Main] [Settings+]: 0033
+21:47:34 INFO [Main] [Settings+]: 0018
+21:47:34 INFO [Main] [Settings+]: 0125
+21:47:34 INFO [Main] [Settings+]: 0142
+21:47:34 INFO [Main] [Settings+]: 0159
+21:47:34 INFO [Main] [Settings+]: 0095
+21:47:34 INFO [Main] [Settings+]: 0072
+21:47:34 INFO [Main] [Settings+]: 0073
+21:47:34 INFO [Main] [Settings+]: 0083
+21:47:34 INFO [Main] [Settings+]: 0084
+21:47:34 INFO [Main] [Settings+]: 0099
+21:47:34 INFO [Main] [Settings+]: 0168
+21:47:34 INFO [Main] [Settings+]: 0090
+21:47:34 INFO [Main] [Settings+]: 0094
+21:47:34 INFO [Main] [Settings+]: 0075
+21:47:34 INFO [Main] [Settings+]: 0076
+21:47:34 INFO [Main] [Settings+]: 0077
+21:47:34 INFO [Main] [Settings+]: 0023
+21:47:34 INFO [Main] [Settings+]: 0065
+21:47:34 INFO [Main] [Settings+]: 0101
+21:47:34 INFO [Main] [Settings+]: 0102
+21:47:34 INFO [Main] [Settings+]: 0128
+21:47:34 INFO [Main] [Settings+]: 0108
+21:47:34 INFO [Main] [Settings+]: 0136
+21:47:34 INFO [Main] [Settings+]: 0042
+21:47:34 INFO [Main] [Settings+]: 0056
+21:47:34 INFO [Main] [Settings+]: 0126
+21:47:34 INFO [Main] [Settings+]: 0093
+21:47:34 INFO [Main] [Settings+]: 0066
+21:47:34 INFO [Main] [Settings+]: 0014
+21:47:34 INFO [Main] [Settings+]: 0140
+21:47:34 INFO [Main] [Settings+]: 0081
+21:47:34 INFO [Main] [Settings+]: 0082
+21:47:34 INFO [Main] [Settings+]: 0155
+21:47:34 INFO [Main] [Settings+]: 0141
+21:47:34 INFO [Main] [Settings+]: 0027
+21:47:34 INFO [Main] [Settings+]: 0067
+21:47:34 INFO [Main] [Settings+]: 0068
+21:47:34 INFO [Main] [Settings+]: 0071
+21:47:34 INFO [Main] [Settings+]: 0100
+21:47:34 INFO [Main] [Settings+]: 0135""".replace("21:47:34 INFO [Main] [Settings+]: ", "")
+
+for line in str_input2.split("\n"):
+ if line not in gvs:
+ print(f"if (gv == \"{line}\") ret = \"\";")
diff --git a/script3.py b/script3.py
new file mode 100644
index 0000000..3ebdb7c
--- /dev/null
+++ b/script3.py
@@ -0,0 +1,74 @@
+strInput = """if (gv == "0026") ret = "Restarts level upon death automatically.";
+if (gv == "0052") ret = "Restarts in 0.5 s instead of 1.0 s upon death.";
+if (gv == "0128") ret = "Locks and hides cursor during gameplay.";
+if (gv == "0010") ret = "Flips which side controls which player during 2-player mode.";
+if (gv == "0011") ret = "Limits player 1 controls to one side even when dual mode is inactive.";
+if (gv == "0028") ret = "Disables mouse movement when using a controller thumbstick.";
+if (gv == "0163") ret = "Enables some quick temporary bindings until full customization later. Use 'R' for reset, 'CTRL + R' for full reset, and 'P' to toggle hitboxes in Practice mode.";
+if (gv == "0024") ret = "Shows cursor and pause button during gameplay.";
+if (gv == "0135") ret = "Hides the attempt counter when playing levels.";
+if (gv == "0015") ret = "Flips the location of the pause button.";
+if (gv == "0129") ret = "Disables extra indicators on portals.";
+if (gv == "0130") ret = "Enables extra indicators on orbs.";
+if (gv == "0140") ret = "Disables the scaling effect on all orbs.";
+if (gv == "0141") ret = "Disables the scaling effect on only trigger orbs.";
+if (gv == "0172") ret = "Disables shake effects.";
+if (gv == "0014") ret = "Disables the shake effect that happens upon death.";
+if (gv == "0072") ret = "Disables the effect that happens upon changing gravity.";
+if (gv == "0060") ret = "Sets player icon in mini mode to default.";
+if (gv == "0061") ret = "Toggles between main and secondary color for the teleport effect in spider mode.";
+if (gv == "0062") ret = "Toggles between main and secondary color for the fire effect from dash orbs.";
+if (gv == "0096") ret = "Toggles between main and secondary color for the trail in wave mode.";
+if (gv == "0174") ret = "Hides text in the top left when using start positions or ignore damage.";
+if (gv == "0071") ret = "Hides the checkpoint buttons shown in practice mode.";
+if (gv == "0134") ret = "Hides the attempt counter when playing levels in practice mode.";
+if (gv == "0027") ret = "Places checkpoints automatically in practice mode.";
+if (gv == "0068") ret = "Tries to place checkpoints more often in practice mode.";
+if (gv == "0100") ret = "Shows death effects in practice mode.";
+if (gv == "0125") ret = "Plays normal music in sync to editor levels in practice mode.";
+if (gv == "0166") ret = "Shows hitboxes while in practice mode.";
+if (gv == "0171") ret = "Disables the player's hitbox in practice mode (if hitboxes are shown).";
+if (gv == "0066") ret = "Increases draw capacity for batch nodes at level start. Can improve performance on some levels, but may cause issues on low-end devices.";
+if (gv == "0108") ret = "Enables low detail mode on levels that support it automatically.";
+if (gv == "0082") ret = "Removes the alert shown when starting levels with a high object count.";
+if (gv == "0136") ret = "Removes glow and enter effects while in low detail mode. Levels without LDM show LDM Lite.";
+if (gv == "0042") ret = "Increases maximum locally saved levels from 10 to 100. This refers to level data, not statistics. Enabling this can make your save file considerably larger, so keeping the option off is recommended for quicker saving.";
+if (gv == "0119") ret = "Saves level statistics as usual, but levels need to be redownloaded every time you restart the game. Makes saving and loading faster.";
+if (gv == "0127") ret = "Saves gauntlet levels locally so they do not have to be redownloaded. Increases save time but helpful if you have poor connection.";
+if (gv == "0155") ret = "Disables anti-aliasing on shader effects.";
+if (gv == "0033") ret = "Saves custom songs in a different directory. May fix custom songs not working.";
+if (gv == "0083") ret = "Removes the alert shown when starting levels without the song downloaded.";
+if (gv == "0018") ret = "Stops automatic deletion of custom songs. This is done by default to save space.";
+if (gv == "0142") ret = "Lowers audio sampling rate from 44100 Hz to 24000 Hz. Requires restarting to take effect.";
+if (gv == "0159") ret = "Increases the audio buffer size, which may fix certain issues. Do not enable if audio is working fine. Causes a slight more audio delay. Requires restarting to take effect.";
+if (gv == "0094") ret = "Shows more comments per page. Why not?";
+if (gv == "0090") ret = "Loads comments automatically.";
+if (gv == "0073") ret = "Makes completed levels filter based only on percentage from update 2.1. Useful to rebeat levels for Mana Orbs.";
+if (gv == "0093") ret = "Increases created and saved levels per page from 10 to 20.";
+if (gv == "0084") ret = "Places new levels last in the saved levels list. Useful if you want to manually move levels to the top.";
+if (gv == "0126") ret = "Shows decimals in level progress.";
+if (gv == "0099") ret = "Toggles viewing the leaderboard percentage you have on levels. To upload your level progress to the level leaderboard, you need to replay levels completed before 2.11.";
+if (gv == "0095") ret = "Does not do anything... Well, nothing useful.";
+if (gv == "0167") ret = "Adds an extra confirmation window when exiting levels.";
+if (gv == "0168") ret = "Makes transitions between menu pages faster.";
+if (gv == "0040") ret = "Toggles the percent label in game";
+if (gv == "0074") ret = "Toggles the restart button on the pause menu";
+if (gv == "0109") ret = "Toggles the extra info/debug label in game";
+if (gv == "0113") ret = "Flips the platformer controls";
+if (gv == "0153") ret = "Whether the player should explode on death";
+if (gv == "0022") ret = "Whether the game should use higher audio quality";
+if (gv == "0075") ret = "(Parental Controls) Disables comments (known to be buggy)";
+if (gv == "0076") ret = "(Parental Controls) Disables account posts";
+if (gv == "0077") ret = "(Parental Controls) Removes the featured levels button in the creator menu";
+if (gv == "0023") ret = "Toggles smooth fix";
+if (gv == "0065") ret = "Toggles move optimization";
+if (gv == "0101") ret = "Forces smooth fix to be on";
+if (gv == "0102") ret = "Toggles smooth fix in the editor";
+if (gv == "0056") ret = "Disables the high object alert";
+if (gv == "0081") ret = "Disables the shake effect in levels";
+if (gv == "0067") ret = "Increases the accuracy of start positions";""".replace("if (gv == \"", "").replace("\") ret = \"", "").replace("\";", "")
+
+for line in strInput.split("\n"):
+ gv = line[:4]
+ desc = line[-len(line)+4:]
+ print("{\"" + gv + "\", \"" + desc + "\"},")
diff --git a/src/SettingsLayer.cpp b/src/SettingsLayer.cpp
new file mode 100644
index 0000000..27ce50c
--- /dev/null
+++ b/src/SettingsLayer.cpp
@@ -0,0 +1,516 @@
+#include "./SettingsLayer.hpp"
+
+SearchPopup* SearchPopup::create(SearchCB callback) {
+ auto ret = new SearchPopup();
+ if (ret && ret->initAnchored(210.f, 180.f, callback)) {
+ ret->autorelease();
+ return ret;
+ }
+ delete ret;
+ return nullptr;
+}
+
+bool SearchPopup::setup(SearchCB callback) {
+ m_callback = callback;
+
+ this->setTitle("Search");
+
+ m_input = TextInput::create(150.f, "Query here");
+ m_mainLayer->addChildAtPosition(m_input, Anchor::Center);
+
+ auto btn = CCMenuItemSpriteExtra::create(
+ ButtonSprite::create("Search"), this, menu_selector(SearchPopup::onSearch)
+ );
+ auto menu = CCMenu::create();
+ menu->addChild(btn);
+
+ m_mainLayer->addChildAtPosition(menu, Anchor::Bottom);
+
+ return true;
+}
+
+void SearchPopup::onSearch(CCObject* sender) {
+ m_callback(
+ m_input->getString()
+ );
+ m_closeBtn->activate();
+}
+
+SettingCell* SettingCell::create(std::string name, std::string gv, SettingCellType type) {
+ auto ret = new SettingCell();
+ if (ret && ret->init(name, gv, type)) {
+ ret->autorelease();
+ return ret;
+ }
+ delete ret;
+ return nullptr;
+}
+
+bool SettingCell::init(std::string name, std::string gv, SettingCellType type) {
+ m_name = name;
+ m_gameVariable = gv;
+ m_type = type;
+
+ auto nameLabel = CCLabelBMFont::create(
+ name.c_str(), "bigFont.fnt"
+ );
+ // nameLabel->setScale(0.9f);
+ nameLabel->limitLabelWidth(15.f, 0.9f, 0.5f);
+
+ auto menu = CCMenu::create();
+
+ // needed so that the switch statement isn't fussy about variables
+ CCSprite* spr;
+ CCMenuItemSpriteExtra* btn;
+ TextInput* input;
+ CCLabelBMFont* text;
+
+ auto fmodEngine = FMODAudioEngine::sharedEngine();
+
+ switch (type) {
+ case Default:
+ m_toggler = CCMenuItemToggler::createWithStandardSprites(
+ this,
+ menu_selector(SettingCell::onCheckboxToggled),
+ 1.f
+ );
+ m_toggler->toggle(
+ GameManager::get()->getGameVariable(gv.c_str())
+ );
+
+ spr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png");
+ spr->setScale(0.75f);
+ btn = CCMenuItemSpriteExtra::create(
+ spr, this, menu_selector(SettingCell::onInfo)
+ );
+ btn->setPositionX(-35.f);
+
+ menu->addChild(btn);
+ menu->addChild(m_toggler);
+ break;
+ case FMODDebug:
+ spr = ButtonSprite::create("Debug", "goldFont.fnt", "GJ_button_05.png");
+ spr->setScale(0.7f);
+ btn = CCMenuItemSpriteExtra::create(
+ spr, this, menu_selector(SettingCell::onFMODDebug)
+ );
+ btn->setPositionX(-20.f);
+ menu->addChild(btn);
+ break;
+ case SongSelect:
+ spr = CCSprite::createWithSpriteFrameName("GJ_savedSongsBtn_001.png");
+ spr->setScale(0.7f);
+ menu->addChild(CCMenuItemSpriteExtra::create(
+ spr, this, menu_selector(SettingCell::onSongSelect)
+ ));
+ break;
+ case SongOffset:
+ input = TextInput::create(100.f, "Offset");
+ input->getInputNode()->setAllowedChars("0123456789");
+ if (fmodEngine->m_musicOffset != 0) {
+ input->setString(std::to_string(fmodEngine->m_musicOffset));
+ }
+ input->setCallback([this, fmodEngine](std::string offset) {
+ fmodEngine->m_musicOffset = std::stoi(offset);
+ });
+ input->setPositionX(-35.f);
+ menu->addChild(input);
+ break;
+ case Separator:
+ nameLabel->setOpacity(0.f);
+ text = CCLabelBMFont::create(
+ name.c_str(),
+ "goldFont.fnt"
+ );
+ text->limitLabelWidth(300.f, 1.f, 0.1);
+ this->addChildAtPosition(text, Anchor::Center);
+ break;
+ }
+
+ this->addChildAtPosition(nameLabel, Anchor::Left, ccp(10.f, 0.f));
+ this->addChildAtPosition(menu, Anchor::Right, ccp(-25.f, 0.f));
+
+ nameLabel->setAnchorPoint({ 0.f, 0.5f });
+
+ this->setContentSize({
+ 365.f,
+ 50.f
+ });
+
+ return true;
+}
+
+void SettingCell::onFMODDebug(CCObject* sender) {
+ auto mol = MoreOptionsLayer::create();
+ mol->onFMODDebug(sender);
+}
+
+void SettingCell::onSongSelect(CCObject* sender) {
+ auto mol = MoreOptionsLayer::create();
+ mol->onSongBrowser(sender);
+}
+
+void SettingCell::onCheckboxToggled(CCObject* sender) {
+ GameManager::get()->setGameVariable(m_gameVariable.c_str(), !m_toggler->isOn());
+ log::debug("set gv_{} to {}", m_gameVariable, !m_toggler->isOn());
+}
+
+std::string descForGV(std::string gv) {
+ std::map descriptions = {
+ {"0026", "Restarts level upon death automatically."},
+ {"0052", "Restarts in 0.5 s instead of 1.0 s upon death."},
+ {"0128", "Locks and hides cursor during gameplay."},
+ {"0010", "Flips which side controls which player during 2-player mode."},
+ {"0011", "Limits player 1 controls to one side even when dual mode is inactive."},
+ {"0028", "Disables mouse movement when using a controller thumbstick."},
+ {"0163", "Enables some quick temporary bindings until full customization later. Use 'R' for reset, 'CTRL + R' for full reset, and 'P' to toggle hitboxes in Practice mode."},
+ {"0024", "Shows cursor and pause button during gameplay."},
+ {"0135", "Hides the attempt counter when playing levels."},
+ {"0015", "Flips the location of the pause button."},
+ {"0129", "Disables extra indicators on portals."},
+ {"0130", "Enables extra indicators on orbs."},
+ {"0140", "Disables the scaling effect on all orbs."},
+ {"0141", "Disables the scaling effect on only trigger orbs."},
+ {"0172", "Disables shake effects."},
+ {"0014", "Disables the shake effect that happens upon death."},
+ {"0072", "Disables the effect that happens upon changing gravity."},
+ {"0060", "Sets player icon in mini mode to default."},
+ {"0061", "Toggles between main and secondary color for the teleport effect in spider mode."},
+ {"0062", "Toggles between main and secondary color for the fire effect from dash orbs."},
+ {"0096", "Toggles between main and secondary color for the trail in wave mode."},
+ {"0174", "Hides text in the top left when using start positions or ignore damage."},
+ {"0071", "Hides the checkpoint buttons shown in practice mode."},
+ {"0134", "Hides the attempt counter when playing levels in practice mode."},
+ {"0027", "Places checkpoints automatically in practice mode."},
+ {"0068", "Tries to place checkpoints more often in practice mode."},
+ {"0100", "Shows death effects in practice mode."},
+ {"0125", "Plays normal music in sync to editor levels in practice mode."},
+ {"0166", "Shows hitboxes while in practice mode."},
+ {"0171", "Disables the player's hitbox in practice mode (if hitboxes are shown)."},
+ {"0066", "Increases draw capacity for batch nodes at level start. Can improve performance on some levels, but may cause issues on low-end devices."},
+ {"0108", "Enables low detail mode on levels that support it automatically."},
+ {"0082", "Removes the alert shown when starting levels with a high object count."},
+ {"0136", "Removes glow and enter effects while in low detail mode. Levels without LDM show LDM Lite."},
+ {"0042", "Increases maximum locally saved levels from 10 to 100. This refers to level data, not statistics. Enabling this can make your save file considerably larger, so keeping the option off is recommended for quicker saving."},
+ {"0119", "Saves level statistics as usual, but levels need to be redownloaded every time you restart the game. Makes saving and loading faster."},
+ {"0127", "Saves gauntlet levels locally so they do not have to be redownloaded. Increases save time but helpful if you have poor connection."},
+ {"0155", "Disables anti-aliasing on shader effects."},
+ {"0033", "Saves custom songs in a different directory. May fix custom songs not working."},
+ {"0083", "Removes the alert shown when starting levels without the song downloaded."},
+ {"0018", "Stops automatic deletion of custom songs. This is done by default to save space."},
+ {"0142", "Lowers audio sampling rate from 44100 Hz to 24000 Hz. Requires restarting to take effect."},
+ {"0159", "Increases the audio buffer size, which may fix certain issues. Do not enable if audio is working fine. Causes a slight more audio delay. Requires restarting to take effect."},
+ {"0094", "Shows more comments per page. Why not?"},
+ {"0090", "Loads comments automatically."},
+ {"0073", "Makes completed levels filter based only on percentage from update 2.1. Useful to rebeat levels for Mana Orbs."},
+ {"0093", "Increases created and saved levels per page from 10 to 20."},
+ {"0084", "Places new levels last in the saved levels list. Useful if you want to manually move levels to the top."},
+ {"0126", "Shows decimals in level progress."},
+ {"0099", "Toggles viewing the leaderboard percentage you have on levels. To upload your level progress to the level leaderboard, you need to replay levels completed before 2.11."},
+ {"0095", "Does not do anything... Well, nothing useful."},
+ {"0167", "Adds an extra confirmation window when exiting levels."},
+ {"0168", "Makes transitions between menu pages faster."},
+ {"0040", "Toggles the percent label in game"},
+ {"0074", "Toggles the restart button on the pause menu"},
+ {"0109", "Toggles the extra info/debug label in game"},
+ {"0113", "Flips the platformer controls"},
+ {"0153", "Whether the player should explode on death"},
+ {"0022", "Whether the game should use higher audio quality"},
+ {"0075", "(Parental Controls) Disables comments (known to be buggy)"},
+ {"0076", "(Parental Controls) Disables account posts"},
+ {"0077", "(Parental Controls) Removes the search button in the creator menu"},
+ {"0023", "Toggles smooth fix"},
+ {"0065", "Toggles move optimization"},
+ {"0101", "Forces smooth fix to be on"},
+ {"0102", "Toggles smooth fix in the editor"},
+ {"0056", "Disables the high object alert"},
+ {"0081", "Disables the shake effect in levels"},
+ {"0067", "Increases the accuracy of start positions"},
+ };
+
+ return descriptions.at(gv);
+}
+
+void SettingCell::onInfo(CCObject* sender) {
+ FLAlertLayer::create(
+ m_name.c_str(),
+ descForGV(m_gameVariable).c_str(),
+ "OK"
+ )->show();
+}
+
+SettingsLayer* SettingsLayer::create() {
+ auto ret = new SettingsLayer();
+ if (ret && ret->initAnchored(500.f, 280.f)) {
+ ret->autorelease();
+ return ret;
+ }
+ delete ret;
+ return nullptr;
+}
+
+CCSprite* createCategoryBtnSprite(std::string name, bool isSelected = false) {
+ auto sprite = CCSprite::createWithSpriteFrameName(
+ isSelected ? "GJ_longBtn02_001.png" : "GJ_longBtn01_001.png"
+ );
+ auto text = CCLabelBMFont::create(name.c_str(), "bigFont.fnt");
+ text->limitLabelWidth(75.f, 0.7f, 0.1f);
+ sprite->setScale(0.8f);
+ sprite->addChildAtPosition(text, Anchor::Center);
+ return sprite;
+}
+
+CCMenuItemSpriteExtra* createCategoryBtn(std::string name, CCObject* target, SettingPage page, SEL_MenuHandler callback) {
+ auto btn = CCMenuItemSpriteExtra::create(
+ createCategoryBtnSprite(name), target, callback
+ );
+ btn->setUserObject(CCInteger::create(page));
+ btn->setID(name);
+ return btn;
+}
+
+bool SettingsLayer::setup() {
+ m_noElasticity = true;
+ auto bg = CCScale9Sprite::create("square02b_001.png");
+ bg->setContentSize({
+ 100.f, 245.f
+ });
+ bg->setColor(ccc3(0,0,0));
+ bg->setOpacity(75);
+
+ auto menu = CCMenu::create();
+
+ #define CATEGORY_BTN(name, page) menu->addChild( \
+ createCategoryBtn(name, this, page, menu_selector(SettingsLayer::onCategoryBtn)) \
+ );
+
+ // auto sprite = CCSprite::createWithSpriteFrameName("GJ_longBtn01_001.png");
+ // auto text = CCLabelBMFont::create("test", "bigFont.fnt");
+ // text->setScale(0.7f);
+ // sprite->setScale(0.8f);
+ // sprite->addChildAtPosition(text, Anchor::Center);
+ // auto btn = CCMenuItemSpriteExtra::create(
+ // sprite, this, nullptr
+ // );
+
+ CATEGORY_BTN("Gameplay", SettingPage::Gameplay)
+ CATEGORY_BTN("Practice", SettingPage::Practice)
+ CATEGORY_BTN("Perf.", SettingPage::Performance)
+ CATEGORY_BTN("Audio", SettingPage::Audio)
+ CATEGORY_BTN("Misc", SettingPage::Misc)
+ CATEGORY_BTN("Keys", SettingPage::Keybinds)
+
+ menu->setLayout(
+ ColumnLayout::create()
+ ->setAxisAlignment(AxisAlignment::Even)
+ ->setAxisReverse(true)
+ );
+ menu->setContentSize(bg->getContentSize());
+ menu->setPosition(bg->getPosition());
+ menu->setAnchorPoint({ 0.f, 0.f });
+ menu->updateLayout();
+ menu->setPositionX(
+ bg->getContentWidth() / 2 - menu->getContentWidth() / 2
+ );
+ bg->addChild(menu);
+
+ m_mainLayer->addChildAtPosition(bg, Anchor::Left, ccp(65.f, 0.f));
+
+ switchPage(SettingPage::Gameplay, true, typeinfo_cast(this->getChildByIDRecursive("Gameplay")));
+
+ auto searchBtnSpr = CCSprite::createWithSpriteFrameName("gj_findBtn_001.png");
+ auto searchBtn = CCMenuItemSpriteExtra::create(
+ searchBtnSpr, this, menu_selector(SettingsLayer::onSearchBtn)
+ );
+ auto searchMenu = CCMenu::create();
+ searchMenu->addChild(searchBtn);
+ m_mainLayer->addChildAtPosition(searchMenu, Anchor::TopRight);
+
+ return true;
+}
+
+void SettingsLayer::onSearchBtn(CCObject* sender) {
+ SearchPopup::create([this](std::string query) {
+ auto newList = CCArray::create();
+
+ using namespace geode::utils::string;
+
+ // lazily getting the original list back
+ // lazy as in I am being lazy
+
+ auto page = static_cast(static_cast(
+ m_currentBtn->getUserObject()
+ )->getValue());
+
+ switchPage(page, false, m_currentBtn);
+
+ if (query == "") return;
+
+ for (auto cell : CCArrayExt(m_listItems)) {
+ if (toLower(cell->m_name).find(toLower(query)) != std::string::npos) {
+ if (cell->m_type == SettingCellType::Separator) continue;
+ newList->addObject(
+ SettingCell::create(cell->m_name, cell->m_gameVariable, cell->m_type)
+ );
+ }
+ }
+
+ m_listItems = newList;
+ m_listView->removeFromParent();
+ this->refreshList();
+ })->show();
+}
+
+void SettingsLayer::onCategoryBtn(CCObject* sender) {
+ auto node = static_cast(sender);
+ int page = static_cast(
+ node->getUserObject()
+ )->getValue();
+
+ switchPage(static_cast(page), false, node);
+}
+
+void SettingsLayer::switchPage(SettingPage page, bool isFirstRun, CCMenuItemSpriteExtra* btn) {
+ m_listItems = CCArray::create();
+
+ #define SETTING(name, gv) m_listItems->addObject( \
+ SettingCell::create(name, gv) \
+ );
+ #define SETTING_WITH_TYPE(name, type) m_listItems->addObject( \
+ SettingCell::create(name, "", type) \
+ );
+ #define SEPARATOR(text) m_listItems->addObject( \
+ SettingCell::create(text, "", SettingCellType::Separator) \
+ );
+
+ switch (page) {
+ case Gameplay:
+ SETTING("Auto Retry", "0026")
+ SETTING("Flip 2P controls", "0010")
+ SETTING("Always Limit Controls", "0011")
+ SETTING("Show Cursor In-Game", "0024")
+ SETTING("Disable Thumbstick", "0028")
+ SETTING("Show Percent", "0040")
+ SETTING("Fast Reset", "0052")
+ SETTING("Restart Button", "0074")
+ SETTING("Extra Info", "0109")
+ SETTING("Flip Plat. Controls", "0113")
+ SETTING("Decimal Percent", "0126")
+ SETTING("Orb Labels", "0130")
+ SETTING("Hide Attempts", "0134")
+ SETTING("Quick Keys", "0163")
+ SETTING("Confirm Exit", "0167")
+ SETTING("Hide Playtest Text", "0174")
+ SETTING("Flip Pause Button", "0015")
+ SEPARATOR("Icon Options")
+ SETTING("Explode Player on Death", "0153")
+ SETTING("Default Mini Icon", "0026")
+ SETTING("Switch Spider Teleport Color", "0061")
+ SETTING("Switch Dash Fire Color", "0062")
+ SETTING("Switch Wave Trail Color", "0096")
+ break;
+ case Audio:
+ SETTING("Load Songs into Memory", "0010")
+ SETTING("Higher Audio Quality", "0022")
+ SETTING("Change Song Path", "0033")
+ SETTING("No Song Limit", "0018")
+ SETTING("Normal Music in Editor", "0125")
+ SETTING("Reduce Audio Quality", "0142")
+ SETTING("Audio Fix 01", "0159")
+ SETTING_WITH_TYPE("FMOD Debug", SettingCellType::FMODDebug)
+ SETTING_WITH_TYPE("Local Songs", SettingCellType::SongSelect)
+ SETTING_WITH_TYPE("Song Offset (MS)", SettingCellType::SongOffset)
+ break;
+ case Misc:
+ SETTING("Do Not...", "0095")
+ SETTING("Disable Gravity Effect", "0072")
+ SETTING("New Completed Filter", "0073")
+ SETTING("Disable Song Alert", "0083")
+ SETTING("Manual Level Order", "0084")
+ SETTING("Show Leaderboard Percent", "0099")
+ SETTING("Fast Menu", "0168")
+ SEPARATOR("Comments")
+ SETTING("Autoload Comments", "0090")
+ SETTING("More Comments Mode", "0094")
+ SEPARATOR("Parent Controls")
+ SETTING("Disable Comments", "0075")
+ SETTING("Disable Account Comments", "0076")
+ SETTING("Featured Levels Only", "0077")
+ break;
+ case Performance:
+ SETTING("Smooth Fix", "0023")
+ SETTING("Move Optimization", "0065")
+ SETTING("Force Smooth Fix", "0101")
+ SETTING("Smooth Fix in the Editor", "0102")
+ SETTING("Lock Cursor In-Game", "0128")
+ SEPARATOR("LDM")
+ SETTING("Auto LDM", "0108")
+ SETTING("Extra LDM", "0136")
+ SEPARATOR("Improvements (may cause issues on low end devices)")
+ SETTING("Increase Max Levels", "0042")
+ SETTING("Disable Object Alert", "0056")
+ SETTING("Save Gauntlet Levels", "0126")
+ SETTING("Increase Local Levels Per Page", "0093")
+ SETTING("High Capacity Mode", "0066")
+ SEPARATOR("Disable Toggles")
+ SETTING("Disable Explosion Shake", "0014")
+ SETTING("Disable Orb Scale", "0140")
+ SETTING("Disable Shake Effect", "0081")
+ SETTING("Disable High Object Alert", "0082")
+ SETTING("Disable Shader Anti-Aliasing", "0155")
+ SETTING("Disable Trigger Orb Scale", "0141")
+ break;
+ case Practice:
+ SETTING("Auto Checkpoints", "0027")
+ SETTING("High Start Position Accuracy", "0067")
+ SETTING("Quick Checkpoint Mode", "0068")
+ SETTING("Hide Practice Button", "0071")
+ SETTING("Practice Death Effect", "0100")
+ SETTING("Hide Attempts in Practice", "0135")
+ break;
+ case Keybinds:
+ // I need to manually create (but not show) the original
+ // MoreOptionsLayer in order to do this so that I don't
+ // accidently cause incompats with Custom Keybinds
+ auto mol = MoreOptionsLayer::create();
+ mol->onKeybindings(btn);
+
+ // auto dummyNode = CCNode::create();
+ // auto text = CCLabelBMFont::create("There's nothing here!", "goldFont.fnt");
+ // dummyNode->addChildAtPosition(text, Anchor::Center);
+
+ // m_listItems->addObject(dummyNode);
+ SEPARATOR("There's nothing here!")
+
+ break;
+ }
+ if (!isFirstRun) {
+ m_listView->removeFromParent();
+ }
+
+ this->refreshList();
+
+ if (m_currentBtn) {
+ m_currentBtn->setSprite(
+ createCategoryBtnSprite(m_currentBtn->getID())
+ );
+ }
+ btn->setSprite(
+ createCategoryBtnSprite(btn->getID(), true)
+ );
+ m_currentBtn = btn;
+}
+
+void SettingsLayer::refreshList() {
+ m_listView = ListView::create(m_listItems, 50.f, 365.f, 245.f);
+ m_listView->ignoreAnchorPointForPosition(false);
+ for (auto cell : CCArrayExt(m_listItems)) {
+ cell->setContentSize({
+ 365.f,
+ 50.f
+ });
+ cell->updateLayout();
+ }
+
+ m_mainLayer->addChildAtPosition(m_listView, Anchor::Right, ccp(-195.f, 0.f));
+}
diff --git a/src/SettingsLayer.hpp b/src/SettingsLayer.hpp
new file mode 100644
index 0000000..3b77397
--- /dev/null
+++ b/src/SettingsLayer.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include
+
+using namespace geode::prelude;
+
+enum SettingPage {
+ Gameplay,
+ Practice,
+ Performance,
+ Audio,
+ Misc,
+ Keybinds
+};
+
+enum SettingCellType {
+ Default,
+ FMODDebug,
+ SongSelect,
+ SongOffset,
+ Separator
+};
+
+using SearchCB = std::function;
+class SearchPopup : public geode::Popup {
+protected:
+ TextInput* m_input;
+ SearchCB m_callback;
+ bool setup(SearchCB) override;
+ void onSearch(CCObject*);
+public:
+ static SearchPopup* create(SearchCB callback);
+};
+
+class SettingCell : public CCNode {
+protected:
+ CCMenuItemToggler* m_toggler;
+
+ bool init(std::string name, std::string gv, SettingCellType type);
+ void onCheckboxToggled(CCObject* sender);
+ void onFMODDebug(CCObject*);
+ void onSongSelect(CCObject*);
+ void onInfo(CCObject*);
+public:
+ std::string m_name;
+ std::string m_gameVariable;
+ SettingCellType m_type;
+ static SettingCell* create(std::string name, std::string gv, SettingCellType type = SettingCellType::Default);
+};
+
+class SettingsLayer : public geode::Popup<> {
+protected:
+ CCArray* m_listItems;
+ ListView* m_listView;
+ CCMenuItemSpriteExtra* m_currentBtn;
+
+ bool setup() override;
+ void createSettingCheckbox(
+ std::string name,
+ std::string gv
+ );
+ void switchPage(SettingPage, bool, CCMenuItemSpriteExtra*);
+ void onCategoryBtn(CCObject*);
+ void onSearchBtn(CCObject*);
+
+ void refreshList();
+public:
+ static SettingsLayer* create();
+};
+
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..77af524
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,12 @@
+#include
+#include
+
+#include "./SettingsLayer.hpp"
+
+using namespace geode::prelude;
+
+class $modify(OptionsLayer) {
+ void onOptions(CCObject* sender) {
+ SettingsLayer::create()->show();
+ }
+};