summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/inspect-types.cpp87
-rw-r--r--editor/scenery-editor.cpp20
-rw-r--r--serialize/old-savegame.cpp29
-rw-r--r--serialize/savegame.cpp56
-rw-r--r--src/light.cpp5
-rw-r--r--src/light.hpp4
-rw-r--r--src/scenery.cpp2
-rw-r--r--src/scenery.hpp4
-rw-r--r--test/critter.cpp12
-rw-r--r--test/save.cpp16
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;