diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2024-01-23 13:26:25 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2024-01-23 13:26:25 +0100 |
commit | 573f8f2bcbfd86119e00a13fedc5abf2bf7deafb (patch) | |
tree | 9bd432319d6ab9d780590269f6dfeff77f470985 /src | |
parent | daa32487d907dd069d2339a7e8f5e8ce3ec2e999 (diff) |
w
Diffstat (limited to 'src')
-rw-r--r-- | src/object-type.hpp | 2 | ||||
-rw-r--r-- | src/scenery.cpp | 239 | ||||
-rw-r--r-- | src/scenery.hpp | 68 |
3 files changed, 214 insertions, 95 deletions
diff --git a/src/object-type.hpp b/src/object-type.hpp index d723e4ba..8c84c31a 100644 --- a/src/object-type.hpp +++ b/src/object-type.hpp @@ -3,7 +3,7 @@ namespace floormat { enum class object_type : unsigned char { - none, critter, scenery, light, door_new, COUNT, + none, critter, scenery, light, COUNT, }; } // namespace floormat diff --git a/src/scenery.cpp b/src/scenery.cpp index 24431a6b..e80f6f68 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -9,83 +9,135 @@ namespace floormat { -scenery_proto::scenery_proto() -{ - type = object_type::scenery; -} +namespace { + +template<typename... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; + +template<typename T> struct proto_to_scenery_; +template<> struct proto_to_scenery_<generic_scenery_proto> { using type = generic_scenery; }; +template<> struct proto_to_scenery_<door_scenery_proto> { using type = door_scenery; }; +template<typename T> using proto_to_scenery = typename proto_to_scenery_<T>::type; + +template<typename T> struct scenery_to_proto_; +template<> struct scenery_to_proto_<generic_scenery> { using type = generic_scenery_proto; }; +template<> struct scenery_to_proto_<door_scenery> { using type = door_scenery_proto; }; +template<typename T> using scenery_to_proto = typename scenery_to_proto_<T>::type; + +} // namespace + +scenery_proto::scenery_proto() { type = object_type::scenery; } scenery_proto& scenery_proto::operator=(const scenery_proto&) = default; scenery_proto::scenery_proto(const scenery_proto&) = default; scenery_proto::~scenery_proto() noexcept = default; scenery_proto::operator bool() const { return atlas != nullptr; } -bool scenery::can_activate(size_t) const -{ - return atlas && interactive; -} +bool generic_scenery_proto::operator==(const generic_scenery_proto& p) const = default; +bool door_scenery_proto::operator==(const door_scenery_proto& p) const = default; +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; } +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} +{} -void scenery::update(size_t, float dt) +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) { - auto& s = *this; - if (!s.active) + if (!s.atlas || active) return; - switch (s.sc_type) - { - default: - case scenery_type::none: - case scenery_type::generic: + fm_assert(s.atlas); + auto& anim = *s.atlas; + const auto hz = uint8_t(s.atlas->info().fps); + const auto nframes = (int)anim.info().nframes; + fm_debug_assert(anim.info().fps > 0 && anim.info().fps <= 0xff); + + auto delta_ = int(s.delta) + int(65535u * dt); + delta_ = std::min(65535, delta_); + const auto frame_time = int(1.f/hz * 65535); + const auto n = (uint8_t)std::clamp(delta_ / frame_time, 0, 255); + s.delta = (uint16_t)std::clamp(delta_ - frame_time*n, 0, 65535); + fm_debug_assert(s.delta >= 0); + if (n == 0) return; - case scenery_type::door: { - fm_assert(atlas); - auto& anim = *atlas; - const auto hz = uint8_t(atlas->info().fps); - const auto nframes = (int)anim.info().nframes; - fm_debug_assert(anim.info().fps > 0 && anim.info().fps <= 0xff); - - auto delta_ = int(s.delta) + int(65535u * dt); - delta_ = std::min(65535, delta_); - const auto frame_time = int(1.f/hz * 65535); - const auto n = (uint8_t)std::clamp(delta_ / frame_time, 0, 255); - s.delta = (uint16_t)std::clamp(delta_ - frame_time*n, 0, 65535); - fm_debug_assert(s.delta >= 0); - if (n == 0) - return; - const int8_t dir = s.closing ? 1 : -1; - const int fr = s.frame + dir*n; - s.active = fr > 0 && fr < nframes-1; - pass_mode p; - if (fr <= 0) - p = pass_mode::pass; - else if (fr >= nframes-1) - p = pass_mode::blocked; - else - p = pass_mode::see_through; - set_bbox(offset, bbox_offset, bbox_size, p); - const auto new_frame = (uint16_t)std::clamp(fr, 0, nframes-1); - //Debug{} << "frame" << new_frame << nframes-1; - s.frame = new_frame; - if (!s.active) - s.delta = s.closing = 0; - //if ((p == pass_mode::pass) != (old_pass == pass_mode::pass)) Debug{} << "update: need reposition" << (s.frame == 0 ? "-1" : "1"); - } - } + const int8_t dir = closing ? 1 : -1; + const int fr = s.frame + dir*n; + active = fr > 0 && fr < nframes-1; + pass_mode p; + if (fr <= 0) + p = pass_mode::pass; + else if (fr >= nframes-1) + p = pass_mode::blocked; + else + p = pass_mode::see_through; + s.set_bbox(s.offset, s.bbox_offset, s.bbox_size, p); + const auto new_frame = (uint16_t)std::clamp(fr, 0, nframes-1); + //Debug{} << "frame" << new_frame << nframes-1; + s.frame = new_frame; + if (!active) + s.delta = closing = 0; + //if ((p == pass_mode::pass) != (old_pass == pass_mode::pass)) Debug{} << "update: need reposition" << (s.frame == 0 ? "-1" : "1"); } -Vector2 scenery::ordinal_offset(Vector2b offset) const +Vector2 door_scenery::ordinal_offset(const scenery& s, Vector2b offset) const { constexpr auto bTILE_SIZE = Vector2b(iTILE_SIZE2); - if (sc_type == scenery_type::door) - { - constexpr auto off_closed_ = Vector2b(0, -bTILE_SIZE[1]/2+2); - constexpr auto off_opened_ = Vector2b(-bTILE_SIZE[0]+2, -bTILE_SIZE[1]/2+2); - const auto off_closed = rotate_point(off_closed_, rotation::N, r); - const auto off_opened = rotate_point(off_opened_, rotation::N, r); - const auto vec = frame == atlas->info().nframes-1 ? off_closed : off_opened; - return Vector2(offset) + Vector2(vec); - } - return Vector2(offset); + constexpr auto off_closed_ = Vector2b(0, -bTILE_SIZE[1]/2+2); + constexpr auto off_opened_ = Vector2b(-bTILE_SIZE[0]+2, -bTILE_SIZE[1]/2+2); + const auto off_closed = rotate_point(off_closed_, rotation::N, s.r); + const auto off_opened = rotate_point(off_opened_, rotation::N, s.r); + const auto vec = s.frame == s.atlas->info().nframes-1 ? off_closed : off_opened; + return Vector2(offset) + Vector2(vec); +} + +bool door_scenery::can_activate(const scenery&, size_t) const { return interactive; } + +bool door_scenery::activate(scenery& s, size_t) +{ + if (active) + return false; + fm_assert(s.frame == 0 || s.frame == s.atlas->info().nframes-1); + closing = s.frame == 0; + s.frame += closing ? 1 : -1; + active = true; + return true; +} + +bool scenery::can_activate(size_t i) const +{ + if (!atlas) + return false; + + return std::visit( + [&]<typename T>(const T& sc) { return sc.can_activate(*this, i); }, + subtype + ); +} + +void scenery::update(size_t i, float dt) +{ + return std::visit( + [&]<typename T>(T& sc) { sc.update(*this, i, dt); }, + subtype + ); +} + +Vector2 scenery::ordinal_offset(Vector2b offset) const +{ + return std::visit( + [&]<typename T>(const T& sc) { return sc.ordinal_offset(*this, offset); }, + subtype + ); } float scenery::depth_offset() const @@ -100,26 +152,12 @@ float scenery::depth_offset() const return ret; } -bool scenery::activate(size_t) +bool scenery::activate(size_t i) { - auto& s = *this; - if (s.active) - return false; - - switch (s.sc_type) - { - default: - case scenery_type::none: - case scenery_type::generic: - break; - case scenery_type::door: - fm_assert(s.frame == 0 || s.frame == atlas->info().nframes-1); - s.closing = s.frame == 0; - s.frame += s.closing ? 1 : -1; - s.active = true; - return true; - } - return false; + return std::visit( + [&]<typename T>(T& sc) { return sc.activate(*this, i); }, + subtype + ); } bool scenery_proto::operator==(const object_proto& e0) const @@ -130,9 +168,23 @@ bool scenery_proto::operator==(const object_proto& e0) const if (!object_proto::operator==(e0)) return false; - const auto& s0 = static_cast<const scenery_proto&>(e0); - return sc_type == s0.sc_type && active == s0.active && - closing == s0.closing && interactive == s0.interactive; + const auto& sc = static_cast<const scenery_proto&>(e0); + + if (subtype.index() != sc.subtype.index()) + return false; + + return std::visit( + [](const auto& a, const auto& b) { + if constexpr(std::is_same_v< std::decay_t<decltype(a)>, std::decay_t<decltype(b)> >) + return a == b; + else + { + std::unreachable(); + return false; + } + }, + subtype, sc.subtype + ); } object_type scenery::type() const noexcept { return object_type::scenery; } @@ -141,16 +193,25 @@ scenery::operator scenery_proto() const { scenery_proto ret; static_cast<object_proto&>(ret) = object::operator object_proto(); - ret.sc_type = sc_type; - ret.active = active; - ret.closing = closing; - ret.interactive = interactive; + std::visit( + [&]<typename T>(const T& s) { ret.subtype = scenery_to_proto<T>(s); }, + subtype + ); return ret; } +scenery_variants scenery::subtype_from_proto(object_id id, struct chunk& c, const scenery_proto_variants& variant) +{ + return std::visit( + [&]<typename T>(const T& p) { + return scenery_variants { std::in_place_type_t<proto_to_scenery<T>>{}, id, c, p }; + }, + variant + ); +} + scenery::scenery(object_id id, struct chunk& c, const scenery_proto& proto) : - object{id, c, proto}, sc_type{proto.sc_type}, active{proto.active}, - closing{proto.closing}, interactive{proto.interactive} + object{id, c, proto}, subtype{ subtype_from_proto(id, c, proto.subtype) } { #ifndef FM_NO_DEBUG if (id != 0) diff --git a/src/scenery.hpp b/src/scenery.hpp index 61345ab6..2c28d936 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -4,6 +4,8 @@ #include "rotation.hpp" #include "object.hpp" #include <type_traits> +#include <variant> +#include <Corrade/Containers/Optional.h> #include <Magnum/Math/Vector2.h> #include <Magnum/Magnum.h> @@ -18,13 +20,29 @@ enum class scenery_type : unsigned char { door, // todo remove it }; -struct scenery_proto : object_proto +struct generic_scenery_proto { - scenery_type sc_type = scenery_type::none; unsigned char active : 1 = false; - unsigned char closing : 1 = false; unsigned char interactive : 1 = false; + bool operator==(const generic_scenery_proto& p) const; +}; + +struct door_scenery_proto +{ + unsigned char active : 1 = false; + unsigned char interactive : 1 = false; + unsigned char closing : 1 = false; + + bool operator==(const door_scenery_proto& p) const; +}; + +using scenery_proto_variants = std::variant<generic_scenery_proto, door_scenery_proto>; + +struct scenery_proto : object_proto +{ + scenery_proto_variants subtype; + scenery_proto(); scenery_proto(const scenery_proto&); ~scenery_proto() noexcept override; @@ -33,13 +51,49 @@ struct scenery_proto : object_proto explicit operator bool() const; }; -struct scenery final : object +struct scenery; + +struct generic_scenery { - scenery_type sc_type : 3 = scenery_type::none; unsigned char active : 1 = false; + unsigned char interactive : 1 = false; + + void update(scenery& sc, size_t i, float dt); + Vector2 ordinal_offset(const scenery& sc, Vector2b offset) const; + bool can_activate(const scenery& sc, size_t i) const; + bool activate(scenery& sc, size_t i); + + object_type type() const noexcept; + enum scenery_type scenery_type() const; + explicit operator generic_scenery_proto() const; + + generic_scenery(object_id id, struct chunk& c, const generic_scenery_proto& p); +}; + +struct door_scenery +{ unsigned char closing : 1 = false; + unsigned char active : 1 = false; unsigned char interactive : 1 = false; + void update(scenery& sc, size_t i, float dt); + Vector2 ordinal_offset(const scenery& sc, Vector2b offset) const; + bool can_activate(const scenery& sc, size_t i) const; + bool activate(scenery& sc, size_t i); + + object_type type() const noexcept; + enum scenery_type scenery_type() const; + explicit operator door_scenery_proto() const; + + door_scenery(object_id id, struct chunk& c, const door_scenery_proto& p); +}; + +using scenery_variants = std::variant<generic_scenery, door_scenery>; + +struct scenery final : object +{ + scenery_variants subtype; + void update(size_t i, float dt) override; Vector2 ordinal_offset(Vector2b offset) const override; float depth_offset() const override; @@ -48,6 +102,10 @@ struct scenery final : object object_type type() const noexcept override; explicit operator scenery_proto() const; + enum scenery_type scenery_type() const; + + static scenery_variants subtype_from_proto(object_id id, struct chunk& c, + const scenery_proto_variants& variants); private: friend class world; |