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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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 68931381d9ebfdfbd480f0a2f4cf7adc1fb7bc76 Mon Sep 17 00:00:00 2001 From: Dregu Date: Sat, 14 Oct 2023 23:55:07 +0300 Subject: [PATCH 29/42] dump more recursive output for console --- src/game_api/lua_libs/lua_libs.cpp | 29 ++++++++++++++++++++++++++++- src/game_api/script/lua_console.cpp | 6 ++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/game_api/lua_libs/lua_libs.cpp b/src/game_api/lua_libs/lua_libs.cpp index ceb681af2..37fd74d86 100644 --- a/src/game_api/lua_libs/lua_libs.cpp +++ b/src/game_api/lua_libs/lua_libs.cpp @@ -796,7 +796,7 @@ local function format(_, str) return fmt and string.format(fmt, fn()) or tostring(fn()) else error(err, 0) - end + end end)) end @@ -955,4 +955,31 @@ return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } )serp"; lua["serpent"] = lua.require_script("serpent", serpent_code); + + lua.script(R"##( + function dump(o, d, n) + local n = n or 0 + local t = nil + if getmetatable(o) and (not d or n < d) then + t = {} + if o.pairs then + for k,v in pairs(o) do + t[k] = dump(v, d, n+1) + end + else + for k,v in pairs(getmetatable(o)) do + if k:sub(0, 2) ~= "__" and k ~= "class_cast" and k ~= "class_check" and k ~= "new" then + t[k] = dump(o[k], d, n+1) + end + end + end + else + t = o + end + return t + end + function dump_string(o, d) + return serpent.line(dump(o, d), {comment=false, indent=" "}) + end + )##"); } diff --git a/src/game_api/script/lua_console.cpp b/src/game_api/script/lua_console.cpp index cde609f58..e30ff43c3 100644 --- a/src/game_api/script/lua_console.cpp +++ b/src/game_api/script/lua_console.cpp @@ -1046,8 +1046,10 @@ std::string LuaConsole::execute_raw(std::string code) } else { - sol::function serpent = lua["serpent"]["block"]; - return serpent(ret); + // sol::function serpent = lua["serpent"]["block"]; + // return serpent(ret); + sol::function dump_string = lua["dump_string"]; + return dump_string(ret, 2); } } catch (const sol::error& e) From 8f6f1e4430467e8faec298d9df9c0b51a0e5f154 Mon Sep 17 00:00:00 2001 From: Dregu Date: Sun, 15 Oct 2023 10:50:57 +0300 Subject: [PATCH 30/42] don't limit amount of console messages in queue --- src/game_api/script/lua_vm.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index f7a2fb1f9..b7e681085 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -363,20 +363,28 @@ end /// Standard lua print function, prints directly to the terminal but not to the game lua["lua_print"] = lua["print"]; + /// Print a log message on screen. lua["print"] = [](std::string message) -> void { auto backend = LuaBackend::get_calling_backend(); + bool is_console = !strcmp(backend->get_id(), "dev/lua_console"); backend->messages.push_back({message, std::chrono::system_clock::now(), ImVec4(1.0f, 1.0f, 1.0f, 1.0f)}); - if (backend->messages.size() > 20) + if (backend->messages.size() > 40 && !is_console) backend->messages.pop_front(); backend->lua["lua_print"](message); }; - /// Print a log message to ingame console. - lua["console_print"] = [](std::string message) -> void + /// Print a log message to ingame console with a comment identifying the script that sent it. + lua["console_print"] = [&lua](std::string message) -> void { auto backend = LuaBackend::get_calling_backend(); + bool is_console = !strcmp(backend->get_id(), "dev/lua_console"); + if (is_console) + { + lua["print"](std::move(message)); + return; + } auto in_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::tm time_buf; localtime_s(&time_buf, &in_time_t); From 5df01eac59c4e73819f3f45e0a1a06517c8a8640 Mon Sep 17 00:00:00 2001 From: Dregu Date: Sun, 15 Oct 2023 11:33:48 +0300 Subject: [PATCH 31/42] docs --- docs/game_data/spel2.lua | 7 ++++++- docs/generate.py | 11 +++++++++-- docs/src/includes/_globals.md | 11 ++++++++++- docs/src/includes/_home.md | 2 +- src/game_api/script/lua_vm.cpp | 9 +++++---- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index ffa664047..4ddeecfaa 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -53,7 +53,7 @@ function lua_print() end ---@param message string ---@return nil function print(message) end ----Print a log message to ingame console. +---Print a log message to ingame console with a comment identifying the script that sent it. ---@param message string ---@return nil function console_print(message) end @@ -73,6 +73,11 @@ function prinspect(...) end ---@vararg any ---@return nil function messpect(...) end +---Dump the object (table, container, class) as a recursive table, for pretty printing in console. Don't use this for anything except debug printing. Unsafe. +---@param any any +---@param depth integer? +---@return table +function dump(object, depth) end ---Adds a command that can be used in the console. ---@param name string ---@param cmd function diff --git a/docs/generate.py b/docs/generate.py index 339d7945c..e44df546a 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -306,7 +306,7 @@ def print_lf(lf): print("\n# Unsafe mode") print( - "Setting `meta.unsafe = true` enables the rest of the standard Lua libraries like unrestricted `io` and `os`, loading dlls with require and `package.loadlib`. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory." + "Setting `meta.unsafe = true` enables the rest of the standard Lua libraries like unrestricted `io`, `os`, `ffi` and `debug`, loading dlls with require, `package.loadlib`, the [network functions](#Network-functions) and some [debug functions](#Debug-functions). Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory." ) print("\n# Modules") @@ -393,7 +393,14 @@ def print_lf(lf): cat = "Message functions" elif any( subs in func["name"] - for subs in ["get_rva", "get_virtual_rva", "raise", "dump_network"] + for subs in [ + "get_rva", + "get_virtual_rva", + "raise", + "dump_network", + "dump", + "dump_string", + ] ): cat = "Debug functions" elif any(subs in func["name"] for subs in ["_option"]): diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 138da2c7c..30c12d1c7 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -342,6 +342,15 @@ If you set such a callback and then play the same sound yourself you have to wai ## Debug functions +### dump + + +> Search script examples for [dump](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=dump) + +#### table dump(object object, optional depth) + +Dump the object (table, container, class) as a recursive table, for pretty printing in console. Don't use this for anything except debug printing. Unsafe. + ### dump_network @@ -1983,7 +1992,7 @@ Prinspect to ingame console. #### nil console_print(string message) -Print a log message to ingame console. +Print a log message to ingame console with a comment identifying the script that sent it. ### log_print diff --git a/docs/src/includes/_home.md b/docs/src/includes/_home.md index c08dce91a..4efbe5a83 100644 --- a/docs/src/includes/_home.md +++ b/docs/src/includes/_home.md @@ -140,7 +140,7 @@ end ``` # Unsafe mode -Setting `meta.unsafe = true` enables the rest of the standard Lua libraries like unrestricted `io` and `os`, loading dlls with require and `package.loadlib`. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory. +Setting `meta.unsafe = true` enables the rest of the standard Lua libraries like unrestricted `io`, `os`, `ffi` and `debug`, loading dlls with require, `package.loadlib`, the [network functions](#Network-functions) and some [debug functions](#Debug-functions). Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory. # Modules You can load modules with `require "mymod"` or `require "mydir.mymod"`, just put `mymod.lua` in the same directory the script is, or in `mydir/` to keep things organized. diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index b7e681085..5ebd92de4 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -413,6 +413,7 @@ end /// Same as `print` lua["message"] = [&lua](std::string message) -> void { lua["print"](message); }; + /// Prints any type of object by first funneling it through `inspect`, no need for a manual `tostring` or `inspect`. lua["prinspect"] = [&lua](sol::variadic_args objects) -> void { @@ -430,10 +431,14 @@ end lua["print"](std::move(message)); } }; + /// Same as `prinspect` lua["messpect"] = [&lua](sol::variadic_args objects) -> void { lua["prinspect"](objects); }; + /// Dump the object (table, container, class) as a recursive table, for pretty printing in console. Don't use this for anything except debug printing. Unsafe. + // lua["dump"] = [](object object, optional depth) -> table + /// Adds a command that can be used in the console. lua["register_console_command"] = [](std::string name, sol::function cmd) { @@ -2780,7 +2785,6 @@ void add_partial_safe_libraries(sol::environment& env) std::string fullpath = datadir + "/" + filename; std::string dirpath = std::filesystem::path(fullpath).parent_path().string(); auto is_based = check_safe_io_path(fullpath, datadir); - DEBUG("io.open_data: safe:{} pack:{} based:{} mode:{} {}", is_safe, is_pack, is_based, mode.value_or("r"), fullpath); if (is_safe && !is_based) { luaL_error(global_vm, "Attempted to open data file outside data directory"); @@ -2799,7 +2803,6 @@ void add_partial_safe_libraries(sol::environment& env) std::string fullpath = std::string(backend->get_root()) + "/" + filename; auto is_based = check_safe_io_path(fullpath, backend->get_root()); std::string dirpath = std::filesystem::path(fullpath).parent_path().string(); - DEBUG("io.open_mod: safe:{} pack:{} based:{} mode:{} {}", is_safe, is_pack, is_based, mode.value_or("r"), fullpath); if (is_safe) { if (!is_based) @@ -2829,7 +2832,6 @@ void add_partial_safe_libraries(sol::environment& env) std::string fullpath = datadir + "/" + filename; std::string dirpath = std::filesystem::path(fullpath).parent_path().string(); auto is_based = check_safe_io_path(fullpath, datadir); - DEBUG("os.remove_data: safe:{} pack:{} based:{} {}", is_safe, is_pack, is_based, fullpath); if (is_safe && !is_based) { luaL_error(global_vm, "Attempted to remove data file outside data directory"); @@ -2846,7 +2848,6 @@ void add_partial_safe_libraries(sol::environment& env) std::string fullpath = std::string(backend->get_root()) + "/" + filename; auto is_based = check_safe_io_path(fullpath, backend->get_root()); std::string dirpath = std::filesystem::path(fullpath).parent_path().string(); - DEBUG("os.remove_mod: safe:{} pack:{} based:{} {}", is_safe, is_pack, is_based, fullpath); if (is_safe) { if (!is_based) From ddd0827a1d763573255aadd710977426616f808b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 10:15:32 +0000 Subject: [PATCH 32/42] update slate[no ci] --- docs/index.html | 15 ++++++++++++--- docs/light.html | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/index.html b/docs/index.html index f68b9fc0e..9ce4fdbe0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -450,6 +450,9 @@
  • Debug functions
      +
    • + dump +
    • dump_network
    • @@ -3831,7 +3834,7 @@

      math

      base

      string

      message(name) end

      Unsafe mode

      -

      Setting meta.unsafe = true enables the rest of the standard Lua libraries like unrestricted io and os, loading dlls with require and package.loadlib. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory.

      +

      Setting meta.unsafe = true enables the rest of the standard Lua libraries like unrestricted io, os, ffi and debug, loading dlls with require, package.loadlib, the network functions and some debug functions. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory.

      Modules

      You can load modules with require "mymod" or require "mydir.mymod", just put mymod.lua in the same directory the script is, or in mydir/ to keep things organized.

      @@ -4122,7 +4125,13 @@

      Debug functions

      dump_network

      +

      Debug functions

      dump

      +
      +

      Search script examples for dump

      +
      +

      table dump(object object, optional depth)

      +

      Dump the object (table, container, class) as a recursive table, for pretty printing in console. Don't use this for anything except debug printing. Unsafe.

      +

      dump_network

      Search script examples for dump_network

      @@ -5214,7 +5223,7 @@

      console_print

      Search script examples for console_print

      nil console_print(string message)

      -

      Print a log message to ingame console.

      +

      Print a log message to ingame console with a comment identifying the script that sent it.

      log_print

      Search script examples for log_print

      diff --git a/docs/light.html b/docs/light.html index 386291e73..b89f7cecc 100644 --- a/docs/light.html +++ b/docs/light.html @@ -450,6 +450,9 @@
    • Debug functions
        +
      • + dump +
      • dump_network
      • @@ -3831,7 +3834,7 @@

        math

        base

        string

        message(name) end

        Unsafe mode

        -

        Setting meta.unsafe = true enables the rest of the standard Lua libraries like unrestricted io and os, loading dlls with require and package.loadlib. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory.

        +

        Setting meta.unsafe = true enables the rest of the standard Lua libraries like unrestricted io, os, ffi and debug, loading dlls with require, package.loadlib, the network functions and some debug functions. Using unsafe scripts requires users to enable the option in the overlunky.ini file which is found in the Spelunky 2 installation directory.

        Modules

        You can load modules with require "mymod" or require "mydir.mymod", just put mymod.lua in the same directory the script is, or in mydir/ to keep things organized.

        @@ -4122,7 +4125,13 @@

        Debug functions

        dump_network

        +

        Debug functions

        dump

        +
        +

        Search script examples for dump

        +
        +

        table dump(object object, optional depth)

        +

        Dump the object (table, container, class) as a recursive table, for pretty printing in console. Don't use this for anything except debug printing. Unsafe.

        +

        dump_network

        Search script examples for dump_network

        @@ -5214,7 +5223,7 @@

        console_print

        Search script examples for console_print

    • nil console_print(string message)

      -

      Print a log message to ingame console.

      +

      Print a log message to ingame console with a comment identifying the script that sent it.

      log_print

      Search script examples for log_print

      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 33/42] 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 4ebebebb9fee464d899ca51bb27fac879b8c3fdb Mon Sep 17 00:00:00 2001 From: Dregu Date: Mon, 16 Oct 2023 12:32:25 +0300 Subject: [PATCH 34/42] fix infinite loop detection again --- src/game_api/script/lua_backend.cpp | 3 +++ src/game_api/script/lua_backend.hpp | 3 +++ src/game_api/script/lua_vm.cpp | 19 +++++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/game_api/script/lua_backend.cpp b/src/game_api/script/lua_backend.cpp index 3aee0fa04..b559c79b8 100644 --- a/src/game_api/script/lua_backend.cpp +++ b/src/game_api/script/lua_backend.cpp @@ -1330,6 +1330,9 @@ bool LuaBackend::process_vanilla_render_callbacks(ON event) if (!get_enabled()) return skip; + // used in infinite loop detection to see if game is hanging because a script is hanging + frame_counter++; + auto now = get_frame_count(); VanillaRenderContext render_ctx; for (auto& [id, callback] : callbacks) diff --git a/src/game_api/script/lua_backend.hpp b/src/game_api/script/lua_backend.hpp index 835cd8168..0693bdf47 100644 --- a/src/game_api/script/lua_backend.hpp +++ b/src/game_api/script/lua_backend.hpp @@ -322,6 +322,9 @@ class LuaBackend std::map images; + size_t frame_counter{0}; + bool infinite_loop_detection{true}; + LuaBackend(SoundManager* sound_manager, LuaConsole* console); virtual ~LuaBackend(); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 5ebd92de4..663eb5df1 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -224,15 +224,15 @@ void populate_lua_state(sol::state& lua, SoundManager* sound_manager) { auto infinite_loop = [](lua_State* argst, [[maybe_unused]] lua_Debug* argdb) { - static uint32_t last_frame = 0; - auto state = State::get().ptr(); - if (last_frame == state->time_startup) - luaL_error(argst, "Hit Infinite Loop Detection of 1bln instructions"); - last_frame = state->time_startup; + static size_t last_frame = 0; + auto backend = LuaBackend::get_calling_backend(); + if (last_frame == backend->frame_counter && backend->infinite_loop_detection) + luaL_error(argst, "Hit Infinite Loop Detection of 420 million instructions"); + last_frame = backend->frame_counter; }; lua_sethook(lua.lua_state(), NULL, 0, 0); - lua_sethook(lua.lua_state(), infinite_loop, LUA_MASKCOUNT, 500000000); + lua_sethook(lua.lua_state(), infinite_loop, LUA_MASKCOUNT, 420000000); lua.safe_script(R"( -- This function walks up the stack until it finds an _ENV that is not _G @@ -2160,6 +2160,13 @@ end return inputs; }; + /// Disable the Infinite Loop Detection of 420 million instructions per frame, if you know what you're doing and need to perform some serious calculations that hang the game updates for several seconds. + lua["set_infinite_loop_detection_enabled"] = [](bool enable) + { + auto backend = LuaBackend::get_calling_backend(); + backend->infinite_loop_detection = enable; + }; + 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 cb5575fa02e7c2e1343b224b54b5f1830703bb3a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:15:34 +0000 Subject: [PATCH 35/42] update slate[no ci] --- docs/game_data/spel2.lua | 4 ++++ docs/index.html | 9 +++++++++ docs/light.html | 9 +++++++++ docs/src/includes/_globals.md | 9 +++++++++ 4 files changed, 31 insertions(+) diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 4ddeecfaa..3e0f3894f 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1268,6 +1268,10 @@ function inputs_to_buttons(inputs) end ---@param buttons BUTTON ---@return INPUTS function buttons_to_inputs(x, y, buttons) end +---Disable the Infinite Loop Detection of 420 million instructions per frame, if you know what you're doing and need to perform some serious calculations that hang the game updates for several seconds. +---@param enable boolean +---@return nil +function set_infinite_loop_detection_enabled(enable) end ---@return boolean function toast_visible() end ---@return boolean diff --git a/docs/index.html b/docs/index.html index 9ce4fdbe0..b62ecf257 100644 --- a/docs/index.html +++ b/docs/index.html @@ -869,6 +869,9 @@
    • set_frametime_unfocused
    • +
    • + set_infinite_loop_detection_enabled +
    • set_journal_enabled
    • @@ -5005,6 +5008,12 @@

      set_frametime_unfocused

      nil set_frametime_unfocused(optional frametime)

      Set engine target frametime when game is unfocused (1/framerate, default 1/33). Always capped by the engine frametime. Set to 0 to go as fast as possible. Call without arguments to reset.

      +

      set_infinite_loop_detection_enabled

      +
      +

      Search script examples for set_infinite_loop_detection_enabled

      +
      +

      nil set_infinite_loop_detection_enabled(bool enable)

      +

      Disable the Infinite Loop Detection of 420 million instructions per frame, if you know what you're doing and need to perform some serious calculations that hang the game updates for several seconds.

      set_journal_enabled

      Search script examples for set_journal_enabled

      diff --git a/docs/light.html b/docs/light.html index b89f7cecc..cb0debc37 100644 --- a/docs/light.html +++ b/docs/light.html @@ -869,6 +869,9 @@
    • set_frametime_unfocused
    • +
    • + set_infinite_loop_detection_enabled +
    • set_journal_enabled
    • @@ -5005,6 +5008,12 @@

      set_frametime_unfocused

      nil set_frametime_unfocused(optional frametime)

      Set engine target frametime when game is unfocused (1/framerate, default 1/33). Always capped by the engine frametime. Set to 0 to go as fast as possible. Call without arguments to reset.

      +

      set_infinite_loop_detection_enabled

      +
      +

      Search script examples for set_infinite_loop_detection_enabled

      +
      +

      nil set_infinite_loop_detection_enabled(bool enable)

      +

      Disable the Infinite Loop Detection of 420 million instructions per frame, if you know what you're doing and need to perform some serious calculations that hang the game updates for several seconds.

      set_journal_enabled

      Search script examples for set_journal_enabled

      diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 30c12d1c7..fc7123016 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1672,6 +1672,15 @@ Set engine target frametime (1/framerate, default 1/60). Always capped by your G Set engine target frametime when game is unfocused (1/framerate, default 1/33). Always capped by the engine frametime. Set to 0 to go as fast as possible. Call without arguments to reset. +### set_infinite_loop_detection_enabled + + +> Search script examples for [set_infinite_loop_detection_enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_infinite_loop_detection_enabled) + +#### nil set_infinite_loop_detection_enabled(bool enable) + +Disable the Infinite Loop Detection of 420 million instructions per frame, if you know what you're doing and need to perform some serious calculations that hang the game updates for several seconds. + ### set_journal_enabled 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 36/42] 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 37/42] 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 38/42] 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 39/42] `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", From 6e59a9df76be580437dc3dcfe63f05d58c5ea239 Mon Sep 17 00:00:00 2001 From: Dregu Date: Mon, 16 Oct 2023 22:44:25 +0300 Subject: [PATCH 40/42] rando screen checks --- examples/randomizer2.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/randomizer2.lua b/examples/randomizer2.lua index 7cb2d872a..c04984717 100644 --- a/examples/randomizer2.lua +++ b/examples/randomizer2.lua @@ -6,7 +6,7 @@ meta.description = [[THIS REQUIRES 'PLAYLUNKY VERSION > NIGHTLY' IN MODLUNKY! YO I recommend resetting options to defaults after updating to 2.9 for a more balanced experience. Speaking of balance... Fair, balanced, beginner friendly... These are not words I would use to describe The Randomizer. Fun though? Abso-hecking-lutely.]] -meta.version = "2.9c" +meta.version = "2.9d" meta.author = "Dregu" --[[OPTIONS]] @@ -591,12 +591,14 @@ local function spawn_boss(boss) end set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end if has(all_boss_levels, state.theme) and options.bosses then next_boss = pick(insert_bosses) end end, ON.PRE_LEVEL_GENERATION) set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end if has(all_boss_levels, state.theme) and options.bosses then local boss = next_boss spawn_boss(boss) @@ -1798,6 +1800,7 @@ set_callback(function() end, ON.START) set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end local in_shop = {} local items = get_entities_by(0, MASK.ITEM | MASK.MOUNT | MASK.PLAYER | MASK.MONSTER, LAYER.BOTH) for i,v in ipairs(items) do @@ -1996,6 +1999,7 @@ set_post_entity_spawn(function(ent) end, SPAWN_TYPE.ANY, 0, ENT_TYPE.ITEM_USHABTI) set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end local coffins = get_entities_by_type(ENT_TYPE.ITEM_COFFIN) for i,v in ipairs(coffins) do local ent = get_entity(v) @@ -3119,6 +3123,7 @@ end, ON.GUIFRAME)]] --[[LIQUIDS]] --[[SPIKES]] set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end swapping_liquid = (not has(all_boss_levels, state.theme)) and prng:random() < options.liquid_chance/100 if state.theme == THEME.OLMEC then replace_drop(DROP.OLMEC_SISTERS_BOMBBOX, pick(crate_items)) @@ -3223,6 +3228,7 @@ set_pre_tile_code_callback(function(x, y, l) end, "timed_forcefield") set_callback(function() + if state.screen ~= SCREEN.LEVEL then return end for i,v in ipairs(get_entities_by_type(ENT_TYPE.FLOOR_QUICKSAND)) do local decos = entity_get_items_by(v, ENT_TYPE.DECORATION_GENERIC, 0) for j,d in ipairs(decos) do From abf1343315d8fb6b4112a697ad950ce7d2cd95df Mon Sep 17 00:00:00 2001 From: Dregu Date: Mon, 16 Oct 2023 23:04:40 +0300 Subject: [PATCH 41/42] rando: remove hundun random boss shenanigans --- examples/randomizer2.lua | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/examples/randomizer2.lua b/examples/randomizer2.lua index c04984717..1494a4a15 100644 --- a/examples/randomizer2.lua +++ b/examples/randomizer2.lua @@ -6,7 +6,7 @@ meta.description = [[THIS REQUIRES 'PLAYLUNKY VERSION > NIGHTLY' IN MODLUNKY! YO I recommend resetting options to defaults after updating to 2.9 for a more balanced experience. Speaking of balance... Fair, balanced, beginner friendly... These are not words I would use to describe The Randomizer. Fun though? Abso-hecking-lutely.]] -meta.version = "2.9d" +meta.version = "2.9e" meta.author = "Dregu" --[[OPTIONS]] @@ -522,12 +522,7 @@ local function spawn_boss(boss) elseif state.theme == THEME.TIAMAT then spawn_critical(boss, 18.5, 60.5, LAYER.FRONT, 0, 0) elseif state.theme == THEME.HUNDUN then - local ce = get_entity(get_entities_by(ENT_TYPE.ACTIVEFLOOR_CRUSHING_ELEVATOR, MASK.ACTIVEFLOOR, LAYER.FRONT)[1]) - if ce then - spawn_over(boss, ce.uid, 1, 12) - else - spawn_critical(boss, 17.5, 107, LAYER.FRONT, 0, 0) - end + spawn_critical(boss, 18.5, 106.5, LAYER.FRONT, 0, 0) elseif state.theme == THEME.EGGPLANT_WORLD then spawn_critical(boss, 23.5, 116.5, LAYER.FRONT, 0, 0) end @@ -577,12 +572,7 @@ local function spawn_boss(boss) elseif state.theme == THEME.TIAMAT then spawn_critical(boss, 17.5, 59.5, LAYER.FRONT, 0, 0) elseif state.theme == THEME.HUNDUN then - local ce = get_entity(get_entities_by(ENT_TYPE.ACTIVEFLOOR_CRUSHING_ELEVATOR, MASK.ACTIVEFLOOR, LAYER.FRONT)[1]) - if ce then - spawn_over(boss, ce.uid, 0, 11) - else - spawn_critical(boss, 17.5, 106, LAYER.FRONT, 0, 0) - end + spawn_critical(boss, 17.5, 105.5, LAYER.FRONT, 0, 0) elseif state.theme == THEME.EGGPLANT_WORLD then spawn_critical(boss, 22.5, 115.5, LAYER.FRONT, 0, 0) end @@ -741,12 +731,6 @@ local function random_bosses(enable) end end, SPAWN_TYPE.ANY, MASK.ACTIVEFLOOR, ENT_TYPE.ACTIVEFLOOR_OLMEC) - -- Don't collide moving tiamat platforms with floor - boss_cbs[#boss_cbs + 1] = set_post_entity_spawn(function(e) - e.flags = clr_flag(e.flags, ENT_FLAG.COLLIDES_WALLS) - end, SPAWN_TYPE.ANY, MASK.ACTIVEFLOOR, - { ENT_TYPE.ACTIVEFLOOR_TIAMAT_PLATFORM, ENT_TYPE.ACTIVEFLOOR_TIAMAT_SHOULDERPLATFORM }) - -- Don't kill tiamat or kingu with lame regen blocks boss_cbs[#boss_cbs + 1] = set_post_entity_spawn(function(e) e:set_pre_kill(function(_, _, killer) @@ -756,13 +740,6 @@ local function random_bosses(enable) end) end, SPAWN_TYPE.ANY, MASK.MONSTER, {ENT_TYPE.MONS_TIAMAT, ENT_TYPE.MONS_KINGU}) - -- Replace (moving) yama platforms with activefloor - boss_cbs[#boss_cbs + 1] = set_pre_entity_spawn(function(type, x, y, l, overlay) - if overlay then - return spawn_over(ENT_TYPE.ACTIVEFLOOR_TIAMAT_PLATFORM, overlay.uid, x, y+0.25) - end - end, SPAWN_TYPE.ANY, MASK.FLOOR, ENT_TYPE.FLOOR_YAMA_PLATFORM) - -- Lol boss_cbs[#boss_cbs + 1] = set_post_entity_spawn(function(e) if state.theme == THEME.OLMEC then From cb08eb1f6c92f1457f0441513c0d20904dc92688 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:12:34 +0000 Subject: [PATCH 42/42] update slate[no ci] --- docs/index.html | 151 +++++++++++++++++++++++++++++++++++++++++++++--- docs/light.html | 151 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 284 insertions(+), 18 deletions(-) diff --git a/docs/index.html b/docs/index.html index b62ecf257..7aa6bec2b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -524,6 +524,9 @@
    • get_entities_by
    • +
    • + get_entities_by_draw_depth +
    • get_entities_by_type
    • @@ -713,6 +716,15 @@
    • activate_hundun_hack
    • +
    • + add_custom_type +
    • +
    • + add_money +
    • +
    • + add_money_slot +
    • change_poison_timer
    • @@ -758,6 +770,9 @@
    • get_character_heart_color
    • +
    • + get_current_money +
    • get_frame
    • @@ -767,6 +782,9 @@
    • get_frametime_unfocused
    • +
    • + get_hud +
    • get_id
    • @@ -3639,6 +3657,9 @@
    • QUEST_FLAG
    • +
    • + RECURSIVE_MODE +
    • RENDER_INFO_OVERRIDE
    • @@ -4295,6 +4316,13 @@

      get_entities_by

      ENT_TYPE> entity_types, int mask, LAYER layer)

      array<int> get_entities_by(ENT_TYPE entity_type, int mask, LAYER layer)

      Get uids of entities by some conditions (ENT_TYPE, 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

      +
      +

      array<int> get_entities_by_draw_depth(int draw_depth, LAYER l)

      array<int> get_entities_by_draw_depth(array draw_depths, LAYER l)

      +

      Get uids of entities by draw_depth. Can also use table of draw_depths. +You can later use filter_entities if you want specific entity

      get_entities_by_type

      local types = {ENT_TYPE.MONS_SNAKE, ENT_TYPE.MONS_BAT}
       set_callback(function()
           local uids = get_entities_by_type(ENT_TYPE.MONS_SNAKE, ENT_TYPE.MONS_BAT)
      @@ -4670,6 +4698,27 @@ 

      nil activate_hundun_hack(bool ac

      Activate custom variables for y coordinate limit for hundun and spawn of it's heads note: because those variables are custom and game does not initiate them, you need to do it yourself for each 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

      +
      +

      ENT_TYPE add_custom_type(array<ENT_TYPE> types)

      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 that can be used for custom EntityDB

      +

      add_money

      +
      +

      Search script examples for add_money

      +
      +

      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). Returns the current money after the transaction

      +

      add_money_slot

      +
      +

      Search script examples for add_money_slot

      +
      +

      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). Returns the current money after the transaction

      change_poison_timer

      Search script examples for change_poison_timer

      @@ -4763,6 +4812,13 @@

      get_character_heart_color

      Color get_character_heart_color(ENT_TYPE type_id)

      Same as Player.get_heart_color

      +

      get_current_money

      +
      +

      Search script examples for 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

      Search script examples for get_frame

      @@ -4781,7 +4837,11 @@

      get_frametime_unfocused

      optional<double> get_frametime_unfocused()

      Get engine target frametime when game is unfocused (1/framerate, default 1/33).

      -

      get_id

      +

      get_hud

      +
      +

      Search script examples for get_hud

      +
      +

      HudData get_hud()

      get_id

      Search script examples for get_id

      @@ -10640,8 +10700,23 @@

      LevelGenSystem

      -ThemeInfo -themes[18] +array<ThemeInfo, 18> +themes + + + +int +flags + + + +int +flags2 + + + +int +flags3 @@ -15302,12 +15377,12 @@

      SoundMeta

      array<float, 38> 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 array<float, 38> right_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 bool @@ -15534,6 +15609,11 @@

      GameManager

      save_related + +BackgroundSound +main_menu_music + +

      GameProps

      @@ -15595,14 +15675,19 @@

      Items

      + + + + + - - - + + +
      Index of leader player in coop
      array<SelectPlayerSlot, MAX_PLAYERS>player_select
      array<Inventory, MAX_PLAYERS> player_inventory
      array<SelectPlayerSlot, MAX_PLAYERS>player_selectarray<Player, MAX_PLAYERS>playersTable of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body)

      JournalProgressStainSlot

      @@ -19705,6 +19790,26 @@

      Entity

      +nil +kill_recursive(bool destroy_corpse, Entity responsible, optional mask, const array ent_types, RECURSIVE_MODE rec_mode) +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 + + +nil +kill_recursive(bool destroy_corpse, Entity responsible) +Short for using RECURSIVE_MODE.NONE + + +nil +destroy_recursive(optional mask, const array ent_types, RECURSIVE_MODE rec_mode) +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 + + +nil +destroy_recursive() +Short for using RECURSIVE_MODE.NONE + + CallbackId set_pre_virtual(ENTITY_OVERRIDE entry, function fun) Hooks before the virtual function at index entry. @@ -24509,7 +24614,7 @@

      Drill

      nil -trigger() +trigger(optional play_sound_effect) @@ -31550,6 +31655,34 @@

      QUEST_FLAG

      +

      RECURSIVE_MODE

      +
      +

      Search script examples for RECURSIVE_MODE

      +
      + + + + + + + + + + + + + + + + + + + + + + + +
      NameDataDescription
      EXCLUSIVERECURSIVE_MODE::EXCLUSIVEIn this mode the provided ENT_TYPE and MASK will not be affected nor will entities attached to them
      INCLUSIVERECURSIVE_MODE::INCLUSIVEIn 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
      NONERECURSIVE_MODE::NONEIgnores provided ENT_TYPE and MASK and affects all the entities

      RENDER_INFO_OVERRIDE

      Search script examples for RENDER_INFO_OVERRIDE

      diff --git a/docs/light.html b/docs/light.html index cb0debc37..8fc3a20c1 100644 --- a/docs/light.html +++ b/docs/light.html @@ -524,6 +524,9 @@
    • get_entities_by
    • +
    • + get_entities_by_draw_depth +
    • get_entities_by_type
    • @@ -713,6 +716,15 @@
    • activate_hundun_hack
    • +
    • + add_custom_type +
    • +
    • + add_money +
    • +
    • + add_money_slot +
    • change_poison_timer
    • @@ -758,6 +770,9 @@
    • get_character_heart_color
    • +
    • + get_current_money +
    • get_frame
    • @@ -767,6 +782,9 @@
    • get_frametime_unfocused
    • +
    • + get_hud +
    • get_id
    • @@ -3639,6 +3657,9 @@
    • QUEST_FLAG
    • +
    • + RECURSIVE_MODE +
    • RENDER_INFO_OVERRIDE
    • @@ -4295,6 +4316,13 @@

      get_entities_by

      ENT_TYPE> entity_types, int mask, LAYER layer)

      array<int> get_entities_by(ENT_TYPE entity_type, int mask, LAYER layer)

      Get uids of entities by some conditions (ENT_TYPE, 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

      +
      +

      array<int> get_entities_by_draw_depth(int draw_depth, LAYER l)

      array<int> get_entities_by_draw_depth(array draw_depths, LAYER l)

      +

      Get uids of entities by draw_depth. Can also use table of draw_depths. +You can later use filter_entities if you want specific entity

      get_entities_by_type

      local types = {ENT_TYPE.MONS_SNAKE, ENT_TYPE.MONS_BAT}
       set_callback(function()
           local uids = get_entities_by_type(ENT_TYPE.MONS_SNAKE, ENT_TYPE.MONS_BAT)
      @@ -4670,6 +4698,27 @@ 

      nil activate_hundun_hack(bool ac

      Activate custom variables for y coordinate limit for hundun and spawn of it's heads note: because those variables are custom and game does not initiate them, you need to do it yourself for each 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

      +
      +

      ENT_TYPE add_custom_type(array<ENT_TYPE> types)

      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 that can be used for custom EntityDB

      +

      add_money

      +
      +

      Search script examples for add_money

      +
      +

      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). Returns the current money after the transaction

      +

      add_money_slot

      +
      +

      Search script examples for add_money_slot

      +
      +

      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). Returns the current money after the transaction

      change_poison_timer

      Search script examples for change_poison_timer

      @@ -4763,6 +4812,13 @@

      get_character_heart_color

      Color get_character_heart_color(ENT_TYPE type_id)

      Same as Player.get_heart_color

      +

      get_current_money

      +
      +

      Search script examples for 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

      Search script examples for get_frame

      @@ -4781,7 +4837,11 @@

      get_frametime_unfocused

      optional<double> get_frametime_unfocused()

      Get engine target frametime when game is unfocused (1/framerate, default 1/33).

      -

      get_id

      +

      get_hud

      +
      +

      Search script examples for get_hud

      +
      +

      HudData get_hud()

      get_id

      Search script examples for get_id

      @@ -10640,8 +10700,23 @@

      LevelGenSystem

      -ThemeInfo -themes[18] +array<ThemeInfo, 18> +themes + + + +int +flags + + + +int +flags2 + + + +int +flags3 @@ -15302,12 +15377,12 @@

      SoundMeta

      array<float, 38> 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 array<float, 38> right_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 bool @@ -15534,6 +15609,11 @@

      GameManager

      save_related + +BackgroundSound +main_menu_music + +

      GameProps

      @@ -15595,14 +15675,19 @@

      Items

      + + + + + - - - + + +
      Index of leader player in coop
      array<SelectPlayerSlot, MAX_PLAYERS>player_select
      array<Inventory, MAX_PLAYERS> player_inventory
      array<SelectPlayerSlot, MAX_PLAYERS>player_selectarray<Player, MAX_PLAYERS>playersTable of players, also keeps the dead body until they are destroyed (necromancer revive also destroys the old body)

      JournalProgressStainSlot

      @@ -19705,6 +19790,26 @@

      Entity

      +nil +kill_recursive(bool destroy_corpse, Entity responsible, optional mask, const array ent_types, RECURSIVE_MODE rec_mode) +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 + + +nil +kill_recursive(bool destroy_corpse, Entity responsible) +Short for using RECURSIVE_MODE.NONE + + +nil +destroy_recursive(optional mask, const array ent_types, RECURSIVE_MODE rec_mode) +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 + + +nil +destroy_recursive() +Short for using RECURSIVE_MODE.NONE + + CallbackId set_pre_virtual(ENTITY_OVERRIDE entry, function fun) Hooks before the virtual function at index entry. @@ -24509,7 +24614,7 @@

      Drill

      nil -trigger() +trigger(optional play_sound_effect) @@ -31550,6 +31655,34 @@

      QUEST_FLAG

      +

      RECURSIVE_MODE

      +
      +

      Search script examples for RECURSIVE_MODE

      +
      + + + + + + + + + + + + + + + + + + + + + + + +
      NameDataDescription
      EXCLUSIVERECURSIVE_MODE::EXCLUSIVEIn this mode the provided ENT_TYPE and MASK will not be affected nor will entities attached to them
      INCLUSIVERECURSIVE_MODE::INCLUSIVEIn 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
      NONERECURSIVE_MODE::NONEIgnores provided ENT_TYPE and MASK and affects all the entities

      RENDER_INFO_OVERRIDE

      Search script examples for RENDER_INFO_OVERRIDE