summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/imgui-editors.cpp2
-rw-r--r--editor/inspect-types.cpp24
-rw-r--r--serialize/old-savegame.cpp49
-rw-r--r--serialize/savegame.cpp67
-rw-r--r--serialize/scenery.cpp51
-rw-r--r--src/chunk-scenery.cpp2
-rw-r--r--src/scenery.cpp46
-rw-r--r--src/scenery.hpp10
-rw-r--r--test/scenery.cpp3
-rw-r--r--test/serializer.cpp18
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;
}