diff options
-rw-r--r-- | editor/inspect-types.cpp | 87 | ||||
-rw-r--r-- | editor/scenery-editor.cpp | 20 | ||||
-rw-r--r-- | serialize/old-savegame.cpp | 29 | ||||
-rw-r--r-- | serialize/savegame.cpp | 56 | ||||
-rw-r--r-- | src/light.cpp | 5 | ||||
-rw-r--r-- | src/light.hpp | 4 | ||||
-rw-r--r-- | src/scenery.cpp | 2 | ||||
-rw-r--r-- | src/scenery.hpp | 4 | ||||
-rw-r--r-- | test/critter.cpp | 12 | ||||
-rw-r--r-- | test/save.cpp | 16 |
10 files changed, 161 insertions, 74 deletions
diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index be8851ce..520f0f3c 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -91,39 +91,56 @@ struct entity_accessors<object, inspect_intent_t> { } }; -template<> -struct entity_accessors<scenery, inspect_intent_t> { +template<> struct entity_accessors<generic_scenery, inspect_intent_t> +{ static constexpr auto accessors() { - using E = Entity<scenery>; + using E = Entity<generic_scenery>; auto tuple0 = entity_accessors<object, inspect_intent_t>::accessors(); auto tuple = std::tuple{ + E::type<bool>::field{"active"_s, + [](const generic_scenery& x) { return x.active; }, + [](generic_scenery& x, bool b) { x.active = b; }, + constantly(st::readonly), + }, E::type<bool>::field{"interactive"_s, - [](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 { - [&](const door_scenery&) { return st::enabled; }, - [&](const generic_scenery&) { return st::enabled; }, - }, x.subtype); - }, + [](const generic_scenery& x) { return x.interactive; }, + [](generic_scenery& x, bool b) { x.interactive = b; }, + constantly(st::enabled), }, }; return std::tuple_cat(tuple0, tuple); } }; -template<typename T, typename = void> struct has_anim_atlas : std::false_type {}; +template<> struct entity_accessors<door_scenery, inspect_intent_t> +{ + static constexpr auto accessors() + { + using E = Entity<door_scenery>; + auto tuple0 = entity_accessors<object, inspect_intent_t>::accessors(); + auto tuple = std::tuple{ + E::type<bool>::field{"closing"_s, + [](const door_scenery& x) { return x.closing; }, + [](door_scenery& x, bool b) { x.closing = b; }, + constantly(st::readonly), + }, + E::type<bool>::field{"active"_s, + [](const door_scenery& x) { return x.active; }, + [](door_scenery& x, bool b) { x.active = b; }, + constantly(st::readonly), + }, + E::type<bool>::field{"interactive"_s, + [](const door_scenery& x) { return x.interactive; }, + [](door_scenery& x, bool b) { x.interactive = b; }, + constantly(st::enabled), + }, + }; + return std::tuple_cat(tuple0, tuple); + } +}; + +template<typename, typename = void> struct has_anim_atlas : std::false_type {}; template<typename T> requires requires (const T& x) { { x.atlas } -> std::convertible_to<const std::shared_ptr<anim_atlas>&>; } @@ -187,7 +204,7 @@ struct enum_values<rotation, U> }; template<typename T, typename Intent> -static bool inspect_type(T& x, Intent) +bool inspect_type(T& x, Intent) { size_t width = 0; visit_tuple([&](const auto& field) { @@ -271,7 +288,8 @@ struct entity_accessors<light, inspect_intent_t> } }; -template bool inspect_type(scenery&, inspect_intent_t); +template bool inspect_type(generic_scenery&, inspect_intent_t); +template bool inspect_type(door_scenery&, inspect_intent_t); template bool inspect_type(critter&, inspect_intent_t); template bool inspect_type(light&, inspect_intent_t); @@ -280,11 +298,26 @@ bool inspect_object_subtype(object& x) const auto type = x.type(); switch (type) { - case object_type::scenery: return inspect_type(static_cast<scenery&>(x), inspect_intent_t{}); + case object_type::scenery: { + auto& sc = static_cast<scenery&>(x); + const auto sc_type = sc.scenery_type(); + switch (sc_type) + { + case scenery_type::none: + case scenery_type::COUNT: + break; + case scenery_type::generic: + return inspect_type(static_cast<generic_scenery&>(sc), inspect_intent_t{}); + case scenery_type::door: + return inspect_type(static_cast<door_scenery&>(sc), inspect_intent_t{}); + } + fm_warn_once("unknown scenery subtype '%d'", (int)sc_type); [[fallthrough]]; + } 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::COUNT: break; - case object_type::none: break; + case object_type::none: + case object_type::COUNT: + break; } fm_warn_once("unknown object subtype '%d'", (int)type); return false; diff --git a/editor/scenery-editor.cpp b/editor/scenery-editor.cpp index 7a99bbb0..28d94263 100644 --- a/editor/scenery-editor.cpp +++ b/editor/scenery-editor.cpp @@ -102,8 +102,26 @@ start: } } else + { + const auto sc_type = s.proto.scenery_type(); // todo check collision at pos - w.make_object<scenery>(w.make_id(), pos, s.proto); + switch (sc_type) + { + case scenery_type::none: + case scenery_type::COUNT: + break; + case scenery_type::generic: + w.make_object<generic_scenery>(w.make_id(), pos, std::get<generic_scenery_proto>(s.proto.subtype), s.proto); + goto ok; + case scenery_type::door: + w.make_object<door_scenery>(w.make_id(), pos, std::get<door_scenery_proto>(s.proto.subtype), s.proto); + goto ok; + } + fm_abort("place_tile: wrong scenery type %d", (int)sc_type); +ok: + void(); + } + } } // namespace floormat diff --git a/serialize/old-savegame.cpp b/serialize/old-savegame.cpp index 46b83590..31136798 100644 --- a/serialize/old-savegame.cpp +++ b/serialize/old-savegame.cpp @@ -186,7 +186,7 @@ bool read_object_flags(binary_reader<T>& s, U& e) } else if constexpr(tag == object_type::critter) { - e.playable = !!(flags & 1 << 2); + e.playable = !!(flags & 1 << 2); } else { @@ -355,7 +355,7 @@ void reader_state::read_chunks(reader_t& s) } uint32_t object_count = 0; if (PROTO >= 8) [[likely]] - object_count << s; + object_count << s; SET_CHUNK_SIZE(); @@ -482,6 +482,7 @@ void reader_state::read_chunks(reader_t& s) uint16_t delta_; delta_ << s; sc.delta = uint32_t(sc.delta) * 65536u; } + _world->make_object<generic_scenery, false>(oid, {ch, local}, *val, sc); } else if (const auto* val = std::get_if<door_scenery_proto>(&sc.subtype)) { @@ -490,10 +491,11 @@ void reader_state::read_chunks(reader_t& s) uint16_t delta_; delta_ << s; sc.delta = uint32_t(sc.delta) * 65536u; } + _world->make_object<door_scenery, false>(oid, {ch, local}, *val, sc); } + else + fm_soft_assert(false); } - auto e = _world->make_object<scenery, false>(oid, {ch, local}, sc); - (void)e; break; } case object_type::light: { @@ -564,10 +566,12 @@ void reader_state::read_old_scenery(reader_t& s, chunk_coords_ ch, size_t i) atlasid id; id << s; const bool exact = id & meta_short_scenery_bit_; const auto r = rotation(id >> sizeof(id)*8-1-rotation_BITS & rotation_MASK); + const global_coords coord{ch, local_coords{i}}; id &= ~scenery_id_flag_mask_; auto sc = lookup_scenery(id); - (void)sc.atlas->group(r); sc.r = r; + (void)sc.atlas->group(r); + if (!exact) { if (read_object_flags(s, sc)) @@ -589,6 +593,7 @@ 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 (auto* val = std::get_if<generic_scenery_proto>(&sc.subtype)) { if (val->active) @@ -625,10 +630,18 @@ void reader_state::read_old_scenery(reader_t& s, chunk_coords_ ch, size_t i) } } } + else + fm_soft_assert(false); + } + else + { + if (auto* val = std::get_if<generic_scenery_proto>(&sc.subtype)) + auto e = _world->make_object<generic_scenery, false>(_world->make_id(), coord, *val, sc); + else if (auto* val = std::get_if<door_scenery_proto>(&sc.subtype)) + auto e = _world->make_object<door_scenery, false>(_world->make_id(), coord, *val, sc); + else + fm_soft_assert(false); } - global_coords coord{ch, local_coords{i}}; - auto e = _world->make_object<scenery, false>(_world->make_id(), coord, sc); - (void)e; } void reader_state::deserialize_world(ArrayView<const char> buf, proto_t proto) diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index b152fb31..4bc1c7ad 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -412,12 +412,16 @@ struct writer final : visitor_<writer, true> template<typename F> void write_scenery_proto(const scenery& obj, F&& f) // todo! replace scenery::subtype with inheritance! { auto sc_type = obj.scenery_type(); - fm_debug_assert(sc_type != scenery_type::none); + fm_debug_assert(sc_type != scenery_type::none && sc_type < scenery_type::COUNT); visit(sc_type, f); - std::visit( - [&](auto& subtype) { visit_scenery_proto(subtype, f); }, - obj.subtype - ); + switch (sc_type) + { + case scenery_type::generic: visit_scenery_proto(static_cast<const generic_scenery&>(obj), f); break; + case scenery_type::door: visit_scenery_proto(static_cast<const door_scenery&>(obj), f); break; + case scenery_type::none: + case scenery_type::COUNT: + std::unreachable(); + } } template<typename F> void write_object(o_object& obj, chunk* c, F&& f) @@ -815,10 +819,16 @@ struct reader final : visitor_<reader, false> } } - template<typename F> void visit_object_proto(o_scenery& obj, std::nullptr_t, F&& f) + template<typename F> + void read_scenery(std::shared_ptr<scenery>& ret, const object_proto& pʹ, const object_header_s& h, F&& f) { + const auto coord = global_coords{h.ch->coord(), h.tile}; auto sc_type = scenery_type::none; visit(sc_type, f); + + scenery_proto sc; + static_cast<object_proto&>(sc) = pʹ; + switch (sc_type) { case scenery_type::none: @@ -827,19 +837,20 @@ struct reader final : visitor_<reader, false> case scenery_type::generic: { generic_scenery_proto p; visit_scenery_proto(p, f); - obj.subtype = move(p); - goto ok; + sc.subtype = move(p); // todo! extract into make_scenery() + ret = w.make_object<generic_scenery>(h.id, coord, move(p), move(sc)); + return; } case scenery_type::door: { door_scenery_proto p; visit_scenery_proto(p, f); - obj.subtype = move(p); - goto ok; + sc.subtype = move(p); + ret = w.make_object<door_scenery>(h.id, coord, move(p), move(sc)); + return; } } + fm_throw("invalid sc_type {}"_cf, (int)sc_type); -ok: - void(); } template<typename Obj, typename Proto, typename Header> @@ -856,7 +867,7 @@ ok: return w.make_object<Obj>(h0.id, coord, move(p)); } - template<typename F> [[nodiscard]] std::shared_ptr<object> read_object(chunk* ch, F&& f) + template<typename F> void read_object(chunk* ch, F&& f) { std::shared_ptr<object> obj; object_id id = 0; @@ -870,8 +881,8 @@ ok: .tile = tile, }; - object_proto pʹ; - visit_object_header(pʹ, s, f); + object_proto p; + visit_object_header(p, s, f); switch (type) { @@ -883,17 +894,20 @@ ok: critter_header_s h{ .offset_frac = offset_frac, }; - obj = make_object<critter, critter_proto, critter_header_s>(s, move(pʹ), move(h), f); + obj = make_object<critter, critter_proto, critter_header_s>(s, move(p), move(h), f); goto ok; } case object_type::light: - obj = make_object<light, light_proto, std::nullptr_t>(s, move(pʹ), {}, f); + obj = make_object<light, light_proto, std::nullptr_t>(s, move(p), {}, f); goto ok; - case object_type::scenery: - obj = make_object<scenery, scenery_proto, std::nullptr_t>(s, move(pʹ), {}, f); + case object_type::scenery: { + std::shared_ptr<scenery> objʹ; + read_scenery(objʹ, move(p), s, f); + obj = move(objʹ); goto ok; } - fm_throw("invalid sc_type {}"_cf, (int)type); + } + fm_throw("invalid object_type {}"_cf, (int)type); ok: fm_assert(obj); fm_debug_assert(obj->c == ch); @@ -905,8 +919,6 @@ ok: fm_soft_assert(object_counter >= id); else if (PROTO == 20) [[unlikely]] object_counter = Math::max(object_counter, id); - - return obj; } bool deserialize_header_(binary_reader<const char*>& s, ArrayView<const char> buf) diff --git a/src/light.cpp b/src/light.cpp index 3e25dfc5..4f280d97 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -15,9 +15,12 @@ light_proto::light_proto() type = object_type::light; } +light_proto::~light_proto() noexcept = default; light_proto::light_proto(const light_proto&) = default; light_proto& light_proto::operator=(const light_proto&) = default; -light_proto::~light_proto() noexcept = default; +light_proto::light_proto(light_proto&&) noexcept = default; +light_proto& light_proto::operator=(light_proto&&) noexcept = default; + bool light_proto::operator==(const light_proto&) const = default; light::light(object_id id, class chunk& c, const light_proto& proto) : diff --git a/src/light.hpp b/src/light.hpp index 0033f1c5..ea65a23f 100644 --- a/src/light.hpp +++ b/src/light.hpp @@ -9,10 +9,12 @@ namespace floormat { struct light_proto : object_proto { + ~light_proto() noexcept override; light_proto(); light_proto(const light_proto&); light_proto& operator=(const light_proto&); - ~light_proto() noexcept override; + light_proto(light_proto&&) noexcept; + light_proto& operator=(light_proto&&) noexcept; bool operator==(const light_proto&) const; float max_distance = 0; diff --git a/src/scenery.cpp b/src/scenery.cpp index 6f49cfca..4d47da5a 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -64,7 +64,7 @@ bool scenery_proto::operator==(const object_proto& e0) const // --- scenery --- -enum object_type scenery::type() const override { return object_type::scenery; } // NOLINT(*-convert-*-to-static) +enum object_type scenery::type() const noexcept { return object_type::scenery; } // NOLINT(*-convert-*-to-static) float scenery::depth_offset() const { diff --git a/src/scenery.hpp b/src/scenery.hpp index 32ba3742..be57296d 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -37,7 +37,7 @@ using scenery_proto_variants = std::variant<generic_scenery_proto, door_scenery_ struct scenery_proto : object_proto { - scenery_proto_variants subtype; + scenery_proto_variants subtype; // todo! add std::monostate scenery_proto() noexcept; scenery_proto(const scenery_proto&) noexcept; @@ -55,9 +55,9 @@ struct scenery : object float depth_offset() const override; enum object_type type() const noexcept override; virtual enum scenery_type scenery_type() const = 0; + virtual explicit operator scenery_proto() const; protected: - virtual explicit operator scenery_proto() const; scenery(object_id id, class chunk& c, const scenery_proto& proto); }; diff --git a/test/critter.cpp b/test/critter.cpp index cc46ef72..f7ce3b35 100644 --- a/test/critter.cpp +++ b/test/critter.cpp @@ -309,10 +309,14 @@ void test3(StringView instance_name, const Function& make_dt, double accel, rota w[{{-1,-1,0}, {13,13}}].t.wall_west() = W; w[{{1,1,0}, {4,5}}].t.wall_north() = W; w[{{1,1,0}, {5,4}}].t.wall_west() = W; - (void)w.make_object<scenery, false>(w.make_id(), {{}, {0, 0}}, S); - (void)w.make_object<scenery, false>(w.make_id(), {{}, {1, 1}}, S); - (void)w.make_object<scenery, false>(w.make_id(), {{}, {14, 14}}, S); - (void)w.make_object<scenery, false>(w.make_id(), {{}, {15, 15}}, S); + (void)w.make_object<generic_scenery, false>(w.make_id(), {{}, {0, 0}}, + std::get<generic_scenery_proto>(S.subtype), S); // todo! + (void)w.make_object<generic_scenery, false>(w.make_id(), {{}, {1, 1}}, + std::get<generic_scenery_proto>(S.subtype), S); + (void)w.make_object<generic_scenery, false>(w.make_id(), {{}, {14, 14}}, + std::get<generic_scenery_proto>(S.subtype), S); + (void)w.make_object<generic_scenery, false>(w.make_id(), {{}, {15, 15}}, + std::get<generic_scenery_proto>(S.subtype), S); w[chunk_coords_{}].sort_objects(); if (no_unroll) diff --git a/test/save.cpp b/test/save.cpp index e5dcf9a6..44dfb722 100644 --- a/test/save.cpp +++ b/test/save.cpp @@ -35,8 +35,10 @@ chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch) c[{K, K }].wall_west() = { metal2, 0 }; c[{K, K+1}].wall_north() = { metal2, 0 }; c[{K+1, K }].wall_west() = { metal2, 0 }; - w.make_object<scenery>(w.make_id(), {ch, {3, 4}}, table); - w.make_object<scenery>(w.make_id(), {ch, {K, K+1}}, control_panel); + w.make_object<generic_scenery>(w.make_id(), {ch, {3, 4}}, + std::get<generic_scenery_proto>(table.subtype), table); // todo! + w.make_object<generic_scenery>(w.make_id(), {ch, {K, K+1}}, + std::get<generic_scenery_proto>(control_panel.subtype), control_panel); // todo! const auto add_player = [&](StringView name, Vector2i coord, bool playable) { critter_proto cproto; @@ -51,20 +53,20 @@ chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch) add_player("Player 3", {15, 11}, true); { - auto& e = *w.make_object<scenery>(w.make_id(), {ch, {K+3, K+1}}, door); + auto& e = *w.make_object<door_scenery>(w.make_id(), {ch, {K+3, K+1}}, + std::get<door_scenery_proto>(door.subtype), door); const auto end = e.atlas->info().nframes-1; constexpr auto dt = Second / 60; fm_assert(e.frame == end); - { auto& x = std::get<door_scenery>(e.subtype); - fm_assert(!x.active); + { fm_assert(!e.active); e.activate(e.index()); - fm_assert(x.active); + fm_assert(e.active); { auto index = e.index(); e.update(index, dt); } fm_assert(e.frame != end); for (int i = 0; i < 60*3; i++) { auto index = e.index(); e.update(index, dt); } fm_assert(e.frame == 0); - fm_assert(!x.active); + fm_assert(!e.active); } } return c; |