From d991bc9e285a70c98e011642f1a1d1aba674c6bc Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 20 Oct 2023 20:05:48 +0200 Subject: [PATCH 01/31] one pattern less --- src/game_api/rpc.cpp | 8 ++++---- src/game_api/rpc.hpp | 2 +- src/game_api/script/lua_vm.cpp | 2 +- src/game_api/search.cpp | 12 ++---------- src/injected/ui_util.cpp | 2 +- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 3e39554dc..ce5e1c8ed 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -37,6 +37,7 @@ #include "memory.hpp" // for write_mem_prot, write_mem_recoverable #include "movable.hpp" // for Movable #include "particles.hpp" // for ParticleEmitterInfo +#include "screen.hpp" // #include "search.hpp" // for get_address, find_inst #include "state.hpp" // for State, get_state_ptr, enum_to_layer #include "state_structs.hpp" // for ShopRestrictedItem, Illumination @@ -1510,11 +1511,10 @@ void game_log(std::string message) game_log_fun(log_stream, message.c_str(), nullptr, LogLevel::Info); } -void call_death_screen() +void load_death_screen() { - using DeathScreen = void(); - static auto death_screen = (DeathScreen*)get_address("death_screen"); - death_screen(); + auto state = State::get().ptr(); + state->screen_death->init(); } void save_progress() diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 3b366f459..77771ba5c 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -114,7 +114,7 @@ void update_liquid_collision_at(float x, float y, bool add); bool disable_floor_embeds(bool disable); void set_cursepot_ghost_enabled(bool enable); void game_log(std::string message); -void call_death_screen(); +void load_death_screen(); void save_progress(); void set_level_string(std::u16string_view text); void set_ending_unlock(ENT_TYPE type); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 3a79003b7..973eb50c6 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1923,7 +1923,7 @@ end lua["log_print"] = game_log; /// Immediately ends the run with the death screen, also calls the [save_progress](#save_progress) - lua["load_death_screen"] = call_death_screen; + lua["load_death_screen"] = load_death_screen; /// Saves the game to savegame.sav, unless game saves are blocked in the settings. Also runs the ON.SAVE callback. Fails and returns false, if you're trying to save too often (2s). lua["save_progress"] = []() -> bool diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index a566dcbd1..594a311e0 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1264,6 +1264,7 @@ std::unordered_map g_address_rules{ "show_journal"sv, // aka render_journal / open journal chapter // Break on GameManager.journal_ui.state, open the journal + // Or go to state->death screen, to first virtul function, the second call in that virtual is the function PatternCommandBuffer{} .find_inst("88 5F 04 80 FB 0B 0F"_gh) .at_exe() @@ -1878,16 +1879,7 @@ std::unordered_map g_address_rules{ .function_start(), }, { - // Set write bp on write_to_file, take a death in game, return from write_to_file, then from the next function as well - // you should be now in the death_screen function, the first call is also the save_progress function - "death_screen"sv, - PatternCommandBuffer{} - .find_inst("4D 0F 44 C1 49 8B 88 F0 12 00 00"_gh) - .at_exe() - .function_start(), - }, - { - // see death_screen + // go to state->death screen, to first virtual, this is the first call in that virtual "save_progress"sv, PatternCommandBuffer{} .find_inst("48 8B 90 F0 12 00 00 8B 5A 28"_gh) diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 2f073f953..9c41da597 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -767,5 +767,5 @@ std::pair UI::spawn_position() void UI::load_death_screen() { - call_death_screen(); + ::load_death_screen(); } From 6c1673c5ad13e2f4502ef4c2f82039f30f0f0813 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 20 Oct 2023 20:56:09 +0200 Subject: [PATCH 02/31] 4 pattern less --- src/game_api/level_api.cpp | 89 ++++++++-------------------------- src/game_api/search.cpp | 32 ------------ src/game_api/state.cpp | 2 +- src/game_api/virtual_table.cpp | 6 +++ src/game_api/virtual_table.hpp | 1 + 5 files changed, 29 insertions(+), 101 deletions(-) diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 8791b6d22..61fe393be 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -848,65 +848,6 @@ void level_gen(LevelGenSystem* level_gen_sys, float param_2, size_t param_3) g_levels_to_load.clear(); } -using TransGenFun = void(ThemeInfo*); -TransGenFun* g_trans_gen_trampoline{nullptr}; -TransGenFun* g_trans_gen2_trampoline{nullptr}; -using TransGenFun3 = void(size_t, size_t, ThemeInfo*); -TransGenFun3* g_trans_gen3_trampoline{nullptr}; -using TransGenFun4 = void(size_t, size_t, size_t, size_t, size_t, size_t); -TransGenFun4* g_trans_gen4_trampoline{nullptr}; -// generic transition hook -void trans_gen(ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen_trampoline(theme); - post_level_generation(); -} -// cosmic transition hook -void trans_gen2(ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen2_trampoline(theme); - post_level_generation(); -} -// cog-duat transition hook -void trans_gen3(size_t a, size_t b, ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - auto state = State::get().ptr(); - // trampoline will call the generic trans_gen if not going to duat - if (state->theme_next == 12 && pre_level_generation()) - return; - g_trans_gen3_trampoline(a, b, theme); - if (state->theme_next == 12) - post_level_generation(); -} -// olmecship transition hook -void trans_gen4(size_t a, size_t b, size_t c, size_t d, size_t e, size_t f) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen4_trampoline(a, b, c, d, e, f); - post_level_generation(); -} - using LoadScreenFun = void(StateMemory*, size_t, size_t); LoadScreenFun* g_load_screen_trampoline{nullptr}; void load_screen(StateMemory* state, size_t param_2, size_t param_3) @@ -1560,10 +1501,6 @@ void LevelGenData::init() g_load_screen_trampoline = (LoadScreenFun*)get_address("load_screen_func"sv); g_unload_layer_trampoline = (UnloadLayerFun*)get_address("unload_layer"sv); g_init_layer_trampoline = (InitLayerFun*)get_address("init_layer"sv); - g_trans_gen_trampoline = (TransGenFun*)get_address("spawn_transition"sv); - g_trans_gen2_trampoline = (TransGenFun*)get_address("spawn_transition_cosmic"sv); - g_trans_gen3_trampoline = (TransGenFun3*)get_address("spawn_transition_duat"sv); - g_trans_gen4_trampoline = (TransGenFun4*)get_address("spawn_transition_olmecship"sv); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); @@ -1581,15 +1518,11 @@ void LevelGenData::init() DetourAttach((void**)&g_load_screen_trampoline, load_screen); DetourAttach((void**)&g_unload_layer_trampoline, unload_layer); DetourAttach((void**)&g_init_layer_trampoline, load_layer); - DetourAttach((void**)&g_trans_gen_trampoline, trans_gen); - DetourAttach((void**)&g_trans_gen2_trampoline, trans_gen2); - DetourAttach((void**)&g_trans_gen3_trampoline, trans_gen3); - DetourAttach((void**)&g_trans_gen4_trampoline, trans_gen4); const LONG error = DetourTransactionCommit(); if (error != NO_ERROR) { - DEBUG("Failed hooking HandleTileCode: {}\n", error); + DEBUG("Failed hooking LevelGenData stuff: {}\n", error); } } @@ -1880,6 +1813,26 @@ uint32_t ThemeInfo::get_aux_id() void LevelGenSystem::init() { data->init(); + + for (auto theme : themes) + { + if (theme == theme_arena) // no reason to? + continue; + + hook_vtable( // spawn_transition + theme, + [](ThemeInfo* th, void (*original)(ThemeInfo*)) + { + push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); + OnScopeExit pop{[] + { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; + + if (pre_level_generation()) + return; + original(th); + post_level_generation(); + }); + } } void LevelGenSystem::populate_level_hook(ThemeInfo* self, uint64_t param_2, uint64_t param_3, uint64_t param_4, PopulateLevelFun* original) diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 594a311e0..33487779b 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -2069,38 +2069,6 @@ std::unordered_map g_address_rules{ .function_start(), //.from_exe_base(0x22c061d0), }, - { - "spawn_transition"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_DWELLING, VIRT_FUNC::THEME_SPAWN_TRANSITION) - .at_exe(), - //.from_exe_base(0x22afe5c0), - }, - { - "spawn_transition_cosmic"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_COSMICOCEAN, VIRT_FUNC::THEME_SPAWN_TRANSITION) - .at_exe(), - //.from_exe_base(0x22b373b0), - }, - { - "spawn_transition_duat"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_CITY_OF_GOLD, VIRT_FUNC::THEME_SPAWN_TRANSITION) - .at_exe(), - //.from_exe_base(0x22b34940), - }, - { - "spawn_transition_olmecship"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_BASECAMP, VIRT_FUNC::THEME_SPAWN_TRANSITION) - .at_exe(), - //.from_exe_base(0x22b2d350), - }, }; std::unordered_map g_cached_addresses; diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 668e7737a..a78f7963d 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -280,6 +280,7 @@ State& State::get() } auto addr_location = get_address("state_location"); STATE = State{addr_location}; + get_is_init() = true; if (get_do_hooks()) { @@ -310,7 +311,6 @@ State& State::get() DEBUG("Not applying patches, someone has already done it"); } } - get_is_init() = true; } return STATE; } diff --git a/src/game_api/virtual_table.cpp b/src/game_api/virtual_table.cpp index f256fffed..3d1fd0ea3 100644 --- a/src/game_api/virtual_table.cpp +++ b/src/game_api/virtual_table.cpp @@ -15,3 +15,9 @@ size_t get_virtual_function_address(VTABLE_OFFSET table_entry, uint32_t function size_t* func_address = reinterpret_cast(first_table_entry + ((static_cast(table_entry) + function_index) * sizeof(size_t))); return *func_address - mem.exe_ptr; } + +size_t get_virtual_function_address(void* object, uint32_t function_index) +{ + auto v_table = *static_cast(object); + return *(v_table + function_index); +} diff --git a/src/game_api/virtual_table.hpp b/src/game_api/virtual_table.hpp index 8a4a3c728..ed368b9ce 100644 --- a/src/game_api/virtual_table.hpp +++ b/src/game_api/virtual_table.hpp @@ -990,3 +990,4 @@ enum class VTABLE_OFFSET }; size_t get_virtual_function_address(VTABLE_OFFSET table_entry, uint32_t function_index); +size_t get_virtual_function_address(void* object, uint32_t function_index); From 5cfba4ab2a0966aae0655a8034c6a97b4bdb4968 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 20 Oct 2023 21:46:41 +0200 Subject: [PATCH 03/31] another one --- src/game_api/rpc.cpp | 8 +++----- src/game_api/search.cpp | 9 --------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index ce5e1c8ed..fbf1e9d02 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1940,11 +1940,9 @@ void create_level() void set_death_enabled(bool enable) { - static size_t offset = 0; - if (offset == 0) - { - offset = get_address("dead_players"); - } + auto state = State::get().ptr(); + static size_t offset = get_virtual_function_address(state->screen_level, 1); + if (offset != 0) { if (!enable) diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 33487779b..1cb03c539 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -2060,15 +2060,6 @@ std::unordered_map g_address_rules{ .function_start(), //.from_exe_base(0x228b58f0), }, - { - "dead_players"sv, - // I guess it writes 14 to screen_next before the death screen pops up. Apparently it's a SCREEN_LEVEL virtual too. - PatternCommandBuffer{} - .find_inst("4c 8b b8 e8 12 00 00 48 8b 80 f0 12 00 00"_gh) - .at_exe() - .function_start(), - //.from_exe_base(0x22c061d0), - }, }; std::unordered_map g_cached_addresses; From 9438478d253b9d54e81ae74f603f95b0550144e1 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 20 Oct 2023 21:56:07 +0200 Subject: [PATCH 04/31] just rename --- src/game_api/rpc.cpp | 6 +++--- src/game_api/rpc.hpp | 2 +- src/game_api/script/lua_vm.cpp | 2 +- src/injected/ui_util.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index fbf1e9d02..3d1809fd3 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1938,7 +1938,7 @@ void create_level() create_layer(1); } -void set_death_enabled(bool enable) +void set_level_logic_enabled(bool enable) { auto state = State::get().ptr(); static size_t offset = get_virtual_function_address(state->screen_level, 1); @@ -1946,8 +1946,8 @@ void set_death_enabled(bool enable) if (offset != 0) { if (!enable) - write_mem_recoverable("death_disable", offset, "\xC3\x90"sv, true); + write_mem_recoverable("set_level_logic_enabled", offset, "\xC3\x90"sv, true); else - recover_mem("death_disable"); + recover_mem("set_level_logic_enabled"); } } diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 77771ba5c..b24257d62 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -138,4 +138,4 @@ void destroy_layer(uint8_t layer); void destroy_level(); void create_layer(uint8_t layer); void create_level(); -void set_death_enabled(bool enable); +void set_level_logic_enabled(bool enable); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 973eb50c6..7b230907b 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2155,7 +2155,7 @@ end lua["create_layer"] = create_layer; /// 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; + lua["set_level_logic_enabled"] = set_level_logic_enabled; /// Converts INPUTS to (x, y, BUTTON) lua["inputs_to_buttons"] = [](INPUTS inputs) -> std::tuple diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 9c41da597..9a72e1fcd 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -38,7 +38,7 @@ void UI::godmode_companions(bool g) } void UI::death_enabled(bool g) { - set_death_enabled(g); + set_level_logic_enabled(g); } std::pair UI::click_position(float x, float y) { From 704d9b9ee910173a0fcfab9f194841f1bb5acf72 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:17:46 +0200 Subject: [PATCH 05/31] make it big letters --- src/game_api/script/lua_vm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 7b230907b..6b4ccc024 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1910,13 +1910,13 @@ end /// Get the rva for a pattern name, used for debugging. lua["get_rva"] = [](std::string_view address_name) -> std::string { - return fmt::format("{:x}", get_address(address_name) - Memory::get().at_exe(0)); + return fmt::format("{:X}", get_address(address_name) - Memory::get().at_exe(0)); }; /// Get the rva for a vtable offset and index, used for debugging. lua["get_virtual_rva"] = [](VTABLE_OFFSET offset, uint32_t index) -> std::string { - return fmt::format("{:x}", get_virtual_function_address(offset, index)); + return fmt::format("{:X}", get_virtual_function_address(offset, index)); }; /// Log to spelunky.log From ff9b1dacecdaf1768efc637f7598256336ab6e73 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:20:00 +0200 Subject: [PATCH 06/31] screen level thing --- src/game_api/screen.hpp | 4 +++- src/game_api/script/usertypes/screen_lua.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/game_api/screen.hpp b/src/game_api/screen.hpp index 0c4eb878a..f9a25174c 100644 --- a/src/game_api/screen.hpp +++ b/src/game_api/screen.hpp @@ -24,7 +24,7 @@ class Screen uint32_t unknown_zero; virtual void init() = 0; - virtual void handle_player() = 0; // for normal level: death, camera zoom, camera bounds, some save data stuff + virtual void handle_player() = 0; // for normal level: death, camera zoom (level/shop), camera bounds, some save data stuff virtual ~Screen() = 0; virtual void render() = 0; // mostly used by the non gameplay screens to draw textures and text @@ -484,6 +484,8 @@ class ScreenLevel : public Screen // ID: 12 { public: uint8_t buttons; + /// Delay after player death to open the death screen + int8_t time_till_death_screen; }; class ScreenTransition : public Screen // ID: 13 diff --git a/src/game_api/script/usertypes/screen_lua.cpp b/src/game_api/script/usertypes/screen_lua.cpp index d7847429d..fdbfd7bf6 100644 --- a/src/game_api/script/usertypes/screen_lua.cpp +++ b/src/game_api/script/usertypes/screen_lua.cpp @@ -340,6 +340,8 @@ void register_usertypes(sol::state& lua) "ScreenLevel", "buttons", &ScreenLevel::buttons, + "time_till_death_screen", + &ScreenLevel::time_till_death_screen, sol::base_classes, sol::bases()); From 56e3813432f5dda83d1ac75c06e07971c447f600 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:12:43 +0200 Subject: [PATCH 07/31] super small optimisation in `get_entities_by` --- src/game_api/entity_lookup.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/game_api/entity_lookup.cpp b/src/game_api/entity_lookup.cpp index 5b9273b0b..e27088238 100644 --- a/src/game_api/entity_lookup.cpp +++ b/src/game_api/entity_lookup.cpp @@ -102,7 +102,7 @@ void foreach_mask(uint32_t mask, Layer* l, FunT&& fun) std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer) { - auto state = State::get(); + auto state = State::get().ptr(); std::vector found; const std::vector proper_types = get_proper_types(std::move(entity_types)); @@ -124,25 +124,27 @@ std::vector get_entities_by(std::vector entity_types, uint32 if (layer == LAYER::BOTH) { + auto layer_front = state->layers[0]; + auto layer_back = state->layers[1]; if (proper_types.empty() || proper_types[0] == 0) { if (mask == 0) // all entities { // this exception for small improvments with calling reserve once - found.reserve(found.size() + (size_t)state.layer(0)->all_entities.size + (size_t)state.layer(1)->all_entities.size); - found.insert(found.end(), state.layer(0)->all_entities.uids().begin(), state.layer(0)->all_entities.uids().end()); - found.insert(found.end(), state.layer(1)->all_entities.uids().begin(), state.layer(1)->all_entities.uids().end()); + found.reserve(found.size() + (size_t)layer_front->all_entities.size + (size_t)layer_back->all_entities.size); + found.insert(found.end(), layer_front->all_entities.uids().begin(), layer_front->all_entities.uids().end()); + found.insert(found.end(), layer_back->all_entities.uids().begin(), layer_back->all_entities.uids().end()); } else // all types { - foreach_mask(mask, state.layer(0), insert_all_uids); - foreach_mask(mask, state.layer(1), insert_all_uids); + foreach_mask(mask, layer_front, insert_all_uids); + foreach_mask(mask, layer_back, insert_all_uids); } } else { - foreach_mask(mask, state.layer(0), push_matching_types); - foreach_mask(mask, state.layer(1), push_matching_types); + foreach_mask(mask, layer_front, push_matching_types); + foreach_mask(mask, layer_back, push_matching_types); } } else @@ -150,11 +152,11 @@ std::vector get_entities_by(std::vector entity_types, uint32 uint8_t correct_layer = enum_to_layer(layer); if (proper_types.empty() || proper_types[0] == 0) // all types { - foreach_mask(mask, state.layer(correct_layer), insert_all_uids); + foreach_mask(mask, state->layers[correct_layer], insert_all_uids); } else { - foreach_mask(mask, state.layer(correct_layer), push_matching_types); + foreach_mask(mask, state->layers[correct_layer], push_matching_types); } } return found; @@ -167,6 +169,7 @@ std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius) { + // TODO: use entitie regions? auto state = State::get(); std::vector found; const std::vector proper_types = get_proper_types(std::move(entity_types)); @@ -175,7 +178,7 @@ std::vector get_entities_at(std::vector entity_types, uint32 for (auto& item : entities.entities()) { auto [ix, iy] = item->position(); - float distance = sqrt(pow(x - ix, 2.0f) + pow(y - iy, 2.0f)); + float distance = std::sqrt(std::pow(x - ix, 2.0f) + std::pow(y - iy, 2.0f)); if (distance < radius && entity_type_check(proper_types, item->type->id)) { found.push_back(item->uid); @@ -201,6 +204,7 @@ std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer) { + // TODO: use entitie regions? auto state = State::get(); std::vector result; const std::vector proper_types = get_proper_types(std::move(entity_types)); From 84322192d74b4b4429a530f8b2df325850c35aeb Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:15:00 +0200 Subject: [PATCH 08/31] comment out unused stuff --- src/game_api/level_api.cpp | 15 +++++++++------ src/game_api/level_api.hpp | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 61fe393be..2e714cb22 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -1850,12 +1850,15 @@ void LevelGenSystem::populate_level_hook(ThemeInfo* self, uint64_t param_2, uint original(self, param_2, param_3, param_4); } -void LevelGenSystem::populate_transition_hook(ThemeInfo* self, PopulateTransitionFun* original) -{ - pre_level_generation(); - original(self); - post_level_generation(); -} +// void LevelGenSystem::populate_transition_hook([[maybe_unused]] ThemeInfo* self, [[maybe_unused]] PopulateTransitionFun* original) +//{ +// push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); +// OnScopeExit pop{[] +// { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; +// pre_level_generation(); +// original(self); +// post_level_generation(); +// } void LevelGenSystem::do_procedural_spawn_hook(ThemeInfo* self, SpawnInfo* spawn_info, DoProceduralSpawnFun* original) { push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_PROCEDURAL); diff --git a/src/game_api/level_api.hpp b/src/game_api/level_api.hpp index ab8108d36..6a5cbbc74 100644 --- a/src/game_api/level_api.hpp +++ b/src/game_api/level_api.hpp @@ -431,8 +431,8 @@ struct LevelGenSystem using PopulateLevelFun = void(ThemeInfo*, uint64_t, uint64_t, uint64_t); static void populate_level_hook(ThemeInfo*, uint64_t, uint64_t, uint64_t, PopulateLevelFun*); - using PopulateTransitionFun = void(ThemeInfo*); - static void populate_transition_hook(ThemeInfo*, PopulateTransitionFun*); + // using PopulateTransitionFun = void(ThemeInfo*); + // static void populate_transition_hook(ThemeInfo*, PopulateTransitionFun*); using DoProceduralSpawnFun = void(ThemeInfo*, SpawnInfo*); static void do_procedural_spawn_hook(ThemeInfo*, SpawnInfo*, DoProceduralSpawnFun*); From 31c9628368b366c3e5f2fa2c1c1695569751b949 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:15:33 +0200 Subject: [PATCH 09/31] fix some super rare bug --- src/game_api/state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index a78f7963d..7ef2c9319 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -724,7 +724,7 @@ uint8_t enum_to_layer(const LAYER layer) auto player = state->items->player(static_cast(std::abs((int)layer) - 1)); if (player != nullptr) { - return player->layer; + return player->layer > 1 ? 0 : player->layer; } } return 0; From b256fda431d482f1f0c569033071cef3c51e27a2 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:16:45 +0200 Subject: [PATCH 10/31] if layer() and layer_local() do the same thing, just use one, so everything is local unless you specifically want main --- src/game_api/entities_floors.cpp | 2 +- src/game_api/level_api.cpp | 10 +++++----- src/game_api/rpc.cpp | 8 ++++---- src/game_api/spawn_api.cpp | 22 +++++++++++----------- src/game_api/state.hpp | 5 +---- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/game_api/entities_floors.cpp b/src/game_api/entities_floors.cpp index b889524bb..4d8cd54a6 100644 --- a/src/game_api/entities_floors.cpp +++ b/src/game_api/entities_floors.cpp @@ -801,7 +801,7 @@ void Door::unlock(bool unlock) { if (!main_door->door_blocker) { - main_door->door_blocker = state.layer_local(layer)->spawn_entity_over(door_bg_large, this, 0, 2.0); + main_door->door_blocker = state.layer(layer)->spawn_entity_over(door_bg_large, this, 0, 2.0); main_door->door_blocker->animation_frame = 1; } } diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 2e714cb22..7544b72d7 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -1278,7 +1278,7 @@ bool handle_chance(SpawnInfo* spawn_info) auto level_gen_data = State::get().ptr()->level_gen->data; uint8_t layer = 0; - auto* layer_ptr = State::get().layer_local(layer); + auto* layer_ptr = State::get().layer(layer); for (const CommunityChance& community_chance : g_community_chances) { if (community_chance.test_func(community_chance, spawn_info->x, spawn_info->y, layer_ptr)) @@ -1664,7 +1664,7 @@ std::uint32_t LevelGenData::register_chance_logic_provider(std::uint32_t chance_ { provider.is_valid = [](float x, float y, uint8_t layer) { - return g_DefaultTestFunc(x, y, State::get().layer_local(layer)); + return g_DefaultTestFunc(x, y, State::get().layer(layer)); }; } @@ -1688,7 +1688,7 @@ std::uint32_t LevelGenData::define_extra_spawn(std::uint32_t num_spawns_front_la { provider.is_valid = [](float x, float y, uint8_t layer) { - return g_DefaultTestFunc(x, y, State::get().layer_local(layer)); + return g_DefaultTestFunc(x, y, State::get().layer(layer)); }; } @@ -2102,13 +2102,13 @@ bool LevelGenSystem::set_procedural_spawn_chance(uint32_t chance_id, uint32_t in bool default_spawn_is_valid(float x, float y, LAYER layer) { uint8_t correct_layer = enum_to_layer(layer); - return g_DefaultTestFunc(x, y, State::get().layer_local(correct_layer)); + return g_DefaultTestFunc(x, y, State::get().layer(correct_layer)); } bool position_is_valid(float x, float y, LAYER layer, POS_TYPE flags) { uint8_t correct_layer = enum_to_layer(layer); - return g_PositionTestFunc(x, y, State::get().layer_local(correct_layer), (uint32_t)flags); + return g_PositionTestFunc(x, y, State::get().layer(correct_layer), (uint32_t)flags); } void override_next_levels(std::vector next_levels) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 3d1809fd3..facffbc13 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1412,14 +1412,14 @@ void add_item_to_shop(int32_t item_uid, int32_t shop_owner_uid) { if (owner->type->id == it) // TODO: check what happens if it's not room owner/shopkeeper { - auto state = State::get(); + auto state = State::get().ptr(); item->flags = setflag(item->flags, 23); // shop item item->flags = setflag(item->flags, 20); // Enable button prompt (flag is problably: show dialogs and other fx) - state.layer_local(item->layer)->spawn_entity_over(to_id("ENT_TYPE_FX_SALEICON"), item, 0, 0); - state.layer_local(item->layer)->spawn_entity_over(to_id("ENT_TYPE_FX_SALEDIALOG_CONTAINER"), item, 0, 0.5); + state->layers[item->layer]->spawn_entity_over(to_id("ENT_TYPE_FX_SALEICON"), item, 0, 0); + state->layers[item->layer]->spawn_entity_over(to_id("ENT_TYPE_FX_SALEDIALOG_CONTAINER"), item, 0, 0.5); ItemOwnerDetails iod{shop_owner_uid, owner->type->id}; - state.ptr()->room_owners.owned_items.insert({item->uid, iod}); + state->room_owners.owned_items.insert({item->uid, iod}); return; } } diff --git a/src/game_api/spawn_api.cpp b/src/game_api/spawn_api.cpp index 7cf829e97..600319403 100644 --- a/src/game_api/spawn_api.cpp +++ b/src/game_api/spawn_api.cpp @@ -166,7 +166,7 @@ int32_t spawn_entity_abs(ENT_TYPE entity_type, float x, float y, LAYER layer, fl std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, vx, vy, false)->uid; + return State::get().layer(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, vx, vy, false)->uid; } int32_t spawn_entity_snap_to_floor(ENT_TYPE entity_type, float x, float y, LAYER layer) @@ -178,7 +178,7 @@ int32_t spawn_entity_snap_to_floor(ENT_TYPE entity_type, float x, float y, LAYER std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity_snap_to_floor(entity_type, x + offset_position.first, y + offset_position.second)->uid; + return State::get().layer(actual_layer)->spawn_entity_snap_to_floor(entity_type, x + offset_position.first, y + offset_position.second)->uid; } int32_t spawn_entity_snap_to_grid(ENT_TYPE entity_type, float x, float y, LAYER layer) @@ -190,7 +190,7 @@ int32_t spawn_entity_snap_to_grid(ENT_TYPE entity_type, float x, float y, LAYER std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, 0.0f, 0.0f, true)->uid; + return State::get().layer(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, 0.0f, 0.0f, true)->uid; } int32_t spawn_entity_abs_nonreplaceable(ENT_TYPE entity_type, float x, float y, LAYER layer, float vx, float vy) @@ -215,7 +215,7 @@ int32_t spawn_entity_over(ENT_TYPE entity_type, uint32_t over_uid, float x, floa if (layer > 1) return -1; - return state.layer_local(layer)->spawn_entity_over(entity_type, overlay, x, y)->uid; + return state.layer(layer)->spawn_entity_over(entity_type, overlay, x, y)->uid; } int32_t spawn_door_abs(float x, float y, LAYER layer, uint8_t w, uint8_t l, uint8_t t) @@ -227,7 +227,7 @@ int32_t spawn_door_abs(float x, float y, LAYER layer, uint8_t w, uint8_t l, uint std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_door(x + offset_position.first, y + offset_position.second, w, l, t)->uid; + return State::get().layer(actual_layer)->spawn_door(x + offset_position.first, y + offset_position.second, w, l, t)->uid; } void spawn_backdoor_abs(float x, float y) @@ -238,8 +238,8 @@ void spawn_backdoor_abs(float x, float y) auto state = State::get(); DEBUG("Spawning backdoor on {}, {}", x, y); - Layer* front_layer = state.layer_local(0); - Layer* back_layer = state.layer_local(1); + Layer* front_layer = state.layer(0); + Layer* back_layer = state.layer(1); front_layer->spawn_entity(to_id("ENT_TYPE_FLOOR_DOOR_LAYER"), x, y, false, 0.0, 0.0, true); back_layer->spawn_entity(to_id("ENT_TYPE_FLOOR_DOOR_LAYER"), x, y, false, 0.0, 0.0, true); front_layer->spawn_entity(to_id("ENT_TYPE_LOGICAL_PLATFORM_SPAWNER"), x, y - 1.0f, false, 0.0, 0.0, true); @@ -255,7 +255,7 @@ int32_t spawn_apep(float x, float y, LAYER layer, bool right) std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_apep(x + offset_position.first, y + offset_position.second, right)->uid; + return State::get().layer(actual_layer)->spawn_apep(x + offset_position.first, y + offset_position.second, right)->uid; } int32_t spawn_tree(float x, float y, LAYER layer) @@ -274,7 +274,7 @@ int32_t spawn_tree(float x, float y, LAYER layer, uint16_t height) x = std::roundf(x + offset_position.first); y = std::roundf(y + offset_position.second); - Layer* layer_ptr = State::get().layer_local(actual_layer); + Layer* layer_ptr = State::get().layer(actual_layer); // Needs some space on top if (x < 0 || static_cast(x) >= g_level_max_x || y < 1 || static_cast(y) + 2 >= g_level_max_y || height == 1 || @@ -358,7 +358,7 @@ int32_t spawn_mushroom(float x, float y, LAYER l, uint16_t height) // height rel std::pair offset(0.0f, 0.0f); const auto actual_layer = enum_to_layer(l, offset); - const auto layer_ptr = State::get().layer_local(actual_layer); + const auto layer_ptr = State::get().layer(actual_layer); const uint32_t i_x = static_cast(x + offset.first + 0.5f); uint32_t i_y = static_cast(y + offset.second + 0.5f); static const auto base = to_id("ENT_TYPE_FLOOR_MUSHROOM_BASE"); @@ -427,7 +427,7 @@ int32_t spawn_unrolled_player_rope(float x, float y, LAYER layer, TEXTURE textur std::pair offset(0.0f, 0.0f); const auto actual_layer = enum_to_layer(layer, offset); - const auto layer_ptr = State::get().layer_local(actual_layer); + const auto layer_ptr = State::get().layer(actual_layer); const uint32_t i_x = static_cast(x + offset.first + 0.5f); const uint32_t i_y = static_cast(y + offset.second + 0.5f); const float g_x = static_cast(i_x); diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index d74a015b1..c3923c980 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -309,14 +309,11 @@ struct State StateMemory* ptr() const; StateMemory* ptr_local() const; + // use only if you only want the layer, otherwise use `ptr()->layers` Layer* layer(uint8_t index) const { return ptr()->layers[index]; } - Layer* layer_local(uint8_t index) const - { - return ptr_local()->layers[index]; - } void godmode(bool g); void godmode_companions(bool g); From e649a7711412173aea8c0e3eb2d21669d464964b Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:50:31 +0200 Subject: [PATCH 11/31] use advantages of map and initializer list for `worn_backitem` function, other small stuff --- src/game_api/entities_chars.cpp | 2 +- src/game_api/entities_chars.hpp | 9 +++++---- src/game_api/layer.cpp | 2 +- src/game_api/rpc.cpp | 13 ++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/game_api/entities_chars.cpp b/src/game_api/entities_chars.cpp index 099065e9f..02b651b70 100644 --- a/src/game_api/entities_chars.cpp +++ b/src/game_api/entities_chars.cpp @@ -68,7 +68,7 @@ uint8_t Player::kapala_blood_amount() void Player::set_jetpack_fuel(uint8_t fuel) { static auto jetpackID = to_id("ENT_TYPE_ITEM_JETPACK"); - for (auto item : items.entities()) + for (auto item : items.entities()) // TODO: should probably change to powerups right? { if (item->type->id == jetpackID) { diff --git a/src/game_api/entities_chars.hpp b/src/game_api/entities_chars.hpp index f326d5a51..d84c752e6 100644 --- a/src/game_api/entities_chars.hpp +++ b/src/game_api/entities_chars.hpp @@ -7,9 +7,10 @@ #include // for u16string, allocator #include // for vector -#include "aliases.hpp" // for ENT_TYPE -#include "color.hpp" // for Color -#include "movable.hpp" // for Movable +#include "aliases.hpp" // for ENT_TYPE +#include "color.hpp" // for Color +#include "containers/custom_map.hpp" // +#include "movable.hpp" // for Movable struct Illumination; struct PlayerInputs; @@ -60,7 +61,7 @@ class Ai class PowerupCapable : public Movable { public: - std::map powerups; // type id -> entity + custom_map powerups; // type id -> entity /// Removes a currently applied powerup. Specify `ENT_TYPE.ITEM_POWERUP_xxx`, not `ENT_TYPE.ITEM_PICKUP_xxx`! Removing the Eggplant crown does not seem to undo the throwing of eggplants, the other powerups seem to work. void remove_powerup(ENT_TYPE powerup_type); diff --git a/src/game_api/layer.cpp b/src/game_api/layer.cpp index 735f8cd2f..a6aed792c 100644 --- a/src/game_api/layer.cpp +++ b/src/game_api/layer.cpp @@ -149,7 +149,7 @@ Entity* Layer::spawn_apep(float x, float y, bool right) { static const auto head_id = to_id("ENT_TYPE_MONS_APEP_HEAD"); static const auto body_id = to_id("ENT_TYPE_MONS_APEP_BODY"); - static const auto facing_left_flag = 1 << 16; + constexpr auto facing_left_flag = 1 << 16; Entity* apep_head = spawn_entity(head_id, x, y, false, 0.0f, 0.0f, true); const bool facing_left = apep_head->flags & facing_left_flag; diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index facffbc13..de53e99e4 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -613,7 +613,7 @@ void unequip_backitem(uint32_t who_uid) int32_t worn_backitem(uint32_t who_uid) { - static const std::unordered_set backitem_types = { + static const auto backitem_types = { to_id("ENT_TYPE_ITEM_JETPACK"), to_id("ENT_TYPE_ITEM_HOVERPACK"), to_id("ENT_TYPE_ITEM_POWERPACK"), @@ -623,14 +623,13 @@ int32_t worn_backitem(uint32_t who_uid) }; auto ent = get_entity_ptr(who_uid)->as(); - if (ent != nullptr) + if (ent != nullptr && !ent->powerups.empty()) { - for (const auto& [powerup_type, powerup_entity] : ent->powerups) + for (auto powerup_type : backitem_types) { - if (backitem_types.count(powerup_type) > 0) - { - return powerup_entity->uid; - } + auto it = ent->powerups.find(powerup_type); + if (it != ent->powerups.end()) + return it->second->uid; } } return -1; From f6ab87e96ef7c6cc4b695566e8a2b7be4893c857 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:34:36 +0200 Subject: [PATCH 12/31] one more pattern gone --- src/game_api/rpc.cpp | 18 ++++++++++++------ src/game_api/search.cpp | 13 +++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index de53e99e4..dad9135c5 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -757,6 +757,8 @@ void set_time_jelly_enabled(bool b) bool is_inside_active_shop_room(float x, float y, LAYER layer) { + // this functions just calculates the room index and then loops thru state->room_owners->owned_rooms and compares the room index + // TODO: we could probably get rid of this pattern and write that ourselves static size_t offset = get_address("coord_inside_active_shop_room"); if (offset != 0) { @@ -769,20 +771,24 @@ bool is_inside_active_shop_room(float x, float y, LAYER layer) bool is_inside_shop_zone(float x, float y, LAYER layer) { + // this function is weird, the main check does this (where rax is the room_template): + // ecx = rax - 0x41 + // cmp cx, 0x17 + // ja return 0 + // + // if it doesn't jump there is a bunch of coordinate checks but also state.presence_flags, flipped rooms ... + static size_t offset = 0; - static void* rcx = nullptr; + auto state = State::get().ptr_main(); // the game gets level gen from heap pointer and we always get it from state, not sure if it matters if (offset == 0) { offset = get_address("coord_inside_shop_zone"); - size_t* tmp = (size_t*)get_address("coord_inside_shop_zone_rcx"); - auto heap_ptr = OnHeapPointer(*tmp); - rcx = heap_ptr.decode(); } if (offset != 0) { - typedef bool coord_inside_shop_zone_func(void*, uint32_t layer, float x, float y); + typedef bool coord_inside_shop_zone_func(LevelGenSystem*, uint32_t layer, float x, float y); static coord_inside_shop_zone_func* ciszf = (coord_inside_shop_zone_func*)(offset); - return ciszf(rcx, enum_to_layer(layer), x, y); + return ciszf(state->level_gen, enum_to_layer(layer), x, y); } return false; } diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 1cb03c539..5e45528f6 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -530,6 +530,7 @@ std::unordered_map g_address_rules{ }, { "state_location"sv, + // actually it's state offset, at the time of writing this comment it's 4A0, found ... almost everywhere PatternCommandBuffer{} .find_inst("\x49\x0F\x44\xC0"sv) .find_next_inst("\x49\x0F\x44\xC0"sv) @@ -1114,6 +1115,7 @@ std::unordered_map g_address_rules{ "coord_inside_active_shop_room"sv, // Same pattern as default_zoom_level_shop, check the condition before the jump that decides whether to activate // the shop zoom level or regular zoom level + // Found at second to last function inside screen level -> handle players (second) virtual function PatternCommandBuffer{} .find_inst("\xF3\x0F\x11\xB0****\x49"sv) .offset(-0x24) @@ -1123,22 +1125,13 @@ std::unordered_map g_address_rules{ { "coord_inside_shop_zone"sv, // Can be found in same function as default_zoom_level_shop, check the condition higher up + // Also found in screen level -> handle players (second) virtual function but more like in the middle of it PatternCommandBuffer{} .find_inst("\x40\x8A\xBB\xA0\x00\x00\x00\x89\xFA\xE8"sv) .offset(0x9) .decode_call() .at_exe(), }, - { - "coord_inside_shop_zone_rcx"sv, - // See coord_inside_shop_zone, a little higher up rcx gets set to an on heap pointer - PatternCommandBuffer{} - .find_inst("\x0F\x84****\x48\x8B\x05****\x4A\x8D\x0C\x08"sv) - .find_next_inst("\x0F\x84****\x48\x8B\x05****\x4A\x8D\x0C\x08"sv) - .offset(0x6) - .decode_pc() - .at_exe(), - }, { "enforce_camp_camera_bounds"sv, // Go into basecamp, put a write bp on state.camera.bounds.top From 7c357f22b1d395d323548b0729fefb98a1ccec84 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:00:50 +0200 Subject: [PATCH 13/31] add `set_camera_layer_control_enabled` --- .../set_camera_layer_control_enabled.lua | 24 +++++++ src/game_api/rpc.cpp | 69 ++++++++++++------- src/game_api/rpc.hpp | 1 + src/game_api/script/lua_vm.cpp | 5 ++ src/game_api/script/usertypes/state_lua.cpp | 3 +- src/game_api/search.cpp | 17 +++++ src/game_api/spawn_api.cpp | 37 +++++----- src/game_api/state.hpp | 34 ++++++--- src/injected/ui.cpp | 2 +- 9 files changed, 138 insertions(+), 54 deletions(-) create mode 100644 docs/examples/set_camera_layer_control_enabled.lua diff --git a/docs/examples/set_camera_layer_control_enabled.lua b/docs/examples/set_camera_layer_control_enabled.lua new file mode 100644 index 000000000..5140c0d27 --- /dev/null +++ b/docs/examples/set_camera_layer_control_enabled.lua @@ -0,0 +1,24 @@ +set_camera_layer_control_enabled(false) + +g_current_timer = nil +-- default load_time 36 +function change_layer(layer_to, load_time) + + if state.camera_layer == layer_to then + return + end + if g_current_timer ~= nil then + clear_callback(g_current_timer) + g_current_timer = nil + end + -- if we don't want the load time, we can just change the actual layer + if load_time == nil or load_time == 0 then + state.camera_layer = layer_to + return + end + + state.layer_transition_timer = load_time + state.transition_to_layer = layer_to + -- actual layer change after time delay + set_timeout(function() state.camera_layer = layer_to end, load_time) +end diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index dad9135c5..246c024b5 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -20,29 +20,30 @@ #include // for _Uset_traits<>::allocator_type, _Use... #include // for min, max, pair, find -#include "custom_types.hpp" // for get_custom_entity_types, CUSTOM_TYPE -#include "entities_chars.hpp" // for Player (ptr only), PowerupCapable -#include "entities_floors.hpp" // for ExitDoor, Door -#include "entities_items.hpp" // for StretchChain, PunishBall, Container -#include "entities_liquids.hpp" // for Liquid -#include "entities_mounts.hpp" // for Mount -#include "entity.hpp" // for get_entity_ptr, to_id, Entity, EntityDB -#include "entity_lookup.hpp" // -#include "game_manager.hpp" // -#include "game_patches.hpp" // -#include "items.hpp" // for Items -#include "layer.hpp" // for EntityList, EntityList::Range, Layer -#include "logger.h" // for DEBUG -#include "math.hpp" // for AABB -#include "memory.hpp" // for write_mem_prot, write_mem_recoverable -#include "movable.hpp" // for Movable -#include "particles.hpp" // for ParticleEmitterInfo -#include "screen.hpp" // -#include "search.hpp" // for get_address, find_inst -#include "state.hpp" // for State, get_state_ptr, enum_to_layer -#include "state_structs.hpp" // for ShopRestrictedItem, Illumination -#include "thread_utils.hpp" // for OnHeapPointer -#include "virtual_table.hpp" // for get_virtual_function_address, VIRT_FUNC +#include "containers/custom_vector.hpp" // +#include "custom_types.hpp" // for get_custom_entity_types, CUSTOM_TYPE +#include "entities_chars.hpp" // for Player (ptr only), PowerupCapable +#include "entities_floors.hpp" // for ExitDoor, Door +#include "entities_items.hpp" // for StretchChain, PunishBall, Container +#include "entities_liquids.hpp" // for Liquid +#include "entities_mounts.hpp" // for Mount +#include "entity.hpp" // for get_entity_ptr, to_id, Entity, EntityDB +#include "entity_lookup.hpp" // +#include "game_manager.hpp" // +#include "game_patches.hpp" // +#include "items.hpp" // for Items +#include "layer.hpp" // for EntityList, EntityList::Range, Layer +#include "logger.h" // for DEBUG +#include "math.hpp" // for AABB +#include "memory.hpp" // for write_mem_prot, write_mem_recoverable +#include "movable.hpp" // for Movable +#include "particles.hpp" // for ParticleEmitterInfo +#include "screen.hpp" // +#include "search.hpp" // for get_address, find_inst +#include "state.hpp" // for State, get_state_ptr, enum_to_layer +#include "state_structs.hpp" // for ShopRestrictedItem, Illumination +#include "thread_utils.hpp" // for OnHeapPointer +#include "virtual_table.hpp" // for get_virtual_function_address, VIRT_FUNC uint32_t setflag(uint32_t flags, int bit) // shouldn't we change those to #define ? { @@ -803,7 +804,7 @@ ParticleEmitterInfo* generate_world_particles(uint32_t particle_emitter_id, uint if (entity != nullptr) { auto state = get_state_ptr(); - typedef ParticleEmitterInfo* generate_particles_func(std::vector*, uint32_t, Entity*); + typedef ParticleEmitterInfo* generate_particles_func(custom_vector*, uint32_t, Entity*); static generate_particles_func* gpf = (generate_particles_func*)(offset); return gpf(state->particle_emitters, particle_emitter_id, entity); } @@ -875,7 +876,7 @@ Illumination* create_illumination_internal(Color color, float size, float x, flo float position[] = {x, y}; - typedef Illumination* create_illumination_func(std::vector*, float*, Color*, uint32_t r9, float size, uint8_t flags, uint32_t uid, uint8_t); + typedef Illumination* create_illumination_func(custom_vector*, float*, Color*, uint32_t r9, float size, uint8_t flags, uint32_t uid, uint8_t); static create_illumination_func* cif = (create_illumination_func*)(offset); auto emitted_light = cif(state->lightsources, position, &color, 0x2, size, 32, uid, 0x0); @@ -1956,3 +1957,21 @@ void set_level_logic_enabled(bool enable) recover_mem("set_level_logic_enabled"); } } + +void set_camera_layer_control_enabled(bool enable) +{ + static auto offset = get_address("camera_layer_controll"); + static auto offset2 = get_address("player_behavior_layer_switch"); + if (!offset || !offset2) + return; + + if (enable) + { + recover_mem("set_camera_layer_control"); + } + else + { + write_mem_recoverable("set_camera_layer_control", offset, get_nop(7), true); + write_mem_recoverable("set_camera_layer_control", offset2, get_nop(18), true); + } +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index b24257d62..c62e544da 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_level_logic_enabled(bool enable); +void set_camera_layer_control_enabled(bool enable); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 6b4ccc024..adaa51048 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2196,6 +2196,11 @@ end backend->infinite_loop_detection = enable; }; + /// This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. + /// Letting you control those manually. + /// 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( diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index 87751a4b0..3afffcae4 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -347,7 +347,8 @@ void register_usertypes(sol::state& lua) statememory_type["correct_ushabti"] = &StateMemory::correct_ushabti; statememory_type["items"] = &StateMemory::items; statememory_type["camera_layer"] = &StateMemory::camera_layer; - statememory_type["layer_transition_timer"] = &StateMemory::layer_transition_effect_timer; + statememory_type["layer_transition_timer"] = &StateMemory::layer_transition_timer; + statememory_type["transition_to_layer"] = &StateMemory::transition_to_layer; statememory_type["screen_team_select"] = &StateMemory::screen_team_select; statememory_type["screen_character_select"] = &StateMemory::screen_character_select; statememory_type["screen_transition"] = &StateMemory::screen_transition; diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 5e45528f6..e9b59b411 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -2053,6 +2053,23 @@ std::unordered_map g_address_rules{ .function_start(), //.from_exe_base(0x228b58f0), }, + { + "camera_layer_controll"sv, + // overwrites state.camera_layer every frame + PatternCommandBuffer{} + .get_address("state_refresh"sv) + .find_after_inst("8A 80 A0 00 00 00"_gh) + .at_exe(), + }, + { + "player_behavior_layer_switch"sv, + // function in player behavior (index 7), we need instruction that sets state.layer_transition_timer = 0x24 and state.transition_to_layer = (dest layer) + PatternCommandBuffer{} + .find_after_inst("41 80 FC 01 0F 95 C1"_gh) + .find_inst("\xE8"sv) + .offset(0x5) + .at_exe(), + }, }; std::unordered_map g_cached_addresses; diff --git a/src/game_api/spawn_api.cpp b/src/game_api/spawn_api.cpp index 600319403..5492a97b8 100644 --- a/src/game_api/spawn_api.cpp +++ b/src/game_api/spawn_api.cpp @@ -12,23 +12,24 @@ #include // for pair, identity, min, _Find_fn, find #include // for vector, allocator, _Vector_iterator -#include "entities_chars.hpp" // for Player -#include "entities_items.hpp" // for ClimbableRope -#include "entities_liquids.hpp" // for Lava -#include "entities_monsters.hpp" // for Shopkeeper, RoomOwner -#include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... -#include "items.hpp" // -#include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x -#include "level_api.hpp" // for LevelGenSystem, ThemeInfo -#include "logger.h" // for DEBUG -#include "math.hpp" // for AABB -#include "memory.hpp" // for write_mem_prot, memory_read -#include "prng.hpp" // for PRNG, PRNG::PRNG_CLASS, PRNG::ENTIT... -#include "script/events.hpp" // for post_entity_spawn, pre_entity_spawn -#include "search.hpp" // for get_address -#include "state.hpp" // for enum_to_layer, State, StateMemory -#include "state_structs.hpp" // for LiquidTileSpawnData, LiquidPhysics -#include "util.hpp" // for OnScopeExit +#include "containers/custom_vector.hpp" // +#include "entities_chars.hpp" // for Player +#include "entities_items.hpp" // for ClimbableRope +#include "entities_liquids.hpp" // for Lava +#include "entities_monsters.hpp" // for Shopkeeper, RoomOwner +#include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... +#include "items.hpp" // +#include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x +#include "level_api.hpp" // for LevelGenSystem, ThemeInfo +#include "logger.h" // for DEBUG +#include "math.hpp" // for AABB +#include "memory.hpp" // for write_mem_prot, memory_read +#include "prng.hpp" // for PRNG, PRNG::PRNG_CLASS, PRNG::ENTIT... +#include "script/events.hpp" // for post_entity_spawn, pre_entity_spawn +#include "search.hpp" // for get_address +#include "state.hpp" // for enum_to_layer, State, StateMemory +#include "state_structs.hpp" // for LiquidTileSpawnData, LiquidPhysics +#include "util.hpp" // for OnScopeExit struct Items; @@ -75,7 +76,7 @@ void spawn_liquid(ENT_TYPE entity_type, float x, float y) float light_size = 1.0f; uint32_t flags = 0x63; - using construct_illumination_ptr_fun_t = Illumination*(std::vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); + using construct_illumination_ptr_fun_t = Illumination*(custom_vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); static auto construct_illumination_ptr_call = (construct_illumination_ptr_fun_t*)get_address("generate_illumination"); auto ill_ptr = construct_illumination_ptr_call(state->lightsources, position, color, 2, light_size, flags, lava->uid, lava->layer); diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index c3923c980..5546a8843 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -6,8 +6,9 @@ #include // for pair #include // for vector -#include "aliases.hpp" // for ENT_TYPE, LAYER -#include "state_structs.hpp" // for JournalProgressStickerSlot, Illumination (p... +#include "aliases.hpp" // for ENT_TYPE, LAYER +#include "containers/custom_vector.hpp" // +#include "state_structs.hpp" // for JournalProgressStickerSlot, Illumination (p... class Entity; class ScreenArenaIntro; @@ -249,8 +250,8 @@ struct StateMemory QuestsInfo* quests; AITarget* ai_targets; // e.g. hired hand uid -> snake uid LiquidPhysics* liquid_physics; - std::vector* particle_emitters; - std::vector* lightsources; + custom_vector* particle_emitters; + custom_vector* lightsources; EntityLookup* entity_lookup; // This is a Robin Hood Table @@ -258,10 +259,12 @@ struct StateMemory uint32_t padding13; RobinHoodTableEntry* uid_to_entity_data; - custom_vector> backlayer_player_related1; // inside vector: player and destination layer? - uint32_t layer_transition_effect_timer; - /// The currently drawn layer, can't be changed - uint8_t camera_layer; + custom_vector> entities_switching_layer; // inside vector: entity and destination layer + // only entities that go thru the layer (char_*, ghost, doesn't care about held items) + // does not care about the entity:set_layer() either + + uint32_t layer_transition_timer; + uint8_t transition_to_layer; uint8_t unknown31a; // padding probably uint8_t unknown31b; uint8_t unknown31c; @@ -269,7 +272,20 @@ struct StateMemory RoomOwnersInfo room_owners; /// Number of frames since the game was launched uint32_t time_startup; - uint32_t special_visibility_flags; + + union + { + uint32_t special_visibility_flags; // it's actually four 8bit values, last one is not even a flag, it's just layer + + struct + { + uint8_t crust_visibility; + uint8_t compass_visibility; + uint8_t special_compass_visibility; + uint8_t camera_layer; + }; + }; + /// Camera bounds and position Camera* camera; uint8_t unknown40; diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index f5cf8ef56..13b1dd16d 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -7537,7 +7537,7 @@ struct TextureViewer static TextureViewer texture_viewer{0, -1}; void render_vanilla_stuff() { - if (peek_layer && g_state->layer_transition_effect_timer == 0) + if (peek_layer && g_state->layer_transition_timer == 0) { uint8_t other_layer = g_state->camera_layer ? 0 : 1; auto [bbox_left, bbox_top] = UI::click_position(-1.0f, 1.0f); From 7965a6f30f19f7acf018d7743277d2efeaa04650 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 22 Oct 2023 21:13:22 +0200 Subject: [PATCH 14/31] this idea is as dead as it can be --- src/game_api/fix_entity_descriptions.cpp | 312 ----------------------- src/game_api/fix_entity_descriptions.hpp | 4 - src/game_api/strings.cpp | 1 - 3 files changed, 317 deletions(-) delete mode 100644 src/game_api/fix_entity_descriptions.cpp delete mode 100644 src/game_api/fix_entity_descriptions.hpp diff --git a/src/game_api/fix_entity_descriptions.cpp b/src/game_api/fix_entity_descriptions.cpp deleted file mode 100644 index 1da36422b..000000000 --- a/src/game_api/fix_entity_descriptions.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include "fix_entity_descriptions.hpp" - -#include // for uint32_t -#include // for operator new -#include // for pair, min -#include // for allocator, vector, _Vector_const_iterator - -#include "entity.hpp" // for get_type, EntityDB - -// To update the description mapping, uncomment the #define below, and paste in the contents -// of the asset doc spreadsheet, run OL and it will dump the vector to stdout. Paste that over -// the vector at the bottom and it's up to date. - -// #define RECREATE_ENTITY_DESCRIPTIONS - -#ifdef RECREATE_ENTITY_DESCRIPTIONS -void recreate_entity_descriptions() -{ - std::string mapping_data = R"(ITEM_WHIP -ITEM_WHIP_FLAME -ITEM_BOMB -ITEM_PASTEBOMB -ITEM_ROPE -ITEM_CLIMBABLE_ROPE -ITEM_UNROLLED_ROPE -ITEM_BLOOD -ITEM_EGGSHIP -ITEM_PARENTSSHIP -ITEM_OLMECSHIP -ITEM_IDOL -ITEM_MADAMETUSK_IDOL -ITEM_MADAMETUSK_IDOLNOTE -ITEM_HOLDTHEIDOL -ITEM_TOTEM_SPEAR -ITEM_JUNGLE_SPEAR_COSMETIC -ITEM_JUNGLE_SPEAR_DAMAGING -ITEM_LION_SPEAR -ITEM_BIG_SPEAR -ITEM_ROCK -ITEM_WEB -ITEM_WEBSHOT -ITEM_GIANTSPIDER_WEBSHOT -ITEM_HANGSTRAND -ITEM_HANGANCHOR -ITEM_WOODEN_ARROW -ITEM_BROKEN_ARROW -ITEM_METAL_ARROW -ITEM_LIGHT_ARROW -ITEM_PLASMACANNON_SHOT -ITEM_SCEPTER_ANUBISSHOT -ITEM_SCEPTER_ANUBISSPECIALSHOT -ITEM_SCEPTER_PLAYERSHOT -ITEM_UFO_LASER_SHOT -ITEM_LAMASSU_LASER_SHOT -ITEM_SORCERESS_DAGGER_SHOT -ITEM_LASERTRAP_SHOT -ITEM_SPARK -ITEM_TIAMAT_SHOT -ITEM_FIREBALL -ITEM_HUNDUN_FIREBALL -ITEM_FLAMETHROWER_FIREBALL -ITEM_LEAF -ITEM_ACIDSPIT -ITEM_INKSPIT -ITEM_ACIDBUBBLE -ITEM_CRABMAN_ACIDBUBBLE -ITEM_CRABMAN_CLAW -ITEM_CRABMAN_CLAWCHAIN -ITEM_CHEST -ITEM_VAULTCHEST -ITEM_ENDINGTREASURE_TIAMAT -ITEM_ENDINGTREASURE_HUNDUN -ITEM_KEY -ITEM_LOCKEDCHEST -ITEM_LOCKEDCHEST_KEY -ITEM_CRATE -ITEM_DMCRATE -ITEM_TUTORIAL_MONSTER_SIGN -ITEM_CONSTRUCTION_SIGN -ITEM_SHORTCUT_SIGN -ITEM_SPEEDRUN_SIGN -ITEM_BASECAMP_TUTORIAL_SIGN -ITEM_BOOMBOX -ITEM_TV -ITEM_TELESCOPE -ITEM_WALLTORCH -ITEM_WALLTORCHFLAME -ITEM_LITWALLTORCH -ITEM_AUTOWALLTORCH -ITEM_TORCH -ITEM_TORCHFLAME -ITEM_LAMP -ITEM_LAMPFLAME -ITEM_REDLANTERN -ITEM_REDLANTERNFLAME -ITEM_PRESENT -ITEM_GHIST_PRESENT -ITEM_BULLET -ITEM_FREEZERAYSHOT -ITEM_CLONEGUNSHOT -ITEM_ICECAGE -ITEM_BROKEN_MATTOCK -ITEM_PUNISHBALL -ITEM_PUNISHCHAIN -ITEM_CHAIN -ITEM_CHAIN_LASTPIECE -ITEM_SLIDINGWALL_CHAIN -ITEM_SLIDINGWALL_CHAIN_LASTPIECE -ITEM_COFFIN -ITEM_FLY -ITEM_OLMECCANNON_BOMBS -ITEM_OLMECCANNON_UFO -ITEM_LANDMINE -ITEM_CURSING_CLOUD -ITEM_UDJAT_SOCKET -ITEM_USHABTI -ITEM_TURKEY_NECK -ITEM_HONEY -ITEM_GIANTCLAM_TOP -ITEM_PLAYERGHOST -ITEM_PLAYERGHOST_BREATH -ITEM_DIE -ITEM_DICE_BET -ITEM_DICE_PRIZE_DISPENSER -ITEM_LASERBEAM -ITEM_HORIZONTALLASERBEAM -ITEM_ANUBIS_COFFIN -ITEM_SPIKES -ITEM_EGGSHIP_HOOK -ITEM_AXOLOTL_BUBBLESHOT -ITEM_POTOFGOLD -ITEM_STICKYTRAP_PIECE -ITEM_STICKYTRAP_LASTPIECE -ITEM_STICKYTRAP_BALL -ITEM_SKULLDROPTRAP -ITEM_FROZEN_LIQUID -ITEM_ALIVE_EMBEDDED_ON_ICE -ITEM_DEPLOYED_PARACHUTE -ITEM_SLIDINGWALL_SWITCH -ITEM_SLIDINGWALL_SWITCH_REWARD -ITEM_GIANTFLY_HEAD -ITEM_PALACE_CANDLE_FLAME -ITEM_SNAP_TRAP -ITEM_EMPRESS_GRAVE -ITEM_TENTACLE -ITEM_TENTACLE_PIECE -ITEM_TENTACLE_LAST_PIECE -ITEM_MINIGAME_SHIP -ITEM_MINIGAME_UFO -ITEM_MINIGAME_ASTEROID_BG -ITEM_MINIGAME_ASTEROID -ITEM_MINIGAME_BROKEN_ASTEROID -ITEM_POT -ITEM_CURSEDPOT -ITEM_SKULL -ITEM_BONES -ITEM_COOKFIRE -ITEM_LAVAPOT -ITEM_SCRAP -ITEM_EGGPLANT -ITEM_ICESPIRE -ITEM_PALACE_CANDLE -ITEM_SKULLDROPTRAP_SKULL -ITEM_FLOATING_ORB -ITEM_EGGSAC -ITEM_GOLDBAR -ITEM_GOLDBARS -ITEM_DIAMOND -ITEM_EMERALD -ITEM_SAPPHIRE -ITEM_RUBY -ITEM_NUGGET -ITEM_GOLDCOIN -ITEM_EMERALD_SMALL -ITEM_SAPPHIRE_SMALL -ITEM_RUBY_SMALL -ITEM_NUGGET_SMALL -ITEM_PICKUP_TORNJOURNALPAGE -ITEM_PICKUP_JOURNAL -ITEM_PICKUP_ROPE -ITEM_PICKUP_ROPEPILE 1342 -ITEM_PICKUP_BOMBBAG 1343 -ITEM_PICKUP_BOMBBOX 1344 -ITEM_PICKUP_ROYALJELLY 1355 -ITEM_PICKUP_COOKEDTURKEY -ITEM_PICKUP_GIANTFOOD -ITEM_PICKUP_ELIXIR -ITEM_PICKUP_CLOVER -ITEM_PICKUP_SEEDEDRUNSUNLOCKER -ITEM_PICKUP_SPECTACLES 1345 -ITEM_PICKUP_CLIMBINGGLOVES 1346 -ITEM_PICKUP_PITCHERSMITT 1347 -ITEM_PICKUP_SPRINGSHOES 1348 -ITEM_PICKUP_SPIKESHOES 1349 -ITEM_PICKUP_PASTE 1350 -ITEM_PICKUP_COMPASS 1351 -ITEM_PICKUP_SPECIALCOMPASS -ITEM_PICKUP_PARACHUTE 1352 -ITEM_PICKUP_UDJATEYE -ITEM_PICKUP_KAPALA -ITEM_PICKUP_HEDJET 1353 -ITEM_PICKUP_CROWN -ITEM_PICKUP_EGGPLANTCROWN -ITEM_PICKUP_TRUECROWN -ITEM_PICKUP_ANKH -ITEM_PICKUP_TABLETOFDESTINY -ITEM_PICKUP_SKELETON_KEY 1354 -ITEM_PICKUP_PLAYERBAG -ITEM_POWERUP_PASTE -ITEM_POWERUP_CLIMBING_GLOVES -ITEM_POWERUP_SPIKE_SHOES -ITEM_POWERUP_SPRING_SHOES -ITEM_POWERUP_KAPALA -ITEM_POWERUP_SPECTACLES -ITEM_POWERUP_PITCHERSMITT -ITEM_POWERUP_UDJATEYE -ITEM_POWERUP_PARACHUTE -ITEM_POWERUP_COMPASS -ITEM_POWERUP_SPECIALCOMPASS -ITEM_POWERUP_HEDJET -ITEM_POWERUP_CROWN -ITEM_POWERUP_EGGPLANTCROWN -ITEM_POWERUP_TRUECROWN -ITEM_POWERUP_ANKH -ITEM_POWERUP_TABLETOFDESTINY -ITEM_POWERUP_SKELETON_KEY -ITEM_CAPE -ITEM_VLADS_CAPE -ITEM_PURCHASABLE_CAPE -ITEM_JETPACK - -ITEM_JETPACK_MECH -ITEM_PURCHASABLE_JETPACK 1356 -ITEM_TELEPORTER_BACKPACK -ITEM_PURCHASABLE_TELEPORTER_BACKPACK -ITEM_HOVERPACK -ITEM_PURCHASABLE_HOVERPACK -ITEM_POWERPACK -ITEM_PURCHASABLE_POWERPACK -ITEM_WEBGUN -ITEM_SHOTGUN -ITEM_FREEZERAY -ITEM_CROSSBOW -ITEM_CAMERA -ITEM_TELEPORTER -ITEM_MATTOCK -ITEM_BOOMERANG -ITEM_MACHETE -ITEM_EXCALIBUR -ITEM_BROKENEXCALIBUR -ITEM_PLASMACANNON -ITEM_SCEPTER -ITEM_CLONEGUN -ITEM_HOUYIBOW -ITEM_WOODEN_SHIELD -ITEM_METAL_SHIELD )"; - - const std::regex line_regex(R"(^([A-Z_]+)\s+([0-9]+)$)"); - auto ss = std::stringstream(mapping_data); - std::smatch m; - std::cout << "const std::vector> description_mapping = { \n"; - for (std::string line; std::getline(ss, line, '\n');) - { - if (std::regex_match(line, m, line_regex)) - { - auto entity_id = to_id(fmt::format("ENT_TYPE_{}", m.str(1))); - if (entity_id != -1) - { - std::cout << "{" << entity_id << ", " << m.str(2) << "},\n"; - } - else - { - DEBUG("Entity type {} is unknown", m.str(1)); - } - } - } - std::cout << "};\n\n"; -} -#endif - -void fix_entity_descriptions(STRINGID invalid_string_id) -{ -#ifdef RECREATE_ENTITY_DESCRIPTIONS - recreate_entity_descriptions(); -#endif - - const std::vector> description_mapping = { - {512, 1342}, - {513, 1343}, - {514, 1344}, - {517, 1355}, - {524, 1345}, - {525, 1346}, - {526, 1347}, - {527, 1348}, - {528, 1349}, - {529, 1350}, - {530, 1351}, - {532, 1352}, - {535, 1353}, - {541, 1354}, - {569, 1356}, - }; - - for (const auto& [entity_id, description_id] : description_mapping) - { - auto type = get_type(entity_id); - if (type != nullptr && type->description == invalid_string_id) - { - type->description = description_id; - } - } -} diff --git a/src/game_api/fix_entity_descriptions.hpp b/src/game_api/fix_entity_descriptions.hpp deleted file mode 100644 index d44314398..000000000 --- a/src/game_api/fix_entity_descriptions.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "aliases.hpp" - -void fix_entity_descriptions(STRINGID invalid_string_id); diff --git a/src/game_api/strings.cpp b/src/game_api/strings.cpp index b2fd9e8e5..b7d1a9720 100644 --- a/src/game_api/strings.cpp +++ b/src/game_api/strings.cpp @@ -100,7 +100,6 @@ void OnToast(char16_t* buffer) void strings_init() { g_original_string_ids_end = get_type(1)->description; // get wrong stringid from bordertile - // fix_entity_descriptions(g_original_string_ids_end); // disabled until someone wants to work on this auto addr_format_shopitem = Memory::get().at_exe(get_virtual_function_address(VTABLE_OFFSET::ITEM_PICKUP_ROPEPILE, 7)); auto addr_npcdialogue = get_address("speech_bubble_fun"); From 824380ac420fe779d7f01a70854e37c6df8a83ca Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 22 Oct 2023 21:13:37 +0200 Subject: [PATCH 15/31] use local, why not --- src/game_api/rpc.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 246c024b5..8574a0b30 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -779,16 +779,13 @@ bool is_inside_shop_zone(float x, float y, LAYER layer) // // if it doesn't jump there is a bunch of coordinate checks but also state.presence_flags, flipped rooms ... - static size_t offset = 0; - auto state = State::get().ptr_main(); // the game gets level gen from heap pointer and we always get it from state, not sure if it matters - if (offset == 0) - { - offset = get_address("coord_inside_shop_zone"); - } + static size_t offset = get_address("coord_inside_shop_zone"); + auto state = State::get().ptr(); // the game gets level gen from heap pointer and we always get it from state, not sure if it matters + if (offset != 0) { typedef bool coord_inside_shop_zone_func(LevelGenSystem*, uint32_t layer, float x, float y); - static coord_inside_shop_zone_func* ciszf = (coord_inside_shop_zone_func*)(offset); + coord_inside_shop_zone_func* ciszf = (coord_inside_shop_zone_func*)(offset); return ciszf(state->level_gen, enum_to_layer(layer), x, y); } return false; From cf4745e181f36548b212462e2a2cef90cd287e3c Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 28 Oct 2023 20:06:49 +0200 Subject: [PATCH 16/31] move illumination to separate file, expose the full version of the function --- src/game_api/illumination.cpp | 52 +++++++++++++ src/game_api/illumination.hpp | 73 +++++++++++++++++++ src/game_api/rpc.cpp | 50 +------------ src/game_api/rpc.hpp | 4 - src/game_api/script/lua_vm.cpp | 10 ++- .../usertypes/entities_activefloors_lua.cpp | 4 +- .../usertypes/entities_backgrounds_lua.cpp | 2 +- .../script/usertypes/entities_chars_lua.cpp | 1 + .../usertypes/entities_decorations_lua.cpp | 2 +- .../script/usertypes/entities_floors_lua.cpp | 4 +- .../script/usertypes/entities_fx_lua.cpp | 6 +- .../script/usertypes/entities_items_lua.cpp | 3 +- .../script/usertypes/entities_liquids_lua.cpp | 2 +- .../script/usertypes/entities_logical_lua.cpp | 4 +- .../usertypes/entities_monsters_lua.cpp | 4 +- src/game_api/script/usertypes/state_lua.cpp | 2 + src/game_api/spawn_api.cpp | 20 +++-- src/game_api/state.hpp | 3 +- src/game_api/state_structs.hpp | 49 ------------- src/injected/ui.cpp | 1 + src/injected/ui_util.cpp | 1 + 21 files changed, 168 insertions(+), 129 deletions(-) create mode 100644 src/game_api/illumination.cpp create mode 100644 src/game_api/illumination.hpp diff --git a/src/game_api/illumination.cpp b/src/game_api/illumination.cpp new file mode 100644 index 000000000..02590c1b0 --- /dev/null +++ b/src/game_api/illumination.cpp @@ -0,0 +1,52 @@ +#include "illumination.hpp" + +#include "color.hpp" +#include "entity.hpp" +#include "math.hpp" +#include "search.hpp" +#include "state.hpp" +#include "thread_utils.hpp" +#include + +Illumination* create_illumination(Vec2 pos, Color col, LIGHT_TYPE type, float size, uint8_t light_flags, int32_t uid, LAYER layer) +{ + static size_t offset = get_address("generate_illumination"); + + if (offset != 0) + { + auto state = get_state_ptr(); + + typedef Illumination* create_illumination_func(custom_vector*, Vec2*, Color, LIGHT_TYPE, float, uint8_t light_flags, int32_t uid, uint8_t layer); + static create_illumination_func* cif = (create_illumination_func*)(offset); + auto emitted_light = cif(state->lightsources, &pos, std::move(col), type, size, light_flags, uid, enum_to_layer(layer)); + return emitted_light; + } + return nullptr; +} + +Illumination* create_illumination(Color color, float size, float x, float y) +{ + return create_illumination(Vec2{x, y}, std::move(color), LIGHT_TYPE::NONE, size, 0x20, -1, LAYER::FRONT); +} + +Illumination* create_illumination(Color color, float size, int32_t uid) +{ + auto entity = get_entity_ptr(uid); + if (entity != nullptr) + { + return create_illumination(Vec2{entity->abs_x, entity->abs_y}, std::move(color), LIGHT_TYPE::FOLLOW_ENTITY, size, 0x20, uid, (LAYER)entity->layer); + } + return nullptr; +} + +void refresh_illumination(Illumination* illumination) +{ + static uint32_t* offset = 0; + if (offset == 0) + { + size_t** heap_offset = (size_t**)get_address("refresh_illumination_heap_offset"); + auto illumination_counter = OnHeapPointer(**heap_offset); + offset = illumination_counter.decode(); + } + illumination->timer = *offset; +} diff --git a/src/game_api/illumination.hpp b/src/game_api/illumination.hpp new file mode 100644 index 000000000..4b0d18731 --- /dev/null +++ b/src/game_api/illumination.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include "aliases.hpp" + +struct Color; +struct Vec2; + +enum class LIGHT_TYPE : uint8_t +{ + NONE = 0x0, + FOLLOW_CAMERA = 0x1, + FOLLOW_ENTITY = 0x2, + ROOM_LIGHT = 0x4, +}; + +struct LightParams // it's probably just Color +{ + float red; // default = 1.0 (can go over 1.0 for oversaturation) + float green; + float blue; + float size; +}; + +struct Illumination +{ + union + { + /// Table of light1, light2, ... etc. + std::array lights; + struct + { + LightParams light1; + LightParams light2; + LightParams light3; + /// It's rendered on objects around, not as an actual bright spot + LightParams light4; + }; + }; + float brightness; + float brightness_multiplier; + float light_pos_x; + float light_pos_y; + float offset_x; + float offset_y; + float distortion; + int32_t entity_uid; + uint32_t timer; + union + { + /// see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags + uint32_t flags; + struct + { + // TODO: separate flags in api + + uint8_t light_flags; // no reason to expose this + + /// Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness + /// Rectangle always uses light1, even when it's disabled in flags + LIGHT_TYPE type_flags; + uint8_t layer; + bool enabled; + }; + }; +}; + +Illumination* create_illumination(Vec2 pos, Color color, LIGHT_TYPE type, float size, uint8_t light_flags, int32_t uid, LAYER layer); +Illumination* create_illumination(Color color, float size, float x, float y); +Illumination* create_illumination(Color color, float size, int32_t uid); +void refresh_illumination(Illumination* illumination); diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 8574a0b30..70b9e18b9 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -31,6 +31,7 @@ #include "entity_lookup.hpp" // #include "game_manager.hpp" // #include "game_patches.hpp" // +#include "illumination.hpp" // #include "items.hpp" // for Items #include "layer.hpp" // for EntityList, EntityList::Range, Layer #include "logger.h" // for DEBUG @@ -863,55 +864,6 @@ void extinguish_particles(ParticleEmitterInfo* particle_emitter) } } -Illumination* create_illumination_internal(Color color, float size, float x, float y, int32_t uid) -{ - static size_t offset = get_address("generate_illumination"); - - if (offset != 0) - { - auto state = get_state_ptr(); - - float position[] = {x, y}; - - typedef Illumination* create_illumination_func(custom_vector*, float*, Color*, uint32_t r9, float size, uint8_t flags, uint32_t uid, uint8_t); - static create_illumination_func* cif = (create_illumination_func*)(offset); - auto emitted_light = cif(state->lightsources, position, &color, 0x2, size, 32, uid, 0x0); - - // turn on Enabled flag - emitted_light->flags = emitted_light->flags | (1U << (25 - 1)); - - return emitted_light; - } - return nullptr; -} - -Illumination* create_illumination(Color color, float size, float x, float y) -{ - return create_illumination_internal(color, size, x, y, -1); -} - -Illumination* create_illumination(Color color, float size, uint32_t uid) -{ - auto entity = get_entity_ptr(uid); - if (entity != nullptr) - { - return create_illumination_internal(color, size, entity->abs_x, entity->abs_y, uid); - } - return nullptr; -} - -void refresh_illumination(Illumination* illumination) -{ - static uint32_t* offset = 0; - if (offset == 0) - { - size_t** heap_offset = (size_t**)get_address("refresh_illumination_heap_offset"); - auto illumination_counter = OnHeapPointer(**heap_offset); - offset = illumination_counter.decode(); - } - illumination->timer = *offset; -} - void set_journal_enabled(bool b) { get_journal_enabled() = b; diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index c62e544da..bc187a46f 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -77,10 +77,6 @@ ParticleEmitterInfo* generate_screen_particles(uint32_t particle_emitter_id, flo void advance_screen_particles(ParticleEmitterInfo* particle_emitter); void render_screen_particles(ParticleEmitterInfo* particle_emitter); void extinguish_particles(ParticleEmitterInfo* particle_emitter); -Illumination* create_illumination_internal(Color color, float size, float x, float y, int32_t uid); -Illumination* create_illumination(Color color, float size, float x, float y); -Illumination* create_illumination(Color color, float size, uint32_t uid); -void refresh_illumination(Illumination* illumination); void set_journal_enabled(bool b); void set_camp_camera_bounds_enabled(bool b); void set_explosion_mask(int32_t mask); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index adaa51048..f45a8b656 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -38,6 +38,7 @@ #include "entity_lookup.hpp" // #include "game_manager.hpp" // for get_game_manager #include "handle_lua_function.hpp" // for handle_function +#include "illumination.hpp" // #include "items.hpp" // for Inventory #include "layer.hpp" // for g_level_max_x #include "lua_backend.hpp" // for LuaBackend, ON @@ -96,7 +97,7 @@ #include "usertypes/texture_lua.hpp" // for register_usertypes #include "usertypes/vanilla_render_lua.hpp" // for VanillaRenderContext #include "usertypes/vtables_lua.hpp" // for register_usertypes -#include "virtual_table.hpp" +#include "virtual_table.hpp" // struct Illumination; @@ -1798,11 +1799,12 @@ end lua["change_poison_timer"] = change_poison_timer; auto create_illumination = sol::overload( - static_cast(::create_illumination), - static_cast(::create_illumination)); + static_cast(::create_illumination), + static_cast(::create_illumination), + static_cast(::create_illumination)); /// Creates a new Illumination. Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example lua["create_illumination"] = create_illumination; - /// Refreshes an Illumination, keeps it from fading out + /// Refreshes an Illumination, keeps it from fading out (updates the timer, keeping it in sync with the game render) lua["refresh_illumination"] = refresh_illumination; /// Removes all liquid that is about to go out of bounds, which crashes the game. diff --git a/src/game_api/script/usertypes/entities_activefloors_lua.cpp b/src/game_api/script/usertypes/entities_activefloors_lua.cpp index 66f24e2a4..e7cad6460 100644 --- a/src/game_api/script/usertypes/entities_activefloors_lua.cpp +++ b/src/game_api/script/usertypes/entities_activefloors_lua.cpp @@ -10,9 +10,9 @@ #include "entities_activefloors.hpp" // for Olmec, ClamBase, Crushtrap, Ele... #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "particles.hpp" // IWYU pragma: keep -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_backgrounds_lua.cpp b/src/game_api/script/usertypes/entities_backgrounds_lua.cpp index aed70bc2c..c6e060874 100644 --- a/src/game_api/script/usertypes/entities_backgrounds_lua.cpp +++ b/src/game_api/script/usertypes/entities_backgrounds_lua.cpp @@ -10,7 +10,7 @@ #include "entities_backgrounds.hpp" // for BGEggshipRoom, BGShootingStar #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesBG { diff --git a/src/game_api/script/usertypes/entities_chars_lua.cpp b/src/game_api/script/usertypes/entities_chars_lua.cpp index b6157bd82..d708773cb 100644 --- a/src/game_api/script/usertypes/entities_chars_lua.cpp +++ b/src/game_api/script/usertypes/entities_chars_lua.cpp @@ -11,6 +11,7 @@ #include "entities_chars.hpp" // for Ai, Player, PowerupCapable, get_charac... #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // for Inventory, Inventory::acquired_powerups #include "state_structs.hpp" // IWYU pragma: keep diff --git a/src/game_api/script/usertypes/entities_decorations_lua.cpp b/src/game_api/script/usertypes/entities_decorations_lua.cpp index 0c63a2470..7f9216a75 100644 --- a/src/game_api/script/usertypes/entities_decorations_lua.cpp +++ b/src/game_api/script/usertypes/entities_decorations_lua.cpp @@ -10,7 +10,7 @@ #include "entities_decorations.hpp" // for PalaceSign, CrossBeam, DecoRegen... #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesDecorations { diff --git a/src/game_api/script/usertypes/entities_floors_lua.cpp b/src/game_api/script/usertypes/entities_floors_lua.cpp index b7da6cdcc..1ac1b6ddc 100644 --- a/src/game_api/script/usertypes/entities_floors_lua.cpp +++ b/src/game_api/script/usertypes/entities_floors_lua.cpp @@ -11,8 +11,8 @@ #include "entities_floors.hpp" // for MotherStatue, Floor, ExitDoor, Door #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep namespace NEntitiesFloors { diff --git a/src/game_api/script/usertypes/entities_fx_lua.cpp b/src/game_api/script/usertypes/entities_fx_lua.cpp index 352cba841..fd047eb9b 100644 --- a/src/game_api/script/usertypes/entities_fx_lua.cpp +++ b/src/game_api/script/usertypes/entities_fx_lua.cpp @@ -8,9 +8,9 @@ #include // for move, declval #include // for min, max -#include "entities_fx.hpp" // for FxSaleContainer, Button, FxAnkhRotatingSpark -#include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "entities_fx.hpp" // for FxSaleContainer, Button, FxAnkhRotatingSpark +#include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_items_lua.cpp b/src/game_api/script/usertypes/entities_items_lua.cpp index 31c3337ce..1748576f1 100644 --- a/src/game_api/script/usertypes/entities_items_lua.cpp +++ b/src/game_api/script/usertypes/entities_items_lua.cpp @@ -11,9 +11,10 @@ #include "entities_items.hpp" // for Spark, Boombox, AnkhPowerup, Arrow #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // IWYU pragma: keep #include "particles.hpp" // IWYU pragma: keep -#include "sound_manager.hpp" // +#include "sound_manager.hpp" // IWYU pragma: keep #include "state_structs.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_liquids_lua.cpp b/src/game_api/script/usertypes/entities_liquids_lua.cpp index 98125a29c..2d8d21b95 100644 --- a/src/game_api/script/usertypes/entities_liquids_lua.cpp +++ b/src/game_api/script/usertypes/entities_liquids_lua.cpp @@ -10,7 +10,7 @@ #include "entities_liquids.hpp" // for Liquid, Lava, Lava::emitted_light #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesLiquids { diff --git a/src/game_api/script/usertypes/entities_logical_lua.cpp b/src/game_api/script/usertypes/entities_logical_lua.cpp index 6ac97572c..2047a75fb 100644 --- a/src/game_api/script/usertypes/entities_logical_lua.cpp +++ b/src/game_api/script/usertypes/entities_logical_lua.cpp @@ -10,8 +10,8 @@ #include "entities_logical.hpp" // for Portal, DMSpawning, LogicalDoor, Our... #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep namespace NEntitiesLogical { diff --git a/src/game_api/script/usertypes/entities_monsters_lua.cpp b/src/game_api/script/usertypes/entities_monsters_lua.cpp index d7215e0ca..1304b8f52 100644 --- a/src/game_api/script/usertypes/entities_monsters_lua.cpp +++ b/src/game_api/script/usertypes/entities_monsters_lua.cpp @@ -10,8 +10,8 @@ #include "entities_monsters.hpp" // for CritterSlime, Hundun, Tiamat, Critt... #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep class Movable; class PowerupCapable; diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index 3afffcae4..b25666708 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -14,6 +14,7 @@ #include "entities_chars.hpp" // IWYU pragma: keep #include "entity.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // for Items, SelectPlayerSlot, Items::is... #include "level_api.hpp" // IWYU pragma: keep #include "online.hpp" // for OnlinePlayer, OnlineLobby, Online @@ -429,6 +430,7 @@ void register_usertypes(sol::state& lua) illumination_type["offset_y"] = &Illumination::offset_y; illumination_type["distortion"] = &Illumination::distortion; illumination_type["entity_uid"] = &Illumination::entity_uid; + illumination_type["timer"] = &Illumination::timer; illumination_type["flags"] = &Illumination::flags; illumination_type["type_flags"] = &Illumination::type_flags; illumination_type["enabled"] = &Illumination::enabled; diff --git a/src/game_api/spawn_api.cpp b/src/game_api/spawn_api.cpp index 5492a97b8..4024ce175 100644 --- a/src/game_api/spawn_api.cpp +++ b/src/game_api/spawn_api.cpp @@ -18,6 +18,7 @@ #include "entities_liquids.hpp" // for Lava #include "entities_monsters.hpp" // for Shopkeeper, RoomOwner #include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... +#include "illumination.hpp" // #include "items.hpp" // #include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x #include "level_api.hpp" // for LevelGenSystem, ThemeInfo @@ -71,15 +72,20 @@ void spawn_liquid(ENT_TYPE entity_type, float x, float y) for (Lava* lava : lavas) { - float position[2] = {lava->x, lava->y}; - float color[4] = {1.782f, 0.575262f, 0.0f, 0.0f}; // green value is randomized! - float light_size = 1.0f; - uint32_t flags = 0x63; + Vec2 position{lava->x, lava->y}; + Color color{1.782f, 0.575262f, 0.0f, 0.0f}; + // green value is randomized in game + // uses PRNG_CLASS::LEVEL_DECO + // green = (PRNG::random_float / 1000 * 0.3 + 0.2) / 1.62 - using construct_illumination_ptr_fun_t = Illumination*(custom_vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); - static auto construct_illumination_ptr_call = (construct_illumination_ptr_fun_t*)get_address("generate_illumination"); + float light_size = 0.6f; // should be 0.8f for dark level + uint8_t flags = 0x63; - auto ill_ptr = construct_illumination_ptr_call(state->lightsources, position, color, 2, light_size, flags, lava->uid, lava->layer); + // using construct_illumination_ptr_fun_t = Illumination*(custom_vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); + // static auto construct_illumination_ptr_call = (construct_illumination_ptr_fun_t*)get_address("generate_illumination"); + + // auto ill_ptr = construct_illumination_ptr_call(state->lightsources, position, color, 2, light_size, flags, lava->uid, lava->layer); + auto ill_ptr = create_illumination(position, color, LIGHT_TYPE::FOLLOW_ENTITY, light_size, flags, lava->uid, (LAYER)lava->layer); lava->emitted_light = ill_ptr; } } diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index 5546a8843..f394f7fa9 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -8,7 +8,7 @@ #include "aliases.hpp" // for ENT_TYPE, LAYER #include "containers/custom_vector.hpp" // -#include "state_structs.hpp" // for JournalProgressStickerSlot, Illumination (p... +#include "state_structs.hpp" // for JournalProgressStickerSlot, ... class Entity; class ScreenArenaIntro; @@ -44,6 +44,7 @@ struct Layer; struct LevelGenSystem; class ThemeInfo; struct Items; +struct Illumination; void fix_liquid_out_of_bounds(); diff --git a/src/game_api/state_structs.hpp b/src/game_api/state_structs.hpp index e913ddb32..0e7fbeb1a 100644 --- a/src/game_api/state_structs.hpp +++ b/src/game_api/state_structs.hpp @@ -19,55 +19,6 @@ struct RobinHoodTableEntry Entity* entity; }; -struct LightParams -{ - float red; // default = 1.0 (can go over 1.0 for oversaturation) - float green; - float blue; - float size; -}; - -struct Illumination -{ - union - { - /// Table of light1, light2, ... etc. - std::array lights; - struct - { - LightParams light1; - LightParams light2; - LightParams light3; - /// It's rendered on objects around, not as an actual bright spot - LightParams light4; - }; - }; - float brightness; - float brightness_multiplier; - float light_pos_x; - float light_pos_y; - float offset_x; - float offset_y; - float distortion; - int32_t entity_uid; - uint32_t timer; - union - { - /// see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags - uint32_t flags; - struct - { - uint8_t light_flags; // no reason to expose this - - /// Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness - /// Rectangle always uses light1, even when it's disabled in flags - uint8_t type_flags; - uint8_t layer; - bool enabled; - }; - }; -}; - struct InputMapping { uint8_t jump; diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 13b1dd16d..e07fb8711 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -39,6 +39,7 @@ #include "file_api.hpp" #include "flags.hpp" #include "game_manager.hpp" +#include "illumination.hpp" #include "items.hpp" #include "level_api.hpp" #include "logger.h" diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 9a72e1fcd..aee03ee37 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -15,6 +15,7 @@ #include "entity.hpp" // for to_id, Entity, get_entity_ptr #include "entity_lookup.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager +#include "illumination.hpp" // #include "items.hpp" // for Items #include "layer.hpp" // for Layer, EntityList::Range, Entit... #include "level_api.hpp" // for LevelGenSystem From 837cd4a8889a62e7aa7b40974032944918edde11 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 28 Oct 2023 22:16:22 +0200 Subject: [PATCH 17/31] fix and update doc --- docs/game_data/spel2.lua | 29 ++++++++++++++++---- docs/generate.py | 2 +- docs/parse_source.py | 1 + docs/src/includes/_globals.md | 48 +++++++++++++++++++++++++++++++--- docs/src/includes/_types.md | 7 +++-- src/game_api/flags.hpp | 16 +++--------- src/game_api/illumination.hpp | 2 +- src/game_api/script/lua_vm.cpp | 2 +- 8 files changed, 81 insertions(+), 26 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index e25d07c15..2ea54d1b0 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1060,20 +1060,30 @@ function add_item_to_shop(item_uid, shop_owner_uid) end ---@param frames integer ---@return nil function change_poison_timer(frames) end ----Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. +---@param pos Vec2 +---@param color Color +---@param type LIGHT_TYPE +---@param size number +---@param flags integer +---@param uid integer +---@param layer LAYER +---@return Illumination +function create_illumination(pos, color, type, size, flags, uid, layer) end +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ---@param color Color ---@param size number ---@param x number ---@param y number ---@return Illumination function create_illumination(color, size, x, y) end ----Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ---@param color Color ---@param size number ---@param uid integer ---@return Illumination function create_illumination(color, size, uid) end ----Refreshes an Illumination, keeps it from fading out +---Refreshes an Illumination, keeps it from fading out (updates the timer, keeping it in sync with the game render) ---@param illumination Illumination ---@return nil function refresh_illumination(illumination) end @@ -1310,6 +1320,12 @@ function buttons_to_inputs(x, y, buttons) end ---@param enable boolean ---@return nil function set_infinite_loop_detection_enabled(enable) end +---This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. +---Letting you control those manually. +---Look at the example on how to mimic game layer switching behavior +---@param enable boolean +---@return nil +function set_camera_layer_control_enabled(enable) end ---@return boolean function toast_visible() end ---@return boolean @@ -2038,8 +2054,9 @@ do ---@field level_gen LevelGenSystem @Entrance and exit coordinates, shop types and all themes ---@field correct_ushabti integer @See `get_correct_ushabti`. == anim_frame - (2 * floor(anim_frame/12)) ---@field items Items @Has the current player count, player inventories and character selections - ---@field camera_layer integer @The currently drawn layer, can't be changed + ---@field camera_layer integer ---@field layer_transition_timer integer + ---@field transition_to_layer integer ---@field screen_team_select ScreenTeamSelect ---@field screen_character_select ScreenCharacterSelect ---@field screen_transition ScreenTransition @@ -2106,8 +2123,9 @@ do ---@field offset_y number ---@field distortion number ---@field entity_uid integer + ---@field timer integer ---@field flags integer @see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags - ---@field type_flags integer @Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags + ---@field type_flags LIGHT_TYPE @Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags ---@field enabled boolean ---@field layer integer @@ -5643,6 +5661,7 @@ function Quad:is_point_inside(x, y, epsilon) end ---@class ScreenLevel : Screen ---@field buttons integer + ---@field time_till_death_screen integer @Delay after player death to open the death screen ---@class ScreenTransition : Screen ---@field woodpanel_pos number diff --git a/docs/generate.py b/docs/generate.py index 7890dabb5..ac37baa95 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -400,7 +400,7 @@ def print_lf(lf): ---- | ---- | -----------""" ) print("bool | all:empty() | Returns true if container is empty, false otherwise") -print("int | aLL:size() | Same as `#container`") +print("int | all:size() | Same as `#container`") print("any | vector:at(int index) | Same as `vector[index]`") print("any | span:at(int index) | Same as `span[index]`") diff --git a/docs/parse_source.py b/docs/parse_source.py index 7d58ff398..b1d8b4336 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -27,6 +27,7 @@ "../src/game_api/game_manager.hpp", "../src/game_api/state.hpp", "../src/game_api/state_structs.hpp", + "../src/game_api/illumination.hpp", "../src/game_api/prng.hpp", "../src/game_api/entities_floors.hpp", "../src/game_api/entities_activefloors.hpp", diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 9543ea915..1798c9a1e 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -158,7 +158,7 @@ They come with some extra functionality: Type | Name | Description ---- | ---- | ----------- bool | all:empty() | Returns true if container is empty, false otherwise -int | aLL:size() | Same as `#container` +int | all:size() | Same as `#container` any | vector:at(int index) | Same as `vector[index]` any | span:at(int index) | Same as `span[index]` any | set:at(int order) | Returns elements in order, it's not an index as sets don't have one @@ -1710,6 +1710,46 @@ Seed the game prng. Set the current adventure seed pair +### set_camera_layer_control_enabled + + +```lua +set_camera_layer_control_enabled(false) + +g_current_timer = nil +-- default load_time 36 +function change_layer(layer_to, load_time) + + if state.camera_layer == layer_to then + return + end + if g_current_timer ~= nil then + clear_callback(g_current_timer) + g_current_timer = nil + end + -- if we don't want the load time, we can just change the actual layer + if load_time == nil or load_time == 0 then + state.camera_layer = layer_to + return + end + + state.layer_transition_timer = load_time + state.transition_to_layer = layer_to + -- actual layer change after time delay + set_timeout(function() state.camera_layer = layer_to end, load_time) +end + +``` + + +> Search script examples for [set_camera_layer_control_enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_layer_control_enabled) + +#### nil set_camera_layer_control_enabled(bool enable) + +This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. +Letting you control those manually. +Look at the example on how to mimic game layer switching behavior + ### set_character_heart_color @@ -2042,11 +2082,13 @@ Steal input from a [Player](#Player), HiredHand or [PlayerGhost](#PlayerGhost) > Search script examples for [create_illumination](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=create_illumination) +#### [Illumination](#Illumination) create_illumination([Vec2](#Vec2) pos, [Color](#Color) color, LIGHT_TYPE type, float size, int flags, int uid, [LAYER](#LAYER) layer) + #### [Illumination](#Illumination) create_illumination([Color](#Color) color, float size, float x, float y) #### [Illumination](#Illumination) create_illumination([Color](#Color) color, float size, int uid) -Creates a new [Illumination](#Illumination). Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +Creates a new [Illumination](#Illumination). Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ### refresh_illumination @@ -2055,7 +2097,7 @@ Creates a new [Illumination](#Illumination). Don't forget to continuously call [ #### nil refresh_illumination([Illumination](#Illumination) illumination) -Refreshes an [Illumination](#Illumination), keeps it from fading out +Refreshes an [Illumination](#Illumination), keeps it from fading out (updates the timer, keeping it in sync with the game render) ## Message functions diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index e93f99fbf..ed5a51ad3 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -1287,8 +1287,9 @@ float | [offset_x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offs float | [offset_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_y) | float | [distortion](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=distortion) | int | [entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=entity_uid) | +int | [timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=timer) | int | [flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags) | see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags -int | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | Only one can be set: 1 - Follow camera, 2 - Follow [Entity](#Entity), 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags +LIGHT_TYPE | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | Only one can be set: 1 - Follow camera, 2 - Follow [Entity](#Entity), 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags bool | [enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=enabled) | int | [layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer) | @@ -2261,6 +2262,7 @@ Derived from [Screen](#Screen) Type | Name | Description ---- | ---- | ----------- int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | +int | [time_till_death_screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=time_till_death_screen) | Delay after player death to open the death screen ### ScreenLogo @@ -2879,8 +2881,9 @@ int | [speechbubble_owner](https://github.com/spelunky-fyi/overlunky/search?l=Lu [LevelGenSystem](#LevelGenSystem) | [level_gen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=level_gen) | Entrance and exit coordinates, shop types and all themes int | [correct_ushabti](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=correct_ushabti) | See `get_correct_ushabti`. == anim_frame - (2 * floor(anim_frame/12)) [Items](#Items) | [items](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=items) | Has the current player count, player inventories and character selections -int | [camera_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera_layer) | The currently drawn layer, can't be changed +int | [camera_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera_layer) | int | [layer_transition_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer_transition_timer) | +int | [transition_to_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=transition_to_layer) | [ScreenTeamSelect](#ScreenTeamSelect) | [screen_team_select](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_team_select) | [ScreenCharacterSelect](#ScreenCharacterSelect) | [screen_character_select](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_character_select) | [ScreenTransition](#ScreenTransition) | [screen_transition](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_transition) | diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index 60cbf73e2..a80542106 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -284,24 +284,14 @@ const char* presence_flags[]{ }; const char* illumination_flags[]{ - // this is actually two 8bit flags as they have different behaviors "1: Disable light1", "2: Enable light2", "3: Enable light3", "4: Enable light4", - "5: ", - "6: ", + "5: Unknown", + "6: Unknown", // always on by default "7: Modulate brightness_multiplier", - "8: ", - // type_flags 1-8: - "9: Follow camera", - "10: Follow entity", - "11: Static, Rectangle, Full brightness", - "12: unused", - "13: unused", - "14: unused", - "15: unused", - "16: unused", + "8: Unknown", }; const char* special_visibility_flags[]{ diff --git a/src/game_api/illumination.hpp b/src/game_api/illumination.hpp index 4b0d18731..7908a897d 100644 --- a/src/game_api/illumination.hpp +++ b/src/game_api/illumination.hpp @@ -67,7 +67,7 @@ struct Illumination }; }; -Illumination* create_illumination(Vec2 pos, Color color, LIGHT_TYPE type, float size, uint8_t light_flags, int32_t uid, LAYER layer); +Illumination* create_illumination(Vec2 pos, Color color, LIGHT_TYPE type, float size, uint8_t flags, int32_t uid, LAYER layer); Illumination* create_illumination(Color color, float size, float x, float y); Illumination* create_illumination(Color color, float size, int32_t uid); void refresh_illumination(Illumination* illumination); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index a1d7ad864..39cb801e7 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -1802,7 +1802,7 @@ end static_cast(::create_illumination), static_cast(::create_illumination), static_cast(::create_illumination)); - /// Creates a new Illumination. Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example + /// Creates a new Illumination. Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. lua["create_illumination"] = create_illumination; /// Refreshes an Illumination, keeps it from fading out (updates the timer, keeping it in sync with the game render) lua["refresh_illumination"] = refresh_illumination; From b64d34c11eb52aa1df33e4a1d641308bb181ba06 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 28 Oct 2023 22:52:20 +0200 Subject: [PATCH 18/31] some more doc stuff --- docs/game_data/spel2.lua | 2 +- docs/src/includes/_enums.md | 14 ++++++++++++++ docs/src/includes/_globals.md | 2 +- docs/src/includes/_types.md | 2 +- src/game_api/flags.hpp | 8 -------- src/game_api/illumination.hpp | 6 +----- src/game_api/script/usertypes/state_lua.cpp | 13 +++++++++++++ 7 files changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 2ea54d1b0..b9ad798de 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -2125,7 +2125,7 @@ do ---@field entity_uid integer ---@field timer integer ---@field flags integer @see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags - ---@field type_flags LIGHT_TYPE @Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags + ---@field type_flags LIGHT_TYPE ---@field enabled boolean ---@field layer integer diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 78e066c82..378ca43f0 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -654,6 +654,20 @@ Name | Data | Description [MAX_LIQUID_PARTICLES](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LEVEL_CONFIG.MAX_LIQUID_PARTICLES) | 15 | [FLAGGED_LIQUID_ROOMS](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LEVEL_CONFIG.FLAGGED_LIQUID_ROOMS) | 16 | +## LIGHT_TYPE + + +> Search script examples for [LIGHT_TYPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE) + + + +Name | Data | Description +---- | ---- | ----------- +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.NONE) | LIGHT_TYPE::NONE | Normal static light, position can be edited to move it around
+[FOLLOW_CAMERA](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.FOLLOW_CAMERA) | LIGHT_TYPE::FOLLOW_CAMERA | Position is updated to the camera position, can be moved around via offset
+[FOLLOW_ENTITY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.FOLLOW_ENTITY) | LIGHT_TYPE::FOLLOW_ENTITY | Position is updated to the entity position (from the uid field), if the uid is not found it will behave as [LIGHT_TYPE](#LIGHT_TYPE).NONE, can be moved around via offset
+[ROOM_LIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.ROOM_LIGHT) | LIGHT_TYPE::ROOM_LIGHT | Rectangle, full brightness always uses light1, disabling light1 does nothing
+ ## LIQUID_POOL diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 1798c9a1e..2dec314e2 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -2082,7 +2082,7 @@ Steal input from a [Player](#Player), HiredHand or [PlayerGhost](#PlayerGhost) > Search script examples for [create_illumination](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=create_illumination) -#### [Illumination](#Illumination) create_illumination([Vec2](#Vec2) pos, [Color](#Color) color, LIGHT_TYPE type, float size, int flags, int uid, [LAYER](#LAYER) layer) +#### [Illumination](#Illumination) create_illumination([Vec2](#Vec2) pos, [Color](#Color) color, [LIGHT_TYPE](#LIGHT_TYPE) type, float size, int flags, int uid, [LAYER](#LAYER) layer) #### [Illumination](#Illumination) create_illumination([Color](#Color) color, float size, float x, float y) diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index ed5a51ad3..59dd3306c 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -1289,7 +1289,7 @@ float | [distortion](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=di int | [entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=entity_uid) | int | [timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=timer) | int | [flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags) | see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags -LIGHT_TYPE | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | Only one can be set: 1 - Follow camera, 2 - Follow [Entity](#Entity), 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags +[LIGHT_TYPE](#LIGHT_TYPE) | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | bool | [enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=enabled) | int | [layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer) | diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index a80542106..6b830568a 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -319,14 +319,6 @@ const char* special_visibility_flags[]{ "22: ", "23: ", "24: ", - "25: In back layer", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", }; const char* basecamp_dialogue_win_flags[]{ diff --git a/src/game_api/illumination.hpp b/src/game_api/illumination.hpp index 7908a897d..c03b0b62f 100644 --- a/src/game_api/illumination.hpp +++ b/src/game_api/illumination.hpp @@ -54,12 +54,8 @@ struct Illumination uint32_t flags; struct { - // TODO: separate flags in api + uint8_t light_flags; // not exposed since flags already is so no reason to essentially expose this again, even thou flags is technically not correct - uint8_t light_flags; // no reason to expose this - - /// Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness - /// Rectangle always uses light1, even when it's disabled in flags LIGHT_TYPE type_flags; uint8_t layer; bool enabled; diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index b25666708..b95ca54ad 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -436,6 +436,19 @@ void register_usertypes(sol::state& lua) illumination_type["enabled"] = &Illumination::enabled; illumination_type["layer"] = &Illumination::layer; + lua.create_named_table("LIGHT_TYPE", "NONE", LIGHT_TYPE::NONE, "FOLLOW_CAMERA", LIGHT_TYPE::FOLLOW_CAMERA, "FOLLOW_ENTITY", LIGHT_TYPE::FOLLOW_ENTITY, "ROOM_LIGHT", LIGHT_TYPE::ROOM_LIGHT); + + /* LIGHT_TYPE + // NONE + // Normal static light, position can be edited to move it around + // FOLLOW_CAMERA + // Position is updated to the camera position, can be moved around via offset + // FOLLOW_ENTITY + // Position is updated to the entity position (from the uid field), if the uid is not found it will behave as LIGHT_TYPE.NONE, can be moved around via offset + // ROOM_LIGHT + // Rectangle, full brightness always uses light1, disabling light1 does nothing + */ + auto camera_type = lua.new_usertype("Camera"); camera_type["bounds_left"] = &Camera::bounds_left; camera_type["bounds_right"] = &Camera::bounds_right; From 1c425b4575d2006a3616c1ab143de304841b075b Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 29 Oct 2023 19:08:16 +0100 Subject: [PATCH 19/31] game api stuff --- src/game_api/game_api.cpp | 32 ++++++++++ src/game_api/game_api.hpp | 72 +++++++++++++++++++++ src/game_api/render_api.cpp | 34 +++------- src/game_api/render_api.hpp | 7 +-- src/game_api/script/lua_vm.cpp | 17 ++--- src/game_api/search.cpp | 111 ++++++++++++++++++--------------- src/game_api/settings_api.cpp | 11 ++-- src/game_api/state.cpp | 48 +++----------- src/game_api/state.hpp | 5 +- src/game_api/texture.cpp | 4 +- src/injected/ui_util.cpp | 4 +- 11 files changed, 205 insertions(+), 140 deletions(-) create mode 100644 src/game_api/game_api.cpp create mode 100644 src/game_api/game_api.hpp diff --git a/src/game_api/game_api.cpp b/src/game_api/game_api.cpp new file mode 100644 index 000000000..7b333c631 --- /dev/null +++ b/src/game_api/game_api.cpp @@ -0,0 +1,32 @@ +#include "game_api.hpp" + +#include "search.hpp" + +GameAPI* GameAPI::get() +{ + using GetGameAPI = GameAPI*(); + static auto addr = (GetGameAPI*)get_address("get_game_api"); + return addr(); +} + +float GameAPI::get_current_zoom() +{ + return renderer->current_zoom + renderer->current_zoom_offset; +} + +float GameAPI::get_target_zoom() +{ + return renderer->target_zoom + renderer->target_zoom_offset; +} + +void GameAPI::set_zoom(std::optional current, std::optional target) +{ + if (current.has_value()) + { + renderer->current_zoom = current.value(); // - renderer->current_zoom_offset; + } + if (target.has_value()) + { + renderer->target_zoom = target.value(); // - renderer->target_zoom_offset; + } +} diff --git a/src/game_api/game_api.hpp b/src/game_api/game_api.hpp new file mode 100644 index 000000000..221c479ef --- /dev/null +++ b/src/game_api/game_api.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +struct Renderer +{ + uint32_t render_width; // sam as window size unless resolution scale is set + uint32_t render_height; + + uint32_t fps; // changing it doesn't seam to do anything + uint32_t fps_denominator; + + uint32_t render_width2; // repeat + uint32_t render_height2; + + uint8_t flags1; + uint8_t flags2; + uint8_t padding[6]; + + uint8_t skip[0x1228]; // tons of pointers + + uint8_t skip2[0x7F284]; // a lot of nothing + + float current_zoom; + float target_zoom; + float target_zoom_offset; + float current_zoom_offset; + float backlayer_light_level; // constantly overwritten by theme virtual get_backlayer_light_level + uint8_t unknown2; + uint8_t unknown3; + uint16_t unknown4; + + uint8_t skip3[0xAE4]; + + size_t swap_chain; + + // somewhere there should be shareds stored + + // added just to have the vtable + virtual ~Renderer() = 0; + virtual void some_dx_stuff() = 0; // it actually has a ton of parameters +}; + +struct GameAPI // size 0x60 +{ + static GameAPI* get(); + + float get_current_zoom(); + float get_target_zoom(); + + void set_zoom(std::optional current, std::optional target); + + bool unknown1; + size_t unknown2; // pointer + Renderer* renderer; + uint32_t window_width; + uint32_t window_height; + + size_t unknown5; // garbage? + size_t unknown6; // exe start + size_t unknown7; // some offset + size_t unknown8; // garbage? + size_t SteamAPI_Callback; // just vtable? don't know much about steam stuff + + uint8_t unknown10a; // bool ? + uint32_t unknown10b; + + size_t unknown11; // garbage? + size_t unknown12; // garbage? +}; diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index 999632af5..a85f35c5a 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -12,6 +12,7 @@ #include // for vector #include "entity.hpp" // for Entity, EntityDB +#include "game_api.hpp" // #include "level_api.hpp" // for ThemeInfo #include "logger.h" // for DEBUG #include "memory.hpp" // for memory_read, to_le_bytes, write_mem_prot @@ -31,21 +32,20 @@ struct Layer; RenderAPI& RenderAPI::get() { - static RenderAPI render_api = []() - { - return RenderAPI{(size_t*)get_address("render_api_callback"sv), get_address("render_api_offset"sv)}; - }(); + static RenderAPI render_api; return render_api; } -size_t RenderAPI::renderer() const +Renderer* RenderAPI::renderer() const { - return memory_read(*api + 0x10); + auto game_api = GameAPI::get(); + return game_api->renderer; } size_t RenderAPI::swap_chain() const { - return memory_read(renderer() + swap_chain_off); + return renderer()->swap_chain; + // return memory_read(renderer() + swap_chain_off); // swap_chain_off from pattern: render_api_offset } void (*g_post_render_game)(){nullptr}; @@ -64,7 +64,6 @@ void render_loading(size_t param_1) } std::optional g_forced_lut_textures[2]{}; -float g_layer_zoom_offset[2]{0}; using RenderLayer = void(const std::vector&, uint8_t, const Camera&, const char**, const char**); RenderLayer* g_render_layer_trampoline{nullptr}; @@ -73,16 +72,6 @@ void render_layer(const std::vector& lightsources, uint8_t layer, if (trigger_vanilla_render_layer_callbacks(ON::RENDER_PRE_LAYER, layer)) return; - static size_t offset = 0; - if (offset == 0) - { - auto addr = State::get_zoom_level_address(); - offset = addr + 8; - } - if (offset != 0) - { - g_layer_zoom_offset[layer] = memory_read(offset); - } // The lhs and rhs LUTs are blended in the shader, but we don't know where that value is CPU side so we can only override // with a single LUT for now if (g_forced_lut_textures[layer]) @@ -117,11 +106,6 @@ void render_game(StateMemory* state) trigger_vanilla_render_callbacks(ON::RENDER_POST_GAME); } -float get_layer_zoom_offset(uint8_t layer) -{ - return g_layer_zoom_offset[layer]; -} - void RenderAPI::set_lut(TEXTURE texture_id, uint8_t layer) { g_forced_lut_textures[layer] = texture_id; @@ -482,7 +466,7 @@ void RenderAPI::draw_world_texture(Texture* texture, Quad source, Quad dest, Col dest.top_left_y, unknown}; - typedef void render_func(size_t, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); + typedef void render_func(Renderer*, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); static render_func* rf = (render_func*)(func_offset); auto texture_name = texture->name; rf(renderer(), shader, &texture_name, 1, destination, &source, (void*)param_7, &color, nullptr); @@ -500,7 +484,7 @@ void RenderAPI::set_advanced_hud() void RenderAPI::reload_shaders() { - using ReloadShadersFun = void(size_t); + using ReloadShadersFun = void(Renderer*); static ReloadShadersFun* reload_shaders_impl = (ReloadShadersFun*)get_address("reload_shaders"sv); reload_shaders_impl(renderer()); } diff --git a/src/game_api/render_api.hpp b/src/game_api/render_api.hpp index 47da31719..352f167fe 100644 --- a/src/game_api/render_api.hpp +++ b/src/game_api/render_api.hpp @@ -18,6 +18,7 @@ struct JournalUI; struct Layer; class Entity; +struct Renderer; using VANILLA_TEXT_ALIGNMENT = uint32_t; using VANILLA_FONT_STYLE = uint32_t; @@ -225,9 +226,6 @@ struct TextureRenderingInfo struct RenderAPI { - const size_t* api; - size_t swap_chain_off; - mutable std::mutex custom_textures_lock; std::unordered_map custom_textures; std::unordered_map original_textures; @@ -236,7 +234,7 @@ struct RenderAPI static RenderAPI& get(); - size_t renderer() const; + Renderer* renderer() const; size_t swap_chain() const; void set_lut(TEXTURE texture_id, uint8_t layer); @@ -366,7 +364,6 @@ struct RenderInfo void init_render_api_hooks(); bool& get_journal_enabled(); void on_open_journal_chapter(JournalUI* journal_ui, uint8_t chapter, bool instant, bool play_sound); -float get_layer_zoom_offset(uint8_t layer); void render_draw_depth(Layer* layer, uint8_t draw_depth, float bbox_left, float bbox_bottom, float bbox_right, float bbox_top); struct HudInventory diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 39cb801e7..6303f0e27 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -36,6 +36,7 @@ #include "entities_items.hpp" // for Container, Player... #include "entity.hpp" // for get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager #include "handle_lua_function.hpp" // for handle_function #include "illumination.hpp" // @@ -671,27 +672,26 @@ end lua["read_prng"] = []() -> std::vector { return read_prng(); }; - using Toast = void(const char16_t*); - using Say = void(size_t, Entity*, const char16_t*, int, bool); - /// Show a message that looks like a level feeling. lua["toast"] = [](std::u16string message) { + using Toast = void(const char16_t*); static Toast* toast_fun = (Toast*)get_address("toast"); toast_fun(message.c_str()); }; /// Show a message coming from an entity lua["say"] = [](uint32_t entity_uid, std::u16string message, int sound_type, bool top) { + using Say = void(HudData*, Entity*, const char16_t*, int, bool); static auto say = (Say*)get_address("speech_bubble_fun"); - static const auto say_context = get_address("say_context"); + const auto hud = get_hud(); auto entity = get_entity_ptr(entity_uid); if (entity == nullptr) return; - say(say_context, entity, message.c_str(), sound_type, top); + say(hud, entity, message.c_str(), sound_type, top); }; /// Add an integer option that the user can change in the UI. Read with `options.name`, `value` is the default. Keep in mind these are just soft /// limits, you can override them in the UI with double click. @@ -1108,7 +1108,7 @@ end /// Set the `more_flags` field from entity by uid lua["set_entity_flags2"] = set_entity_flags2; /// Deprecated - /// As the name is misleading. use entity `move_state` field instead + /// As the name is misleading. use Movable.`move_state` field instead lua["get_entity_ai_state"] = get_entity_ai_state; /// Get `state.level_flags` lua["get_level_flags"] = get_level_flags; @@ -1118,7 +1118,10 @@ end lua["get_entity_type"] = get_entity_type; /// Get the current set zoom level lua["get_zoom_level"] = []() -> float - { return State::get_zoom_level(); }; + { + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); + }; /// Get the game coordinates at the screen position (`x`, `y`) lua["game_position"] = [](float x, float y) -> std::pair { return State::click_position(x, y); }; diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index e9b59b411..46881a4a5 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -572,25 +572,25 @@ std::unordered_map g_address_rules{ .decode_pc(4) .at_exe(), }, - { - "render_api_callback"sv, - // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time - // to hook the Steam overlay, at the beginning of that function is the pointer we need - PatternCommandBuffer{} - .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) - .offset(0x8) - .decode_pc() - .at_exe(), - }, - { - // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: - // mov rcx,qword ptr ds:[rsi+80FD0] - "render_api_offset"sv, - PatternCommandBuffer{} - .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) - .offset(0x11) - .decode_imm(), - }, + //{ + // "render_api_callback"sv, + // // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time + // // to hook the Steam overlay, at the beginning of that function is the pointer we need + // PatternCommandBuffer{} + // .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) + // .offset(0x8) + // .decode_pc() + // .at_exe(), + //}, + //{ + // // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: + // // mov rcx,qword ptr ds:[rsi+80FD0] + // "render_api_offset"sv, + // PatternCommandBuffer{} + // .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) + // .offset(0x11) + // .decode_imm(), + //}, { // in load_item it's written to RCX and then calls spawn_entity "entity_factory"sv, @@ -1044,30 +1044,30 @@ std::unordered_map g_address_rules{ .decode_pc(2) .at_exe(), }, - { - "zoom_level"sv, - // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 - // Go into the shop, search for 12.5, put a write bp on that address. - // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are - // in variables (loaded into xmm6) - // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the - // return value of the call just above, so look in that function at the bottom. There will - // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the - // base for the big offset. - PatternCommandBuffer{} - .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) - .decode_pc() - .at_exe(), - }, - { - "zoom_level_offset"sv, - // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. - // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present - // current solution uses the target_zoom offset - PatternCommandBuffer{} - .find_inst("\xF3\x0F\x11\xB0****\x49"sv) - .decode_imm(4), - }, + //{ + // "zoom_level"sv, + // // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 + // // Go into the shop, search for 12.5, put a write bp on that address. + // // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are + // // in variables (loaded into xmm6) + // // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the + // // return value of the call just above, so look in that function at the bottom. There will + // // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the + // // base for the big offset. + // PatternCommandBuffer{} + // .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) + // .decode_pc() + // .at_exe(), + //}, + //{ + // "zoom_level_offset"sv, + // // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. + // // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present + // // current solution uses the target_zoom offset + // PatternCommandBuffer{} + // .find_inst("\xF3\x0F\x11\xB0****\x49"sv) + // .decode_imm(4), + //}, { "default_zoom_level"sv, // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. @@ -1534,15 +1534,6 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, - { - "say_context"sv, - // Find the pattern for `say`, go one up higher in the callstack and look what writes to rcx - PatternCommandBuffer{} - .find_after_inst("\xC6\x44\x24\x20\x01\x48\x8D\x0D"sv) - .offset(-0x3) - .decode_pc() - .at_exe(), - }, { "force_dark_level"sv, // Put a write bp on State.level_flags (3rd byte, containing dark level flag) @@ -2070,6 +2061,15 @@ std::unordered_map g_address_rules{ .offset(0x5) .at_exe(), }, + { + "get_game_api"sv, + // can be found together with get_feat function + PatternCommandBuffer{} + .find_after_inst("49 89 CE 4C 8B 79 08"_gh) + .find_inst("\xE8"sv) + .decode_call() + .at_exe(), + }, }; std::unordered_map g_cached_addresses; @@ -2081,6 +2081,13 @@ void preload_addresses() { if (auto address = rule(mem, exe, address_name)) { + for (auto& [k, v] : g_cached_addresses) + { + if (v == address.value() && k != address_name) + { + DEBUG("Two patterns refer to the same address: {} & {}", k, address_name); + } + } g_cached_addresses[address_name] = address.value(); } } diff --git a/src/game_api/settings_api.cpp b/src/game_api/settings_api.cpp index 1a1eba15c..604c70ddf 100644 --- a/src/game_api/settings_api.cpp +++ b/src/game_api/settings_api.cpp @@ -8,8 +8,8 @@ #include // for hash, move, conditional_t #include // for unordered_set, _Uset_traits<>::allocator_type -#include "render_api.hpp" // for RenderAPI -#include "search.hpp" // for get_address +#include "game_api.hpp" // +#include "search.hpp" // for get_address union SettingValue { @@ -102,8 +102,11 @@ std::optional get_setting(GAME_SETTING setting) { if ((setting == GAME_SETTING::FREQUENCY_NUMERATOR || setting == GAME_SETTING::FREQUENCY_DENOMINATOR) && get_setting(GAME_SETTING::WINDOW_MODE) == 1u) { - size_t renderer = RenderAPI::get().renderer(); - return *reinterpret_cast(renderer + (setting == GAME_SETTING::FREQUENCY_NUMERATOR ? 0x10 : 0x14)); + auto game_api = GameAPI::get(); + if (setting == GAME_SETTING::FREQUENCY_NUMERATOR) + return game_api->renderer->fps; + else + return game_api->renderer->fps_denominator; } if (SettingData* data = get_setting_data(setting)) diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 7ef2c9319..5873fedc1 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -13,6 +13,7 @@ #include "entities_chars.hpp" // for Player #include "entity.hpp" // for to_id, Entity, HookWithId, EntityDB #include "entity_hooks_info.hpp" // for Player +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager, SaveR... #include "game_patches.hpp" // #include "items.hpp" // for Items, SelectPlayerSlot @@ -332,6 +333,12 @@ StateMemory* State::ptr_local() const return p.decode_local(); } +float get_zoom_level() +{ + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); +} + std::pair State::click_position(float x, float y) { float cz = get_zoom_level(); @@ -350,40 +357,6 @@ std::pair State::screen_position(float x, float y) return {rx, ry}; } -size_t State::get_zoom_level_address() -{ - static const size_t obj1 = get_address("zoom_level"); - static const size_t zoom_level_offset = get_address("zoom_level_offset"); - size_t obj2 = memory_read(obj1); - if (obj2 == 0) - { - return 0; - } - - size_t obj3 = memory_read(obj2 + 0x10); - if (obj3 == 0) - { - return 0; - } - return obj3 + zoom_level_offset; -} - -float State::get_zoom_level() -{ - static size_t offset = 0; - if (offset == 0) - { - auto addr = get_zoom_level_address(); - if (addr == 0) - { - return 13.5; - } - offset = addr - 4; - } - auto state = State::get().ptr(); - return memory_read(offset) + get_layer_zoom_offset(state->camera_layer); -} - void State::zoom(float level) { auto roomx = ptr()->w; @@ -434,11 +407,8 @@ void State::zoom(float level) write_mem_prot(zoom_telescope, level_str, true); // overwrite the current value - auto zla = get_zoom_level_address(); - if (zla != 0) - { - write_mem_prot(zla, level_str, true); - } + auto game_api = GameAPI::get(); + game_api->set_zoom(std::nullopt, level); } void StateMemory::force_current_theme(uint32_t t) diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index f394f7fa9..7cf1b152c 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -293,8 +293,7 @@ struct StateMemory int8_t unknown41; // other character related (hired hand, basecamp characters) uint8_t unknown42; uint8_t unknown43; - uint32_t unknown44; - uint64_t unknown45; + uint32_t unknown44; // probably padding /// This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! void force_current_theme(uint32_t t); @@ -336,8 +335,6 @@ struct State void godmode_companions(bool g); void darkmode(bool g); - static size_t get_zoom_level_address(); - static float get_zoom_level(); void zoom(float level); static std::pair click_position(float x, float y); diff --git a/src/game_api/texture.cpp b/src/game_api/texture.cpp index bb495b842..3cbd0a5b1 100644 --- a/src/game_api/texture.cpp +++ b/src/game_api/texture.cpp @@ -261,8 +261,6 @@ void reload_texture(const char* texture_name) } void reload_texture(const char** texture_name) { - class Renderer; - using LoadTextureFunT = void(Renderer*, const char**); auto& render = RenderAPI::get(); @@ -272,7 +270,7 @@ void reload_texture(const char** texture_name) // to the wanted function static constexpr size_t c_LoadTextureVirtualIndex = 0x2E; - auto renderer_ptr = (Renderer*)render.renderer(); + auto renderer_ptr = render.renderer(); auto load_texture = *vtable_find(renderer_ptr, c_LoadTextureVirtualIndex); load_texture(renderer_ptr, texture_name); } diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index aee03ee37..8a31c699c 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -14,6 +14,7 @@ #include "entities_mounts.hpp" // for Mount #include "entity.hpp" // for to_id, Entity, get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager #include "illumination.hpp" // #include "items.hpp" // for Items @@ -84,7 +85,8 @@ void UI::transition(uint8_t world, uint8_t level, uint8_t theme) } float UI::get_zoom_level() { - return State::get_zoom_level(); + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); } void UI::teleport(float x, float y, bool s, float vx, float vy, bool snap) { From de66eaa6c04fbe4b8589ad27b3e7b42311fc13cf Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 29 Oct 2023 20:19:09 +0100 Subject: [PATCH 20/31] fix example --- docs/examples/set_camera_layer_control_enabled.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/examples/set_camera_layer_control_enabled.lua b/docs/examples/set_camera_layer_control_enabled.lua index 5140c0d27..633d07ac4 100644 --- a/docs/examples/set_camera_layer_control_enabled.lua +++ b/docs/examples/set_camera_layer_control_enabled.lua @@ -19,6 +19,5 @@ function change_layer(layer_to, load_time) state.layer_transition_timer = load_time state.transition_to_layer = layer_to - -- actual layer change after time delay - set_timeout(function() state.camera_layer = layer_to end, load_time) + state.camera_layer = layer_to end From 33b6df2fbdf4bf73908462039b70a4fc17b85a7a Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 29 Oct 2023 20:20:28 +0100 Subject: [PATCH 21/31] why this is fine locally but not on remote? --- src/game_api/script/lua_vm.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 6303f0e27..c0f89c1fb 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -672,17 +672,18 @@ end lua["read_prng"] = []() -> std::vector { return read_prng(); }; + using Toast = void(const char16_t*); + using Say = void(HudData*, Entity*, const char16_t*, int, bool); + /// Show a message that looks like a level feeling. lua["toast"] = [](std::u16string message) { - using Toast = void(const char16_t*); - static Toast* toast_fun = (Toast*)get_address("toast"); + static auto toast_fun = (Toast*)get_address("toast"); toast_fun(message.c_str()); }; /// Show a message coming from an entity lua["say"] = [](uint32_t entity_uid, std::u16string message, int sound_type, bool top) { - using Say = void(HudData*, Entity*, const char16_t*, int, bool); static auto say = (Say*)get_address("speech_bubble_fun"); const auto hud = get_hud(); From 78546ae9a9d33bda8b19835f6a92be5cacd7e4a5 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 29 Oct 2023 20:21:10 +0100 Subject: [PATCH 22/31] bring back zoom hack --- src/game_api/game_api.cpp | 5 ++++- src/game_api/render_api.cpp | 9 +++++++++ src/game_api/render_api.hpp | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/game_api/game_api.cpp b/src/game_api/game_api.cpp index 7b333c631..b4521058a 100644 --- a/src/game_api/game_api.cpp +++ b/src/game_api/game_api.cpp @@ -1,6 +1,8 @@ #include "game_api.hpp" +#include "render_api.hpp" #include "search.hpp" +#include "state.hpp" GameAPI* GameAPI::get() { @@ -11,7 +13,8 @@ GameAPI* GameAPI::get() float GameAPI::get_current_zoom() { - return renderer->current_zoom + renderer->current_zoom_offset; + auto state = State::get().ptr(); + return renderer->current_zoom + get_layer_transition_zoom_offset(state->camera_layer); } float GameAPI::get_target_zoom() diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index a85f35c5a..011694831 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -64,6 +64,7 @@ void render_loading(size_t param_1) } std::optional g_forced_lut_textures[2]{}; +float g_layer_zoom_offset[2]; using RenderLayer = void(const std::vector&, uint8_t, const Camera&, const char**, const char**); RenderLayer* g_render_layer_trampoline{nullptr}; @@ -72,6 +73,9 @@ void render_layer(const std::vector& lightsources, uint8_t layer, if (trigger_vanilla_render_layer_callbacks(ON::RENDER_PRE_LAYER, layer)) return; + auto game_api = GameAPI::get(); + g_layer_zoom_offset[layer] = game_api->renderer->current_zoom_offset; + // The lhs and rhs LUTs are blended in the shader, but we don't know where that value is CPU side so we can only override // with a single LUT for now if (g_forced_lut_textures[layer]) @@ -106,6 +110,11 @@ void render_game(StateMemory* state) trigger_vanilla_render_callbacks(ON::RENDER_POST_GAME); } +float get_layer_transition_zoom_offset(uint8_t l) +{ + return g_layer_zoom_offset[l]; +} + void RenderAPI::set_lut(TEXTURE texture_id, uint8_t layer) { g_forced_lut_textures[layer] = texture_id; diff --git a/src/game_api/render_api.hpp b/src/game_api/render_api.hpp index 352f167fe..d0c90d1f7 100644 --- a/src/game_api/render_api.hpp +++ b/src/game_api/render_api.hpp @@ -365,6 +365,7 @@ void init_render_api_hooks(); bool& get_journal_enabled(); void on_open_journal_chapter(JournalUI* journal_ui, uint8_t chapter, bool instant, bool play_sound); void render_draw_depth(Layer* layer, uint8_t draw_depth, float bbox_left, float bbox_bottom, float bbox_right, float bbox_top); +float get_layer_transition_zoom_offset(uint8_t layer); struct HudInventory { From b8ad3f0b358201ba1387c6ee3081ac953a3195ee Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 30 Oct 2023 20:21:31 +0100 Subject: [PATCH 23/31] fix ui flags --- src/game_api/flags.hpp | 61 ++++++++++++------------- src/game_api/render_api.cpp | 6 +-- src/injected/ui.cpp | 88 +++++++++++++------------------------ 3 files changed, 64 insertions(+), 91 deletions(-) diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index 6b830568a..954e91c2d 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -1,4 +1,5 @@ -const char* themes[]{ + +std::array themes{ "1: Dwelling", "2: Jungle", "2: Volcana", @@ -18,7 +19,7 @@ const char* themes[]{ "0: Base camp", }; -const char* themes_short[]{ +std::array themes_short{ "Dwelling", "Jungle", "Volcana", @@ -38,7 +39,7 @@ const char* themes_short[]{ "Base camp", }; -const char* entity_flags[]{ +std::array entity_flags{ "1: Invisible", "2: Indestructable/special floor", "3: Solid (wall)", @@ -73,7 +74,7 @@ const char* entity_flags[]{ "32: Use overlay draw depth", }; -const char* more_flags[]{ +std::array more_flags{ "1: Take damage on collision after throw", "2: Revived (HH)", "3: Blocks shield", @@ -108,7 +109,7 @@ const char* more_flags[]{ "32: ", }; -const char* entity_type_properties_flags[]{ +std::array entity_type_properties_flags{ "1: Apply solid block beautification", "2: Treat as FLOORSTYLED", "3: ", @@ -143,7 +144,7 @@ const char* entity_type_properties_flags[]{ "32: Unused", }; -const char* level_flags[]{ +std::array level_flags{ "1: Upbeat dwelling intro music played", "2: ", "3: Running tutorial speedrun", @@ -178,7 +179,7 @@ const char* level_flags[]{ "32: ", }; -const char* journal_flags[]{ +std::array journal_flags{ "1: I was a pacifist", "2: I was a vegan", "3: I was a vegetarian", @@ -213,7 +214,7 @@ const char* journal_flags[]{ "32: ", }; -const char* quest_flags[]{ +std::array quest_flags{ "1: Reset", "2: Dark level spawned in world", "3: Vault spawned in world", @@ -248,7 +249,7 @@ const char* quest_flags[]{ "32: ", }; -const char* presence_flags[]{ +std::array presence_flags{ "1: Udjat eye", "2: Black market", "3: Vlad's castle/drill", @@ -283,7 +284,7 @@ const char* presence_flags[]{ "32: ", }; -const char* illumination_flags[]{ +std::array illumination_flags{ "1: Disable light1", "2: Enable light2", "3: Enable light3", @@ -294,7 +295,7 @@ const char* illumination_flags[]{ "8: Unknown", }; -const char* special_visibility_flags[]{ +std::array special_visibility_flags{ "1: Crust embedded items shown", "2: Crust embedded items shown (level transition)", "3: ", @@ -321,7 +322,7 @@ const char* special_visibility_flags[]{ "24: ", }; -const char* basecamp_dialogue_win_flags[]{ +std::array basecamp_dialogue_win_flags{ "1: Ana Spelunky saved", "2: Margaret Tunnel saved", "3: Colin Northward saved", @@ -356,7 +357,7 @@ const char* basecamp_dialogue_win_flags[]{ "32: ", }; -const char* places_flags[]{ +std::array places_flags{ "Dwelling", "Jungle", "Volcana", @@ -375,7 +376,7 @@ const char* places_flags[]{ "Cosmic Ocean", }; -const char* people_flags[]{ +std::array people_flags{ "Ana Spelunky", "Margaret Tunnel", "Colin Northward", @@ -416,7 +417,7 @@ const char* people_flags[]{ "Eggplant King", }; -const char* bestiary_flags[]{ +std::array bestiary_flags{ "Snake", "Spider", "Bat", @@ -497,7 +498,7 @@ const char* bestiary_flags[]{ "Mech Rider", }; -const char* items_flags[]{ +std::array items_flags{ "Rope Pile", "Bomb Bag", "Bomb Box", @@ -554,7 +555,7 @@ const char* items_flags[]{ "Four-Leaf Clover", }; -const char* traps_flags[]{ +std::array traps_flags{ "Spikes", "Arrow Trap", "Totem Trap", @@ -581,7 +582,7 @@ const char* traps_flags[]{ "Egg Sac", }; -const char* shortcut_flags[]{ +std::array shortcut_flags{ "None", "Met Terra", "1-4: $2,000", @@ -595,7 +596,7 @@ const char* shortcut_flags[]{ "5-1: Golden Key (Unlocked)", }; -/*const char *empty_flags[]{ +/*std::array empty_flags{ "1: ", "2: ", "3: ", @@ -629,7 +630,7 @@ const char* shortcut_flags[]{ "31: ", "32: ",};*/ -const char* button_flags[]{ +std::array button_flags{ "Jp", "Wp", "Bm", @@ -637,14 +638,14 @@ const char* button_flags[]{ "Rn", "Dr", }; -const char* direction_flags[]{ +std::array direction_flags{ "Left", "Down", "Up", "Right", }; -const char* liquid_pool_names[]{ +std::array liquid_pool_names{ "Water", "Coarse Water", "Lava", @@ -652,7 +653,7 @@ const char* liquid_pool_names[]{ "Stagnant Lava", }; -const char* mask_names[]{ +std::array mask_names{ "Player", "Mount", "Monster", @@ -670,7 +671,7 @@ const char* mask_names[]{ "Lava", }; -const char* char_states[]{ +std::array char_states{ "Flailing", "Standing", "Sitting", @@ -696,7 +697,7 @@ const char* char_states[]{ "Dying", }; -const char* screen_names[]{ +std::array screen_names{ "Logo", "Intro", "Prologue", @@ -729,7 +730,7 @@ const char* screen_names[]{ "Online Lobby", }; -const char* pause_types[]{ +std::array pause_types{ "1: Menu", "2: Fade (janky camera, default)", "4: Cutscene", @@ -739,7 +740,7 @@ const char* pause_types[]{ "Freeze on PRE_UPDATE", // this is not a real state.pause flag, it's only used by ui.cpp for magic }; -const char* levelgen_flags[]{ +std::array levelgen_flags{ "1: Should generate path", "2: Can spawn vault", "3: Can spawn shops", @@ -750,7 +751,7 @@ const char* levelgen_flags[]{ "8: unknown", }; -const char* levelgen_flags2[]{ +std::array levelgen_flags2{ "1: Spawns background decorations on ground (ceiling if false)", "2: Spawns fake ladder/chain midbg?", "3: Spawn entrance door background (Ignored in 7-1 to 7-2 transition)", @@ -761,7 +762,7 @@ const char* levelgen_flags2[]{ "8: Has ghost", }; -const char* levelgen_flags3[]{ +std::array levelgen_flags3{ "1: Can spawn angry NPCs", "2: Can echo", "3: Can spawn Dead are Restless", @@ -772,7 +773,7 @@ const char* levelgen_flags3[]{ "8: unknown", }; -const char* level_chances[]{ +std::array level_chances{ "backroom", "backroom interconnect", "backroom hidden door", diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index 011694831..407e357e2 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -64,7 +64,7 @@ void render_loading(size_t param_1) } std::optional g_forced_lut_textures[2]{}; -float g_layer_zoom_offset[2]; +float g_layer_zoom_offset[2]{0}; using RenderLayer = void(const std::vector&, uint8_t, const Camera&, const char**, const char**); RenderLayer* g_render_layer_trampoline{nullptr}; @@ -110,9 +110,9 @@ void render_game(StateMemory* state) trigger_vanilla_render_callbacks(ON::RENDER_POST_GAME); } -float get_layer_transition_zoom_offset(uint8_t l) +float get_layer_transition_zoom_offset(uint8_t layer) { - return g_layer_zoom_offset[l]; + return g_layer_zoom_offset[layer]; } void RenderAPI::set_lut(TEXTURE texture_id, uint8_t layer) diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index e07fb8711..eae247ca0 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -3480,6 +3480,23 @@ void render_light(const char* name, LightParams* light) ImGui::PopID(); } +template +void render_flags(const std::array names_array, T* flag_field) +{ + for (int idx{0}; idx < SIZE && idx < sizeof(T) * 8; ++idx) + { + // just simplified version of CheckboxFlagsT + + T value = (T)std::pow(2, idx); + bool on = (*flag_field & value) == value; + + if (ImGui::Checkbox(names_array[idx], &on)) + { + *flag_field ^= value; + } + } +} + void render_illumination(Illumination* light, const char* sect = "") { ImGui::PushID(sect); @@ -3498,10 +3515,7 @@ void render_illumination(Illumination* light, const char* sect = "") ImGui::InputFloat("Offset X##LightPosX", &light->offset_x); ImGui::InputFloat("Offset Y##LightPosY", &light->offset_y); ImGui::DragFloat("Distortion##LightDistortion", &light->distortion, 0.01f); - for (int i = 0; i < 11; i++) - { - ImGui::CheckboxFlags(illumination_flags[i], &light->flags, (int)std::pow(2, i)); - } + render_flags(illumination_flags, &light->flags); ImGui::PopID(); } @@ -5792,10 +5806,7 @@ void render_options() if (submenu("Frame advance / Engine pause type")) { ImGui::PushID("PauseType"); - for (int i = 1; i < 7; i++) - { - ImGui::CheckboxFlags(pause_types[i], &g_pause_type, (int)std::pow(2, i)); - } + render_flags(pause_types, &g_pause_type); ImGui::PopID(); endmenu(); } @@ -7119,10 +7130,7 @@ void render_entity_props(int uid, bool detached = false) ImGui::InputScalar("Search flags##SearchFlags", ImGuiDataType_U32, &entity->type->search_flags, 0, 0, "%p", ImGuiInputTextFlags_ReadOnly); if (submenu("Properties flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(entity_type_properties_flags[i], &entity->type->properties_flags, (int)std::pow(2, i)); - } + render_flags(entity_type_properties_flags, &entity->type->properties_flags); endmenu(); } endmenu(); @@ -7316,18 +7324,12 @@ void render_entity_props(int uid, bool detached = false) } if (submenu("Flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(entity_flags[i], &entity->flags, (int)std::pow(2, i)); - } + render_flags(entity_flags, &entity->flags); endmenu(); } if (submenu("More Flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(more_flags[i], &entity->more_flags, (int)std::pow(2, i)); - } + render_flags(more_flags, &entity->more_flags); endmenu(); } if (is_movable && submenu("Input Display")) @@ -8083,67 +8085,37 @@ void render_game_props() } if (submenu("Level flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(level_flags[i], &g_state->level_flags, (int)std::pow(2, i)); - } + render_flags(level_flags, &g_state->level_flags); endmenu(); } if (submenu("Quest flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(quest_flags[i], &g_state->quest_flags, (int)std::pow(2, i)); - } + render_flags(quest_flags, &g_state->quest_flags); endmenu(); } if (submenu("Journal flags")) { - for (int i = 0; i < 21; i++) - { - ImGui::CheckboxFlags(journal_flags[i], &g_state->journal_flags, (int)std::pow(2, i)); - } + render_flags(journal_flags, &g_state->journal_flags); endmenu(); } if (submenu("Presence flags")) { - for (int i = 0; i < 11; i++) - { - ImGui::CheckboxFlags(presence_flags[i], &g_state->presence_flags, (int)std::pow(2, i)); - } + render_flags(presence_flags, &g_state->presence_flags); endmenu(); } if (submenu("Special visibility flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(special_visibility_flags[i], &g_state->special_visibility_flags, (int)std::pow(2, i)); - } + render_flags(special_visibility_flags, &g_state->special_visibility_flags); endmenu(); } if (submenu("Level generation flags")) { - auto flags = (int)g_state->level_gen->flags; - auto flags2 = (int)g_state->level_gen->flags2; - auto flags3 = (int)g_state->level_gen->flags3; ImGui::SeparatorText("Flags 1"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags[i], &flags, (int)std::pow(2, i)); - } + render_flags(levelgen_flags, &g_state->level_gen->flags); ImGui::SeparatorText("Flags 2"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags2[i], &flags2, (int)std::pow(2, i)); - } + render_flags(levelgen_flags2, &g_state->level_gen->flags2); ImGui::SeparatorText("Flags 3"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags3[i], &flags3, (int)std::pow(2, i)); - } - g_state->level_gen->flags = (uint8_t)flags; - g_state->level_gen->flags2 = (uint8_t)flags2; - g_state->level_gen->flags3 = (uint8_t)flags3; + render_flags(levelgen_flags3, &g_state->level_gen->flags3); if (g_state->current_theme) { ImGui::SeparatorText("Theme flags"); From 91c3c2efdafabb6de8317ea1912905986b366d48 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:54:52 +0100 Subject: [PATCH 24/31] improve flags in UI --- src/game_api/flags.hpp | 658 +++++++++++++++++++---------------------- src/injected/ui.cpp | 7 +- 2 files changed, 316 insertions(+), 349 deletions(-) diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index 954e91c2d..d13b09060 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -40,321 +40,321 @@ std::array themes_short{ }; std::array entity_flags{ - "1: Invisible", - "2: Indestructable/special floor", - "3: Solid (wall)", - "4: Passes through objects", - "5: Passes through everything", - "6: Take no damage", - "7: Throwable/Knockbackable", - "8: Is platform", - "9: Climbable (as rope/ladder)", - "10: No gravity", - "11: Interact with liquid", - "12: Stunnable", - "13: Collides walls", - "14: Interact with semisolids?", - "15: Can be stomped", - "16: Power stomps", - "17: Facing left", - "18: Pickupable", - "19: Usable item?", - "20: Enable button prompt", - "21: Interact with webs", - "22: Can be carried through exit", - "23: Shop item", - "24: Shop floor (break to aggro)", - "25: Passes through player", - "26: Exploded", - "27: Crushed", - "28: Pause AI and physics", - "29: Dead", - "30: Character", - "31: Movable", - "32: Use overlay draw depth", + "Invisible", + "Indestructable/special floor", + "Solid (wall)", + "Passes through objects", + "Passes through everything", + "Take no damage", + "Throwable/Knockbackable", + "Is platform", + "Climbable (as rope/ladder)", + "No gravity", + "Interact with liquid", + "Stunnable", + "Collides walls", + "Interact with semisolids?", + "Can be stomped", + "Power stomps", + "Facing left", + "Pickupable", + "Usable item?", + "Enable button prompt", + "Interact with webs", + "Can be carried through exit", + "Shop item", + "Shop floor (break to aggro)", + "Passes through player", + "Exploded", + "Crushed", + "Pause AI and physics", + "Dead", + "Character", + "Movable", + "Use overlay draw depth", }; std::array more_flags{ - "1: Take damage on collision after throw", - "2: Revived (HH)", - "3: Blocks shield", - "4: Passes through shield", - "5: Being moved by shield", - "6: Just spawned?", - "7: Done spawning?", - "8: Stuck in something", - "9: Stuck in something, 1 frame later", - "10: Pitchers mitt antigravity", - "11: Swimming", - "12: Hit ground", - "13: Hit wall", - "14: Falling", - "15: Cursed effect", - "16: Elixir buff", - "17: Disable input", - "18: Shop item held by player", - "19: Falling platform something", - "20: Yangs turkey", - "21: Floor can be decorated", - "22: Disable collision", - "23: JumpWhipped to shorthop", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "Take damage on collision after throw", + "Revived (HH)", + "Blocks shield", + "Passes through shield", + "Being moved by shield", + "Just spawned?", + "Done spawning?", + "Stuck in something", + "Stuck in something, 1 frame later", + "Pitchers mitt antigravity", + "Swimming", + "Hit ground", + "Hit wall", + "Falling", + "Cursed effect", + "Elixir buff", + "Disable input", + "Shop item held by player", + "Falling platform something", + "Yangs turkey", + "Floor can be decorated", + "Disable collision", + "JumpWhipped to shorthop", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; std::array entity_type_properties_flags{ - "1: Apply solid block beautification", - "2: Treat as FLOORSTYLED", - "3: ", - "4: ", - "5: Squirts blood on damage", - "6: Climbable (as wall)", - "7: ", - "8: Fireproof", - "9: Double fire and explosive damage", - "10: Is on fire/lights on fire", - "11: Poison immunity", - "12: Poisons arrows when hit", - "13: Curse immunity", - "14: Cursed pot effect (curses you when destroyed closeby)", - "15: ", - "16: ", - "17: ", - "18: Can be carried through layer doors", - "19: ", - "20: ", - "21: Can spawn monsters on top", - "22: ", - "23: Can be cloned", - "24: Can be bubbled", - "25: Can be telefragged", - "26: Disable updates?", - "27: Kill entity when overlay lost?", - "28: Unused", - "29: Unused", - "30: Unused", - "31: Unused", - "32: Unused", + "Apply solid block beautification", + "Treat as FLOORSTYLED", + "Unknown", + "Unknown", + "Squirts blood on damage", + "Climbable (as wall)", + "Unknown", + "Fireproof", + "Double fire and explosive damage", + "Is on fire/lights on fire", + "Poison immunity", + "Poisons arrows when hit", + "Curse immunity", + "Cursed pot effect (curses you when destroyed closeby)", + "Unknown", + "Unknown", + "Unknown", + "Can be carried through layer doors", + "Unknown", + "Unknown", + "Can spawn monsters on top", + "Unknown", + "Can be cloned", + "Can be bubbled", + "Can be telefragged", + "Disable updates?", + "Kill entity when overlay lost?", + "", + "", + "", + "", + "", }; std::array level_flags{ - "1: Upbeat dwelling intro music played", - "2: ", - "3: Running tutorial speedrun", - "4: Level has pet", - "5: Level has Tun/shop?", - "6: Tun killed?", - "7: Ghost pot level?", - "8: Dead are restless", - "9: Bees/Metal clanking/Oppression", - "10: Angry shopkeeper", - "11: Angry Tun", - "12: Angry caveman shopkeeper", - "13: Angry ghist shopkeeper", - "14: Angry Yang", - "15: Angry Tusk", - "16: Angry Waddler", - "17: Shop level?", - "18: Dark level (draw halo)", - "19: Altar destroyed", - "20: Allow pause", - "21: Hide hud, transition", - "22: Hide hud, camp", - "23: Have clover", - "24: Show forgiveness toast", - "25: ", - "26: Daily challenge options", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "Upbeat dwelling intro music played", + "Unknown", + "Running tutorial speedrun", + "Level has pet", + "Level has Tun/shop?", + "Tun killed?", + "Ghost pot level?", + "Dead are restless", + "Bees/Metal clanking/Oppression", + "Angry shopkeeper", + "Angry Tun", + "Angry caveman shopkeeper", + "Angry ghist shopkeeper", + "Angry Yang", + "Angry Tusk", + "Angry Waddler", + "Shop level?", + "Dark level (draw halo)", + "Altar destroyed", + "Allow pause", + "Hide hud, transition", + "Hide hud, camp", + "Have clover", + "Show forgiveness toast", + "Unknown", + "Daily challenge options", + "", + "", + "", + "", + "", + "", }; std::array journal_flags{ - "1: I was a pacifist", - "2: I was a vegan", - "3: I was a vegetarian", - "4: I was a petty criminal", - "5: I was a wanted criminal", - "6: I was a crime lord", - "7: I was a king", - "8: I was a queen", - "9: I was a fool", - "10: I was an eggplant", - "11: I didn't care for treasure", - "12: I liked pets", - "13: I loved pets", - "14: I took damage", - "15: I survived death once", - "16: I slayed Kingu", - "17: I slayed Osiris", - "18: I defeated Tiamat", - "19: I defeated Hundun", - "20: I became one with the Cosmos", - "21: I eventually died", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "I was a pacifist", + "I was a vegan", + "I was a vegetarian", + "I was a petty criminal", + "I was a wanted criminal", + "I was a crime lord", + "I was a king", + "I was a queen", + "I was a fool", + "I was an eggplant", + "I didn't care for treasure", + "I liked pets", + "I loved pets", + "I took damage", + "I survived death once", + "I slayed Kingu", + "I slayed Osiris", + "I defeated Tiamat", + "I defeated Hundun", + "I became one with the Cosmos", + "I eventually died", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; std::array quest_flags{ - "1: Reset", - "2: Dark level spawned in world", - "3: Vault spawned in world", - "4: Next level has shopkeeper outpost (if possible)", - "5: Shop spawned", - "6: Shortcut used", - "7: Seeded mode", - "8: Daily challenge mode", - "9: Caveman Shopkeeper aggroed", - "10: Waddler aggroed", - "11: Shop bought out", - "12: Eggplant crown picked up", - "13: ", - "14: ", - "15: ", - "16: ", - "17: Udjat eye spawned", - "18: Black market spawned", - "19: Drill spawned", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", - "25: Moon challenge spawned", - "26: Star challenge spawned", - "27: Sun challenge spawned", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "Reset", + "Dark level spawned in world", + "Vault spawned in world", + "Next level has shopkeeper outpost (if possible)", + "Shop spawned", + "Shortcut used", + "Seeded mode", + "Daily challenge mode", + "Caveman Shopkeeper aggroed", + "Waddler aggroed", + "Shop bought out", + "Eggplant crown picked up", + "", + "", + "", + "", + "Udjat eye spawned", + "Black market spawned", + "Drill spawned", + "", + "", + "", + "", + "", + "Moon challenge spawned", + "Star challenge spawned", + "Sun challenge spawned", + "", + "", + "", + "", + "", }; std::array presence_flags{ - "1: Udjat eye", - "2: Black market", - "3: Vlad's castle/drill", - "4: ", - "5: ", - "6: ", - "7: ", - "8: ", - "9: Moon challenge", - "10: Star challenge", - "11: Sun challenge", - "12: ", - "13: ", - "14: ", - "15: ", - "16: ", - "17: ", - "18: ", - "19: ", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "Udjat eye", + "Black market", + "Vlad's castle/drill", + "", + "", + "", + "", + "", + "Moon challenge", + "Star challenge", + "Sun challenge", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; std::array illumination_flags{ - "1: Disable light1", - "2: Enable light2", - "3: Enable light3", - "4: Enable light4", - "5: Unknown", - "6: Unknown", // always on by default - "7: Modulate brightness_multiplier", - "8: Unknown", + "Disable light1", + "Enable light2", + "Enable light3", + "Enable light4", + "Unknown", + "Unknown", // always on by default + "Modulate brightness_multiplier", + "Unknown", }; std::array special_visibility_flags{ - "1: Crust embedded items shown", - "2: Crust embedded items shown (level transition)", - "3: ", - "4: ", - "5: ", - "6: ", - "7: ", - "8: ", - "9: Compass door markers shown", - "10: Compass door markers shown (level transition)", - "11: ", - "12: ", - "13: ", - "14: ", - "15: ", - "16: ", - "17: Special compass door markers shown", - "18: Special compass door markers shown (level transition)", - "19: ", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", + "Crust embedded items shown", + "Crust embedded items shown (level transition)", + "", + "", + "", + "", + "", + "", + "Compass door markers shown", + "Compass door markers shown (level transition)", + "", + "", + "", + "", + "", + "", + "Special compass door markers shown", + "Special compass door markers shown (level transition)", + "", + "", + "", + "", + "", + "", }; std::array basecamp_dialogue_win_flags{ - "1: Ana Spelunky saved", - "2: Margaret Tunnel saved", - "3: Colin Northward saved", - "4: Roffy D. Sloth saved", - "5: Alto Singh saved", - "6: Liz Mutton saved", - "7: Nekka The Eagle saved", - "8: LISE project saved", - "9: Coco Von Diamonds saved", - "10: Manfred Tunnel saved", - "11: Little Jay saved", - "12: Tina Flan saved", - "13: Valerie Crump saved", - "14: Au saved", - "15: Demi Von Diamonds saved", - "16: Pilot saved", - "17: Princess Airyn saved", - "18: Dirk Yamaoka saved", - "19: Tiamat win", - "20: Hundun win", - "21: ", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", + "Ana Spelunky saved", + "Margaret Tunnel saved", + "Colin Northward saved", + "Roffy D. Sloth saved", + "Alto Singh saved", + "Liz Mutton saved", + "Nekka The Eagle saved", + "LISE project saved", + "Coco Von Diamonds saved", + "Manfred Tunnel saved", + "Little Jay saved", + "Tina Flan saved", + "Valerie Crump saved", + "Au saved", + "Demi Von Diamonds saved", + "Pilot saved", + "Princess Airyn saved", + "Dirk Yamaoka saved", + "Tiamat win", + "Hundun win", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; std::array places_flags{ @@ -596,40 +596,6 @@ std::array shortcut_flags{ "5-1: Golden Key (Unlocked)", }; -/*std::array empty_flags{ - "1: ", -"2: ", -"3: ", -"4: ", -"5: ", -"6: ", -"7: ", -"8: ", -"9: ", -"10: ", -"11: ", -"12: ", -"13: ", -"14: ", -"15: ", -"16: ", - "17: ", -"18: ", -"19: ", -"20: ", -"21: ", -"22: ", -"23: ", -"24: ", -"25: ", -"26: ", -"27: ", -"28: ", -"29: ", -"30: ", -"31: ", -"32: ",};*/ - std::array button_flags{ "Jp", "Wp", @@ -741,36 +707,36 @@ std::array pause_types{ }; std::array levelgen_flags{ - "1: Should generate path", - "2: Can spawn vault", - "3: Can spawn shops", - "4: Can have outpost?", - "5: Should spawn hard floor decorations", - "6: Apply ambient occlusion", - "7: Should spawn behind-floor and below-floorstyled decorations", - "8: unknown", + "Should generate path", + "Can spawn vault", + "Can spawn shops", + "Can have outpost?", + "Should spawn hard floor decorations", + "Apply ambient occlusion", + "Should spawn behind-floor and below-floorstyled decorations", + "Unknown", }; std::array levelgen_flags2{ - "1: Spawns background decorations on ground (ceiling if false)", - "2: Spawns fake ladder/chain midbg?", - "3: Spawn entrance door background (Ignored in 7-1 to 7-2 transition)", - "4: Procedural backlayer door midbg indicator related", - "5: Spawn backlayer border/background", - "6: Should spawn procedural backlayers", - "7: Should spawn backlayer torches", - "8: Has ghost", + "Spawns background decorations on ground (ceiling if false)", + "Spawns fake ladder/chain midbg?", + "Spawn entrance door background (Ignored in 7-1 to 7-2 transition)", + "Procedural backlayer door midbg indicator related", + "Spawn backlayer border/background", + "Should spawn procedural backlayers", + "Should spawn backlayer torches", + "Has ghost", }; std::array levelgen_flags3{ - "1: Can spawn angry NPCs", - "2: Can echo", - "3: Can spawn Dead are Restless", - "4: Can spawn procedural skeletons", - "5: Can have quests?", - "6: Can spawn player coffins", - "7: unknown", - "8: unknown", + "Can spawn angry NPCs", + "Can echo", + "Can spawn Dead are Restless", + "Can spawn procedural skeletons", + "Can have quests?", + "Can spawn player coffins", + "Unknown", + "Unknown", }; std::array level_chances{ diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index eae247ca0..9f6151730 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -3481,7 +3481,7 @@ void render_light(const char* name, LightParams* light) } template -void render_flags(const std::array names_array, T* flag_field) +void render_flags(const std::array names_array, T* flag_field, bool show_number = true) { for (int idx{0}; idx < SIZE && idx < sizeof(T) * 8; ++idx) { @@ -3490,7 +3490,8 @@ void render_flags(const std::array names_array, T* flag_field T value = (T)std::pow(2, idx); bool on = (*flag_field & value) == value; - if (ImGui::Checkbox(names_array[idx], &on)) + if (names_array[idx][0] != '\0' && + ImGui::Checkbox(show_number ? fmt::format("{}: {}", idx + 1, names_array[idx]).c_str() : names_array[idx], &on)) { *flag_field ^= value; } @@ -5806,7 +5807,7 @@ void render_options() if (submenu("Frame advance / Engine pause type")) { ImGui::PushID("PauseType"); - render_flags(pause_types, &g_pause_type); + render_flags(pause_types, &g_pause_type, false); ImGui::PopID(); endmenu(); } From be56a7d5dc67183683929b016dd38b128fb52808 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:02:06 +0100 Subject: [PATCH 25/31] small random stuff --- docs/game_data/spel2.lua | 16 +++++++++------- docs/src/includes/_globals.md | 5 ++--- docs/src/includes/_types.md | 16 +++++++++------- src/game_api/color.hpp | 6 ++++++ src/game_api/entities_chars.hpp | 2 +- src/game_api/entity_lookup.cpp | 2 +- src/game_api/game_api.hpp | 6 +++--- src/game_api/illumination.hpp | 6 ++++++ src/game_api/script/usertypes/entity_lua.cpp | 1 + src/game_api/script/usertypes/state_lua.cpp | 4 +++- src/game_api/state.hpp | 18 ++++++++++-------- 11 files changed, 51 insertions(+), 31 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index b9ad798de..fae909481 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1992,12 +1992,12 @@ do ---@field pools LiquidPool[] @size: 5 ---@class StateMemory - ---@field screen_last integer @Previous SCREEN, used to check where we're coming from when loading another SCREEN - ---@field screen integer @Current SCREEN, generally read-only or weird things will happen - ---@field screen_next integer @Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. + ---@field screen_last SCREEN @Previous SCREEN, used to check where we're coming from when loading another SCREEN + ---@field screen SCREEN @Current SCREEN, generally read-only or weird things will happen + ---@field screen_next SCREEN @Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. ---@field ingame integer @Is 1 when you in a game, is set to 0 or 1 in main menu, can't be trusted there, normally in a level is 1 unless you go to the options ---@field playing integer @Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it - ---@field pause PAUSE @8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controller by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. + ---@field pause PAUSE @8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. ---@field width integer @level width in rooms (number of rooms horizontally) ---@field height integer @level height in rooms (number of rooms vertically) ---@field kali_favor integer @@ -2014,7 +2014,7 @@ do ---@field level_start integer @Level number to start new runs in ---@field theme THEME @Current THEME number, used to pick the music and by some game logic like choosing the next level on transition ---@field theme_next THEME @Next THEME number, used when loading a new level or transition - ---@field theme_start integer @THEME to start new runs in + ---@field theme_start THEME @THEME to start new runs in ---@field current_theme ThemeInfo @Points to the current ThemeInfo ---@field force_current_theme fun(self, t: integer): nil @This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! ---@field shoppie_aggro integer @Current shoppie aggro @@ -2041,7 +2041,7 @@ do ---@field win_state integer @0 = no win 1 = tiamat win 2 = hundun win 3 = CO win; set this and next doorway leads to victory scene ---@field illumination Illumination @The global level illumination, very big and bright. ---@field money_last_levels integer - ---@field money_shop_total integer @Total negative amount spent in shops during the run

The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` + ---@field money_shop_total integer @Total amount spent in shops and sold idols during the run

The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` ---@field player_inputs PlayerInputs @Access the player inputs even when no player entities are available ---@field quests QuestsInfo @NPC quest states ---@field camera Camera @Camera bounds and position @@ -2096,7 +2096,7 @@ do ---@field journal_progress_stain_count integer ---@field journal_progress_stain_slots JournalProgressStainSlot[] @size: 30 @blood splats and paw prints in journal progress page ---@field journal_progress_theme_count integer - ---@field journal_progress_theme_slots integer[] @size: 9 @visited themes in journal progress page + ---@field journal_progress_theme_slots THEME[] @size: 9 @visited themes in journal progress page ---@field theme_info ThemeInfo @Points to the current ThemeInfo ---@field logic LogicList @Level logic like dice game and cutscenes ---@field liquid LiquidPhysics @@ -2108,6 +2108,7 @@ do ---@field green number ---@field blue number ---@field size number + ---@field as_color fun(self): Color @Returns LightParams as Color, note that size = alpha ---@class Illumination ---@field lights LightParams[] @size: 4 @Table of light1, light2, ... etc. @@ -2266,6 +2267,7 @@ function PRNG:random(min, max) end ---@field set_rgba fun(self, red: integer, green: integer, blue: integer, alpha: integer): Color @Changes color based on given RGBA colors in 0..255 range ---@field get_ucolor fun(self): uColor @Returns the `uColor` used in `GuiDrawContext` drawing functions ---@field set_ucolor fun(self, color: uColor): Color @Changes color based on given uColor + ---@field set fun(self, other: Color): Color @Copies the values of different Color to this one ---@class Animation ---@field id integer diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 2dec314e2..85141bcde 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1735,8 +1735,7 @@ function change_layer(layer_to, load_time) state.layer_transition_timer = load_time state.transition_to_layer = layer_to - -- actual layer change after time delay - set_timeout(function() state.camera_layer = layer_to end, load_time) + state.camera_layer = layer_to end ``` @@ -3676,7 +3675,7 @@ Use `get_entities_overlapping_hitbox` instead #### int get_entity_ai_state(int uid) -As the name is misleading. use entity `move_state` field instead +As the name is misleading. use [Movable](#Movable).`move_state` field instead ### set_arrowtrap_projectile diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index 59dd3306c..f33db0fe8 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -640,6 +640,7 @@ tuple<int, int, int, int> | [get_rgba()](https://github.com/spelunky-fyi/o [Color](#Color) | [set_rgba(int red, int green, int blue, int alpha)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_rgba) | Changes color based on given RGBA colors in 0..255 range [uColor](#Aliases) | [get_ucolor()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_ucolor) | Returns the `uColor` used in `GuiDrawContext` drawing functions [Color](#Color) | [set_ucolor(uColor color)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_ucolor) | Changes color based on given [uColor](#Aliases) +[Color](#Color) | [set(Color other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | Copies the values of different [Color](#Color) to this one ### CutsceneBehavior @@ -1303,6 +1304,7 @@ float | [red](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=red) | float | [green](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=green) | float | [blue](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=blue) | float | [size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=size) | +[Color](#Color) | [as_color()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=as_color) | Returns [LightParams](#LightParams) as [Color](#Color), note that size = alpha ## Liquid types @@ -2819,12 +2821,12 @@ Can be accessed via global [state](#state) Type | Name | Description ---- | ---- | ----------- -int | [screen_last](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_last) | Previous [SCREEN](#SCREEN), used to check where we're coming from when loading another [SCREEN](#SCREEN) -int | [screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen) | Current [SCREEN](#SCREEN), generally read-only or weird things will happen -int | [screen_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_next) | Next [SCREEN](#SCREEN), used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. +[SCREEN](#SCREEN) | [screen_last](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_last) | Previous [SCREEN](#SCREEN), used to check where we're coming from when loading another [SCREEN](#SCREEN) +[SCREEN](#SCREEN) | [screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen) | Current [SCREEN](#SCREEN), generally read-only or weird things will happen +[SCREEN](#SCREEN) | [screen_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_next) | Next [SCREEN](#SCREEN), used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. int | [ingame](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ingame) | Is 1 when you in a game, is set to 0 or 1 in main menu, can't be trusted there, normally in a level is 1 unless you go to the options int | [playing](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=playing) | Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it -[PAUSE](#PAUSE) | [pause](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause) | 8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controller by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. +[PAUSE](#PAUSE) | [pause](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause) | 8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. int | [width](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=width) | level width in rooms (number of rooms horizontally) int | [height](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=height) | level height in rooms (number of rooms vertically) int | [kali_favor](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=kali_favor) | @@ -2841,7 +2843,7 @@ int | [level_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=leve int | [level_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=level_start) | Level number to start new runs in [THEME](#THEME) | [theme](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme) | Current [THEME](#THEME) number, used to pick the music and by some game logic like choosing the next level on transition [THEME](#THEME) | [theme_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_next) | Next [THEME](#THEME) number, used when loading a new level or transition -int | [theme_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_start) | [THEME](#THEME) to start new runs in +[THEME](#THEME) | [theme_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_start) | [THEME](#THEME) to start new runs in [ThemeInfo](#ThemeInfo) | [current_theme](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=current_theme) | Points to the current [ThemeInfo](#ThemeInfo) nil | [force_current_theme(int t)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=force_current_theme) | This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! int | [shoppie_aggro](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=shoppie_aggro) | Current shoppie aggro @@ -2868,7 +2870,7 @@ int | [saved_hamsters](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q= int | [win_state](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=win_state) | 0 = no win 1 = tiamat win 2 = hundun win 3 = CO win; set this and next doorway leads to victory scene [Illumination](#Illumination) | [illumination](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=illumination) | The global level illumination, very big and bright. int | [money_last_levels](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_last_levels) | -int | [money_shop_total](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_shop_total) | Total negative amount spent in shops during the run

The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` +int | [money_shop_total](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_shop_total) | Total amount spent in shops and sold idols during the run

The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` [PlayerInputs](#PlayerInputs) | [player_inputs](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_inputs) | Access the player inputs even when no player entities are available [QuestsInfo](#QuestsInfo) | [quests](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=quests) | [NPC](#NPC) quest states [Camera](#Camera) | [camera](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera) | [Camera](#Camera) bounds and position @@ -2923,7 +2925,7 @@ array<[JournalProgressStickerSlot](#JournalProgressStickerSlot), 40> | [jo int | [journal_progress_stain_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_stain_count) | array<[JournalProgressStainSlot](#JournalProgressStainSlot), 30> | [journal_progress_stain_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_stain_slots) | blood splats and paw prints in journal progress page int | [journal_progress_theme_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_count) | -array<int, 9> | [journal_progress_theme_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_slots) | visited themes in journal progress page +array<[THEME](#THEME), 9> | [journal_progress_theme_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_slots) | visited themes in journal progress page [ThemeInfo](#ThemeInfo) | [theme_info](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_info) | Points to the current [ThemeInfo](#ThemeInfo) [LogicList](#LogicList) | [logic](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=logic) | Level logic like dice game and cutscenes [LiquidPhysics](#LiquidPhysics) | [liquid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=liquid) | diff --git a/src/game_api/color.hpp b/src/game_api/color.hpp index 5cb196d83..20df107c8 100644 --- a/src/game_api/color.hpp +++ b/src/game_api/color.hpp @@ -153,6 +153,12 @@ struct Color uint8_t alpha = (color >> 24U) & 0xFF; return set_rgba(red, green, blue, alpha); } + /// Copies the values of different Color to this one + Color& set(Color other) + { + *this = other; + return *this; + } float r{0.0f}; float g{0.0f}; diff --git a/src/game_api/entities_chars.hpp b/src/game_api/entities_chars.hpp index d84c752e6..863161149 100644 --- a/src/game_api/entities_chars.hpp +++ b/src/game_api/entities_chars.hpp @@ -8,7 +8,6 @@ #include // for vector #include "aliases.hpp" // for ENT_TYPE -#include "color.hpp" // for Color #include "containers/custom_map.hpp" // #include "movable.hpp" // for Movable @@ -16,6 +15,7 @@ struct Illumination; struct PlayerInputs; struct Inventory; struct PlayerSlot; +struct Color; class Entity; class Ai diff --git a/src/game_api/entity_lookup.cpp b/src/game_api/entity_lookup.cpp index e27088238..3e9b609fb 100644 --- a/src/game_api/entity_lookup.cpp +++ b/src/game_api/entity_lookup.cpp @@ -178,7 +178,7 @@ std::vector get_entities_at(std::vector entity_types, uint32 for (auto& item : entities.entities()) { auto [ix, iy] = item->position(); - float distance = std::sqrt(std::pow(x - ix, 2.0f) + std::pow(y - iy, 2.0f)); + float distance = (float)std::sqrt(std::pow(x - ix, 2) + std::pow(y - iy, 2)); if (distance < radius && entity_type_check(proper_types, item->type->id)) { found.push_back(item->uid); diff --git a/src/game_api/game_api.hpp b/src/game_api/game_api.hpp index 221c479ef..552b6bfdc 100644 --- a/src/game_api/game_api.hpp +++ b/src/game_api/game_api.hpp @@ -6,13 +6,13 @@ struct Renderer { - uint32_t render_width; // sam as window size unless resolution scale is set + uint32_t render_width; // same as window size unless resolution scale is set uint32_t render_height; uint32_t fps; // changing it doesn't seam to do anything uint32_t fps_denominator; - uint32_t render_width2; // repeat + uint32_t render_width2; // repeat? uint32_t render_height2; uint8_t flags1; @@ -36,7 +36,7 @@ struct Renderer size_t swap_chain; - // somewhere there should be shareds stored + // somewhere there should be shaders stored // added just to have the vtable virtual ~Renderer() = 0; diff --git a/src/game_api/illumination.hpp b/src/game_api/illumination.hpp index c03b0b62f..7376ce473 100644 --- a/src/game_api/illumination.hpp +++ b/src/game_api/illumination.hpp @@ -22,6 +22,12 @@ struct LightParams // it's probably just Color float green; float blue; float size; + + /// Returns LightParams as Color, note that size = alpha + Color* as_color() + { + return reinterpret_cast(this); + }; }; struct Illumination diff --git a/src/game_api/script/usertypes/entity_lua.cpp b/src/game_api/script/usertypes/entity_lua.cpp index a8aaec46e..5c492c951 100644 --- a/src/game_api/script/usertypes/entity_lua.cpp +++ b/src/game_api/script/usertypes/entity_lua.cpp @@ -53,6 +53,7 @@ void register_usertypes(sol::state& lua) color_type["set_rgba"] = &Color::set_rgba; color_type["get_ucolor"] = &Color::get_ucolor; color_type["set_ucolor"] = &Color::set_ucolor; + color_type["set"] = &Color::set; /// Used in EntityDB lua.new_usertype( diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index b95ca54ad..d0872d672 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -412,7 +412,9 @@ void register_usertypes(sol::state& lua) "blue", &LightParams::blue, "size", - &LightParams::size); + &LightParams::size, + "as_color", + &LightParams::as_color); /// Generic obcject for lights in the game, you can make your own with [create_illumination](#create_illumination)
/// Used in StateMemory, Player, PlayerGhost, BurningRopeEffect ... diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index 7cf1b152c..2f694a1de 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -51,13 +51,15 @@ void fix_liquid_out_of_bounds(); #pragma pack(push, 1) // disable struct padding struct StateMemory { + using SCREEN = uint32_t; + size_t p00; /// Previous SCREEN, used to check where we're coming from when loading another SCREEN - uint32_t screen_last; + SCREEN screen_last; /// Current SCREEN, generally read-only or weird things will happen - uint32_t screen; + SCREEN screen; /// Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. - uint32_t screen_next; + SCREEN screen_next; /// Shows the current loading state (0=Not loading, 1=Fadeout, 2=Loading, 3=Fadein). Writing 1 or 2 will trigger a screen load to `screen_next`. uint32_t loading; /// The global level illumination, very big and bright. @@ -75,7 +77,7 @@ struct StateMemory /// Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it uint8_t playing; /// 8bit flags, multiple might be active at the same time - /// 1: Menu: Pauses the level timer and engine. Can't set, controller by the menu. + /// 1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu. /// 2: Fade/Loading: Pauses all timers and engine. /// 4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes. /// 8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs. @@ -106,15 +108,15 @@ struct StateMemory /// 0 - none, 1 - item, 3 - kapala int8_t kali_gifts; int32_t outposts_spawned; - /// Total negative amount spent in shops during the run
- /// The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` + /// Total amount spent in shops and sold idols during the run
+ /// The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` int32_t money_shop_total; /// World number to start new runs in uint8_t world_start; /// Level number to start new runs in uint8_t level_start; /// THEME to start new runs in - uint8_t theme_start; + THEME theme_start; uint8_t b5f; /// Current seed in seeded mode, just set to a funny value and does nothing in adventure mode uint32_t seed; @@ -172,7 +174,7 @@ struct StateMemory std::array journal_progress_stain_slots; uint8_t journal_progress_theme_count; /// visited themes in journal progress page - std::array journal_progress_theme_slots; + std::array journal_progress_theme_slots; uint8_t unknown3; uint8_t unknown4; uint8_t unknown5a; From 4c5c4f691d683a24c89b3d480efa977a2b533440 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:31:36 +0100 Subject: [PATCH 26/31] add more `:set()` functions, unless there is some lua alternative? --- src/game_api/color.hpp | 2 +- src/game_api/math.hpp | 21 +++++++++++++++++++- src/game_api/script/usertypes/hitbox_lua.cpp | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/game_api/color.hpp b/src/game_api/color.hpp index 20df107c8..fe9288e97 100644 --- a/src/game_api/color.hpp +++ b/src/game_api/color.hpp @@ -154,7 +154,7 @@ struct Color return set_rgba(red, green, blue, alpha); } /// Copies the values of different Color to this one - Color& set(Color other) + Color& set(Color& other) { *this = other; return *this; diff --git a/src/game_api/math.hpp b/src/game_api/math.hpp index 20470e419..48d97677c 100644 --- a/src/game_api/math.hpp +++ b/src/game_api/math.hpp @@ -41,6 +41,11 @@ struct Vec2 diff *= diff; // pow return (float)std::sqrt(diff.x + diff.y); } + Vec2& set(Vec2& other) + { + *this = other; + return *this; + } Vec2 operator+(const Vec2& a) const { @@ -271,6 +276,11 @@ struct AABB return false; } + AABB& set(AABB& other) + { + *this = other; + return *this; + } /* std::tuple split() {} // just for the autodoc @@ -382,7 +392,11 @@ struct Triangle { return is_point_inside(Vec2{x, y}, epsilon); } - + Triangle& set(Triangle& other) + { + *this = other; + return *this; + } /* // just for the autodoc /// Returns the corner points std::tuple split(); @@ -521,6 +535,11 @@ struct Quad { return is_point_inside(Vec2{x, y}, epsilon); } + Quad& set(Quad& other) + { + *this = other; + return *this; + } /* // just for the autodoc /// Returns the corners in order: bottom_left, bottom_right, top_right, top_left diff --git a/src/game_api/script/usertypes/hitbox_lua.cpp b/src/game_api/script/usertypes/hitbox_lua.cpp index 05201a5b0..87f030c88 100644 --- a/src/game_api/script/usertypes/hitbox_lua.cpp +++ b/src/game_api/script/usertypes/hitbox_lua.cpp @@ -67,6 +67,8 @@ void register_usertypes(sol::state& lua) &Vec2::rotate, "distance_to", &Vec2::distance_to, + "set", + &Vec2::set, "split", // &Vec2::split); // for the autodoc &Vec2::operator std::pair); @@ -108,6 +110,8 @@ void register_usertypes(sol::state& lua) &AABB::height, "is_point_inside", is_point_inside, + "set", + &AABB::set, "split", // &Vec2::split); // for the autodoc &AABB::operator std::tuple); @@ -144,6 +148,8 @@ void register_usertypes(sol::state& lua) &Triangle::area, "is_point_inside", is_point_inside_triangle, + "set", + &Triangle::set, "split", // &Triangle::split); // for the autodoc &Triangle::operator std::tuple); @@ -185,6 +191,8 @@ void register_usertypes(sol::state& lua) &Quad::flip_vertically, "is_point_inside", is_point_inside_quad, + "set", + &Quad::set, "split", // &Quad::split); // for the autodoc &Quad::operator std::tuple); From 4881181d0cd905f3a78dc8758ebda694e5bac92c Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:31:50 +0100 Subject: [PATCH 27/31] wtf --- src/game_api/screen.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/screen.hpp b/src/game_api/screen.hpp index c86c3477a..bcd32ede1 100644 --- a/src/game_api/screen.hpp +++ b/src/game_api/screen.hpp @@ -1074,7 +1074,7 @@ struct JournalUI uint8_t unknown1; uint16_t unknown2; /// Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page. - /// Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) + /// Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) custom_vector pages; custom_vector pages_tmp; // pages are constructed in the show_journal function and put here, later transfered to the pages vector uint32_t current_page; From 5b317b89488b54c84f600a6e5d0ee95ef6d4645a Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 20:22:49 +0100 Subject: [PATCH 28/31] some non important changes --- src/game_api/entities_items.hpp | 2 +- src/game_api/math.cpp | 8 ++++---- src/game_api/math.hpp | 20 ++++++++++---------- src/game_api/script/usertypes/hitbox_lua.cpp | 14 +++++++------- src/game_api/search.cpp | 1 + 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/game_api/entities_items.hpp b/src/game_api/entities_items.hpp index a95b00a3c..b4e24d141 100644 --- a/src/game_api/entities_items.hpp +++ b/src/game_api/entities_items.hpp @@ -152,7 +152,7 @@ class Flame : public Movable class FlameSize : public Flame { public: - /// if changed, gradually goes down (0.03 per frame) to the default size + /// if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` float flame_size; }; diff --git a/src/game_api/math.cpp b/src/game_api/math.cpp index bae4186ea..e09db53b9 100644 --- a/src/game_api/math.cpp +++ b/src/game_api/math.cpp @@ -1,6 +1,6 @@ #include "math.hpp" -bool Triangle::is_point_inside(Vec2 p, float epsilon) const +bool Triangle::is_point_inside(const Vec2 p, float epsilon) const { // you can compare it eather by area or by angle // not sure if one if faster thne the order, so i left code for both @@ -35,19 +35,19 @@ Vec2 intersection(const Vec2 A, const Vec2 B, const Vec2 C, const Vec2 D) return Vec2{(b1 * c - b * c1) / det, (a * c1 - a1 * c) / det}; } -float two_lines_angle(Vec2 A, Vec2 common, Vec2 B) +float two_lines_angle(const Vec2 A, const Vec2 common, const Vec2 B) { Vec2 ab = common - B; Vec2 bc = A - common; return std::atan2((bc.y * ab.x - bc.x * ab.y), (bc.x * ab.x + bc.y * ab.y)); }; -float two_lines_angle(Vec2 line1_A, Vec2 line1_B, Vec2 line2_A, Vec2 line2_B) +float two_lines_angle(const Vec2 line1_A, const Vec2 line1_B, const Vec2 line2_A, const Vec2 line2_B) { return two_lines_angle(line1_A, intersection(line1_A, line1_B, line2_A, line2_B), line2_B); }; -bool Quad::is_point_inside(Vec2 p, float epsilon) const +bool Quad::is_point_inside(const Vec2 p, float epsilon) const { std::tuple points = *this; diff --git a/src/game_api/math.hpp b/src/game_api/math.hpp index 48d97677c..7cf3593ec 100644 --- a/src/game_api/math.hpp +++ b/src/game_api/math.hpp @@ -41,7 +41,7 @@ struct Vec2 diff *= diff; // pow return (float)std::sqrt(diff.x + diff.y); } - Vec2& set(Vec2& other) + Vec2& set(const Vec2& other) { *this = other; return *this; @@ -276,7 +276,7 @@ struct AABB return false; } - AABB& set(AABB& other) + AABB& set(const AABB& other) { *this = other; return *this; @@ -379,11 +379,11 @@ struct Triangle { return std::abs((A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)) / 2.0f); } - bool is_point_inside(Vec2 p) const + bool is_point_inside(const Vec2 p) const { return is_point_inside(p, 0.0001f); } - bool is_point_inside(Vec2 p, float epsilon) const; + bool is_point_inside(const Vec2 p, float epsilon) const; bool is_point_inside(float x, float y) const { return is_point_inside(Vec2{x, y}, 0.0001f); @@ -392,7 +392,7 @@ struct Triangle { return is_point_inside(Vec2{x, y}, epsilon); } - Triangle& set(Triangle& other) + Triangle& set(const Triangle& other) { *this = other; return *this; @@ -522,11 +522,11 @@ struct Quad return *this; } - bool is_point_inside(Vec2 p) const + bool is_point_inside(const Vec2 p) const { return is_point_inside(p, 0.00001f); } - bool is_point_inside(Vec2 p, float epsilon) const; + bool is_point_inside(const Vec2 p, float epsilon) const; bool is_point_inside(float x, float y) const { return is_point_inside(Vec2{x, y}, 0.00001f); @@ -535,7 +535,7 @@ struct Quad { return is_point_inside(Vec2{x, y}, epsilon); } - Quad& set(Quad& other) + Quad& set(const Quad& other) { *this = other; return *this; @@ -570,7 +570,7 @@ struct Quad Vec2 intersection(const Vec2 A, const Vec2 B, const Vec2 C, const Vec2 D); /// Mesures angle between two lines with one common point -float two_lines_angle(Vec2 A, Vec2 common, Vec2 B); +float two_lines_angle(const Vec2 A, const Vec2 common, const Vec2 B); /// Gets line1_A, intersection point and line2_B and calls the 3 parameter version of this function -float two_lines_angle(Vec2 line1_A, Vec2 line1_B, Vec2 line2_A, Vec2 line2_B); +float two_lines_angle(const Vec2 line1_A, const Vec2 line1_B, const Vec2 line2_A, const Vec2 line2_B); diff --git a/src/game_api/script/usertypes/hitbox_lua.cpp b/src/game_api/script/usertypes/hitbox_lua.cpp index 87f030c88..a581bb568 100644 --- a/src/game_api/script/usertypes/hitbox_lua.cpp +++ b/src/game_api/script/usertypes/hitbox_lua.cpp @@ -120,8 +120,8 @@ void register_usertypes(sol::state& lua) static_cast(&Triangle::offset), static_cast(&Triangle::offset)); auto is_point_inside_triangle = sol::overload( - static_cast(&Triangle::is_point_inside), - static_cast(&Triangle::is_point_inside), + static_cast(&Triangle::is_point_inside), + static_cast(&Triangle::is_point_inside), static_cast(&Triangle::is_point_inside), static_cast(&Triangle::is_point_inside)); @@ -155,14 +155,14 @@ void register_usertypes(sol::state& lua) &Triangle::operator std::tuple); auto is_point_inside_quad = sol::overload( - static_cast(&Quad::is_point_inside), - static_cast(&Quad::is_point_inside), + static_cast(&Quad::is_point_inside), + static_cast(&Quad::is_point_inside), static_cast(&Quad::is_point_inside), static_cast(&Quad::is_point_inside)); lua.new_usertype( "Quad", - sol::constructors{}, + sol::constructors{}, "bottom_left_x", &Quad::bottom_left_x, "bottom_left_y", @@ -198,8 +198,8 @@ void register_usertypes(sol::state& lua) &Quad::operator std::tuple); auto two_lines_angle = sol::overload( - static_cast(::two_lines_angle), - static_cast(::two_lines_angle)); + static_cast(::two_lines_angle), + static_cast(::two_lines_angle)); lua["intersection"] = intersection; lua["two_lines_angle"] = two_lines_angle; } diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 46881a4a5..102ac3c3f 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -488,6 +488,7 @@ std::unordered_map g_address_rules{ }, { "game_free"sv, + // it's function that decides to use game_free or custom_free by the address PatternCommandBuffer{} .find_inst("\x48\x83\x7e\x18\x00"sv) .offset(-0x10) From c99216a23a4259827bbc567d94f5c308a20dd090 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 20:28:59 +0100 Subject: [PATCH 29/31] particle stuff, move the particle functions from rpc, expose some more particle stuff, allow for making custom `ParticleDB` --- src/game_api/particles.cpp | 79 ++++++++++++++++++- src/game_api/particles.hpp | 37 +++++---- src/game_api/rpc.cpp | 72 ----------------- src/game_api/rpc.hpp | 5 -- .../script/usertypes/particles_lua.cpp | 7 ++ 5 files changed, 108 insertions(+), 92 deletions(-) diff --git a/src/game_api/particles.cpp b/src/game_api/particles.cpp index e209831fa..c38ea89dc 100644 --- a/src/game_api/particles.cpp +++ b/src/game_api/particles.cpp @@ -8,7 +8,9 @@ #include // for unordered_map, _Umap_traits<>::allocator_type #include // for min, max +#include "entity.hpp" // #include "search.hpp" // for get_address +#include "state.hpp" // #include "texture.hpp" // for get_texture, Texture ParticleDB* particle_db_ptr() @@ -103,7 +105,7 @@ const Particle EmittedParticlesInfo::operator[](const size_type idx) const }; } -ParticleDB* get_particle_type(uint32_t id) +ParticleDB* get_particle_type(PARTICLEEMITTER id) { static std::unordered_map mapping = {}; if (mapping.size() == 0) @@ -150,3 +152,78 @@ const std::vector& list_particles() } return particles; } + +ParticleDB::ParticleDB(PARTICLEEMITTER particle_id) + : ParticleDB(*get_particle_type(particle_id)){}; + +ParticleEmitterInfo* generate_world_particles(PARTICLEEMITTER particle_emitter_id, uint32_t uid) +{ + static size_t offset = get_address("generate_world_particles"); + + if (offset != 0) + { + auto entity = get_entity_ptr(uid); + if (entity != nullptr) + { + auto state = get_state_ptr(); + typedef ParticleEmitterInfo* generate_particles_func(custom_vector*, uint32_t, Entity*); + static generate_particles_func* gpf = (generate_particles_func*)(offset); + return gpf(state->particle_emitters, particle_emitter_id, entity); + } + } + return nullptr; +} + +ParticleEmitterInfo* generate_screen_particles(PARTICLEEMITTER particle_emitter_id, float x, float y) +{ + static size_t offset = get_address("generate_screen_particles"); + + if (offset != 0) + { + typedef ParticleEmitterInfo* generate_particles_func(uint32_t, float, float, size_t); + static generate_particles_func* gpf = (generate_particles_func*)(offset); + return gpf(particle_emitter_id, x, y, 0); + } + return nullptr; +} + +void advance_screen_particles(ParticleEmitterInfo* particle_emitter) +{ + static size_t offset = get_address("advance_screen_particles"); + + if (offset != 0) + { + typedef void advance_particles_func(ParticleEmitterInfo*); + static advance_particles_func* apf = (advance_particles_func*)(offset); + apf(particle_emitter); + } +} + +void render_screen_particles(ParticleEmitterInfo* particle_emitter) +{ + static size_t offset = get_address("render_screen_particles"); + + if (offset != 0) + { + typedef void render_particles_func(ParticleEmitterInfo*, size_t, size_t, size_t); + static render_particles_func* rpf = (render_particles_func*)(offset); + rpf(particle_emitter, 0, 0, 0); + } +} + +void extinguish_particles(ParticleEmitterInfo* particle_emitter) +{ + // removing from state only applies to world emitters, but it just won't find the screen one in the vector, so no big deal + auto state = get_state_ptr(); + std::erase(*state->particle_emitters, particle_emitter); + + using generic_free_func = void(void*); + static generic_free_func* generic_free = (generic_free_func*)get_address("generic_free"); + + if (particle_emitter != nullptr) + { + generic_free(particle_emitter->emitted_particles.memory); + generic_free(particle_emitter->emitted_particles_back_layer.memory); + generic_free(particle_emitter); + } +} diff --git a/src/game_api/particles.hpp b/src/game_api/particles.hpp index 503fe8267..281531293 100644 --- a/src/game_api/particles.hpp +++ b/src/game_api/particles.hpp @@ -14,9 +14,11 @@ struct Texture; +using PARTICLEEMITTER = uint32_t; + struct ParticleDB { - uint32_t id; + PARTICLEEMITTER id; int32_t spawn_count_min; // minimum amount of particles to spawn in an iteration, actual value is random between this value and spawn_count (-1 = no lower bound, uses spawn_count) uint32_t spawn_count; // total amount of particles to spawn for 1 iteration (check with PETTING_PET or MOUNT_TAMED, amount of hearts shown) int32_t lifespan_min; // minimum lifespan of a particle, actual value is random between this value and lifespan (-1 = no lower bound, uses lifespan) @@ -66,6 +68,9 @@ struct ParticleDB uint8_t unknown41; uint32_t unknown42; + ParticleDB(const ParticleDB& other) = default; + ParticleDB(const PARTICLEEMITTER particle_id); + std::uint64_t get_texture(); bool set_texture(std::uint32_t texture_id); }; @@ -104,8 +109,8 @@ struct EmittedParticlesInfo void* memory; uint16_t* max_lifetimes; uint16_t* lifetimes; - size_t unknown7; - size_t unknown8; + float* unknown7; + float* unknown8; float* x_positions; float* y_positions; float* unknown_x_positions; @@ -115,12 +120,12 @@ struct EmittedParticlesInfo float* heights; float* x_velocities; float* y_velocities; - size_t unknown18; - size_t unknown19; - size_t unknown20; - size_t unknown21; - size_t unknown22; - size_t unknown23; + uint8_t* unknown18; + uint8_t* unknown19; + uint8_t* unknown20; + uint8_t* unknown21; + uint8_t* unknown22; + uint8_t* unknown23; template class IteratorImpl : public neo::iterator_facade> @@ -200,10 +205,9 @@ struct ParticleEmitterInfo float offset_x; float offset_y; - uint8_t unknown54a; // layer? - uint8_t unknown54b; - uint8_t unknown54c; - uint8_t unknown54d; + uint8_t layer; + uint8_t draw_depth; + uint8_t padding_probably[2]; float unknown55; uint32_t unknown56; uint32_t total_particles; @@ -212,5 +216,10 @@ struct ParticleEmitterInfo uint32_t unknown60; }; -ParticleDB* get_particle_type(uint32_t id); +ParticleDB* get_particle_type(PARTICLEEMITTER id); const std::vector& list_particles(); +ParticleEmitterInfo* generate_world_particles(PARTICLEEMITTER particle_emitter_id, uint32_t uid); +ParticleEmitterInfo* generate_screen_particles(PARTICLEEMITTER particle_emitter_id, float x, float y); +void advance_screen_particles(ParticleEmitterInfo* particle_emitter); +void render_screen_particles(ParticleEmitterInfo* particle_emitter); +void extinguish_particles(ParticleEmitterInfo* particle_emitter); diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 70b9e18b9..64ba19d26 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -792,78 +792,6 @@ bool is_inside_shop_zone(float x, float y, LAYER layer) return false; } -ParticleEmitterInfo* generate_world_particles(uint32_t particle_emitter_id, uint32_t uid) -{ - static size_t offset = get_address("generate_world_particles"); - - if (offset != 0) - { - auto entity = get_entity_ptr(uid); - if (entity != nullptr) - { - auto state = get_state_ptr(); - typedef ParticleEmitterInfo* generate_particles_func(custom_vector*, uint32_t, Entity*); - static generate_particles_func* gpf = (generate_particles_func*)(offset); - return gpf(state->particle_emitters, particle_emitter_id, entity); - } - } - return nullptr; -} - -ParticleEmitterInfo* generate_screen_particles(uint32_t particle_emitter_id, float x, float y) -{ - static size_t offset = get_address("generate_screen_particles"); - - if (offset != 0) - { - typedef ParticleEmitterInfo* generate_particles_func(uint32_t, float, float, size_t); - static generate_particles_func* gpf = (generate_particles_func*)(offset); - return gpf(particle_emitter_id, x, y, 0); - } - return nullptr; -} - -void advance_screen_particles(ParticleEmitterInfo* particle_emitter) -{ - static size_t offset = get_address("advance_screen_particles"); - - if (offset != 0) - { - typedef void advance_particles_func(ParticleEmitterInfo*); - static advance_particles_func* apf = (advance_particles_func*)(offset); - apf(particle_emitter); - } -} - -void render_screen_particles(ParticleEmitterInfo* particle_emitter) -{ - static size_t offset = get_address("render_screen_particles"); - - if (offset != 0) - { - typedef void render_particles_func(ParticleEmitterInfo*, size_t, size_t, size_t); - static render_particles_func* rpf = (render_particles_func*)(offset); - rpf(particle_emitter, 0, 0, 0); - } -} - -void extinguish_particles(ParticleEmitterInfo* particle_emitter) -{ - // removing from state only applies to world emitters, but it just won't find the screen one in the vector, so no big deal - auto state = get_state_ptr(); - std::erase(*state->particle_emitters, particle_emitter); - - using generic_free_func = void(void*); - static generic_free_func* generic_free = (generic_free_func*)get_address("generic_free"); - - if (particle_emitter != nullptr) - { - generic_free(particle_emitter->emitted_particles.memory); - generic_free(particle_emitter->emitted_particles_back_layer.memory); - generic_free(particle_emitter); - } -} - void set_journal_enabled(bool b) { get_journal_enabled() = b; diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index bc187a46f..6428577d3 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -72,11 +72,6 @@ void force_olmec_phase_0(bool b); void set_ghost_spawn_times(uint32_t normal = 10800, uint32_t cursed = 9000); void set_time_ghost_enabled(bool b); void set_time_jelly_enabled(bool b); -ParticleEmitterInfo* generate_world_particles(uint32_t particle_emitter_id, uint32_t uid); -ParticleEmitterInfo* generate_screen_particles(uint32_t particle_emitter_id, float x, float y); -void advance_screen_particles(ParticleEmitterInfo* particle_emitter); -void render_screen_particles(ParticleEmitterInfo* particle_emitter); -void extinguish_particles(ParticleEmitterInfo* particle_emitter); void set_journal_enabled(bool b); void set_camp_camera_bounds_enabled(bool b); void set_explosion_mask(int32_t mask); diff --git a/src/game_api/script/usertypes/particles_lua.cpp b/src/game_api/script/usertypes/particles_lua.cpp index b727a8351..bf35a1b90 100644 --- a/src/game_api/script/usertypes/particles_lua.cpp +++ b/src/game_api/script/usertypes/particles_lua.cpp @@ -45,6 +45,7 @@ void register_usertypes(sol::state& lua) /// Used in ParticleDB, [get_particle_type](#get_particle_type) auto particledb_type = lua.new_usertype("ParticleDB"); + particledb_type["new"] = sol::constructors(); particledb_type["id"] = &ParticleDB::id; particledb_type["spawn_count_min"] = &ParticleDB::spawn_count_min; particledb_type["spawn_count"] = &ParticleDB::spawn_count; @@ -82,6 +83,8 @@ void register_usertypes(sol::state& lua) "ParticleEmitterInfo", "particle_type", &ParticleEmitterInfo::particle_type, + "particle_type2", + &ParticleEmitterInfo::particle_type2, "particle_count", sol::property([](ParticleEmitterInfo& e) -> uint32_t { return e.emitted_particles.particle_count; }), @@ -98,6 +101,10 @@ void register_usertypes(sol::state& lua) &ParticleEmitterInfo::offset_x, "offset_y", &ParticleEmitterInfo::offset_y, + "layer", + &ParticleEmitterInfo::layer, + "draw_depth", + &ParticleEmitterInfo::draw_depth, "emitted_particles", &ParticleEmitterInfo::emitted_particles, "emitted_particles_back_layer", From 07e37a35486cd62c4e42c700d2e77d6cf898dd9c Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 1 Nov 2023 20:39:25 +0100 Subject: [PATCH 30/31] update doc --- docs/game_data/spel2.lua | 27 ++++++++++++++----- docs/src/includes/_globals.md | 8 +++--- docs/src/includes/_types.md | 15 ++++++++--- .../script/usertypes/particles_lua.cpp | 3 +-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index fae909481..63eef3c97 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1371,16 +1371,16 @@ function set_character_heart_color(type_id, color) end ---@return CustomMovableBehavior function make_custom_behavior(behavior_name, state_id, base_behavior) end ---Get the [ParticleDB](https://spelunky-fyi.github.io/overlunky/#ParticleDB) details of the specified ID ----@param id integer +---@param id PARTICLEEMITTER ---@return ParticleDB function get_particle_type(id) end ---Generate particles of the specified type around the specified entity uid (use e.g. `local emitter = generate_world_particles(PARTICLEEMITTER.PETTING_PET, players[1].uid)`). You can then decouple the emitter from the entity with `emitter.entity_uid = -1` and freely move it around. See the `particles.lua` example script for more details. ----@param particle_emitter_id integer +---@param particle_emitter_id PARTICLEEMITTER ---@param uid integer ---@return ParticleEmitterInfo function generate_world_particles(particle_emitter_id, uid) end ---Generate particles of the specified type at a certain screen coordinate (use e.g. `local emitter = generate_screen_particles(PARTICLEEMITTER.CHARSELECTOR_TORCHFLAME_FLAMES, 0.0, 0.0)`). See the `particles.lua` example script for more details. ----@param particle_emitter_id integer +---@param particle_emitter_id PARTICLEEMITTER ---@param x number ---@param y number ---@return ParticleEmitterInfo @@ -3610,7 +3610,7 @@ function Movable:generic_update_world(move, sprint_factor, disable_gravity, on_r ---@field emitted_light Illumination ---@class FlameSize : Flame - ---@field flame_size number @if changed, gradually goes down (0.03 per frame) to the default size + ---@field flame_size number @if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` ---@class ClimbableRope : Movable ---@field segment_nr_inverse integer @@ -4367,7 +4367,7 @@ function MovableBehavior:get_state_id() end ---@field set_get_next_state_id fun(self, get_next_state_id: fun(movable: Movable, base_fun: function): integer): nil @Set the `get_next_state_id` function of a `CustomMovableBehavior`, this will be called every frame when
the movable is updated. If an `get_next_state_id` is already set it will be overridden. The signature
of the function is `int get_next_state_id(Movable movable, function base_fun))`, use this to move to another state, return `nil`.
or this behaviors `state_id` to remain in this behavior. If no base behavior is set `base_fun` will be `nil`. ---@class ParticleDB - ---@field id integer + ---@field id PARTICLEEMITTER ---@field spawn_count_min integer ---@field spawn_count integer ---@field lifespan_min integer @@ -4400,6 +4400,7 @@ function MovableBehavior:get_state_id() end ---@class ParticleEmitterInfo ---@field particle_type ParticleDB + ---@field particle_type2 ParticleDB ---@field particle_count integer ---@field particle_count_back_layer integer ---@field entity_uid integer @@ -4407,6 +4408,8 @@ function MovableBehavior:get_state_id() end ---@field y number ---@field offset_x number ---@field offset_y number + ---@field layer integer + ---@field draw_depth integer ---@field emitted_particles Particle[] ---@field emitted_particles_back_layer Particle[] @@ -5348,6 +5351,7 @@ function VanillaRenderContext:draw_world_poly_filled(points, color) end ---@field y number ---@field rotate fun(self, angle: number, px: number, py: number): Vec2 ---@field distance_to fun(self, other: Vec2): number @Just simple pythagoras theorem + ---@field set fun(self, other: Vec2): Vec2 ---@field split fun(self): number, number ---@class AABB @@ -5362,6 +5366,7 @@ function VanillaRenderContext:draw_world_poly_filled(points, color) end ---@field center fun(self): number, number @Short for `(aabb.left + aabb.right) / 2.0f, (aabb.top + aabb.bottom) / 2.0f`. ---@field width fun(self): number @Short for `aabb.right - aabb.left`. ---@field height fun(self): number @Short for `aabb.top - aabb.bottom`. + ---@field set fun(self, other: AABB): AABB ---@field split fun(self): number, number, number, number local AABB = nil ---Grows or shrinks the AABB by the given amount in all directions. @@ -5393,6 +5398,7 @@ function AABB:is_point_inside(x, y) end ---@field get_angles fun(self): number, number, number @Returns ABC, BCA, CAB angles in radians ---@field scale fun(self, scale: number): Triangle ---@field area fun(self): number + ---@field set fun(self, other: Triangle): Triangle ---@field split fun(self): Vec2, Vec2, Vec2 @Returns the corner points local Triangle = nil ---@param off Vec2 @@ -5428,6 +5434,7 @@ function Triangle:is_point_inside(x, y, epsilon) end ---@field rotate fun(self, angle: number, px: number, py: number): Quad @Rotates a Quad by an angle, px/py are not offsets, use `:get_AABB():center()` to get approximated center for simetrical quadrangle ---@field flip_horizontally fun(self): Quad ---@field flip_vertically fun(self): Quad + ---@field set fun(self, other: Quad): Quad ---@field split fun(self): Vec2, Vec2, Vec2, Vec2 @Returns the corners in order: bottom_left, bottom_right, top_right, top_left local Quad = nil ---Check if point lies inside of triangle @@ -5833,7 +5840,7 @@ function Quad:is_point_inside(x, y, epsilon) end ---@field page_timer integer ---@field fade_timer integer ---@field opacity integer - ---@field pages JournalPage[] @Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) + ---@field pages JournalPage[] @Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) ---@class JournalPage ---@field background TextureRenderingInfo @@ -6339,6 +6346,14 @@ function EntityDB:new(other) end ---@return EntityDB function EntityDB:new(other) end +ParticleDB = nil +---@param other ParticleDB +---@return ParticleDB +function ParticleDB:new(other) end +---@param particle_id PARTICLEEMITTER +---@return ParticleDB +function ParticleDB:new(particle_id) end + CustomTheme = nil ---Create a new theme with an id and base theme, overriding defaults. Check [theme functions that are default enabled here](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/script/usertypes/level_lua.cpp). ---@param theme_id_ integer diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 85141bcde..fb4b69e9b 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -2417,7 +2417,7 @@ Extinguish a particle emitter (use the return value of `generate_world_particles > Search script examples for [generate_screen_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_screen_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_screen_particles(int particle_emitter_id, float x, float y) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_screen_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, float x, float y) Generate particles of the specified type at a certain screen coordinate (use e.g. `local emitter = generate_screen_particles(PARTICLEEMITTER.CHARSELECTOR_TORCHFLAME_FLAMES, 0.0, 0.0)`). See the `particles.lua` example script for more details. @@ -2426,7 +2426,7 @@ Generate particles of the specified type at a certain screen coordinate (use e.g > Search script examples for [generate_world_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_world_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_world_particles(int particle_emitter_id, int uid) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_world_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, int uid) Generate particles of the specified type around the specified entity uid (use e.g. `local emitter = generate_world_particles(PARTICLEEMITTER.PETTING_PET, players[1].uid)`). You can then decouple the emitter from the entity with `emitter.entity_uid = -1` and freely move it around. See the `particles.lua` example script for more details. @@ -2435,7 +2435,7 @@ Generate particles of the specified type around the specified entity uid (use e. > Search script examples for [get_particle_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_particle_type) -#### [ParticleDB](#ParticleDB) get_particle_type(int id) +#### [ParticleDB](#ParticleDB) get_particle_type([PARTICLEEMITTER](#PARTICLEEMITTER) id) Get the [ParticleDB](#ParticleDB) details of the specified ID @@ -3946,7 +3946,7 @@ Use this only when no other approach works, this call can be expensive if overus > Search script examples for [generate_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_particles(int particle_emitter_id, int uid) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, int uid) Use `generate_world_particles` diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index f33db0fe8..40963f3e2 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -571,6 +571,7 @@ float | [width()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=width float | [height()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=height) | Short for `aabb.top - aabb.bottom`. bool | [is_point_inside(Vec2 p)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Checks if point lies between left/right and top/bottom bool | [is_point_inside(float x, float y)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[AABB](#AABB) | [set(AABB other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<float, float, float, float> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | ### BackgroundMusic @@ -810,6 +811,7 @@ float | [top_left_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=to [Quad](#Quad) | [flip_vertically()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flip_vertically) | bool | [is_point_inside(Vec2 p, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Check if point lies inside of triangle
Because of the imprecise nature of floating point values, the `epsilon` value is needed to compare the floats, the default value is `0.00001` bool | [is_point_inside(float x, float y, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[Quad](#Quad) | [set(Quad other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<[Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2)> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | Returns the corners in order: bottom_left, bottom_right, top_right, top_left ### RenderInfo @@ -925,6 +927,7 @@ tuple<float, float, float> | [get_angles()](https://github.com/spelunky-fy float | [area()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=area) | bool | [is_point_inside(Vec2 p, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Check if point lies inside of triangle
Because of the imprecise nature of floating point values, the `epsilon` value is needed to compare the floats, the default value is `0.0001` bool | [is_point_inside(float x, float y, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[Triangle](#Triangle) | [set(Triangle other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<[Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2)> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | Returns the corner points ### UdpServer @@ -946,6 +949,7 @@ float | [x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=x) | float | [y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=y) | [Vec2](#Vec2) | [rotate(float angle, float px, float py)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=rotate) | float | [distance_to(Vec2 other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=distance_to) | Just simple pythagoras theorem +[Vec2](#Vec2) | [set(Vec2 other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<float, float> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | ## Input types @@ -1661,7 +1665,9 @@ Used in [ParticleDB](#ParticleDB), [get_particle_type](#get_particle_type) Type | Name | Description ---- | ---- | ----------- -int | [id](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=id) | +[ParticleDB](#ParticleDB) | [new(ParticleDB other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ParticleDB) | +[ParticleDB](#ParticleDB) | [new(PARTICLEEMITTER particle_id)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ParticleDB) | +[PARTICLEEMITTER](#PARTICLEEMITTER) | [id](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=id) | int | [spawn_count_min](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_count_min) | int | [spawn_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_count) | int | [lifespan_min](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=lifespan_min) | @@ -1700,6 +1706,7 @@ Used in [ScreenCharacterSelect](#ScreenCharacterSelect), [ScreenTitle](#ScreenTi Type | Name | Description ---- | ---- | ----------- [ParticleDB](#ParticleDB) | [particle_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_type) | +[ParticleDB](#ParticleDB) | [particle_type2](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_type2) | int | [particle_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_count) | int | [particle_count_back_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_count_back_layer) | int | [entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=entity_uid) | @@ -1707,6 +1714,8 @@ float | [x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=x) | float | [y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=y) | float | [offset_x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_x) | float | [offset_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_y) | +int | [layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer) | +int | [draw_depth](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=draw_depth) | array<[Particle](#Particle)> | [emitted_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=emitted_particles) | array<[Particle](#Particle)> | [emitted_particles_back_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=emitted_particles_back_layer) | @@ -1849,7 +1858,7 @@ int | [max_page_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q= int | [page_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=page_timer) | int | [fade_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=fade_timer) | int | [opacity](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=opacity) | -vector<[JournalPage](#JournalPage)> | [pages](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pages) | Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) +vector<[JournalPage](#JournalPage)> | [pages](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pages) | Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) ### OnlineLobbyScreenPlayer @@ -6037,7 +6046,7 @@ Derived from [Entity](#Entity) [Movable](#Movable) [Flame](#Flame) Type | Name | Description ---- | ---- | ----------- -float | [flame_size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flame_size) | if changed, gradually goes down (0.03 per frame) to the default size +float | [flame_size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flame_size) | if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` ### Fly diff --git a/src/game_api/script/usertypes/particles_lua.cpp b/src/game_api/script/usertypes/particles_lua.cpp index bf35a1b90..4c426b1dd 100644 --- a/src/game_api/script/usertypes/particles_lua.cpp +++ b/src/game_api/script/usertypes/particles_lua.cpp @@ -44,8 +44,7 @@ void register_usertypes(sol::state& lua) lua["generate_particles"] = generate_world_particles; /// Used in ParticleDB, [get_particle_type](#get_particle_type) - auto particledb_type = lua.new_usertype("ParticleDB"); - particledb_type["new"] = sol::constructors(); + auto particledb_type = lua.new_usertype("ParticleDB", sol::constructors()); particledb_type["id"] = &ParticleDB::id; particledb_type["spawn_count_min"] = &ParticleDB::spawn_count_min; particledb_type["spawn_count"] = &ParticleDB::spawn_count; From 4efa40449bf29a4de5d306a4f301ef4898adab65 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:58:52 +0100 Subject: [PATCH 31/31] write our some more obvious stuff --- src/game_api/game_api.hpp | 71 ++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/src/game_api/game_api.hpp b/src/game_api/game_api.hpp index 552b6bfdc..a8df03247 100644 --- a/src/game_api/game_api.hpp +++ b/src/game_api/game_api.hpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include struct Renderer { @@ -19,22 +21,75 @@ struct Renderer uint8_t flags2; uint8_t padding[6]; - uint8_t skip[0x1228]; // tons of pointers - - uint8_t skip2[0x7F284]; // a lot of nothing - + uint8_t skip[0x1228]; // tons of pointers, some structs with vtables + + uint8_t skip2[0x7F048]; // a lot of nothing + + size_t unknown38; // bool? + float unknown39; // not sure if actually float + float unknown40; + float unknown41; + uint8_t unknown42[4]; + const char** unknown43a; // font/floor it's changing + const char** unknown43b; // noise0.dds + const char** unknown43c; // noise1.dds + size_t unknown44[4]; // null? + size_t unknown45; // bool? + + // feels like two standard containers or something + size_t* unknown46; + size_t* unknown47; + size_t unknown48; + size_t* unknown49; + size_t* unknown50; + size_t unknown51; // + + uint32_t unknown52; + uint32_t unknown53; // padding probably + size_t unknown54; // sometimes -1 sometimes pointer + uint32_t unknown55; // -1 + uint32_t unknown56; // null + size_t unknown57; + size_t unknown58; + uint16_t unknown59; // 2k + uint16_t unknown60a; // 512 + uint16_t unknown60b[2]; // padding? + size_t* unknown61[4]; + size_t unknown62; // bool? + std::unordered_map unknown63; // not sure about the key/value + + // bounch of vectors that probably used to load textures or something, they all seam to contain names of the .dds files + // when i checked all seam to be already cleared and just have the data leftover, the "const char**" pointers identical as in texturedb + + size_t unknown64[6]; // possibly two more vectors? + std::vector unknown65; // splash 0,1,2 + std::vector unknown66; // fonts, basecamp, pet + std::vector unknown67; // fonts and menu textures + characters (character select screen textures?) + std::vector unknown68; // main menu background textures? + size_t unknown69[3]; // probably also vector, but it's null when i checked + std::vector unknown70; // menu textures? + std::vector unknown71; // only the ai.dds + size_t unknown[8]; // null + + uint8_t unknown80; + uint8_t unknown81; + uint8_t unknown82; + uint8_t unknown83; // padding probably float current_zoom; float target_zoom; float target_zoom_offset; float current_zoom_offset; float backlayer_light_level; // constantly overwritten by theme virtual get_backlayer_light_level - uint8_t unknown2; - uint8_t unknown3; - uint16_t unknown4; + uint8_t unknown84; + uint8_t unknown85; + uint8_t unknown86[6]; // padding probably + + size_t* unknown87; // some vtables - uint8_t skip3[0xAE4]; + uint8_t skip3[0xAD8]; // probably some static arrays of ... stuff size_t swap_chain; + // 3 more pointers, some bit fields, then 5 more pointers // somewhere there should be shaders stored