From 623e6998d75175c8b590472973e70fa753ee169e Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:24:22 +0200 Subject: [PATCH 01/33] small `get_proper_types` optimisation --- src/game_api/rpc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 0b53eda97..966cc1b55 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1341,7 +1341,7 @@ bool entity_type_check(const std::vector& types_array, const ENT_TYPE std::vector get_proper_types(std::vector ent_types) { - for (size_t i = 0; i < ent_types.size(); i++) + for (size_t i = 0; i < ent_types.size(); ++i) { if (ent_types[i] >= (uint32_t)CUSTOM_TYPE::ACIDBUBBLE) { @@ -1353,8 +1353,8 @@ std::vector get_proper_types(std::vector ent_types) else if (!extra_types.empty()) { auto it = ent_types.begin() + i; - it = ent_types.erase(it); - ent_types.insert(it, extra_types.begin(), extra_types.end()); + *it = extra_types[0]; + ent_types.insert(++it, extra_types.begin() + 1, extra_types.end()); i += extra_types.size() - 1; } } From 04a75a25969664c56ea8c6f083306b885318521f Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:26:23 +0200 Subject: [PATCH 02/33] change `custom_type_names` to vector, since we only iterate thru it --- src/game_api/custom_types.cpp | 4 ++-- src/game_api/custom_types.hpp | 2 +- src/game_api/script/usertypes/entity_lua.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game_api/custom_types.cpp b/src/game_api/custom_types.cpp index f0459dc5e..15cb8d204 100644 --- a/src/game_api/custom_types.cpp +++ b/src/game_api/custom_types.cpp @@ -9,7 +9,7 @@ #include "entity.hpp" // for to_id -const std::map custom_type_names = { +const std::vector> custom_type_names = { {CUSTOM_TYPE::ACIDBUBBLE, "ACIDBUBBLE"}, {CUSTOM_TYPE::ALIEN, "ALIEN"}, {CUSTOM_TYPE::ALTAR, "ALTAR"}, @@ -1574,7 +1574,7 @@ bool is_type_movable(ENT_TYPE type) return false; } -const std::map& get_custom_types_map() +const std::vector>& get_custom_types_vector() { return custom_type_names; } diff --git a/src/game_api/custom_types.hpp b/src/game_api/custom_types.hpp index b89561190..645936dd1 100644 --- a/src/game_api/custom_types.hpp +++ b/src/game_api/custom_types.hpp @@ -347,4 +347,4 @@ enum class CUSTOM_TYPE : uint32_t std::span get_custom_entity_types(CUSTOM_TYPE type); bool is_type_movable(ENT_TYPE type); -const std::map& get_custom_types_map(); +const std::vector>& get_custom_types_vector(); diff --git a/src/game_api/script/usertypes/entity_lua.cpp b/src/game_api/script/usertypes/entity_lua.cpp index 7cccdb145..52911cd4f 100644 --- a/src/game_api/script/usertypes/entity_lua.cpp +++ b/src/game_api/script/usertypes/entity_lua.cpp @@ -355,9 +355,9 @@ void register_usertypes(sol::state& lua) auto name = item.name.substr(9, item.name.size()); lua["ENT_TYPE"][name] = item.id; } - for (auto& elm : get_custom_types_map()) + for (auto& elm : get_custom_types_vector()) { - lua["ENT_TYPE"][elm.second] = elm.first; + lua["ENT_TYPE"][elm.second] = (uint32_t)elm.first; } lua.create_named_table("REPEAT_TYPE", "NO_REPEAT", REPEAT_TYPE::NoRepeat, "LINEAR", REPEAT_TYPE::Linear, "BACK_AND_FORTH", REPEAT_TYPE::BackAndForth); From ec00a21ea248462c85269fcc784e02cc47d9a459 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:28:23 +0200 Subject: [PATCH 03/33] add `add_custom_type` function for creating CUSTOM_TYPE --- src/game_api/custom_types.cpp | 22 +++++++++++++++++++++- src/game_api/custom_types.hpp | 3 +++ src/game_api/rpc.cpp | 5 +++++ src/game_api/rpc.hpp | 1 + src/game_api/script/lua_vm.cpp | 3 +++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/game_api/custom_types.cpp b/src/game_api/custom_types.cpp index 15cb8d204..b2e9523ff 100644 --- a/src/game_api/custom_types.cpp +++ b/src/game_api/custom_types.cpp @@ -355,6 +355,9 @@ std::span make_custom_entity_type_list(StrArgs... ent_type_ids) return {s_entity_types.begin(), s_entity_types.end()}; } +std::map> user_custom_types; +uint32_t g_last_custom_id = (uint32_t)custom_type_max; + std::span get_custom_entity_types(CUSTOM_TYPE type) { if (type < CUSTOM_TYPE::ACIDBUBBLE) @@ -1560,11 +1563,28 @@ std::span get_custom_entity_types(CUSTOM_TYPE type) return make_custom_entity_type_list("ENT_TYPE_MONS_YETIKING"); case CUSTOM_TYPE::YETIQUEEN: return make_custom_entity_type_list("ENT_TYPE_MONS_YETIQUEEN"); + default: + { + auto it = user_custom_types.find(type); + if (it != user_custom_types.end()) + { + return {it->second.begin(), it->second.end()}; + } + } } - return {}; } +CUSTOM_TYPE add_new_custom_type(std::vector types) +{ + if (types.empty()) + return (CUSTOM_TYPE)0; + + ++g_last_custom_id; + user_custom_types.emplace((CUSTOM_TYPE)g_last_custom_id, std::move(types)); + return (CUSTOM_TYPE)g_last_custom_id; +} + bool is_type_movable(ENT_TYPE type) { auto movable_types = get_custom_entity_types(CUSTOM_TYPE::MOVABLE); diff --git a/src/game_api/custom_types.hpp b/src/game_api/custom_types.hpp index 645936dd1..683fdd5da 100644 --- a/src/game_api/custom_types.hpp +++ b/src/game_api/custom_types.hpp @@ -345,6 +345,9 @@ enum class CUSTOM_TYPE : uint32_t YETIQUEEN, }; +constexpr CUSTOM_TYPE custom_type_max = CUSTOM_TYPE::YETIQUEEN; + std::span get_custom_entity_types(CUSTOM_TYPE type); +CUSTOM_TYPE add_new_custom_type(std::vector types); bool is_type_movable(ENT_TYPE type); const std::vector>& get_custom_types_vector(); diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 966cc1b55..56977ea6c 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -2058,3 +2058,8 @@ void set_boss_door_control_enabled(bool enable) else recover_mem("set_boss_door_control_enabled"); } + +ENT_TYPE add_custom_type(std::vector types) +{ + return (ENT_TYPE)add_new_custom_type(std::move(types)); +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 2b6c155fa..c41c896a1 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -143,3 +143,4 @@ void activate_tiamat_position_hack(bool activate); void activate_crush_elevator_hack(bool activate); void activate_hundun_hack(bool activate); void set_boss_door_control_enabled(bool enable); +ENT_TYPE add_custom_type(std::vector types); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index a9f789d9b..090db0f06 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2039,6 +2039,9 @@ end /// This will also prevent game crashing when there is no exit door when they are in level lua["set_boss_door_control_enabled"] = set_boss_door_control_enabled; + /// Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn + lua["add_custom_type"] = add_custom_type; + 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( From a0dfec57e832ccdc7f74836e17f1ad5df52989c1 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:33:07 +0200 Subject: [PATCH 04/33] move entity lookups to a separate file to deuce the rpc size a bit --- docs/game_data/spel2.lua | 4 + docs/parse_source.py | 1 + docs/src/includes/_globals.md | 9 + src/game_api/entity_lookup.cpp | 315 ++++++++++++++++++++++++++++ src/game_api/entity_lookup.hpp | 31 +++ src/game_api/layer.cpp | 8 +- src/game_api/layer.hpp | 3 +- src/game_api/level_api.cpp | 1 + src/game_api/rpc.cpp | 303 +------------------------- src/game_api/rpc.hpp | 21 -- src/game_api/script/lua_backend.cpp | 1 + src/game_api/script/lua_vm.cpp | 1 + src/injected/ui_util.cpp | 1 + 13 files changed, 370 insertions(+), 329 deletions(-) create mode 100644 src/game_api/entity_lookup.cpp create mode 100644 src/game_api/entity_lookup.hpp diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index f9f06777c..0dce27874 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1198,6 +1198,10 @@ function activate_hundun_hack(activate) end ---@param enable boolean ---@return nil function set_boss_door_control_enabled(enable) end +---Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +---@param types ENT_TYPE[] +---@return ENT_TYPE +function add_custom_type(types) end ---@return boolean function toast_visible() end ---@return boolean diff --git a/docs/parse_source.py b/docs/parse_source.py index 8f00d989e..28150c711 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -14,6 +14,7 @@ header_files = [ "../src/game_api/math.hpp", "../src/game_api/rpc.hpp", + "../src/game_api/entity_lookup.hpp", "../src/game_api/drops.hpp", "../src/game_api/spawn_api.hpp", "../src/game_api/script.hpp", diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 01babc41e..f4d5a0660 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1157,6 +1157,15 @@ Activate custom variables for y coordinate limit for hundun and spawn of it's he note: because those variables are custom and game does not initiate them, you need to do it yourself for each [Hundun](#Hundun) entity, recommending `set_post_entity_spawn` default game value are: y_limit = 98.5, rising_speed_x = 0, rising_speed_y = 0.0125, bird_head_spawn_y = 55, snake_head_spawn_y = 71 +### add_custom_type + + +> Search script examples for [add_custom_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=add_custom_type) + +#### [ENT_TYPE](#ENT_TYPE) add_custom_type(array<[ENT_TYPE](#ENT_TYPE)> types) + +Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn + ### change_poison_timer diff --git a/src/game_api/entity_lookup.cpp b/src/game_api/entity_lookup.cpp new file mode 100644 index 000000000..016f4b855 --- /dev/null +++ b/src/game_api/entity_lookup.cpp @@ -0,0 +1,315 @@ +#include "entity_lookup.hpp" + +#include +#include + +#include "aliases.hpp" +#include "custom_types.hpp" +#include "entity.hpp" +#include "layer.hpp" +#include "math.hpp" +#include "state.hpp" + +bool entity_type_check(const std::vector& types_array, const ENT_TYPE find) +{ + if (types_array.empty() || types_array[0] == 0 || std::find(types_array.begin(), types_array.end(), find) != types_array.end()) + return true; + + return false; +} + +std::vector get_proper_types(std::vector ent_types) +{ + for (size_t i = 0; i < ent_types.size(); ++i) + { + if (ent_types[i] >= (uint32_t)CUSTOM_TYPE::ACIDBUBBLE) + { + auto extra_types = get_custom_entity_types(static_cast(ent_types[i])); + if (extra_types.size() == 1) + { + ent_types[i] = extra_types[0]; + } + else if (!extra_types.empty()) + { + auto it = ent_types.begin() + i; + *it = extra_types[0]; + ent_types.insert(++it, extra_types.begin() + 1, extra_types.end()); + i += extra_types.size() - 1; + } + } + } + return ent_types; +} + +int32_t get_grid_entity_at(float x, float y, LAYER layer) +{ + auto state = State::get(); + uint8_t actual_layer = enum_to_layer(layer); + + if (Entity* ent = state.layer(actual_layer)->get_grid_entity_at(x, y)) + return ent->uid; + + return -1; +} + +std::vector get_entities() +{ + return get_entities_by({}, 0, LAYER::BOTH); +} + +std::vector get_entities_by_layer(LAYER layer) +{ + return get_entities_by({}, 0, layer); +} + +std::vector get_entities_by_type(std::vector entity_types) +{ + return get_entities_by(std::move(entity_types), 0, LAYER::BOTH); +} +std::vector get_entities_by_type(ENT_TYPE entity_type) +{ + return get_entities_by(std::vector{entity_type}, 0, LAYER::BOTH); +} + +std::vector get_entities_by_mask(uint32_t mask) +{ + return get_entities_by({}, mask, LAYER::BOTH); +} + +template +requires std::is_invocable_v +void foreach_mask(uint32_t mask, Layer* l, FunT&& fun) +{ + if (mask == 0) + { + fun(l->all_entities); + } + else + { + for (uint32_t test_flag = 1U; test_flag < 0x8000; test_flag <<= 1U) + { + if (mask & test_flag) + { + const auto& it = l->entities_by_mask.find(test_flag); + if (it != l->entities_by_mask.end()) + { + fun(it->second); + } + } + } + } +} + +std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer) +{ + auto state = State::get(); + std::vector found; + const std::vector proper_types = get_proper_types(std::move(entity_types)); + + auto push_matching_types = [&proper_types, &found](const EntityList& entities) + { + for (auto& item : entities.entities()) + { + if (entity_type_check(proper_types, item->type->id)) + { + found.push_back(item->uid); + } + } + }; + auto insert_all_uids = [&found](const EntityList& entities) + { + const auto uids = entities.uids(); + found.insert(found.end(), uids.begin(), uids.end()); + }; + + if (layer == LAYER::BOTH) + { + 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()); + } + else // all types + { + foreach_mask(mask, state.layer(0), insert_all_uids); + foreach_mask(mask, state.layer(1), insert_all_uids); + } + } + else + { + foreach_mask(mask, state.layer(0), push_matching_types); + foreach_mask(mask, state.layer(1), push_matching_types); + } + } + else + { + 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); + } + else + { + foreach_mask(mask, state.layer(correct_layer), push_matching_types); + } + } + return found; +} + +std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER layer) +{ + return get_entities_by(std::vector{entity_type}, mask, layer); +} + +std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius) +{ + auto state = State::get(); + std::vector found; + const std::vector proper_types = get_proper_types(std::move(entity_types)); + auto push_entities_at = [&x, &y, &radius, &proper_types, &found](const EntityList& entities) + { + for (auto& item : entities.entities()) + { + auto [ix, iy] = item->position(); + float distance = sqrt(pow(x - ix, 2.0f) + pow(y - iy, 2.0f)); + if (distance < radius && entity_type_check(proper_types, item->type->id)) + { + found.push_back(item->uid); + } + } + }; + if (layer == LAYER::BOTH) + { + foreach_mask(mask, state.layer(0), push_entities_at); + foreach_mask(mask, state.layer(1), push_entities_at); + } + else + { + foreach_mask(mask, state.layer(enum_to_layer(layer)), push_entities_at); + } + return found; +} + +std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float x, float y, LAYER layer, float radius) +{ + return get_entities_at(std::vector{entity_type}, mask, x, y, layer, radius); +} + +std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer) +{ + auto state = State::get(); + std::vector result; + const std::vector proper_types = get_proper_types(std::move(entity_types)); + if (layer == LAYER::BOTH) + { + std::vector result2; + result = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(0)); + result2 = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(1)); + result.insert(result.end(), result2.begin(), result2.end()); + } + else + { + uint8_t actual_layer = enum_to_layer(layer); + result = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(actual_layer)); + } + return result; +} +std::vector get_entities_overlapping_hitbox(ENT_TYPE entity_type, uint32_t mask, AABB hitbox, LAYER layer) +{ + return get_entities_overlapping_hitbox(std::vector{entity_type}, mask, hitbox, layer); +} + +std::vector get_entities_overlapping(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer) +{ + return get_entities_overlapping_hitbox(std::move(entity_types), mask, {sx, sy2, sx2, sy}, layer); +} +std::vector get_entities_overlapping(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer) +{ + return get_entities_overlapping_hitbox(std::vector{entity_type}, mask, {sx, sy2, sx2, sy}, layer); +} + +std::vector get_entities_overlapping_by_pointer(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer) +{ + std::vector found; + foreach_mask(mask, layer, [&entity_types, &found, &sx, &sy, &sx2, &sy2](const EntityList& entities) + { + for (auto& item : entities.entities()) + { + if (entity_type_check(std::move(entity_types), item->type->id) && item->overlaps_with(sx, sy, sx2, sy2)) + { + found.push_back(item->uid); + } + } }); + + return found; +} +std::vector get_entities_overlapping_by_pointer(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer) +{ + return get_entities_overlapping_by_pointer(std::vector{entity_type}, mask, sx, sy, sx2, sy2, layer); +} + +bool entity_has_item_uid(uint32_t uid, uint32_t item_uid) +{ + Entity* entity = get_entity_ptr(uid); + if (entity == nullptr) + return false; + + return entity->items.contains(item_uid); +}; + +bool entity_has_item_type(uint32_t uid, std::vector entity_types) +{ + Entity* entity = get_entity_ptr(uid); + if (entity == nullptr) + return false; + if (entity->items.size > 0) + { + const std::vector proper_types = get_proper_types(std::move(entity_types)); + for (auto item : entity->items.entities()) + { + if (entity_type_check(proper_types, item->type->id)) + return true; + } + } + return false; +} +bool entity_has_item_type(uint32_t uid, ENT_TYPE entity_type) +{ + return entity_has_item_type(uid, std::vector{entity_type}); +} + +std::vector entity_get_items_by(uint32_t uid, std::vector entity_types, uint32_t mask) +{ + std::vector found; + Entity* entity = get_entity_ptr(uid); + if (entity == nullptr) + return found; + if (entity->items.size > 0) + { + const std::vector proper_types = get_proper_types(std::move(entity_types)); + if ((!proper_types.size() || !proper_types[0]) && !mask) // all items + { + const auto uids = entity->items.uids(); + found.insert(found.end(), uids.begin(), uids.end()); + } + else + { + for (auto item : entity->items.entities()) + { + if ((mask == 0 || (item->type->search_flags & mask)) && entity_type_check(proper_types, item->type->id)) + { + found.push_back(item->uid); + } + } + } + } + return found; +} +std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask) +{ + return entity_get_items_by(uid, std::vector{entity_type}, mask); +} diff --git a/src/game_api/entity_lookup.hpp b/src/game_api/entity_lookup.hpp new file mode 100644 index 000000000..5bb74fd9d --- /dev/null +++ b/src/game_api/entity_lookup.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "aliases.hpp" +#include "math.hpp" + +struct Layer; + +int32_t get_grid_entity_at(float x, float y, LAYER layer); +std::vector get_entities(); +std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer); +std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER layer); +std::vector get_entities_by_type(std::vector entity_types); +std::vector get_entities_by_type(ENT_TYPE entity_type); +std::vector get_entities_by_mask(uint32_t mask); +std::vector get_entities_by_layer(LAYER layer); +std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius); +std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float x, float y, LAYER layer, float radius); +std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer); +std::vector get_entities_overlapping_hitbox(ENT_TYPE entity_type, uint32_t mask, AABB hitbox, LAYER layer); +std::vector get_entities_overlapping(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer); +std::vector get_entities_overlapping(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer); +std::vector get_entities_overlapping_by_pointer(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer); +std::vector get_entities_overlapping_by_pointer(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer); +bool entity_has_item_uid(uint32_t uid, uint32_t item_uid); +bool entity_has_item_type(uint32_t uid, std::vector entity_types); +bool entity_has_item_type(uint32_t uid, ENT_TYPE entity_type); +std::vector entity_get_items_by(uint32_t uid, std::vector entity_types, uint32_t mask); +std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask); diff --git a/src/game_api/layer.cpp b/src/game_api/layer.cpp index 68e52051d..c6efbf381 100644 --- a/src/game_api/layer.cpp +++ b/src/game_api/layer.cpp @@ -9,7 +9,6 @@ #include "entity.hpp" // for Entity, to_id, EntityDB, entity_factory #include "logger.h" // for DEBUG #include "movable.hpp" // for Movable -#include "rpc.hpp" // for entity_get_items_by #include "search.hpp" // for get_address #include "state.hpp" // for State, StateMemory @@ -164,18 +163,17 @@ Entity* Layer::spawn_apep(float x, float y, bool right) int current_uid = apep_head->uid; do { - auto body_parts = entity_get_items_by(current_uid, {}, 0); + auto body_parts = apep_head->items; int temp = current_uid; - for (auto body_part_uid : body_parts) + for (auto body_part : body_parts.entities()) { - Entity* body_part = get_entity_ptr(body_part_uid); body_part->flags = right ? body_part->flags & ~facing_left_flag : body_part->flags | facing_left_flag; body_part->x *= -1.0f; if (body_part->type->id == body_id) { - current_uid = body_part_uid; + current_uid = body_part->uid; } } if (temp == current_uid) diff --git a/src/game_api/layer.hpp b/src/game_api/layer.hpp index 8def12e0c..71441afad 100644 --- a/src/game_api/layer.hpp +++ b/src/game_api/layer.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include // for size_t #include // for uint32_t, int32_t, uint64_t, uint8_t #include // for less @@ -151,7 +152,7 @@ struct Layer EntityList entities_overlaping_grid[g_level_max_y][g_level_max_x]; // static entities (like midbg, decorations) that overlap this grid position EntityList unknown_entities2; - EntityList entities_by_draw_depth[53]; + std::array entities_by_draw_depth; EntityList unknown_entities3; // debris, explosions, laserbeams etc. ? EntityList unknown_entities4; // explosions, laserbeams, BG_LEVEL_*_SOOT ? only for short time while there are spawned? std::vector unknown_vector; // add_to_layer uses this diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 226e3e854..eb4769d4a 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -22,6 +22,7 @@ #include "entities_items.hpp" // #include "entities_monsters.hpp" // for GHOST_BEHAVIOR, GHOST_BEHAVIOR::MED... #include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... +#include "entity_lookup.hpp" // #include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x #include "logger.h" // for DEBUG #include "memory.hpp" // for to_le_bytes, write_mem_prot, Execut... diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 56977ea6c..388dfcdb2 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -27,6 +27,7 @@ #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 @@ -142,17 +143,6 @@ void stack_entities(uint32_t bottom_uid, uint32_t top_uid, const float (&offset) } } -int32_t get_grid_entity_at(float x, float y, LAYER layer) -{ - auto state = State::get(); - uint8_t actual_layer = enum_to_layer(layer); - - if (Entity* ent = state.layer(actual_layer)->get_grid_entity_at(x, y)) - return ent->uid; - - return -1; -} - void move_entity_abs(uint32_t uid, float x, float y, float vx, float vy) { auto ent = get_entity_ptr(uid); @@ -306,204 +296,6 @@ std::vector filter_entities(std::vector entities, std::funct return filtered_entities; } -std::vector get_entities() -{ - return get_entities_by({}, 0, LAYER::BOTH); -} - -std::vector get_entities_by_layer(LAYER layer) -{ - return get_entities_by({}, 0, layer); -} - -std::vector get_entities_by_type(std::vector entity_types) -{ - return get_entities_by(std::move(entity_types), 0, LAYER::BOTH); -} -std::vector get_entities_by_type(ENT_TYPE entity_type) -{ - return get_entities_by(std::vector{entity_type}, 0, LAYER::BOTH); -} - -std::vector get_entities_by_mask(uint32_t mask) -{ - return get_entities_by({}, mask, LAYER::BOTH); -} - -template -requires std::is_invocable_v -void foreach_mask(uint32_t mask, Layer* l, FunT&& fun) -{ - if (mask == 0) - { - fun(l->all_entities); - } - else - { - for (uint32_t test_flag = 1U; test_flag < 0x8000; test_flag <<= 1U) - { - if (mask & test_flag) - { - const auto& it = l->entities_by_mask.find(test_flag); - if (it != l->entities_by_mask.end()) - { - fun(it->second); - } - } - } - } -} - -std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer) -{ - auto state = State::get(); - std::vector found; - const std::vector proper_types = get_proper_types(std::move(entity_types)); - - auto push_matching_types = [&proper_types, &found](const EntityList& entities) - { - for (auto& item : entities.entities()) - { - if (entity_type_check(proper_types, item->type->id)) - { - found.push_back(item->uid); - } - } - }; - auto insert_all_uids = [&found](const EntityList& entities) - { - const auto uids = entities.uids(); - found.insert(found.end(), uids.begin(), uids.end()); - }; - - if (layer == LAYER::BOTH) - { - 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()); - } - else // all types - { - foreach_mask(mask, state.layer(0), insert_all_uids); - foreach_mask(mask, state.layer(1), insert_all_uids); - } - } - else - { - foreach_mask(mask, state.layer(0), push_matching_types); - foreach_mask(mask, state.layer(1), push_matching_types); - } - } - else - { - 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); - } - else - { - foreach_mask(mask, state.layer(correct_layer), push_matching_types); - } - } - return found; -} -std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER layer) -{ - return get_entities_by(std::vector{entity_type}, mask, layer); -} - -std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius) -{ - auto state = State::get(); - std::vector found; - const std::vector proper_types = get_proper_types(std::move(entity_types)); - auto push_entities_at = [&x, &y, &radius, &proper_types, &found](const EntityList& entities) - { - for (auto& item : entities.entities()) - { - auto [ix, iy] = item->position(); - float distance = sqrt(pow(x - ix, 2.0f) + pow(y - iy, 2.0f)); - if (distance < radius && entity_type_check(proper_types, item->type->id)) - { - found.push_back(item->uid); - } - } - }; - if (layer == LAYER::BOTH) - { - foreach_mask(mask, state.layer(0), push_entities_at); - foreach_mask(mask, state.layer(1), push_entities_at); - } - else - { - foreach_mask(mask, state.layer(enum_to_layer(layer)), push_entities_at); - } - return found; -} -std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float x, float y, LAYER layer, float radius) -{ - return get_entities_at(std::vector{entity_type}, mask, x, y, layer, radius); -} - -std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer) -{ - auto state = State::get(); - std::vector result; - const std::vector proper_types = get_proper_types(std::move(entity_types)); - if (layer == LAYER::BOTH) - { - std::vector result2; - result = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(0)); - result2 = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(1)); - result.insert(result.end(), result2.begin(), result2.end()); - } - else - { - uint8_t actual_layer = enum_to_layer(layer); - result = get_entities_overlapping_by_pointer(proper_types, mask, hitbox.left, hitbox.bottom, hitbox.right, hitbox.top, state.layer(actual_layer)); - } - return result; -} -std::vector get_entities_overlapping_hitbox(ENT_TYPE entity_type, uint32_t mask, AABB hitbox, LAYER layer) -{ - return get_entities_overlapping_hitbox(std::vector{entity_type}, mask, hitbox, layer); -} - -std::vector get_entities_overlapping(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer) -{ - return get_entities_overlapping_hitbox(std::move(entity_types), mask, {sx, sy2, sx2, sy}, layer); -} -std::vector get_entities_overlapping(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer) -{ - return get_entities_overlapping_hitbox(std::vector{entity_type}, mask, {sx, sy2, sx2, sy}, layer); -} - -std::vector get_entities_overlapping_by_pointer(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer) -{ - std::vector found; - foreach_mask(mask, layer, [&entity_types, &found, &sx, &sy, &sx2, &sy2](const EntityList& entities) - { - for (auto& item : entities.entities()) - { - if (entity_type_check(std::move(entity_types), item->type->id) && item->overlaps_with(sx, sy, sx2, sy2)) - { - found.push_back(item->uid); - } - } }); - - return found; -} -std::vector get_entities_overlapping_by_pointer(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer) -{ - return get_entities_overlapping_by_pointer(std::vector{entity_type}, mask, sx, sy, sx2, sy2, layer); -} - void set_door_target(uint32_t uid, uint8_t w, uint8_t l, uint8_t t) { if (auto door = get_entity_ptr(uid)->as()) @@ -553,68 +345,6 @@ void entity_remove_item(uint32_t uid, uint32_t item_uid) entity->remove_item(item_uid); } -bool entity_has_item_uid(uint32_t uid, uint32_t item_uid) -{ - Entity* entity = get_entity_ptr(uid); - if (entity == nullptr) - return false; - - return entity->items.contains(item_uid); -}; - -bool entity_has_item_type(uint32_t uid, std::vector entity_types) -{ - Entity* entity = get_entity_ptr(uid); - if (entity == nullptr) - return false; - if (entity->items.size > 0) - { - const std::vector proper_types = get_proper_types(std::move(entity_types)); - for (auto item : entity->items.entities()) - { - if (entity_type_check(proper_types, item->type->id)) - return true; - } - } - return false; -} -bool entity_has_item_type(uint32_t uid, ENT_TYPE entity_type) -{ - return entity_has_item_type(uid, std::vector{entity_type}); -} - -std::vector entity_get_items_by(uint32_t uid, std::vector entity_types, uint32_t mask) -{ - std::vector found; - Entity* entity = get_entity_ptr(uid); - if (entity == nullptr) - return found; - if (entity->items.size > 0) - { - const std::vector proper_types = get_proper_types(std::move(entity_types)); - if ((!proper_types.size() || !proper_types[0]) && !mask) // all items - { - const auto uids = entity->items.uids(); - found.insert(found.end(), uids.begin(), uids.end()); - } - else - { - for (auto item : entity->items.entities()) - { - if ((mask == 0 || (item->type->search_flags & mask)) && entity_type_check(proper_types, item->type->id)) - { - found.push_back(item->uid); - } - } - } - } - return found; -} -std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask) -{ - return entity_get_items_by(uid, std::vector{entity_type}, mask); -} - void lock_door_at(float x, float y) { std::vector items = get_entities_at({}, 0, x, y, LAYER::FRONT, 1); @@ -1331,37 +1061,6 @@ uint32_t waddler_entity_type_in_slot(uint8_t slot) return 0; } -bool entity_type_check(const std::vector& types_array, const ENT_TYPE find) -{ - if (types_array.empty() || types_array[0] == 0 || std::find(types_array.begin(), types_array.end(), find) != types_array.end()) - return true; - - return false; -} - -std::vector get_proper_types(std::vector ent_types) -{ - for (size_t i = 0; i < ent_types.size(); ++i) - { - if (ent_types[i] >= (uint32_t)CUSTOM_TYPE::ACIDBUBBLE) - { - auto extra_types = get_custom_entity_types(static_cast(ent_types[i])); - if (extra_types.size() == 1) - { - ent_types[i] = extra_types[0]; - } - else if (!extra_types.empty()) - { - auto it = ent_types.begin() + i; - *it = extra_types[0]; - ent_types.insert(++it, extra_types.begin() + 1, extra_types.end()); - i += extra_types.size() - 1; - } - } - } - return ent_types; -} - void enter_door(int32_t player_uid, int32_t door_uid) { auto player = get_entity_ptr(player_uid); diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index c41c896a1..12aaa9e7c 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -23,7 +23,6 @@ void attach_entity(Entity* overlay, Entity* attachee); void attach_entity_by_uid(uint32_t overlay_uid, uint32_t attachee_uid); int32_t attach_ball_and_chain(uint32_t uid, float off_x, float off_y); void stack_entities(uint32_t bottom_uid, uint32_t top_uid, const float (&offset)[2]); -int32_t get_grid_entity_at(float x, float y, LAYER layer); void move_entity_abs(uint32_t uid, float x, float y, float vx, float vy); void move_entity_abs(uint32_t uid, float x, float y, float vx, float vy, LAYER layer); void move_liquid_abs(uint32_t uid, float x, float y, float vx, float vy); @@ -39,30 +38,10 @@ std::vector get_players(StateMemory* state); std::tuple screen_aabb(float x1, float y1, float x2, float y2); float screen_distance(float x); std::vector filter_entities(std::vector entities, std::function predicate); -std::vector get_entities(); -std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer); -std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER layer); -std::vector get_entities_by_type(std::vector entity_types); -std::vector get_entities_by_type(ENT_TYPE entity_type); -std::vector get_entities_by_mask(uint32_t mask); -std::vector get_entities_by_layer(LAYER layer); -std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius); -std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float x, float y, LAYER layer, float radius); -std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer); -std::vector get_entities_overlapping_hitbox(ENT_TYPE entity_type, uint32_t mask, AABB hitbox, LAYER layer); -std::vector get_entities_overlapping(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer); -std::vector get_entities_overlapping(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, LAYER layer); -std::vector get_entities_overlapping_by_pointer(std::vector entity_types, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer); -std::vector get_entities_overlapping_by_pointer(ENT_TYPE entity_type, uint32_t mask, float sx, float sy, float sx2, float sy2, Layer* layer); void set_door_target(uint32_t uid, uint8_t w, uint8_t l, uint8_t t); std::tuple get_door_target(uint32_t uid); void set_contents(uint32_t uid, ENT_TYPE item_entity_type); void entity_remove_item(uint32_t uid, uint32_t item_uid); -bool entity_has_item_uid(uint32_t uid, uint32_t item_uid); -bool entity_has_item_type(uint32_t uid, std::vector entity_types); -bool entity_has_item_type(uint32_t uid, ENT_TYPE entity_type); -std::vector entity_get_items_by(uint32_t uid, std::vector entity_types, uint32_t mask); -std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask); void lock_door_at(float x, float y); void unlock_door_at(float x, float y); uint32_t get_frame_count_main(); diff --git a/src/game_api/script/lua_backend.cpp b/src/game_api/script/lua_backend.cpp index f64752a9d..1d4b432de 100644 --- a/src/game_api/script/lua_backend.cpp +++ b/src/game_api/script/lua_backend.cpp @@ -14,6 +14,7 @@ #include "constants.hpp" // for no_return_str #include "entities_chars.hpp" // for Player #include "entity.hpp" // for Entity, get_entity_ptr +#include "entity_lookup.hpp" // #include "handle_lua_function.hpp" // for handle_function #include "items.hpp" // for Inventory #include "level_api.hpp" // for LevelGenData, LevelGenSy... diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 090db0f06..0402657f0 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -35,6 +35,7 @@ #include "entities_chars.hpp" // for Player #include "entities_items.hpp" // for Container, Player... #include "entity.hpp" // for get_entity_ptr +#include "entity_lookup.hpp" // #include "game_manager.hpp" // for get_game_manager #include "handle_lua_function.hpp" // for handle_function #include "items.hpp" // for Inventory diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index d74586c2f..7835c6f1b 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -13,6 +13,7 @@ #include "entities_items.hpp" // for Torch #include "entities_mounts.hpp" // for Mount #include "entity.hpp" // for to_id, Entity, get_entity_ptr +#include "entity_lookup.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager #include "items.hpp" // for Items #include "layer.hpp" // for Layer, EntityList::Range, Entit... From 5a08dbeeba3af7703cf7165d7c478c6a64e082e7 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 15:59:41 +0200 Subject: [PATCH 05/33] add `get_entities_by_draw_depth` --- src/game_api/entity_lookup.cpp | 27 +++++++++++++++++++++++++++ src/game_api/entity_lookup.hpp | 2 ++ src/game_api/state.hpp | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/game_api/entity_lookup.cpp b/src/game_api/entity_lookup.cpp index 016f4b855..5b9273b0b 100644 --- a/src/game_api/entity_lookup.cpp +++ b/src/game_api/entity_lookup.cpp @@ -313,3 +313,30 @@ std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, ui { return entity_get_items_by(uid, std::vector{entity_type}, mask); } + +std::vector get_entities_by_draw_depth(uint8_t draw_depth, LAYER l) +{ + return get_entities_by_draw_depth(std::vector{draw_depth}, l); +} + +std::vector get_entities_by_draw_depth(std::vector draw_depths, LAYER l) +{ + auto state = State::get().ptr_local(); + std::vector found; + auto actual_layer = enum_to_layer(l); + for (auto draw_depth : draw_depths) + { + if (draw_depth > 52) + continue; + + auto uids_layer1 = state->layers[actual_layer]->entities_by_draw_depth[draw_depth].uids(); + found.insert(found.end(), uids_layer1.begin(), uids_layer1.end()); + + if (l == LAYER::BOTH) // if it's both, then the actual_layer is 0 + { + auto uids_layer2 = state->layers[1]->entities_by_draw_depth[draw_depth].uids(); + found.insert(found.end(), uids_layer2.begin(), uids_layer2.end()); + } + } + return found; +} diff --git a/src/game_api/entity_lookup.hpp b/src/game_api/entity_lookup.hpp index 5bb74fd9d..d9c67fa1e 100644 --- a/src/game_api/entity_lookup.hpp +++ b/src/game_api/entity_lookup.hpp @@ -29,3 +29,5 @@ bool entity_has_item_type(uint32_t uid, std::vector entity_types); bool entity_has_item_type(uint32_t uid, ENT_TYPE entity_type); std::vector entity_get_items_by(uint32_t uid, std::vector entity_types, uint32_t mask); std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask); +std::vector get_entities_by_draw_depth(uint8_t draw_depth, LAYER l); +std::vector get_entities_by_draw_depth(std::vector draw_depths, LAYER l); diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index aa5c4d2d6..d74a015b1 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -242,7 +242,7 @@ struct StateMemory Items* items; /// Entrance and exit coordinates, shop types and all themes LevelGenSystem* level_gen; - Layer* layers[2]; + std::array layers; /// Level logic like dice game and cutscenes LogicList* logic; /// NPC quest states From fa792bceb3b2de2d28c1bbb5ac568c33468d0b90 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:27:40 +0200 Subject: [PATCH 06/33] add `kill_recursive` and `destroy_recursive`, update doc --- docs/game_data/spel2.lua | 37 +++++++++++++++ docs/src/includes/_enums.md | 13 +++++ docs/src/includes/_globals.md | 12 +++++ docs/src/includes/_types.md | 4 ++ src/game_api/entity.cpp | 50 ++++++++++++++++++++ src/game_api/entity.hpp | 26 ++++++++++ src/game_api/script/lua_vm.cpp | 8 ++++ src/game_api/script/usertypes/entity_lua.cpp | 20 ++++++++ 8 files changed, 170 insertions(+) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 0dce27874..23cff9e7b 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1202,6 +1202,18 @@ function set_boss_door_control_enabled(enable) end ---@param types ENT_TYPE[] ---@return ENT_TYPE function add_custom_type(types) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depth integer +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depth, l) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depths integer[] +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depths, l) end ---@return boolean function toast_visible() end ---@return boolean @@ -2361,6 +2373,31 @@ function Entity:overlaps_with(rect_left, rect_bottom, rect_right, rect_top) end ---@param other Entity ---@return boolean function Entity:overlaps_with(other) end +---Kill entity along with all entities attached to it. Be aware that for example killing push block with this function will also kill anything on top of it, any items, players, monsters etc. +---To a that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check mask, if the entity doesn't match, it will look in the provided ENT_TYPE's +---destroy_corpse and responsible are the standard parameters for the kill funciton +---@param destroy_corpse boolean +---@param responsible Entity +---@param mask integer? +---@param ent_types ENT_TYPE[] +---@param rec_mode RECURSIVE_MODE +---@return nil +function Entity:kill_recursive(destroy_corpse, responsible, mask, ent_types, rec_mode) end +---Short for using RECURSIVE_MODE.NONE +---@param destroy_corpse boolean +---@param responsible Entity +---@return nil +function Entity:kill_recursive(destroy_corpse, responsible) end +---Destroy entity along with all entities attached to it. Be aware that for example destroying push block with this function will also destroy anything on top of it, any items, players, monsters etc. +---To a that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check the mask, if the entity doesn't match, it will look in the provided ENT_TYPE's +---@param mask integer? +---@param ent_types ENT_TYPE[] +---@param rec_mode RECURSIVE_MODE +---@return nil +function Entity:destroy_recursive(mask, ent_types, rec_mode) end +---Short for using RECURSIVE_MODE.NONE +---@return nil +function Entity:destroy_recursive() end ---@class Movable : Entity ---@field move Vec2 @{movex, movey} diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 0853597b3..a5ad75ede 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -897,6 +897,19 @@ Name | Data | Description [STAR_CHALLENGE_SPAWNED](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=QUEST_FLAG.STAR_CHALLENGE_SPAWNED) | 26 | [SUN_CHALLENGE_SPAWNED](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=QUEST_FLAG.SUN_CHALLENGE_SPAWNED) | 27 | +## RECURSIVE_MODE + + +> Search script examples for [RECURSIVE_MODE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=RECURSIVE_MODE) + + + +Name | Data | Description +---- | ---- | ----------- +[EXCLUSIVE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=RECURSIVE_MODE.EXCLUSIVE) | RECURSIVE_MODE::EXCLUSIVE | In this mode the provided [ENT_TYPE](#ENT_TYPE) and [MASK](#MASK) will not be affected nor will entities attached to them
+[INCLUSIVE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=RECURSIVE_MODE.INCLUSIVE) | RECURSIVE_MODE::INCLUSIVE | In this mode the provided [ENT_TYPE](#ENT_TYPE) and [MASK](#MASK) will be the only affected entities, anything outside of the specified mask or type will not be touched including entities attached to them
For this mode you have to specify at least one mask or [ENT_TYPE](#ENT_TYPE), otherwise nothing will be affected
+[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=RECURSIVE_MODE.NONE) | RECURSIVE_MODE::NONE | Ignores provided [ENT_TYPE](#ENT_TYPE) and [MASK](#MASK) and affects all the entities
+ ## RENDER_INFO_OVERRIDE diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index f4d5a0660..d59810b87 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -588,6 +588,18 @@ end Get uids of entities by some conditions ([ENT_TYPE](#ENT_TYPE), [MASK](#MASK)). Set `entity_type` or `mask` to `0` to ignore that, can also use table of entity_types. Recommended to always set the mask, even if you look for one entity type +### get_entities_by_draw_depth + + +> Search script examples for [get_entities_by_draw_depth](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_entities_by_draw_depth) + +#### array<int> get_entities_by_draw_depth(int draw_depth, [LAYER](#LAYER) l) + +#### array<int> get_entities_by_draw_depth(array draw_depths, [LAYER](#LAYER) l) + +Get uids of entities by draw_depth. Can also use table of draw_depths. +You can later use [filter_entities](#filter_entities) if you want specific entity + ### get_entities_by_type diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index e62cc7f36..7588e1755 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -3776,6 +3776,10 @@ nil | [set_invisible(bool value)](https://github.com/spelunky-fyi/overlunky/sear array<int> | [get_items()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_items) | bool | [is_in_liquid()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_in_liquid) | Returns true if entity is in water/lava bool | [is_cursed()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_cursed) | +nil | [kill_recursive(bool destroy_corpse, Entity responsible, optional mask, const array ent_types, RECURSIVE_MODE rec_mode)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=kill_recursive) | Kill entity along with all entities attached to it. Be aware that for example killing push block with this function will also kill anything on top of it, any items, players, monsters etc.
To a that, you can inclusively or exclusively limit certain [MASK](#MASK) and [ENT_TYPE](#ENT_TYPE). Note: the function will first check mask, if the entity doesn't match, it will look in the provided [ENT_TYPE](#ENT_TYPE)'s
destroy_corpse and responsible are the standard parameters for the kill funciton +nil | [kill_recursive(bool destroy_corpse, Entity responsible)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=kill_recursive) | Short for using [RECURSIVE_MODE](#RECURSIVE_MODE).NONE +nil | [destroy_recursive(optional mask, const array ent_types, RECURSIVE_MODE rec_mode)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=destroy_recursive) | Destroy entity along with all entities attached to it. Be aware that for example destroying push block with this function will also destroy anything on top of it, any items, players, monsters etc.
To a that, you can inclusively or exclusively limit certain [MASK](#MASK) and [ENT_TYPE](#ENT_TYPE). Note: the function will first check the mask, if the entity doesn't match, it will look in the provided [ENT_TYPE](#ENT_TYPE)'s +nil | [destroy_recursive()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=destroy_recursive) | Short for using [RECURSIVE_MODE](#RECURSIVE_MODE).NONE [CallbackId](#Aliases) | [set_pre_virtual(ENTITY_OVERRIDE entry, function fun)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_pre_virtual) | Hooks before the virtual function at index `entry`. [CallbackId](#Aliases) | [set_post_virtual(ENTITY_OVERRIDE entry, function fun)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_post_virtual) | Hooks after the virtual function at index `entry`. nil | [clear_virtual(CallbackId callback_id)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=clear_virtual) | Clears the hook given by `callback_id`, alternatively use `clear_callback()` inside the hook. diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index ffd860334..51662f604 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -476,3 +476,53 @@ void Movable::set_position(float to_x, float to_y) State::get().ptr()->camera->calculated_focus_y += dy; } } + +template +void recursive(Entity* ent, std::optional mask, std::vector ent_types, RECURSIVE_MODE rec_mode, F func) +{ + auto acutal_mask = [](uint32_t m) -> uint32_t // for the MASK.ANY + { return m == 0 ? 0xFFFF : m; }; + + if (rec_mode == RECURSIVE_MODE::EXCLUSIVE) + { + if (mask.has_value() && (acutal_mask(mask.value()) & ent->type->search_flags) != 0) + return; + + if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) != ent_types.end()) + return; + } + else if (rec_mode == RECURSIVE_MODE::INCLUSIVE) + { + if (mask.has_value() && (acutal_mask(mask.value()) & ent->type->search_flags) == 0) + { + if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) == ent_types.end()) + return; + } + else if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) == ent_types.end()) + return; + } + const std::vector items{ent->items.entities().begin(), ent->items.entities().end()}; + for (auto entity : items) + { + recursive(entity, mask, ent_types, rec_mode, func); + } + func(ent); +} + +void Entity::kill_recursive(bool destroy_corpse, Entity* responsible, std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode) +{ + auto kill_func = [destroy_corpse, &responsible](Entity* ent) -> void + { + ent->kill(destroy_corpse, responsible); + }; + recursive(this, mask, ent_types, rec_mode, kill_func); +} + +void Entity::destroy_recursive(std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode) +{ + auto destroy_func = [](Entity* ent) -> void + { + ent->destroy(); + }; + recursive(this, mask, ent_types, rec_mode, destroy_func); +} diff --git a/src/game_api/entity.hpp b/src/game_api/entity.hpp index 00b33ab06..1638bc7cb 100644 --- a/src/game_api/entity.hpp +++ b/src/game_api/entity.hpp @@ -3,6 +3,7 @@ #include // for size_t #include // for uint8_t, uint32_t, int32_t, uint16_t, int64_t #include // for function, equal_to +#include // #include // for span #include // for allocator, string #include // for string_view @@ -30,6 +31,13 @@ struct EntityHooksInfo; using ENT_FLAG = uint32_t; using ENT_MORE_FLAG = uint32_t; +enum class RECURSIVE_MODE +{ + EXCLUSIVE, + INCLUSIVE, + NONE, +}; + class Entity { public: @@ -210,6 +218,24 @@ class Entity std::span get_items(); + /// Kill entity along with all entities attached to it. Be aware that for example killing push block with this function will also kill anything on top of it, any items, players, monsters etc. + /// To avoid that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check mask, if the entity doesn't match, it will look in the provided ENT_TYPE's + /// destroy_corpse and responsible are the standard parameters for the kill funciton + void kill_recursive(bool destroy_corpse, Entity* responsible, std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode); + /// Short for using RECURSIVE_MODE.NONE + void kill_recursive(bool destroy_corpse, Entity* responsible) + { + kill_recursive(destroy_corpse, responsible, std::nullopt, {}, RECURSIVE_MODE::NONE); + }; + /// Destroy entity along with all entities attached to it. Be aware that for example destroying push block with this function will also destroy anything on top of it, any items, players, monsters etc. + /// To avoid that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check the mask, if the entity doesn't match, it will look in the provided ENT_TYPE's + void destroy_recursive(std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode); + /// Short for using RECURSIVE_MODE.NONE + void destroy_recursive() + { + destroy_recursive(std::nullopt, {}, RECURSIVE_MODE::NONE); + } + template T* as() { diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 0402657f0..8f8df7bb2 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2043,6 +2043,14 @@ end /// Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn lua["add_custom_type"] = add_custom_type; + auto get_entities_by_draw_depth = sol::overload( + static_cast (*)(uint8_t, LAYER)>(::get_entities_by_draw_depth), + static_cast (*)(std::vector, LAYER)>(::get_entities_by_draw_depth)); + + /// Get uids of entities by draw_depth. Can also use table of draw_depths. + /// You can later use [filter_entities](#filter_entities) if you want specific entity + lua["get_entities_by_draw_depth"] = get_entities_by_draw_depth; + 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/entity_lua.cpp b/src/game_api/script/usertypes/entity_lua.cpp index 52911cd4f..ae31d375b 100644 --- a/src/game_api/script/usertypes/entity_lua.cpp +++ b/src/game_api/script/usertypes/entity_lua.cpp @@ -207,6 +207,13 @@ void register_usertypes(sol::state& lua) static_cast(&Entity::overlaps_with), static_cast(&Entity::overlaps_with)); + auto kill_recursive = sol::overload( + static_cast(&Entity::kill_recursive), + static_cast, std::vector, RECURSIVE_MODE)>(&Entity::kill_recursive)); + auto destroy_recursive = sol::overload( + static_cast(&Entity::destroy_recursive), + static_cast, std::vector, RECURSIVE_MODE)>(&Entity::destroy_recursive)); + auto entity_type = lua.new_usertype("Entity"); entity_type["type"] = &Entity::type; entity_type["overlay"] = std::move(overlay); @@ -271,6 +278,8 @@ void register_usertypes(sol::state& lua) entity_type["get_items"] = &Entity::get_items; entity_type["is_in_liquid"] = &Entity::is_in_liquid; entity_type["is_cursed"] = &Entity::is_cursed; + entity_type["kill_recursive"] = kill_recursive; + entity_type["destroy_recursive"] = destroy_recursive; /* Entity // user_data // You can put any arbitrary lua object here for custom entities or player stats, which is then saved across level transitions for players and carried items, mounts etc... This field is local to the script and multiple scripts can write different things in the same entity. The data is saved right before ON.PRE_LOAD_SCREEN from a level and loaded right before ON.POST_LOAD_SCREEN to a level or transition. It is not available yet in post_entity_spawn, but that is a good place to initialize it for new custom entities. See example for more. @@ -360,6 +369,17 @@ void register_usertypes(sol::state& lua) lua["ENT_TYPE"][elm.second] = (uint32_t)elm.first; } + lua.create_named_table("RECURSIVE_MODE", "EXCLUSIVE", RECURSIVE_MODE::EXCLUSIVE, "INCLUSIVE", RECURSIVE_MODE::INCLUSIVE, "NONE", RECURSIVE_MODE::NONE); + /* RECURSIVE_MODE + // EXCLUSIVE + // In this mode the provided ENT_TYPE and MASK will not be affected nor will entities attached to them + // INCLUSIVE + // In this mode the provided ENT_TYPE and MASK will be the only affected entities, anything outside of the specified mask or type will not be touched including entities attached to them + // For this mode you have to specify at least one mask or ENT_TYPE, otherwise nothing will be affected + // NONE + // Ignores provided ENT_TYPE and MASK and affects all the entities + */ + lua.create_named_table("REPEAT_TYPE", "NO_REPEAT", REPEAT_TYPE::NoRepeat, "LINEAR", REPEAT_TYPE::Linear, "BACK_AND_FORTH", REPEAT_TYPE::BackAndForth); lua.create_named_table("SHAPE", "RECTANGLE", SHAPE::RECTANGLE, "CIRCLE", SHAPE::CIRCLE); lua.create_named_table("BUTTON", "JUMP", 1, "WHIP", 2, "BOMB", 4, "ROPE", 8, "RUN", 16, "DOOR", 32); From 241198e25e892a0efecff62431682f1c1c65c346 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 18:37:18 +0200 Subject: [PATCH 07/33] add special code for the mega jellyfish --- src/game_api/entity.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index 51662f604..23d2d4b4d 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -15,6 +15,7 @@ #include "containers/custom_map.hpp" // for custom_map #include "entities_chars.hpp" // for Player +#include "entities_monsters.hpp" // #include "entity_hooks_info.hpp" // for EntityHooksInfo #include "memory.hpp" // for write_mem_prot #include "movable.hpp" // for Movable @@ -506,6 +507,31 @@ void recursive(Entity* ent, std::optional mask, std::vector { recursive(entity, mask, ent_types, rec_mode, func); } + + { + static const ENT_TYPE jellys[] = { + to_id("ENT_TYPE_MONS_MEGAJELLYFISH"), + to_id("ENT_TYPE_MONS_MEGAJELLYFISH_BACKGROUND"), + }; + static const ENT_TYPE jellys_tails[] = { + to_id("ENT_TYPE_FX_MEGAJELLYFISH_TAIL"), + to_id("ENT_TYPE_FX_MEGAJELLYFISH_TAIL_BG"), + }; + + if (type->id == jellys[0] || type->id == jellys[1]) // special only for MEGAJELLYFISH + { + auto true_type = (MegaJellyfish*)this; + auto currend_uid = true_type->tail_bg_uid; + for (int idx = 0; idx < 8; ++idx) + { + auto tail_ent = get_entity_ptr(currend_uid + idx); + if (tail_ent != nullptr && (tail_ent->type->id == jellys_tails[0] || tail_ent->type->id == jellys_tails[1])) // only kill the tail + { + recursive(tail_ent, mask, ent_types, rec_mode, func); + } + } + } + } func(ent); } From 1410ab2eccae7c7792391df68361016dbf09d901 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 18:38:51 +0200 Subject: [PATCH 08/33] test code before pushing? --- src/game_api/entity.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index 23d2d4b4d..fef6b66fa 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -518,9 +518,9 @@ void recursive(Entity* ent, std::optional mask, std::vector to_id("ENT_TYPE_FX_MEGAJELLYFISH_TAIL_BG"), }; - if (type->id == jellys[0] || type->id == jellys[1]) // special only for MEGAJELLYFISH + if (ent->type->id == jellys[0] || ent->type->id == jellys[1]) // special only for MEGAJELLYFISH { - auto true_type = (MegaJellyfish*)this; + auto true_type = (MegaJellyfish*)ent; auto currend_uid = true_type->tail_bg_uid; for (int idx = 0; idx < 8; ++idx) { From 120ad5f5a1bf5282d145fcf879d4c8ae7d7c0900 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 20:34:08 +0200 Subject: [PATCH 09/33] make `kill_recursive` and `destroy_recursive` always affect the main entity --- src/game_api/entity.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index fef6b66fa..36c58aae3 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -479,7 +479,7 @@ void Movable::set_position(float to_x, float to_y) } template -void recursive(Entity* ent, std::optional mask, std::vector ent_types, RECURSIVE_MODE rec_mode, F func) +bool recursive(Entity* ent, std::optional mask, std::vector ent_types, RECURSIVE_MODE rec_mode, F func) { auto acutal_mask = [](uint32_t m) -> uint32_t // for the MASK.ANY { return m == 0 ? 0xFFFF : m; }; @@ -487,20 +487,20 @@ void recursive(Entity* ent, std::optional mask, std::vector if (rec_mode == RECURSIVE_MODE::EXCLUSIVE) { if (mask.has_value() && (acutal_mask(mask.value()) & ent->type->search_flags) != 0) - return; + return false; if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) != ent_types.end()) - return; + return false; } else if (rec_mode == RECURSIVE_MODE::INCLUSIVE) { if (mask.has_value() && (acutal_mask(mask.value()) & ent->type->search_flags) == 0) { if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) == ent_types.end()) - return; + return false; } else if (std::find(ent_types.begin(), ent_types.end(), ent->type->id) == ent_types.end()) - return; + return false; } const std::vector items{ent->items.entities().begin(), ent->items.entities().end()}; for (auto entity : items) @@ -533,6 +533,7 @@ void recursive(Entity* ent, std::optional mask, std::vector } } func(ent); + return true; } void Entity::kill_recursive(bool destroy_corpse, Entity* responsible, std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode) @@ -541,7 +542,8 @@ void Entity::kill_recursive(bool destroy_corpse, Entity* responsible, std::optio { ent->kill(destroy_corpse, responsible); }; - recursive(this, mask, ent_types, rec_mode, kill_func); + if (!recursive(this, mask, ent_types, rec_mode, kill_func)) + kill(destroy_corpse, responsible); } void Entity::destroy_recursive(std::optional mask, const std::vector ent_types, RECURSIVE_MODE rec_mode) @@ -550,5 +552,6 @@ void Entity::destroy_recursive(std::optional mask, const std::vectordestroy(); }; - recursive(this, mask, ent_types, rec_mode, destroy_func); + if (!recursive(this, mask, ent_types, rec_mode, destroy_func)) + destroy(); } From b60a0261aadda3476a2e54588880fed9b2e31d6e Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 7 Oct 2023 21:41:41 +0200 Subject: [PATCH 10/33] update doc --- docs/game_data/spel2.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 4065a5ad3..6f17503e7 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1219,6 +1219,22 @@ function set_frametime_unfocused(frametime) end ---Get engine target frametime when game is unfocused (1/framerate, default 1/33). ---@return double? function get_frametime_unfocused() end +---Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +---@param types ENT_TYPE[] +---@return ENT_TYPE +function add_custom_type(types) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depth integer +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depth, l) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depths integer[] +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depths, l) end ---@return boolean function toast_visible() end ---@return boolean From 2b00768cdf98f75ce9b62af6a6ca60b2b17c2b49 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:14:34 +0200 Subject: [PATCH 11/33] add `get_current_money` and `add_money_hud` functions --- docs/examples/add_money_hud.lua | 25 +++++++++++++++++++++++++ src/game_api/items.hpp | 4 ++-- src/game_api/render_api.cpp | 6 ++++++ src/game_api/render_api.hpp | 5 ++++- src/game_api/rpc.cpp | 20 ++++++++++++++++++++ src/game_api/rpc.hpp | 2 ++ src/game_api/script/lua_vm.cpp | 8 ++++++++ src/game_api/search.cpp | 9 +++++++++ 8 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 docs/examples/add_money_hud.lua diff --git a/docs/examples/add_money_hud.lua b/docs/examples/add_money_hud.lua new file mode 100644 index 000000000..cb0a0607d --- /dev/null +++ b/docs/examples/add_money_hud.lua @@ -0,0 +1,25 @@ + +function add_money(amount) + -- need to change the amount first, this alone would just change the amount of money in the HUD + -- without the little pop up below, showing how much money you gained + -- there is also state.money_last_levels but that's only used in the transition to display the difference + state.money_shop_total += amount + + -- it's actually subtracting the amount from the number on screen first for the effect + add_money_hud(amount) +end + +-- just another way of achieving the same thing +-- there doesn't seam to be difference between changing money in state.money_shop_total vs inventory.money +function add_money_slot(amount, slot) + -- check if the money will be negative after the transaction + -- it's actually fine for the game to have negative money, this is just example + if get_current_money() + amount >= 0 then + -- just another of way of changing the money + -- there is also inventory.collected_money_total but that's meant for the money last level, so no reason to edit it + state.items.player_inventory[slot].money += amount + add_money_hud(amount) + return true + end + return false +end diff --git a/src/game_api/items.hpp b/src/game_api/items.hpp index 4a3589fdf..a4402a39e 100644 --- a/src/game_api/items.hpp +++ b/src/game_api/items.hpp @@ -10,7 +10,7 @@ class Player; struct Inventory { /// Sum of the money collected in current level - uint32_t money; + int32_t money; uint8_t bombs; uint8_t ropes; /// Used to transfer information to transition/next level. Is not updated during a level @@ -92,7 +92,7 @@ struct Inventory /// You can use `ON.PRE_LEVEL_GENERATION` to access/edit this std::array acquired_powerups; /// Total money collected during previous levels (so excluding the current one) - uint32_t collected_money_total; + int32_t collected_money_total; }; struct SelectPlayerSlot diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index 8ea4ed966..c1081ce63 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -867,3 +867,9 @@ void TextRenderingInfo::rotate(float angle, std::optional px, std::option letter->top.C.x *= inverse_ratio; } } + +HudData* get_hud() +{ + static auto hud = (HudData*)get_address("hud"); + return hud; +} diff --git a/src/game_api/render_api.hpp b/src/game_api/render_api.hpp index 39978ca19..e6c19c5f9 100644 --- a/src/game_api/render_api.hpp +++ b/src/game_api/render_api.hpp @@ -408,7 +408,8 @@ struct HudMoney : HudElement { int32_t total; int32_t counter; - int32_t timer; + uint8_t timer; + // padding? }; struct HudData @@ -439,3 +440,5 @@ struct Hud float opacity; HudData* data; }; + +HudData* get_hud(); diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 03aabb590..65609e563 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1826,3 +1826,23 @@ ENT_TYPE add_custom_type(std::vector types) { return (ENT_TYPE)add_new_custom_type(std::move(types)); } + +int32_t get_current_money() +{ + auto state = State::get().ptr(); + int32_t money = state->money_shop_total; + for (auto& inventory : state->items->player_inventories) + { + money += inventory.money; + money += inventory.collected_money_total; + } + return money; +} + +int32_t add_money_hud(int32_t amount, std::optional display_time) +{ + auto hud = get_hud(); + hud->money.counter += amount; + hud->money.timer = display_time.value_or(0x3C); + return get_current_money(); +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 4693c60b5..a463d02dd 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -128,3 +128,5 @@ std::optional get_frametime(); void set_frametime_inactive(std::optional frametime); std::optional get_frametime_inactive(); ENT_TYPE add_custom_type(std::vector types); +int32_t get_current_money(); +int32_t add_money_hud(int32_t amount, std::optional display_time); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 3a5ca69a3..6dc8663fe 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2100,6 +2100,14 @@ end /// You can later use [filter_entities](#filter_entities) if you want specific entity lua["get_entities_by_draw_depth"] = get_entities_by_draw_depth; + /// Just convinet way of getting the current amount of money + /// short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] + lua["get_current_money"] = get_current_money; + + /// Display the effect of adding or subtracting money in the hud, does not actually change the amount of money + /// It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) + lua["add_money_hud"] = add_money_hud; + 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/search.cpp b/src/game_api/search.cpp index f27008208..4459f4dec 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -2026,6 +2026,15 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, + { + "hud"sv, + // you can get the address from the render_hud (first parameter), it's global/static, so just find good refrence to it + PatternCommandBuffer{} + .find_after_inst("41 C6 47 6B 01"_gh) + .find_inst("48 8D 0D"_gh) + .decode_pc() + .at_exe(), + }, }; std::unordered_map g_cached_addresses; From 480881f0bfe3cf26cfb9b466b3a5defbbf5c63d1 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:19:05 +0200 Subject: [PATCH 12/33] update doc --- docs/game_data/spel2.lua | 10 +++++++ docs/src/includes/_globals.md | 50 ++++++++++++++++++++++++++++++++++ src/game_api/script/lua_vm.cpp | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 6f17503e7..2034e0add 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1235,6 +1235,16 @@ function get_entities_by_draw_depth(draw_depth, l) end ---@param l LAYER ---@return integer[] function get_entities_by_draw_depth(draw_depths, l) end +---Just convenient way of getting the current amount of money +---short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] +---@return integer +function get_current_money() end +---Display the effect of adding or subtracting money in the hud, does not actually change the amount of money +---It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) +---@param amount integer +---@param display_time integer? +---@return integer +function add_money_hud(amount, display_time) end ---@return boolean function toast_visible() end ---@return boolean diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 53dacaec8..cf4b1c282 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1179,6 +1179,46 @@ default game value are: y_limit = 98.5, rising_speed_x = 0, rising_speed_y = 0.0 Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +### add_money_hud + + +```lua + +function add_money(amount) + -- need to change the amount first, this alone would just change the amount of money in the HUD + -- without the little pop up below, showing how much money you gained + -- there is also state.money_last_levels but that's only used in the transition to display the difference + state.money_shop_total += amount + + -- it's actually subtracting the amount from the number on screen first for the effect + add_money_hud(amount) +end + +-- just another way of achieving the same thing +-- there doesn't seam to be difference between changing money in state.money_shop_total vs inventory.money +function add_money_slot(amount, slot) + -- check if the money will be negative after the transaction + -- it's actually fine for the game to have negative money, this is just example + if get_current_money() + amount >= 0 then + -- just another of way of changing the money + -- there is also inventory.collected_money_total but that's meant for the money last level, so no reason to edit it + state.items.player_inventory[slot].money += amount + add_money_hud(amount) + return true + end + return false +end + +``` + + +> Search script examples for [add_money_hud](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=add_money_hud) + +#### int add_money_hud(int amount, optional display_time) + +Display the effect of adding or subtracting money in the hud, does not actually change the amount of money +It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) + ### change_poison_timer @@ -1271,6 +1311,16 @@ Get the current adventure seed pair Same as `Player.get_heart_color` +### get_current_money + + +> Search script examples for [get_current_money](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_current_money) + +#### int get_current_money() + +Just convenient way of getting the current amount of money +short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] + ### get_frame diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 6dc8663fe..2729085e9 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2100,7 +2100,7 @@ end /// You can later use [filter_entities](#filter_entities) if you want specific entity lua["get_entities_by_draw_depth"] = get_entities_by_draw_depth; - /// Just convinet way of getting the current amount of money + /// Just convenient way of getting the current amount of money /// short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] lua["get_current_money"] = get_current_money; From 7d339358c86ca0e510062a9776c6973369131c84 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:45:45 +0200 Subject: [PATCH 13/33] expose `get_hud` --- src/game_api/script/usertypes/vanilla_render_lua.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/script/usertypes/vanilla_render_lua.cpp b/src/game_api/script/usertypes/vanilla_render_lua.cpp index 7a569086b..3e3fcf425 100644 --- a/src/game_api/script/usertypes/vanilla_render_lua.cpp +++ b/src/game_api/script/usertypes/vanilla_render_lua.cpp @@ -943,6 +943,6 @@ void register_usertypes(sol::state& lua) hud_type["opacity"] = &Hud::opacity; hud_type["data"] = &Hud::data; - // TODO: maybe add a getter for HudData outside render_hud, I think it's always at 0x22e18020 + lua["get_hud"] = get_hud; }; } // namespace NVanillaRender From 4f5649f74b7cde036a70b876b28636e7556ba055 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 15:14:16 +0200 Subject: [PATCH 14/33] change the `add_money_hud` --- docs/examples/add_money_hud.lua | 25 ------------------------- src/game_api/rpc.cpp | 18 +++++++++++++++++- src/game_api/rpc.hpp | 3 ++- src/game_api/script/lua_vm.cpp | 10 +++++++--- 4 files changed, 26 insertions(+), 30 deletions(-) delete mode 100644 docs/examples/add_money_hud.lua diff --git a/docs/examples/add_money_hud.lua b/docs/examples/add_money_hud.lua deleted file mode 100644 index cb0a0607d..000000000 --- a/docs/examples/add_money_hud.lua +++ /dev/null @@ -1,25 +0,0 @@ - -function add_money(amount) - -- need to change the amount first, this alone would just change the amount of money in the HUD - -- without the little pop up below, showing how much money you gained - -- there is also state.money_last_levels but that's only used in the transition to display the difference - state.money_shop_total += amount - - -- it's actually subtracting the amount from the number on screen first for the effect - add_money_hud(amount) -end - --- just another way of achieving the same thing --- there doesn't seam to be difference between changing money in state.money_shop_total vs inventory.money -function add_money_slot(amount, slot) - -- check if the money will be negative after the transaction - -- it's actually fine for the game to have negative money, this is just example - if get_current_money() + amount >= 0 then - -- just another of way of changing the money - -- there is also inventory.collected_money_total but that's meant for the money last level, so no reason to edit it - state.items.player_inventory[slot].money += amount - add_money_hud(amount) - return true - end - return false -end diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 65609e563..34c26b205 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1839,9 +1839,25 @@ int32_t get_current_money() return money; } -int32_t add_money_hud(int32_t amount, std::optional display_time) +int32_t add_money(int32_t amount, std::optional display_time) { + auto state = State::get().ptr(); + auto hud = get_hud(); + state->money_shop_total += amount; + hud->money.counter += amount; + hud->money.timer = display_time.value_or(0x3C); + return get_current_money(); +} + +int32_t add_money_slot(int32_t amount, uint8_t player_slot, std::optional display_time) +{ + auto state = State::get().ptr(); auto hud = get_hud(); + uint8_t slot = player_slot - 1; + if (slot > 3) + return get_current_money(); + + state->items->player_inventories[slot].money += amount; hud->money.counter += amount; hud->money.timer = display_time.value_or(0x3C); return get_current_money(); diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index a463d02dd..d282c933e 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -129,4 +129,5 @@ void set_frametime_inactive(std::optional frametime); std::optional get_frametime_inactive(); ENT_TYPE add_custom_type(std::vector types); int32_t get_current_money(); -int32_t add_money_hud(int32_t amount, std::optional display_time); +int32_t add_money(int32_t amount, std::optional display_time); +int32_t add_money_slot(int32_t amount, uint8_t player_slot, std::optional display_time); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 2729085e9..3825ec943 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2104,9 +2104,13 @@ end /// short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] lua["get_current_money"] = get_current_money; - /// Display the effect of adding or subtracting money in the hud, does not actually change the amount of money - /// It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) - lua["add_money_hud"] = add_money_hud; + /// Adds money to the state.money_shop_total and displays the effect on the HUD for money change + /// Can be negative, default display_time = 60 (about 2s) + lua["add_money"] = add_money; + + /// Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change + /// Can be negative, default display_time = 60 (about 2s) + lua["add_money_slot"] = add_money_slot; 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); From 099d40b75ee1b06a80c1f8f83e8d1576b7dfadc1 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:27:28 +0200 Subject: [PATCH 15/33] support custom types in `kill_recursive` and `destroy_recursive` --- src/game_api/entity.cpp | 5 +++-- src/game_api/entity_lookup.hpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index 36c58aae3..c8bc6fd11 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -17,6 +17,7 @@ #include "entities_chars.hpp" // for Player #include "entities_monsters.hpp" // #include "entity_hooks_info.hpp" // for EntityHooksInfo +#include "entity_lookup.hpp" // #include "memory.hpp" // for write_mem_prot #include "movable.hpp" // for Movable #include "movable_behavior.hpp" // for MovableBehavior @@ -542,7 +543,7 @@ void Entity::kill_recursive(bool destroy_corpse, Entity* responsible, std::optio { ent->kill(destroy_corpse, responsible); }; - if (!recursive(this, mask, ent_types, rec_mode, kill_func)) + if (!recursive(this, mask, get_proper_types(ent_types), rec_mode, kill_func)) kill(destroy_corpse, responsible); } @@ -552,6 +553,6 @@ void Entity::destroy_recursive(std::optional mask, const std::vectordestroy(); }; - if (!recursive(this, mask, ent_types, rec_mode, destroy_func)) + if (!recursive(this, mask, get_proper_types(ent_types), rec_mode, destroy_func)) destroy(); } diff --git a/src/game_api/entity_lookup.hpp b/src/game_api/entity_lookup.hpp index d9c67fa1e..c774cbb0a 100644 --- a/src/game_api/entity_lookup.hpp +++ b/src/game_api/entity_lookup.hpp @@ -31,3 +31,4 @@ std::vector entity_get_items_by(uint32_t uid, std::vector en std::vector entity_get_items_by(uint32_t uid, ENT_TYPE entity_type, uint32_t mask); std::vector get_entities_by_draw_depth(uint8_t draw_depth, LAYER l); std::vector get_entities_by_draw_depth(std::vector draw_depths, LAYER l); +std::vector get_proper_types(std::vector ent_types); From 3a98f9c6212222255d19fb44d598368b5a7ad06c Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:27:39 +0200 Subject: [PATCH 16/33] add `enter_closed_door_crash` --- src/game_api/game_patches.cpp | 34 ++++++++++++++++++++++++++++++++++ src/game_api/game_patches.hpp | 1 + src/game_api/search.cpp | 8 ++++++++ src/game_api/state.cpp | 1 + 4 files changed, 44 insertions(+) diff --git a/src/game_api/game_patches.cpp b/src/game_api/game_patches.cpp index f71b096fd..c40919471 100644 --- a/src/game_api/game_patches.cpp +++ b/src/game_api/game_patches.cpp @@ -284,3 +284,37 @@ void set_skip_tiamat_cutscene(bool skip) else recover_mem("set_skip_tiamat_cutscene"); } + +void patch_entering_closed_door_crash() +{ + static bool once = false; + if (once) + return; + + size_t addr = get_address("enter_closed_door_crash"); + size_t return_addr; + { + auto memory = Memory::get(); + auto rva = find_inst(memory.exe(), "\x49\x39\xD4", addr - memory.exe_ptr, addr - memory.exe_ptr + 0x3F5, "patch_entering_closed_door_crash"); + if (rva == 0) + return; + size_t jump_addr = memory.at_exe(rva + 3); + size_t offset = memory_read(jump_addr + 2); + return_addr = jump_addr + 6 + offset; + } + std::string_view new_code{ + "\x48\x85\xC0"sv // test rax,rax + "\x74\x0D"sv // je + "\x48\x8B\x48\x08"sv // mov rcx,QWORD PTR [rax+0x8] // game code + "\x41\x8B\x47\x28"sv // mov eax,DWORD PTR [r15+0x28] // game code + "\xE9\x00\x00\x00\x00"sv // jmp (offset needs to be updated after we know the address) + }; + + auto new_code_addr = patch_and_redirect(addr, 8, new_code, true, return_addr); + if (new_code_addr == 0) + return; + + int32_t rel = static_cast((addr + 8) - (new_code_addr + 18)); + write_mem_prot(new_code_addr + 14, rel, true); + once = true; +} diff --git a/src/game_api/game_patches.hpp b/src/game_api/game_patches.hpp index 0adbc7548..78e5fd14b 100644 --- a/src/game_api/game_patches.hpp +++ b/src/game_api/game_patches.hpp @@ -6,3 +6,4 @@ void patch_liquid_OOB(); void set_skip_olmec_cutscene(bool skip); void patch_tiamat_kill_crash(); void set_skip_tiamat_cutscene(bool skip); +void patch_entering_closed_door_crash(); diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 4459f4dec..1b5f2026f 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -2035,6 +2035,14 @@ std::unordered_map g_address_rules{ .decode_pc() .at_exe(), }, + { + "enter_closed_door_crash"sv, + // third virtual in behavior of the dog in walking state, the exact line crashing the game when pet tries to enter closed door (tiamat/hundun) + PatternCommandBuffer{} + .find_after_inst("FF 90 A8 00 00 00 48 89 F1"_gh) + .offset(0x5) + .at_exe(), + }, }; std::unordered_map g_cached_addresses; diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 7a4dd4dc6..2cb5da1d0 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -300,6 +300,7 @@ State& State::get() patch_orbs_limit(); patch_olmec_kill_crash(); patch_liquid_OOB(); + patch_entering_closed_door_crash(); } else { From fea192fb39b3905ff9dd01cacf7fe089cf40fd13 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 23:31:50 +0200 Subject: [PATCH 17/33] expose `main_menu_music`, fix `LevelGenSystem` in doc --- docs/game_data/spel2.lua | 19 +++++-- docs/src/includes/_globals.md | 50 +++++++------------ docs/src/includes/_types.md | 6 ++- src/game_api/level_api.hpp | 2 +- .../script/usertypes/game_manager_lua.cpp | 1 + src/game_api/script/usertypes/level_lua.cpp | 2 +- 6 files changed, 43 insertions(+), 37 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 2034e0add..4f8ee5cc5 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1239,12 +1239,19 @@ function get_entities_by_draw_depth(draw_depths, l) end ---short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] ---@return integer function get_current_money() end ----Display the effect of adding or subtracting money in the hud, does not actually change the amount of money ----It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) +---Adds money to the state.money_shop_total and displays the effect on the HUD for money change +---Can be negative, default display_time = 60 (about 2s) ---@param amount integer ---@param display_time integer? ---@return integer -function add_money_hud(amount, display_time) end +function add_money(amount, display_time) end +---Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change +---Can be negative, default display_time = 60 (about 2s) +---@param amount integer +---@param player_slot integer +---@param display_time integer? +---@return integer +function add_money_slot(amount, player_slot, display_time) end ---@return boolean function toast_visible() end ---@return boolean @@ -1571,6 +1578,8 @@ function set_lut(texture_id, layer) end ---@param layer LAYER ---@return nil function reset_lut(layer) end +---@return HudData +function get_hud() end ---Alters the drop chance for the provided monster-item combination (use e.g. set_drop_chance(DROPCHANCE.MOLE_MATTOCK, 10) for a 1 in 10 chance) ---Use `-1` as dropchance_id to reset all to default ---@param dropchance_id integer @@ -2175,6 +2184,7 @@ do ---@field pause_ui PauseUI ---@field journal_ui JournalUI ---@field save_related SaveRelated + ---@field main_menu_music BackgroundSound ---@class SaveRelated ---@field journal_popup_ui JournalPopupUI @@ -4558,6 +4568,9 @@ function MovableBehavior:get_state_id() end ---@field spawn_room_y integer ---@field exit_doors custom_Array ---@field themes ThemeInfo[] @size: 18 + ---@field flags integer + ---@field flags2 integer + ---@field flags3 integer ---@class PostRoomGenerationContext ---@field set_room_template fun(self, x: integer, y: integer, layer: LAYER, room_template: ROOM_TEMPLATE): boolean @Set the room template at the given index and layer, returns `false` if the index is outside of the level. diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index cf4b1c282..72d13daad 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1179,45 +1179,25 @@ default game value are: y_limit = 98.5, rising_speed_x = 0, rising_speed_y = 0.0 Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn -### add_money_hud +### add_money -```lua - -function add_money(amount) - -- need to change the amount first, this alone would just change the amount of money in the HUD - -- without the little pop up below, showing how much money you gained - -- there is also state.money_last_levels but that's only used in the transition to display the difference - state.money_shop_total += amount - - -- it's actually subtracting the amount from the number on screen first for the effect - add_money_hud(amount) -end +> Search script examples for [add_money](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=add_money) --- just another way of achieving the same thing --- there doesn't seam to be difference between changing money in state.money_shop_total vs inventory.money -function add_money_slot(amount, slot) - -- check if the money will be negative after the transaction - -- it's actually fine for the game to have negative money, this is just example - if get_current_money() + amount >= 0 then - -- just another of way of changing the money - -- there is also inventory.collected_money_total but that's meant for the money last level, so no reason to edit it - state.items.player_inventory[slot].money += amount - add_money_hud(amount) - return true - end - return false -end +#### int add_money(int amount, optional display_time) -``` +Adds money to the state.money_shop_total and displays the effect on the HUD for money change +Can be negative, default display_time = 60 (about 2s) + +### add_money_slot -> Search script examples for [add_money_hud](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=add_money_hud) +> Search script examples for [add_money_slot](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=add_money_slot) -#### int add_money_hud(int amount, optional display_time) +#### int add_money_slot(int amount, int player_slot, optional display_time) -Display the effect of adding or subtracting money in the hud, does not actually change the amount of money -It actually subtracts the amount of money first for the effect, look at the example, default display_time = 60 (about 2s) +Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change +Can be negative, default display_time = 60 (about 2s) ### change_poison_timer @@ -1348,6 +1328,14 @@ Get engine target frametime (1/framerate, default 1/60). Get engine target frametime when game is unfocused (1/framerate, default 1/33). +### get_hud + + +> Search script examples for [get_hud](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_hud) + +#### [HudData](#HudData) get_hud() + + ### get_id diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index b7608905c..e2a61e6eb 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -1198,7 +1198,10 @@ float | [spawn_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn int | [spawn_room_x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_room_x) | int | [spawn_room_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_room_y) | custom_array<[Vec2](#Vec2)> | [exit_doors](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=exit_doors) | -[ThemeInfo](#ThemeInfo) | [themes[18]](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=themes) | +array<[ThemeInfo](#ThemeInfo), 18> | [themes](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=themes) | +int | [flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags) | +int | [flags2](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags2) | +int | [flags3](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags3) | ## Lighting types @@ -2471,6 +2474,7 @@ Type | Name | Description [PauseUI](#PauseUI) | [pause_ui](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause_ui) | [JournalUI](#JournalUI) | [journal_ui](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_ui) | [SaveRelated](#SaveRelated) | [save_related](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=save_related) | +[BackgroundSound](#BackgroundSound) | [main_menu_music](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=main_menu_music) | ### GameProps diff --git a/src/game_api/level_api.hpp b/src/game_api/level_api.hpp index cd6d900b3..87d40f9e4 100644 --- a/src/game_api/level_api.hpp +++ b/src/game_api/level_api.hpp @@ -436,7 +436,7 @@ struct LevelGenSystem uint64_t unknown2; union { - ThemeInfo* themes[18]; + std::array themes; struct { ThemeInfo* theme_dwelling; diff --git a/src/game_api/script/usertypes/game_manager_lua.cpp b/src/game_api/script/usertypes/game_manager_lua.cpp index 2ab569002..f16cbee0a 100644 --- a/src/game_api/script/usertypes/game_manager_lua.cpp +++ b/src/game_api/script/usertypes/game_manager_lua.cpp @@ -69,6 +69,7 @@ void register_usertypes(sol::state& lua) gamemanager_type["pause_ui"] = &GameManager::pause_ui; gamemanager_type["journal_ui"] = &GameManager::journal_ui; gamemanager_type["save_related"] = &GameManager::save_related; + gamemanager_type["main_menu_music"] = &GameManager::main_menu_music; lua.new_usertype( "SaveRelated", diff --git a/src/game_api/script/usertypes/level_lua.cpp b/src/game_api/script/usertypes/level_lua.cpp index 40eb8226b..797940fb2 100644 --- a/src/game_api/script/usertypes/level_lua.cpp +++ b/src/game_api/script/usertypes/level_lua.cpp @@ -1398,7 +1398,7 @@ void register_usertypes(sol::state& lua) &LevelGenSystem::exit_doors, "themes", sol::property([](LevelGenSystem& lgs) - { return std::ref(lgs.themes); }), + { return std::ref(lgs.themes) /**/; }), "flags", &LevelGenSystem::flags, "flags2", From ceaf70f0d48bd11648424560fbc28661d8cec781 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 8 Oct 2023 23:44:59 +0200 Subject: [PATCH 18/33] so, not needed anymore --- src/game_api/script/usertypes/level_lua.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game_api/script/usertypes/level_lua.cpp b/src/game_api/script/usertypes/level_lua.cpp index 797940fb2..c213c903e 100644 --- a/src/game_api/script/usertypes/level_lua.cpp +++ b/src/game_api/script/usertypes/level_lua.cpp @@ -1397,8 +1397,7 @@ void register_usertypes(sol::state& lua) "exit_doors", &LevelGenSystem::exit_doors, "themes", - sol::property([](LevelGenSystem& lgs) - { return std::ref(lgs.themes) /**/; }), + &LevelGenSystem::themes, "flags", &LevelGenSystem::flags, "flags2", From 19a3a951a1e86499c98dbf82710ba096efa904e8 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 9 Oct 2023 00:07:37 +0200 Subject: [PATCH 19/33] add info about the return of add_money --- docs/game_data/spel2.lua | 4 ++-- docs/src/includes/_globals.md | 4 ++-- src/game_api/script/lua_vm.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 4f8ee5cc5..199d3d9f2 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1240,13 +1240,13 @@ function get_entities_by_draw_depth(draw_depths, l) end ---@return integer function get_current_money() end ---Adds money to the state.money_shop_total and displays the effect on the HUD for money change ----Can be negative, default display_time = 60 (about 2s) +---Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction ---@param amount integer ---@param display_time integer? ---@return integer function add_money(amount, display_time) end ---Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change ----Can be negative, default display_time = 60 (about 2s) +---Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction ---@param amount integer ---@param player_slot integer ---@param display_time integer? diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 72d13daad..112d88f94 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1187,7 +1187,7 @@ Adds new custom type (group of ENT_TYPE) that can be later used in functions lik #### int add_money(int amount, optional display_time) Adds money to the state.money_shop_total and displays the effect on the HUD for money change -Can be negative, default display_time = 60 (about 2s) +Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction ### add_money_slot @@ -1197,7 +1197,7 @@ Can be negative, default display_time = 60 (about 2s) #### int add_money_slot(int amount, int player_slot, optional display_time) Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change -Can be negative, default display_time = 60 (about 2s) +Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction ### change_poison_timer diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 3825ec943..3266d1052 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2105,11 +2105,11 @@ end lua["get_current_money"] = get_current_money; /// Adds money to the state.money_shop_total and displays the effect on the HUD for money change - /// Can be negative, default display_time = 60 (about 2s) + /// Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction lua["add_money"] = add_money; /// Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change - /// Can be negative, default display_time = 60 (about 2s) + /// Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction lua["add_money_slot"] = add_money_slot; 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); From e0cdc3cbd14401fed40386e96b9b7ad98ac4c167 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:44:44 +0200 Subject: [PATCH 20/33] fix `drill:trigger()` not making any noise and add optional bool for the activation sound effect --- docs/game_data/spel2.lua | 2 +- docs/src/includes/_types.md | 2 +- src/game_api/entities_activefloors.cpp | 6 +++++- src/game_api/entities_activefloors.hpp | 2 +- src/game_api/sound_manager.cpp | 15 ++++++++++----- src/game_api/sound_manager.hpp | 1 + 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 199d3d9f2..eeabd878e 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -2822,7 +2822,7 @@ function Movable:generic_update_world(move, sprint_factor, disable_gravity, on_r ---@field sound1 SoundMeta ---@field sound2 SoundMeta ---@field top_chain_piece Entity - ---@field trigger fun(self): nil + ---@field trigger fun(self, play_sound_effect: boolean?): nil ---@class ThinIce : Movable ---@field strength integer @counts down when standing on, maximum is 134 as based of this value it changes animation_frame, and above that value it changes to wrong sprite diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index e2a61e6eb..f72ce459a 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -5665,7 +5665,7 @@ Type | Name | Description [SoundMeta](#SoundMeta) | [sound1](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=sound1) | [SoundMeta](#SoundMeta) | [sound2](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=sound2) | [Entity](#Entity) | [top_chain_piece](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=top_chain_piece) | -nil | [trigger()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=trigger) | +nil | [trigger(optional play_sound_effect)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=trigger) | ### DummyPurchasableEntity diff --git a/src/game_api/entities_activefloors.cpp b/src/game_api/entities_activefloors.cpp index 145540261..05d50ffdb 100644 --- a/src/game_api/entities_activefloors.cpp +++ b/src/game_api/entities_activefloors.cpp @@ -23,7 +23,7 @@ uint8_t Olmec::broken_floaters() return broken; } -void Drill::trigger() +void Drill::trigger(std::optional play_sound_effect) { if (move_state != 0 || standing_on_uid != -1) { @@ -41,5 +41,9 @@ void Drill::trigger() using construct_soundposition_ptr_fun_t = SoundMeta*(uint32_t id, bool background_sound); static const auto construct_soundposition_ptr_call = (construct_soundposition_ptr_fun_t*)get_address("construct_soundmeta"); sound1 = construct_soundposition_ptr_call(0x159, 0); + sound1->start(); sound2 = construct_soundposition_ptr_call(0x153, 0); + sound2->start(); + if (play_sound_effect.value_or(false)) + play_sound_by_id(0xA4, uid); } diff --git a/src/game_api/entities_activefloors.hpp b/src/game_api/entities_activefloors.hpp index 7be149e3a..975354d81 100644 --- a/src/game_api/entities_activefloors.hpp +++ b/src/game_api/entities_activefloors.hpp @@ -113,7 +113,7 @@ class Drill : public Movable Entity* top_chain_piece; uint8_t unknown1; // it's forced to 0, for whatever reason - void trigger(); + void trigger(std::optional play_sound_effect); }; class ThinIce : public Movable diff --git a/src/game_api/sound_manager.cpp b/src/game_api/sound_manager.cpp index 75bfd3991..9b4adbe75 100644 --- a/src/game_api/sound_manager.cpp +++ b/src/game_api/sound_manager.cpp @@ -894,17 +894,16 @@ int32_t sound_name_to_id(const VANILLA_SOUND s_name) return -1; } -SoundMeta* play_sound(VANILLA_SOUND sound, uint32_t source_uid) +SoundMeta* play_sound_by_id(uint32_t sound_id, uint32_t source_uid) { + if (sound_id == -1) + return nullptr; + using play_sound = SoundMeta*(int32_t); static auto play_sound_func = (play_sound*)get_address("play_sound"); Entity* source = get_entity_ptr(source_uid); SoundMeta* sound_info{nullptr}; - auto sound_id = sound_name_to_id(sound); - - if (sound_id == -1) - return nullptr; if (source_uid == ~0 || source != nullptr) // don't play the sound if the entity is not valid { @@ -914,3 +913,9 @@ SoundMeta* play_sound(VANILLA_SOUND sound, uint32_t source_uid) } return sound_info; } + +SoundMeta* play_sound(VANILLA_SOUND sound, uint32_t source_uid) +{ + auto sound_id = sound_name_to_id(sound); + return play_sound_by_id(sound_id, source_uid); +} diff --git a/src/game_api/sound_manager.hpp b/src/game_api/sound_manager.hpp index e1e41d137..48c3e3c5a 100644 --- a/src/game_api/sound_manager.hpp +++ b/src/game_api/sound_manager.hpp @@ -294,3 +294,4 @@ struct BackgroundSound : public SoundMeta /// Use source_uid to make the sound be played at the location of that entity, set it -1 to just play it "everywhere" /// Returns SoundMeta (read only), beware that after the sound starts, that memory is no longer valid SoundMeta* play_sound(VANILLA_SOUND sound, uint32_t source_uid); +SoundMeta* play_sound_by_id(uint32_t sound_id, uint32_t source_uid); From 2cf25828e3e5a26d39fe4ef16182b005878ace7d Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:19:09 +0200 Subject: [PATCH 21/33] add helper function `get_nop` --- src/game_api/memory.cpp | 54 ++++++++++++++++++++++++++++++++++++++++- src/game_api/memory.hpp | 1 + 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/game_api/memory.cpp b/src/game_api/memory.cpp index 482e6ac80..4215e8f8c 100644 --- a/src/game_api/memory.cpp +++ b/src/game_api/memory.cpp @@ -195,7 +195,59 @@ size_t patch_and_redirect(size_t addr, size_t replace_size, const std::string_vi VirtualProtect(new_code, new_memory_size, PAGE_EXECUTE_READ, &dummy); int32_t rel = static_cast((size_t)new_code - (addr + jump_size)); - const std::string redirect_code = fmt::format("\xE9{}{}"sv, to_le_bytes(rel), std::string(replace_size - jump_size, '\x90')); + const std::string redirect_code = fmt::format("\xE9{}{}"sv, to_le_bytes(rel), get_nop(replace_size - jump_size)); write_mem_prot(addr, redirect_code, true); return (size_t)new_code; } + +std::string get_nop(size_t size, bool true_nop) +{ + if (true_nop) + return std::string(size, '\x90'); + + switch (size) + { + case 0: + return ""; + case 1: + return "\x90"s; + case 2: + return "\x66\x90"s; + case 3: + return "\x0F\x1F\x00"s; + case 4: + return "\x0F\x1F\x40\x00"s; + case 5: + return "\x0F\x1F\x44\x00\x00"s; + case 6: + return "\x66\x0F\x1F\x44\x00\x00"s; + case 7: + return "\x0F\x1F\x80\x00\x00\x00\x00"s; + case 8: + return "\x0F\x1F\x84\x00\x00\x00\x00\x00"s; + case 9: + return "\x66\x0F\x1F\x84\x00\x00\x00\x00\x00"s; + case 10: + return "\x66\x2E\x0F\x1F\x84\x00\x00\x00\x00\x00"s; + default: + { + std::string ret_str; + size_t remaning = size; + + for (uint8_t idx = 10; idx > 0; --idx) + { + size_t d_t = remaning / idx; + if (d_t > 0) + { + std::string c_nop = get_nop(idx); + for (; d_t > 0; --d_t) + { + ret_str += c_nop; + remaning -= idx; + } + } + } + return ret_str; + } + } +} diff --git a/src/game_api/memory.hpp b/src/game_api/memory.hpp index 27eaab119..a22248f57 100644 --- a/src/game_api/memory.hpp +++ b/src/game_api/memory.hpp @@ -95,6 +95,7 @@ void write_mem(size_t addr, std::string payload); size_t function_start(size_t off, uint8_t outside_byte = '\xcc'); void write_mem_recoverable(std::string name, size_t addr, std::string_view payload, bool prot); void recover_mem(std::string name, size_t addr = NULL); +std::string get_nop(size_t size, bool true_nop = false); // similar to ExecutableMemory but writes automatic jump from and back, moves the code it replaces etc. // it needs at least 5 bytes to move, use just_nop = true to nuke the oryginal code From f6d1b33eefe57a56cbc5ed94cf4caa5056950d48 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:13:04 +0200 Subject: [PATCH 22/33] expose `items.players` (why not), also no need for `std::ref` --- docs/game_data/spel2.lua | 3 +- docs/src/includes/_types.md | 3 +- src/game_api/items.hpp | 1 + src/game_api/script/usertypes/state_lua.cpp | 39 ++++++++------------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index eeabd878e..c20dc008f 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1892,8 +1892,9 @@ do ---@field is_pet_cursed boolean[] @size: 4 ---@field is_pet_poisoned boolean[] @size: 4 ---@field leader integer @Index of leader player in coop - ---@field player_inventory Inventory[] @size: MAX_PLAYERS ---@field player_select SelectPlayerSlot[] @size: MAX_PLAYERS + ---@field player_inventory Inventory[] @size: MAX_PLAYERS + ---@field players Player[] @size: MAX_PLAYERS @Array of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body) ---@class LiquidPhysicsEngine ---@field pause boolean diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index f72ce459a..0b178e926 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -2496,8 +2496,9 @@ array<[ENT_TYPE](#ENT_TYPE), 4> | [saved_pets](https://github.com/spelunky array<bool, 4> | [is_pet_cursed](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_pet_cursed) | array<bool, 4> | [is_pet_poisoned](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_pet_poisoned) | int | [leader](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=leader) | Index of leader player in coop -array<[Inventory](#Inventory), MAX_PLAYERS> | [player_inventory](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_inventory) | array<[SelectPlayerSlot](#SelectPlayerSlot), MAX_PLAYERS> | [player_select](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_select) | +array<[Inventory](#Inventory), MAX_PLAYERS> | [player_inventory](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_inventory) | +array<[Player](#Player), MAX_PLAYERS> | [players](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=players) | Table of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body) ### JournalProgressStainSlot diff --git a/src/game_api/items.hpp b/src/game_api/items.hpp index a4402a39e..e8e4c88d4 100644 --- a/src/game_api/items.hpp +++ b/src/game_api/items.hpp @@ -113,6 +113,7 @@ struct Items uint8_t unknown2; uint8_t unknown3; uint32_t unknown; // padding probably + /// Table of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body) std::array players; std::array player_inventories; std::array player_select_slots; diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index e4f65b7b9..3f94b8438 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -12,14 +12,15 @@ #include // for move, declval, decay_t, reference_... #include // for min, max -#include "entity.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 -#include "screen.hpp" // IWYU pragma: keep -#include "screen_arena.hpp" // IWYU pragma: keep -#include "state.hpp" // for StateMemory, State, StateMemory::a... -#include "state_structs.hpp" // for ArenaConfigArenas, ArenaConfigItems +#include "entities_chars.hpp" // IWYU pragma: keep +#include "entity.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 +#include "screen.hpp" // IWYU pragma: keep +#include "screen_arena.hpp" // IWYU pragma: keep +#include "state.hpp" // for StateMemory, State, StateMemory::a... +#include "state_structs.hpp" // for ArenaConfigArenas, ArenaConfigItems namespace NState { @@ -199,6 +200,7 @@ void register_usertypes(sol::state& lua) &SelectPlayerSlot::character, "texture", &SelectPlayerSlot::texture_id); + /// Used in StateMemory lua.new_usertype( "Items", @@ -212,27 +214,17 @@ void register_usertypes(sol::state& lua) &Items::is_pet_cursed, "is_pet_poisoned", &Items::is_pet_poisoned, - - // had to be done this way as autodoc doesn't like sol::property stuff - /*"leader", - &Items::leader,*/ - /*"player_inventory", - &Items::player_inventories,*/ - /*"player_select", - &Items::player_select_slots,*/ - //); stop autodoc here - "leader", sol::property([](Items& s) -> uint8_t { return s.leader + 1; }, [](Items& s, uint8_t leader) { s.leader = leader - 1; }), "player_select", - sol::property([](Items& s) - { return std::ref(s.player_select_slots); }), + &Items::player_select_slots, "player_inventory", - sol::property([](Items& s) - { return std::ref(s.player_inventories); })); + &Items::player_inventories, + "players", + &Items::players); /// Used in LiquidPool lua.new_usertype( @@ -275,8 +267,7 @@ void register_usertypes(sol::state& lua) lua.new_usertype( "LiquidPhysics", "pools", - sol::property([](LiquidPhysics& lp) - { return std::ref(lp.pools) /**/; })); + &LiquidPhysics::pools); lua.create_named_table( "LIQUID_POOL", From e957c113c5deb8b0441f2874252e1b7561309817 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:50:18 +0200 Subject: [PATCH 23/33] add rpc back --- src/game_api/layer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game_api/layer.cpp b/src/game_api/layer.cpp index efa236a6d..735f8cd2f 100644 --- a/src/game_api/layer.cpp +++ b/src/game_api/layer.cpp @@ -9,6 +9,7 @@ #include "entity.hpp" // for Entity, to_id, EntityDB, entity_factory #include "logger.h" // for DEBUG #include "movable.hpp" // for Movable +#include "rpc.hpp" // #include "search.hpp" // for get_address #include "state.hpp" // for State, StateMemory From 2841c0544aefab69697edd0101e83f01faf55444 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:42:21 +0200 Subject: [PATCH 24/33] comm --- src/game_api/entities_floors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/entities_floors.cpp b/src/game_api/entities_floors.cpp index 5627dd97b..b889524bb 100644 --- a/src/game_api/entities_floors.cpp +++ b/src/game_api/entities_floors.cpp @@ -728,7 +728,7 @@ void ForceField::activate_laserbeam(bool turn_on) void Door::unlock(bool unlock) { - // TODO: DOOR_EGGSHIP, DOOR_EGGSHIP_ATREZZO, DOOR_EGGSHIP_ROOM ? + // TODO: DOOR_EGGSHIP, DOOR_EGGSHIP_ATREZZO, DOOR_EGGSHIP_ROOM, HUNDUN ? static const ENT_TYPE entrence_door = to_id("ENT_TYPE_FLOOR_DOOR_ENTRANCE"); static const ENT_TYPE locked_door = to_id("ENT_TYPE_FLOOR_DOOR_LOCKED"); static const ENT_TYPE COG_door = to_id("ENT_TYPE_FLOOR_DOOR_COG"); From ac41ad0a89922e489a546bc5d1305b2f558c0a95 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:43:58 +0200 Subject: [PATCH 25/33] a little bit screen stuff --- src/game_api/screen.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/game_api/screen.hpp b/src/game_api/screen.hpp index 2e588417e..0c4eb878a 100644 --- a/src/game_api/screen.hpp +++ b/src/game_api/screen.hpp @@ -23,12 +23,10 @@ class Screen float render_timer; uint32_t unknown_zero; - // this first virtual does not appear to be the destructor in the game - // but is made one here to appease Clang + virtual void init() = 0; + virtual void handle_player() = 0; // for normal level: death, camera zoom, camera bounds, some save data stuff virtual ~Screen() = 0; - virtual void v1() = 0; - virtual void v2() = 0; - virtual void render() = 0; + virtual void render() = 0; // mostly used by the non gameplay screens to draw textures and text std::uint32_t reserve_callback_id(); void unhook(std::uint32_t id); From 242c30c582e065e0c4b8ee7f1b8568c297b4b472 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 14 Oct 2023 20:28:58 +0200 Subject: [PATCH 26/33] expose sound channels params --- src/game_api/script/sol_helper.hpp | 95 +++++++++++++++++++++ src/game_api/script/usertypes/sound_lua.cpp | 11 ++- src/game_api/sound_manager.hpp | 11 ++- 3 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/game_api/script/sol_helper.hpp diff --git a/src/game_api/script/sol_helper.hpp b/src/game_api/script/sol_helper.hpp new file mode 100644 index 000000000..2dc6a596d --- /dev/null +++ b/src/game_api/script/sol_helper.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include +#include +#include + +template +struct ZeroIndexArray +{ + using value_type = T; + using iterator_category = std::contiguous_iterator_tag; + using difference_type = size_t; + using pointer = T*; + using reference = T&; + using iterator = T*; + + ZeroIndexArray(T* data, size_t size) + : data(data), data_size(size){}; + + ZeroIndexArray(std::span arr) + { + data = arr.data(); + data_size = arr.size(); + }; + ~ZeroIndexArray() + { + data_size = 0; + }; + + T& operator[](int index) + { + return data[index]; + } + T& operator=(T&& other) noexcept + { + // Guard self assignment + if (this == &other) + return *this; + + data = other.data; + data_size = other.data_size; + return *this; + } + iterator begin() const + { + return iterator(data); + } + iterator end() const + { + return iterator(data + data_size); + } + bool empty() const + { + return data_size == 0; + } + size_t size() const + { + return data_size; + } + T* data; + size_t data_size{0}; +}; + +namespace sol +{ +template +struct is_container> : std::true_type +{ +}; + +template +struct usertype_container> +{ + static int size(lua_State* L) + { + ZeroIndexArray& v = sol::stack::get&>(L, 1); + return stack::push(L, v.size()); + } + // Used by default implementation + static auto begin(lua_State*, ZeroIndexArray& self) + { + return self.begin(); + } + static auto end(lua_State*, ZeroIndexArray& self) + { + return self.end(); + } + static std::ptrdiff_t index_adjustment(lua_State*, ZeroIndexArray&) + { + return 0; + } +}; +} // namespace sol diff --git a/src/game_api/script/usertypes/sound_lua.cpp b/src/game_api/script/usertypes/sound_lua.cpp index e31b2ae01..077a1194c 100644 --- a/src/game_api/script/usertypes/sound_lua.cpp +++ b/src/game_api/script/usertypes/sound_lua.cpp @@ -27,6 +27,7 @@ #include "logger.h" // for DEBUG #include "script/lua_backend.hpp" // for LuaBackend #include "script/safe_cb.hpp" // for make_safe_cb +#include "script/sol_helper.hpp" // #include "sound_manager.hpp" // for CustomSound, PlayingSound, SoundMa... #include "string_aliases.hpp" // for VANILLA_SOUND @@ -167,10 +168,12 @@ void register_usertypes(sol::state& lua, SoundManager* sound_manager) &SoundMeta::x, "y", &SoundMeta::y, - //"left_channel", - //&SoundMeta::left_channel, // TODO: index 0-37 instead of 1-38 - //"right_channel", - //&SoundMeta::right_channel, + "left_channel", + sol::property([&lua](SoundMeta* sm) // -> std::array + { return ZeroIndexArray(sm->left_channel) /**/; }), + "right_channel", + sol::property([](SoundMeta* sm) // -> std::array + { return ZeroIndexArray(sm->right_channel) /**/; }), "start_over", &SoundMeta::start_over, "playing", diff --git a/src/game_api/sound_manager.hpp b/src/game_api/sound_manager.hpp index 51c49b591..c23011705 100644 --- a/src/game_api/sound_manager.hpp +++ b/src/game_api/sound_manager.hpp @@ -264,10 +264,13 @@ struct SoundMeta { float x; float y; - SoundInfo* sound_info; // param to FMOD::Studio::EventInstance::SetParameterByID (this ptr + 0x30) - uint64_t fmod_param_id; // param to FMOD::Studio::EventInstance::SetParameterByID - std::array left_channel; // VANILLA_SOUND_PARAM - std::array right_channel; // VANILLA_SOUND_PARAM + SoundInfo* sound_info; // param to FMOD::Studio::EventInstance::SetParameterByID (this ptr + 0x30) + uint64_t fmod_param_id; // param to FMOD::Studio::EventInstance::SetParameterByID + + /// Use VANILLA_SOUND_PARAM as index, warning: special case with first index at 0, loop using pairs will get you all results but the key/index will be wrong, ipairs will have correct key/index but will skip the first element + std::array left_channel; + /// Use VANILLA_SOUND_PARAM as index warning: special case with first index at 0, loop using pairs will get you all results but the key/index will be wrong, ipairs will have correct key/index but will skip the first element + std::array right_channel; /// when false, current track starts from the beginning, is immediately set back to true bool start_over; From 9cd1bdf03cf6a0efd7e7f3d008de2400763358b5 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 14 Oct 2023 20:46:49 +0200 Subject: [PATCH 27/33] update doc --- docs/game_data/spel2.lua | 73 +++++++++++++++++++-- docs/src/includes/_types.md | 4 +- src/game_api/script/usertypes/sound_lua.cpp | 11 ++-- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index ffa664047..aa2976628 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1235,6 +1235,39 @@ function set_frametime_unfocused(frametime) end ---Get engine target frametime when game is unfocused (1/framerate, default 1/33). ---@return double? function get_frametime_unfocused() end +---Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +---@param types ENT_TYPE[] +---@return ENT_TYPE +function add_custom_type(types) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depth integer +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depth, l) end +---Get uids of entities by draw_depth. Can also use table of draw_depths. +---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity +---@param draw_depths integer[] +---@param l LAYER +---@return integer[] +function get_entities_by_draw_depth(draw_depths, l) end +---Just convenient way of getting the current amount of money +---short for state->money_shop_total + loop[inventory.money + inventory.collected_money_total] +---@return integer +function get_current_money() end +---Adds money to the state.money_shop_total and displays the effect on the HUD for money change +---Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction +---@param amount integer +---@param display_time integer? +---@return integer +function add_money(amount, display_time) end +---Adds money to the state.items.player_inventory[player_slot].money and displays the effect on the HUD for money change +---Can be negative, default display_time = 60 (about 2s). Returns the current money after the transaction +---@param amount integer +---@param player_slot integer +---@param display_time integer? +---@return integer +function add_money_slot(amount, player_slot, display_time) end ---Destroys all layers and all entities in the level. Usually a bad idea, unless you also call create_level and spawn the player back in. ---@return nil function destroy_level() end @@ -1589,6 +1622,8 @@ function set_lut(texture_id, layer) end ---@param layer LAYER ---@return nil function reset_lut(layer) end +---@return HudData +function get_hud() end ---Alters the drop chance for the provided monster-item combination (use e.g. set_drop_chance(DROPCHANCE.MOLE_MATTOCK, 10) for a 1 in 10 chance) ---Use `-1` as dropchance_id to reset all to default ---@param dropchance_id integer @@ -1901,8 +1936,9 @@ do ---@field is_pet_cursed boolean[] @size: 4 ---@field is_pet_poisoned boolean[] @size: 4 ---@field leader integer @Index of leader player in coop - ---@field player_inventory Inventory[] @size: MAX_PLAYERS ---@field player_select SelectPlayerSlot[] @size: MAX_PLAYERS + ---@field player_inventory Inventory[] @size: MAX_PLAYERS + ---@field players Player[] @size: MAX_PLAYERS @Table of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body) ---@class LiquidPhysicsEngine ---@field pause boolean @@ -2148,6 +2184,7 @@ do ---@field pause_ui PauseUI ---@field journal_ui JournalUI ---@field save_related SaveRelated + ---@field main_menu_music BackgroundSound ---@class SaveRelated ---@field journal_popup_ui JournalPopupUI @@ -2377,6 +2414,31 @@ function Entity:overlaps_with(rect_left, rect_bottom, rect_right, rect_top) end ---@param other Entity ---@return boolean function Entity:overlaps_with(other) end +---Kill entity along with all entities attached to it. Be aware that for example killing push block with this function will also kill anything on top of it, any items, players, monsters etc. +---To a that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check mask, if the entity doesn't match, it will look in the provided ENT_TYPE's +---destroy_corpse and responsible are the standard parameters for the kill funciton +---@param destroy_corpse boolean +---@param responsible Entity +---@param mask integer? +---@param ent_types ENT_TYPE[] +---@param rec_mode RECURSIVE_MODE +---@return nil +function Entity:kill_recursive(destroy_corpse, responsible, mask, ent_types, rec_mode) end +---Short for using RECURSIVE_MODE.NONE +---@param destroy_corpse boolean +---@param responsible Entity +---@return nil +function Entity:kill_recursive(destroy_corpse, responsible) end +---Destroy entity along with all entities attached to it. Be aware that for example destroying push block with this function will also destroy anything on top of it, any items, players, monsters etc. +---To a that, you can inclusively or exclusively limit certain MASK and ENT_TYPE. Note: the function will first check the mask, if the entity doesn't match, it will look in the provided ENT_TYPE's +---@param mask integer? +---@param ent_types ENT_TYPE[] +---@param rec_mode RECURSIVE_MODE +---@return nil +function Entity:destroy_recursive(mask, ent_types, rec_mode) end +---Short for using RECURSIVE_MODE.NONE +---@return nil +function Entity:destroy_recursive() end ---@class Movable : Entity ---@field move Vec2 @{movex, movey} @@ -2824,7 +2886,7 @@ function Movable:generic_update_world(move, sprint_factor, disable_gravity, on_r ---@field sound1 SoundMeta ---@field sound2 SoundMeta ---@field top_chain_piece Entity - ---@field trigger fun(self): nil + ---@field trigger fun(self, play_sound_effect: boolean?): nil ---@class ThinIce : Movable ---@field strength integer @counts down when standing on, maximum is 134 as based of this value it changes animation_frame, and above that value it changes to wrong sprite @@ -4570,6 +4632,9 @@ function MovableBehavior:get_state_id() end ---@field spawn_room_y integer ---@field exit_doors custom_Array ---@field themes ThemeInfo[] @size: 18 + ---@field flags integer + ---@field flags2 integer + ---@field flags3 integer ---@class PostRoomGenerationContext ---@field set_room_template fun(self, x: integer, y: integer, layer: LAYER, room_template: ROOM_TEMPLATE): boolean @Set the room template at the given index and layer, returns `false` if the index is outside of the level. @@ -4704,8 +4769,8 @@ function CustomSound:play(paused, sound_type) end ---@class SoundMeta ---@field x number ---@field y number - ---@field left_channel number[] @size: 38 - ---@field right_channel number[] @size: 38 + ---@field left_channel number[] @size: 38 @Use VANILLA_SOUND_PARAM as index, warning: special case with first index at 0, loop using tuples will get you all results but the key/index will be wrong, ituples will have correct key/index but will skip the first element + ---@field right_channel number[] @size: 38 @Use VANILLA_SOUND_PARAM as index warning: special case with first index at 0, loop using tuples will get you all results but the key/index will be wrong, ituples will have correct key/index but will skip the first element ---@field start_over boolean @when false, current track starts from the beginning, is immediately set back to true ---@field playing boolean @set to false to turn off diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index a693cdaf5..de273e7a5 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -2623,8 +2623,8 @@ Type | Name | Description ---- | ---- | ----------- 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) | -array<float, 38> | [left_channel](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=left_channel) | -array<float, 38> | [right_channel](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=right_channel) | +array<float, 38> | [left_channel](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=left_channel) | Use [VANILLA_SOUND_PARAM](#VANILLA_SOUND_PARAM) as index, warning: special case with first index at 0, loop using pairs will get you all results but the key/index will be wrong, ipairs will have correct key/index but will skip the first element +array<float, 38> | [right_channel](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=right_channel) | Use [VANILLA_SOUND_PARAM](#VANILLA_SOUND_PARAM) as index warning: special case with first index at 0, loop using pairs will get you all results but the key/index will be wrong, ipairs will have correct key/index but will skip the first element bool | [start_over](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=start_over) | when false, current track starts from the beginning, is immediately set back to true bool | [playing](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=playing) | set to false to turn off diff --git a/src/game_api/script/usertypes/sound_lua.cpp b/src/game_api/script/usertypes/sound_lua.cpp index 077a1194c..6514c6aa8 100644 --- a/src/game_api/script/usertypes/sound_lua.cpp +++ b/src/game_api/script/usertypes/sound_lua.cpp @@ -162,6 +162,11 @@ void register_usertypes(sol::state& lua, SoundManager* sound_manager) "set_parameter", &PlayingSound::set_parameter); + auto left_channel = sol::property([&lua](SoundMeta* sm) + { return ZeroIndexArray(sm->left_channel); }); + auto right_channel = sol::property([](SoundMeta* sm) + { return ZeroIndexArray(sm->right_channel); }); + lua.new_usertype( "SoundMeta", "x", @@ -169,11 +174,9 @@ void register_usertypes(sol::state& lua, SoundManager* sound_manager) "y", &SoundMeta::y, "left_channel", - sol::property([&lua](SoundMeta* sm) // -> std::array - { return ZeroIndexArray(sm->left_channel) /**/; }), + left_channel, "right_channel", - sol::property([](SoundMeta* sm) // -> std::array - { return ZeroIndexArray(sm->right_channel) /**/; }), + right_channel, "start_over", &SoundMeta::start_over, "playing", From 3559c033960301c8dad912425bb2681d0828b985 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 14 Oct 2023 21:59:13 +0200 Subject: [PATCH 28/33] this works, for me at leats --- src/game_api/script/sol_helper.hpp | 2 +- src/game_api/script/usertypes/sound_lua.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/game_api/script/sol_helper.hpp b/src/game_api/script/sol_helper.hpp index 2dc6a596d..d50334f27 100644 --- a/src/game_api/script/sol_helper.hpp +++ b/src/game_api/script/sol_helper.hpp @@ -76,7 +76,7 @@ struct usertype_container> static int size(lua_State* L) { ZeroIndexArray& v = sol::stack::get&>(L, 1); - return stack::push(L, v.size()); + return sol::stack::push(L, v.size()); } // Used by default implementation static auto begin(lua_State*, ZeroIndexArray& self) diff --git a/src/game_api/script/usertypes/sound_lua.cpp b/src/game_api/script/usertypes/sound_lua.cpp index 6514c6aa8..29976615a 100644 --- a/src/game_api/script/usertypes/sound_lua.cpp +++ b/src/game_api/script/usertypes/sound_lua.cpp @@ -162,11 +162,6 @@ void register_usertypes(sol::state& lua, SoundManager* sound_manager) "set_parameter", &PlayingSound::set_parameter); - auto left_channel = sol::property([&lua](SoundMeta* sm) - { return ZeroIndexArray(sm->left_channel); }); - auto right_channel = sol::property([](SoundMeta* sm) - { return ZeroIndexArray(sm->right_channel); }); - lua.new_usertype( "SoundMeta", "x", @@ -174,9 +169,13 @@ void register_usertypes(sol::state& lua, SoundManager* sound_manager) "y", &SoundMeta::y, "left_channel", - left_channel, + //&SoundMeta::left_channel, + sol::property([](SoundMeta* sm) + { return ZeroIndexArray(sm->left_channel) /**/; }), "right_channel", - right_channel, + //&SoundMeta::right_channel, + sol::property([](SoundMeta* sm) + { return ZeroIndexArray(sm->right_channel) /**/; }), "start_over", &SoundMeta::start_over, "playing", From 2b79d8290f1bf36a800414585f0e1b0e4457c111 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 15 Oct 2023 17:31:32 +0200 Subject: [PATCH 29/33] just comment --- src/game_api/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index e95dcd194..a566dcbd1 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -652,7 +652,7 @@ std::unordered_map g_address_rules{ "spawn_liquid"sv, // See tile code for water (0xea for 1.23.3) in handle_tile_code, last call before returning PatternCommandBuffer{} - .find_inst("\xE8****\xE9****\x48\x81\xC6"sv) + .find_inst("\xE8****\xE9****\x48\x81\xC6"sv) // alternative find_after_inst("41 0F 28 D1 41 B9 90 03 00 00"_gh) .decode_call() .at_exe(), }, From 33db1fd3b8e0f5cbbbca9e395ae4bcd0c4bf964f Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:46:58 +0200 Subject: [PATCH 30/33] fix `vlad_flying` tilecode --- src/game_api/level_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 6191fce84..fdb671b29 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -225,7 +225,7 @@ std::array g_community_tile_codes{ CommunityTileCode{"firebug", "ENT_TYPE_MONS_FIREBUG"}, CommunityTileCode{"vampire", "ENT_TYPE_MONS_VAMPIRE", g_spawn_not_snapped_to_floor}, CommunityTileCode{"vampire_flying", "ENT_TYPE_MONS_VAMPIRE"}, - CommunityTileCode{"vlad_flying", "ENT_TYPE_MONS_VLAD"}, + CommunityTileCode{"vlad_flying", "ENT_TYPE_MONS_VLAD", g_spawn_not_snapped_to_floor}, CommunityTileCode{"osiris", "ENT_TYPE_MONS_OSIRIS_HEAD"}, CommunityTileCode{"anubis2", "ENT_TYPE_MONS_ANUBIS2"}, CommunityTileCode{"assassin", "ENT_TYPE_MONS_FEMALE_JIANGSHI"}, From ac7cc6af6a6bf57be38fddc57f05bc7932542a48 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:23:42 +0200 Subject: [PATCH 31/33] empty add_custom_type --- docs/game_data/spel2.lua | 5 +++++ docs/src/includes/_globals.md | 3 +++ src/game_api/custom_types.cpp | 7 ++++--- src/game_api/rpc.cpp | 5 +++++ src/game_api/rpc.hpp | 1 + src/game_api/script/lua_vm.cpp | 5 +++++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index aa2976628..b94607456 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1236,9 +1236,14 @@ function set_frametime_unfocused(frametime) end ---@return double? function get_frametime_unfocused() end ---Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +---Use empty array or no parameter to get new uniqe ENT_TYPE that can be used for custom EntityDB ---@param types ENT_TYPE[] ---@return ENT_TYPE function add_custom_type(types) end +---Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +---Use empty array or no parameter to get new uniqe ENT_TYPE that can be used for custom EntityDB +---@return ENT_TYPE +function add_custom_type() end ---Get uids of entities by draw_depth. Can also use table of draw_depths. ---You can later use [filter_entities](https://spelunky-fyi.github.io/overlunky/#filter_entities) if you want specific entity ---@param draw_depth integer diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index d83184c82..dd82ca698 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1177,7 +1177,10 @@ default game value are: y_limit = 98.5, rising_speed_x = 0, rising_speed_y = 0.0 #### [ENT_TYPE](#ENT_TYPE) add_custom_type(array<[ENT_TYPE](#ENT_TYPE)> types) +#### [ENT_TYPE](#ENT_TYPE) add_custom_type() + Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn +Use empty array or no parameter to get new uniqe [ENT_TYPE](#ENT_TYPE) that can be used for custom [EntityDB](#EntityDB) ### add_money diff --git a/src/game_api/custom_types.cpp b/src/game_api/custom_types.cpp index bc93b3ece..d698fd2ff 100644 --- a/src/game_api/custom_types.cpp +++ b/src/game_api/custom_types.cpp @@ -1578,10 +1578,11 @@ std::span get_custom_entity_types(CUSTOM_TYPE type) CUSTOM_TYPE add_new_custom_type(std::vector types) { - if (types.empty()) - return (CUSTOM_TYPE)0; - ++g_last_custom_id; + if (types.empty()) + { + types.push_back(g_last_custom_id); + } user_custom_types.emplace((CUSTOM_TYPE)g_last_custom_id, std::move(types)); return (CUSTOM_TYPE)g_last_custom_id; } diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 1aac6cecc..3e39554dc 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1847,6 +1847,11 @@ ENT_TYPE add_custom_type(std::vector types) return (ENT_TYPE)add_new_custom_type(std::move(types)); } +ENT_TYPE add_custom_type() +{ + return (ENT_TYPE)add_new_custom_type({}); +} + int32_t get_current_money() { auto state = State::get().ptr(); diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 106ea1df2..3b366f459 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -130,6 +130,7 @@ std::optional get_frametime(); void set_frametime_inactive(std::optional frametime); std::optional get_frametime_inactive(); ENT_TYPE add_custom_type(std::vector types); +ENT_TYPE add_custom_type(); int32_t get_current_money(); int32_t add_money(int32_t amount, std::optional display_time); int32_t add_money_slot(int32_t amount, uint8_t player_slot, std::optional display_time); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 4d339c862..539c67d15 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2101,7 +2101,12 @@ end /// Get engine target frametime when game is unfocused (1/framerate, default 1/33). lua["get_frametime_unfocused"] = get_frametime_inactive; + auto add_custom_type = sol::overload( + static_cast)>(::add_custom_type), + static_cast(::add_custom_type)); + /// Adds new custom type (group of ENT_TYPE) that can be later used in functions like get_entities_by or set_(pre/post)_entity_spawn + /// Use empty array or no parameter to get new uniqe ENT_TYPE that can be used for custom EntityDB lua["add_custom_type"] = add_custom_type; auto get_entities_by_draw_depth = sol::overload( From 38dc6f76f0a754484baf36c96a9198f03cce059c Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:40:09 +0200 Subject: [PATCH 32/33] acutally fix the `vlad_flying` --- src/game_api/level_api.cpp | 97 +++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index fdb671b29..eabccd78f 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -225,7 +225,16 @@ std::array g_community_tile_codes{ CommunityTileCode{"firebug", "ENT_TYPE_MONS_FIREBUG"}, CommunityTileCode{"vampire", "ENT_TYPE_MONS_VAMPIRE", g_spawn_not_snapped_to_floor}, CommunityTileCode{"vampire_flying", "ENT_TYPE_MONS_VAMPIRE"}, - CommunityTileCode{"vlad_flying", "ENT_TYPE_MONS_VLAD", g_spawn_not_snapped_to_floor}, + CommunityTileCode{ + "vlad_flying", + "ENT_TYPE_MONS_VLAD", + [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + if (Movable* vlad = (Movable*)layer->spawn_entity(self.entity_id, x, y, false, 0.0f, 0.0f, true)) + { + vlad->move_state = 9; + } + }}, CommunityTileCode{"osiris", "ENT_TYPE_MONS_OSIRIS_HEAD"}, CommunityTileCode{"anubis2", "ENT_TYPE_MONS_ANUBIS2"}, CommunityTileCode{"assassin", "ENT_TYPE_MONS_FEMALE_JIANGSHI"}, @@ -420,20 +429,14 @@ std::array g_community_tile_codes{ }, CommunityTileCode{"boulder", "ENT_TYPE_ACTIVEFLOOR_BOULDER"}, CommunityTileCode{"apep", "ENT_TYPE_MONS_APEP_HEAD"}, - CommunityTileCode{ - "apep_left", - "ENT_TYPE_MONS_APEP_HEAD", - []([[maybe_unused]] const CommunityTileCode& self, float x, float y, Layer* layer) - { - layer->spawn_apep(x, y, is_room_flipped(x, y)); - }}, - CommunityTileCode{ - "apep_right", - "ENT_TYPE_MONS_APEP_HEAD", - []([[maybe_unused]] const CommunityTileCode& self, float x, float y, Layer* layer) - { - layer->spawn_apep(x, y, !is_room_flipped(x, y)); - }}, + CommunityTileCode{"apep_left", "ENT_TYPE_MONS_APEP_HEAD", []([[maybe_unused]] const CommunityTileCode& self, float x, float y, Layer* layer) + { + layer->spawn_apep(x, y, is_room_flipped(x, y)); + }}, + CommunityTileCode{"apep_right", "ENT_TYPE_MONS_APEP_HEAD", []([[maybe_unused]] const CommunityTileCode& self, float x, float y, Layer* layer) + { + layer->spawn_apep(x, y, !is_room_flipped(x, y)); + }}, CommunityTileCode{"olmite_naked", "ENT_TYPE_MONS_OLMITE_NAKED"}, CommunityTileCode{"olmite_helmet", "ENT_TYPE_MONS_OLMITE_HELMET"}, CommunityTileCode{ @@ -471,46 +474,34 @@ std::array g_community_tile_codes{ CommunityTileCode{"punishball_attach_bottom", "ENT_TYPE_ITEM_PUNISHBALL", g_spawn_punishball_attach<0, -1>}, CommunityTileCode{"critter_slime", "ENT_TYPE_MONS_CRITTERSLIME"}, CommunityTileCode{"skull", "ENT_TYPE_ITEM_SKULL"}, - CommunityTileCode{ - "venom", - "ENT_TYPE_ITEM_ACIDSPIT", - [](const CommunityTileCode& self, float x, float y, Layer* layer) - { - layer->spawn_entity(self.entity_id, x, y - 1, false, 0, 0, false); - }}, + CommunityTileCode{"venom", "ENT_TYPE_ITEM_ACIDSPIT", [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + layer->spawn_entity(self.entity_id, x, y - 1, false, 0, 0, false); + }}, CommunityTileCode{"arrow_wooden", "ENT_TYPE_ITEM_WOODEN_ARROW"}, CommunityTileCode{"arrow_metal", "ENT_TYPE_ITEM_METAL_ARROW"}, - CommunityTileCode{ - "arrow_wooden_poison", - "ENT_TYPE_ITEM_WOODEN_ARROW", - [](const CommunityTileCode& self, float x, float y, Layer* layer) - { - Arrow* arrow = layer->spawn_entity_snap_to_floor(self.entity_id, x, y)->as(); - arrow->poison_arrow(true); - }}, - CommunityTileCode{ - "arrow_metal_poison", - "ENT_TYPE_ITEM_METAL_ARROW", - [](const CommunityTileCode& self, float x, float y, Layer* layer) - { - Arrow* arrow = layer->spawn_entity_snap_to_floor(self.entity_id, x, y)->as(); - arrow->poison_arrow(true); - }}, - CommunityTileCode{ - "movable_spikes", - "ENT_TYPE_ITEM_SPIKES", - [](const CommunityTileCode& self, float x, float y, Layer* layer) - { - auto do_spawn = [=]() - { - std::vector entities_neighbour = get_entities_overlapping_by_pointer({}, 0, x - 0.5f, y - 1.5f, x + 0.5f, y - 0.5f, layer); - if (!entities_neighbour.empty()) - { - layer->spawn_entity_over(self.entity_id, get_entity_ptr(entities_neighbour.front()), 0.0f, 1.0f); - } - }; - g_attachee_requiring_entities.push_back({{{x, y - 1}}, do_spawn}); - }}, + CommunityTileCode{"arrow_wooden_poison", "ENT_TYPE_ITEM_WOODEN_ARROW", [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + Arrow* arrow = layer->spawn_entity_snap_to_floor(self.entity_id, x, y)->as(); + arrow->poison_arrow(true); + }}, + CommunityTileCode{"arrow_metal_poison", "ENT_TYPE_ITEM_METAL_ARROW", [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + Arrow* arrow = layer->spawn_entity_snap_to_floor(self.entity_id, x, y)->as(); + arrow->poison_arrow(true); + }}, + CommunityTileCode{"movable_spikes", "ENT_TYPE_ITEM_SPIKES", [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + auto do_spawn = [=]() + { + std::vector entities_neighbour = get_entities_overlapping_by_pointer({}, 0, x - 0.5f, y - 1.5f, x + 0.5f, y - 0.5f, layer); + if (!entities_neighbour.empty()) + { + layer->spawn_entity_over(self.entity_id, get_entity_ptr(entities_neighbour.front()), 0.0f, 1.0f); + } + }; + g_attachee_requiring_entities.push_back({{{x, y - 1}}, do_spawn}); + }}, CommunityTileCode{"boombox", "ENT_TYPE_ITEM_BOOMBOX"}, // CommunityTileCode{ // "lake_imposter", From 741a7321daa718377278dd7969ce80f8a307bdff Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Mon, 16 Oct 2023 19:19:47 +0200 Subject: [PATCH 33/33] `vampire_flying` the same thing --- src/game_api/level_api.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index eabccd78f..8791b6d22 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -224,7 +224,16 @@ std::array g_community_tile_codes{ CommunityTileCode{"monkey", "ENT_TYPE_MONS_MONKEY"}, CommunityTileCode{"firebug", "ENT_TYPE_MONS_FIREBUG"}, CommunityTileCode{"vampire", "ENT_TYPE_MONS_VAMPIRE", g_spawn_not_snapped_to_floor}, - CommunityTileCode{"vampire_flying", "ENT_TYPE_MONS_VAMPIRE"}, + CommunityTileCode{ + "vampire_flying", + "ENT_TYPE_MONS_VAMPIRE", + [](const CommunityTileCode& self, float x, float y, Layer* layer) + { + if (Movable* vampire = (Movable*)layer->spawn_entity(self.entity_id, x, y, false, 0.0f, 0.0f, true)) + { + vampire->move_state = 9; + } + }}, CommunityTileCode{ "vlad_flying", "ENT_TYPE_MONS_VLAD",