Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions src/hooks/InputRoulette.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <Util.h>

#include <Geode/Geode.hpp>

#include <Geode/modify/PlayLayer.hpp>
#include <Geode/modify/GJBaseGameLayer.hpp>

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 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 <cy>yellow</c>, you have a 50/50 chance of dying, press while <cr>red</c>, and you're guaranteed to die. Press while <cg>green</c>, and you're safe.\n<cl>created by Cheeseworks</c>")
->setCategory(category::misc)
->setSillyTier(SillyTier::High)
->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<InputRouletteGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->playPause(false);
};
};
};

void resetLevel() {
PlayLayer::resetLevel();

if (auto gjbgl = GJBaseGameLayer::get()) {
if (auto meter = modify_cast<InputRouletteGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->playPause(true);
};
};

void resetLevelFromStart() {
PlayLayer::resetLevelFromStart();

if (auto gjbgl = GJBaseGameLayer::get()) {
if (auto meter = modify_cast<InputRouletteGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->playPause(true);
};
};
};

HORRIBLE_TOGGLE_MODIFY(GJBaseGameLayer, InputRouletteGJBaseGameLayer);
148 changes: 148 additions & 0 deletions src/hooks/obstructive/RealFloating.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <Util.h>

#include <Geode/Geode.hpp>

#include <Geode/modify/PlayLayer.hpp>

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.\n<cl>created by Cheeseworks</c>")
->setCategory(category::obstructive)
->setSillyTier(SillyTier::Medium)
->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);
95 changes: 95 additions & 0 deletions src/hooks/playerlife/LimitedJump.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <Util.h>

#include <Geode/Geode.hpp>

#include <Geode/modify/PlayLayer.hpp>
#include <Geode/modify/GJBaseGameLayer.hpp>

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 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.\n<cl>created by Cheeseworks</c>")
->setCategory(category::playerlife)
->setSillyTier(SillyTier::Medium)
->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(pl->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<LimitedJumpGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->playPause(false);
};
};
};

void resetLevel() {
PlayLayer::resetLevel();

if (auto gjbgl = GJBaseGameLayer::get()) {
if (auto meter = modify_cast<LimitedJumpGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->reset();
};
};

void resetLevelFromStart() {
PlayLayer::resetLevelFromStart();

if (auto gjbgl = GJBaseGameLayer::get()) {
if (auto meter = modify_cast<LimitedJumpGJBaseGameLayer*>(gjbgl)->getHealthMeter()) meter->reset();
};
};
};

HORRIBLE_TOGGLE_MODIFY(GJBaseGameLayer, LimitedJumpGJBaseGameLayer);
2 changes: 2 additions & 0 deletions src/util/ui/Include.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "Captcha.hpp"
#include "InputMeter.hpp"
#include "JumpHealthMeter.hpp"
#include "LabelArea.hpp"
#include "MathQuiz.hpp"
#include "RandomAd.hpp"
Expand Down
29 changes: 29 additions & 0 deletions src/util/ui/InputMeter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <Geode/Geode.hpp>

namespace horrible {
namespace ui {
class InputMeter final : public cocos2d::CCNode {
private:
struct Impl;
std::unique_ptr<Impl> 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;
};
};
};
Loading