diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua
index 2708a6b55..103176e4c 100644
--- a/docs/game_data/spel2.lua
+++ b/docs/game_data/spel2.lua
@@ -1336,6 +1336,9 @@ function speechbubble_visible() end
function cancel_toast() end
---@return nil
function cancel_speechbubble() end
+---Returns RawInput, a game structure for raw keyboard and controller state
+---@return RawInput
+function get_raw_input() end
---Seed the game prng.
---@param seed integer
---@return nil
@@ -2241,6 +2244,11 @@ do
---@field timer integer
---@field slide_position number
+---@class SomeInput
+ ---@field enabled boolean
+ ---@field input_index integer
+ ---@field buttons integer
+
---@class GameProps
---@field buttons integer @Might be used for some menu inputs not found in buttons_menu
---@field buttons_extra integer @Might be used for some menu inputs not found in buttons_menu
@@ -2248,6 +2256,22 @@ do
---@field buttons_menu MENU_INPUT @Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in ON.PRE_UPDATE.
---@field game_has_focus boolean
---@field modal_open integer
+ ---@field some_input SomeInput[] @size: 12 @Yet another place for some buttons in some random order, too tired to make another enum for them
+ ---@field input_index integer[] @size: 5 @Input index for players 1-4 and maybe for the menu controls. -1: disabled, 0..3: keyboards, 4..7: Xinput, 8..11: other controllers
+
+---@class RawInput
+ ---@field keyboard KeyboardKey[] @size: 112 @State of all keyboard buttons in a random game order as usual
+ ---@field controller ControllerInput[] @size: 12 @State of controller buttons per controller. Zero-based indexing, i.e. use game_props.input_index directly to index this.
+
+---@class KeyboardKey
+ ---@field down boolean @Key is being held
+
+---@class ControllerInput
+ ---@field buttons ControllerButton[] @size: 16
+
+---@class ControllerButton
+ ---@field down boolean @Button is being held
+ ---@field pressed boolean @Button was just pressed down this frame
---@class PRNG
---@field seed fun(self, seed: integer): nil @Same as `seed_prng`
diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md
index 3396d67a8..292738700 100644
--- a/docs/src/includes/_enums.md
+++ b/docs/src/includes/_enums.md
@@ -871,6 +871,8 @@ Name | Data | Description
[POST_LEVEL_DESTRUCTION](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.POST_LEVEL_DESTRUCTION) | ON::POST_LEVEL_DESTRUCTION | Runs right after the current level has been unloaded and all entities destroyed. Runs in pretty much all screens, even ones without entities. The screen has already changed at this point, meaning the screen being destoyed is in state.screen_last.
[PRE_LAYER_DESTRUCTION](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.PRE_LAYER_DESTRUCTION) | ON::PRE_LAYER_DESTRUCTION | Params: [LAYER](#LAYER) layer
Runs right before a layer is unloaded and any entities there destroyed. Runs in pretty much all screens, even ones without entities. The screen has already changed at this point, meaning the screen being destoyed is in state.screen_last.
[POST_LAYER_DESTRUCTION](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.POST_LAYER_DESTRUCTION) | ON::POST_LAYER_DESTRUCTION | Params: [LAYER](#LAYER) layer
Runs right after a layer has been unloaded and any entities there destroyed. Runs in pretty much all screens, even ones without entities. The screen has already changed at this point, meaning the screen being destoyed is in state.screen_last.
+[PRE_PROCESS_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.PRE_PROCESS_INPUT) | ON::PRE_PROCESS_INPUT | Runs right before the game gets input from various devices and writes to a bunch of buttons-variables. Return true to disable all game input completely.
+[POST_PROCESS_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.POST_PROCESS_INPUT) | ON::POST_PROCESS_INPUT | Runs right after the game gets input from various devices and writes to a bunch of buttons-variables. Probably the first chance you have to capture or edit buttons_gameplay or buttons_menu sort of things.
## PARTICLEEMITTER
diff --git a/docs/src/includes/_events.md b/docs/src/includes/_events.md
index 309a790b1..511d91bc8 100644
--- a/docs/src/includes/_events.md
+++ b/docs/src/includes/_events.md
@@ -605,3 +605,17 @@ Params: [LAYER](#LAYER) layer
Runs right before a layer is unloaded and any
> Search script examples for [ON.POST_LAYER_DESTRUCTION](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.POST_LAYER_DESTRUCTION)
Params: [LAYER](#LAYER) layer
Runs right after a layer has been unloaded and any entities there destroyed. Runs in pretty much all screens, even ones without entities. The screen has already changed at this point, meaning the screen being destoyed is in state.screen_last.
+
+## ON.PRE_PROCESS_INPUT
+
+
+> Search script examples for [ON.PRE_PROCESS_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.PRE_PROCESS_INPUT)
+
+Runs right before the game gets input from various devices and writes to a bunch of buttons-variables. Return true to disable all game input completely.
+
+## ON.POST_PROCESS_INPUT
+
+
+> Search script examples for [ON.POST_PROCESS_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ON.POST_PROCESS_INPUT)
+
+Runs right after the game gets input from various devices and writes to a bunch of buttons-variables. Probably the first chance you have to capture or edit buttons_gameplay or buttons_menu sort of things.
diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md
index e26bf8c08..2ace05e95 100644
--- a/docs/src/includes/_globals.md
+++ b/docs/src/includes/_globals.md
@@ -2064,6 +2064,15 @@ Returns: [ImGuiIO](#ImGuiIO) for raw keyboard, mouse and xinput gamepad stuff.
- Note: [Overlunky](#Overlunky)/etc will eat all keys it is currently configured to use, your script will only get leftovers.
- Note: [Gamepad](#Gamepad) is basically [XINPUT_GAMEPAD](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad) but variables are renamed and values are normalized to -1.0..1.0 range.
+### get_raw_input
+
+
+> Search script examples for [get_raw_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_raw_input)
+
+#### [RawInput](#RawInput) get_raw_input()
+
+Returns [RawInput](#RawInput), a game structure for raw keyboard and controller state
+
### mouse_position
diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md
index 302c9f723..2a9c60ab1 100644
--- a/docs/src/includes/_types.md
+++ b/docs/src/includes/_types.md
@@ -652,6 +652,14 @@ tuple<int, int, int, int> | [get_rgba()](https://github.com/spelunky-fyi/o
[Color](#Color) | [set_ucolor(uColor color)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_ucolor) | Changes color based on given [uColor](#Aliases)
[Color](#Color) | [set(Color other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | Copies the values of different [Color](#Color) to this one
+### ControllerButton
+
+
+Type | Name | Description
+---- | ---- | -----------
+bool | [down](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=down) | [Button](#Button) is being held
+bool | [pressed](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pressed) | [Button](#Button) was just pressed down this frame
+
### CutsceneBehavior
@@ -744,6 +752,13 @@ Type | Name | Description
[ENT_TYPE](#ENT_TYPE) | [owner_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=owner_type) |
int | [owner_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=owner_uid) |
+### KeyboardKey
+
+
+Type | Name | Description
+---- | ---- | -----------
+bool | [down](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=down) | Key is being held
+
### Letter
@@ -983,6 +998,13 @@ tuple<float, float> | [split()](https://github.com/spelunky-fyi/overlunky/
## Input types
+### ControllerInput
+
+
+Type | Name | Description
+---- | ---- | -----------
+array<[ControllerButton](#ControllerButton), 16> | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) |
+
### Gamepad
Used in [ImGuiIO](#ImGuiIO)
@@ -1061,6 +1083,23 @@ array<[PlayerSlotSettings](#PlayerSlotSettings), MAX_PLAYERS> | [player_se
[PlayerSlotSettings](#PlayerSlotSettings) | [player_slot_3_settings](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_slot_3_settings) |
[PlayerSlotSettings](#PlayerSlotSettings) | [player_slot_4_settings](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_slot_4_settings) |
+### RawInput
+
+
+Type | Name | Description
+---- | ---- | -----------
+array<[KeyboardKey](#KeyboardKey), 112> | [keyboard](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=keyboard) | State of all keyboard buttons in a random game order as usual
+array<[ControllerInput](#ControllerInput), 12> | [controller](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=controller) | State of controller buttons per controller. Zero-based indexing, i.e. use game_props.input_index directly to index this.
+
+### SomeInput
+
+
+Type | Name | Description
+---- | ---- | -----------
+bool | [enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=enabled) |
+int | [input_index](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=input_index) |
+int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) |
+
## Journal types
@@ -2793,6 +2832,8 @@ int | [buttons_extra](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=b
[MENU_INPUT](#MENU_INPUT) | [buttons_menu](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_menu) | Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in [ON](#ON).PRE_UPDATE.
bool | [game_has_focus](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=game_has_focus) |
int | [modal_open](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=modal_open) |
+array<[SomeInput](#SomeInput), 12> | [some_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=some_input) | Yet another place for some buttons in some random order, too tired to make another enum for them
+array<int, 5> | [input_index](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=input_index) | Input index for players 1-4 and maybe for the menu controls. -1: disabled, 0..3: keyboards, 4..7: Xinput, 8..11: other controllers
### Items
diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp
index ee54ceb8d..139cbc405 100644
--- a/src/game_api/flags.hpp
+++ b/src/game_api/flags.hpp
@@ -767,3 +767,19 @@ std::array renderer_flags1{
"Liquid smoothing",
"unknown",
};
+
+std::array player_inputs{
+ "Disabled",
+ "Keyboard 1",
+ "Keyboard 2",
+ "Keyboard 3",
+ "Keyboard 4",
+ "XInput 1",
+ "XInput 2",
+ "XInput 3",
+ "XInput 4",
+ "Controller 1",
+ "Controller 2",
+ "Controller 3",
+ "Controller 4",
+};
diff --git a/src/game_api/game_manager.hpp b/src/game_api/game_manager.hpp
index 34d4521dd..5c5dcddc6 100644
--- a/src/game_api/game_manager.hpp
+++ b/src/game_api/game_manager.hpp
@@ -92,7 +92,9 @@ struct ControllerInput
struct RawInput
{
+ /// State of all keyboard buttons in a random game order as usual
std::array keyboard;
+ /// State of controller buttons per controller. Zero-based indexing, i.e. use game_props.input_index directly to index this.
std::array controller;
};
@@ -128,8 +130,8 @@ struct GameProps
/// Yet another place for some buttons in some random order, too tired to make another enum for them
std::array some_input;
- std::array input_index; // 0-3 keyboards, 4->controllers, if not used it's -1
- // for example if you just run the game and use OL to warp somewhere immediately there will be no controller setup, so all of those will be -1
+ /// Input index for players 1-4 and maybe for the menu controls. -1: disabled, 0..3: keyboards, 4..7: Xinput, 8..11: other controllers
+ std::array input_index;
// uint8_t padding_probably1[3];
diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp
index 8fb97ab21..9372f7ff5 100644
--- a/src/game_api/script/lua_vm.cpp
+++ b/src/game_api/script/lua_vm.cpp
@@ -2628,6 +2628,10 @@ end
// POST_LAYER_DESTRUCTION
// Params: LAYER layer
// Runs right after a layer has been unloaded and any entities there destroyed. Runs in pretty much all screens, even ones without entities. The screen has already changed at this point, meaning the screen being destoyed is in state.screen_last.
+ // PRE_PROCESS_INPUT
+ // Runs right before the game gets input from various devices and writes to a bunch of buttons-variables. Return true to disable all game input completely.
+ // POST_PROCESS_INPUT
+ // Runs right after the game gets input from various devices and writes to a bunch of buttons-variables. Probably the first chance you have to capture or edit buttons_gameplay or buttons_menu sort of things.
*/
lua.create_named_table(
diff --git a/src/game_api/script/usertypes/game_manager_lua.cpp b/src/game_api/script/usertypes/game_manager_lua.cpp
index 9bf46d21d..f8f248fdb 100644
--- a/src/game_api/script/usertypes/game_manager_lua.cpp
+++ b/src/game_api/script/usertypes/game_manager_lua.cpp
@@ -8,9 +8,10 @@
#include // for move, declval
#include // for min, max
-#include "game_manager.hpp" // for GameManager, JournalPopupUI, GameProps
-#include "memory.hpp" // for memory_read TODO:temp
-#include "screen.hpp" // IWYU pragma: keep
+#include "game_manager.hpp" // for GameManager, JournalPopupUI, GameProps
+#include "memory.hpp" // for memory_read TODO:temp
+#include "screen.hpp" // IWYU pragma: keep
+#include "script/sol_helper.hpp" //
namespace NGM
{
@@ -128,7 +129,9 @@ void register_usertypes(sol::state& lua)
"keyboard",
&RawInput::keyboard,
"controller",
- &RawInput::controller);
+ //&RawInput::controller,
+ sol::property([](RawInput& r)
+ { return ZeroIndexArray(r.controller) /**/; }));
lua.new_usertype(
"KeyboardKey",
"down",
diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp
index 569739eea..885aa3c1e 100644
--- a/src/injected/ui.cpp
+++ b/src/injected/ui.cpp
@@ -1914,6 +1914,11 @@ void quick_start(uint8_t screen, uint8_t world, uint8_t level, uint8_t theme)
g_game_manager->main_menu_music = nullptr;
}
+ if (g_game_manager->game_props->input_index[0] == -1)
+ g_game_manager->game_props->input_index[0] = 0;
+ if (g_game_manager->game_props->input_index[4] == -1)
+ g_game_manager->game_props->input_index[0] = 0;
+
// TODO: this doesn't quite work, loads intro after character selection
g_state->screen_character_select->available_mine_entrances = 4;
}
@@ -8033,6 +8038,8 @@ void render_players()
update_players();
for (auto player : g_players)
{
+ ImGui::Text("%d:", player->input_ptr->player_slot + 1);
+ ImGui::SameLine();
render_uid(player->uid, "players");
}
}
@@ -8281,7 +8288,6 @@ void render_game_props()
{
if (ImGui::MenuItem("Respawn dead players"))
respawn();
- ImGui::TextWrapped("New players spawned here can't be controlled, but can be used to test some things that require multiple players.");
if (ImGui::SliderScalar("Number of players##SetNumPlayers", ImGuiDataType_U8, &g_state->items->player_count, &u8_one, &u8_four, "%d", ImGuiSliderFlags_AlwaysClamp))
{
std::array active_players{false, false, false, false};
@@ -8320,7 +8326,34 @@ void render_game_props()
}
}
}
+ ImGui::SeparatorText("Players");
render_players();
+ ImGui::SeparatorText("Player inputs");
+ ImGui::PushID("PlayerInputIndex");
+ for (unsigned int i = 0; i < 5; ++i)
+ {
+ ImGui::PushID(i);
+ auto label = i < 4 ? fmt::format("Player {}##PlayerInput{}", i + 1, i) : "Menu?";
+ auto index = g_game_manager->game_props->input_index[i];
+ if (ImGui::BeginCombo(label.c_str(), player_inputs[index + 1]))
+ {
+ for (int8_t j = -1; j < 12; j++)
+ {
+ const bool item_selected = (j == index);
+ const char* item_text = player_inputs[j + 1];
+
+ ImGui::PushID(j);
+ if (ImGui::Selectable(item_text, item_selected))
+ g_game_manager->game_props->input_index[i] = j;
+ if (item_selected)
+ ImGui::SetItemDefaultFocus();
+ ImGui::PopID();
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopID();
+ }
+ ImGui::PopID();
endmenu();
}
if (submenu("Level flags"))