summaryrefslogtreecommitdiffhomepage
path: root/src/entity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/entity.cpp')
-rw-r--r--src/entity.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/entity.cpp b/src/entity.cpp
new file mode 100644
index 00000000..07289291
--- /dev/null
+++ b/src/entity.cpp
@@ -0,0 +1,199 @@
+#include "entity.hpp"
+#include "world.hpp"
+#include "rotation.inl"
+#include "chunk.inl"
+#include "RTree.hpp"
+#include <algorithm>
+
+namespace floormat {
+
+bool entity_proto::operator==(const entity_proto&) const = default;
+entity_proto& entity_proto::operator=(const entity_proto&) = default;
+entity_proto::~entity_proto() noexcept = default;
+entity_proto::entity_proto() = default;
+entity_proto::entity_proto(const entity_proto&) = default;
+
+entity::entity(std::uint64_t id, struct world& w, entity_type type) noexcept :
+ id{id}, w{w}, type{type}
+{
+}
+
+entity::entity(std::uint64_t id, struct world& w, entity_type type, const entity_proto& proto) noexcept :
+ id{id}, w{w}, atlas{proto.atlas},
+ offset{proto.offset}, bbox_offset{proto.bbox_offset},
+ bbox_size{proto.bbox_size}, delta{proto.delta},
+ frame{proto.frame}, type{type}, r{proto.r}, pass{proto.pass}
+{
+ fm_assert(type == proto.type);
+}
+
+entity::~entity() noexcept
+{
+ fm_debug_assert(id);
+ if (w.is_teardown()) [[unlikely]]
+ return;
+ auto& c = chunk();
+ if (!c._teardown) [[likely]]
+ {
+ if (chunk::bbox bb; c._bbox_for_scenery(*this, bb))
+ c._remove_bbox(bb);
+ auto it = std::find_if(c._entities.cbegin(), c._entities.cend(), [id = id](const auto& x) { return x->id == id; });
+ fm_debug_assert(it != c._entities.cend());
+ c._entities.erase(it);
+ }
+ w.do_kill_entity(id);
+}
+
+std::uint32_t entity_proto::ordinal(local_coords local) const
+{
+ return entity::ordinal(local, offset);
+}
+
+std::uint32_t entity::ordinal() const
+{
+ return ordinal(coord.local(), offset);
+}
+
+std::uint32_t entity::ordinal(local_coords xy, Vector2b offset)
+{
+ constexpr auto x_size = (std::uint32_t)TILE_MAX_DIM * (std::uint32_t)iTILE_SIZE[0];
+ auto vec = Vector2ui(xy) * Vector2ui(iTILE_SIZE2) + Vector2ui(offset);
+ return vec[1] * x_size + vec[0];
+}
+
+struct chunk& entity::chunk() const
+{
+ return w[coord.chunk()];
+}
+
+auto entity::iter() const -> It
+{
+ auto& c = chunk();
+ auto it = std::find_if(c._entities.cbegin(), c._entities.cend(), [id = id](const auto& x) { return x->id == id; });
+ fm_assert(it != c._entities.cend());
+ return it;
+}
+
+bool entity::operator==(const entity_proto& o) const
+{
+ return atlas.get() == o.atlas.get() &&
+ type == o.type && frame == o.frame && r == o.r && pass == o.pass &&
+ offset == o.offset && bbox_offset == o.bbox_offset &&
+ bbox_size == o.bbox_size && delta == o.delta;
+}
+
+void entity::rotate(It, struct chunk&, rotation new_r)
+{
+ w[coord.chunk()].with_scenery_update(*this, [&]() {
+ bbox_offset = rotate_point(bbox_offset, r, new_r);
+ bbox_size = rotate_size(bbox_size, r, new_r);
+ r = new_r;
+ });
+}
+
+Pair<global_coords, Vector2b> entity::normalize_coords(global_coords coord, Vector2b cur_offset, Vector2i new_offset)
+{
+ auto tmp = Vector2i(cur_offset) + new_offset;
+ auto new_off = tmp % iTILE_SIZE2;
+ auto tile = tmp / iTILE_SIZE2;
+ return { coord + tile, Vector2b(new_off) };
+}
+
+bool entity::can_move_to(Vector2i delta, struct chunk& c)
+{
+ auto [coord_, offset_] = normalize_coords(coord, offset, delta);
+ auto& c_ = coord.chunk() == coord_.chunk() ? c : w[coord_.chunk()];
+
+ const auto center = Vector2(coord_.local())*TILE_SIZE2 + Vector2(offset_) + Vector2(bbox_offset),
+ half_bbox = Vector2(bbox_size)*.5f,
+ min = center - half_bbox, max = center + half_bbox;
+
+ bool ret = true;
+ c_.rtree()->Search(min.data(), max.data(), [&](std::uint64_t data, const auto&) {
+ auto id2 = std::bit_cast<collision_data>(data).data;
+ if (id2 != id)
+ return ret = false;
+ else
+ return true;
+ });
+ return ret;
+}
+
+void entity::move(It it, Vector2i delta, struct chunk& c)
+{
+ auto e_ = *it;
+ auto& e = *e_;
+ auto& w = e.w;
+ auto& coord = e.coord;
+ auto& offset = e.offset;
+
+ auto& es = c._entities;
+ auto [coord_, offset_] = normalize_coords(coord, offset, delta);
+
+ if (coord_ == coord && offset_ == offset)
+ return;
+
+ if (e.atlas->info().fps == 0)
+ c.mark_scenery_modified(false);
+
+ if (coord_ != coord)
+ Debug{} << "coord" << Vector2i(coord_.chunk()) << Vector2i(coord_.local());
+
+ bool same_chunk = coord_.chunk() == coord.chunk();
+ chunk::bbox bb0, bb1;
+ bool b0 = c._bbox_for_scenery(e, bb0);
+ coord = coord_; offset = offset_;
+ bool b1 = c._bbox_for_scenery(e, bb1);
+
+ if (same_chunk)
+ {
+ c._replace_bbox(bb0, bb1, b0, b1);
+ auto it_ = std::lower_bound(es.cbegin(), es.cend(), e_, [ord = e.ordinal()](const auto& a, const auto&) { return a->ordinal() < ord; });
+ if (it_ != it)
+ {
+ auto pos0 = std::distance(es.cbegin(), it), pos1 = std::distance(es.cbegin(), it_);
+ if (pos1 > pos0)
+ {
+ Debug{} << "decr";
+ pos1--;
+ }
+ else
+ Debug{} << "no decr";
+ es.erase(it);
+ [[maybe_unused]] auto size = es.size();
+ es.insert(es.cbegin() + pos1, std::move(e_));
+ }
+ }
+ else
+ {
+ auto& c2 = w[coord.chunk()];
+ if (e.atlas->info().fps == 0)
+ c2.mark_scenery_modified(false);
+ c._remove_bbox(bb0);
+ c2._add_bbox(bb1);
+ c.remove_entity(it);
+ auto it_ = std::lower_bound(c2._entities.cbegin(), c2._entities.cend(), e_,
+ [ord = e.ordinal()](const auto& a, const auto&) { return a->ordinal() < ord; });
+ c2._entities.insert(it_, std::move(e_));
+ }
+}
+
+entity::operator entity_proto() const
+{
+ entity_proto x;
+ x.atlas = atlas;
+ x.offset = offset;
+ x.bbox_offset = bbox_offset;
+ x.bbox_size = bbox_size;
+ x.delta = delta;
+ x.frame = frame;
+ x.type = type;
+ x.r = r;
+ x.pass = pass;
+ return x;
+}
+
+bool entity::can_activate(It, struct chunk&) const { return false; }
+bool entity::activate(It, struct chunk&) { return false; }
+
+} // namespace floormat