From 01d71b5924088fe6355cfb904d673bac368fc020 Mon Sep 17 00:00:00 2001 From: Cheeseworks Date: Sun, 7 Jun 2026 08:34:18 -0500 Subject: [PATCH 1/5] initial draft --- src/hooks/InputRoulette.cpp | 15 +++++++++++++++ src/hooks/obstructive/Notifications.cpp | 15 +++++++++++++++ src/hooks/obstructive/RealFloating.cpp | 15 +++++++++++++++ src/hooks/playerlife/LimitedJump.cpp | 15 +++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 src/hooks/InputRoulette.cpp create mode 100644 src/hooks/obstructive/Notifications.cpp create mode 100644 src/hooks/obstructive/RealFloating.cpp create mode 100644 src/hooks/playerlife/LimitedJump.cpp diff --git a/src/hooks/InputRoulette.cpp b/src/hooks/InputRoulette.cpp new file mode 100644 index 0000000..99d6234 --- /dev/null +++ b/src/hooks/InputRoulette.cpp @@ -0,0 +1,15 @@ +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +#define THIS_ID "input_roulette" + +static auto const o = Option::create(THIS_ID) + ->setName("Input Roulette") + ->setDescription("A meter will show on the side of the screen during gameplay hovering over a bar showing the safe and not-so-safe areas the floating arrow can be on when you press your jump button. If the arrow happens to be in an unsafe area when you press jump, your character has an 80% chance of dying.\ncreated by Cheeseworks") + ->setCategory(category::misc) + ->setSillyTier(SillyTier::High) + ->autoRegister(); \ No newline at end of file diff --git a/src/hooks/obstructive/Notifications.cpp b/src/hooks/obstructive/Notifications.cpp new file mode 100644 index 0000000..b4be740 --- /dev/null +++ b/src/hooks/obstructive/Notifications.cpp @@ -0,0 +1,15 @@ +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +#define THIS_ID "notifications" + +static auto const o = Option::create(THIS_ID) + ->setName("Notifications") + ->setDescription("During gameplay, notifications will begin to pile up. If you let too many pile up before checking them, you'll be forced out of the level.\ncreated by Cheeseworks") + ->setCategory(category::obstructive) + ->setSillyTier(SillyTier::Medium) + ->autoRegister(); \ No newline at end of file diff --git a/src/hooks/obstructive/RealFloating.cpp b/src/hooks/obstructive/RealFloating.cpp new file mode 100644 index 0000000..8629d63 --- /dev/null +++ b/src/hooks/obstructive/RealFloating.cpp @@ -0,0 +1,15 @@ +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +#define THIS_ID "da_real_floating_btn" + +static auto const o = Option::create(THIS_ID) + ->setName("da real floating button") + ->setDescription("You'll have a floating button constantly zooming around your screen during gameplay. If you press it, your character will immediately die.\ncreated by Cheeseworks") + ->setCategory(category::obstructive) + ->setSillyTier(SillyTier::Medium) + ->autoRegister(); \ No newline at end of file diff --git a/src/hooks/playerlife/LimitedJump.cpp b/src/hooks/playerlife/LimitedJump.cpp new file mode 100644 index 0000000..2893447 --- /dev/null +++ b/src/hooks/playerlife/LimitedJump.cpp @@ -0,0 +1,15 @@ +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +#define THIS_ID "limited_jump" + +static auto const o = Option::create(THIS_ID) + ->setName("Limited Jump Energy") + ->setDescription("You only have a limited amount of jumps you can make for a period of time, which will regenerate at a random pace over time. Run out of jumps too quickly, and your character will die.\ncreated by Cheeseworks") + ->setCategory(category::playerlife) + ->setSillyTier(SillyTier::Medium) + ->autoRegister(); \ No newline at end of file From 6e81af33deb54bc51d64a832fbf284813eabd9bc Mon Sep 17 00:00:00 2001 From: Cheeseworks Date: Mon, 13 Jul 2026 08:07:20 -0500 Subject: [PATCH 2/5] notifications will be moved to standalone mod --- src/hooks/obstructive/Notifications.cpp | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/hooks/obstructive/Notifications.cpp diff --git a/src/hooks/obstructive/Notifications.cpp b/src/hooks/obstructive/Notifications.cpp deleted file mode 100644 index b4be740..0000000 --- a/src/hooks/obstructive/Notifications.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include - -using namespace geode::prelude; -using namespace horrible::prelude; - -#define THIS_ID "notifications" - -static auto const o = Option::create(THIS_ID) - ->setName("Notifications") - ->setDescription("During gameplay, notifications will begin to pile up. If you let too many pile up before checking them, you'll be forced out of the level.\ncreated by Cheeseworks") - ->setCategory(category::obstructive) - ->setSillyTier(SillyTier::Medium) - ->autoRegister(); \ No newline at end of file From f94b3b39de92b86b9d66924d2d6c55cca055e148 Mon Sep 17 00:00:00 2001 From: Cheeseworks Date: Wed, 19 Aug 2026 20:37:43 -0500 Subject: [PATCH 3/5] da real floating btn --- src/hooks/InputRoulette.cpp | 2 +- src/hooks/obstructive/RealFloating.cpp | 137 ++++++++++++++++++++++++- src/hooks/playerlife/LimitedJump.cpp | 2 +- 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/src/hooks/InputRoulette.cpp b/src/hooks/InputRoulette.cpp index 99d6234..5cacbc0 100644 --- a/src/hooks/InputRoulette.cpp +++ b/src/hooks/InputRoulette.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/src/hooks/obstructive/RealFloating.cpp b/src/hooks/obstructive/RealFloating.cpp index 8629d63..1a7ed56 100644 --- a/src/hooks/obstructive/RealFloating.cpp +++ b/src/hooks/obstructive/RealFloating.cpp @@ -1,7 +1,9 @@ -#include +#include #include +#include + using namespace geode::prelude; using namespace horrible::prelude; @@ -12,4 +14,135 @@ static auto const o = Option::create(THIS_ID) ->setDescription("You'll have a floating button constantly zooming around your screen during gameplay. If you press it, your character will immediately die.\ncreated by Cheeseworks") ->setCategory(category::obstructive) ->setSillyTier(SillyTier::Medium) - ->autoRegister(); \ No newline at end of file + ->autoRegister(); + +static constexpr auto g_icons = std::to_array({ + themes::icons::Default, + themes::icons::Breakeode, + themes::icons::Classic, + themes::icons::ColonThree, + themes::icons::CryingSkull, + themes::icons::Durr, + themes::icons::Pride, + themes::icons::TransPride, + themes::icons::SapphireSDK, + themes::icons::SoggyCat, + themes::icons::SteamHappy, + themes::icons::TheYellowOne, +}); + +static constexpr auto g_frames = std::to_array({ + CircleBaseColor::Blue, + CircleBaseColor::Cyan, + CircleBaseColor::DarkAqua, + CircleBaseColor::DarkPurple, + CircleBaseColor::Gray, + CircleBaseColor::Green, + CircleBaseColor::Pink, + CircleBaseColor::Red, +}); + +class $modify(RealFloatingPlayLayer, PlayLayer) { + HORRIBLE_DELEGATE_HOOKS(THIS_ID); + + struct Fields final { + Button* theBtn = nullptr; + }; + + HORRIBLE_SETUP_INTERFACE_FUNC { + auto f = m_fields.self(); + + if (!on) return cue::resetNode(f->theBtn); + + nextPosition(f->theBtn); + }; + + void setupHasCompleted() { + PlayLayer::setupHasCompleted(); + HORRIBLE_SETUP_INTERFACE_FUNC_NAME(true); + }; + + void resetLevel() { + PlayLayer::resetLevel(); + nextPosition(m_fields->theBtn); + }; + + void destroyPlayer(PlayerObject* player, GameObject* object) { + PlayLayer::destroyPlayer(player, object); + if (object == m_anticheatSpike || !m_playerDied) return; + + auto f = m_fields.self(); + + if (f->theBtn) f->theBtn->stopAllActions(); + }; + + void nextPosition(CCNode* sender) { + auto f = m_fields.self(); + + auto const pos = sender ? sender->getPosition() : getRandomEdge(m_uiLayer->getScaledContentSize(), {}); + + cue::resetNode(f->theBtn); + sender = nullptr; + + f->theBtn = makeButton(false); + + f->theBtn->setPosition(pos); + + m_uiLayer->addChild(f->theBtn, HIGHEST_Z); + + sfx::play(sfx::file::click); + + f->theBtn->runAction( + CCSequence::createWithTwoActions( + CCMoveTo::create(rng::get(1.25f, 0.5f), getRandomEdge(m_uiLayer->getScaledContentSize(), f->theBtn->getPosition())), + CCCallFuncN::create(this, callfuncN_selector(RealFloatingPlayLayer::nextPosition)))); + }; + + Button* makeButton(bool autoPos = true) { + auto theBtn = Button::createWithNode( + CircleButtonSprite::createWithSpriteFrameName( + themes::getIconSprite(g_icons[rng::get(g_icons.size() - 1)]), + 0.875f, + g_frames[rng::get(g_frames.size() - 1)]), + [this](auto) { + destroyPlayer(m_player1, nullptr); + }); + theBtn->setID("press-me-btn"_spr); + theBtn->setScale(rng::get(2.5f, 1.25f)); + + if (autoPos) theBtn->setPosition(getRandomEdge(m_uiLayer->getScaledContentSize(), theBtn->getPosition())); + + return theBtn; + }; + + CCPoint getRandomEdge(CCPoint const& maxSize, CCPoint const& currentPos) const { + auto x = currentPos.x; + auto y = currentPos.y; + + if (currentPos.x <= 0.f) { + x = maxSize.x; + y = rng::get(maxSize.y); + } else if (currentPos.x >= maxSize.x) { + x = 0.f; + y = rng::get(maxSize.y); + } else if (currentPos.y <= 0.f) { + x = rng::get(maxSize.x); + y = maxSize.y; + } else if (currentPos.y >= maxSize.y) { + x = rng::get(maxSize.x); + y = 0.f; + } else { + if (rng::flip()) { + x = (rng::flip() ? 0.f : maxSize.x); + y = rng::get(maxSize.y); + } else { + x = rng::get(maxSize.x); + y = (rng::flip() ? 0.f : maxSize.y); + }; + }; + + return {x, y}; + }; +}; + +HORRIBLE_TOGGLE_MODIFY(PlayLayer, RealFloatingPlayLayer); \ No newline at end of file diff --git a/src/hooks/playerlife/LimitedJump.cpp b/src/hooks/playerlife/LimitedJump.cpp index 2893447..34fefda 100644 --- a/src/hooks/playerlife/LimitedJump.cpp +++ b/src/hooks/playerlife/LimitedJump.cpp @@ -1,4 +1,4 @@ -#include +#include #include From 2da06bdfd0e87d5329e49ebfe52cbd26cca6bcd5 Mon Sep 17 00:00:00 2001 From: Cheeseworks Date: Wed, 19 Aug 2026 23:55:00 -0500 Subject: [PATCH 4/5] WOO limited jump inputs !!! --- src/hooks/playerlife/LimitedJump.cpp | 84 +++++++++++++++++++- src/util/ui/Include.h | 1 + src/util/ui/JumpHealthMeter.hpp | 31 ++++++++ src/util/ui/src/JumpHealthMeter.cpp | 110 +++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 src/util/ui/JumpHealthMeter.hpp create mode 100644 src/util/ui/src/JumpHealthMeter.cpp diff --git a/src/hooks/playerlife/LimitedJump.cpp b/src/hooks/playerlife/LimitedJump.cpp index 34fefda..11d3ca9 100644 --- a/src/hooks/playerlife/LimitedJump.cpp +++ b/src/hooks/playerlife/LimitedJump.cpp @@ -2,6 +2,9 @@ #include +#include +#include + using namespace geode::prelude; using namespace horrible::prelude; @@ -9,7 +12,84 @@ using namespace horrible::prelude; static auto const o = Option::create(THIS_ID) ->setName("Limited Jump Energy") - ->setDescription("You only have a limited amount of jumps you can make for a period of time, which will regenerate at a random pace over time. Run out of jumps too quickly, and your character will die.\ncreated by Cheeseworks") + ->setDescription("You only have a limited amount of jump inputs you can make for a period of time, which will regenerate at a random pace over time. Run out of available inputs too quickly, and your character will die.\ncreated by Cheeseworks") ->setCategory(category::playerlife) ->setSillyTier(SillyTier::Medium) - ->autoRegister(); \ No newline at end of file + ->autoRegister(); + +class $modify(LimitedJumpGJBaseGameLayer, GJBaseGameLayer) { + HORRIBLE_DELEGATE_HOOKS(THIS_ID); + + struct Fields final { + JumpHealthMeter* jumpMeter = nullptr; + }; + + HORRIBLE_SETUP_INTERFACE_FUNC { + auto f = m_fields.self(); + + if (!on) return cue::resetNode(f->jumpMeter); + + if (auto pl = PlayLayer::get()) { + f->jumpMeter = JumpHealthMeter::create([this, meter = f->jumpMeter]() { + if (auto pl = PlayLayer::get()) pl->destroyPlayer(m_player1, nullptr); + }); + f->jumpMeter->setID("jump-energy-meter"_spr); + f->jumpMeter->setZOrder(HIGHEST_Z); + f->jumpMeter->setAnchorPoint({1, 0}); + + pl->m_uiLayer->addChildAtPosition(f->jumpMeter, Anchor::BottomRight, {-8.75f, 8.75f}, false); + }; + }; + + bool init() { + if (!GJBaseGameLayer::init()) return false; + + queueInMainThread([self = WeakRef(this)]() { + if (auto s = self.lock()) s->HORRIBLE_SETUP_INTERFACE_FUNC_NAME(); + }); + + return true; + }; + + void handleButton(bool down, int button, bool isPlayer1) { + GJBaseGameLayer::handleButton(down, button, isPlayer1); + if (button == 1 && down) m_fields->jumpMeter->jump(); + }; + + JumpHealthMeter* getHealthMeter() noexcept { + if (auto f = m_fields.self()) return f->jumpMeter; + return nullptr; + }; +}; + +class $modify(LimitedJumpPlayLayer, PlayLayer) { + HORRIBLE_DELEGATE_HOOKS(THIS_ID); + + void destroyPlayer(PlayerObject* player, GameObject* object) { + PlayLayer::destroyPlayer(player, object); + + if (m_playerDied) { + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->playPause(false); + }; + }; + }; + + void resetLevel() { + PlayLayer::resetLevel(); + + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->reset(); + }; + }; + + void resetLevelFromStart() { + PlayLayer::resetLevelFromStart(); + + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->reset(); + }; + }; +}; + +HORRIBLE_TOGGLE_MODIFY(GJBaseGameLayer, LimitedJumpGJBaseGameLayer); \ No newline at end of file diff --git a/src/util/ui/Include.h b/src/util/ui/Include.h index 60a93c3..a9b435f 100644 --- a/src/util/ui/Include.h +++ b/src/util/ui/Include.h @@ -1,6 +1,7 @@ #pragma once #include "Captcha.hpp" +#include "JumpHealthMeter.hpp" #include "LabelArea.hpp" #include "MathQuiz.hpp" #include "RandomAd.hpp" diff --git a/src/util/ui/JumpHealthMeter.hpp b/src/util/ui/JumpHealthMeter.hpp new file mode 100644 index 0000000..851adb3 --- /dev/null +++ b/src/util/ui/JumpHealthMeter.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace horrible { + namespace ui { + class JumpHealthMeter final : public cocos2d::CCNode { + using Callback = geode::Function; + + private: + struct Impl; + std::unique_ptr m_impl; + + protected: + JumpHealthMeter(); + ~JumpHealthMeter(); + + void update(float dt) override; + + bool init(Callback&& cb); + + public: + static JumpHealthMeter* create(Callback&& cb); + + void jump(); + void reset(); + + void playPause(bool play); + }; + }; +}; \ No newline at end of file diff --git a/src/util/ui/src/JumpHealthMeter.cpp b/src/util/ui/src/JumpHealthMeter.cpp new file mode 100644 index 0000000..9256839 --- /dev/null +++ b/src/util/ui/src/JumpHealthMeter.cpp @@ -0,0 +1,110 @@ +#include "../JumpHealthMeter.hpp" + +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +struct JumpHealthMeter::Impl final { + uint8_t jumps = rng::get(15, 7); + float pace = rng::get(50.f, 25.f); + + Label* healthLabel = nullptr; + ProgressBar* healthMeter = nullptr; + + Callback callback = nullptr; + + constexpr auto getColorForJumps(uint8_t remaining) { + if (jumps <= 6) return ""; + if (jumps <= 14) return ""; + + return ""; + }; + + void updateLabel() { + if (healthLabel) healthLabel->setRichText(fmt::format("{}{} Inputs Remaining", getColorForJumps(jumps), jumps)); + }; +}; + +JumpHealthMeter::JumpHealthMeter() : m_impl(std::make_unique()) {}; +JumpHealthMeter::~JumpHealthMeter() {}; + +bool JumpHealthMeter::init(Callback&& cb) { + m_impl->callback = std::move(cb); + + if (!CCNode::init()) return false; + + setContentSize({150.f, 27.5f}); + + m_impl->healthMeter = ProgressBar::create(); + m_impl->healthMeter->setID("meter"); + m_impl->healthMeter->setFillColor(colors::pink); + m_impl->healthMeter->setAnchorPoint(anchor::center); + m_impl->healthMeter->updateProgress(0.f); + + cue::rescaleToMatch(m_impl->healthMeter, getScaledContentWidth() - 8.75f); + + addChildAtPosition(m_impl->healthMeter, Anchor::Bottom, {0.f, 3.75f}); + + m_impl->healthLabel = Label::createRich(fmt::format("{}{} Inputs Remaining", m_impl->getColorForJumps(m_impl->jumps), m_impl->jumps), font::big); + m_impl->healthLabel->setID("label"); + m_impl->healthLabel->setScale(0.375f); + + addChildAtPosition(m_impl->healthLabel, Anchor::Top, {0.f, m_impl->healthLabel->getScaledContentHeight() * -0.625f}); + + scheduleUpdate(); + + return true; +}; + +void JumpHealthMeter::update(float dt) { + if (m_impl->healthMeter) { + auto per = m_impl->healthMeter->getProgress(); + + m_impl->healthMeter->updateProgress(per + (dt * m_impl->pace)); + + if (m_impl->healthMeter->getProgress() >= 100.f) { + if (m_impl->jumps <= 25) m_impl->jumps++; + + m_impl->updateLabel(); + m_impl->healthMeter->updateProgress(0.f); + + m_impl->pace = rng::get(50.f, 25.f); + }; + }; +}; + +void JumpHealthMeter::jump() { + if (m_impl->jumps <= 0) m_impl->callback(); + + m_impl->jumps--; + m_impl->updateLabel(); +}; + +void JumpHealthMeter::reset() { + unscheduleUpdate(); + + m_impl->jumps = rng::get(15, 7); + + if (m_impl->healthMeter) m_impl->healthMeter->updateProgress(0.f); + m_impl->updateLabel(); + + scheduleUpdate(); +}; + +void JumpHealthMeter::playPause(bool play) { + play ? scheduleUpdate() : unscheduleUpdate(); +}; + +JumpHealthMeter* JumpHealthMeter::create(Callback&& cb) { + auto ret = new JumpHealthMeter(); + if (ret->init(std::move(cb))) { + ret->autorelease(); + return ret; + }; + + delete ret; + return nullptr; +}; \ No newline at end of file From aaf1f33f5fd22b9beef987daa425b4317e1e280d Mon Sep 17 00:00:00 2001 From: Cheeseworks Date: Thu, 20 Aug 2026 20:29:30 -0500 Subject: [PATCH 5/5] ready --- src/hooks/InputRoulette.cpp | 95 +++++++++++++++++++++++++++- src/hooks/playerlife/LimitedJump.cpp | 2 +- src/util/ui/Include.h | 1 + src/util/ui/InputMeter.hpp | 29 +++++++++ src/util/ui/src/InputMeter.cpp | 95 ++++++++++++++++++++++++++++ src/util/ui/src/JumpHealthMeter.cpp | 7 +- 6 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 src/util/ui/InputMeter.hpp create mode 100644 src/util/ui/src/InputMeter.cpp diff --git a/src/hooks/InputRoulette.cpp b/src/hooks/InputRoulette.cpp index 5cacbc0..8cdc7b8 100644 --- a/src/hooks/InputRoulette.cpp +++ b/src/hooks/InputRoulette.cpp @@ -2,6 +2,9 @@ #include +#include +#include + using namespace geode::prelude; using namespace horrible::prelude; @@ -9,7 +12,95 @@ using namespace horrible::prelude; static auto const o = Option::create(THIS_ID) ->setName("Input Roulette") - ->setDescription("A meter will show on the side of the screen during gameplay hovering over a bar showing the safe and not-so-safe areas the floating arrow can be on when you press your jump button. If the arrow happens to be in an unsafe area when you press jump, your character has an 80% chance of dying.\ncreated by Cheeseworks") + ->setDescription("A meter will show on the right-hand side of the screen during gameplay indicating the safe and not-so-safe times to press your jump button. If the arrow happens to be in an unsafe area when you press jump, you're at risk. Press while yellow, you have a 50/50 chance of dying, press while red, and you're guaranteed to die. Press while green, and you're safe.\ncreated by Cheeseworks") ->setCategory(category::misc) ->setSillyTier(SillyTier::High) - ->autoRegister(); \ No newline at end of file + ->autoRegister(); + +class $modify(InputRouletteGJBaseGameLayer, GJBaseGameLayer) { + HORRIBLE_DELEGATE_HOOKS(THIS_ID); + + struct Fields final { + InputMeter* inputMeter = nullptr; + }; + + HORRIBLE_SETUP_INTERFACE_FUNC { + auto f = m_fields.self(); + + if (!on) return cue::resetNode(f->inputMeter); + + if (auto pl = PlayLayer::get()) { + f->inputMeter = InputMeter::create(); + f->inputMeter->setID("input-chance-meter"_spr); + f->inputMeter->setScale(1.25f); + f->inputMeter->setZOrder(HIGHEST_Z); + + pl->m_uiLayer->addChildAtPosition(f->inputMeter, Anchor::Right, {-12.5f, 0.f}, false); + }; + }; + + bool init() { + if (!GJBaseGameLayer::init()) return false; + + queueInMainThread([self = WeakRef(this)]() { + if (auto s = self.lock()) s->HORRIBLE_SETUP_INTERFACE_FUNC_NAME(); + }); + + return true; + }; + + void handleButton(bool down, int button, bool isPlayer1) { + GJBaseGameLayer::handleButton(down, button, isPlayer1); + + if (button == 1 && down) { + if (auto pl = PlayLayer::get()) { + auto f = m_fields.self(); + + if (f->inputMeter) { // nesting hell :D + if (f->inputMeter->isDeath()) { + pl->destroyPlayer(pl->m_player1, nullptr); + } else if (f->inputMeter->isDanger()) { + rng::flip() ? pl->destroyPlayer(pl->m_player1, nullptr) : Notification::create("Careful when you jump!", NotificationIcon::Warning, 0.5f)->show(); + }; + }; + }; + }; + }; + + InputMeter* getHealthMeter() noexcept { + if (auto f = m_fields.self()) return f->inputMeter; + return nullptr; + }; +}; + +class $modify(InputRoulettePlayLayer, PlayLayer) { + HORRIBLE_DELEGATE_HOOKS(THIS_ID); + + void destroyPlayer(PlayerObject* player, GameObject* object) { + PlayLayer::destroyPlayer(player, object); + + if (m_playerDied) { + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->playPause(false); + }; + }; + }; + + void resetLevel() { + PlayLayer::resetLevel(); + + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->playPause(true); + }; + }; + + void resetLevelFromStart() { + PlayLayer::resetLevelFromStart(); + + if (auto gjbgl = GJBaseGameLayer::get()) { + if (auto meter = modify_cast(gjbgl)->getHealthMeter()) meter->playPause(true); + }; + }; +}; + +HORRIBLE_TOGGLE_MODIFY(GJBaseGameLayer, InputRouletteGJBaseGameLayer); \ No newline at end of file diff --git a/src/hooks/playerlife/LimitedJump.cpp b/src/hooks/playerlife/LimitedJump.cpp index 11d3ca9..bf43fea 100644 --- a/src/hooks/playerlife/LimitedJump.cpp +++ b/src/hooks/playerlife/LimitedJump.cpp @@ -31,7 +31,7 @@ class $modify(LimitedJumpGJBaseGameLayer, GJBaseGameLayer) { if (auto pl = PlayLayer::get()) { f->jumpMeter = JumpHealthMeter::create([this, meter = f->jumpMeter]() { - if (auto pl = PlayLayer::get()) pl->destroyPlayer(m_player1, nullptr); + if (auto pl = PlayLayer::get()) pl->destroyPlayer(pl->m_player1, nullptr); }); f->jumpMeter->setID("jump-energy-meter"_spr); f->jumpMeter->setZOrder(HIGHEST_Z); diff --git a/src/util/ui/Include.h b/src/util/ui/Include.h index a9b435f..d4037fd 100644 --- a/src/util/ui/Include.h +++ b/src/util/ui/Include.h @@ -1,6 +1,7 @@ #pragma once #include "Captcha.hpp" +#include "InputMeter.hpp" #include "JumpHealthMeter.hpp" #include "LabelArea.hpp" #include "MathQuiz.hpp" diff --git a/src/util/ui/InputMeter.hpp b/src/util/ui/InputMeter.hpp new file mode 100644 index 0000000..4dfaca5 --- /dev/null +++ b/src/util/ui/InputMeter.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace horrible { + namespace ui { + class InputMeter final : public cocos2d::CCNode { + private: + struct Impl; + std::unique_ptr m_impl; + + protected: + InputMeter(); + ~InputMeter(); + + void update(float dt) override; + + bool init() override; + + public: + static InputMeter* create(); + + void playPause(bool play); + + bool isDeath() const noexcept; + bool isDanger() const noexcept; + }; + }; +}; \ No newline at end of file diff --git a/src/util/ui/src/InputMeter.cpp b/src/util/ui/src/InputMeter.cpp new file mode 100644 index 0000000..cb5508d --- /dev/null +++ b/src/util/ui/src/InputMeter.cpp @@ -0,0 +1,95 @@ +#include "../InputMeter.hpp" + +#include + +#include + +using namespace geode::prelude; +using namespace horrible::prelude; + +#define PACE_RANGES 250.f, 175.f + +struct InputMeter::Impl final { + float chance = 0.f; + float pace = rng::get(PACE_RANGES); + + FMODLevelVisualizer* meter = nullptr; + CCSprite* indicator = nullptr; + + constexpr ccColor3B const& getChanceColor(float chance) { + if (chance >= 85.f) return colors::red; + if (chance >= 60.f) return colors::yellow; + + return colors::green; + }; +}; + +InputMeter::InputMeter() : m_impl(std::make_unique()) {}; +InputMeter::~InputMeter() {}; + +bool InputMeter::init() { + if (!CCNode::init()) return false; + + setAnchorPoint(anchor::center); + setContentSize({10.f, 100.f}); + + m_impl->meter = FMODLevelVisualizer::create(); + m_impl->meter->setID("meter"); + m_impl->meter->updateVisualizer(m_impl->chance, m_impl->chance, 1.f); + + addChildAtPosition(m_impl->meter, Anchor::Center, {}, false); + + m_impl->indicator = CCSprite::createWithSpriteFrameName("edit_rightBtn_001.png"); + m_impl->indicator->setID("indicator"); + m_impl->indicator->setScale(0.875f); + m_impl->indicator->setPosition({getScaledContentWidth() * -1.f, m_impl->chance}); + m_impl->indicator->setColor(m_impl->getChanceColor(m_impl->chance)); + + addChild(m_impl->indicator, 1); + + scheduleUpdate(); + + return true; +}; + +void InputMeter::update(float dt) { + if (m_impl->meter && m_impl->indicator) { + m_impl->chance += dt * m_impl->pace; + + m_impl->meter->updateVisualizer(m_impl->chance / 100.f, m_impl->chance / 100.f, dt); + + m_impl->indicator->setPositionY(m_impl->chance); + m_impl->indicator->setColor(m_impl->getChanceColor(m_impl->chance)); + + if (m_impl->chance <= 0.f) { + m_impl->chance = 0.f; + m_impl->pace = rng::get(PACE_RANGES); + } else if (m_impl->chance >= 100.f) { + m_impl->chance = 100.f; + m_impl->pace = rng::get(PACE_RANGES) * -1.f; + }; + }; +}; + +void InputMeter::playPause(bool play) { + play ? scheduleUpdate() : unscheduleUpdate(); +}; + +bool InputMeter::isDeath() const noexcept { + return m_impl->chance >= 85.f; +}; + +bool InputMeter::isDanger() const noexcept { + return m_impl->chance <= 85.f && m_impl->chance >= 60.f; +}; + +InputMeter* InputMeter::create() { + auto ret = new InputMeter(); + if (ret->init()) { + ret->autorelease(); + return ret; + }; + + delete ret; + return nullptr; +}; \ No newline at end of file diff --git a/src/util/ui/src/JumpHealthMeter.cpp b/src/util/ui/src/JumpHealthMeter.cpp index 9256839..1679779 100644 --- a/src/util/ui/src/JumpHealthMeter.cpp +++ b/src/util/ui/src/JumpHealthMeter.cpp @@ -7,9 +7,11 @@ using namespace geode::prelude; using namespace horrible::prelude; +#define PACE_RANGES 50.f, 25.f + struct JumpHealthMeter::Impl final { uint8_t jumps = rng::get(15, 7); - float pace = rng::get(50.f, 25.f); + float pace = rng::get(PACE_RANGES); Label* healthLabel = nullptr; ProgressBar* healthMeter = nullptr; @@ -36,6 +38,7 @@ bool JumpHealthMeter::init(Callback&& cb) { if (!CCNode::init()) return false; + setAnchorPoint(anchor::center); setContentSize({150.f, 27.5f}); m_impl->healthMeter = ProgressBar::create(); @@ -71,7 +74,7 @@ void JumpHealthMeter::update(float dt) { m_impl->updateLabel(); m_impl->healthMeter->updateProgress(0.f); - m_impl->pace = rng::get(50.f, 25.f); + m_impl->pace = rng::get(PACE_RANGES); }; }; };