diff options
-rw-r--r-- | editor/imgui-raii.cpp | 14 | ||||
-rw-r--r-- | editor/imgui-raii.hpp | 7 | ||||
-rw-r--r-- | editor/inspect-types.cpp | 34 | ||||
-rw-r--r-- | editor/inspect.cpp | 14 | ||||
-rw-r--r-- | editor/tests-private.hpp | 14 | ||||
-rw-r--r-- | editor/tests/hole-test.cpp | 98 | ||||
-rw-r--r-- | editor/vobj-editor.cpp | 30 | ||||
-rw-r--r-- | entity/concepts.hpp | 1 | ||||
-rw-r--r-- | serialize/savegame.cpp | 6 | ||||
-rw-r--r-- | src/hole-cut.cpp | 9 | ||||
-rw-r--r-- | src/hole.cpp | 70 | ||||
-rw-r--r-- | src/hole.hpp | 26 | ||||
-rw-r--r-- | test/hole.cpp | 16 | ||||
-rw-r--r-- | test/save.cpp | 16 | ||||
-rw-r--r-- | vobj/vobj.json | 5 |
15 files changed, 310 insertions, 50 deletions
diff --git a/editor/imgui-raii.cpp b/editor/imgui-raii.cpp index 8cae0818..b5997c7b 100644 --- a/editor/imgui-raii.cpp +++ b/editor/imgui-raii.cpp @@ -1,5 +1,6 @@ #include "imgui-raii.hpp" #include "compat/assert.hpp" +#include <cstdio> #include <Corrade/Containers/StringView.h> #include <Magnum/Magnum.h> #include <Magnum/Math/Color.h> @@ -159,4 +160,17 @@ raii_wrapper begin_child(StringView name, const ImVec2& size, int flags, int win return {&ImGui::EndChild}; } +const char* label_left_(StringView label, char* buf, size_t buf_len, float width); + +const char* label_left_(StringView label, char* buf, size_t buf_len, float width) +{ + std::snprintf(buf, buf_len, "##%s", label.data()); + float x = ImGui::GetCursorPosX(); + ImGui::TextEx(label.data(), label.data() + label.size()); + ImGui::SameLine(); + ImGui::SetCursorPosX(x + width + ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SetNextItemWidth(-1); + return buf; +} + } // namespace floormat::imgui diff --git a/editor/imgui-raii.hpp b/editor/imgui-raii.hpp index 63c749bb..59134deb 100644 --- a/editor/imgui-raii.hpp +++ b/editor/imgui-raii.hpp @@ -67,4 +67,11 @@ private: float font_size; }; +template<std::size_t N> +const char* label_left(StringView label, char(&buf)[N], float width) +{ + const char* label_left_(StringView, char*, size_t, float); + return label_left_(label, static_cast<char*>(buf), N, width); +} + } // namespace floormat::imgui diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index cb85f574..0968eeec 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -9,6 +9,7 @@ #include "src/chunk.hpp" #include "src/critter.hpp" #include "src/light.hpp" +#include "src/hole.hpp" #include <Corrade/Containers/ArrayViewStl.h> #include <imgui.h> @@ -140,6 +141,38 @@ template<> struct entity_accessors<door_scenery, inspect_intent_t> } }; +template<> struct entity_accessors<hole, inspect_intent_t> +{ + static constexpr auto accessors() + { + using E = Entity<hole>; + auto tuple0 = entity_accessors<object, inspect_intent_t>::accessors(); + auto tuple = std::tuple{ + E::type<uint8_t>::field{"height"_s, + &hole::height, + &hole::set_height, + [](const hole& x) { return x.flags.is_wall ? st::enabled : st::readonly; }, + }, + E::type<uint8_t>::field{"z-offset"_s, + &hole::z_offset, + &hole::set_z_offset, + [](const hole& x) { return x.flags.is_wall ? st::enabled : st::readonly; }, + constantly(constraints::range<uint8_t>{0, tile_size_z}), + }, + E::type<bool>::field{"on-render"_s, + [](const hole& x) { return x.flags.on_render; }, + [](hole& x, bool value) { x.set_enabled(value, x.flags.on_physics); }, + }, + E::type<bool>::field{ + "on-physics"_s, + [](const hole& x) { return x.flags.on_physics; }, + [](hole& x, bool value) { x.set_enabled(x.flags.on_render, value); }, + }, + }; + return std::tuple_cat(tuple0, tuple); + } +}; + template<typename, typename = void> struct has_anim_atlas : std::false_type {}; template<typename T> @@ -318,6 +351,7 @@ bool inspect_object_subtype(object& x) } case object_type::critter: return inspect_type(static_cast<critter&>(x), inspect_intent_t{}); case object_type::light: return inspect_type(static_cast<light&>(x), inspect_intent_t{}); + case object_type::hole: return inspect_type(static_cast<hole&>(x), inspect_intent_t{}); } fm_warn_once("unknown object subtype '%d'", (int)type); return false; diff --git a/editor/inspect.cpp b/editor/inspect.cpp index e7d8878a..349b5e3d 100644 --- a/editor/inspect.cpp +++ b/editor/inspect.cpp @@ -17,18 +17,6 @@ namespace floormat::entities { namespace { -template<std::size_t N> -const char* label_left(StringView label, char(&buf)[N], size_t width) -{ - std::snprintf(buf, N, "##%s", label.data()); - float x = ImGui::GetCursorPosX(); - ImGui::TextEx(label.data(), label.data() + label.size()); - ImGui::SameLine(); - ImGui::SetCursorPosX(x + (float)width + ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SetNextItemWidth(-1); - return buf; -} - template<typename T> struct IGDT_; template<> struct IGDT_<uint8_t> : std::integral_constant<int, ImGuiDataType_U8> {}; template<> struct IGDT_<int8_t> : std::integral_constant<int, ImGuiDataType_S8> {}; @@ -89,7 +77,7 @@ bool do_inspect_field(void* datum, const erased_accessor& accessor, field_repr r should_disable = should_disable || !accessor.can_write(); [[maybe_unused]] auto disabler = begin_disabled(should_disable); bool ret = false; - const char* const label = label_left(accessor.field_name, buf, label_width); + const char* const label = label_left(accessor.field_name, buf, (float)label_width); T value{}; accessor.read_fun(datum, accessor.reader, &value); auto orig = value; diff --git a/editor/tests-private.hpp b/editor/tests-private.hpp index dc939878..d6a04cf5 100644 --- a/editor/tests-private.hpp +++ b/editor/tests-private.hpp @@ -32,7 +32,7 @@ protected: enum class Test : uint32_t { //todo add a speedometer overlay test - none, path, raycast, region, walk, COUNT, + none, path, raycast, region, walk, hole, COUNT, }; struct tests_data final : tests_data_ @@ -46,6 +46,7 @@ struct tests_data final : tests_data_ static Pointer<base_test> make_test_raycast(); static Pointer<base_test> make_test_region(); static Pointer<base_test> make_test_walk(); + static Pointer<base_test> make_test_hole(); Pointer<base_test> current_test; Test current_index = Test::none; @@ -58,11 +59,12 @@ struct tests_data final : tests_data_ }; static constexpr test_tuple fields[] = { - { "None"_s, Test::none, make_test_none, }, - { "Path search"_s, Test::path, make_test_path, }, - { "Raycasting"_s, Test::raycast, make_test_raycast }, - { "Region"_s, Test::region, make_test_region }, - { "Walking"_s, Test::walk, make_test_walk }, + { "None"_s, Test::none, make_test_none, }, + { "Path search"_s, Test::path, make_test_path, }, + { "Raycasting"_s, Test::raycast, make_test_raycast }, + { "Region"_s, Test::region, make_test_region }, + { "Walking"_s, Test::walk, make_test_walk }, + { "Hole"_s, Test::hole, make_test_hole }, }; }; diff --git a/editor/tests/hole-test.cpp b/editor/tests/hole-test.cpp new file mode 100644 index 00000000..6cb60449 --- /dev/null +++ b/editor/tests/hole-test.cpp @@ -0,0 +1,98 @@ +#include "../tests-private.hpp" +#include "compat/shared-ptr-wrapper.hpp" +#include "src/tile-constants.hpp" +#include "src/chunk-region.hpp" +#include "src/object.hpp" +#include "src/world.hpp" +#include "../app.hpp" +#include "../imgui-raii.hpp" +#include "floormat/main.hpp" +#include "src/critter.hpp" + +namespace floormat::tests { +namespace { + +using namespace floormat::imgui; + +struct hole_test final : base_test +{ + ~hole_test() noexcept override = default; + + bool handle_key(app& a, const key_event& e, bool is_down) override; + bool handle_mouse_click(app& a, const mouse_button_event& e, bool is_down) override; + bool handle_mouse_move(app& a, const mouse_move_event& e) override; + void draw_overlay(app& a) override; + void draw_ui(app& a, float menu_bar_height) override; + void update_pre(app& a, const Ns& dt) override; + void update_post(app&, const Ns&) override {} +}; + +bool hole_test::handle_key(app& a, const key_event& e, bool is_down) +{ + return false; +} + +bool hole_test::handle_mouse_click(app& a, const mouse_button_event& e, bool is_down) +{ + return false; +} + +bool hole_test::handle_mouse_move(app& a, const mouse_move_event& e) +{ + return false; +} + +void hole_test::draw_overlay(app& a) +{ +} + +void hole_test::draw_ui(app& a, float menu_bar_height) +{ + const auto& m = a.main(); + const auto size_x = ImGui::GetWindowSize().x; + const auto window_size = ImVec2{size_x, size_x}; + //const auto dpi = m.dpi_scale(); + constexpr auto igcf = ImGuiChildFlags_None; + constexpr auto igwf = ImGuiWindowFlags_NoDecoration; + + ImGui::NewLine(); + + char buf[32]; + + ImGui::LabelText("##test-area", "Test area"); + + ImGui::NewLine(); + if (auto b1 = imgui::begin_child("Test area"_s, window_size, igcf, igwf)) + { + const auto& win = *ImGui::GetCurrentWindow(); + ImDrawList& draw = *win.DrawList; + } + ImGui::NewLine(); + + const auto label_width = ImGui::CalcTextSize("MMMM").x; + + label_left("width", buf, label_width); + ImGui::NewLine(); + + label_left("height", buf, label_width); + ImGui::NewLine(); + + label_left("x", buf, label_width); + ImGui::NewLine(); + + label_left("y", buf, label_width); + ImGui::NewLine(); + + label_left("z", buf, label_width); + ImGui::NewLine(); +} + +void hole_test::update_pre(app& a, const Ns& dt) +{ +} + +} // namespace + +Pointer<base_test> tests_data::make_test_hole() { return Pointer<hole_test>{InPlaceInit}; } + +} // namespace floormat::tests diff --git a/editor/vobj-editor.cpp b/editor/vobj-editor.cpp index e82c6d27..9eb36e39 100644 --- a/editor/vobj-editor.cpp +++ b/editor/vobj-editor.cpp @@ -1,6 +1,7 @@ #include "vobj-editor.hpp" #include "src/world.hpp" #include "src/light.hpp" +#include "src/hole.hpp" #include "loader/loader.hpp" #include "loader/vobj-cell.hpp" #include "app.hpp" @@ -74,6 +75,8 @@ start: while (auto id = a.get_object_colliding_with_cursor()) #pragma clang diagnostic ignored "-Wweak-vtables" #endif +namespace { + struct light_factory final : vobj_factory { object_type type() const override; @@ -98,6 +101,32 @@ std::shared_ptr<object> light_factory::make(world& w, object_id id, global_coord return ret; } +struct hole_factory final : vobj_factory +{ + object_type type() const override; + const vobj_cell& info() const override; + std::shared_ptr<object> make(world& w, object_id id, global_coords pos) const override; +}; + +object_type hole_factory::type() const { return object_type::hole; } + +const vobj_cell& hole_factory::info() const +{ + constexpr auto NAME = "hole"_s; + static const vobj_cell& ret = loader.vobj(NAME); + fm_debug_assert(ret.name == NAME); + fm_debug_assert(ret.atlas != nullptr); + return ret; +} + +std::shared_ptr<object> hole_factory::make(world& w, object_id id, global_coords pos) const +{ + auto ret = w.make_object<hole>(id, pos, hole_proto{}); + return ret; +} + +} // namespace + auto vobj_editor::make_vobj_type_map() -> std::map<String, vobj_> { constexpr auto add = [](auto& m, std::unique_ptr<vobj_factory>&& x) { @@ -106,6 +135,7 @@ auto vobj_editor::make_vobj_type_map() -> std::map<String, vobj_> }; std::map<String, vobj_> map; add(map, std::make_unique<light_factory>()); + add(map, std::make_unique<hole_factory>()); return map; } diff --git a/entity/concepts.hpp b/entity/concepts.hpp index 871f6bbe..5e01fcda 100644 --- a/entity/concepts.hpp +++ b/entity/concepts.hpp @@ -38,6 +38,7 @@ template<typename F, typename T, typename FieldType> concept FieldWriter_ptr = requires(T& x, move_qualified<FieldType> value, F f) { requires std::is_reference_v<decltype(x.*f)>; requires !std::is_const_v<std::remove_reference_t<decltype(x.*f)>>; + { x.*f } -> std::convertible_to<FieldType&>; { x.*f = value }; }; diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index ef46a3f9..5c7e9b04 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -203,6 +203,8 @@ struct visitor_ case object_type::critter: self.visit(obj.atlas, atlas_type::anim, f); break; + case object_type::hole: + fm_abort("todo! not implemented"); } fm_debug_assert(obj.atlas); @@ -448,6 +450,8 @@ struct writer final : visitor_<writer, true> case object_type::scenery: write_scenery_proto(static_cast<const scenery&>(obj), f); goto ok; + case object_type::hole: + fm_abort("todo! not implemented"); } fm_assert(false); ok: void(); @@ -896,6 +900,8 @@ ok: obj = move(objʹ); goto ok; } + case object_type::hole: + fm_abort("todo! not implemented"); } fm_throw("invalid object_type {}"_cf, (int)type); ok: diff --git a/src/hole-cut.cpp b/src/hole-cut.cpp index f87a6c2f..3983e4fd 100644 --- a/src/hole-cut.cpp +++ b/src/hole-cut.cpp @@ -156,7 +156,10 @@ constexpr cut_rectangle_result cut_rectangleʹ(bbox input, bbox hole) auto h1 = hole.position + Vector2i{hole.bbox_size} - hhalf; if (check_empty(r0, r1, h0, h1, input.bbox_size, hole.bbox_size)) - return {0, {}}; + return { + .size = 1, + .array = {{ rect { r0, r1 }, }}, + }; const bool sx = h0.x() <= r0.x(); const bool ex = h1.x() >= r1.x(); @@ -166,12 +169,12 @@ constexpr cut_rectangle_result cut_rectangleʹ(bbox input, bbox hole) auto val = uint8_t(sx << 0 | ex << 1 | sy << 2 | ey << 3); CORRADE_ASSUME(val < 16); const auto elt = elements[val]; + const auto sz = elt.size; cut_rectangle_result res = { - .size = elt.size, + .size = sz, .array = {}, }; - const auto sz = elt.size; CORRADE_ASSUME(sz <= 8); for (auto i = 0u; i < sz; i++) diff --git a/src/hole.cpp b/src/hole.cpp index 783fa5ee..d013187b 100644 --- a/src/hole.cpp +++ b/src/hole.cpp @@ -2,39 +2,55 @@ #include "chunk.hpp" #include "tile-constants.hpp" #include "shaders/shader.hpp" +#include "compat/non-const.hpp" namespace floormat { namespace { } // namespace +hole_proto::~hole_proto() noexcept = default; +hole_proto::hole_proto() = default; +hole_proto::hole_proto(const hole_proto&) = default; +hole_proto& hole_proto::operator=(const hole_proto&) = default; +hole_proto::hole_proto(hole_proto&&) noexcept = default; +hole_proto& hole_proto::operator=(hole_proto&&) noexcept = default; + +bool hole_proto::flags::operator==(const struct flags&) const = default; +bool hole_proto::operator==(const hole_proto&) const = default; + hole::hole(object_id id, floormat::chunk& c, const hole_proto& proto): - object{id, c, proto} + object{id, c, proto}, height{proto.height}, flags{proto.flags} { + } hole::~hole() noexcept { - c->mark_ground_modified(); - c->mark_walls_modified(); - c->mark_passability_modified(); -} + if (c->is_teardown()) [[unlikely]] + return; -void hole::update(const std::shared_ptr<object>& ptr, size_t& i, const Ns& dt) -{ + mark_chunk_modified(); } +void hole::update(const std::shared_ptr<object>&, size_t&, const Ns&) {} + hole::operator hole_proto() const { hole_proto ret; static_cast<object_proto&>(ret) = object_proto(*this); - ret.max_distance = max_distance; - ret.color = color; - ret.falloff = falloff; - ret.enabled = enabled; + ret.height = height; + ret.flags = flags; return ret; } +void hole::mark_chunk_modified() +{ + c->mark_ground_modified(); // todo conditionalize + c->mark_walls_modified(); // todo conditionalize + c->mark_passability_modified(); +} + float hole::depth_offset() const { constexpr auto ret = 4 / tile_shader::depth_tile_size; @@ -47,8 +63,38 @@ Vector2 hole::ordinal_offset(Vector2b) const return ret; } +void hole::set_height(uint8_t heightʹ) +{ + if (height != heightʹ) + { + const_cast<uint8_t&>(height) = heightʹ; + mark_chunk_modified(); + } +} + +void hole::set_z_offset(uint8_t z) +{ + if (z_offset != z) + { + const_cast<uint8_t&>(z_offset) = z; + mark_chunk_modified(); + } +} + + +void hole::set_enabled(bool on_render, bool on_physics) +{ + non_const(flags).on_render = on_render; + + if (flags.on_physics != on_physics) + { + non_const(flags).on_physics = on_physics; + mark_chunk_modified(); + } +} + object_type hole::type() const noexcept { return object_type::hole; } bool hole::is_virtual() const { return true; } -bool hole::is_dynamic() const { return false; } +bool hole::is_dynamic() const { return true; } } // namespace floormat diff --git a/src/hole.hpp b/src/hole.hpp index 64e53149..49d1e809 100644 --- a/src/hole.hpp +++ b/src/hole.hpp @@ -14,18 +14,23 @@ struct hole_proto final : object_proto hole_proto& operator=(hole_proto&&) noexcept; bool operator==(const hole_proto&) const; + struct flags + { + bool operator==(const flags&) const; + + bool on_render : 1 = true; + bool on_physics : 1 = true; + bool is_wall : 1 = false; + }; + uint8_t height = 0; - bool on_render : 1 = true; - bool on_physics : 1 = true; - bool is_wall : 1 = false; + struct flags flags; }; struct hole final : object { - uint8_t _height = 0; - const bool on_render : 1 = true; - const bool on_physics : 1 = true; - const bool is_wall : 1 = false; + const uint8_t height = 0, z_offset = tile_size_z/2; + const struct hole_proto::flags flags; hole(object_id id, class chunk& c, const hole_proto& proto); ~hole() noexcept override; @@ -37,9 +42,16 @@ struct hole final : object bool is_dynamic() const override; bool is_virtual() const override; + void set_height(uint8_t height); + void set_z_offset(uint8_t z); + void set_enabled(bool on_render, bool on_physics); + explicit operator hole_proto() const; friend class world; + +private: + void mark_chunk_modified(); }; struct cut_rectangle_result diff --git a/test/hole.cpp b/test/hole.cpp index 248ae59d..d0a80a9c 100644 --- a/test/hole.cpp +++ b/test/hole.cpp @@ -27,18 +27,22 @@ void test1(Vector2i offset) fm_assert_not_equal(0, cut(rect, {{ 49, -49}, {50, 50}}, offset)); #endif #if 1 - fm_assert_equal(0, cut(rect, {{50, 0}, {50, 50}}, offset)); - fm_assert_equal(0, cut(rect, {{ 0, 50}, {50, 50}}, offset)); - fm_assert_equal(0, cut(rect, {{50, 50}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{ 0, 0}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{50, 0}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{ 0, 50}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{50, 50}, {50, 50}}, offset)); #endif #if 1 fm_assert_equal(1, cut(rect, {{ 9, 9}, {70, 70}}, offset)); fm_assert_equal(1, cut(rect, {{10, 10}, {70, 70}}, offset)); + fm_assert_equal(2, cut(rect, {{20, 20}, {70, 70}}, offset)); #endif #if 1 - fm_assert_equal(1, cut(rect, {{1, 0}, {50, 50}}, offset)); - fm_assert_equal(1, cut(rect, {{0, 1}, {50, 50}}, offset)); - fm_assert_equal(2, cut(rect, {{1, 1}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{ 1, 0}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{ 0, 1}, {50, 50}}, offset)); + fm_assert_equal(2, cut(rect, {{ 1, 1}, {50, 50}}, offset)); + fm_assert_equal(2, cut(rect, {{49, 49}, {50, 50}}, offset)); + fm_assert_equal(1, cut(rect, {{50, 50}, {50, 50}}, offset)); #endif #if 1 // todo! coverage diff --git a/test/save.cpp b/test/save.cpp index f596cf00..3d4a71a0 100644 --- a/test/save.cpp +++ b/test/save.cpp @@ -1,14 +1,15 @@ #include "app.hpp" #include "src/world.hpp" -#include "loader/loader.hpp" -#include "loader/scenery-cell.hpp" #include "src/scenery.hpp" #include "src/scenery-proto.hpp" #include "src/critter.hpp" #include "src/light.hpp" +#include "src/hole.hpp" #include "src/ground-atlas.hpp" #include "src/anim-atlas.hpp" #include "src/nanosecond.inl" +#include "loader/loader.hpp" +#include "loader/scenery-cell.hpp" #include <Corrade/Utility/Path.h> namespace floormat { @@ -98,8 +99,10 @@ void assert_chunks_equal(const chunk& a, const chunk& b) switch (type) { case object_type::none: - case object_type::COUNT: std::unreachable(); + case object_type::COUNT: + fm_assert(false); case object_type::critter: { + // todo! remove duplication const auto& e1 = static_cast<const critter&>(ae); const auto& e2 = static_cast<const critter&>(be); const auto p1 = critter_proto(e1), p2 = critter_proto(e2); @@ -120,6 +123,13 @@ void assert_chunks_equal(const chunk& a, const chunk& b) fm_assert(p1 == p2); break; } + case object_type::hole: { + const auto& e1 = static_cast<const hole&>(ae); + const auto& e2 = static_cast<const hole&>(be); + const auto p1 = hole_proto(e1), p2 = hole_proto(e2); + fm_assert(p1 == p2); + break; + } } } } diff --git a/vobj/vobj.json b/vobj/vobj.json index 34e32ea0..f491e025 100644 --- a/vobj/vobj.json +++ b/vobj/vobj.json @@ -3,5 +3,10 @@ "name": "light", "description": "Light", "image": "light" + }, + { + "name": "hole", + "description": "Hole", + "image": "hole" } ] |