diff options
-rw-r--r-- | editor/imgui-editors.cpp | 2 | ||||
-rw-r--r-- | editor/inspect-types.cpp | 24 | ||||
-rw-r--r-- | serialize/old-savegame.cpp | 49 | ||||
-rw-r--r-- | serialize/savegame.cpp | 67 | ||||
-rw-r--r-- | serialize/scenery.cpp | 51 | ||||
-rw-r--r-- | src/chunk-scenery.cpp | 2 | ||||
-rw-r--r-- | src/scenery.cpp | 46 | ||||
-rw-r--r-- | src/scenery.hpp | 10 | ||||
-rw-r--r-- | test/scenery.cpp | 3 | ||||
-rw-r--r-- | test/serializer.cpp | 18 |
10 files changed, 208 insertions, 64 deletions
diff --git a/editor/imgui-editors.cpp b/editor/imgui-editors.cpp index 358cd200..1c15989f 100644 --- a/editor/imgui-editors.cpp +++ b/editor/imgui-editors.cpp @@ -29,7 +29,7 @@ using vobj_ = vobj_editor::vobj_; StringView scenery_type_to_string(const scenery_& sc) { - switch (sc.proto.sc_type) + switch (sc.proto.scenery_type()) { case scenery_type::none: return "none"_s; case scenery_type::generic: return "generic"_s; diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index c500b050..ab3c04d1 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -66,6 +66,9 @@ struct entity_accessors<object, inspect_intent_t> { } }; +template<typename... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; + template<> struct entity_accessors<scenery, inspect_intent_t> { static constexpr auto accessors() @@ -74,8 +77,25 @@ struct entity_accessors<scenery, inspect_intent_t> { auto tuple0 = entity_accessors<object, inspect_intent_t>::accessors(); auto tuple = std::tuple{ E::type<bool>::field{"interactive"_s, - [](const scenery& x) { return x.interactive; }, - [](scenery& x, bool value) { x.interactive = value; } + [](const scenery& x) { + return std::visit(overloaded { + [&](const door_scenery& s) { return s.interactive; }, + [&](const generic_scenery& s) { return s.interactive; }, + }, x.subtype); + }, + [](scenery& x, bool b) { + return std::visit(overloaded { + [&](door_scenery& s) { s.interactive = b; }, + [&](generic_scenery& s) { s.interactive = b; }, + }, x.subtype); + }, + [](const scenery& x) { + return std::visit(overloaded { + [&](door_scenery&) { return st::enabled; }, + [&](generic_scenery&) { return st::enabled; }, + [](auto&&) { return st::hidden; }, + }, x.subtype); + }, }, }; return std::tuple_cat(tuple0, tuple); diff --git a/serialize/old-savegame.cpp b/serialize/old-savegame.cpp index d63447a4..8a050022 100644 --- a/serialize/old-savegame.cpp +++ b/serialize/old-savegame.cpp @@ -171,9 +171,17 @@ bool read_object_flags(binary_reader<T>& s, U& e) fm_throw("invalid object type '{}'"_cf, (int)e.type); if constexpr(tag == object_type::scenery) { - e.active = !!(flags & 1 << 2); - e.closing = !!(flags & 1 << 3); - e.interactive = !!(flags & 1 << 4); + if (auto* val = std::get_if<generic_scenery_proto>(&e.subtype)) + { + val->active = !!(flags & 1 << 2); + val->interactive = !!(flags & 1 << 4); + } + else if (auto* val = std::get_if<door_scenery_proto>(&e.subtype)) + { + val->active = !!(flags & 1 << 2); + val->closing = !!(flags & 1 << 3); + val->interactive = !!(flags & 1 << 4); + } } else if constexpr(tag == object_type::critter) { @@ -461,8 +469,16 @@ void reader_state::read_chunks(reader_t& s) s >> sc.offset[1]; } read_bbox(s, sc); - if (sc.active) - sc.delta << s; + if (const auto* val = std::get_if<generic_scenery_proto>(&sc.subtype)) + { + if (val->active) + sc.delta << s; + } + else if (const auto* val = std::get_if<door_scenery_proto>(&sc.subtype)) + { + if (val->active) + sc.delta << s; + } } auto e = _world->make_object<scenery, false>(oid, {ch, local}, sc); (void)e; @@ -561,12 +577,25 @@ void reader_state::read_old_scenery(reader_t& s, chunk_coords_ ch, size_t i) sc.bbox_offset[0] << s; sc.bbox_offset[1] << s; } - if (sc.active) + if (auto* val = std::get_if<generic_scenery_proto>(&sc.subtype)) { - if (PROTO >= 4) [[likely]] - sc.delta << s; - else - sc.delta = (uint16_t)Math::clamp(int(s.read<float>() * 65535), 0, 65535); + if (val->active) + { + if (PROTO >= 4) [[likely]] + sc.delta << s; + else + sc.delta = (uint16_t)Math::clamp(int(s.read<float>() * 65535), 0, 65535); + } + } + else if (auto* val = std::get_if<door_scenery_proto>(&sc.subtype)) + { + if (val->active) + { + if (PROTO >= 4) [[likely]] + sc.delta << s; + else + sc.delta = (uint16_t)Math::clamp(int(s.read<float>() * 65535), 0, 65535); + } } } global_coords coord{ch, local_coords{i}}; diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index e7d24195..a71118e8 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -148,7 +148,6 @@ struct visitor_ break; case object_type::scenery: case object_type::critter: - case object_type::door_new: static_cast<Derived&>(*this).visit(non_const(obj.atlas), atlas_type::anim, f); break; case object_type::none: @@ -172,11 +171,9 @@ struct visitor_ switch (type) { - case object_type::door_new: fm_assert(false && "todo"); case object_type::critter: do_visit(static_cast<critter&>(obj), f); return; case object_type::scenery: do_visit(static_cast<scenery&>(obj), f); return; case object_type::light: do_visit(static_cast<light&>(obj), f); return; - //case object_type::door: do_visit(static_cast<door&>(obj), f); return; case object_type::COUNT: case object_type::none: break; @@ -224,37 +221,71 @@ struct visitor_ setter(obj, flags & bits); } - template<typename F> void visit(scenery& obj, F&& f) + template<typename F> void visit(generic_scenery& s, F&& f) { - auto sc_type = obj.sc_type; - do_visit(sc_type, f); - obj.sc_type = sc_type; + constexpr struct { + uint8_t bits; + bool(*getter)(const generic_scenery&); + void(*setter)(generic_scenery&, bool); + } pairs[] = { + { 1 << 0, + [](const auto& sc) { return !!sc.active; }, + [](auto& sc, bool value) { sc.active = value; } + }, + { 1 << 2, + [](const auto& sc) { return !!sc.interactive; }, + [](auto& sc, bool value) { sc.interactive = value; } + }, + }; + uint8_t flags = 0; + for (auto [bits, getter, setter] : pairs) + flags |= bits * getter(s); + do_visit(flags, f); + for (auto [bits, getter, setter] : pairs) + setter(s, flags & bits); + } + + template<typename F> void visit(door_scenery& s, F&& f) + { constexpr struct { uint8_t bits; - bool(*getter)(const scenery&); - void(*setter)(scenery&, bool); + bool(*getter)(const door_scenery&); + void(*setter)(door_scenery&, bool); } pairs[] = { { 1 << 0, - [](const scenery& sc) { return !!sc.active; }, - [](scenery& sc, bool value) { sc.active = value; } + [](const auto& sc) { return !!sc.active; }, + [](auto& sc, bool value) { sc.active = value; } }, { 1 << 1, - [](const scenery& sc) { return !!sc.closing; }, - [](scenery& sc, bool value) { sc.closing = value; } + [](const auto& sc) { return !!sc.closing; }, + [](auto& sc, bool value) { sc.closing = value; } }, { 1 << 2, - [](const scenery& sc) { return !!sc.interactive; }, - [](scenery& sc, bool value) { sc.interactive = value; } + [](const auto& sc) { return !!sc.interactive; }, + [](auto& sc, bool value) { sc.interactive = value; } }, }; uint8_t flags = 0; for (auto [bits, getter, setter] : pairs) - flags |= bits * getter(obj); + flags |= bits * getter(s); do_visit(flags, f); for (auto [bits, getter, setter] : pairs) - setter(obj, flags & bits); + setter(s, flags & bits); + } + + template<typename F> void visit(scenery& obj, F&& f) + { + auto sc_type = obj.scenery_type(); + do_visit(sc_type, f); + if (sc_type != obj.scenery_type()) + obj.subtype = scenery::subtype_from_scenery_type(obj.id, *obj.c, sc_type); + + std::visit( + [&]<typename T>(T& x) { return do_visit(x, f); }, + obj.subtype + ); } template<typename F> void visit(light& obj, F&& f) @@ -738,8 +769,6 @@ struct reader final : visitor_<reader> case object_type::none: case object_type::COUNT: break; - case object_type::door_new: - fm_assert(false && "todo"); case object_type::light: obj = w.make_unconnected_object<light>(); goto ok; case object_type::critter: diff --git a/serialize/scenery.cpp b/serialize/scenery.cpp index 41b99218..158b4773 100644 --- a/serialize/scenery.cpp +++ b/serialize/scenery.cpp @@ -84,6 +84,8 @@ void adl_serializer<scenery_proto>::to_json(json& j, const scenery_proto& f) { fm_assert(f.atlas); const scenery_proto default_scenery; + const generic_scenery_proto default_generic_scenery; + const door_scenery_proto default_door_scenery; if (f.type != default_scenery.type) j["type"] = f.type; j["atlas-name"] = f.atlas->name(); @@ -93,10 +95,22 @@ void adl_serializer<scenery_proto>::to_json(json& j, const scenery_proto& f) j["rotation"] = f.r; if (f.pass != default_scenery.pass) j["pass-mode"] = f.pass; - if (f.active != default_scenery.active) - j["active"] = f.active; - if (f.interactive != default_scenery.interactive) - j["interactive"] = f.interactive; + std::visit(overloaded { + [&](const generic_scenery_proto& x) { + if (x.active != default_generic_scenery.active) + j["active"] = x.active; + if (x.interactive != default_generic_scenery.interactive) + j["interactive"] = x.interactive; + }, + [&](const door_scenery_proto& x) { + if (x.active != default_door_scenery.active) + j["active"] = x.active; + if (x.interactive != default_door_scenery.interactive) + j["interactive"] = x.interactive; + if (x.closing != default_door_scenery.closing) + j["closing"] = x.closing; + }, + }, f.subtype); if (f.offset != default_scenery.offset) j["offset"] = Vector2i(f.offset); if (f.bbox_offset != default_scenery.bbox_offset) @@ -114,17 +128,19 @@ void adl_serializer<scenery_proto>::from_json(const json& j, scenery_proto& f) value = j[s]; }; + const generic_scenery_proto G; + const door_scenery_proto D; + StringView atlas_name = j["atlas-name"]; fm_soft_assert(!atlas_name.isEmpty()); f = {}; f.atlas = loader.anim_atlas(atlas_name, loader_::SCENERY_PATH); - auto type = scenery_type::generic; get("type", type); auto frame = f.frame; get("frame", frame); auto r = f.atlas->first_rotation(); get("rotation", r); pass_mode pass = f.pass; get("pass-mode", pass); - bool active = f.active; get("active", active); - bool interactive = f.interactive; get("interactive", interactive); + bool active = G.active; get("active", active); + bool interactive = G.interactive; get("interactive", interactive); auto offset = Vector2i(f.offset); get("offset", offset); auto bbox_offset = Vector2i(f.bbox_offset); get("bbox-offset", bbox_offset); auto bbox_size = Vector2ui(f.bbox_size); get("bbox-size", bbox_size); @@ -136,32 +152,37 @@ void adl_serializer<scenery_proto>::from_json(const json& j, scenery_proto& f) { default: fm_throw("unhandled scenery type '{}'"_cf, (unsigned)type); - case scenery_type::generic: + case scenery_type::generic: { + auto s = generic_scenery_proto{}; f.type = object_type::scenery; - f.sc_type = scenery_type::generic; f.r = r; f.frame = frame; f.pass = pass; - f.active = active; - f.interactive = interactive; + s.active = active; + s.interactive = interactive; f.offset = Vector2b(offset); f.bbox_offset = Vector2b(bbox_offset); f.bbox_size = Vector2ub(bbox_size); + f.subtype = s; break; - case scenery_type::door: + } + case scenery_type::door: { fm_assert(f.atlas->info().fps > 0 && f.atlas->info().nframes > 0); + auto s = door_scenery_proto{}; f.type = object_type::scenery; - f.sc_type = scenery_type::door; f.r = r; f.frame = uint16_t(f.atlas->group(r).frames.size()-1); f.pass = pass_mode::blocked; - f.interactive = true; - f.closing = false; + s.active = false; + s.interactive = true; + s.closing = false; f.offset = Vector2b(offset); f.bbox_offset = Vector2b(bbox_offset); f.bbox_size = Vector2ub(bbox_size); + f.subtype = s; break; } + } } void adl_serializer<serialized_scenery>::to_json(json& j, const serialized_scenery& val) diff --git a/src/chunk-scenery.cpp b/src/chunk-scenery.cpp index c5b77241..7b4905dd 100644 --- a/src/chunk-scenery.cpp +++ b/src/chunk-scenery.cpp @@ -119,7 +119,7 @@ auto chunk::make_topo_sort_data(object& e, uint32_t mesh_idx) -> topo_sort_data data.slope = (bb_max[1]-bb_min[1])/bb_len; data.bb_min = Vector2s(bb_min - px_start); data.bb_max = Vector2s(bb_max - px_start); - if (sc.sc_type != scenery_type::door) + if (sc.scenery_type() != scenery_type::door) data.mode = topo_sort_data::mode_static; } break; diff --git a/src/scenery.cpp b/src/scenery.cpp index e80f6f68..c3c0b7c1 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -5,13 +5,14 @@ #include "world.hpp" #include "shaders/shader.hpp" #include "src/rotation.inl" +#include "compat/exception.hpp" #include <algorithm> namespace floormat { namespace { -template<typename... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<typename... Ts> struct [[maybe_unused]] overloaded : Ts... { using Ts::operator()...; }; template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; template<typename T> struct proto_to_scenery_; @@ -34,25 +35,42 @@ scenery_proto::~scenery_proto() noexcept = default; scenery_proto::operator bool() const { return atlas != nullptr; } bool generic_scenery_proto::operator==(const generic_scenery_proto& p) const = default; -bool door_scenery_proto::operator==(const door_scenery_proto& p) const = default; +enum scenery_type generic_scenery_proto::scenery_type() const { return scenery_type::generic; } + void generic_scenery::update(scenery&, size_t, float) {} Vector2 generic_scenery::ordinal_offset(const scenery&, Vector2b offset) const { return Vector2(offset); } bool generic_scenery::can_activate(const scenery&, size_t) const { return interactive; } bool generic_scenery::activate(floormat::scenery&, size_t) { return false; } object_type generic_scenery::type() const noexcept { return object_type::scenery; } +enum scenery_type generic_scenery::scenery_type() const { return scenery_type::generic; } + +enum scenery_type scenery_proto::scenery_type() const +{ + return std::visit( + [&]<typename T>(const T& x) { return x.scenery_type(); }, + subtype + ); +} + generic_scenery::operator generic_scenery_proto() const { return { .active = active, .interactive = interactive, }; } generic_scenery::generic_scenery(object_id, struct chunk&, const generic_scenery_proto& p) : active{p.active}, interactive{p.interactive} {} +bool door_scenery_proto::operator==(const door_scenery_proto& p) const = default; +enum scenery_type door_scenery_proto::scenery_type() const { return scenery_type::door; } + +enum scenery_type door_scenery::scenery_type() const { return scenery_type::door; } +door_scenery::operator door_scenery_proto() const { return { .active = active, .interactive = interactive, .closing = closing, }; } + door_scenery::door_scenery(object_id, struct chunk&, const door_scenery_proto& p) : closing{p.closing}, active{p.active}, interactive{p.interactive} {} void door_scenery::update(scenery& s, size_t, float dt) { - if (!s.atlas || active) + if (!s.atlas || !active) return; fm_assert(s.atlas); @@ -200,6 +218,14 @@ scenery::operator scenery_proto() const return ret; } +enum scenery_type scenery::scenery_type() const +{ + return std::visit( + [&]<typename T>(const T& sc) { return sc.scenery_type(); }, + subtype + ); +} + scenery_variants scenery::subtype_from_proto(object_id id, struct chunk& c, const scenery_proto_variants& variant) { return std::visit( @@ -210,6 +236,20 @@ scenery_variants scenery::subtype_from_proto(object_id id, struct chunk& c, cons ); } +scenery_variants scenery::subtype_from_scenery_type(object_id id, struct chunk& c, enum scenery_type type) +{ + switch (type) + { + case scenery_type::generic: + return generic_scenery{id, c, {}}; + case scenery_type::door: + return door_scenery{id, c, {}}; + case scenery_type::none: + break; + } + fm_throw("invalid scenery type"_cf, (int)type); +} + scenery::scenery(object_id id, struct chunk& c, const scenery_proto& proto) : object{id, c, proto}, subtype{ subtype_from_proto(id, c, proto.subtype) } { diff --git a/src/scenery.hpp b/src/scenery.hpp index 2c28d936..df5ac186 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -11,13 +11,15 @@ namespace floormat { +template<typename... Ts> struct [[maybe_unused]] overloaded : Ts... { using Ts::operator()...; }; +template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; + struct chunk; class anim_atlas; class world; enum class scenery_type : unsigned char { - none, generic, - door, // todo remove it + none, generic, door, }; struct generic_scenery_proto @@ -26,6 +28,7 @@ struct generic_scenery_proto unsigned char interactive : 1 = false; bool operator==(const generic_scenery_proto& p) const; + enum scenery_type scenery_type() const; }; struct door_scenery_proto @@ -35,6 +38,7 @@ struct door_scenery_proto unsigned char closing : 1 = false; bool operator==(const door_scenery_proto& p) const; + enum scenery_type scenery_type() const; }; using scenery_proto_variants = std::variant<generic_scenery_proto, door_scenery_proto>; @@ -49,6 +53,7 @@ struct scenery_proto : object_proto scenery_proto& operator=(const scenery_proto&); bool operator==(const object_proto& proto) const override; explicit operator bool() const; + enum scenery_type scenery_type() const; }; struct scenery; @@ -106,6 +111,7 @@ struct scenery final : object static scenery_variants subtype_from_proto(object_id id, struct chunk& c, const scenery_proto_variants& variants); + static scenery_variants subtype_from_scenery_type(object_id id, struct chunk& c, enum scenery_type type); private: friend class world; diff --git a/test/scenery.cpp b/test/scenery.cpp index 01f30781..42de1ab6 100644 --- a/test/scenery.cpp +++ b/test/scenery.cpp @@ -9,9 +9,6 @@ namespace { void test_loading() { fm_assert(!loader.sceneries().isEmpty()); - - for (const auto& [name, proto] : loader.sceneries()) - fm_assert(proto.sc_type != scenery_type::none); } } // namespace diff --git a/test/serializer.cpp b/test/serializer.cpp index e14f5c1e..6153d6ee 100644 --- a/test/serializer.cpp +++ b/test/serializer.cpp @@ -53,15 +53,17 @@ chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch) const auto index = e.index(); const auto end = e.atlas->info().nframes-1; fm_assert(e.frame == end); - fm_assert(!e.active); - e.activate(e.index()); - fm_assert(e.active); - e.update(index, 1.f/60); - fm_assert(e.frame != end); - for (int i = 0; i < 60*3; i++) + { auto& x = std::get<door_scenery>(e.subtype); + fm_assert(!x.active); + e.activate(e.index()); + fm_assert(x.active); e.update(index, 1.f/60); - fm_assert(e.frame == 0); - fm_assert(!e.active); + fm_assert(e.frame != end); + for (int i = 0; i < 60*3; i++) + e.update(index, 1.f/60); + fm_assert(e.frame == 0); + fm_assert(!x.active); + } } return c; } |