Skip to content

Commit 105337b

Browse files
committed
buncha tweaks
1 parent 778688b commit 105337b

7 files changed

Lines changed: 54 additions & 303 deletions

File tree

include/Horrible.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace horrible {
4444
private:
4545
std::vector<Option> m_options; // Array of registered options
4646
std::vector<std::string> m_categories; // Array of auto-registered categories
47+
4748
std::unordered_map<std::string_view, std::vector<geode::Function<void(bool)>>> m_delegates; // Map of option ID to array of delegates to call when that option is toggled
4849

4950
protected:

include/README.md

Lines changed: 1 addition & 263 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# [<img src="../logo.png" width="30" alt="The mod's logo." />](https://www.geode-sdk.org/mods/arcticwoof.horrible_ideas) Horrible Ideas
22
A plethora of ways to ruin your gaming experience...
33

4-
> [!WARNING]
5-
> This is heavily outdated.
6-
74
## API
85
Let's start off by adding this mod as a dependency in your `mod.json`!
96
```jsonc
@@ -20,263 +17,4 @@ You can directly access the Horrible Ideas mod menu API by including the [`Horri
2017
#include <arcticwoof.horrible_ideas/include/Horrible.hpp>
2118

2219
using namespace horrible;
23-
```
24-
25-
### Classes
26-
Here are some important classes we highly suggest you keep in mind while working with the API.
27-
28-
#### class `horrible::OptionManager`
29-
The manager class for Horrible Ideas mod options.
30-
- `static OptionManager*` **`get()`**: Get option manager singleton
31-
- `void` **`registerOption(Option const& option)`**: Register a new option
32-
- `Option const&` **`option`**: Constructed option object
33-
- `bool` **`isEnabled(std::string_view id)`** `const`: Returns the toggle state of an option
34-
- `void` **`setOption(std::string_view id, bool enable)`** `const`: Set the toggle state of an option
35-
- `std::string_view` **`id`**: The ID of the option to toggle
36-
- `bool` **`enable`**: Boolean to toggle to
37-
38-
#### enum class `horrible::SillyTier`
39-
An enum class that defines how chaotic or funny an option is.
40-
41-
#### class `geode::PlatformID`
42-
A dynamic enum class provided by Geode that defines the player's current platform.
43-
44-
#### struct `horrible::Option`
45-
The object structure of an option.
46-
- `std::string` **`id`**: Unique ID of the option
47-
- `std::string` **`name`**: Name of the option
48-
- `std::string` **`description`**: Description of the option
49-
- `std::string` **`category`**: Name of the category this option should be under
50-
- `SillyTier` **`silly`**: How silly the option is
51-
- `bool` **`restart`** (`false`): If the option requires a restart to take effect
52-
- `std::vector<PlatformID>` **`platforms`** (`{ PlatformID::Desktop, PlatformID::Mobile }`): Platforms that the option supports
53-
54-
#### class `horrible::HorribleOptionEvent`
55-
An event that fires any time any option is changed.
56-
- `std::string` **`getId()`** `const`: Get the unique ID of the option
57-
- `bool` **`getToggled()`** `const`: Get the toggle boolean of the option
58-
59-
#### class `horrible::HorribleOptionEventFilter`
60-
- `ListenerResult` **`handle(geode::Function<Callback> fn, HorribleOptionEvent* event)`**: Event handler
61-
- `geode::Function<Callback>` **`fn`**: Callback function containing a pointer to the event that fired
62-
- `HorribleOptionEvent*` **`event`**: Pointer to the event that fired
63-
64-
#### Summary
65-
| Type | Name | Description |
66-
| ------------ | --------------------------- | ------------------------------------ |
67-
| `class` | `OptionManager` | Manager for Horrible Ideas options |
68-
| `enum class` | `SillyTier` | Defines how silly an option is |
69-
| `struct` | `Option` | Represents a toggleable option |
70-
| `class` | `HorribleOptionEvent` | Fired when an option is toggled |
71-
| `class` | `HorribleOptionEventFilter` | Filters through option toggle events |
72-
73-
### Options
74-
You can register and check any and as many options as you desire through this API.
75-
76-
> [!IMPORTANT]
77-
> To work with options, you will first be required to access the pointer to the **`OptionManager`** class by using `OptionManager::get()` to define a variable to use in your code.
78-
>
79-
> ```cpp
80-
> auto optMgr = OptionManager::get();
81-
> ```
82-
>
83-
> This way, you can now safely use its methods to work directly with Horrible Ideas's API to handle your own custom options.
84-
85-
#### Registering
86-
This mod makes it easy for players to access the options they want to use. You can register your own options by using the **`OptionManager::registerOption`** method inside an `$on_mod(Loaded)` block. You will need to pass one parameter, which is a constructed **`Option`** object for the option you want to register.
87-
88-
*Required fields of the **`Option`** struct are, in order: `id`, `name`, `description`, `category`, and `silly`. Optional fields are `restart` and `platforms`.*
89-
90-
> [!TIP]
91-
> Be sure to prefix your option's unique ID with your Geode mod ID by appending **`_spr`** after the end of the string to prevent conflicts with this mod or other mods that may also register options with possibly identical IDs.
92-
93-
```cpp
94-
$on_mod(Loaded){
95-
auto optMgr = OptionManager::get();
96-
97-
optMgr->registerOption({
98-
"something-interesting"_spr,
99-
"Something Interesting",
100-
"This is something that is very interesting.",
101-
"Stuff!",
102-
SillyTier::Medium,
103-
});
104-
};
105-
```
106-
107-
You can include optional fields **`restart`** and **`platforms`** as well! Set `restart` to `true` or `false` depending on whether your option is only meant to load once per session. The array for `platforms` uses Geode's dynamic **`PlatformID`** class to identify the exact platform the player is running Geometry Dash on. By default, Horrible Ideas sets every option to be compatible for `PlatformID::Desktop` and `PlatformID::Mobile`, essentially covering all platforms. However, you can also get very specific about the exact platform you can run your own options on if absolutely necessary, though such a case may not present itself often.
108-
109-
> [!IMPORTANT]
110-
> Even if `restart` is **enabled** for your option, the global event for it *will still fire* whenever the player changes it mid-game. What this setting does is actually just notify the player that your option will only load after they restart the game.
111-
112-
```cpp
113-
$on_mod(Loaded){
114-
auto optMgr = OptionManager::get();
115-
116-
optMgr->registerOption({
117-
"cool-things"_spr,
118-
"Cool Things",
119-
"Some really really cool things.",
120-
"Stuff!",
121-
SillyTier::Low,
122-
true, // Notify player to restart
123-
{
124-
PlatformID::Android32,
125-
PlatformID::X64 // Support specific platforms
126-
},
127-
});
128-
};
129-
```
130-
131-
This will automatically include your option in Horrible Ideas's pre-existing list of options, and will appear in the menu for the player whenever they open it.
132-
133-
#### Handling
134-
Once you've registered an option on `$on_mod(Loaded)`, you can use other methods to work with the option.
135-
136-
##### Static Conditioning
137-
You can begin by using **`OptionManager::isEnabled`** and provide your option's unique ID to check if an option is enabled or disabled.
138-
```cpp
139-
using namespace geode::prelude;
140-
using namespace horrible;
141-
142-
class $modify(CoolThingsPlayLayer, PlayLayer) {
143-
struct Fields {
144-
bool enabled = OptionManager::get()->isEnabled("cool-things"_spr); // If this option is set to true or false
145-
};
146-
147-
bool init(GJGameLevel * level, bool useReplay, bool dontCreateObjects) {
148-
if (!PlayLayer::init(level, useReplay, dontCreateObjects)) return false;
149-
150-
if (m_fields->enabled) {
151-
// do stuff!
152-
};
153-
154-
return true;
155-
};
156-
};
157-
```
158-
159-
##### Events
160-
If you would like or need to re-implement or remove an option's functionality live as the player clicks on the toggle for that option, you can listen to the **`HorribleOptionEvent`** global event, which fires any time any option is toggled.
161-
162-
> [!IMPORTANT]
163-
> Be sure to return `ListenerResult::Propagate` after you're finished handling each event to ensure other remaining event instances function properly.
164-
165-
```cpp
166-
using namespace geode::prelude;
167-
using namespace horrible;
168-
169-
class $modify(SomethingInterestingMenuLayer, MenuLayer) {
170-
struct Fields {
171-
bool enabled = OptionManager::get()->isEnabled("something-interesting"_spr);
172-
EventListener<HorribleOptionEventFilter> m_optionListener; // Listen to any option being toggled
173-
};
174-
175-
bool init() {
176-
if (!MenuLayer::init()) return false;
177-
178-
if (m_fields->enabled) {
179-
// implemented logic here
180-
};
181-
182-
this->template addEventListener<HorribleOptionEventFilter>(
183-
[this](HorribleOptionEvent* event) {
184-
if (event->getToggled()) {
185-
// handle re-implementation here
186-
};
187-
188-
return ListenerResult::Propagate;
189-
},
190-
"something-interesting"_spr
191-
);
192-
193-
return true;
194-
};
195-
};
196-
```
197-
198-
## Optional API
199-
Let's start off by adding this mod as an optional dependency in your `mod.json`!
200-
```jsonc
201-
"dependencies": {
202-
"arcticwoof.horrible_ideas": {
203-
"importance": "suggested",
204-
"version": ">=1.0.0"
205-
}
206-
}
207-
```
208-
209-
You can directly access the Horrible Ideas mod menu optional API by including the [`OptionalAPI.hpp`](OptionalAPI.hpp) file in your code. Make sure to include the **`horrible`** namespace to directly access all needed classes and methods.
210-
```cpp
211-
#include <arcticwoof.horrible_ideas/include/OptionalAPI.hpp>
212-
213-
using namespace horrible;
214-
```
215-
216-
### Classes
217-
These classes mirror the main API but return `geode::Result` values so callers can safely handle functions even if Horrible Ideas is not loaded.
218-
219-
#### class `horrible::HorribleOptionEventV2`
220-
- `std::string` **`getId()`** `const`
221-
- `bool` **`getToggled()`** `const`
222-
223-
#### class `horrible::HorribleOptionEventFilterV2`
224-
- `ListenerResult` **`handle(geode::Function<Callback> fn, HorribleOptionEventV2* event)`**
225-
226-
#### class `horrible::OptionManagerV2`
227-
- `static Result<>` **`registerOption(Option const& option)`**
228-
- `static Result<bool>` **`isEnabled(std::string_view id)`**
229-
- `static Result<>` **`setOption(std::string_view id, bool enable)`**
230-
231-
#### Summary
232-
| Type | Name | Description |
233-
| ------- | ----------------------------- | ------------------------------------ |
234-
| `class` | `OptionManagerV2` | Manager for Horrible Ideas options |
235-
| `class` | `HorribleOptionEventV2` | Fired when an option is toggled |
236-
| `class` | `HorribleOptionEventFilterV2` | Filters through option toggle events |
237-
238-
### Option
239-
You can register and check any and as many options as you desire through this API. Most of the examples given and implied context here will be derived from earlier examples.
240-
241-
#### Registering
242-
Here's how you can register your own options through the optional API.
243-
244-
```cpp
245-
$on_mod(Loaded){
246-
auto res = OptionManagerV2::registerOption({
247-
"optional-something"_spr,
248-
"Optional Something",
249-
"An option registered through the optional API.",
250-
"Optional",
251-
SillyTier::Low,
252-
});
253-
254-
if (res.isErr()) log::error("Failed to register option: {}", res.unwrapErr());
255-
};
256-
```
257-
258-
### Events
259-
Listening for state changes to a specific option.
260-
261-
```cpp
262-
EventListener<HorribleOptionEventFilterV2> listener = {
263-
[](HorribleOptionEventV2* ev) {
264-
if (ev->getToggled()) {
265-
// re-implement here
266-
};
267-
268-
return ListenerResult::Propagate;
269-
},
270-
HorribleOptionEventFilterV2("optional-something"_spr)
271-
};
272-
```
273-
274-
## Watch Out!
275-
Some common pitfalls may include the following.
276-
- Forgetting to use `_spr` when defining unique option ID
277-
- Not returning `ListenerResult::Propagate` in event callbacks
278-
- Registering options outside the `$on_mod(Loaded)` block
279-
280-
Always double-check your code to make sure it follows safe practices.
281-
282-
*Happy modding!*
20+
```

mod.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
"type": "int",
177177
"name": "Math Quiz Chance",
178178
"description": "<cj>Richard's Math Quiz!</c> - Chance of Richard's math quiz appearing in a level.",
179-
"default": 25,
179+
"default": 35,
180180
"min": 0,
181181
"max": 100,
182182
"control": {
@@ -210,7 +210,7 @@
210210
"type": "int",
211211
"name": "Framerate Freeze Chance",
212212
"description": "<cj>Random 90%+ FPS Drop</c> - Chance of your game freezing after reaching 90% in a classic level.",
213-
"default": 10,
213+
"default": 5,
214214
"min": 0,
215215
"max": 100,
216216
"control": {
@@ -227,7 +227,7 @@
227227
"type": "int",
228228
"name": "Spam Challenge Chance",
229229
"description": "<cj>Spam Challenge!</c> - Chance of the spam challenge appearing.",
230-
"default": 50,
230+
"default": 65,
231231
"min": 0,
232232
"max": 100,
233233
"control": {
@@ -244,7 +244,7 @@
244244
"type": "int",
245245
"name": "Achievement Sound Chance",
246246
"description": "<cj>Random Achievements</c> - Chance of the achievement sound playing.",
247-
"default": 75,
247+
"default": 25,
248248
"min": 0,
249249
"max": 100,
250250
"control": {
@@ -278,7 +278,7 @@
278278
"type": "int",
279279
"name": "No Jump Chance",
280280
"description": "<cj>Randomly Don't Jump</c> - Chance of your jump input not registering.",
281-
"default": 25,
281+
"default": 50,
282282
"min": 0,
283283
"max": 100,
284284
"control": {
@@ -295,7 +295,7 @@
295295
"type": "int",
296296
"name": "Sleep Chance",
297297
"description": "<cj>Sleepy Player</c> - Chance of your player falling asleep.",
298-
"default": 75,
298+
"default": 65,
299299
"min": 0,
300300
"max": 100,
301301
"control": {
@@ -312,7 +312,7 @@
312312
"type": "int",
313313
"name": "Sticky Chance",
314314
"description": "<cj>Sticky Grounds</c> - Chance of your player sticking to a surface.",
315-
"default": 25,
315+
"default": 35,
316316
"min": 0,
317317
"max": 100,
318318
"control": {
@@ -329,7 +329,7 @@
329329
"type": "int",
330330
"name": "Pause Chance",
331331
"description": "<cj>Random Pauses</c> - Chance of the game pausing while playing.",
332-
"default": 5,
332+
"default": 25,
333333
"min": 0,
334334
"max": 100,
335335
"control": {
@@ -346,7 +346,7 @@
346346
"type": "int",
347347
"name": "Random Mirror Portal Chance",
348348
"description": "<cj>Random Mirror Portal</c> - Chance of the mirror portal activating while playing.",
349-
"default": 5,
349+
"default": 10,
350350
"min": 0,
351351
"max": 100,
352352
"control": {
@@ -380,7 +380,7 @@
380380
"type": "int",
381381
"name": "Dementia Chance",
382382
"description": "<cj>Dementia</c> - Chance of your player teleporting back.",
383-
"default": 10,
383+
"default": 25,
384384
"min": 0,
385385
"max": 100,
386386
"control": {
@@ -397,7 +397,7 @@
397397
"type": "int",
398398
"name": "Fake Crash Chance",
399399
"description": "<cj>Fake Crash</c> - Chance of a fake crash happening when you die.",
400-
"default": 10,
400+
"default": 15,
401401
"min": 0,
402402
"max": 100,
403403
"control": {
@@ -414,7 +414,7 @@
414414
"type": "int",
415415
"name": "Flip Chance",
416416
"description": "<cj>FLIPPED!</c> - Chance of your screen flipping mid-game.",
417-
"default": 10,
417+
"default": 25,
418418
"min": 0,
419419
"max": 100,
420420
"control": {

0 commit comments

Comments
 (0)