From b0b88bde4035f8152206279060d8508f10ff28a3 Mon Sep 17 00:00:00 2001 From: Dregu Date: Wed, 1 Nov 2023 11:22:20 +0200 Subject: [PATCH 01/30] weird ui pause experiments --- src/injected/ui.cpp | 100 +++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index f5cf8ef56..4aeb67ed0 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -248,7 +248,7 @@ static ImFont *font, *bigfont, *hugefont; float g_x = 0, g_y = 0, g_vx = 0, g_vy = 0, g_dx = 0, g_dy = 0, g_zoom = 13.5f, g_hue = 0.63f, g_sat = 0.66f, g_val = 0.66f, g_camera_speed = 1.0f; ImVec2 startpos; int g_held_id = -1, g_last_id = -1, g_over_id = -1, g_current_item = 0, g_filtered_count = 0, g_last_frame = 0, - g_last_gun = 0, g_last_time = -1, g_level_time = -1, g_total_time = -1, g_pause_time = -1, + g_last_gun = 0, g_last_time = -1, g_level_time = -1, g_total_time = -1, g_force_width = 0, g_force_height = 0, g_pause_at = -1, g_hitbox_mask = 0x80BF, g_last_type = -1, g_force_level_width = 4, g_force_level_height = 4; unsigned int g_level_width = 0, grid_x = 0, grid_y = 0, g_pause_type = 0x2; uint8_t g_level = 1, g_world = 1, g_to = 0; @@ -268,7 +268,7 @@ std::vector g_selected_ids; bool set_focus_entity = false, set_focus_world = false, set_focus_zoom = false, set_focus_finder = false, set_focus_uid = false, scroll_to_entity = false, scroll_top = false, click_teleport = false, throw_held = false, paused = false, show_app_metrics = false, lock_entity = false, lock_player = false, freeze_last = false, freeze_level = false, freeze_total = false, hide_ui = false, - enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, pause_updates = true, death_disable = false; + enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, death_disable = false; std::optional quest_yang_state, quest_sisters_state, quest_horsing_state, quest_sparrow_state, quest_tusk_state, quest_beg_state; Entity* g_entity = 0; Entity* g_held_entity = 0; @@ -2102,52 +2102,40 @@ void force_kits() bool toggle_pause() { g_pause_at = -1; - g_pause_time = -1; - if (g_pause_type & 0x40) - { - if (!paused) - { - g_pause_time = g_state->time_startup; - pause_updates = true; - paused = true; - } - else - { - g_pause_time = -1; - pause_updates = false; - paused = false; - } - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); - } - else - { - if (g_state->pause == 0) - { - g_state->pause = (uint8_t)g_pause_type; - paused = true; - } - else - { - g_state->pause = 0; - paused = false; - } - } + g_state->pause ^= ((uint8_t)g_pause_type & ~0x40); + g_ui_scripts["pause"]->execute(fmt::format("exports.type = {} exports.paused = {} exports.skip = false", g_pause_type, paused)); return paused; } void frame_advance() { - if (g_pause_type & 0x40) + if (g_state->pause == 0 && g_pause_at != -1 && (unsigned)g_pause_at <= UI::get_frame_count()) { - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); + g_state->pause = (uint8_t)g_pause_type & ~0x40; + g_pause_at = -1; } - else + if ((g_pause_type & 0x40) == 0) { - if (g_state->pause == 0 && g_pause_at != -1 && (unsigned)g_pause_at <= UI::get_frame_count()) + paused = g_state->pause & (uint8_t)g_pause_type; + } + auto set_type = g_ui_scripts["pause"]->execute("return exports and exports.set_type or nil"); + if (set_type != "") + { + g_ui_scripts["pause"]->execute("exports.set_type = nil"); + try { - g_state->pause = (uint8_t)g_pause_type; - g_pause_at = -1; + g_pause_type = std::stoi(set_type); } + catch (...) + { + }; + } + auto set_paused = g_ui_scripts["pause"]->execute("if exports then return exports.set_paused else return nil end"); + if (set_paused != "") + { + g_ui_scripts["pause"]->execute("exports.set_paused = nil"); + paused = set_paused == "true"; + toggle_pause(); } } @@ -2940,6 +2928,7 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) } else if (pressed("toggle_pause", wParam)) { + paused = !paused; toggle_pause(); } else if (pressed("toggle_hud", wParam)) @@ -2954,10 +2943,9 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { if (g_pause_type & 0x40) { - if (pause_updates) + if (paused) { - g_pause_time = g_state->time_startup + 1; - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); + g_ui_scripts["pause"]->execute("exports.skip = true"); } } else @@ -8753,7 +8741,35 @@ void imgui_init(ImGuiContext*) { SpelunkyScript* script = new SpelunkyScript( R"( -set_callback(function() return pause_at and pause_at ~= -1 and state.time_startup >= pause_at end, ON.PRE_UPDATE) +meta = { + name = "pause", + author = "overlunky", +} +exports = { + type = 0, + paused = false, + skip = false, +} +set_callback(function() + if test_mask(exports.type, 0x40) then + if exports.paused and exports.skip then + exports.skip = false + state.pause = clr_mask(state.pause, exports.type) + return false + elseif exports.paused then + state.pause = set_mask(state.pause, clr_mask(exports.type, 0x40)) + end + return exports.paused + else + if exports.set_paused ~= nil then + if exports.set_paused then + state.pause = set_mask(state.pause, exports.type) + else + state.pause = clr_mask(state.pause, exports.type) + end + end + end +end, ON.PRE_UPDATE) )", "pause", g_SoundManager.get(), From 38121fa2e077ecb06c201c745e13218ea80c3dee Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 02:15:00 +0200 Subject: [PATCH 02/30] now you're thinking with buckets --- src/game_api/bucket.cpp | 15 ++ src/game_api/bucket.hpp | 28 ++++ src/game_api/script/lua_vm.cpp | 2 + src/game_api/script/usertypes/bucket_lua.cpp | 24 +++ src/game_api/script/usertypes/bucket_lua.hpp | 11 ++ src/injected/ui.cpp | 147 ++++++++++++++++--- 6 files changed, 210 insertions(+), 17 deletions(-) create mode 100644 src/game_api/bucket.cpp create mode 100644 src/game_api/bucket.hpp create mode 100644 src/game_api/script/usertypes/bucket_lua.cpp create mode 100644 src/game_api/script/usertypes/bucket_lua.hpp diff --git a/src/game_api/bucket.cpp b/src/game_api/bucket.cpp new file mode 100644 index 000000000..576884360 --- /dev/null +++ b/src/game_api/bucket.cpp @@ -0,0 +1,15 @@ +#include "bucket.hpp" + +#include "containers/game_allocator.hpp" +#include "memory.hpp" + +Bucket* Bucket::get() +{ + static auto bucket_offset = get_address("hundun_door_control") - 12; // another random CCCC with some space for a pointer + auto bucket = reinterpret_cast(memory_read(bucket_offset)); + if ((size_t)bucket != 0xCCCCCCCCCCCCCCCC) + return bucket; + auto new_bucket = new Bucket(); + write_mem_prot(bucket_offset, new_bucket, true); + return new_bucket; +} diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp new file mode 100644 index 000000000..0a603eec2 --- /dev/null +++ b/src/game_api/bucket.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +class Overlunky +{ + public: + std::map options; + std::map set_options; + + std::map keys; + std::unordered_set ignore_keys; + std::unordered_set ignore_keycodes; +}; + +class Bucket +{ + public: + static Bucket* get(); + + using BucketItem = std::variant; + std::unordered_map data; + Overlunky* overlunky{nullptr}; +}; diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 0c12c0439..22c2e1498 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -62,6 +62,7 @@ #include "strings.hpp" // for change_string #include "thread_utils.hpp" // for OnHeapPointer #include "usertypes/behavior_lua.hpp" // for register_usertypes +#include "usertypes/bucket_lua.hpp" // for register_usertypes #include "usertypes/char_state_lua.hpp" // for register_usertypes #include "usertypes/drops_lua.hpp" // for register_usertypes #include "usertypes/entities_activefloors_lua.hpp" // for register_usertypes @@ -295,6 +296,7 @@ end NSteam::register_usertypes(lua); NVTables::register_usertypes(lua); NLogic::register_usertypes(lua); + NBucket::register_usertypes(lua); StateMemory* main_state = State::get().ptr_main(); diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp new file mode 100644 index 000000000..ad6da2d76 --- /dev/null +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -0,0 +1,24 @@ +#include "bucket_lua.hpp" + +#include "bucket.hpp" + +namespace NBucket +{ +void register_usertypes(sol::state& lua) +{ + /// Used to get and set Overlunky settings in Bucket + auto ol_type = lua.new_usertype("Overlunky", sol::no_constructor); + ol_type["options"] = &Overlunky::options; + ol_type["set_options"] = &Overlunky::set_options; + ol_type["keys"] = &Overlunky::keys; + ol_type["ignore_keys"] = &Overlunky::ignore_keys; + ol_type["ignore_keycodes"] = &Overlunky::ignore_keycodes; + + /// Shared memory structure used for Playlunky-Overlunky interoperability + auto bucket_type = lua.new_usertype("Bucket", sol::no_constructor); + bucket_type["data"] = &Bucket::data; + bucket_type["overlunky"] = sol::readonly(&Bucket::overlunky); + + lua["get_bucket"] = Bucket::get; +} +}; // namespace NBucket diff --git a/src/game_api/script/usertypes/bucket_lua.hpp b/src/game_api/script/usertypes/bucket_lua.hpp new file mode 100644 index 000000000..76df1045b --- /dev/null +++ b/src/game_api/script/usertypes/bucket_lua.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace sol +{ +class state; +} // namespace sol + +namespace NBucket +{ +void register_usertypes(sol::state& lua); +}; diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 4aeb67ed0..88f2d61b7 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -29,6 +29,7 @@ #include "olfont.h" +#include "bucket.hpp" #include "console.hpp" #include "custom_types.hpp" #include "entities_chars.hpp" @@ -275,6 +276,7 @@ Entity* g_held_entity = 0; StateMemory* g_state = 0; SaveData* g_save = 0; GameManager* g_game_manager = 0; +Bucket* g_bucket = 0; std::map entity_names; std::map entity_full_names; std::string active_tab = "", activate_tab = "", detach_tab = "", focused_tool = "", g_load_void = ""; @@ -2319,6 +2321,9 @@ void respawn() bool pressed(std::string keyname, WPARAM wParam) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { return false; @@ -2336,11 +2341,14 @@ bool pressed(std::string keyname, WPARAM wParam) { wParam += OL_KEY_ALT; } - return wParam == (unsigned)keycode; + return wParam == (unsigned)keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool pressing(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { return false; @@ -2361,11 +2369,14 @@ bool pressing(std::string keyname) { wParam += OL_KEY_ALT; } - return wParam == (unsigned)keycode && (GetKeyState(key) & 0x8000); + return wParam == (unsigned)keycode && (GetKeyState(key) & 0x8000) && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool clicked(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2406,11 +2417,14 @@ bool clicked(std::string keyname) } } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dblclicked(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2437,11 +2451,14 @@ bool dblclicked(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool held(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2468,11 +2485,14 @@ bool held(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool released(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2499,11 +2519,14 @@ bool released(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dragging(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2530,7 +2553,7 @@ bool dragging(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dragged(std::string keyname) // unused @@ -2646,6 +2669,20 @@ void set_selected_type(uint32_t id) g_last_type = id; } +void toggle_lights() +{ + if (options["lights"] && g_state->illumination) + { + g_state->illumination->flags |= (1U << 24); + } + else if (!options["lights"] && g_state->illumination) + { + g_state->illumination->flags &= ~(1U << 16); + if ((g_state->level_flags & (1U << 17)) > 0) + g_state->illumination->flags &= ~(1U << 24); + } +} + bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { ImGuiContext& g = *GImGui; @@ -2965,16 +3002,7 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) else if (pressed("toggle_lights", wParam)) { options["lights"] = !options["lights"]; - if (options["lights"] && g_state->illumination) - { - g_state->illumination->flags |= (1U << 24); - } - else if (!options["lights"] && g_state->illumination) - { - g_state->illumination->flags &= ~(1U << 16); - if ((g_state->level_flags & (1U << 17)) > 0) - g_state->illumination->flags &= ~(1U << 24); - } + toggle_lights(); } else if (pressed("toggle_ghost", wParam)) { @@ -5406,6 +5434,30 @@ void render_keyconfig() keys = default_keys; save_config(cfgfile); } + if (g_bucket->overlunky->ignore_keys.size()) + { + std::string s; + for (auto const& e : g_bucket->overlunky->ignore_keys) + { + s += e; + s += ','; + } + s.pop_back(); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Warning: The following keys have been disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); + } + if (g_bucket->overlunky->ignore_keycodes.size()) + { + std::string s; + for (auto const& e : g_bucket->overlunky->ignore_keycodes) + { + s += fmt::format("0x{:x}", e); + s += ','; + } + s.pop_back(); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Warning: The following keycodes have been disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); + } ImGui::BeginTable("##keyconfig", 4); ImGui::TableSetupColumn("Tool"); ImGui::TableSetupColumn("Keys"); @@ -9160,6 +9212,64 @@ void check_focus() } } +std::unordered_set legal_options{ + "disable_ghost_timer", + "disable_pause", + "draw_entityinfo", + "draw_grid", + "draw_hitboxes", + "draw_hitboxes_interpolated", + "draw_hotbar", + "draw_hud", + "draw_path", + "draw_script_messages", + "fade_script_messages", + "fly_mode", + "god_mode", + "god_mode_companions", + "hd_cursor", + "keyboard_control", + "lights", + "mouse_control", + "noclip", + "smooth_camera", +}; + +void update_bucket() +{ + g_bucket->overlunky->keys = keys; + g_bucket->overlunky->options = options; + for (auto [k, v] : g_bucket->overlunky->set_options) + { + if (!legal_options.contains(k)) + continue; + options[k] = v; + if (k == "disable_ghost_timer") + { + UI::set_time_ghost_enabled(!options["disable_ghost_timer"]); + UI::set_time_jelly_enabled(!options["disable_ghost_timer"]); + UI::set_cursepot_ghost_enabled(!options["disable_ghost_timer"]); + } + else if (k == "god_mode") + { + UI::godmode(options["god_mode"]); + } + else if (k == "god_mode") + { + UI::godmode_companions(options["god_mode_companions"]); + } + else if (k == "noclip") + { + toggle_noclip(); + } + else if (k == "lights") + { + toggle_lights(); + } + } + g_bucket->overlunky->set_options.clear(); +} + void post_draw() { check_focus(); @@ -9171,6 +9281,7 @@ void post_draw() force_cheats(); force_lights(); frame_advance(); + update_bucket(); } void create_box(std::vector items) @@ -9216,6 +9327,8 @@ void init_ui() g_state = State::get().ptr_main(); g_save = UI::savedata(); g_game_manager = get_game_manager(); + g_bucket = Bucket::get(); + g_bucket->overlunky = new Overlunky(); g_Console = std::make_unique(g_SoundManager.get()); g_Console->set_max_history_size(1000); From 6869b22ef5986325da67517861e9a6844374f9df Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 03:09:58 +0200 Subject: [PATCH 03/30] more bucket options --- src/game_api/bucket.hpp | 7 +++--- src/game_api/script/usertypes/bucket_lua.cpp | 1 + src/injected/ui.cpp | 23 ++++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp index 0a603eec2..0a317d407 100644 --- a/src/game_api/bucket.hpp +++ b/src/game_api/bucket.hpp @@ -6,11 +6,13 @@ #include #include +using BucketItem = std::variant; + class Overlunky { public: - std::map options; - std::map set_options; + std::map options; + std::map set_options; std::map keys; std::unordered_set ignore_keys; @@ -22,7 +24,6 @@ class Bucket public: static Bucket* get(); - using BucketItem = std::variant; std::unordered_map data; Overlunky* overlunky{nullptr}; }; diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp index ad6da2d76..d51b23f3c 100644 --- a/src/game_api/script/usertypes/bucket_lua.cpp +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -19,6 +19,7 @@ void register_usertypes(sol::state& lua) bucket_type["data"] = &Bucket::data; bucket_type["overlunky"] = sol::readonly(&Bucket::overlunky); + /// Returns a shared pointer to the Bucket lua["get_bucket"] = Bucket::get; } }; // namespace NBucket diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 88f2d61b7..b590a6534 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -5443,7 +5443,7 @@ void render_keyconfig() s += ','; } s.pop_back(); - ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Warning: The following keys have been disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "The following keys are disabled by scripts:"); ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); } if (g_bucket->overlunky->ignore_keycodes.size()) @@ -5455,7 +5455,7 @@ void render_keyconfig() s += ','; } s.pop_back(); - ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Warning: The following keycodes have been disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "The following keycodes are disabled by scripts:"); ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); } ImGui::BeginTable("##keyconfig", 4); @@ -9233,17 +9233,27 @@ std::unordered_set legal_options{ "mouse_control", "noclip", "smooth_camera", + "pause_type", }; void update_bucket() { g_bucket->overlunky->keys = keys; - g_bucket->overlunky->options = options; + for (auto [k, v] : options) + { + g_bucket->overlunky->options[k] = options[k]; + } + g_bucket->overlunky->options["pause_type"] = g_pause_type; + for (auto [k, v] : g_bucket->overlunky->set_options) { if (!legal_options.contains(k)) continue; - options[k] = v; + + if (options.contains(k)) + if (auto* val = std::get_if(&v)) + options[k] = *val; + if (k == "disable_ghost_timer") { UI::set_time_ghost_enabled(!options["disable_ghost_timer"]); @@ -9266,6 +9276,11 @@ void update_bucket() { toggle_lights(); } + else if (k == "pause_type") + { + if (auto* val = std::get_if(&v)) + g_pause_type = (uint32_t)*val; + } } g_bucket->overlunky->set_options.clear(); } From 542f3e3c2d8fcae5374d2a6ddbcb1c4821d73e84 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 04:35:32 +0200 Subject: [PATCH 04/30] some documentation --- src/game_api/bucket.hpp | 15 ++- src/game_api/script/usertypes/bucket_lua.cpp | 103 +++++++++++++++++++ 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp index 0a317d407..490a949e4 100644 --- a/src/game_api/bucket.hpp +++ b/src/game_api/bucket.hpp @@ -7,16 +7,23 @@ #include using BucketItem = std::variant; +using KEY = int64_t; class Overlunky { public: + /// Current Overlunky options. Read only. std::map options; + /// Write some select options here to change Overlunky options. Just use the same keys as in options. std::map set_options; - - std::map keys; + /// Current Overlunky key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. + std::map keys; + /// Disable some key bindings in Overlunky, whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. std::unordered_set ignore_keys; - std::unordered_set ignore_keycodes; + /// Disable keys that may or may not be used by Overlunky. You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. + std::unordered_set ignore_keycodes; + /// TODO: Disable Overlunky features. Doesn't do anything yet. + std::unordered_set ignore_features; }; class Bucket @@ -24,6 +31,8 @@ class Bucket public: static Bucket* get(); + /// You can store arbitrary simple values here in Playlunky to be read in on Overlunky script for example. std::unordered_map data; + /// Access Overlunky options here, nil if Overlunky is not loaded. Overlunky* overlunky{nullptr}; }; diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp index d51b23f3c..6e88ab4af 100644 --- a/src/game_api/script/usertypes/bucket_lua.cpp +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -13,6 +13,7 @@ void register_usertypes(sol::state& lua) ol_type["keys"] = &Overlunky::keys; ol_type["ignore_keys"] = &Overlunky::ignore_keys; ol_type["ignore_keycodes"] = &Overlunky::ignore_keycodes; + ol_type["ignore_features"] = &Overlunky::ignore_features; /// Shared memory structure used for Playlunky-Overlunky interoperability auto bucket_type = lua.new_usertype("Bucket", sol::no_constructor); @@ -21,5 +22,107 @@ void register_usertypes(sol::state& lua) /// Returns a shared pointer to the Bucket lua["get_bucket"] = Bucket::get; + + /// Keycodes to be used in various input functions. Most are standard, but the OL_ prefixed keys are specific to Overlunky::ignore_keycodes. + lua.create_named_table("KEY"); + lua["KEY"]["OL_MOD_CTRL"] = 0x100; + lua["KEY"]["OL_MOD_SHIFT"] = 0x200; + lua["KEY"]["OL_MOD_ALT"] = 0x800; + lua["KEY"]["OL_MOUSE_1"] = 0x401; + lua["KEY"]["OL_MOUSE_2"] = 0x402; + lua["KEY"]["OL_MOUSE_3"] = 0x403; + lua["KEY"]["OL_MOUSE_4"] = 0x404; + lua["KEY"]["OL_MOUSE_5"] = 0x405; + lua["KEY"]["OL_MOUSE_WHEEL_DOWN"] = 0x411; + lua["KEY"]["OL_MOUSE_WHEEL_UP"] = 0x412; + + lua["KEY"]["BACKSPACE"] = 0x08; + lua["KEY"]["TAB"] = 0x09; + lua["KEY"]["CLEAR"] = 0x0C; + lua["KEY"]["RETURN"] = 0x0D; + lua["KEY"]["SHIFT"] = 0x10; + lua["KEY"]["CTRL"] = 0x11; + lua["KEY"]["ALT"] = 0x12; + lua["KEY"]["PAUSE"] = 0x13; + lua["KEY"]["CAPS"] = 0x14; + lua["KEY"]["ESCAPE"] = 0x1B; + lua["KEY"]["SPACE"] = 0x20; + lua["KEY"]["PGUP"] = 0x21; + lua["KEY"]["PGDN"] = 0x22; + lua["KEY"]["END"] = 0x23; + lua["KEY"]["HOME"] = 0x24; + lua["KEY"]["LEFT"] = 0x25; + lua["KEY"]["UP"] = 0x26; + lua["KEY"]["RIGHT"] = 0x27; + lua["KEY"]["DOWN"] = 0x28; + lua["KEY"]["SELECT"] = 0x29; + lua["KEY"]["PRINT"] = 0x2A; + lua["KEY"]["EXECUTE"] = 0x2B; + lua["KEY"]["SNAPSHOT"] = 0x2C; + lua["KEY"]["INSERT"] = 0x2D; + lua["KEY"]["DELETE"] = 0x2E; + lua["KEY"]["NUMPAD0"] = 0x60; + lua["KEY"]["NUMPAD1"] = 0x61; + lua["KEY"]["NUMPAD2"] = 0x62; + lua["KEY"]["NUMPAD3"] = 0x63; + lua["KEY"]["NUMPAD4"] = 0x64; + lua["KEY"]["NUMPAD5"] = 0x65; + lua["KEY"]["NUMPAD6"] = 0x66; + lua["KEY"]["NUMPAD7"] = 0x67; + lua["KEY"]["NUMPAD8"] = 0x68; + lua["KEY"]["NUMPAD9"] = 0x69; + lua["KEY"]["MULTIPLY"] = 0x6A; + lua["KEY"]["ADD"] = 0x6B; + lua["KEY"]["SEPARATOR"] = 0x6C; + lua["KEY"]["SUBTRACT"] = 0x6D; + lua["KEY"]["DECIMAL"] = 0x6E; + lua["KEY"]["DIVIDE"] = 0x6F; + lua["KEY"]["F1"] = 0x70; + lua["KEY"]["F2"] = 0x71; + lua["KEY"]["F3"] = 0x72; + lua["KEY"]["F4"] = 0x73; + lua["KEY"]["F5"] = 0x74; + lua["KEY"]["F6"] = 0x75; + lua["KEY"]["F7"] = 0x76; + lua["KEY"]["F8"] = 0x77; + lua["KEY"]["F9"] = 0x78; + lua["KEY"]["F10"] = 0x79; + lua["KEY"]["F11"] = 0x7A; + lua["KEY"]["F12"] = 0x7B; + lua["KEY"]["F13"] = 0x7C; + lua["KEY"]["F14"] = 0x7D; + lua["KEY"]["F15"] = 0x7E; + lua["KEY"]["F16"] = 0x7F; + lua["KEY"]["F17"] = 0x80; + lua["KEY"]["F18"] = 0x81; + lua["KEY"]["F19"] = 0x82; + lua["KEY"]["F20"] = 0x83; + lua["KEY"]["F21"] = 0x84; + lua["KEY"]["F22"] = 0x85; + lua["KEY"]["F23"] = 0x86; + lua["KEY"]["F24"] = 0x87; + lua["KEY"]["LSHIFT"] = 0xA0; + lua["KEY"]["RSHIFT"] = 0xA1; + lua["KEY"]["LCONTROL"] = 0xA2; + lua["KEY"]["RCONTROL"] = 0xA3; + lua["KEY"]["LALT"] = 0xA4; + lua["KEY"]["RALT"] = 0xA5; + lua["KEY"]["PLUS"] = 0xBB; + lua["KEY"]["COMMA"] = 0xBC; + lua["KEY"]["MINUS"] = 0xBD; + lua["KEY"]["PERIOD"] = 0xBE; + lua["KEY"]["OEM_1"] = 0xBA; + lua["KEY"]["OEM_2"] = 0xBF; + lua["KEY"]["OEM_3"] = 0xC0; + lua["KEY"]["OEM_4"] = 0xDB; + lua["KEY"]["OEM_5"] = 0xDC; + lua["KEY"]["OEM_6"] = 0xDD; + lua["KEY"]["OEM_7"] = 0xDE; + lua["KEY"]["OEM_8"] = 0xDF; + lua["KEY"]["OEM_102"] = 0xE2; + for (char c = '0'; c < '9'; c++) + lua["KEY"][std::string{c}] = (int)c; + for (char c = 'A'; c < 'Z'; c++) + lua["KEY"][std::string{c}] = (int)c; } }; // namespace NBucket From 3ad82885eba0b89a27e1c5de5feaa64cbea62737 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 04:40:02 +0200 Subject: [PATCH 05/30] examples --- examples/tas_controls.lua | 152 ++++++++++++++++++++++++++++++++++++++ examples/tas_test.lua | 4 +- examples/tas_tool.lua | 11 +-- src/injected/ui.cpp | 1 + 4 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 examples/tas_controls.lua diff --git a/examples/tas_controls.lua b/examples/tas_controls.lua new file mode 100644 index 000000000..95306c16a --- /dev/null +++ b/examples/tas_controls.lua @@ -0,0 +1,152 @@ +meta = { + name = "Supplemental TAS controls", + description = [[Examples how to override and recreate core Overlunky features. + +This script works in both Overlunky and Playlunky. When Overlunky is loaded, it will disable all matching OL keybinds to make sure they don't interfere with the substitutes implemented in the mod. + +- (Ctrl+Space) toggle FADE+PRE_UPDATE pause +- (Space) frame advance +- (PgUp/PgDn/Home/End) engine frametime control +- (Ctrl+PgUp/PgDn/Home/End) zoom control +- (Ins) toggle level start auto pause +- (Del) toggle skip fades + +]], + author = "Dregu", + version = "1.1" +} + +control = { + type = PAUSE.FADE, + paused = false, + skip = false, + level = false, + fade = false, + frametime = 1/60, + zoom = 13.5, +} + +override_keys = { + KEY.INSERT, + KEY.DELETE, + KEY.HOME, + KEY.END, + KEY.PGUP, + KEY.PGDN, + KEY.SPACE, +} + +set_global_interval(function() + if KEY and get_bucket and get_bucket().overlunky then + local ol = get_bucket().overlunky + for _, v in pairs(override_keys) do + ol.ignore_keycodes:insert(nil, v) + ol.ignore_keycodes:insert(nil, KEY.OL_MOD_CTRL | v) + end + return false + end +end, 60) + + +set_callback(function() + if state.loading > 0 and control.fade then + set_frametime(0) + elseif state.loading > 0 then + set_frametime(1/60) + else + set_frametime(control.frametime) + end + set_frametime_unfocused(0) + if state.logic.ouroboros and get_zoom_level() > 13.5 then + zoom(13.5) + elseif not state.logic.ouroboros then + zoom(control.zoom) + end + if control.fade then + state.fadevalue = 0 + state.fadeout = 0 + state.fadein = 0 + state.loading_black_screen_timer = 0 + end + if control.level and state.loading == 3 and ((not control.fade and state.fadeout == 1) or control.fade) then + control.paused = true + control.skip = false + state.fadevalue = 0 + state.loading = 0 + end + if control.paused and control.skip then + control.skip = false + state.pause = clr_mask(state.pause, control.type) + return false + elseif control.paused then + state.pause = set_mask(state.pause, control.type) + end + return control.paused and not test_flag(state.pause, PAUSE.MENU) +end, ON.PRE_UPDATE) + +set_callback(function(ctx) + local iio = get_io() + if iio.keyctrl then + if iio.keypressed(0x20, false) then --SPACE + control.paused = not control.paused + if control.paused then + state.pause = set_mask(state.pause, control.type) + else + state.pause = clr_mask(state.pause, control.type) + end + elseif iio.keypressed(0x21, true) then --PGUP + control.zoom = control.zoom - 1 + if control.zoom < 1 then control.zoom = 1 end + zoom(control.zoom) + elseif iio.keypressed(0x22, true) then --PGDN + control.zoom = control.zoom + 1 + zoom(control.zoom) + elseif iio.keypressed(0x24, false) then --HOME + control.zoom = 13.5 + zoom(control.zoom) + elseif iio.keypressed(0x23, false) then --END + control.zoom = 0 + zoom(control.zoom) + end + elseif iio.keypressed(0x20, true) and control.paused then --SPACE + control.skip = true + elseif iio.keypressed(0x21, true) then --PGUP + control.frametime = control.frametime / 1.2 + set_frametime(control.frametime) + elseif iio.keypressed(0x22, true) then --PGDN + control.frametime = control.frametime * 1.2 + if control.frametime > 1 then control.frametime = 1 end + set_frametime(control.frametime) + elseif iio.keypressed(0x24, false) then --HOME + control.frametime = 1/60 + set_frametime() + elseif iio.keypressed(0x23, false) then --END + control.frametime = 1/12 + set_frametime(control.frametime) + elseif iio.keypressed(0x2D, false) then --INS + control.level = not control.level + elseif iio.keypressed(0x2E, false) then --DEL + control.fade = not control.fade + end +end, ON.GUIFRAME) + +register_option_callback("tas_controls", nil, function(ctx) + local old_paused = control.paused + control.paused = ctx:win_check("Paused", control.paused) + if control.paused ~= old_paused then + if control.paused then + state.pause = set_mask(state.pause, control.type) + else + state.pause = clr_mask(state.pause, control.type) + end + end + control.skip = ctx:win_check("Frame advance", control.skip) + control.level = ctx:win_check("Pause on level start", control.level) + control.fade = ctx:win_check("Skip fades", control.fade) + control.type = ctx:win_input_int("Pause type", control.type) + local fps = ctx:win_drag_float("FPS", control.frametime > 0 and 1 / control.frametime or 0, 0, 240) + ctx:win_inline() if ctx:win_button("Reset##ResetFPS") then fps = 60 end + control.frametime = fps > 0 and 1 / fps or 0 + control.zoom = ctx:win_drag_float("Zoom", control.zoom, 1, 100) + ctx:win_inline() if ctx:win_button("Reset##ResetZoom") then control.zoom = 13.5 end +end) diff --git a/examples/tas_test.lua b/examples/tas_test.lua index 4c1581d0a..3a32da412 100644 --- a/examples/tas_test.lua +++ b/examples/tas_test.lua @@ -1,6 +1,6 @@ meta.name = "TAS test" -meta.version = "WIP" -meta.description = "Simple test for TASing a seeded run. Works from start to finish, although you might have to wait a frame or two at the start of boss levels because of cutscene skip weirdness. The rerecording stuff is very WIP." +meta.version = "VERY OBSOLETE" +meta.description = "Seriously don't use or even read this." meta.author = "Dregu" local seed = 0 diff --git a/examples/tas_tool.lua b/examples/tas_tool.lua index df99ac0b1..16f2cff09 100644 --- a/examples/tas_tool.lua +++ b/examples/tas_tool.lua @@ -1,13 +1,6 @@ meta.name = "TAS Tool" -meta.version = "WIP" -meta.description = [[DISCLAIMER: -- STILL BUGGY AND NOTHING WORKS PERFECTLY -- SAVES MIGHT NOT WORK BECAUSE OF STUPID JSON SERIALIZER -- SAVES WILL PROBABLY BREAK IN A LATER UPDATE BECAUSE OF ^ -- TELEPORT DESYNC FOR UNKNOWN REASONS IN LATER STAGES LIKE TIAMAT IS VERY POSSIBLE -- SHOPPIE AGGRO AND STATUS EFFECTS ETC ARE NOT FULLY IMPLEMENTED ON RERECORD - -This is the Tool for Tool Assisted Speedrunning Spelunky 2, to be used in seeded runs with Overlunky frame advance features. In a TAS, you record your inputs frame by frame, with the ability to rerecord your steps if you screw up. The resulting inputs are then played back in real time to recreate the perfect inhuman run. Using unsafe mode to save and load files at will. (Saves .json files to game root.)]] +meta.version = "OBSOLETE" +meta.description = "Discontinued in favor of TAS Wizard by Cosine. Still a good script example but a bad TAS tool." meta.author = "Dregu" meta.unsafe = true diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index b590a6534..c08e70b5b 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -8791,6 +8791,7 @@ void imgui_init(ImGuiContext*) if (g_ui_scripts.find("pause") == g_ui_scripts.end()) { + // TODO: Revert this weird example SpelunkyScript* script = new SpelunkyScript( R"( meta = { From f90f2787b23865ff69c4c3c5767ea6a712357501 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 05:23:38 +0200 Subject: [PATCH 06/30] docs --- docs/game_data/lua_enums.txt | 259 +++++++++++++++-- docs/game_data/spel2.lua | 277 +++++++++++++++++-- docs/game_data/tile_codes.txt | 51 ++-- docs/game_data/vtable_sizes.csv | 6 +- docs/generate.py | 1 + docs/generate_emmylua.py | 5 +- docs/parse_source.py | 11 +- docs/src/includes/_enums.md | 11 + docs/src/includes/_globals.md | 11 +- docs/src/includes/_home.md | 1 + docs/src/includes/_types.md | 22 ++ docs/validator.py | 3 +- src/game_api/aliases.hpp | 1 + src/game_api/bucket.hpp | 3 +- src/game_api/script/usertypes/bucket_lua.cpp | 9 +- 15 files changed, 580 insertions(+), 91 deletions(-) diff --git a/docs/game_data/lua_enums.txt b/docs/game_data/lua_enums.txt index f8219d697..6f63cbe45 100644 --- a/docs/game_data/lua_enums.txt +++ b/docs/game_data/lua_enums.txt @@ -327,29 +327,56 @@ DYNAMIC_TEXTURE = { } ENTITY_OVERRIDE = { ACTIVATE = 25, - APPLY_MOVEMENT = 71, + APPLY_FRICTION = 84, CAN_BE_PUSHED = 10, - CHECK_IS_FALLING = 75, + CAN_ENTER = 46, + CAN_JUMP = 37, + CHECK_OUT_OF_BOUNDS = 58, + CLONED_TO = 63, + COLLECT_TREASURE = 70, CREATE_RENDERING_INFO = 1, + CRUSH = 90, DAMAGE = 48, DESTROY = 5, + DROP = 69, DTOR = 0, + ENTER = 42, + ENTER_ATTEMPT = 40, + FALL = 83, FLOOR_UPDATE = 38, + FREEZE = 52, FRICTION = 17, GET_HELD_ENTITY = 22, + HIDE_HUD = 41, INIT = 36, + INITIALIZE = 75, IS_IN_LIQUID = 12, + IS_ON_FIRE = 45, + IS_UNLOCKED = 45, KILL = 3, LEDGE_GRAB = 31, + LIGHT_LEVEL = 44, + LIGHT_ON_FIRE = 53, ON_COLLISION1 = 4, ON_COLLISION2 = 26, - PROCESS_INPUT = 77, + ON_HIT = 49, + PICKED_UP = 80, + PICKED_UP_BY = 68, + PICK_UP = 67, + PROCESS_INPUT = 78, + SET_CURSED = 54, SET_INVISIBLE = 15, + STANDING_ON = 60, + STOMPED_BY = 61, + STOMP_DAMAGE = 43, STOOD_ON = 32, + STUN = 51, + THROWN_BY = 62, TRIGGER_ACTION = 24, UPDATE_STATE_MACHINE = 2, WALKED_OFF = 30, - WALKED_ON = 29 + WALKED_ON = 29, + WEB_COLLISION = 55 } ENT_FLAG = { CAN_BE_STOMPED = 15, @@ -1842,6 +1869,136 @@ JUNGLESISTERS = { PARSNIP_RESCUED = 2, WARNING_ONE_WAY_DOOR = 4 } +KEY = { + ["0"] = 48, + ["1"] = 49, + ["2"] = 50, + ["3"] = 51, + ["4"] = 52, + ["5"] = 53, + ["6"] = 54, + ["7"] = 55, + ["8"] = 56, + A = 65, + ADD = 107, + ALT = 18, + B = 66, + BACKSPACE = 8, + C = 67, + CAPS = 20, + CLEAR = 12, + COMMA = 188, + CTRL = 17, + D = 68, + DECIMAL = 110, + DELETE = 46, + DIVIDE = 111, + DOWN = 40, + E = 69, + END = 35, + ESCAPE = 27, + EXECUTE = 43, + F = 70, + F1 = 112, + F2 = 113, + F3 = 114, + F4 = 115, + F5 = 116, + F6 = 117, + F7 = 118, + F8 = 119, + F9 = 120, + F10 = 121, + F11 = 122, + F12 = 123, + F13 = 124, + F14 = 125, + F15 = 126, + F16 = 127, + F17 = 128, + F18 = 129, + F19 = 130, + F20 = 131, + F21 = 132, + F22 = 133, + F23 = 134, + F24 = 135, + G = 71, + H = 72, + HOME = 36, + I = 73, + INSERT = 45, + J = 74, + K = 75, + L = 76, + LALT = 164, + LCONTROL = 162, + LEFT = 37, + LSHIFT = 160, + M = 77, + MINUS = 189, + MULTIPLY = 106, + N = 78, + NUMPAD0 = 96, + NUMPAD1 = 97, + NUMPAD2 = 98, + NUMPAD3 = 99, + NUMPAD4 = 100, + NUMPAD5 = 101, + NUMPAD6 = 102, + NUMPAD7 = 103, + NUMPAD8 = 104, + NUMPAD9 = 105, + O = 79, + OEM_1 = 186, + OEM_2 = 191, + OEM_3 = 192, + OEM_4 = 219, + OEM_5 = 220, + OEM_6 = 221, + OEM_7 = 222, + OEM_8 = 223, + OEM_102 = 226, + OL_MOD_ALT = 2048, + OL_MOD_CTRL = 256, + OL_MOD_SHIFT = 512, + OL_MOUSE_1 = 1025, + OL_MOUSE_2 = 1026, + OL_MOUSE_3 = 1027, + OL_MOUSE_4 = 1028, + OL_MOUSE_5 = 1029, + OL_MOUSE_WHEEL_DOWN = 1041, + OL_MOUSE_WHEEL_UP = 1042, + P = 80, + PAUSE = 19, + PERIOD = 190, + PGDN = 34, + PGUP = 33, + PLUS = 187, + PRINT = 42, + Q = 81, + R = 82, + RALT = 165, + RCONTROL = 163, + RETURN = 13, + RIGHT = 39, + RSHIFT = 161, + S = 83, + SELECT = 41, + SEPARATOR = 108, + SHIFT = 16, + SNAPSHOT = 44, + SPACE = 32, + SUBTRACT = 109, + T = 84, + TAB = 9, + U = 85, + UP = 38, + V = 86, + W = 87, + X = 88, + Y = 89 +} LAYER = { BACK = 1, BOTH = -128, @@ -1878,6 +2035,36 @@ LIQUID_POOL = { STAGNANT_LAVA = 5, WATER = 1 } +LOGIC = { + APEP = 15, + ARENA_1 = 23, + ARENA_2 = 24, + ARENA_3 = 25, + ARENA_ALIEN_BLAST = 26, + ARENA_LOOSE_BOMBS = 27, + BLACK_MARKET = 21, + BUBBLER = 18, + COG_SACRIFICE = 16, + DICESHOP = 6, + DISCOVERY_INFO = 20, + DUAT_BOSSES = 17, + GHOST = 3, + GHOST_TOAST = 4, + JELLYFISH = 22, + MAGMAMAN_SPAWN = 11, + MOON_CHALLENGE = 8, + OLMEC_CUTSCENE = 13, + OUROBOROS = 1, + PLEASURE_PALACE = 19, + PRE_CHALLENGE = 7, + SPEEDRUN = 2, + STAR_CHALLENGE = 9, + SUN_CHALLENGE = 10, + TIAMAT_CUTSCENE = 14, + TUN_AGGRO = 5, + TUTORIAL = 0, + WATER_BUBBLES = 12 +} MASK = { ACTIVEFLOOR = 128, ANY = 0, @@ -1925,6 +2112,10 @@ ON = { ONLINE_LOBBY = 29, OPTIONS = 5, PLAYER_PROFILE = 6, + POST_LAYER_CREATION = 148, + POST_LAYER_DESTRUCTION = 152, + POST_LEVEL_CREATION = 146, + POST_LEVEL_DESTRUCTION = 150, POST_LEVEL_GENERATION = 112, POST_LOAD_JOURNAL_CHAPTER = 139, POST_LOAD_SCREEN = 136, @@ -1933,6 +2124,10 @@ ON = { PRE_GET_FEAT = 140, PRE_GET_RANDOM_ROOM = 113, PRE_HANDLE_ROOM_TILES = 114, + PRE_LAYER_CREATION = 147, + PRE_LAYER_DESTRUCTION = 151, + PRE_LEVEL_CREATION = 145, + PRE_LEVEL_DESTRUCTION = 149, PRE_LEVEL_GENERATION = 110, PRE_LOAD_JOURNAL_CHAPTER = 138, PRE_LOAD_LEVEL_FILES = 109, @@ -2349,6 +2544,11 @@ QUEST_FLAG = { VAULT_SPAWNED = 3, WADDLER_AGGROED = 10 } +RECURSIVE_MODE = { + EXCLUSIVE = 0, + INCLUSIVE = 1, + NONE = 2 +} RENDER_INFO_OVERRIDE = { DTOR = 0, RENDER = 3 @@ -3212,7 +3412,11 @@ TILE_CODE = { APEP = 379, APEP_LEFT = 380, APEP_RIGHT = 381, + ARROW_METAL = 393, + ARROW_METAL_POISON = 395, ARROW_TRAP = 55, + ARROW_WOODEN = 392, + ARROW_WOODEN_POISON = 394, ASSASSIN = 254, AUTOWALLTORCH = 100, BABYLON_FLOOR = 18, @@ -3232,7 +3436,7 @@ TILE_CODE = { BOMB_BOX = 284, BONE_BLOCK = 6, BONE_KEY = 305, - BOOMBOX = 392, + BOOMBOX = 397, BOOMERANG = 318, BOULDER = 378, BUBBLE_PLATFORM = 369, @@ -3315,7 +3519,7 @@ TILE_CODE = { EGGPLANT_CHILD = 159, EGGPLANT_CROWN = 302, EGGPLANT_DOOR = 179, - EGGPLUP = 407, + EGGPLUP = 412, EGGSAC = 344, EGGSAC_BOTTOM = 348, EGGSAC_LEFT = 345, @@ -3343,19 +3547,19 @@ TILE_CODE = { FORCEFIELD = 217, FORCEFIELD_HORIZONTAL = 333, FORCEFIELD_HORIZONTAL_TOP = 334, - FORCEFIELD_TIMED = 411, + FORCEFIELD_TIMED = 416, FORCEFIELD_TOP = 70, FOUNTAIN_DRAIN = 143, FOUNTAIN_HEAD = 142, FROG = 259, FROG_ORANGE = 260, - FURNITURE_CHAIR_LOOKING_LEFT = 400, - FURNITURE_CHAIR_LOOKING_RIGHT = 401, - FURNITURE_CONSTRUCTION_SIGN = 397, - FURNITURE_DININGTABLE = 402, - FURNITURE_DRESSER = 393, - FURNITURE_SIDETABLE = 404, - FURNITURE_SINGLEBED = 398, + FURNITURE_CHAIR_LOOKING_LEFT = 405, + FURNITURE_CHAIR_LOOKING_RIGHT = 406, + FURNITURE_CONSTRUCTION_SIGN = 402, + FURNITURE_DININGTABLE = 407, + FURNITURE_DRESSER = 398, + FURNITURE_SIDETABLE = 409, + FURNITURE_SINGLEBED = 403, GHIST_DOOR2 = 48, GHIST_PRESENT = 358, GHIST_SHOPKEEPER = 220, @@ -3388,7 +3592,7 @@ TILE_CODE = { HOUYIBOW = 205, HUMPHEAD = 331, HUNDUN = 261, - HUNDUN_SPIKES = 403, + HUNDUN_SPIKES = 408, ICEFLOOR = 152, IDOL = 186, IDOL_FLOOR = 187, @@ -3438,7 +3642,7 @@ TILE_CODE = { MOUNT_AXOLOTL = 329, MOUNT_QILIN = 330, MOUNT_ROCKDOG = 328, - MOVABLE_SPIKES = 391, + MOVABLE_SPIKES = 396, MUMMY = 133, MUSHROOM_BASE = 103, NECROMANCER = 136, @@ -3463,7 +3667,7 @@ TILE_CODE = { PALACE_SIGN = 359, PALACE_TABLE = 167, PALACE_TABLE_TRAY = 168, - PANGXIE = 410, + PANGXIE = 415, PARACHUTE = 297, PASTE = 294, PEN_FLOOR = 224, @@ -3480,9 +3684,9 @@ TILE_CODE = { POT = 108, POTOFGOLD = 90, POWDER_KEG = 54, - POWDER_KEG_TIMED = 412, + POWDER_KEG_TIMED = 417, PRESENT = 332, - PROTO_GENERATOR = 406, + PROTO_GENERATOR = 411, PROTO_SHOPKEEPER = 355, PUNISHBALL = 370, PUNISHBALL_ATTACH = 371, @@ -3492,7 +3696,7 @@ TILE_CODE = { PUNISHBALL_ATTACH_TOP = 387, PUSH_BLOCK = 53, QUICKSAND = 66, - QUILLBACK = 396, + QUILLBACK = 401, REDSKELETON = 244, REGENERATING_BLOCK = 31, ROBOT = 123, @@ -3525,7 +3729,7 @@ TILE_CODE = { SINGLEBED = 73, SISTER = 226, SKELETON = 243, - SKELETON_KEY = 395, + SKELETON_KEY = 400, SKULL = 390, SKULL_DROP_TRAP = 353, SLEEPING_HIREDHAND = 222, @@ -3536,7 +3740,7 @@ TILE_CODE = { SNAP_TRAP = 112, SORCERESS = 134, SPARK_TRAP = 163, - SPARROW = 405, + SPARROW = 410, SPECS = 289, SPIDER = 350, SPIDER_FALLING = 351, @@ -3545,7 +3749,7 @@ TILE_CODE = { SPIKEBALL_NO_BOUNCE = 377, SPIKEBALL_TRAP = 376, SPIKES = 51, - SPIKES_UPSIDEDOWN = 413, + SPIKES_UPSIDEDOWN = 418, SPRING_TRAP = 151, STARTING_EXIT = 44, STICKY_TRAP = 177, @@ -3563,7 +3767,7 @@ TILE_CODE = { TEMPLE_FLOOR = 16, THIEF = 228, THINICE = 153, - THIN_ICE = 394, + THIN_ICE = 399, THORN_VINE = 56, TIAMAT = 172, TIKIMAN = 118, @@ -3576,12 +3780,12 @@ TILE_CODE = { TREASURE_VAULTCHEST = 89, TREE_BASE = 102, TRUE_CROWN = 303, - TUN = 408, + TUN = 413, TURKEY = 104, TUTORIAL_MENU_SIGN = 274, TUTORIAL_SPEEDRUN_SIGN = 273, TV = 81, - UDJAT_CHEST = 399, + UDJAT_CHEST = 404, UDJAT_EYE = 298, UDJAT_KEY = 272, UDJAT_SOCKET = 194, @@ -3591,8 +3795,9 @@ TILE_CODE = { USHABTI = 71, VAMPIRE = 249, VAMPIRE_FLYING = 250, - VAN_HORSING = 409, + VAN_HORSING = 414, VAULT_WALL = 195, + VENOM = 391, VINE = 34, VLAD = 126, VLADS_CAPE = 308, diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index e25d07c15..248069a2f 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1782,6 +1782,9 @@ function set_feat_hidden(feat, hidden) end ---@param description string ---@return nil function change_feat(feat, hidden, name, description) end +---Returns the Bucket of data stored in shared memory between Overlunky and Playlunky +---@return Bucket +function get_bucket() end --## Types do @@ -6255,6 +6258,18 @@ function LogicMagmamanSpawn:remove_spawn(ms) end ---@field y integer ---@field timer integer +---@class Overlunky + ---@field options table @Current Overlunky options. Read only. + ---@field set_options table @Write some select options here to change Overlunky options. Just use the same keys as in options. + ---@field keys table @Current Overlunky key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. + ---@field ignore_keys unordered_Array @Disable some key bindings in Overlunky, whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. + ---@field ignore_keycodes unordered_Array @Disable keys that may or may not be used by Overlunky. You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. + ---@field ignore_features unordered_Array @TODO: Disable Overlunky features. Doesn't do anything yet. + +---@class Bucket + ---@field data table @You can store arbitrary simple values here in Playlunky to be read in on Overlunky script for example. + ---@field overlunky Overlunky @Access Overlunky options here, nil if Overlunky is not loaded. + end --## Static class functions @@ -6761,29 +6776,56 @@ DYNAMIC_TEXTURE = { ---@alias DYNAMIC_TEXTURE integer ENTITY_OVERRIDE = { ACTIVATE = 25, - APPLY_MOVEMENT = 71, + APPLY_FRICTION = 84, CAN_BE_PUSHED = 10, - CHECK_IS_FALLING = 75, + CAN_ENTER = 46, + CAN_JUMP = 37, + CHECK_OUT_OF_BOUNDS = 58, + CLONED_TO = 63, + COLLECT_TREASURE = 70, CREATE_RENDERING_INFO = 1, + CRUSH = 90, DAMAGE = 48, DESTROY = 5, + DROP = 69, DTOR = 0, + ENTER = 42, + ENTER_ATTEMPT = 40, + FALL = 83, FLOOR_UPDATE = 38, + FREEZE = 52, FRICTION = 17, GET_HELD_ENTITY = 22, + HIDE_HUD = 41, INIT = 36, + INITIALIZE = 75, IS_IN_LIQUID = 12, + IS_ON_FIRE = 45, + IS_UNLOCKED = 45, KILL = 3, LEDGE_GRAB = 31, + LIGHT_LEVEL = 44, + LIGHT_ON_FIRE = 53, ON_COLLISION1 = 4, ON_COLLISION2 = 26, - PROCESS_INPUT = 77, + ON_HIT = 49, + PICKED_UP = 80, + PICKED_UP_BY = 68, + PICK_UP = 67, + PROCESS_INPUT = 78, + SET_CURSED = 54, SET_INVISIBLE = 15, + STANDING_ON = 60, + STOMPED_BY = 61, + STOMP_DAMAGE = 43, STOOD_ON = 32, + STUN = 51, + THROWN_BY = 62, TRIGGER_ACTION = 24, UPDATE_STATE_MACHINE = 2, WALKED_OFF = 30, - WALKED_ON = 29 + WALKED_ON = 29, + WEB_COLLISION = 55 } ---@alias ENTITY_OVERRIDE integer ENT_FLAG = { @@ -8293,6 +8335,137 @@ JUNGLESISTERS = { WARNING_ONE_WAY_DOOR = 4 } ---@alias JUNGLESISTERS integer +KEY = { + ["0"] = 48, + ["1"] = 49, + ["2"] = 50, + ["3"] = 51, + ["4"] = 52, + ["5"] = 53, + ["6"] = 54, + ["7"] = 55, + ["8"] = 56, + A = 65, + ADD = 107, + ALT = 18, + B = 66, + BACKSPACE = 8, + C = 67, + CAPS = 20, + CLEAR = 12, + COMMA = 188, + CTRL = 17, + D = 68, + DECIMAL = 110, + DELETE = 46, + DIVIDE = 111, + DOWN = 40, + E = 69, + END = 35, + ESCAPE = 27, + EXECUTE = 43, + F = 70, + F1 = 112, + F2 = 113, + F3 = 114, + F4 = 115, + F5 = 116, + F6 = 117, + F7 = 118, + F8 = 119, + F9 = 120, + F10 = 121, + F11 = 122, + F12 = 123, + F13 = 124, + F14 = 125, + F15 = 126, + F16 = 127, + F17 = 128, + F18 = 129, + F19 = 130, + F20 = 131, + F21 = 132, + F22 = 133, + F23 = 134, + F24 = 135, + G = 71, + H = 72, + HOME = 36, + I = 73, + INSERT = 45, + J = 74, + K = 75, + L = 76, + LALT = 164, + LCONTROL = 162, + LEFT = 37, + LSHIFT = 160, + M = 77, + MINUS = 189, + MULTIPLY = 106, + N = 78, + NUMPAD0 = 96, + NUMPAD1 = 97, + NUMPAD2 = 98, + NUMPAD3 = 99, + NUMPAD4 = 100, + NUMPAD5 = 101, + NUMPAD6 = 102, + NUMPAD7 = 103, + NUMPAD8 = 104, + NUMPAD9 = 105, + O = 79, + OEM_1 = 186, + OEM_2 = 191, + OEM_3 = 192, + OEM_4 = 219, + OEM_5 = 220, + OEM_6 = 221, + OEM_7 = 222, + OEM_8 = 223, + OEM_102 = 226, + OL_MOD_ALT = 2048, + OL_MOD_CTRL = 256, + OL_MOD_SHIFT = 512, + OL_MOUSE_1 = 1025, + OL_MOUSE_2 = 1026, + OL_MOUSE_3 = 1027, + OL_MOUSE_4 = 1028, + OL_MOUSE_5 = 1029, + OL_MOUSE_WHEEL_DOWN = 1041, + OL_MOUSE_WHEEL_UP = 1042, + P = 80, + PAUSE = 19, + PERIOD = 190, + PGDN = 34, + PGUP = 33, + PLUS = 187, + PRINT = 42, + Q = 81, + R = 82, + RALT = 165, + RCONTROL = 163, + RETURN = 13, + RIGHT = 39, + RSHIFT = 161, + S = 83, + SELECT = 41, + SEPARATOR = 108, + SHIFT = 16, + SNAPSHOT = 44, + SPACE = 32, + SUBTRACT = 109, + T = 84, + TAB = 9, + U = 85, + UP = 38, + V = 86, + W = 87, + X = 88, + Y = 89 +} +---@alias KEY integer LAYER = { BACK = 1, BOTH = -128, @@ -8332,6 +8505,37 @@ LIQUID_POOL = { WATER = 1 } ---@alias LIQUID_POOL integer +LOGIC = { + APEP = 15, + ARENA_1 = 23, + ARENA_2 = 24, + ARENA_3 = 25, + ARENA_ALIEN_BLAST = 26, + ARENA_LOOSE_BOMBS = 27, + BLACK_MARKET = 21, + BUBBLER = 18, + COG_SACRIFICE = 16, + DICESHOP = 6, + DISCOVERY_INFO = 20, + DUAT_BOSSES = 17, + GHOST = 3, + GHOST_TOAST = 4, + JELLYFISH = 22, + MAGMAMAN_SPAWN = 11, + MOON_CHALLENGE = 8, + OLMEC_CUTSCENE = 13, + OUROBOROS = 1, + PLEASURE_PALACE = 19, + PRE_CHALLENGE = 7, + SPEEDRUN = 2, + STAR_CHALLENGE = 9, + SUN_CHALLENGE = 10, + TIAMAT_CUTSCENE = 14, + TUN_AGGRO = 5, + TUTORIAL = 0, + WATER_BUBBLES = 12 +} +---@alias LOGIC integer MASK = { ACTIVEFLOOR = 128, ANY = 0, @@ -8380,6 +8584,10 @@ ON = { ONLINE_LOBBY = 29, OPTIONS = 5, PLAYER_PROFILE = 6, + POST_LAYER_CREATION = 148, + POST_LAYER_DESTRUCTION = 152, + POST_LEVEL_CREATION = 146, + POST_LEVEL_DESTRUCTION = 150, POST_LEVEL_GENERATION = 112, POST_LOAD_JOURNAL_CHAPTER = 139, POST_LOAD_SCREEN = 136, @@ -8388,6 +8596,10 @@ ON = { PRE_GET_FEAT = 140, PRE_GET_RANDOM_ROOM = 113, PRE_HANDLE_ROOM_TILES = 114, + PRE_LAYER_CREATION = 147, + PRE_LAYER_DESTRUCTION = 151, + PRE_LEVEL_CREATION = 145, + PRE_LEVEL_DESTRUCTION = 149, PRE_LEVEL_GENERATION = 110, PRE_LOAD_JOURNAL_CHAPTER = 138, PRE_LOAD_LEVEL_FILES = 109, @@ -8813,6 +9025,12 @@ QUEST_FLAG = { WADDLER_AGGROED = 10 } ---@alias QUEST_FLAG integer +RECURSIVE_MODE = { + EXCLUSIVE = 0, + INCLUSIVE = 1, + NONE = 2 +} +---@alias RECURSIVE_MODE integer RENDER_INFO_OVERRIDE = { DTOR = 0, RENDER = 3 @@ -9691,7 +9909,11 @@ TILE_CODE = { APEP = 379, APEP_LEFT = 380, APEP_RIGHT = 381, + ARROW_METAL = 393, + ARROW_METAL_POISON = 395, ARROW_TRAP = 55, + ARROW_WOODEN = 392, + ARROW_WOODEN_POISON = 394, ASSASSIN = 254, AUTOWALLTORCH = 100, BABYLON_FLOOR = 18, @@ -9711,7 +9933,7 @@ TILE_CODE = { BOMB_BOX = 284, BONE_BLOCK = 6, BONE_KEY = 305, - BOOMBOX = 392, + BOOMBOX = 397, BOOMERANG = 318, BOULDER = 378, BUBBLE_PLATFORM = 369, @@ -9794,7 +10016,7 @@ TILE_CODE = { EGGPLANT_CHILD = 159, EGGPLANT_CROWN = 302, EGGPLANT_DOOR = 179, - EGGPLUP = 407, + EGGPLUP = 412, EGGSAC = 344, EGGSAC_BOTTOM = 348, EGGSAC_LEFT = 345, @@ -9822,19 +10044,19 @@ TILE_CODE = { FORCEFIELD = 217, FORCEFIELD_HORIZONTAL = 333, FORCEFIELD_HORIZONTAL_TOP = 334, - FORCEFIELD_TIMED = 411, + FORCEFIELD_TIMED = 416, FORCEFIELD_TOP = 70, FOUNTAIN_DRAIN = 143, FOUNTAIN_HEAD = 142, FROG = 259, FROG_ORANGE = 260, - FURNITURE_CHAIR_LOOKING_LEFT = 400, - FURNITURE_CHAIR_LOOKING_RIGHT = 401, - FURNITURE_CONSTRUCTION_SIGN = 397, - FURNITURE_DININGTABLE = 402, - FURNITURE_DRESSER = 393, - FURNITURE_SIDETABLE = 404, - FURNITURE_SINGLEBED = 398, + FURNITURE_CHAIR_LOOKING_LEFT = 405, + FURNITURE_CHAIR_LOOKING_RIGHT = 406, + FURNITURE_CONSTRUCTION_SIGN = 402, + FURNITURE_DININGTABLE = 407, + FURNITURE_DRESSER = 398, + FURNITURE_SIDETABLE = 409, + FURNITURE_SINGLEBED = 403, GHIST_DOOR2 = 48, GHIST_PRESENT = 358, GHIST_SHOPKEEPER = 220, @@ -9867,7 +10089,7 @@ TILE_CODE = { HOUYIBOW = 205, HUMPHEAD = 331, HUNDUN = 261, - HUNDUN_SPIKES = 403, + HUNDUN_SPIKES = 408, ICEFLOOR = 152, IDOL = 186, IDOL_FLOOR = 187, @@ -9917,7 +10139,7 @@ TILE_CODE = { MOUNT_AXOLOTL = 329, MOUNT_QILIN = 330, MOUNT_ROCKDOG = 328, - MOVABLE_SPIKES = 391, + MOVABLE_SPIKES = 396, MUMMY = 133, MUSHROOM_BASE = 103, NECROMANCER = 136, @@ -9942,7 +10164,7 @@ TILE_CODE = { PALACE_SIGN = 359, PALACE_TABLE = 167, PALACE_TABLE_TRAY = 168, - PANGXIE = 410, + PANGXIE = 415, PARACHUTE = 297, PASTE = 294, PEN_FLOOR = 224, @@ -9959,9 +10181,9 @@ TILE_CODE = { POT = 108, POTOFGOLD = 90, POWDER_KEG = 54, - POWDER_KEG_TIMED = 412, + POWDER_KEG_TIMED = 417, PRESENT = 332, - PROTO_GENERATOR = 406, + PROTO_GENERATOR = 411, PROTO_SHOPKEEPER = 355, PUNISHBALL = 370, PUNISHBALL_ATTACH = 371, @@ -9971,7 +10193,7 @@ TILE_CODE = { PUNISHBALL_ATTACH_TOP = 387, PUSH_BLOCK = 53, QUICKSAND = 66, - QUILLBACK = 396, + QUILLBACK = 401, REDSKELETON = 244, REGENERATING_BLOCK = 31, ROBOT = 123, @@ -10004,7 +10226,7 @@ TILE_CODE = { SINGLEBED = 73, SISTER = 226, SKELETON = 243, - SKELETON_KEY = 395, + SKELETON_KEY = 400, SKULL = 390, SKULL_DROP_TRAP = 353, SLEEPING_HIREDHAND = 222, @@ -10015,7 +10237,7 @@ TILE_CODE = { SNAP_TRAP = 112, SORCERESS = 134, SPARK_TRAP = 163, - SPARROW = 405, + SPARROW = 410, SPECS = 289, SPIDER = 350, SPIDER_FALLING = 351, @@ -10024,7 +10246,7 @@ TILE_CODE = { SPIKEBALL_NO_BOUNCE = 377, SPIKEBALL_TRAP = 376, SPIKES = 51, - SPIKES_UPSIDEDOWN = 413, + SPIKES_UPSIDEDOWN = 418, SPRING_TRAP = 151, STARTING_EXIT = 44, STICKY_TRAP = 177, @@ -10042,7 +10264,7 @@ TILE_CODE = { TEMPLE_FLOOR = 16, THIEF = 228, THINICE = 153, - THIN_ICE = 394, + THIN_ICE = 399, THORN_VINE = 56, TIAMAT = 172, TIKIMAN = 118, @@ -10055,12 +10277,12 @@ TILE_CODE = { TREASURE_VAULTCHEST = 89, TREE_BASE = 102, TRUE_CROWN = 303, - TUN = 408, + TUN = 413, TURKEY = 104, TUTORIAL_MENU_SIGN = 274, TUTORIAL_SPEEDRUN_SIGN = 273, TV = 81, - UDJAT_CHEST = 399, + UDJAT_CHEST = 404, UDJAT_EYE = 298, UDJAT_KEY = 272, UDJAT_SOCKET = 194, @@ -10070,8 +10292,9 @@ TILE_CODE = { USHABTI = 71, VAMPIRE = 249, VAMPIRE_FLYING = 250, - VAN_HORSING = 409, + VAN_HORSING = 414, VAULT_WALL = 195, + VENOM = 391, VINE = 34, VLAD = 126, VLADS_CAPE = 308, diff --git a/docs/game_data/tile_codes.txt b/docs/game_data/tile_codes.txt index d6481afec..c30223c7e 100644 --- a/docs/game_data/tile_codes.txt +++ b/docs/game_data/tile_codes.txt @@ -5,7 +5,7 @@ FIREBUG: 248 PALACE_CHANDELIER: 169 BONE_BLOCK: 6 DOOR2: 45 -PANGXIE: 410 +PANGXIE: 415 NONREPLACEABLE_FLOOR: 5 APEP: 379 GHIST_SHOPKEEPER: 220 @@ -44,7 +44,7 @@ GROWABLE_VINE: 35 USHABTI: 71 BABYLON_FLOOR: 18 VLAD_FLOOR: 22 -VAN_HORSING: 409 +VAN_HORSING: 414 STONE_FLOOR: 15 SPIKEBALL_TRAP: 376 MOUNT_AXOLOTL: 329 @@ -52,7 +52,7 @@ PAGODA_FLOOR: 17 BEE: 257 NONREPLACEABLE_BABYLON_FLOOR: 19 BEEHIVE_FLOOR: 21 -QUILLBACK: 396 +QUILLBACK: 401 EXIT: 42 COG_FLOOR: 23 MOTHERSHIP_FLOOR: 24 @@ -139,7 +139,7 @@ TREASURE: 87 SHOP_WALL: 210 TREASURE_CHEST: 88 GOLDBARS: 91 -FORCEFIELD_TIMED: 411 +FORCEFIELD_TIMED: 416 CRATE: 92 CRATE_BOMBS: 93 BACK_HOVERPACK: 311 @@ -168,6 +168,7 @@ CAVEMAN: 114 CAVEMAN_ASLEEP: 115 SCORPION: 116 CAVEMANBOSS: 117 +ARROW_METAL: 393 BODYGUARD: 230 TIKIMAN: 118 WITCHDOCTOR: 119 @@ -196,11 +197,12 @@ JIANGSHI: 138 EXCALIBUR_STONE: 198 OCTOPUS: 139 HERMITCRAB: 140 -PROTO_GENERATOR: 406 +PROTO_GENERATOR: 411 PUNISHBALL_ATTACH: 371 GIANTCLAM: 141 FOUNTAIN_HEAD: 142 FOUNTAIN_DRAIN: 143 +ARROW_WOODEN: 392 KINGU: 144 SLIDINGWALL_CEILING: 146 ALIEN: 148 @@ -244,7 +246,7 @@ EGGSAC_TOP: 346 MOTHER_STATUE: 178 VLAD_FLYING: 251 EGGPLANT_DOOR: 179 -FURNITURE_SINGLEBED: 398 +FURNITURE_SINGLEBED: 403 GIANT_FROG: 180 JUMPDOG: 181 MINISTER: 182 @@ -307,7 +309,7 @@ FROG: 259 FROG_ORANGE: 260 GHOST: 264 GHOST_MED_HAPPY: 266 -POWDER_KEG_TIMED: 412 +POWDER_KEG_TIMED: 417 GHOST_SMALL_ANGRY: 267 OLMITE_NAKED: 382 GHOST_SMALL_SAD: 268 @@ -327,10 +329,10 @@ ROPE: 281 ROPE_PILE: 282 GUN_SHOTGUN: 314 BOMB_BAG: 283 -FURNITURE_DININGTABLE: 402 +FURNITURE_DININGTABLE: 407 BOMB: 285 SEEDED_RUN_UNLOCKER: 288 -HUNDUN_SPIKES: 403 +HUNDUN_SPIKES: 408 SPECS: 289 PITCHERS_MITT: 291 UDJAT_TARGET: 327 @@ -354,7 +356,7 @@ BACK_POWERPACK: 312 SLIDINGWALL: 375 TELEPORTER: 317 MACHETE: 319 -TUN: 408 +TUN: 413 EXCALIBUR: 320 EXCALIBUR_BROKEN: 321 SHIELD_WOODEN: 325 @@ -363,7 +365,7 @@ SPIKEBALL: 342 MOUNT_QILIN: 330 HUMPHEAD: 331 PRESENT: 332 -FURNITURE_CONSTRUCTION_SIGN: 397 +FURNITURE_CONSTRUCTION_SIGN: 402 FORCEFIELD_HORIZONTAL_TOP: 334 ROPE_UNROLLED: 338 COSMIC_ORB: 339 @@ -388,24 +390,27 @@ CRITTER_CRAB: 364 CRITTER_LOCUST: 365 CRITTER_PENGUIN: 366 CRITTER_DRONE: 368 -SPARROW: 405 +SPARROW: 410 BUBBLE_PLATFORM: 369 PUNISHBALL: 370 GIANT_FLY: 372 -SPIKES_UPSIDEDOWN: 413 +SPIKES_UPSIDEDOWN: 418 FLYING_FISH: 373 CRABMAN: 374 OLMITE_ARMORED: 384 PUNISHBALL_ATTACH_RIGHT: 386 CRITTER_SLIME: 389 +VENOM: 391 SKULL: 390 -MOVABLE_SPIKES: 391 -BOOMBOX: 392 -FURNITURE_DRESSER: 393 -SKELETON_KEY: 395 -THIN_ICE: 394 -UDJAT_CHEST: 399 -FURNITURE_CHAIR_LOOKING_LEFT: 400 -FURNITURE_CHAIR_LOOKING_RIGHT: 401 -FURNITURE_SIDETABLE: 404 -EGGPLUP: 407 +ARROW_WOODEN_POISON: 394 +ARROW_METAL_POISON: 395 +MOVABLE_SPIKES: 396 +BOOMBOX: 397 +FURNITURE_DRESSER: 398 +SKELETON_KEY: 400 +THIN_ICE: 399 +UDJAT_CHEST: 404 +FURNITURE_CHAIR_LOOKING_LEFT: 405 +FURNITURE_CHAIR_LOOKING_RIGHT: 406 +FURNITURE_SIDETABLE: 409 +EGGPLUP: 412 diff --git a/docs/game_data/vtable_sizes.csv b/docs/game_data/vtable_sizes.csv index 0ca8e1b9f..5ddfa2499 100644 --- a/docs/game_data/vtable_sizes.csv +++ b/docs/game_data/vtable_sizes.csv @@ -4,7 +4,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 3,ENT_TYPE_FLOOR_BORDERTILE_OCTOPUS,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 4,ENT_TYPE_FLOOR_GENERIC,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 5,ENT_TYPE_FLOOR_SURFACE,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 -6,ENT_TYPE_FLOOR_SURFACE_HIDDEN,48206,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,ret,0x2287cbd0,0x2287d100,ret 0,ret true,ret,ret,ret,ret,0x228bff80,0x2287d100,ret 0,0x228bfe80,ret,ret,0x228bfe90,0x2292e2c0,0x228bdfb0,ret true,ret 0,ret 0,ret,ret,0x228ee610,0x22997220,0x228bdfb0,0x228c4340,ret 0,ret 0,0x22999730,0x22999750,0x22999aa0,0x228c6ab0,0x22999f90,ret true,ret 0,ret 0,0x22999fc0,ret,0x2299a010,0x228c6ab0,0x228bdfb0,0x2289e280,0x2299bf40,0x2299d580,0x2299d8a0,0x229a3840,0x229a3b00,0xffff817e590b0173,0xffff817f590b0000,0x228a9670 +6,ENT_TYPE_FLOOR_SURFACE_HIDDEN,48206,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,ret,0x2287cbd0,0x2287d100,ret 0,ret true,ret,ret,ret,ret,0x228bff80,0x2287d100,ret 0,0x228bfe80,ret,ret,0x228bfe90,0x2292e2c0,0x228bdfb0,ret true,ret 0,ret 0,ret,ret,0x228ee610,0x22997220,0x228bdfb0,0x228c4340,ret 0,ret 0,0x22999730,0x22999750,0x22999aa0,0x228c6ab0,0x22999f90,ret true,ret 0,ret 0,0x22999fc0,ret,0x2299a010,0x228c6ab0,0x228bdfb0,0x2289e280,0x2299bf40,0x2299d580,0x2299d8a0,0x229a3840,0x229a3b00,0xffff817e87880173,0xffff817f87880000,0x228a9670 7,ENT_TYPE_FLOOR_BASECAMP_SINGLEBED,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 8,ENT_TYPE_FLOOR_BASECAMP_DININGTABLE,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 9,ENT_TYPE_FLOOR_BASECAMP_LONGTABLE,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 @@ -91,7 +91,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 90,ENT_TYPE_FLOOR_FORCEFIELD_TOP,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 91,ENT_TYPE_FLOOR_HORIZONTAL_FORCEFIELD,8946,0x2287aed0,0x227fcd90,0x22974270,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x22974960,0x227fd790,0x227fd930,0x227fd950,0x22974760,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22974240,END OF ENTITY,0x22928d00,ret,0x2287cbd0 92,ENT_TYPE_FLOOR_HORIZONTAL_FORCEFIELD_TOP,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 -93,ENT_TYPE_FLOOR_PEN,48142,0x2287aed0,0x227fcd90,ret,0x22978910,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x22978990,0x90980155e11000c,0x680115c0e030c,0x6801159150009,0x7801159150009,0x9801359130008,0x980135915000a,0xa80135916000b,0xbf7f80095916000b,0xffff8009988b0000 +93,ENT_TYPE_FLOOR_PEN,48142,0x2287aed0,0x227fcd90,ret,0x22978910,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x22978990,0x90980158c8e000c,0x680118a8b030c,0x6801187920009,0x7801187920009,0x9801387900008,0x980138792000a,0xa80138793000b,0xbf7f80098793000b,0xffff8009c7080000 94,ENT_TYPE_FLOOR_TOMB,8906,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x2298cc30,END OF ENTITY,0x22928d00,0x2292c410,0x2288b9e0 95,ENT_TYPE_FLOOR_YAMA_PLATFORM,8866,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x2288d9c0,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 96,ENT_TYPE_FLOOR_EMPRESS_GRAVE,8826,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2288b9e0 @@ -811,7 +811,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 841,ENT_TYPE_BG_EGGSAC_STAINS,2460,0x2287aed0,0x227fcd90,ret,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY 844,ENT_TYPE_LOGICAL_CONSTELLATION,2840,0x2287aed0,0x227fcd90,ret,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f53f0,END OF ENTITY 845,ENT_TYPE_LOGICAL_SHOOTING_STARS_SPAWNER,2802,0x2287aed0,0x227fcd90,0x22a004c0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x22a004b0,END OF ENTITY -846,ENT_TYPE_LOGICAL_DOOR,48710,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY,0xffff8009988b0000,0x403f8009990b0000 +846,ENT_TYPE_LOGICAL_DOOR,48710,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY,0xffff8009c7080000,0x403f8009c7880000 847,ENT_TYPE_LOGICAL_DOOR_AMBIENT_SOUND,2764,0x2287aed0,0x227fcd90,0x229f83c0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f8360,END OF ENTITY 848,ENT_TYPE_LOGICAL_BLACKMARKET_DOOR,2726,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f3af0,END OF ENTITY 849,ENT_TYPE_LOGICAL_ARROW_TRAP_TRIGGER,48672,0x2287aed0,0x227fcd90,0x229f35f0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,0x229f3a20,ret 0,ret,0x229f3a40,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY diff --git a/docs/generate.py b/docs/generate.py index 7890dabb5..4a1fe6a64 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -57,6 +57,7 @@ "ImVec2": "Vec2", "SoundCallbackFunction": "function", "object ": "any ", + "BucketItem": "any", } def replace_all(text): diff --git a/docs/generate_emmylua.py b/docs/generate_emmylua.py index 25eab9678..ac64ac002 100644 --- a/docs/generate_emmylua.py +++ b/docs/generate_emmylua.py @@ -35,16 +35,17 @@ "...va:": "...ent_type:", "set<": "Array<", "span<": "Array<", - + "game_table": "table", "custom_table": "table", "game_Array": "Array", "custom_Array": "Array", - + "const ": "", "EmittedParticlesInfo": "Array", "object": "any", "ImVec2": "Vec2", + "BucketItem": "any", } reFloat = re.compile(r"\bfloat\b") diff --git a/docs/parse_source.py b/docs/parse_source.py index 7d58ff398..2920522f0 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -63,6 +63,7 @@ "../src/game_api/script/usertypes/gui_lua.cpp", "../src/game_api/steam_api.hpp", "../src/game_api/search.hpp", + "../src/game_api/bucket.hpp", ] api_files = [ "../src/game_api/script/script_impl.cpp", @@ -106,6 +107,7 @@ "../src/game_api/script/usertypes/socket_lua.cpp", "../src/game_api/script/usertypes/steam_lua.cpp", "../src/game_api/script/usertypes/logic_lua.cpp", + "../src/game_api/script/usertypes/bucket_lua.cpp", ] vtable_api_files = [ "../src/game_api/script/usertypes/vtables_lua.cpp", @@ -169,8 +171,8 @@ def camel_case_to_snake_case(name): def fix_spaces(thing): thing = thing.strip() - thing = re.sub("\s{2,}", " ", thing) # change double spaces into single - thing = re.sub("(?<=\(|\<)\s", "", thing) # remove spaces after ( or < + thing = re.sub(r"\s{2,}", " ", thing) # change double spaces into single + thing = re.sub(r"(?<=\(|\<)\s", "", thing) # remove spaces after ( or < return thing.replace("*", "").replace("&", "") # remove * and & def getfunc(name): @@ -412,6 +414,9 @@ def run_parse(): (item for item in classes if item["name"] == cpp_type), dict() ) + if "member_funs" not in underlying_cpp_type: + underlying_cpp_type["member_funs"] = {} + if name not in underlying_cpp_type["member_funs"]: underlying_cpp_type["member_funs"][name] = [] @@ -650,7 +655,7 @@ def run_parse(): if var[0].startswith("sol::constructors"): for fun in underlying_cpp_type["member_funs"][cpp_type]: param = fun["param"] - + if cpp_type not in constructors: constructors[cpp_type] = [] constructors[cpp_type].append( diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 78e066c82..1131c43f9 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -609,6 +609,17 @@ Name | Data | Description [GREAT_PARTY_HUH](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=JUNGLESISTERS.GREAT_PARTY_HUH) | 5 | [I_WISH_BROUGHT_A_JACKET](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=JUNGLESISTERS.I_WISH_BROUGHT_A_JACKET) | 6 | +## KEY + + +> Search script examples for [KEY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=KEY) + + +Name | Data | Description +---- | ---- | ----------- +[A](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=KEY.A) | 65 | +...check [lua_enums.txt](game_data/lua_enums.txt)... | | + ## LAYER diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 9543ea915..e1d91a5f1 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1371,6 +1371,15 @@ Force the journal to open on a chapter and entry# when pressing the journal butt Get the current adventure seed pair +### get_bucket + + +> Search script examples for [get_bucket](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_bucket) + +#### [Bucket](#Bucket) get_bucket() + +Returns the [Bucket](#Bucket) of data stored in shared memory between [Overlunky](#Overlunky) and Playlunky + ### get_character_heart_color @@ -1995,7 +2004,7 @@ Returns: [ImGuiIO](#ImGuiIO) for raw keyboard, mouse and xinput gamepad stuff. - Note: The clicked/pressed actions only make sense in `ON.GUIFRAME`. - Note: Lua starts indexing at 1, you need `keysdown[string.byte('A') + 1]` to find the A key. -- Note: Overlunky/etc will eat all keys it is currently configured to use, your script will only get leftovers. +- 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. ### mouse_position diff --git a/docs/src/includes/_home.md b/docs/src/includes/_home.md index 4efbe5a83..122f45c2d 100644 --- a/docs/src/includes/_home.md +++ b/docs/src/includes/_home.md @@ -162,3 +162,4 @@ uColor | int; SHORT_TILE_CODE | int; STRINGID | int; FEAT | int; +KEY | int; diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index e93f99fbf..a987ceeb5 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -595,6 +595,15 @@ Type | Name | Description [BackgroundSound](#BackgroundSound) | [pause_menu](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause_menu) | [BackgroundSound](#BackgroundSound) | [death_transition](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=death_transition) | +### Bucket + +Shared memory structure used for Playlunky-[Overlunky](#Overlunky) interoperability + +Type | Name | Description +---- | ---- | ----------- +map<string, any> | [data](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=data) | You can store arbitrary simple values here in Playlunky to be read in on [Overlunky](#Overlunky) script for example. +[Overlunky](#Overlunky) | [overlunky](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=overlunky) | Access [Overlunky](#Overlunky) options here, nil if [Overlunky](#Overlunky) is not loaded. + ### Color @@ -764,6 +773,19 @@ Type | Name | Description int | [get_state_id()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_state_id) | int | [get_state_id()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_state_id) | Get the `state_id` of a behavior, this is the id that needs to be returned from a behavior's
`get_next_state_id` to enter this state, given that the behavior is added to the movable. +### Overlunky + +Used to get and set [Overlunky](#Overlunky) settings in [Bucket](#Bucket) + +Type | Name | Description +---- | ---- | ----------- +map<string, any> | [options](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=options) | Current [Overlunky](#Overlunky) options. Read only. +map<string, any> | [set_options](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_options) | Write some select options here to change [Overlunky](#Overlunky) options. Just use the same keys as in options. +map<string, [KEY](#KEY)> | [keys](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=keys) | Current [Overlunky](#Overlunky) key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. +set<string> | [ignore_keys](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keys) | Disable some key bindings in [Overlunky](#Overlunky), whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. +set<[KEY](#KEY)> | [ignore_keycodes](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keycodes) | Disable keys that may or may not be used by [Overlunky](#Overlunky). You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. +set<string> | [ignore_features](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_features) | TODO: Disable [Overlunky](#Overlunky) features. Doesn't do anything yet. + ### PRNG [PRNG](#PRNG) (short for Pseudo-Random-Number-Generator) holds 10 128bit wide buffers of memory that are mutated on every generation of a random number. diff --git a/docs/validator.py b/docs/validator.py index 6dd73b139..9cc04739b 100644 --- a/docs/validator.py +++ b/docs/validator.py @@ -50,6 +50,7 @@ "ImVec2": "Vec2", "SoundCallbackFunction": "function", "object ": "any ", + "BucketItem": "any", } def replace_all(text): @@ -120,7 +121,7 @@ def check_types(ret): ret = m.group(2) or "nil" if m or m2: param = (m or m2).group(1) - + check_types(ret) check_types(param) diff --git a/src/game_api/aliases.hpp b/src/game_api/aliases.hpp index e665fc5f3..fdf91a883 100644 --- a/src/game_api/aliases.hpp +++ b/src/game_api/aliases.hpp @@ -22,6 +22,7 @@ using WORLD_SHADER = uint8_t; // NoAlias using SHORT_TILE_CODE = uint8_t; using STRINGID = uint32_t; using FEAT = uint8_t; +using KEY = int64_t; inline constexpr uint8_t MAX_PLAYERS = 4; diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp index 490a949e4..53ce0c2b4 100644 --- a/src/game_api/bucket.hpp +++ b/src/game_api/bucket.hpp @@ -9,9 +9,8 @@ using BucketItem = std::variant; using KEY = int64_t; -class Overlunky +struct Overlunky { - public: /// Current Overlunky options. Read only. std::map options; /// Write some select options here to change Overlunky options. Just use the same keys as in options. diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp index 6e88ab4af..a3ce4b19a 100644 --- a/src/game_api/script/usertypes/bucket_lua.cpp +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -20,11 +20,16 @@ void register_usertypes(sol::state& lua) bucket_type["data"] = &Bucket::data; bucket_type["overlunky"] = sol::readonly(&Bucket::overlunky); - /// Returns a shared pointer to the Bucket + /// Returns the Bucket of data stored in shared memory between Overlunky and Playlunky + // lua["get_bucket"] = []() -> Bucket* lua["get_bucket"] = Bucket::get; /// Keycodes to be used in various input functions. Most are standard, but the OL_ prefixed keys are specific to Overlunky::ignore_keycodes. - lua.create_named_table("KEY"); + lua.create_named_table("KEY" + //, "A", 65 + //, "", ...check__[lua_enums.txt]\[game_data/lua_enums.txt\]... + ); + lua["KEY"]["OL_MOD_CTRL"] = 0x100; lua["KEY"]["OL_MOD_SHIFT"] = 0x200; lua["KEY"]["OL_MOD_ALT"] = 0x800; From e81f958d3e8bff469d65f15e772d025ce5b39ebc Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 05:49:30 +0200 Subject: [PATCH 07/30] update example --- examples/tas_controls.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/tas_controls.lua b/examples/tas_controls.lua index 95306c16a..c2f8e7e73 100644 --- a/examples/tas_controls.lua +++ b/examples/tas_controls.lua @@ -13,7 +13,7 @@ This script works in both Overlunky and Playlunky. When Overlunky is loaded, it ]], author = "Dregu", - version = "1.1" + version = "1.0" } control = { @@ -37,8 +37,8 @@ override_keys = { } set_global_interval(function() - if KEY and get_bucket and get_bucket().overlunky then - local ol = get_bucket().overlunky + local ol = get_bucket().overlunky + if ol then for _, v in pairs(override_keys) do ol.ignore_keycodes:insert(nil, v) ol.ignore_keycodes:insert(nil, KEY.OL_MOD_CTRL | v) @@ -87,45 +87,45 @@ end, ON.PRE_UPDATE) set_callback(function(ctx) local iio = get_io() if iio.keyctrl then - if iio.keypressed(0x20, false) then --SPACE + if iio.keypressed(KEY.SPACE, false) then control.paused = not control.paused if control.paused then state.pause = set_mask(state.pause, control.type) else state.pause = clr_mask(state.pause, control.type) end - elseif iio.keypressed(0x21, true) then --PGUP + elseif iio.keypressed(KEY.PGUP, true) then control.zoom = control.zoom - 1 if control.zoom < 1 then control.zoom = 1 end zoom(control.zoom) - elseif iio.keypressed(0x22, true) then --PGDN + elseif iio.keypressed(KEY.PGDN, true) then control.zoom = control.zoom + 1 zoom(control.zoom) - elseif iio.keypressed(0x24, false) then --HOME + elseif iio.keypressed(KEY.HOME, false) then control.zoom = 13.5 zoom(control.zoom) - elseif iio.keypressed(0x23, false) then --END + elseif iio.keypressed(KEY.END, false) then control.zoom = 0 zoom(control.zoom) end - elseif iio.keypressed(0x20, true) and control.paused then --SPACE + elseif iio.keypressed(KEY.SPACE, true) and control.paused then control.skip = true - elseif iio.keypressed(0x21, true) then --PGUP + elseif iio.keypressed(KEY.PGUP, true) then control.frametime = control.frametime / 1.2 set_frametime(control.frametime) - elseif iio.keypressed(0x22, true) then --PGDN + elseif iio.keypressed(KEY.PGDN, true) then control.frametime = control.frametime * 1.2 if control.frametime > 1 then control.frametime = 1 end set_frametime(control.frametime) - elseif iio.keypressed(0x24, false) then --HOME + elseif iio.keypressed(KEY.HOME, false) then control.frametime = 1/60 set_frametime() - elseif iio.keypressed(0x23, false) then --END + elseif iio.keypressed(KEY.END, false) then control.frametime = 1/12 set_frametime(control.frametime) - elseif iio.keypressed(0x2D, false) then --INS + elseif iio.keypressed(KEY.INSERT, false) then control.level = not control.level - elseif iio.keypressed(0x2E, false) then --DEL + elseif iio.keypressed(KEY.DELETE, false) then control.fade = not control.fade end end, ON.GUIFRAME) From 885bb8ff140155a7b3e0b7567efcb40d20ddb7d6 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 07:36:35 +0200 Subject: [PATCH 08/30] add set_start_level_paused --- docs/game_data/spel2.lua | 5 +++++ docs/src/includes/_globals.md | 9 +++++++++ src/game_api/rpc.cpp | 16 ++++++++++++++++ src/game_api/rpc.hpp | 1 + src/game_api/script/lua_vm.cpp | 3 +++ src/game_api/search.cpp | 9 +++++++++ 6 files changed, 43 insertions(+) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 248069a2f..0ed98c5fd 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1296,6 +1296,10 @@ function create_layer(layer) end ---@param enable boolean ---@return nil function set_level_logic_enabled(enable) end +---Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == PAUSE.FADE on the first frame to do what you want. +---@param enable boolean +---@return nil +function set_start_level_paused(enable) end ---Converts INPUTS to (x, y, BUTTON) ---@param inputs INPUTS ---@return number, number, BUTTON @@ -10994,3 +10998,4 @@ local MAX_PLAYERS = 4 ---@alias SHORT_TILE_CODE integer; ---@alias STRINGID integer; ---@alias FEAT integer; +---@alias KEY integer; diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index e1d91a5f1..92f27d1b0 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1851,6 +1851,15 @@ end, ON.PRE_LOAD_SCREEN) Sets the specified setting temporarily. These values are not saved and might reset to the users real settings if they visit the options menu. (Check example.) All settings are available in unsafe mode and only a smaller subset [SAFE_SETTING](#SAFE_SETTING) by default for [Hud](#Hud) and other visuals. Returns false, if setting failed. +### set_start_level_paused + + +> Search script examples for [set_start_level_paused](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_start_level_paused) + +#### nil set_start_level_paused(bool enable) + +Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == [PAUSE](#PAUSE).FADE on the first frame to do what you want. + ### set_storage_layer diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 3e39554dc..7b7f5d669 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1953,3 +1953,19 @@ void set_death_enabled(bool enable) recover_mem("death_disable"); } } + +void set_start_level_paused(bool enable) +{ + static size_t offset = 0; + if (offset == 0) + { + offset = get_address("unpause_level"); + } + if (offset != 0) + { + if (enable) + write_mem_recoverable("start_level_paused", offset, get_nop(3), true); + else + recover_mem("start_level_paused"); + } +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 3b366f459..6db2a2461 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -139,3 +139,4 @@ void destroy_level(); void create_layer(uint8_t layer); void create_level(); void set_death_enabled(bool enable); +void set_start_level_paused(bool enable); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 22c2e1498..3efbdd23b 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2159,6 +2159,9 @@ end /// Setting to false disables all player logic in SCREEN.LEVEL, mainly the death screen from popping up if all players are dead or missing, but also shop camera zoom and some other small things. lua["set_level_logic_enabled"] = set_death_enabled; + /// Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == PAUSE.FADE on the first frame to do what you want. + lua["set_start_level_paused"] = set_start_level_paused; + /// Converts INPUTS to (x, y, BUTTON) lua["inputs_to_buttons"] = [](INPUTS inputs) -> std::tuple { diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index a566dcbd1..e38b4637b 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1840,6 +1840,15 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, + { + // ^ writes to state.pause on state.loading == 3 + "unpause_level"sv, + PatternCommandBuffer{} + .find_inst("\x44\x0F\x29\xBD\x20\x02\x00\x00"sv) + .find_next_inst("c7 46 14 00 00 00 00 24 fd"_gh) + .offset(9) + .at_exe(), + }, { /* it's a static double, just find something that reads it this+0x08 is clearly some kind of framerate related double, cause it's 60, but don't know what it does From 879bc0b7a4672d3bf0d3c9b15e31cbf587e24135 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 21:12:46 +0200 Subject: [PATCH 09/30] add some renderer flags to ui --- src/game_api/flags.hpp | 11 +++++++++++ src/injected/ui.cpp | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index d13b09060..ee54ceb8d 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -756,3 +756,14 @@ std::array level_chances{ "tallroom", "rewardroom", }; + +std::array renderer_flags1{ + "Floor background shadows", + "Emissive lighting", + "Shockwaves, some shadows", + "unknown", + "unknown", + "unknown", + "Liquid smoothing", + "unknown", +}; diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index c7d15e73e..fd38d2d7f 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -39,6 +39,7 @@ #include "entities_mounts.hpp" #include "file_api.hpp" #include "flags.hpp" +#include "game_api.hpp" #include "game_manager.hpp" #include "illumination.hpp" #include "items.hpp" @@ -8165,6 +8166,13 @@ void render_game_props() } endmenu(); } + if (submenu("Renderer flags")) + { + auto game_api = GameAPI::get(); + ImGui::SeparatorText("Flags 1"); + render_flags(renderer_flags1, &game_api->renderer->flags1); + endmenu(); + } if (submenu("Procedural chances")) { static auto hide_zero = true; From a2477e797d6c7f64233aa2beac73be8109e5512f Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 21:44:25 +0200 Subject: [PATCH 10/30] add camera hack to ui --- src/injected/ui.cpp | 155 ++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 76 deletions(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index fd38d2d7f..ae31eb95c 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -271,7 +271,7 @@ std::vector g_selected_ids; bool set_focus_entity = false, set_focus_world = false, set_focus_zoom = false, set_focus_finder = false, set_focus_uid = false, scroll_to_entity = false, scroll_top = false, click_teleport = false, throw_held = false, paused = false, show_app_metrics = false, lock_entity = false, lock_player = false, freeze_last = false, freeze_level = false, freeze_total = false, hide_ui = false, - enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, death_disable = false; + enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, death_disable = false, camera_hack = false; std::optional quest_yang_state, quest_sisters_state, quest_horsing_state, quest_sparrow_state, quest_tusk_state, quest_beg_state; Entity* g_entity = 0; Entity* g_held_entity = 0; @@ -3962,6 +3962,14 @@ void render_narnia() tooltip("Simulate natural level progression even more."); } +void set_camera_hack(bool enable) +{ + camera_hack = enable; + if (g_ui_scripts.find("camera_hack") == g_ui_scripts.end()) + return; + g_ui_scripts["camera_hack"]->set_enabled(camera_hack); +} + void render_camera() { if (set_focus_zoom) @@ -4040,31 +4048,33 @@ void render_camera() ImGui::InputFloat("Camera Focus Y##CameraFocusY", &g_state->camera->focus_y, 0.2f, 1.0f); ImGui::InputFloat("Camera Real X##CameraRealX", &g_state->camera->adjusted_focus_x, 0.2f, 1.0f); ImGui::InputFloat("Camera Real Y##CameraRealY", &g_state->camera->adjusted_focus_y, 0.2f, 1.0f); - if (submenu("Camera Bounds")) + ImGui::SeparatorText("Camera bounds"); + ImGui::InputFloat("Top##CameraBoundTop", &g_state->camera->bounds_top, 0.2f, 1.0f); + ImGui::InputFloat("Bottom##CameraBoundBottom", &g_state->camera->bounds_bottom, 0.2f, 1.0f); + ImGui::InputFloat("Left##CameraBoundLeft", &g_state->camera->bounds_left, 0.2f, 1.0f); + ImGui::InputFloat("Right##CameraBoundRight", &g_state->camera->bounds_right, 0.2f, 1.0f); + if (ImGui::Checkbox("Enable default camera bounds##CameraBoundsLevel", &enable_camera_bounds)) + set_camera_bounds(enable_camera_bounds); + tooltip("Disable to free the camera bounds in a level.\nAutomatically disabled when dragging.", "camera_reset"); + if (ImGui::Checkbox("Enable camp camera bounds##CameraBoundsCamp", &enable_camp_camera)) { - ImGui::InputFloat("Top##CameraBoundTop", &g_state->camera->bounds_top, 0.2f, 1.0f); - ImGui::InputFloat("Bottom##CameraBoundBottom", &g_state->camera->bounds_bottom, 0.2f, 1.0f); - ImGui::InputFloat("Left##CameraBoundLeft", &g_state->camera->bounds_left, 0.2f, 1.0f); - ImGui::InputFloat("Right##CameraBoundRight", &g_state->camera->bounds_right, 0.2f, 1.0f); - if (ImGui::Checkbox("Enable default camera bounds##CameraBoundsLevel", &enable_camera_bounds)) + UI::set_camp_camera_bounds_enabled(enable_camp_camera); + if (!enable_camp_camera && g_state->screen == 11) { - set_camera_bounds(enable_camera_bounds); + g_state->camera->bounds_left = 0.5; + g_state->camera->bounds_right = 74.5; + g_state->camera->bounds_top = 124.5; + g_state->camera->bounds_bottom = 56.5; } - tooltip("Disable to free the camera bounds in a level.\nAutomatically disabled when dragging.", "camera_reset"); - if (ImGui::Checkbox("Enable camp camera bounds##CameraBoundsCamp", &enable_camp_camera)) - { - UI::set_camp_camera_bounds_enabled(enable_camp_camera); - if (!enable_camp_camera && g_state->screen == 11) - { - g_state->camera->bounds_left = 0.5; - g_state->camera->bounds_right = 74.5; - g_state->camera->bounds_top = 124.5; - g_state->camera->bounds_bottom = 56.5; - } - } - tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); - endmenu(); } + tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); + if (ImGui::Checkbox("Follow focused entity absolutely##CameraForcePlayer", &camera_hack)) + { + set_camera_hack(camera_hack); + enable_camera_bounds = !camera_hack; + set_camera_bounds(enable_camera_bounds); + } + tooltip("Enable to remove camera bounds and always center the entity instantly without rubberbanding."); } void render_arrow() @@ -8742,6 +8752,20 @@ void imgui_pre_init(ImGuiContext*) io.ConfigViewportsNoTaskBarIcon = true; } +void add_ui_script(std::string name, bool enable, std::string code) +{ + if (g_ui_scripts.find(name) == g_ui_scripts.end()) + { + SpelunkyScript* script = new SpelunkyScript( + code, + name, + g_SoundManager.get(), + g_Console.get(), + enable); + g_ui_scripts[name] = std::unique_ptr(script); + } +} + void imgui_init(ImGuiContext*) { if (std::setlocale(LC_CTYPE, ".UTF-8") == nullptr) @@ -8771,11 +8795,7 @@ void imgui_init(ImGuiContext*) windows["tool_texture"] = new Window({"Texture viewer", is_tab_detached("tool_texture"), is_tab_open("tool_texture")}); // windows["tool_sound"] = new Window({"Sound player", is_tab_detached("tool_sound"), is_tab_open("tool_sound")}); - if (g_ui_scripts.find("pause") == g_ui_scripts.end()) - { - // TODO: Revert this weird example - SpelunkyScript* script = new SpelunkyScript( - R"( + add_ui_script("pause", true, R"( meta = { name = "pause", author = "overlunky", @@ -8804,38 +8824,27 @@ set_callback(function() end end end -end, ON.PRE_UPDATE) - )", - "pause", - g_SoundManager.get(), - g_Console.get(), - true); - g_ui_scripts["pause"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("dark") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "set_callback(function() state.level_flags = set_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)", - "dark", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["dark"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("light") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "set_callback(function() state.level_flags = clr_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)", - "light", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["light"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("void") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - R"( +end, ON.PRE_UPDATE))"); + add_ui_script("camera_hack", false, R"( +lastpos = Vec2:new() +set_callback(function() + local e = get_entity(state.camera.focused_entity_uid) + if not e then return end + local x,y,l = get_render_position(e.uid) + local pos = Vec2:new(x, y) + if pos:distance_to(lastpos) > 1 then x,y,l = get_position(e.uid) end + lastpos = pos + state.camera.focus_x, state.camera.focus_y = x, y + state.camera.adjusted_focus_x, state.camera.adjusted_focus_y = x, y + state.camera.calculated_focus_x, state.camera.calculated_focus_y = x, y + state.camera.bounds_top = math.huge + state.camera.bounds_bottom = -math.huge + state.camera.bounds_left = -math.huge + state.camera.bounds_right = math.huge +end, ON.RENDER_PRE_GAME))"); + add_ui_script("dark", false, "set_callback(function() state.level_flags = set_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); + add_ui_script("light", false, "set_callback(function() state.level_flags = clr_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); + add_ui_script("void", false, R"( qflags = {2,3,5,17,18,19,25,26,27} disable_virts = {2,3,4,5,6,7,8,9,10,11,12,15,16,17,18,19,20} hooks = {} @@ -8903,23 +8912,8 @@ end set_callback(init_hooks, ON.LOAD) set_callback(init_hooks, ON.SCRIPT_ENABLE) set_callback(clear_hooks, ON.SCRIPT_DISABLE) -)", - "void", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["void"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("level_size") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "", - "level_size", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["level_size"] = std::unique_ptr(script); - } +)"); + add_ui_script("level_size", false, ""); } void imgui_draw() @@ -9217,6 +9211,7 @@ std::unordered_set legal_options{ "noclip", "smooth_camera", "pause_type", + "camera_hack", }; void update_bucket() @@ -9264,6 +9259,14 @@ void update_bucket() if (auto* val = std::get_if(&v)) g_pause_type = (uint32_t)*val; } + else if (k == "camera_hack") + { + if (auto* val = std::get_if(&v)) + { + camera_hack = (uint32_t)*val; + set_camera_hack(camera_hack); + } + } } g_bucket->overlunky->set_options.clear(); } From c861dbbb5989c899a328831d9737381d6c993a5d Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 21:57:14 +0200 Subject: [PATCH 11/30] fix some camera things --- src/injected/ui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index ae31eb95c..f8cf3587b 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -4073,6 +4073,8 @@ void render_camera() set_camera_hack(camera_hack); enable_camera_bounds = !camera_hack; set_camera_bounds(enable_camera_bounds); + enable_camp_camera = !camera_hack; + UI::set_camp_camera_bounds_enabled(enable_camp_camera); } tooltip("Enable to remove camera bounds and always center the entity instantly without rubberbanding."); } @@ -8834,6 +8836,8 @@ set_callback(function() local pos = Vec2:new(x, y) if pos:distance_to(lastpos) > 1 then x,y,l = get_position(e.uid) end lastpos = pos + x = x + state.camera.focus_offset_x + y = y + state.camera.vertical_pan + state.camera.focus_offset_y state.camera.focus_x, state.camera.focus_y = x, y state.camera.adjusted_focus_x, state.camera.adjusted_focus_y = x, y state.camera.calculated_focus_x, state.camera.calculated_focus_y = x, y From 9439ccc5f43e8fdbb9902c43b34316be1f3d04ad Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 22:16:05 +0200 Subject: [PATCH 12/30] add zoom_reset to set proper shop zoom back --- src/game_api/script/lua_vm.cpp | 5 ++++- src/game_api/state.cpp | 17 +++++++++++------ src/game_api/state.hpp | 1 + src/injected/ui.cpp | 19 ++++++++++++++++--- src/injected/ui_util.cpp | 4 ++++ src/injected/ui_util.hpp | 1 + 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index c56c96e17..8b304d9b9 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -966,9 +966,12 @@ end /// Set level flag 18 on post room generation instead, to properly force every level to dark lua["force_dark_level"] = [](bool g) { State::get().darkmode(g); }; - /// Set the zoom level used in levels and shops. 13.5 is the default. + /// Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. lua["zoom"] = [](float level) { State::get().zoom(level); }; + /// Reset the default zoom levels for all areas and sets current zoom level to 13.5. + lua["zoom_reset"] = []() + { State::get().zoom_reset(); }; /// Pause/unpause the game. /// This is just short for `state.pause == 32`, but that produces an audio bug /// I suggest `state.pause == 2`, but that won't run any callback, `state.pause == 16` will do the same but [set_global_interval](#set_global_interval) will still work diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 5873fedc1..ad101da9d 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -393,24 +393,29 @@ void State::zoom(float level) } } - const auto level_str = to_le_bytes(level); - static const auto zoom_level = get_address("default_zoom_level"); static const auto zoom_shop = get_address("default_zoom_level_shop"); static const auto zoom_camp = get_address("default_zoom_level_camp"); static const auto zoom_telescope = get_address("default_zoom_level_telescope"); // overwrite the defaults - write_mem_prot(zoom_level, level_str, true); - write_mem_prot(zoom_shop, level_str, true); - write_mem_prot(zoom_camp, level_str, true); - write_mem_prot(zoom_telescope, level_str, true); + write_mem_recoverable("zoom", zoom_level, level, true); + write_mem_recoverable("zoom", zoom_shop, level, true); + write_mem_recoverable("zoom", zoom_camp, level, true); + write_mem_recoverable("zoom", zoom_telescope, level, true); // overwrite the current value auto game_api = GameAPI::get(); game_api->set_zoom(std::nullopt, level); } +void State::zoom_reset() +{ + recover_mem("zoom"); + auto game_api = GameAPI::get(); + game_api->set_zoom(std::nullopt, 13.5f); +} + void StateMemory::force_current_theme(uint32_t t) { if (t > 0 && t < 19) diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index 2f694a1de..05822b9cf 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -338,6 +338,7 @@ struct State void darkmode(bool g); void zoom(float level); + void zoom_reset(); static std::pair click_position(float x, float y); static std::pair screen_position(float x, float y); diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index f8cf3587b..1e8b5e629 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -157,7 +157,8 @@ std::map default_keys{ {"switch_ui", VK_F12}, {"zoom_in", OL_KEY_CTRL | VK_OEM_COMMA}, {"zoom_out", OL_KEY_CTRL | VK_OEM_PERIOD}, - {"zoom_default", OL_KEY_CTRL | '2'}, + {"zoom_reset", OL_KEY_CTRL | '1'}, + {"zoom_2x", OL_KEY_CTRL | '2'}, {"zoom_3x", OL_KEY_CTRL | '3'}, {"zoom_4x", OL_KEY_CTRL | '4'}, {"zoom_5x", OL_KEY_CTRL | '5'}, @@ -2886,9 +2887,14 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) g_zoom -= 1.0f; set_zoom(); } - else if (pressed("zoom_default", wParam)) + else if (pressed("zoom_reset", wParam)) { g_zoom = 13.5f; + UI::zoom_reset(); + } + else if (pressed("zoom_2x", wParam)) + { + g_zoom = 16.324f; set_zoom(); } else if (pressed("zoom_3x", wParam)) @@ -3987,9 +3993,16 @@ void render_camera() if (ImGui::Button("Default")) { g_zoom = 13.5f; + UI::zoom_reset(); + } + tooltip("Default zoom level.", "zoom_reset"); + ImGui::SameLine(); + if (ImGui::Button("2x")) + { + g_zoom = 16.324f; set_zoom(); } - tooltip("Default zoom level.", "zoom_default"); + tooltip("2 room wide zoom level.", "zoom_2x"); ImGui::SameLine(); if (ImGui::Button("3x")) { diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 8a31c699c..bef25b70e 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -50,6 +50,10 @@ void UI::zoom(float level) { State::get().zoom(level); } +void UI::zoom_reset() +{ + State::get().zoom_reset(); +} uint32_t UI::get_frame_count() { return State::get().get_frame_count(); diff --git a/src/injected/ui_util.hpp b/src/injected/ui_util.hpp index 224cee8f2..36011dd2f 100644 --- a/src/injected/ui_util.hpp +++ b/src/injected/ui_util.hpp @@ -40,6 +40,7 @@ class UI static void death_enabled(bool g); static std::pair click_position(float x, float y); static void zoom(float level); + static void zoom_reset(); static uint32_t get_frame_count(); static void warp(uint8_t world, uint8_t level, uint8_t theme); static void transition(uint8_t world, uint8_t level, uint8_t theme); From 70601a67203aace66983f717b30739bf6efcd62b Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 22:24:41 +0200 Subject: [PATCH 13/30] add bomb fuse to hover tooltip --- src/injected/ui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 1e8b5e629..ee8864c04 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -4441,6 +4441,11 @@ std::string entity_tooltip(Entity* hovered) if (chest->leprechaun) coords += " (LEPRECHAUN)"; } + else if (hovered->type->id == to_id("ENT_TYPE_ITEM_BOMB")) + { + auto bomb = hovered->as(); + coords += fmt::format(" ({} FUSE)", 150 - bomb->idle_counter); + } else if (hovered->type->search_flags & 7) { auto ent = hovered->as(); From 32c03521f0630523dd9ddd925ab2b1bdc067550f Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 22:57:26 +0200 Subject: [PATCH 14/30] add script text filter in ui --- src/injected/ui.cpp | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index ee8864c04..5d92d07ac 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -5984,6 +5984,11 @@ void render_script_files() int num = 0; for (auto file : g_script_files) { + auto id = file.string(); + std::replace(id.begin(), id.end(), '\\', '/'); + if (g_scripts.count(id) > 0) + continue; + ImGui::PushID(num++); auto buttpath = file.parent_path().filename() / file.filename(); std::wstring wbuttstr = buttpath.wstring(); @@ -5995,23 +6000,39 @@ void render_script_files() } ImGui::PopID(); } + std::filesystem::path path = scriptpath; + std::string abspath = scriptpath; + if (std::filesystem::exists(abspath) && std::filesystem::is_directory(abspath)) + { + abspath = std::filesystem::absolute(path).string(); + } if (g_script_files.size() == 0) { - std::filesystem::path path = scriptpath; - std::string abspath = scriptpath; - if (std::filesystem::exists(abspath) && std::filesystem::is_directory(abspath)) - { - abspath = std::filesystem::absolute(path).string(); - } ImGui::TextWrapped("No scripts found. Put .lua files in '%s' or change script_dir in the ini file and reload.", abspath.c_str()); } - if (ImGui::Button("Refresh##RefreshScripts")) + else if (g_script_files.size() > 0 && num == 0) + { + ImGui::TextWrapped("All scripts found in '%s' are already loaded.", abspath.c_str()); + } + if (ImGui::Button("Reload config and refresh scripts##RefreshScripts")) { + load_config(cfgfile); refresh_script_files(); } ImGui::PopID(); } +bool lower_test(char l, char r) +{ + return (std::tolower(l) == std::tolower(r)); +} + +bool find_match(std::string needle, std::string haystack) +{ + std::string::iterator fpos = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), lower_test); + return fpos != haystack.end(); +} + void render_scripts() { ImGui::PushTextWrapPos(0.0f); @@ -6040,6 +6061,11 @@ void render_scripts() ImGui::SameLine(); static bool enabled_only{false}; ImGui::Checkbox("Hide disabled##EnabledScriptsOnly", &enabled_only); + static std::string script_filter; + ImGui::InputText("Search##ScriptFilter", &script_filter); + ImGui::SameLine(); + if (ImGui::Button("Clear##ClearScriptFilter")) + script_filter.clear(); ImGui::PushItemWidth(-1); int i = 0; std::vector unload_scripts; @@ -6052,6 +6078,8 @@ void render_scripts() { if (enabled_only && !script->is_enabled()) continue; + if (!script_filter.empty() && !find_match(script_filter, script->get_name() + " " + script->get_path() + " " + script->get_id())) + continue; ImGui::PushID(i); ImGui::PushID(script_name.c_str()); std::string filename; From dadf4f9eb04fbaa0889cd4d8c9e8bbbdbdf74871 Mon Sep 17 00:00:00 2001 From: Dregu Date: Thu, 2 Nov 2023 23:54:43 +0200 Subject: [PATCH 15/30] add button to set script dir --- src/injected/ui.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++- src/injected/ui.hpp | 36 +++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 5d92d07ac..2c7c1790c 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -5978,6 +5978,106 @@ std::string gen_random(const int len) return tmp_s; } +void set_default_path(IFileDialog* dialog, std::wstring pathStr) +{ + const wchar_t* defaultPathW = pathStr.c_str(); + + IShellItem* folder; + HRESULT result = SHCreateItemFromParsingName(defaultPathW, NULL, IID_PPV_ARGS(&folder)); + + // Valid non results. + if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE)) + return; + + if (!SUCCEEDED(result)) + return; + + dialog->SetFolder(folder); + folder->Release(); +} + +std::vector select_files(std::wstring default_path, DWORD type = 0) +{ + HRESULT hr = S_OK; + std::vector filePaths; + + IFileOpenDialog* fileDlg = NULL; + hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fileDlg)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT([&] + { fileDlg->Release(); }); + set_default_path(fileDlg, default_path); + + IKnownFolderManager* pkfm = NULL; + hr = CoCreateInstance(CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT([&] + { pkfm->Release(); }); + + IKnownFolder* pKnownFolder = NULL; + hr = pkfm->GetFolder(FOLDERID_PublicMusic, &pKnownFolder); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT([&] + { pKnownFolder->Release(); }); + + IShellItem* psi = NULL; + hr = pKnownFolder->GetShellItem(0, IID_PPV_ARGS(&psi)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT([&] + { psi->Release(); }); + + hr = fileDlg->AddPlace(psi, FDAP_BOTTOM); + + DWORD dwOptions; + fileDlg->GetOptions(&dwOptions); + fileDlg->SetOptions(dwOptions | type); + hr = fileDlg->Show(NULL); + if (SUCCEEDED(hr)) + { + IShellItemArray* pRets; + hr = fileDlg->GetResults(&pRets); + if (SUCCEEDED(hr)) + { + DWORD count; + pRets->GetCount(&count); + for (DWORD i = 0; i < count; i++) + { + IShellItem* pRet; + LPWSTR nameBuffer; + pRets->GetItemAt(i, &pRet); + pRet->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &nameBuffer); + filePaths.push_back(std::wstring(nameBuffer)); + pRet->Release(); + CoTaskMemFree(nameBuffer); + } + pRets->Release(); + } + } + return filePaths; +} + +void set_script_dir() +{ + TCHAR buffer[MAX_PATH] = {0}; + GetModuleFileName(NULL, buffer, MAX_PATH); + auto path = std::filesystem::path(std::string(buffer)).parent_path(); + DEBUG("def {}", path.string()); + ShowCursor(true); + auto folder = select_files(path.wstring(), FOS_PICKFOLDERS); + if (!folder.empty()) + { + scriptpath = std::string(cvt.to_bytes(folder.at(0))); + std::replace(scriptpath.begin(), scriptpath.end(), '\\', '/'); + save_config(cfgfile); + refresh_script_files(); + } + ShowCursor(false); +} + void render_script_files() { ImGui::PushID("files"); @@ -6014,6 +6114,9 @@ void render_script_files() { ImGui::TextWrapped("All scripts found in '%s' are already loaded.", abspath.c_str()); } + if (ImGui::Button("Select script directory##SelectScriptDir")) + set_script_dir(); + tooltip(scriptpath.c_str()); if (ImGui::Button("Reload config and refresh scripts##RefreshScripts")) { load_config(cfgfile); @@ -6041,8 +6144,12 @@ void render_scripts() ImGui::SameLine(); ImGui::Checkbox("to console##ConsoleScriptMessages", &options["console_script_messages"]); ImGui::Checkbox("Fade script messages##FadeScriptMessages", &options["fade_script_messages"]); - if (ImGui::Checkbox("Load scripts from default directory##LoadScriptsDefault", &load_script_dir)) + if (ImGui::Checkbox("Load scripts from script directory##LoadScriptsDefault", &load_script_dir)) refresh_script_files(); + ImGui::SameLine(); + if (ImGui::Button("Set##SetScriptDir")) + set_script_dir(); + tooltip(scriptpath.c_str()); if (ImGui::Checkbox("Load scripts from Mods/Packs##LoadScriptsPacks", &load_packs_dir)) refresh_script_files(); if (ImGui::Button("Create new quick script")) diff --git a/src/injected/ui.hpp b/src/injected/ui.hpp index 1ccddf8dd..7e92ff545 100644 --- a/src/injected/ui.hpp +++ b/src/injected/ui.hpp @@ -3,8 +3,44 @@ #include #include +#include // for true_type, is_invocable_r_v #include +template +requires(std::is_function_v) +struct is_invocable_as : std::false_type +{ +}; +template +struct is_invocable_as : std::is_invocable_r +{ +}; +template +inline constexpr auto is_invocable_as_v = is_invocable_as::value; + +template +requires is_invocable_as_v +struct OnScopeExit +{ + OnScopeExit(FunT&& fun) + : Fun{std::forward(fun)} + { + } + ~OnScopeExit() + { + Fun(); + } + FunT Fun; +}; + +#define OL_CONCAT_IMPL(x, y) x##y +#define OL_CONCAT(x, y) OL_CONCAT_IMPL(x, y) +#define ON_SCOPE_EXIT(expr) \ + OnScopeExit OL_CONCAT(on_scope_exit_, __LINE__) \ + { \ + [&]() { expr; } \ + } + const int OL_KEY_CTRL = 0x100; const int OL_KEY_SHIFT = 0x200; const int OL_KEY_ALT = 0x800; From fe69e7474a4f9e5ba2368bd100ad47164260704a Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 00:07:21 +0200 Subject: [PATCH 16/30] fix crust item and eggsac spawning --- src/injected/ui.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 2c7c1790c..fcb72d624 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -1565,12 +1565,24 @@ void spawn_entity_over() { int spawned = UI::spawn_entity_over(item.id, g_over_id, g_dx, g_dy); auto ent = get_entity_ptr(spawned); - ent->set_draw_depth(9); - ent->flags = set_flag(ent->flags, 4); // pass thru objects - ent->flags = set_flag(ent->flags, 10); // no gravity - ent->flags = clr_flag(ent->flags, 13); // collides walls - if (!test_flag(g_state->special_visibility_flags, 1)) - ent->flags = set_flag(ent->flags, 1); // invisible + if (g_dx == 0 && g_dy == 0) + { + ent->set_draw_depth(9); + ent->flags = set_flag(ent->flags, 4); // pass thru objects + ent->flags = set_flag(ent->flags, 10); // no gravity + ent->flags = clr_flag(ent->flags, 13); // collides walls + if (!test_flag(g_state->special_visibility_flags, 1)) + ent->flags = set_flag(ent->flags, 1); // invisible + } + if (item.id == to_id("ENT_TYPE_ITEM_EGGSAC")) + { + if (g_dx == -1 and g_dy == 0) + ent->angle = 3.14159265358979323846f / 2.0f; + else if (g_dx == 1 and g_dy == 0) + ent->angle = 3.0f * 3.14159265358979323846f / 2.0f; + else if (g_dx == 0 and g_dy == -1) + ent->angle = 3.14159265358979323846f; + } if (!lock_entity) g_last_id = spawned; } From a90fdbceb739201ca7597e4cce4a018a5a2e3930 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 00:16:35 +0200 Subject: [PATCH 17/30] fix get_type and ui spawn crash with custom types --- src/game_api/entity_db.cpp | 2 +- src/injected/ui.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game_api/entity_db.cpp b/src/game_api/entity_db.cpp index 629a682ef..83877ad8f 100644 --- a/src/game_api/entity_db.cpp +++ b/src/game_api/entity_db.cpp @@ -97,7 +97,7 @@ EntityDB* get_type(uint32_t id) // Special case: map_ptr might be 0 if it's not initialized. // This only occurs in list_entities; for others, do not check the pointer // to see if this assumption works. - if (!entity_factory_ptr) + if (!entity_factory_ptr || id > 0x393) return nullptr; return entity_factory_ptr->types + id; diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index fcb72d624..46a7c4c70 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -1292,11 +1292,11 @@ std::optional get_spawn_item() auto to_spawn = g_items[g_filtered_items[g_current_item]]; if (g_current_item == 0 && (unsigned)g_filtered_count == g_items.size()) { - if (g_entity) + if (g_entity && g_entity->type->id <= to_id("ENT_TYPE_LIQUID_COARSE_LAVA")) { to_spawn = EntityItem{entity_full_names[g_entity->type->id], g_entity->type->id}; } - else if (g_last_type >= 0) + else if (g_last_type >= 0 && g_last_type <= (int)to_id("ENT_TYPE_LIQUID_COARSE_LAVA")) { to_spawn = EntityItem{entity_full_names[g_last_type], (uint32_t)g_last_type}; } From 1fef0e9b2cf9012120e50f87cf091f824759bb4b Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 00:32:01 +0200 Subject: [PATCH 18/30] docs --- docs/game_data/spel2.lua | 5 ++++- docs/src/includes/_globals.md | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 7660c5c4d..047fb6ce4 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -438,10 +438,13 @@ function god(g) end ---@param g boolean ---@return nil function god_companions(g) end ----Set the zoom level used in levels and shops. 13.5 is the default. +---Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. ---@param level number ---@return nil function zoom(level) end +---Reset the default zoom levels for all areas and sets current zoom level to 13.5. +---@return nil +function zoom_reset() end ---Pause/unpause the game. ---This is just short for `state.pause == 32`, but that produces an audio bug ---I suggest `state.pause == 2`, but that won't run any callback, `state.pause == 16` will do the same but [set_global_interval](https://spelunky-fyi.github.io/overlunky/#set_global_interval) will still work diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 877be95c6..a7c66e789 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -2727,7 +2727,16 @@ Enables or disables the default position based camp camera bounds, to set them m #### nil zoom(float level) -Set the zoom level used in levels and shops. 13.5 is the default. +Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. + +### zoom_reset + + +> Search script examples for [zoom_reset](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=zoom_reset) + +#### nil zoom_reset() + +Reset the default zoom levels for all areas and sets current zoom level to 13.5. ## Room functions From 699ed69b59a96604d4220938128e7fce3be1a7fd Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 01:05:00 +0200 Subject: [PATCH 19/30] remove some unused stuff --- src/injected/ui.cpp | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 46a7c4c70..14d27baa0 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -2135,25 +2135,6 @@ void frame_advance() { paused = g_state->pause & (uint8_t)g_pause_type; } - auto set_type = g_ui_scripts["pause"]->execute("return exports and exports.set_type or nil"); - if (set_type != "") - { - g_ui_scripts["pause"]->execute("exports.set_type = nil"); - try - { - g_pause_type = std::stoi(set_type); - } - catch (...) - { - }; - } - auto set_paused = g_ui_scripts["pause"]->execute("if exports then return exports.set_paused else return nil end"); - if (set_paused != "") - { - g_ui_scripts["pause"]->execute("exports.set_paused = nil"); - paused = set_paused == "true"; - toggle_pause(); - } } void warp_inc(uint8_t w, uint8_t l, uint8_t t) @@ -6017,30 +5998,26 @@ std::vector select_files(std::wstring default_path, DWORD type = 0 hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fileDlg)); if (FAILED(hr)) return filePaths; - ON_SCOPE_EXIT([&] - { fileDlg->Release(); }); + ON_SCOPE_EXIT(fileDlg->Release()); set_default_path(fileDlg, default_path); IKnownFolderManager* pkfm = NULL; hr = CoCreateInstance(CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm)); if (FAILED(hr)) return filePaths; - ON_SCOPE_EXIT([&] - { pkfm->Release(); }); + ON_SCOPE_EXIT(pkfm->Release()); IKnownFolder* pKnownFolder = NULL; hr = pkfm->GetFolder(FOLDERID_PublicMusic, &pKnownFolder); if (FAILED(hr)) return filePaths; - ON_SCOPE_EXIT([&] - { pKnownFolder->Release(); }); + ON_SCOPE_EXIT(pKnownFolder->Release()); IShellItem* psi = NULL; hr = pKnownFolder->GetShellItem(0, IID_PPV_ARGS(&psi)); if (FAILED(hr)) return filePaths; - ON_SCOPE_EXIT([&] - { psi->Release(); }); + ON_SCOPE_EXIT(psi->Release()); hr = fileDlg->AddPlace(psi, FDAP_BOTTOM); @@ -8982,14 +8959,6 @@ set_callback(function() state.pause = set_mask(state.pause, clr_mask(exports.type, 0x40)) end return exports.paused - else - if exports.set_paused ~= nil then - if exports.set_paused then - state.pause = set_mask(state.pause, exports.type) - else - state.pause = clr_mask(state.pause, exports.type) - end - end end end, ON.PRE_UPDATE))"); add_ui_script("camera_hack", false, R"( From 353a4da73ab04088e99b04983ae365547c466a35 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 01:47:01 +0200 Subject: [PATCH 20/30] add selected entitites to bucket --- docs/game_data/spel2.lua | 6 ++ docs/src/includes/_types.md | 6 ++ src/game_api/bucket.hpp | 10 +++ src/game_api/script/usertypes/bucket_lua.cpp | 6 ++ src/injected/ui.cpp | 75 ++++++++++++++------ 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 047fb6ce4..e4068aa70 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -6300,6 +6300,12 @@ function LogicMagmamanSpawn:remove_spawn(ms) end ---@field ignore_keys unordered_Array @Disable some key bindings in Overlunky, whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. ---@field ignore_keycodes unordered_Array @Disable keys that may or may not be used by Overlunky. You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. ---@field ignore_features unordered_Array @TODO: Disable Overlunky features. Doesn't do anything yet. + ---@field selected_uid integer @Currently selected uid in the entity picker or -1 if nothing is selected. + ---@field set_selected_uid integer? @Set currently selected uid in the entity picker or -1 to clear selection. + ---@field selected_uids integer[] @Currently selected uids in the entity finder. + ---@field hovered_uid integer @Currently hovered entity uid or -1 if nothing is hovered. + ---@field set_selected_uid integer? @Set currently selected uid in the entity picker or -1 to clear selection. + ---@field set_selected_uids integer[] @size: ? @Set currently selected uids in the entity finder. ---@class Bucket ---@field data table @You can store arbitrary simple values here in Playlunky to be read in on Overlunky script for example. diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index 886b6e7a1..0f836a45a 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -787,6 +787,12 @@ map<string, [KEY](#KEY)> | [keys](https://github.com/spelunky-fyi/overlunk set<string> | [ignore_keys](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keys) | Disable some key bindings in [Overlunky](#Overlunky), whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. set<[KEY](#KEY)> | [ignore_keycodes](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keycodes) | Disable keys that may or may not be used by [Overlunky](#Overlunky). You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. set<string> | [ignore_features](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_features) | TODO: Disable [Overlunky](#Overlunky) features. Doesn't do anything yet. +int | [selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=selected_uid) | Currently selected uid in the entity picker or -1 if nothing is selected. +optional<int> | [set_selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uid) | Set currently selected uid in the entity picker or -1 to clear selection. +vector<int> | [selected_uids](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=selected_uids) | Currently selected uids in the entity finder. +int | [hovered_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=hovered_uid) | Currently hovered entity uid or -1 if nothing is hovered. +optional<int> | [set_selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uid) | Set currently selected uid in the entity picker or -1 to clear selection. +optional<vector<int>> | [set_selected_uids](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uids) | Set currently selected uids in the entity finder. ### PRNG diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp index 53ce0c2b4..92087a25c 100644 --- a/src/game_api/bucket.hpp +++ b/src/game_api/bucket.hpp @@ -23,6 +23,16 @@ struct Overlunky std::unordered_set ignore_keycodes; /// TODO: Disable Overlunky features. Doesn't do anything yet. std::unordered_set ignore_features; + /// Currently selected uid in the entity picker or -1 if nothing is selected. + int32_t selected_uid; + /// Set currently selected uid in the entity picker or -1 to clear selection. + std::optional set_selected_uid; + /// Currently hovered entity uid or -1 if nothing is hovered. + int32_t hovered_uid; + /// Currently selected uids in the entity finder. + std::vector selected_uids; + /// Set currently selected uids in the entity finder. + std::optional> set_selected_uids; }; class Bucket diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp index a3ce4b19a..5443f78d8 100644 --- a/src/game_api/script/usertypes/bucket_lua.cpp +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -14,6 +14,12 @@ void register_usertypes(sol::state& lua) ol_type["ignore_keys"] = &Overlunky::ignore_keys; ol_type["ignore_keycodes"] = &Overlunky::ignore_keycodes; ol_type["ignore_features"] = &Overlunky::ignore_features; + ol_type["selected_uid"] = &Overlunky::selected_uid; + ol_type["set_selected_uid"] = &Overlunky::set_selected_uid; + ol_type["selected_uids"] = &Overlunky::selected_uids; + ol_type["hovered_uid"] = &Overlunky::hovered_uid; + ol_type["set_selected_uid"] = &Overlunky::set_selected_uid; + ol_type["set_selected_uids"] = &Overlunky::set_selected_uids; /// Shared memory structure used for Playlunky-Overlunky interoperability auto bucket_type = lua.new_usertype("Bucket", sol::no_constructor); diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 14d27baa0..a8c384d78 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -98,7 +98,8 @@ std::map default_keys{ {"toggle_disable_pause", OL_KEY_CTRL | OL_KEY_SHIFT | 'P'}, {"toggle_grid", OL_KEY_CTRL | OL_KEY_SHIFT | 'G'}, {"toggle_hitboxes", OL_KEY_CTRL | OL_KEY_SHIFT | 'H'}, - {"toggle_entityinfo", OL_KEY_CTRL | OL_KEY_SHIFT | 'E'}, + {"toggle_entity_info", OL_KEY_CTRL | OL_KEY_SHIFT | 'E'}, + {"toggle_entity_tooltip", OL_KEY_CTRL | OL_KEY_SHIFT | 'Y'}, {"toggle_hud", OL_KEY_CTRL | 'H'}, {"toggle_lights", OL_KEY_CTRL | 'L'}, {"toggle_ghost", OL_KEY_CTRL | 'O'}, @@ -335,7 +336,8 @@ std::map options = { {"disable_pause", false}, {"draw_grid", false}, {"draw_hitboxes", false}, - {"draw_entityinfo", false}, + {"draw_entity_info", false}, + {"draw_entity_tooltip", false}, {"enable_unsafe_scripts", false}, {"warp_increments_level_count", true}, {"warp_transition", false}, @@ -2944,9 +2946,13 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { options["draw_hitboxes"] = !options["draw_hitboxes"]; } - else if (pressed("toggle_entityinfo", wParam)) + else if (pressed("toggle_entity_info", wParam)) { - options["draw_entityinfo"] = !options["draw_entityinfo"]; + options["draw_entity_info"] = !options["draw_entity_info"]; + } + else if (pressed("toggle_entity_tooltip", wParam)) + { + options["draw_entity_tooltip"] = !options["draw_entity_tooltip"]; } else if (pressed("toggle_grid", wParam)) { @@ -4505,7 +4511,7 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, else draw_list->AddRect(fix_pos(sboxa), fix_pos(sboxb), color, rounded ? 5.0f : 0.0f, 0, 2.0f); - if (options["draw_entityinfo"] && !cross && !filled) + if (options["draw_entity_info"] && !cross && !filled) draw_list->AddText(fix_pos(ImVec2(sboxa.x, sboxb.y)), ImColor(1.0f, 1.0f, 1.0f, 0.8f), entity_tooltip(ent).c_str()); if ((g_hitbox_mask & 0x8000) == 0) @@ -4861,23 +4867,32 @@ void render_clickhandler() grids = screenify({gridline.first, gridline.second}); draw_list->AddLine(fix_pos(ImVec2(base->Pos.x, grids.y)), fix_pos(ImVec2(base->Pos.x + base->Size.x, grids.y)), ImColor(255, 0, 0, 150), 2.0f); } + } - if (ImGui::IsMousePosValid()) + if (ImGui::IsMousePosValid()) + { + ImVec2 mpos = normalize(mouse_pos()); + std::pair cpos = UI::click_position(mpos.x, mpos.y); + std::string coords = fmt::format("{:.2f}, {:.2f} ({:.2f}, {:.2f})", cpos.first, cpos.second, mpos.x, mpos.y); + unsigned int mask = safe_entity_mask; + if (ImGui::GetIO().KeyShift) // TODO: Get the right modifier from mouse_destroy_unsafe { - ImVec2 mpos = normalize(mouse_pos()); - std::pair cpos = UI::click_position(mpos.x, mpos.y); - std::string coords = fmt::format("{:.2f}, {:.2f} ({:.2f}, {:.2f})", cpos.first, cpos.second, mpos.x, mpos.y); - unsigned int mask = safe_entity_mask; - if (ImGui::GetIO().KeyShift) // TODO: Get the right modifier from mouse_destroy_unsafe - { - mask = unsafe_entity_mask; - } - auto hovered = UI::get_entity_at(cpos.first, cpos.second, false, 2, mask); - if (hovered != nullptr) - { + mask = unsafe_entity_mask; + } + auto hovered = UI::get_entity_at(cpos.first, cpos.second, false, 2, mask); + if (hovered) + { + if (options["draw_hitboxes"]) render_hitbox(hovered, true, ImColor(50, 50, 255, 200)); - coords += "\n" + entity_tooltip(hovered); - } + coords += "\n" + entity_tooltip(hovered); + g_bucket->overlunky->hovered_uid = hovered->uid; + } + else + { + g_bucket->overlunky->hovered_uid = -1; + } + if (options["draw_entity_tooltip"]) + { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {4.0f, 4.0f}); tooltip(coords.c_str(), true); ImGui::PopStyleVar(); @@ -5784,8 +5799,10 @@ void render_options() ImGui::SameLine(); ImGui::Checkbox("interpolated##DrawRealBox", &options["draw_hitboxes_interpolated"]); tooltip("Use interpolated render position for smoother hitboxes on hifps.\nActual game logic is not interpolated like this though."); - ImGui::Checkbox("Draw entity info##DrawEntityInfo", &options["draw_entityinfo"]); - tooltip("Draw entity names, uids and some random stuff next to the hitboxes.", "toggle_entityinfo"); + ImGui::Checkbox("Draw hovered entity tooltip##DrawEntityTooltip", &options["draw_entity_tooltip"]); + tooltip("Draw entity names, uids and some random stuff for hovered entitites.", "toggle_entity_tooltip"); + ImGui::Checkbox("Draw all entity info##DrawEntityInfo", &options["draw_entity_info"]); + tooltip("Draw entity names, uids and some random stuff next to all entities.", "toggle_entity_info"); ImGui::Checkbox("Smooth camera dragging", &options["smooth_camera"]); tooltip("Smooth camera movement when dragging, unless paused."); ImGui::SliderFloat("Camera speed##DragSpeed", &g_camera_speed, 1.0f, 5.0f); @@ -9330,7 +9347,8 @@ void check_focus() std::unordered_set legal_options{ "disable_ghost_timer", "disable_pause", - "draw_entityinfo", + "draw_entity_info", + "draw_entity_tooltip", "draw_grid", "draw_hitboxes", "draw_hitboxes_interpolated", @@ -9354,6 +9372,19 @@ std::unordered_set legal_options{ void update_bucket() { + if (g_bucket->overlunky->set_selected_uid.has_value()) + { + g_last_id = g_bucket->overlunky->set_selected_uid.value(); + g_entity = get_entity_ptr(g_last_id); + g_bucket->overlunky->set_selected_uid = std::nullopt; + } + if (g_bucket->overlunky->set_selected_uids.has_value()) + { + g_selected_ids = g_bucket->overlunky->set_selected_uids.value(); + g_bucket->overlunky->set_selected_uids = std::nullopt; + } + g_bucket->overlunky->selected_uid = g_last_id; + g_bucket->overlunky->selected_uids = g_selected_ids; g_bucket->overlunky->keys = keys; for (auto [k, v] : options) { From 7003209117ccad4c214c9263de6e2cf95fa13388 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 02:24:06 +0200 Subject: [PATCH 21/30] update readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fa4253c1..61bebca00 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Current features and their *default* keyboard shortcuts. + **Ctrl+,**: Zoom in + **Ctrl+.**: Zoom out + **Ctrl+0**: Auto fit level width to screen + + **Ctrl+1**: Reset zoom level to default for all areas + **Ctrl+2345**: Zoom to X level width + **Shift+IJKL**: Move camera in desired direction + **Shift+U**: Reset camera focus and bounds @@ -159,7 +160,10 @@ Lua scripting is still buggy and unfinished and the **API might change**, althou + Try toggling the *Capture third-party overlays* option in *Game Capture*. It's possible to hide OL if running the game through Steam. + If you have lag or crashes, check previous question. - If you **don't have scripts** in the Scripts tab - + You didn't put them in the right place. [Follow](#installation-and-usage) the [instructions](#scripts) or use Modlunky2. + + You didn't put them in the right place. [Follow](#installation-and-usage) the [instructions](#scripts), use Modlunky2 or change the script directory in the options. + - If some of the keyboard shortcuts don't work + + Make sure your keyboard is not [ghosting](https://www.microsoft.com/applied-sciences/projects/anti-ghosting-demo) the key combo. + + Make sure Windows is not using the keys for [something stupid](https://superuser.com/questions/1367843/how-to-disable-ctrlshift-keyboard-layout-switch-for-the-same-input-language-i). - If it still doesn't work + Stop by #s2-modding-help on the [community Discord](https://discord.gg/spelunky-community) or [submit an issue](https://github.com/spelunky-fyi/overlunky/issues) + We want to know your OS version, are you using the game on Steam, what you have tried, what tools you are using, how you are using them and all the versions. These can be found in the terminal output or ingame overlays. ("Latest" or WHIP is not a version, but 7f0bc94 or 0.6.1 is.) From 92b060a6866a7ecd5f049bac94fffd3d26cdee47 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 03:08:15 +0200 Subject: [PATCH 22/30] undeprecate and fix set_camera_position --- docs/game_data/spel2.lua | 5 +++++ docs/src/includes/_globals.md | 18 +++++++++--------- src/game_api/entity.cpp | 9 +-------- src/game_api/script/lua_vm.cpp | 3 +-- src/game_api/state.cpp | 8 +++++++- src/injected/ui.cpp | 21 +++------------------ 6 files changed, 26 insertions(+), 38 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index e4068aa70..4d9afeebd 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -881,6 +881,11 @@ function get_aabb_bounds() end ---Gets the current camera position in the level ---@return number, number function get_camera_position() end +---Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in ON.RENDER_PRE_GAME or similar. See Camera for proper camera handling with bounds and rubberbanding. +---@param cx number +---@param cy number +---@return nil +function set_camera_position(cx, cy) end ---Set the nth bit in a number. This doesn't actually change the variable you pass, it just returns the new value you can use. ---@param flags Flags ---@param bit integer diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index a7c66e789..4ce834959 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -2711,6 +2711,15 @@ Translate a distance of `x` tiles to screen distance to be be used in drawing fu Translate an entity position to screen position to be used in drawing functions +### set_camera_position + + +> Search script examples for [set_camera_position](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_position) + +#### nil set_camera_position(float cx, float cy) + +Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in [ON](#ON).RENDER_PRE_GAME or similar. See [Camera](#Camera) for proper camera handling with bounds and rubberbanding. + ### set_camp_camera_bounds_enabled @@ -3723,15 +3732,6 @@ Use [replace_drop](#replace_drop)([DROP](#DROP).ARROWTRAP_WOODENARROW, new_arrow This function never worked properly as too many places in the game individually check for vlads cape and calculate the blood multiplication `default_multiplier` doesn't do anything due to some changes in last game updates, `vladscape_multiplier` only changes the multiplier to some entities death's blood spit -### set_camera_position - - -> Search script examples for [set_camera_position](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_position) - -#### nil set_camera_position(float cx, float cy) - -this doesn't actually work at all. See State -> [Camera](#Camera) the for proper camera handling - ### setflag diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index c8bc6fd11..d5785a814 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -469,14 +469,7 @@ void Movable::set_position(float to_x, float to_y) rendering_info->y_dupe4 += dy; } if (State::get().ptr()->camera->focused_entity_uid == uid) - { - State::get().ptr()->camera->focus_x += dx; - State::get().ptr()->camera->focus_y += dy; - State::get().ptr()->camera->adjusted_focus_x += dx; - State::get().ptr()->camera->adjusted_focus_y += dy; - State::get().ptr()->camera->calculated_focus_x += dx; - State::get().ptr()->camera->calculated_focus_y += dy; - } + State::get().set_camera_position(dx, dy); } template diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 8b304d9b9..239459f74 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1279,8 +1279,7 @@ end { return State::get_camera_position(); }; - /// Deprecated - /// this doesn't actually work at all. See State -> Camera the for proper camera handling + /// Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in ON.RENDER_PRE_GAME or similar. See Camera for proper camera handling with bounds and rubberbanding. lua["set_camera_position"] = set_camera_position; /// Set the nth bit in a number. This doesn't actually change the variable you pass, it just returns the new value you can use. diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index ad101da9d..4ff974a0a 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -451,10 +451,16 @@ std::pair State::get_camera_position() void State::set_camera_position(float cx, float cy) { + static const auto addr = (float*)get_address("camera_position"); auto camera = ptr()->camera; - camera->focused_entity_uid = -1; camera->focus_x = cx; camera->focus_y = cy; + camera->adjusted_focus_x = cx; + camera->adjusted_focus_y = cy; + camera->calculated_focus_x = cx; + camera->calculated_focus_y = cy; + *addr = cx; + *(addr + 1) = cy; } void State::warp(uint8_t w, uint8_t l, uint8_t t) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index a8c384d78..8df182394 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -4081,13 +4081,7 @@ void render_camera() } tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); if (ImGui::Checkbox("Follow focused entity absolutely##CameraForcePlayer", &camera_hack)) - { set_camera_hack(camera_hack); - enable_camera_bounds = !camera_hack; - set_camera_bounds(enable_camera_bounds); - enable_camp_camera = !camera_hack; - UI::set_camp_camera_bounds_enabled(enable_camp_camera); - } tooltip("Enable to remove camera bounds and always center the entity instantly without rubberbanding."); } @@ -5342,11 +5336,8 @@ void render_clickhandler() g_state->camera->focus_x -= (current_pos.first - oryginal_pos.first) * g_camera_speed; g_state->camera->focus_y -= (current_pos.second - oryginal_pos.second) * g_camera_speed; - if (g_state->pause != 0 || !options["smooth_camera"]) - { - g_state->camera->adjusted_focus_x = g_state->camera->focus_x; - g_state->camera->adjusted_focus_y = g_state->camera->focus_y; - } + if (g_state->pause != 0 || paused || !options["smooth_camera"]) + State::get().set_camera_position(g_state->camera->focus_x, g_state->camera->focus_y); startpos = normalize(mouse_pos()); enable_camera_bounds = false; set_camera_bounds(enable_camera_bounds); @@ -8989,13 +8980,7 @@ set_callback(function() lastpos = pos x = x + state.camera.focus_offset_x y = y + state.camera.vertical_pan + state.camera.focus_offset_y - state.camera.focus_x, state.camera.focus_y = x, y - state.camera.adjusted_focus_x, state.camera.adjusted_focus_y = x, y - state.camera.calculated_focus_x, state.camera.calculated_focus_y = x, y - state.camera.bounds_top = math.huge - state.camera.bounds_bottom = -math.huge - state.camera.bounds_left = -math.huge - state.camera.bounds_right = math.huge + set_camera_position(x, y) end, ON.RENDER_PRE_GAME))"); add_ui_script("dark", false, "set_callback(function() state.level_flags = set_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); add_ui_script("light", false, "set_callback(function() state.level_flags = clr_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); From 12f5d0713fa6ab5773ef5e28ab2cd44c445f76df Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 07:53:02 +0200 Subject: [PATCH 23/30] add menu input to api --- docs/game_data/spel2.lua | 7 ++- docs/generate.py | 51 +------------------ docs/parse_source.py | 50 ++++++++++++++++++ docs/src/includes/_enums.md | 46 ++++++++++++----- docs/src/includes/_types.md | 7 ++- docs/validator.py | 51 +------------------ src/game_api/aliases.hpp | 1 + src/game_api/game_manager.hpp | 11 +++- src/game_api/script/lua_vm.cpp | 4 +- .../script/usertypes/game_manager_lua.cpp | 8 +++ 10 files changed, 118 insertions(+), 118 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 4d9afeebd..30672ae5e 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -2233,6 +2233,8 @@ do ---@field journal_ui JournalUI ---@field save_related SaveRelated ---@field main_menu_music BackgroundSound + ---@field buttons_controls integer[] @size: MAX_PLAYERS @Yet another place to get player inputs, in some format + ---@field buttons_movement integer[] @size: MAX_PLAYERS @Yet another place to get player inputs, in some format ---@class SaveRelated ---@field journal_popup_ui JournalPopupUI @@ -2248,7 +2250,10 @@ do ---@field slide_position number ---@class GameProps - ---@field buttons integer + ---@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 + ---@field buttons_menu_previous MENU_INPUT @Previous state of buttons_menu + ---@field buttons_menu MENU_INPUT @Inputs used to control all the menus, independent from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. ---@field game_has_focus boolean ---@class PRNG diff --git a/docs/generate.py b/docs/generate.py index 7d269a06f..3cb7c047c 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -9,56 +9,7 @@ if not os.path.exists("src/includes"): os.makedirs("src/includes") -# if you change anything in here, please update it in validator.py as well -replace_table = { - # standard basic types - "uint8_t": "int", - "uint16_t": "int", - "uint32_t": "int", - "uint64_t": "int", - "int8_t": "int", - "int16_t": "int", - "int32_t": "int", - "int64_t": "int", - "ImU32": "int", - "in_port_t": "int", - "size_t": "int", - "char*": "string", - "wstring": "string", - "u16string": "string", - "string_view": "string", - "char16_t*": "string", - "char16_t": "char", - "pair<": "tuple<", - # std containers - "custom_vector<": "vector<", - "custom_map<": "map<", - "custom_unordered_map<": "map<", - "custom_set<": "set<", - "custom_unordered_set<": "set<", - "game_vector<": "vector<", - "game_map<": "map<", - "game_unordered_map<": "map<", - "game_set<": "set<", - "game_unordered_set<": "set<", - "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not - "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not - # removers - ", identity_hasher<>": "", - "std::": "", - "sol::": "", - "void": "", - "constexpr": "", - "const": "", - "static": "", - # special - "variadic_args va": "ENT_TYPE, ENT_TYPE...", - "EmittedParticlesInfo": "array", - "ImVec2": "Vec2", - "SoundCallbackFunction": "function", - "object ": "any ", - "BucketItem": "any", -} +replace_table = ps.replace_table def replace_all(text): for repl, wth in replace_table.items(): diff --git a/docs/parse_source.py b/docs/parse_source.py index 66a97616c..8e4e06d97 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -11,6 +11,56 @@ if not os.path.exists(".db"): os.makedirs(".db") +replace_table = { + # standard basic types + "uint8_t": "int", + "uint16_t": "int", + "uint32_t": "int", + "uint64_t": "int", + "int8_t": "int", + "int16_t": "int", + "int32_t": "int", + "int64_t": "int", + "ImU32": "int", + "in_port_t": "int", + "size_t": "int", + "char*": "string", + "wstring": "string", + "u16string": "string", + "string_view": "string", + "char16_t*": "string", + "char16_t": "char", + "pair<": "tuple<", + # std containers + "custom_vector<": "vector<", + "custom_map<": "map<", + "custom_unordered_map<": "map<", + "custom_set<": "set<", + "custom_unordered_set<": "set<", + "game_vector<": "vector<", + "game_map<": "map<", + "game_unordered_map<": "map<", + "game_set<": "set<", + "game_unordered_set<": "set<", + "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not + "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not + # removers + ", identity_hasher<>": "", + "std::": "", + "sol::": "", + "void": "", + "constexpr": "", + "const": "", + "static": "", + # special + "variadic_args va": "ENT_TYPE, ENT_TYPE...", + "EmittedParticlesInfo": "array", + "ImVec2": "Vec2", + "SoundCallbackFunction": "function", + "object ": "any ", + "BucketItem": "any", +} + header_files = [ "../src/game_api/math.hpp", "../src/game_api/rpc.hpp", diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 08ee0eebf..3396d67a8 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -465,19 +465,19 @@ Name | Data | Description Name | Data | Description ---- | ---- | ----------- -[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.NONE) | 0 | -[JUMP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JUMP) | 1 | -[WHIP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.WHIP) | 2 | -[BOMB](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.BOMB) | 4 | -[ROPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.ROPE) | 8 | -[RUN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RUN) | 16 | -[DOOR](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOOR) | 32 | -[MENU](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.MENU) | 64 | -[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JOURNAL) | 128 | -[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.LEFT) | 256 | -[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RIGHT) | 512 | -[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.UP) | 1024 | -[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOWN) | 2048 | +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.NONE) | 0x0 | +[JUMP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JUMP) | 0x1 | +[WHIP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.WHIP) | 0x2 | +[BOMB](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.BOMB) | 0x4 | +[ROPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.ROPE) | 0x8 | +[RUN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RUN) | 0x10 | +[DOOR](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOOR) | 0x20 | +[MENU](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.MENU) | 0x40 | +[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JOURNAL) | 0x80 | +[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.LEFT) | 0x100 | +[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RIGHT) | 0x200 | +[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.UP) | 0x400 | +[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOWN) | 0x800 | ## INPUT_FLAG @@ -759,6 +759,26 @@ Name | Data | Description [LIQUID](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MASK.LIQUID) | 0x6000 | Short for ([MASK](#MASK).WATER | [MASK](#MASK).LAVA)
[ANY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MASK.ANY) | 0x0 | Value of 0, treated by all the functions as ANY mask
+## MENU_INPUT + + +> Search script examples for [MENU_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT) + + + +Name | Data | Description +---- | ---- | ----------- +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.NONE) | 0x0 | +[SELECT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.SELECT) | 0x1 | +[BACK](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.BACK) | 0x2 | +[DELETE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.DELETE) | 0x4 | +[RANDOM](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.RANDOM) | 0x8 | +[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.JOURNAL) | 0x10 | +[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.LEFT) | 0x20 | +[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.RIGHT) | 0x40 | +[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.UP) | 0x80 | +[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.DOWN) | 0x100 | + ## ON diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index 0f836a45a..f4da58c21 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -2757,13 +2757,18 @@ Type | Name | Description [JournalUI](#JournalUI) | [journal_ui](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_ui) | [SaveRelated](#SaveRelated) | [save_related](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=save_related) | [BackgroundSound](#BackgroundSound) | [main_menu_music](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=main_menu_music) | +array<int, MAX_PLAYERS> | [buttons_controls](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_controls) | Yet another place to get player inputs, in some format +array<int, MAX_PLAYERS> | [buttons_movement](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_movement) | Yet another place to get player inputs, in some format ### GameProps Type | Name | Description ---- | ---- | ----------- -int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | +int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | Might be used for some menu inputs not found in buttons_menu +int | [buttons_extra](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_extra) | Might be used for some menu inputs not found in buttons_menu +[MENU_INPUT](#MENU_INPUT) | [buttons_menu_previous](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_menu_previous) | Previous state of buttons_menu +[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, independent 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) | ### Items diff --git a/docs/validator.py b/docs/validator.py index 9cc04739b..e7d4f157d 100644 --- a/docs/validator.py +++ b/docs/validator.py @@ -2,56 +2,7 @@ import parse_source as ps -#should be the same as in generate.py -replace_table = { - # standard basic types - "uint8_t": "int", - "uint16_t": "int", - "uint32_t": "int", - "uint64_t": "int", - "int8_t": "int", - "int16_t": "int", - "int32_t": "int", - "int64_t": "int", - "ImU32": "int", - "in_port_t": "int", - "size_t": "int", - "char*": "string", - "wstring": "string", - "u16string": "string", - "string_view": "string", - "char16_t*": "string", - "char16_t": "char", - "pair<": "tuple<", - # std containers - "custom_vector<": "vector<", - "custom_map<": "map<", - "custom_unordered_map<": "map<", - "custom_set<": "set<", - "custom_unordered_set<": "set<", - "game_vector<": "vector<", - "game_map<": "map<", - "game_unordered_map<": "map<", - "game_set<": "set<", - "game_unordered_set<": "set<", - "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not - "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not - # removers - ", identity_hasher<>": "", - "std::": "", - "sol::": "", - "void": "", - "constexpr": "", - "const": "", - "static": "", - # special - "variadic_args va": "ENT_TYPE, ENT_TYPE...", - "EmittedParticlesInfo": "array", - "ImVec2": "Vec2", - "SoundCallbackFunction": "function", - "object ": "any ", - "BucketItem": "any", -} +replace_table = ps.replace_table def replace_all(text): for repl, wth in replace_table.items(): diff --git a/src/game_api/aliases.hpp b/src/game_api/aliases.hpp index fdf91a883..64dc393cb 100644 --- a/src/game_api/aliases.hpp +++ b/src/game_api/aliases.hpp @@ -10,6 +10,7 @@ using VANILLA_SOUND_PARAM = uint32_t; // NoAlias using VANILLA_SOUND_CALLBACK_TYPE = uint32_t; // NoAlias using TEXTURE = int64_t; // NoAlias using INPUTS = uint16_t; // NoAlias +using MENU_INPUT = uint16_t; // NoAlias using BUTTON = uint8_t; // NoAlias using ENT_TYPE = uint32_t; // NoAlias using ROOM_TEMPLATE = uint16_t; // NoAlias diff --git a/src/game_api/game_manager.hpp b/src/game_api/game_manager.hpp index 06295cf72..073779aee 100644 --- a/src/game_api/game_manager.hpp +++ b/src/game_api/game_manager.hpp @@ -72,15 +72,20 @@ struct BackgroundMusic struct GameProps { + /// Might be used for some menu inputs not found in buttons_menu uint32_t buttons; uint32_t unknown1; uint32_t unknown2; uint32_t unknown3; - uint32_t buttons_dupe; + /// Might be used for some menu inputs not found in buttons_menu + uint32_t buttons_extra; uint32_t unknown4; uint32_t unknown5; uint32_t unknown6; - uint32_t buttons_dupe_but_different; + /// Previous state of buttons_menu + MENU_INPUT buttons_menu_previous; + /// Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. + MENU_INPUT buttons_menu; int8_t unknown8; bool game_has_focus; bool unknown9; @@ -92,7 +97,9 @@ struct GameManager { BackgroundMusic* music; SaveRelated* save_related; + /// Yet another place to get player inputs, in some format std::array buttons_controls; + /// Yet another place to get player inputs, in some format std::array buttons_movement; GameProps* game_props; diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 239459f74..5b207dc89 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2214,7 +2214,9 @@ end /// Look at the example on how to mimic game layer switching behavior lua["set_camera_layer_control_enabled"] = set_camera_layer_control_enabled; - lua.create_named_table("INPUTS", "NONE", 0, "JUMP", 1, "WHIP", 2, "BOMB", 4, "ROPE", 8, "RUN", 16, "DOOR", 32, "MENU", 64, "JOURNAL", 128, "LEFT", 256, "RIGHT", 512, "UP", 1024, "DOWN", 2048); + lua.create_named_table("INPUTS", "NONE", 0x0, "JUMP", 0x1, "WHIP", 0x2, "BOMB", 0x4, "ROPE", 0x8, "RUN", 0x10, "DOOR", 0x20, "MENU", 0x40, "JOURNAL", 0x80, "LEFT", 0x100, "RIGHT", 0x200, "UP", 0x400, "DOWN", 0x800); + + lua.create_named_table("MENU_INPUT", "NONE", 0x0, "SELECT", 0x1, "BACK", 0x2, "DELETE", 0x4, "RANDOM", 0x8, "JOURNAL", 0x10, "LEFT", 0x20, "RIGHT", 0x40, "UP", 0x80, "DOWN", 0x100); lua.create_named_table( "ON", diff --git a/src/game_api/script/usertypes/game_manager_lua.cpp b/src/game_api/script/usertypes/game_manager_lua.cpp index f16cbee0a..0633c8732 100644 --- a/src/game_api/script/usertypes/game_manager_lua.cpp +++ b/src/game_api/script/usertypes/game_manager_lua.cpp @@ -70,6 +70,8 @@ void register_usertypes(sol::state& lua) gamemanager_type["journal_ui"] = &GameManager::journal_ui; gamemanager_type["save_related"] = &GameManager::save_related; gamemanager_type["main_menu_music"] = &GameManager::main_menu_music; + gamemanager_type["buttons_controls"] = &GameManager::buttons_controls; + gamemanager_type["buttons_movement"] = &GameManager::buttons_movement; lua.new_usertype( "SaveRelated", @@ -97,6 +99,12 @@ void register_usertypes(sol::state& lua) "GameProps", "buttons", &GameProps::buttons, + "buttons_extra", + &GameProps::buttons_extra, + "buttons_menu_previous", + &GameProps::buttons_menu_previous, + "buttons_menu", + &GameProps::buttons_menu, "game_has_focus", &GameProps::game_has_focus); } From 5aa951466b714bba200638721f1bd3eac39217de Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 09:45:02 +0200 Subject: [PATCH 24/30] add get_global_frame and modal_open --- src/game_api/game_manager.hpp | 2 +- src/game_api/script/lua_vm.cpp | 4 +++- .../script/usertypes/game_manager_lua.cpp | 5 ++++- src/game_api/state.cpp | 15 +++++++++++++++ src/game_api/state.hpp | 2 ++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/game_api/game_manager.hpp b/src/game_api/game_manager.hpp index 073779aee..2bc205643 100644 --- a/src/game_api/game_manager.hpp +++ b/src/game_api/game_manager.hpp @@ -86,7 +86,7 @@ struct GameProps MENU_INPUT buttons_menu_previous; /// Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. MENU_INPUT buttons_menu; - int8_t unknown8; + int8_t modal_open; bool game_has_focus; bool unknown9; bool unknown10; diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 5b207dc89..6007c209a 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1181,8 +1181,10 @@ end lua["lock_door_at"] = lock_door_at; /// Try to unlock the exit at coordinates lua["unlock_door_at"] = unlock_door_at; - /// Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. + /// Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. lua["get_frame"] = get_frame_count; + /// Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. + lua["get_global_frame"] = get_global_frame_count; /// Get the current timestamp in milliseconds since the Unix Epoch. lua["get_ms"] = []() { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); }; diff --git a/src/game_api/script/usertypes/game_manager_lua.cpp b/src/game_api/script/usertypes/game_manager_lua.cpp index 0633c8732..94f189ab2 100644 --- a/src/game_api/script/usertypes/game_manager_lua.cpp +++ b/src/game_api/script/usertypes/game_manager_lua.cpp @@ -106,6 +106,9 @@ void register_usertypes(sol::state& lua) "buttons_menu", &GameProps::buttons_menu, "game_has_focus", - &GameProps::game_has_focus); + &GameProps::game_has_focus, + "modal_open", + sol::property([](GameProps& gp) + { return gp.modal_open == 0; })); } }; // namespace NGM diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 4ff974a0a..dc22c7092 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -36,6 +36,8 @@ #include "virtual_table.hpp" // for get_virtual_function_address, VTABLE... #include "vtable_hook.hpp" // for hook_vtable +static int64_t global_frame_count{0}; + uint16_t StateMemory::get_correct_ushabti() // returns animation_frame of ushabti { return (correct_ushabti + (correct_ushabti / 10) * 2); @@ -628,6 +630,10 @@ uint32_t State::get_frame_count() const { return memory_read((size_t)ptr() - 0xd0); } +int64_t get_global_frame_count() +{ + return global_frame_count; +}; std::vector State::read_prng() const { @@ -643,6 +649,15 @@ using OnStateUpdate = void(StateMemory*); OnStateUpdate* g_state_update_trampoline{nullptr}; void StateUpdate(StateMemory* s) { + auto state = State::get(); + if (s == state.ptr_main()) + { + if (global_frame_count < state.get_frame_count()) + global_frame_count = state.get_frame_count_main(); + else + global_frame_count++; + } + if (!pre_state_update()) { g_state_update_trampoline(s); diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index 05822b9cf..f06a11c01 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -380,3 +380,5 @@ uint8_t enum_to_layer(const LAYER layer); uint32_t lowbias32(uint32_t x); uint32_t lowbias32_r(uint32_t x); + +int64_t get_global_frame_count(); From 7c3842a256b838bb33b613b2d114bf00679b95ee Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 09:54:01 +0200 Subject: [PATCH 25/30] update docs --- docs/game_data/spel2.lua | 8 ++++++-- docs/src/includes/_globals.md | 11 ++++++++++- docs/src/includes/_types.md | 3 ++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 30672ae5e..13d50a8ed 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -738,9 +738,12 @@ function lock_door_at(x, y) end ---@param y number ---@return nil function unlock_door_at(x, y) end ----Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. +---Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. ---@return integer function get_frame() end +---Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. +---@return integer +function get_global_frame() end ---Get the current timestamp in milliseconds since the Unix Epoch. ---@return nil function get_ms() end @@ -2253,8 +2256,9 @@ do ---@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 ---@field buttons_menu_previous MENU_INPUT @Previous state of buttons_menu - ---@field buttons_menu MENU_INPUT @Inputs used to control all the menus, independent from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. + ---@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 ---@class PRNG ---@field seed fun(self, seed: integer): nil @Same as `seed_prng` diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 4ce834959..b8b4bea88 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1406,7 +1406,7 @@ short for state->money_shop_total + loop[inventory.money + inventory.collected_m #### int get_frame() -Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. +Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. ### get_frametime @@ -1426,6 +1426,15 @@ Get engine target frametime (1/framerate, default 1/60). Get engine target frametime when game is unfocused (1/framerate, default 1/33). +### get_global_frame + + +> Search script examples for [get_global_frame](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_global_frame) + +#### int get_global_frame() + +Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. + ### get_hud diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index f4da58c21..4cfb50749 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -2768,8 +2768,9 @@ Type | Name | Description int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | Might be used for some menu inputs not found in buttons_menu int | [buttons_extra](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_extra) | Might be used for some menu inputs not found in buttons_menu [MENU_INPUT](#MENU_INPUT) | [buttons_menu_previous](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_menu_previous) | Previous state of buttons_menu -[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, independent from player inputs. You can probably capture and edit this in [ON](#ON).PRE_UPDATE. +[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) | ### Items From b47ff6834c0391d22ab3f7005980ef116b2f5329 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 10:11:23 +0200 Subject: [PATCH 26/30] fix janky camera with camera hack --- src/injected/ui.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 8df182394..5486bb6d1 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -4081,7 +4081,13 @@ void render_camera() } tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); if (ImGui::Checkbox("Follow focused entity absolutely##CameraForcePlayer", &camera_hack)) + { set_camera_hack(camera_hack); + enable_camera_bounds = !camera_hack; + set_camera_bounds(enable_camera_bounds); + enable_camp_camera = !camera_hack; + UI::set_camp_camera_bounds_enabled(enable_camp_camera); + } tooltip("Enable to remove camera bounds and always center the entity instantly without rubberbanding."); } From 661c867b44be568721d2a7f0d1e1bb1a132b7f40 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 18:08:29 +0200 Subject: [PATCH 27/30] deprecate old input functions --- docs/game_data/spel2.lua | 13 --------- docs/src/includes/_globals.md | 51 ++++++++++++++++------------------ src/game_api/script/lua_vm.cpp | 5 +++- 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 13d50a8ed..fab4efbe6 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -932,19 +932,6 @@ function test_mask(flags, mask) end ---Gets the resolution (width and height) of the screen ---@return integer, integer function get_window_size() end ----Steal input from a Player, HiredHand or PlayerGhost ----@param uid integer ----@return nil -function steal_input(uid) end ----Return input previously stolen with [steal_input](https://spelunky-fyi.github.io/overlunky/#steal_input) ----@param uid integer ----@return nil -function return_input(uid) end ----Send input to entity, has to be previously stolen with [steal_input](https://spelunky-fyi.github.io/overlunky/#steal_input) ----@param uid integer ----@param buttons INPUTS ----@return nil -function send_input(uid, buttons) end ---Clears a callback that is specific to a screen. ---@param screen_id integer ---@param cb_id CallbackId diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index b8b4bea88..e26bf8c08 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -2073,33 +2073,6 @@ Returns: [ImGuiIO](#ImGuiIO) for raw keyboard, mouse and xinput gamepad stuff. Current mouse cursor position in screen coordinates. -### return_input - - -> Search script examples for [return_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=return_input) - -#### nil return_input(int uid) - -Return input previously stolen with [steal_input](#steal_input) - -### send_input - - -> Search script examples for [send_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=send_input) - -#### nil send_input(int uid, [INPUTS](#INPUTS) buttons) - -Send input to entity, has to be previously stolen with [steal_input](#steal_input) - -### steal_input - - -> Search script examples for [steal_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=steal_input) - -#### nil steal_input(int uid) - -Steal input from a [Player](#Player), HiredHand or [PlayerGhost](#PlayerGhost) - ## Lighting functions @@ -3762,6 +3735,30 @@ This function never worked properly as too many places in the game individually `nil testflag()`
+### steal_input + + +> Search script examples for [steal_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=steal_input) + +`nil steal_input(int uid)`
+Deprecated because it's a weird old hack that crashes the game. You can modify inputs in many other ways, like editing `state.player_inputs.player_slot_1.buttons_gameplay` in PRE_UPDATE or a `set_pre_process_input` hook. Steal input from a Player, HiredHand or PlayerGhost. + +### return_input + + +> Search script examples for [return_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=return_input) + +`nil return_input(int uid)`
+Return input previously stolen with [steal_input](#steal_input) + +### send_input + + +> Search script examples for [send_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=send_input) + +`nil send_input(int uid, INPUTS buttons)`
+Send input to entity, has to be previously stolen with [steal_input](#steal_input) + ### read_input diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 6007c209a..6d03569f2 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1320,7 +1320,8 @@ end lua["get_window_size"] = []() -> std::tuple { return {(int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().DisplaySize.y}; }; - /// Steal input from a Player, HiredHand or PlayerGhost + /// Deprecated + /// Deprecated because it's a weird old hack that crashes the game. You can modify inputs in many other ways, like editing `state.player_inputs.player_slot_1.buttons_gameplay` in PRE_UPDATE or a `set_pre_process_input` hook. Steal input from a Player, HiredHand or PlayerGhost. lua["steal_input"] = [](int uid) { static const auto player_ghost = to_id("ENT_TYPE_ITEM_PLAYERGHOST"); @@ -1359,6 +1360,7 @@ end backend->script_input[uid] = newinput; } }; + /// Deprecated /// Return input previously stolen with [steal_input](#steal_input) lua["return_input"] = [](int uid) { @@ -1383,6 +1385,7 @@ end } backend->script_input.erase(uid); }; + /// Deprecated /// Send input to entity, has to be previously stolen with [steal_input](#steal_input) lua["send_input"] = [](int uid, INPUTS buttons) { From f428207af2bce241a1b1a395612f1a1e9e0af02a Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 19:05:42 +0200 Subject: [PATCH 28/30] add generator range hitboxes --- src/injected/ui.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 5486bb6d1..7191178fc 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -4525,6 +4525,8 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, static const auto mine = to_id("ENT_TYPE_ITEM_LANDMINE"); static const auto keg = to_id("ENT_TYPE_ACTIVEFLOOR_POWDERKEG"); static const auto keg2 = to_id("ENT_TYPE_ACTIVEFLOOR_TIMEDPOWDERKEG"); + static const auto sun_generator = to_id("ENT_TYPE_FLOOR_SUNCHALLENGE_GENERATOR"); + static const auto shoppie_generator = to_id("ENT_TYPE_FLOOR_SHOPKEEPER_GENERATOR"); if (type == spark_trap && ent->animation_frame == 7) { @@ -4535,6 +4537,28 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, auto srad = screenify({radx, rady}); draw_list->AddCircle(fix_pos(spos), srad.x - spos.x, ImColor(255, 0, 0, 40), 0, sthick); } + else if ((type == sun_generator || type == shoppie_generator) && !g_players.empty()) + { + auto gen = ent->as(); + float rad = 8.0f; + float min_dist = 100.0f; + for (auto p : g_players) + { + auto [x, y] = UI::get_position(p); + auto a = Vec2(x, y); + auto b = Vec2(gen->x, gen->y); + auto dist = a.distance_to(b); + if (dist < min_dist) + min_dist = dist; + } + bool enabled = min_dist < 8.0f && (type != sun_generator || gen->on_off); + if (cross || enabled || min_dist < 8.0f) + { + auto [radx, rady] = UI::screen_position(render_position.first + rad, render_position.second + rad); + auto srad = screenify({radx, rady}); + draw_list->AddCircle(fix_pos(spos), srad.x - spos.x, enabled ? ImColor(255, 0, 0, 80) : ImColor(0, 200, 128, 60), 0, 2.0f); + } + } else if (type == bomb) { float rad = 1.6f; @@ -4838,6 +4862,9 @@ void render_clickhandler() to_id("ENT_TYPE_FLOOR_TENTACLE_BOTTOM"), to_id("ENT_TYPE_FLOOR_TELEPORTINGBORDER"), to_id("ENT_TYPE_FLOOR_SPIKES"), + to_id("ENT_TYPE_FLOOR_FACTORY_GENERATOR"), + to_id("ENT_TYPE_FLOOR_SHOPKEEPER_GENERATOR"), + to_id("ENT_TYPE_FLOOR_SUNCHALLENGE_GENERATOR"), }; for (auto entity : UI::get_entities_by(additional_fixed_entities, 0x180, (LAYER)(peek_layer ? g_state->camera_layer ^ 1 : g_state->camera_layer))) // FLOOR | ACTIVEFLOOR { From 793fd732c57be7427ee76b976ebb1dbc771e4083 Mon Sep 17 00:00:00 2001 From: Dregu Date: Fri, 3 Nov 2023 23:31:57 +0200 Subject: [PATCH 29/30] just try to resist the temptation --- docs/parse_source.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/parse_source.py b/docs/parse_source.py index 8e4e06d97..ed717c2f0 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -11,6 +11,7 @@ if not os.path.exists(".db"): os.makedirs(".db") +# this is common for generator.py and validator.py, not actually used nor should it be used here replace_table = { # standard basic types "uint8_t": "int", From a53925a333c6208113db710a07d1ff6e520bf912 Mon Sep 17 00:00:00 2001 From: Dregu Date: Sat, 4 Nov 2023 21:56:34 +0200 Subject: [PATCH 30/30] remove old patch watermark code --- src/game_api/bucket.hpp | 2 ++ src/game_api/state.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp index 92087a25c..4babaf4a7 100644 --- a/src/game_api/bucket.hpp +++ b/src/game_api/bucket.hpp @@ -44,4 +44,6 @@ class Bucket std::unordered_map data; /// Access Overlunky options here, nil if Overlunky is not loaded. Overlunky* overlunky{nullptr}; + // Used by state.cpp to determine if patches should still be applied. + bool patches_applied{false}; }; diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index dc22c7092..55e6dff5a 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -9,6 +9,7 @@ #include // for allocator, operator""sv, operator""s #include // for move +#include "bucket.hpp" // for Bucket #include "containers/custom_allocator.hpp" // #include "entities_chars.hpp" // for Player #include "entity.hpp" // for to_id, Entity, HookWithId, EntityDB @@ -296,11 +297,9 @@ State& State::get() strings_init(); init_state_update_hook(); - auto watermark_offset = get_address("destroy_game_manager") - 8; // pulled this out of a hat, its just a random place with some CCCC hopefully - auto watermark = memory_read(watermark_offset); - if (watermark != 0x4C4F4C4F) + auto bucket = Bucket::get(); + if (!bucket->patches_applied) { - write_mem_prot(watermark_offset, "\x4F\x4C\x4F\x4C", true); DEBUG("Applying patches"); patch_tiamat_kill_crash(); patch_orbs_limit(); @@ -308,6 +307,7 @@ State& State::get() patch_liquid_OOB(); patch_ushabti_error(); patch_entering_closed_door_crash(); + bucket->patches_applied = true; } else {