Giter Club home page Giter Club logo

customkeybinds's Introduction

Custom Keybinds

Adds support for customizing keybinds in the editor and while playing.

Adding keybinds through other mods

You can use Custom Keybinds as a dependency by declaring it in your mod.json:

{
    "dependencies": [
        {
            "id": "geode.custom-keybinds",
            "version": "v1.1.0",
            "required": true
        }
    ]
}

Adding a new keybind requires two things: registering it when your mod starts up, and handling callbacks when it's fired.

Registering can be done easily using $execute:

$execute {
    using namespace keybinds;

    BindManager::get()->registerBindable({
        // ID, should be prefixed with mod ID
        "backlip"_spr,
        // Name
        "Do a Backflip!",
        // Description, leave empty for none
        "Throw a backflip",
        // Default binds
        { Keybind::create(KEY_Q, Modifier::None) },
        // Category; use slashes for specifying subcategories. See the
        // Category class for default categories
        "My Mod/Awesome Tricks"
    });
}

Now your bind shows up in the UI, and the user can assign their own binds to it. To make the bind also do stuff, you need to add an event listener for InvokeBindEvent. If your bind is global (can be used from anywhere in-game), you should add the event listener in the same $execute block. If the bind is layer-specific, add it in the layer's init:

bool MyLayer::init() {
    ...

    this->template addEventListener<InvokeBindFilter>([=](InvokeBindEvent* event) {
        if (event->isDown()) {
            // do a backflip!
        }
        // Return Propagate if you want other actions with the same bind to
        // also be fired, or Stop if you want to halt propagation
        return ListenerResult::Propagate;
    }, "backflip"_spr);

    ...
}

Adding keybinds to the editor / PlayLayer is the same - just add the keybind to the Editor / Play categories, and hook EditorUI::init or UILayer::init to handle the callback. You can also pass the ID of an existing action to BindManager::registerBindable to place the bind after, if you for example add a new build category and would like it after the Delete Mode bind.

Global keybinds

You can listen for global keybinds via an $execute block:

$execute {
    new EventListener([=](InvokeBindEvent* event) {
    	// do stuff
	return ListenerResult::Propagate;
    }, InvokeBindFilter(nullptr, "event-id"));
}

Programmatically triggering binds

You can invoke a bind by creating and posting an InvokeBindEvent:

InvokeBindEvent("backflip"_spr, true).post();

You can also trigger a specific key combination with PressBindEvent:

PressBindEvent(Keybind::create(KEY_X, Modifier::None)).post();

Adding new input devices

Custom keybinds has been written to support more input devices, in case you want to add first-class support for a gaming toaster to GD.

To add a new input device, first thing you should do is create a new bind class:

using namespace keybinds;

class ToasterBind : public Bind {
public:
    BreadType m_bread;
    float m_temperature;

public:
    static ToasterBind* create(BreadType type, float temperature) {
        auto ret = new ToasterBind();
        ret->autorelease();
        ret->m_bread = type;
        ret->m_temperature = temperature;
        return ret;
    }
    // Parse from JSON
    static ToasterBind* parse(matjson::Value const& json) {
        return ToasterBind::create(
            static_cast<BreadType>(json["bread"].as_int()),
            static_cast<float>(json["temperature"].as_double())
        );
    }
    // Save to JSON
    matjson::Value save() const override {
        return matjson::Object {
            { "bread", static_cast<int>(m_bread) },
            { "temperature", m_temperature },
        };
    }

    // Getters for members
    BreadType getThisBread() const;
    float getTemperature() const;

    // Get the hash for this bind
    size_t getHash() const override {
        return std::hash<float>()(m_temperature) ^ static_cast<size_t>(m_bread);
    }

    // Check if this bind is equal to another
    bool isEqual(Bind* other) const override {
        if (auto o = typeinfo_cast<ToasterBind*>(other)) {
            return m_temperature == o->m_temperature && m_bread == o->m_bread;
        }
        return false;
    }

    // Convert to string
    // By default, the bind is displayed in the UI as just the string in a
    // label. If you want to show something else, override createLabel()
    std::string toString() const override {
        return fmt::format("Temp {} & Bread {}", m_temperature, m_bread);
    }

    // The device this bind is related to
    DeviceID getDeviceID() const override {
        return "toaster"_spr;
    }
};

Registering a new input device is as simple as calling BindManager::attachDevice:

// Device ID should be prefixed with mod ID as usual
BindManager::get()->attachDevice("toaster"_spr, &MyParser::parse);

If the device is detached while the game is running, you can unregister it similarly with BindManager::detachDevice.

Once you have figured out how to get inputs from your awesome toaster, you need to post PressBindEvents:

void XInput_onGamingToasterCallback(double temp, int bread) {
    // Make sure all bind events are only ever posted in the GD thread !!!!
	Loader::get()->queueInMainThread([=] {
        PressBindEvent(ToasterBind::create(static_cast<float>(temp), bread), true).post();
    });
}

customkeybinds's People

Contributors

fleeym avatar matcool avatar hjfod avatar altalk23 avatar cvolton avatar prevter avatar dankmeme01 avatar ninxout avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.