summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2023-03-14 07:33:47 +0100
committerStanislaw Halik <sthalik@misaki.pl>2023-03-14 07:33:47 +0100
commitdc5e66b5a29fd7de8ddf59852ceefd982289b7c3 (patch)
tree18bf0a274f1595d6d2d6cfb32a3b3825d843e315 /src
parent29bdd5f2170b9d46a8b3b0973c4c0845d6a2b61e (diff)
a
Diffstat (limited to 'src')
-rw-r--r--src/character.cpp121
-rw-r--r--src/character.hpp32
-rw-r--r--src/chunk-collision.cpp43
-rw-r--r--src/chunk-render.cpp88
-rw-r--r--src/chunk.cpp64
-rw-r--r--src/chunk.hpp37
-rw-r--r--src/chunk.inl17
-rw-r--r--src/entity.cpp199
-rw-r--r--src/entity.hpp80
-rw-r--r--src/pass-mode.hpp1
-rw-r--r--src/scenery.cpp134
-rw-r--r--src/scenery.hpp104
-rw-r--r--src/tile.cpp15
-rw-r--r--src/tile.hpp8
-rw-r--r--src/world.cpp29
-rw-r--r--src/world.hpp26
16 files changed, 720 insertions, 278 deletions
diff --git a/src/character.cpp b/src/character.cpp
new file mode 100644
index 00000000..18472954
--- /dev/null
+++ b/src/character.cpp
@@ -0,0 +1,121 @@
+#include "character.hpp"
+#include "src/anim-atlas.hpp"
+#include "loader/loader.hpp"
+#include "src/world.hpp"
+#include "src/entity.hpp"
+#include "src/RTree.hpp"
+#include <cmath>
+
+namespace floormat {
+
+namespace {
+
+template <typename T>
+constexpr T sgn(T val) { return T(T(0) < val) - T(val < T(0)); }
+
+constexpr int tile_size_1 = iTILE_SIZE2.sum()/2,
+ framerate = 96, move_speed = tile_size_1 * 2;
+constexpr float frame_time = 1.f/framerate;
+
+constexpr auto arrows_to_dir(bool L, bool R, bool U, bool D)
+{
+ if (L == R)
+ L = R = false;
+ if (U == D)
+ U = D = false;
+
+ using enum rotation;
+ struct {
+ int lr = 0, ud = 0;
+ rotation r = N;
+ } dir;
+
+ if (L && U)
+ dir = { -1, 0, W };
+ else if (L && D)
+ dir = { 0, 1, S };
+ else if (R && U)
+ dir = { 0, -1, N };
+ else if (R && D)
+ dir = { 1, 0, E };
+ else if (L)
+ dir = { -1, 1, SW };
+ else if (D)
+ dir = { 1, 1, SE };
+ else if (R)
+ dir = { 1, -1, NE };
+ else if (U)
+ dir = { -1, -1, NW };
+
+ return dir;
+}
+
+} // namespace
+
+character::character(std::uint64_t id, struct world& w, entity_type type) : entity{id, w, type}
+{
+ atlas = loader.anim_atlas("npc-walk", loader.ANIM_PATH);
+ bbox_size = {12, 12};
+}
+
+character::~character() = default;
+
+int character::allocate_frame_time(float dt)
+{
+ int d = int(delta) + int(65535u * dt);
+ constexpr int framerate_ = 65535/framerate;
+ static_assert(framerate_ > 0);
+ auto ret = d / framerate_;
+ delta = (std::uint16_t)std::clamp(d - ret*65535, 0, 65535);
+ return ret;
+}
+
+Vector2 character::move_vec(int left_right, int top_bottom)
+{
+ constexpr auto c = move_speed * frame_time;
+ return c * Vector2(sgn(left_right), sgn(top_bottom)).normalized();
+}
+
+void character::set_keys(bool L, bool R, bool U, bool D)
+{
+ b_L = L;
+ b_R = R;
+ b_U = U;
+ b_D = D;
+}
+
+bool character::update(It it, struct chunk& c, float dt)
+{
+ auto [lr, ud, rot] = arrows_to_dir(b_L, b_R, b_U, b_D);
+
+ if (!lr & !ud)
+ {
+ delta = 0;
+ return false;
+ }
+
+ int nframes = allocate_frame_time(dt);
+
+ if (!nframes)
+ return false;
+
+ const auto vec = move_vec(lr, ud);
+ r = rot;
+ c.ensure_passability();
+
+ for (int i = 0; i < nframes; i++)
+ {
+ constexpr auto frac = Vector2(32767);
+ constexpr auto inv_frac = Vector2(1.f/32767);
+ auto offset_ = vec + Vector2(offset_frac) * inv_frac;
+ offset_frac = Vector2s(Vector2(std::fmod(offset_[0], 1.f), std::fmod(offset_[1], 1.f)) * frac);
+ auto off_i = Vector2i(offset_);
+ if (can_move_to(off_i, c))
+ entity::move(it, off_i, c);
+ ++frame %= atlas->info().nframes;
+ }
+ //Debug{} << "pos" << Vector2i(pos.local());
+ return true;
+}
+
+} // namespace floormat
diff --git a/src/character.hpp b/src/character.hpp
new file mode 100644
index 00000000..d69b98dc
--- /dev/null
+++ b/src/character.hpp
@@ -0,0 +1,32 @@
+#pragma once
+#include "src/global-coords.hpp"
+#include "src/rotation.hpp"
+#include "src/entity.hpp"
+#include <memory>
+
+namespace floormat {
+
+struct anim_atlas;
+struct world;
+
+struct character final : entity
+{
+ ~character() override;
+ void set_keys(bool L, bool R, bool U, bool D);
+ bool update(It it, struct chunk& c, float dt) override;
+
+private:
+ int allocate_frame_time(float dt);
+ static Vector2 move_vec(int left_right, int top_bottom);
+ character(std::uint64_t id, struct world& w, entity_type type);
+
+ friend struct world;
+
+ Vector2s offset_frac;
+
+ bool b_L : 1 = false, b_R : 1 = false, b_U : 1 = false, b_D : 1 = false;
+};
+
+template<> struct entity_type_<struct character> : std::integral_constant<entity_type, entity_type::character> {};
+
+} // namespace floormat
diff --git a/src/chunk-collision.cpp b/src/chunk-collision.cpp
index 3cf772ef..032c9d7d 100644
--- a/src/chunk-collision.cpp
+++ b/src/chunk-collision.cpp
@@ -18,10 +18,9 @@ constexpr Vector2 tile_start(std::size_t k)
return TILE_SIZE2 * Vector2(coord) - half_tile;
}
-constexpr Pair<Vector2i, Vector2i> scenery_tile(std::size_t k, const scenery& sc)
+Pair<Vector2i, Vector2i> scenery_tile(const entity& sc)
{
- const local_coords coord{k};
- auto center = iTILE_SIZE2 * Vector2i(coord) + Vector2i(sc.offset) + Vector2i(sc.bbox_offset);
+ auto center = iTILE_SIZE2 * Vector2i(sc.coord.local()) + Vector2i(sc.offset) + Vector2i(sc.bbox_offset);
auto start = center - Vector2i(sc.bbox_size);
auto size = Vector2i(sc.bbox_size)*2;
return { start, start + size, };
@@ -60,15 +59,13 @@ void chunk::ensure_passability() noexcept
_rtree.RemoveAll();
- for (auto i = 0_uz; i < TILE_COUNT; i++)
+ for (const std::shared_ptr<entity>& s : entities())
{
- if (auto s = operator[](i).scenery())
- {
- bbox box;
- if (_bbox_for_scenery(i, box))
- _add_bbox(box);
- }
+ bbox box;
+ if (_bbox_for_scenery(*s, box))
+ _add_bbox(box);
}
+
for (auto i = 0_uz; i < TILE_COUNT; i++)
{
if (const auto* atlas = ground_atlas_at(i))
@@ -99,24 +96,26 @@ void chunk::ensure_passability() noexcept
}
}
-bool chunk::_bbox_for_scenery(std::size_t i, bbox& value) noexcept
+bool chunk::_bbox_for_scenery(const entity& s, bbox& value) noexcept
{
- fm_debug_assert(i < TILE_COUNT);
- auto [atlas, s] = operator[](i).scenery();
- auto [start, end] = scenery_tile(i, s);
- auto id = make_id(collision_type::scenery, s.passability, i);
+ auto [start, end] = scenery_tile(s);
+ auto id = make_id(collision_type::scenery, s.pass, s.id);
value = { .id = id, .start = start, .end = end };
- return atlas && s.passability != pass_mode::pass && Vector2i(s.bbox_size).product() > 0;
+ return s.atlas && s.pass != pass_mode::pass;
}
void chunk::_remove_bbox(const bbox& x)
{
+ if (_scenery_modified)
+ return;
auto start = Vector2(x.start), end = Vector2(x.end);
_rtree.Remove(start.data(), end.data(), x.id);
}
void chunk::_add_bbox(const bbox& x)
{
+ if (_scenery_modified)
+ return;
auto start = Vector2(x.start), end = Vector2(x.end);
_rtree.Insert(start.data(), end.data(), x.id);
}
@@ -145,4 +144,16 @@ void chunk::_replace_bbox(const bbox& x0, const bbox& x1, bool b0, bool b1)
CORRADE_ASSUME(false);
}
+bool chunk::can_place_entity(const entity_proto& proto, local_coords pos)
+{
+ (void)ensure_scenery_mesh();
+
+ const auto center = Vector2(pos)*TILE_SIZE2 + Vector2(proto.offset) + Vector2(proto.bbox_offset),
+ half_bbox = Vector2(proto.bbox_size)*.5f,
+ min = center - half_bbox, max = center + half_bbox;
+ bool ret = true;
+ _rtree.Search(min.data(), max.data(), [&](auto, const auto&) { return ret = false; });
+ return ret;
+}
+
} // namespace floormat
diff --git a/src/chunk-render.cpp b/src/chunk-render.cpp
index bc22ef9b..e370239f 100644
--- a/src/chunk-render.cpp
+++ b/src/chunk-render.cpp
@@ -1,7 +1,8 @@
#include "chunk.hpp"
#include "tile-atlas.hpp"
-#include "anim-atlas.hpp"
#include "shaders/tile.hpp"
+#include "entity.hpp"
+#include "anim-atlas.hpp"
#include <algorithm>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Magnum/GL/Buffer.h>
@@ -17,12 +18,6 @@ static auto make_index_array(std::size_t max)
return array;
}
-struct vertex {
- Vector3 position;
- Vector2 texcoords;
- float depth = -1;
-};
-
auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple
{
if (!_ground_modified)
@@ -111,48 +106,47 @@ auto chunk::ensure_wall_mesh() noexcept -> wall_mesh_tuple
auto chunk::ensure_scenery_mesh() noexcept -> scenery_mesh_tuple
{
- if (!_scenery_modified)
- return { scenery_mesh, scenery_indexes, std::size_t(scenery_mesh.count()/6) };
- _scenery_modified = false;
-
- std::size_t count = 0;
- for (auto i = 0_uz; i < TILE_COUNT; i++)
- if (const auto& atlas = _scenery_atlases[i]; atlas && atlas->info().fps == 0)
- scenery_indexes[count++] = std::uint8_t(i);
-
-#if 0
- std::sort(scenery_indexes.begin(), scenery_indexes.begin() + count,
- [this](std::uint8_t a, std::uint8_t b) {
- return _scenery_atlases[a] < _scenery_atlases[b];
- });
-#endif
- std::array<std::array<vertex, 4>, TILE_COUNT> vertexes;
- for (auto k = 0_uz; k < count; k++)
+ if (_scenery_modified)
{
- const std::uint8_t i = scenery_indexes[k];
- const local_coords pos{i};
- const auto& atlas = _scenery_atlases[i];
- const auto& fr = _scenery_variants[i];
- const auto coord = Vector3(pos) * TILE_SIZE + Vector3(Vector2(fr.offset), 0);
- const auto quad = atlas->frame_quad(coord, fr.r, fr.frame);
- const auto& group = atlas->group(fr.r);
- const auto texcoords = atlas->texcoords_for_frame(fr.r, fr.frame, !group.mirror_from.isEmpty());
- const float depth = tile_shader::depth_value(pos, tile_shader::scenery_depth_offset);
- auto& v = vertexes[k];
- for (auto j = 0_uz; j < 4; j++)
- v[j] = { quad[j], texcoords[j], depth };
+ _scenery_modified = false;
+ const auto count = fm_begin(
+ std::size_t ret = 0;
+ for (const auto& e : _entities)
+ ret += e->atlas->info().fps == 0;
+ return ret;
+ );
+
+ scenery_indexes.clear();
+ scenery_indexes.reserve(count);
+ scenery_vertexes.clear();
+ scenery_vertexes.reserve(count);
+
+ for (const auto& e : _entities)
+ {
+ const auto i = scenery_indexes.size();
+ scenery_indexes.emplace_back();
+ scenery_indexes.back() = tile_atlas::indices(i);
+ const auto& atlas = e->atlas;
+ const auto& fr = *e;
+ const auto pos = e->coord.local();
+ const auto coord = Vector3(pos) * TILE_SIZE + Vector3(Vector2(fr.offset), 0);
+ const auto quad = atlas->frame_quad(coord, fr.r, fr.frame);
+ const auto& group = atlas->group(fr.r);
+ const auto texcoords = atlas->texcoords_for_frame(fr.r, fr.frame, !group.mirror_from.isEmpty());
+ const float depth = tile_shader::depth_value(pos, tile_shader::scenery_depth_offset);
+ scenery_vertexes.emplace_back();
+ auto& v = scenery_vertexes.back();
+ for (auto j = 0_uz; j < 4; j++)
+ v[j] = { quad[j], texcoords[j], depth };
+ }
+
+ GL::Mesh mesh{GL::MeshPrimitive::Triangles};
+ mesh.addVertexBuffer(GL::Buffer{scenery_vertexes}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
+ .setIndexBuffer(GL::Buffer{scenery_indexes}, 0, GL::MeshIndexType::UnsignedShort)
+ .setCount(std::int32_t(6 * count));
+ scenery_mesh = Utility::move(mesh);
}
-
- const auto indexes = make_index_array(count);
- const auto vertex_view = ArrayView{vertexes.data(), count};
- const auto vert_index_view = ArrayView{indexes.data(), count};
-
- GL::Mesh mesh{GL::MeshPrimitive::Triangles};
- mesh.addVertexBuffer(GL::Buffer{vertex_view}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
- .setIndexBuffer(GL::Buffer{vert_index_view}, 0, GL::MeshIndexType::UnsignedShort)
- .setCount(std::int32_t(6 * count));
- scenery_mesh = Utility::move(mesh);
- return { scenery_mesh, scenery_indexes, count };
+ return { scenery_mesh, };
}
} // namespace floormat
diff --git a/src/chunk.cpp b/src/chunk.cpp
index 6cc7ee78..6edced05 100644
--- a/src/chunk.cpp
+++ b/src/chunk.cpp
@@ -1,5 +1,7 @@
#include "chunk.hpp"
#include "src/tile-atlas.hpp"
+#include "anim-atlas.hpp"
+#include <algorithm>
#include <Magnum/GL/Context.h>
namespace floormat {
@@ -22,17 +24,16 @@ bool chunk::empty(bool force) const noexcept
if (!force && !_maybe_empty)
return false;
for (auto i = 0_uz; i < TILE_COUNT; i++)
- if (_ground_atlases[i] || _wall_atlases[i*2 + 0] || _wall_atlases[i*2 + 1] || _scenery_atlases[i])
+ if (_ground_atlases[i] || _wall_atlases[i*2 + 0] || _wall_atlases[i*2 + 1] || !_entities.empty())
return _maybe_empty = false;
+ if (!_entities.empty())
+ return false;
return true;
}
tile_atlas* chunk::ground_atlas_at(std::size_t i) const noexcept { return _ground_atlases[i].get(); }
tile_atlas* chunk::wall_atlas_at(std::size_t i) const noexcept { return _wall_atlases[i].get(); }
-std::shared_ptr<anim_atlas>& chunk::scenery_atlas_at(std::size_t i) noexcept { return _scenery_atlases[i]; }
-scenery& chunk::scenery_at(std::size_t i) noexcept { return _scenery_variants[i]; }
-
tile_ref chunk::operator[](std::size_t idx) noexcept { return { *this, std::uint8_t(idx) }; }
tile_proto chunk::operator[](std::size_t idx) const noexcept { return tile_proto(tile_ref { *const_cast<chunk*>(this), std::uint8_t(idx) }); }
tile_ref chunk::operator[](local_coords xy) noexcept { return operator[](xy.to_index()); }
@@ -89,11 +90,64 @@ void chunk::mark_modified() noexcept
}
chunk::chunk() noexcept = default;
-chunk::~chunk() noexcept = default;
+chunk::~chunk() noexcept
+{
+ _teardown = true;
+ _entities.clear();
+ _rtree.RemoveAll();
+}
chunk::chunk(chunk&&) noexcept = default;
chunk& chunk::operator=(chunk&&) noexcept = default;
bool chunk::bbox::operator==(const bbox& other) const noexcept = default;
+void chunk::add_entity_unsorted(const std::shared_ptr<entity>& e)
+{
+ if (e->atlas->info().fps == 0)
+ mark_scenery_modified(false);
+ if (bbox bb; _bbox_for_scenery(*e, bb))
+ _add_bbox(bb);
+ _entities.push_back(e);
+}
+
+void chunk::sort_entities()
+{
+ mark_scenery_modified(false);
+
+ std::sort(_entities.begin(), _entities.end(), [](const auto& a, const auto& b) {
+ return a->ordinal() < b->ordinal();
+ });
+}
+
+void chunk::add_entity(const std::shared_ptr<entity>& e)
+{
+ if (e->atlas->info().fps == 0)
+ mark_scenery_modified(false);
+ if (bbox bb; _bbox_for_scenery(*e, bb))
+ _add_bbox(bb);
+
+ _entities.reserve(_entities.size() + 1);
+ auto it = std::lower_bound(_entities.cbegin(), _entities.cend(), e, [ord = e->ordinal()](const auto& a, const auto&) {
+ return a->ordinal() < ord;
+ });
+ _entities.insert(it, e);
+}
+
+void chunk::remove_entity(entity_const_iterator it)
+{
+ const auto& e = *it;
+ if (e->atlas->info().fps == 0)
+ mark_scenery_modified(false);
+ if (bbox bb; _bbox_for_scenery(*e, bb))
+ _remove_bbox(bb);
+
+ _entities.erase(it);
+}
+
+const std::vector<std::shared_ptr<entity>>& chunk::entities() const
+{
+ return _entities;
+}
+
} // namespace floormat
diff --git a/src/chunk.hpp b/src/chunk.hpp
index a2fdf53f..a5eba19e 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -11,6 +11,7 @@
namespace floormat {
struct anim_atlas;
+struct entity;
enum class collision : std::uint8_t {
view, shoot, move,
@@ -29,6 +30,7 @@ struct collision_data final {
struct chunk final
{
friend struct tile_ref;
+ friend struct entity;
tile_ref operator[](std::size_t idx) noexcept;
tile_proto operator[](std::size_t idx) const noexcept;
@@ -76,8 +78,12 @@ struct chunk final
struct scenery_mesh_tuple final {
GL::Mesh& mesh;
- const ArrayView<const std::uint8_t> ids;
- const std::size_t size;
+ };
+
+ struct vertex {
+ Vector3 position;
+ Vector2 texcoords;
+ float depth = -1;
};
ground_mesh_tuple ensure_ground_mesh() noexcept;
@@ -87,8 +93,6 @@ struct chunk final
tile_atlas* wall_atlas_at(std::size_t i) const noexcept;
scenery_mesh_tuple ensure_scenery_mesh() noexcept;
- std::shared_ptr<anim_atlas>& scenery_atlas_at(std::size_t i) noexcept;
- scenery& scenery_at(std::size_t i) noexcept;
void ensure_passability() noexcept;
@@ -99,7 +103,18 @@ struct chunk final
template<typename F>
requires requires(F fun) { fun(); }
- void with_scenery_update(std::size_t idx, F&& fun);
+ void with_scenery_update(entity& e, F&& fun);
+
+ using entity_vector = std::vector<std::shared_ptr<entity>>;
+ using entity_const_iterator = typename entity_vector::const_iterator;
+
+ [[nodiscard]] bool can_place_entity(const entity_proto& proto, local_coords pos);
+
+ void add_entity(const std::shared_ptr<entity>& e);
+ void add_entity_unsorted(const std::shared_ptr<entity>& e);
+ void sort_entities();
+ void remove_entity(entity_const_iterator it);
+ const std::vector<std::shared_ptr<entity>>& entities() const;
private:
std::array<std::shared_ptr<tile_atlas>, TILE_COUNT> _ground_atlases;
@@ -108,9 +123,10 @@ private:
std::array<std::shared_ptr<tile_atlas>, TILE_COUNT*2> _wall_atlases;
std::array<std::uint16_t, TILE_COUNT*2> wall_indexes = {};
std::array<variant_t, TILE_COUNT*2> _wall_variants = {};
- std::array<std::shared_ptr<anim_atlas>, TILE_COUNT> _scenery_atlases;
- std::array<std::uint8_t, TILE_COUNT> scenery_indexes = {};
- std::array<scenery, TILE_COUNT> _scenery_variants = {};
+ std::vector<std::shared_ptr<entity>> _entities;
+
+ std::vector<std::array<UnsignedShort, 6>> scenery_indexes;
+ std::vector<std::array<vertex, 4>> scenery_vertexes;
GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate}, scenery_mesh{NoCreate};
@@ -120,7 +136,8 @@ private:
_ground_modified : 1 = true,
_walls_modified : 1 = true,
_scenery_modified : 1 = true,
- _pass_modified : 1 = true;
+ _pass_modified : 1 = true,
+ _teardown : 1 = false;
struct bbox final // NOLINT(cppcoreguidelines-pro-type-member-init)
{
@@ -129,7 +146,7 @@ private:
bool operator==(const bbox& other) const noexcept;
};
- bool _bbox_for_scenery(std::size_t i, bbox& value) noexcept;
+ bool _bbox_for_scenery(const entity& s, bbox& value) noexcept;
void _remove_bbox(const bbox& x);
void _add_bbox(const bbox& x);
void _replace_bbox(const bbox& x0, const bbox& x, bool b0, bool b);
diff --git a/src/chunk.inl b/src/chunk.inl
index 23013099..3d40c30e 100644
--- a/src/chunk.inl
+++ b/src/chunk.inl
@@ -1,16 +1,20 @@
#pragma once
#include "chunk.hpp"
+#include "anim-atlas.hpp"
namespace floormat {
template<typename F>
requires requires(F fun) { fun(); }
-void chunk::with_scenery_update(std::size_t idx, F&& fun)
+void chunk::with_scenery_update(entity& s, F&& fun)
{
static_assert(std::is_convertible_v<decltype(fun()), bool> || std::is_same_v<void, decltype(fun())>);
- const auto& s = scenery_at(idx), s0 = s;
- bbox bb0; bool b0 = _bbox_for_scenery(idx, bb0);
+ // todo handle coord & offset fields
+
+ auto ch = s.coord.chunk();
+ entity_proto s0 = s;
+ bbox bb0; bool b0 = _bbox_for_scenery(s, bb0);
bool modified = true;
if constexpr(!std::is_same_v<void, std::decay_t<decltype(fun())>>)
@@ -20,10 +24,13 @@ void chunk::with_scenery_update(std::size_t idx, F&& fun)
if (!modified)
return;
+ if (s.coord.chunk() != ch) // todo
+ return;
+
if (bbox bb; !is_passability_modified())
- if (bool b = _bbox_for_scenery(idx, bb); b != b0 || bb != bb0)
+ if (bool b = _bbox_for_scenery(s, bb); b != b0 || bb != bb0)
_replace_bbox(bb0, bb, b0, b);
- if (!is_scenery_modified() && scenery::is_mesh_modified(s0, s))
+ if (!is_scenery_modified() && s.atlas->info().fps == 0 && s != s0)
mark_scenery_modified(false);
}
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
diff --git a/src/entity.hpp b/src/entity.hpp
new file mode 100644
index 00000000..18c588bd
--- /dev/null
+++ b/src/entity.hpp
@@ -0,0 +1,80 @@
+#pragma once
+#include "compat/defs.hpp"
+#include "src/global-coords.hpp"
+#include "src/rotation.hpp"
+#include "src/pass-mode.hpp"
+#include <memory>
+#include <vector>
+
+namespace floormat {
+
+template<typename T> struct entity_type_;
+struct anim_atlas;
+struct world;
+
+enum class entity_type : std::uint8_t {
+ none, character, scenery,
+};
+
+struct entity_proto
+{
+ std::shared_ptr<anim_atlas> atlas;
+ Vector2b offset, bbox_offset;
+ Vector2ub bbox_size = Vector2ub(iTILE_SIZE2);
+ std::uint16_t delta = 0, frame = 0;
+ entity_type type = entity_type::none;
+ rotation r : rotation_BITS = rotation::N;
+ pass_mode pass : pass_mode_BITS = pass_mode::see_through;
+
+ std::uint32_t ordinal(local_coords coord) const;
+ entity_proto& operator=(const entity_proto&);
+ entity_proto();
+ entity_proto(const entity_proto&);
+
+ bool operator==(const entity_proto&) const;
+ virtual ~entity_proto() noexcept;
+};
+
+struct entity
+{
+ fm_DECLARE_DELETED_COPY_ASSIGNMENT(entity);
+ using It = typename std::vector<std::shared_ptr<entity>>::const_iterator;
+
+ const std::uint64_t id = 0;
+ world& w;
+ std::shared_ptr<anim_atlas> atlas;
+ global_coords coord;
+ Vector2b offset, bbox_offset;
+ Vector2ub bbox_size;
+ std::uint16_t delta = 0, frame = 0;
+ const entity_type type;
+ rotation r : rotation_BITS = rotation::N;
+ pass_mode pass : pass_mode_BITS = pass_mode::see_through;
+
+ virtual ~entity() noexcept;
+
+ std::uint32_t ordinal() const;
+ static std::uint32_t ordinal(local_coords xy, Vector2b offset);
+ struct chunk& chunk() const;
+ It iter() const;
+
+ virtual bool operator==(const entity_proto& e0) const;
+ operator entity_proto() const;
+
+ virtual bool can_activate(It it, struct chunk& c) const;
+ virtual bool activate(It it, struct chunk& c);
+ virtual bool update(It it, struct chunk& c, float dt) = 0;
+ virtual void rotate(It it, struct chunk& c, rotation r);
+
+ static Pair<global_coords, Vector2b> normalize_coords(global_coords coord, Vector2b cur_offset, Vector2i delta);
+ [[nodiscard]] virtual bool can_move_to(Vector2i delta, struct chunk& c);
+ static void move(It it, Vector2i delta, struct chunk& c);
+
+ friend struct world;
+
+protected:
+ entity(std::uint64_t id, struct world& w, entity_type type) noexcept;
+ entity(std::uint64_t id, struct world& w, entity_type type, const entity_proto& proto) noexcept;
+};
+
+} // namespace floormat
diff --git a/src/pass-mode.hpp b/src/pass-mode.hpp
index 112aafcc..accefaae 100644
--- a/src/pass-mode.hpp
+++ b/src/pass-mode.hpp
@@ -5,5 +5,6 @@ namespace floormat {
enum class pass_mode : std::uint8_t { blocked, see_through, shoot_through, pass, };
constexpr inline std::uint8_t pass_mode_COUNT = 4;
+constexpr inline std::uint8_t pass_mode_BITS = 2;
} // namespace floormat
diff --git a/src/scenery.cpp b/src/scenery.cpp
index e2ebdb49..cf58a8de 100644
--- a/src/scenery.cpp
+++ b/src/scenery.cpp
@@ -2,91 +2,30 @@
#include "anim-atlas.hpp"
#include "chunk.hpp"
#include "compat/assert.hpp"
-#include "rotation.inl"
-#include "chunk.inl"
+#include "world.hpp"
#include <algorithm>
namespace floormat {
-scenery_proto::scenery_proto() noexcept = default;
-scenery_proto::scenery_proto(const std::shared_ptr<anim_atlas>& atlas, const scenery& frame) noexcept :
- atlas{atlas}, frame{frame}
-{}
+scenery_proto::scenery_proto() = default;
-scenery_proto& scenery_proto::operator=(const scenery_proto&) noexcept = default;
-scenery_proto::scenery_proto(const scenery_proto&) noexcept = default;
-scenery_proto::operator bool() const noexcept { return atlas != nullptr; }
+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; }
-scenery_ref::scenery_ref(struct chunk& c, std::size_t i) noexcept :
- atlas{c.scenery_atlas_at(i)}, frame{c.scenery_at(i)},
- c{&c}, idx{std::uint8_t(i)}
-{}
-scenery_ref::scenery_ref(const scenery_ref&) noexcept = default;
-scenery_ref::scenery_ref(scenery_ref&&) noexcept = default;
-struct chunk& scenery_ref::chunk() noexcept { return *c; }
-std::uint8_t scenery_ref::index() const noexcept { return idx; }
-
-scenery_ref& scenery_ref::operator=(const scenery_proto& proto) noexcept
-{
- atlas = proto.atlas;
- frame = proto.frame;
- return *this;
-}
-
-scenery_ref::operator scenery_proto() const noexcept { return { atlas, frame }; }
-scenery_ref::operator bool() const noexcept { return atlas != nullptr; }
-
-scenery::scenery(generic_tag_t, const anim_atlas& atlas, rotation r, frame_t frame,
- pass_mode passability, bool active, bool interactive,
- Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size) :
- frame{frame},
- offset{offset},
- bbox_offset{rotate_point(bbox_offset, atlas.first_rotation(), r)},
- bbox_size{rotate_size(bbox_size, atlas.first_rotation(), r)},
- r{r}, type{scenery_type::generic},
- passability{passability},
- active{active}, interactive{interactive}
-{
- fm_assert(r < rotation_COUNT);
- fm_assert(frame < atlas.group(r).frames.size());
-}
-
-scenery::scenery(door_tag_t, const anim_atlas& atlas, rotation r, bool is_open,
- Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size) :
- frame{frame_t(is_open ? 0 : atlas.group(r).frames.size()-1)},
- offset{offset},
- bbox_offset{rotate_point(bbox_offset, atlas.first_rotation(), r)},
- bbox_size{rotate_size(bbox_size, atlas.first_rotation(), r)},
- r{r}, type{scenery_type::door},
- passability{is_open ? pass_mode::pass : pass_mode::blocked},
- interactive{true}
-{
- fm_assert(r < rotation_COUNT);
- fm_assert(atlas.group(r).frames.size() >= 2);
-}
-
-void scenery_ref::rotate(rotation new_r)
-{
- c->with_scenery_update(idx, [&]() {
- auto& s = frame;
- s.bbox_offset = rotate_point(s.bbox_offset, s.r, new_r);
- s.bbox_size = rotate_size(s.bbox_size, s.r, new_r);
- s.r = new_r;
- });
-}
-
-bool scenery_ref::can_activate() const noexcept
+bool scenery::can_activate(It, struct chunk&) const
{
- return atlas && frame.interactive;
+ return atlas && interactive;
}
-bool scenery_ref::update(float dt)
+bool scenery::update(It, struct chunk&, float dt)
{
- auto& s = frame;
+ auto& s = *this;
if (!s.active)
return false;
- switch (s.type)
+ switch (s.sc_type)
{
default:
case scenery_type::none:
@@ -109,25 +48,25 @@ bool scenery_ref::update(float dt)
const int fr = s.frame + dir*n;
s.active = fr > 0 && fr < nframes-1;
if (fr <= 0)
- s.passability = pass_mode::pass;
+ s.pass = pass_mode::pass;
else if (fr >= nframes-1)
- s.passability = pass_mode::blocked;
+ s.pass = pass_mode::blocked;
else
- s.passability = pass_mode::see_through;
- s.frame = (scenery::frame_t)std::clamp(fr, 0, nframes-1);
+ s.pass = pass_mode::see_through;
+ s.frame = (std::uint16_t)std::clamp(fr, 0, nframes-1);
if (!s.active)
s.delta = s.closing = 0;
return true;
}
}
-bool scenery_ref::activate()
+bool scenery::activate(It, struct chunk&)
{
- auto& s = frame;
- if (!*this || s.active)
+ auto& s = *this;
+ if (s.active)
return false;
- switch (s.type)
+ switch (s.sc_type)
{
default:
case scenery_type::none:
@@ -143,22 +82,29 @@ bool scenery_ref::activate()
return false;
}
-bool scenery::is_mesh_modified(const scenery& s0, const scenery& s)
+bool scenery::operator==(const entity_proto& e0) const
{
- if (s.interactive != s0.interactive || s.type != s0.type)
- return true;
- if (!s.interactive)
- return s.r != s0.r || s.offset != s0.offset || s0.frame != s.frame || s.type != s0.type;
- return false;
+ if (!entity::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;
}
-#ifdef __GNUG__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-bool scenery::operator==(const scenery&) const noexcept = default;
-#ifdef __GNUG__
-#pragma GCC diagnostic pop
-#endif
+scenery::scenery(std::uint64_t id, struct world& w, entity_type type, const scenery_proto& proto) :
+ entity{id, w, type}, sc_type{proto.sc_type}, active{proto.active},
+ closing{proto.closing}, interactive{proto.interactive}
+{
+ fm_assert(type == proto.type);
+ atlas = proto.atlas;
+ offset = proto.offset;
+ bbox_offset = proto.bbox_offset;
+ bbox_size = proto.bbox_size;
+ delta = proto.delta;
+ frame = proto.frame;
+ r = proto.r;
+ pass = proto.pass;
+}
} // namespace floormat
diff --git a/src/scenery.hpp b/src/scenery.hpp
index 9d9ad6e6..6f8afadd 100644
--- a/src/scenery.hpp
+++ b/src/scenery.hpp
@@ -2,6 +2,7 @@
#include "pass-mode.hpp"
#include "tile-defs.hpp"
#include "rotation.hpp"
+#include "entity.hpp"
#include <cstdint>
#include <memory>
#include <type_traits>
@@ -12,104 +13,43 @@ namespace floormat {
struct chunk;
struct anim_atlas;
+struct world;
enum class scenery_type : std::uint8_t {
none, generic, door,
};
-struct scenery final
+struct scenery_proto : entity_proto
{
- struct none_tag_t final {};
- struct generic_tag_t final {};
- struct door_tag_t final {};
-
- static constexpr auto none = none_tag_t{};
- static constexpr auto generic = generic_tag_t{};
- static constexpr auto door = door_tag_t{};
-
- using frame_t = std::uint16_t;
-
- std::uint16_t delta = 0;
- frame_t frame = 0;
- Vector2b offset, bbox_offset;
- Vector2ub bbox_size{usTILE_SIZE2/2};
- rotation r : 3 = rotation::N;
- scenery_type type : 3 = scenery_type::none;
- pass_mode passability : 2 = pass_mode::shoot_through;
+ scenery_type sc_type : 3 = scenery_type::none;
std::uint8_t active : 1 = false;
std::uint8_t closing : 1 = false;
std::uint8_t interactive : 1 = false;
- constexpr scenery() noexcept;
- constexpr scenery(none_tag_t) noexcept;
- scenery(generic_tag_t, const anim_atlas& atlas, rotation r, frame_t frame,
- pass_mode passability, bool active, bool interactive,
- Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size);
- scenery(door_tag_t, const anim_atlas& atlas, rotation r, bool is_open,
- Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size);
-
- static bool is_mesh_modified(const scenery& s1, const scenery& s2);
-
- bool operator==(const scenery&) const noexcept;
+ scenery_proto();
+ scenery_proto(const scenery_proto&);
+ ~scenery_proto() noexcept override;
+ scenery_proto& operator=(const scenery_proto&);
+ operator bool() const;
};
-constexpr scenery::scenery() noexcept : scenery{scenery::none_tag_t{}} {}
-constexpr scenery::scenery(none_tag_t) noexcept {}
-
-struct scenery_proto final
+struct scenery final : entity
{
- std::shared_ptr<anim_atlas> atlas;
- scenery frame;
-
- scenery_proto() noexcept;
- scenery_proto(const std::shared_ptr<anim_atlas>& atlas, const scenery& frame) noexcept;
- scenery_proto& operator=(const scenery_proto&) noexcept;
- scenery_proto(const scenery_proto&) noexcept;
-
- template<typename... Ts>
- scenery_proto(scenery::generic_tag_t, const std::shared_ptr<anim_atlas>& atlas, Ts&&... args) :
- atlas{atlas}, frame{scenery::generic, *atlas, std::forward<Ts>(args)...}
- {}
-
- template<typename... Ts>
- scenery_proto(scenery::door_tag_t, const std::shared_ptr<anim_atlas>& atlas, Ts&&... args) :
- atlas{atlas}, frame{scenery::door, *atlas, std::forward<Ts>(args)...}
- {}
-
- operator bool() const noexcept;
-};
-
-struct scenery_ref final {
- scenery_ref(struct chunk& c, std::size_t i) noexcept;
- scenery_ref(const scenery_ref&) noexcept;
- scenery_ref(scenery_ref&&) noexcept;
- scenery_ref& operator=(const scenery_ref&) = delete;
- scenery_ref& operator=(const scenery_proto& proto) noexcept;
-
- operator scenery_proto() const noexcept;
- operator bool() const noexcept;
-
- struct chunk& chunk() noexcept;
- std::uint8_t index() const noexcept;
-
- template<std::size_t N> std::tuple_element_t<N, scenery_ref>& get() & { if constexpr(N == 0) return atlas; else return frame; }
- template<std::size_t N> std::tuple_element_t<N, scenery_ref>& get() && { if constexpr(N == 0) return atlas; else return frame; }
-
- std::shared_ptr<anim_atlas>& atlas;
- scenery& frame;
+ scenery_type sc_type : 3 = scenery_type::none;
+ std::uint8_t active : 1 = false;
+ std::uint8_t closing : 1 = false;
+ std::uint8_t interactive : 1 = false;
- bool can_activate() const noexcept;
- bool activate();
- bool update(float dt);
- void rotate(rotation r);
+ bool can_activate(It it, struct chunk& c) const override;
+ bool activate(It it, struct chunk& c) override;
+ bool update(It it, struct chunk& c, float dt) override;
+ bool operator==(const entity_proto& p) const override;
private:
- struct chunk* c;
- std::uint8_t idx;
+ friend struct world;
+ scenery(std::uint64_t id, struct world& w, entity_type type, const scenery_proto& proto);
};
-} // namespace floormat
+template<> struct entity_type_<scenery> : std::integral_constant<entity_type, entity_type::scenery> {};
-template<> struct std::tuple_size<floormat::scenery_ref> final : std::integral_constant<std::size_t, 2> {};
-template<> struct std::tuple_element<0, floormat::scenery_ref> final { using type = std::shared_ptr<floormat::anim_atlas>; };
-template<> struct std::tuple_element<1, floormat::scenery_ref> final { using type = floormat::scenery; };
+} // namespace floormat
diff --git a/src/tile.cpp b/src/tile.cpp
index 25b413a9..75ef71e7 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -6,42 +6,36 @@ namespace floormat {
bool operator==(const tile_proto& a, const tile_proto& b) noexcept {
return a.ground() == b.ground() &&
a.wall_north() == b.wall_north() &&
- a.wall_west() == b.wall_west() &&
- a.scenery() == b.scenery();
+ a.wall_west() == b.wall_west();
};
tile_image_proto tile_proto::ground() const noexcept { return { ground_atlas, ground_variant }; }
tile_image_proto tile_proto::wall_north() const noexcept { return { wall_north_atlas, wall_north_variant }; }
tile_image_proto tile_proto::wall_west() const noexcept { return { wall_west_atlas, wall_west_variant }; }
-scenery_proto tile_proto::scenery() const noexcept { return { scenery_atlas, scenery_frame }; }
tile_ref::tile_ref(struct chunk& c, std::uint8_t i) noexcept : _chunk{&c}, i{i} {}
std::shared_ptr<tile_atlas> tile_ref::ground_atlas() noexcept { return _chunk->_ground_atlases[i]; }
std::shared_ptr<tile_atlas> tile_ref::wall_north_atlas() noexcept { return _chunk->_wall_atlases[i*2+0]; }
std::shared_ptr<tile_atlas> tile_ref::wall_west_atlas() noexcept { return _chunk->_wall_atlases[i*2+1]; }
-std::shared_ptr<anim_atlas> tile_ref::scenery_atlas() noexcept { return _chunk->_scenery_atlases[i]; }
std::shared_ptr<const tile_atlas> tile_ref::ground_atlas() const noexcept { return _chunk->_ground_atlases[i]; }
std::shared_ptr<const tile_atlas> tile_ref::wall_north_atlas() const noexcept { return _chunk->_wall_atlases[i*2+0]; }
std::shared_ptr<const tile_atlas> tile_ref::wall_west_atlas() const noexcept { return _chunk->_wall_atlases[i*2+1]; }
-std::shared_ptr<const anim_atlas> tile_ref::scenery_atlas() const noexcept { return _chunk->_scenery_atlases[i]; }
tile_image_ref tile_ref::ground() noexcept { return {_chunk->_ground_atlases[i], _chunk->_ground_variants[i] }; }
tile_image_ref tile_ref::wall_north() noexcept { return {_chunk->_wall_atlases[i*2+0], _chunk->_wall_variants[i*2+0] }; }
tile_image_ref tile_ref::wall_west() noexcept { return {_chunk->_wall_atlases[i*2+1], _chunk->_wall_variants[i*2+1] }; }
-scenery_ref tile_ref::scenery() noexcept { return { *_chunk, i }; }
tile_image_proto tile_ref::ground() const noexcept { return { _chunk->_ground_atlases[i], _chunk->_ground_variants[i] }; }
tile_image_proto tile_ref::wall_north() const noexcept { return { _chunk->_wall_atlases[i*2+0], _chunk->_wall_variants[i*2+0] }; }
tile_image_proto tile_ref::wall_west() const noexcept { return { _chunk->_wall_atlases[i*2+1], _chunk->_wall_variants[i*2+1] }; }
-scenery_proto tile_ref::scenery() const noexcept { return { _chunk->_scenery_atlases[i], _chunk->_scenery_variants[i] }; }
tile_ref::operator tile_proto() const noexcept
{
return {
- _chunk->_ground_atlases[i], _chunk->_wall_atlases[i*2+0], _chunk->_wall_atlases[i*2+1], _chunk->_scenery_atlases[i],
- _chunk->_ground_variants[i], _chunk->_wall_variants[i*2+0], _chunk->_wall_variants[i*2+1], _chunk->_scenery_variants[i],
+ _chunk->_ground_atlases[i], _chunk->_wall_atlases[i*2+0], _chunk->_wall_atlases[i*2+1],
+ _chunk->_ground_variants[i], _chunk->_wall_variants[i*2+0], _chunk->_wall_variants[i*2+1],
};
}
@@ -52,8 +46,7 @@ bool operator==(const tile_ref& a, const tile_ref& b) noexcept
else
return a.ground() == b.ground() &&
a.wall_north() == b.wall_north() &&
- a.wall_west() == b.wall_west() &&
- a.scenery() == b.scenery();
+ a.wall_west() == b.wall_west();
}
} // namespace floormat
diff --git a/src/tile.hpp b/src/tile.hpp
index 7a6604dd..5eed7633 100644
--- a/src/tile.hpp
+++ b/src/tile.hpp
@@ -1,6 +1,5 @@
#pragma once
#include "tile-image.hpp"
-#include "scenery.hpp"
namespace floormat {
@@ -10,14 +9,11 @@ struct anim_atlas;
struct tile_proto final
{
std::shared_ptr<tile_atlas> ground_atlas, wall_north_atlas, wall_west_atlas;
- std::shared_ptr<anim_atlas> scenery_atlas;
variant_t ground_variant = 0, wall_north_variant = 0, wall_west_variant = 0;
- struct scenery scenery_frame;
tile_image_proto ground() const noexcept;
tile_image_proto wall_north() const noexcept;
tile_image_proto wall_west() const noexcept;
- scenery_proto scenery() const noexcept;
friend bool operator==(const tile_proto& a, const tile_proto& b) noexcept;
};
@@ -29,22 +25,18 @@ struct tile_ref final
tile_image_ref ground() noexcept;
tile_image_ref wall_north() noexcept;
tile_image_ref wall_west() noexcept;
- scenery_ref scenery() noexcept;
tile_image_proto ground() const noexcept;
tile_image_proto wall_north() const noexcept;
tile_image_proto wall_west() const noexcept;
- scenery_proto scenery() const noexcept;
std::shared_ptr<tile_atlas> ground_atlas() noexcept;
std::shared_ptr<tile_atlas> wall_north_atlas() noexcept;
std::shared_ptr<tile_atlas> wall_west_atlas() noexcept;
- std::shared_ptr<anim_atlas> scenery_atlas() noexcept;
std::shared_ptr<const tile_atlas> ground_atlas() const noexcept;
std::shared_ptr<const tile_atlas> wall_north_atlas() const noexcept;
std::shared_ptr<const tile_atlas> wall_west_atlas() const noexcept;
- std::shared_ptr<const anim_atlas> scenery_atlas() const noexcept;
explicit operator tile_proto() const noexcept;
diff --git a/src/world.cpp b/src/world.cpp
index 1fcee1d8..ea767422 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -1,5 +1,6 @@
#include "world.hpp"
#include "chunk.hpp"
+#include "entity.hpp"
namespace floormat {
@@ -7,6 +8,14 @@ world::world() : world{initial_capacity}
{
}
+world::~world() noexcept
+{
+ _teardown = true;
+ _last_chunk = {};
+ _chunks.clear();
+ _entities.clear();
+}
+
world::world(std::size_t capacity) : _chunks{capacity, hasher}
{
_chunks.max_load_factor(max_load_factor);
@@ -69,4 +78,24 @@ void world::collect(bool force)
fm_debug("world: collected %zu/%zu chunks", len, len0);
}
+static constexpr std::uint64_t min_id = 1u << 16;
+std::uint64_t world::entity_counter = min_id;
+
+void world::do_make_entity(const std::shared_ptr<entity>& e, global_coords pos)
+{
+ fm_debug_assert(e->id > min_id && &e->w == this);
+ fm_assert(Vector2ui(e->bbox_size).product() > 0);
+ fm_assert(e->type != entity_type::none);
+ e->coord = pos;
+ _entities[e->id] = e;
+ operator[](pos.chunk()).add_entity(e);
+}
+
+void world::do_kill_entity(std::uint64_t id)
+{
+ fm_debug_assert(id > min_id);
+ auto cnt = _entities.erase(id);
+ fm_debug_assert(cnt > 0);
+}
+
} // namespace floormat
diff --git a/src/world.hpp b/src/world.hpp
index 36703fd3..6781ee3c 100644
--- a/src/world.hpp
+++ b/src/world.hpp
@@ -9,6 +9,9 @@
namespace floormat {
+struct entity;
+template<typename T> struct entity_type_;
+
struct world final
{
private:
@@ -24,13 +27,24 @@ private:
return int_hash((std::size_t)c.y << 16 | (std::size_t)c.x);
};
std::unordered_map<chunk_coords, chunk, decltype(hasher)> _chunks;
+ std::unordered_map<std::uint64_t, std::weak_ptr<entity>> _entities;
std::size_t _last_collection = 0;
std::size_t _collect_every = 64;
+ bool _teardown : 1 = false;
+
+ static std::uint64_t entity_counter;
+
explicit world(std::size_t capacity);
+ void do_make_entity(const std::shared_ptr<entity>& e, global_coords pos);
+ void do_kill_entity(std::uint64_t id);
+
+ friend struct entity;
+
public:
explicit world();
+ ~world() noexcept;
struct pair final { chunk& c; tile_ref t; }; // NOLINT
@@ -52,6 +66,18 @@ public:
void set_collect_threshold(std::size_t value) { _collect_every = value; }
std::size_t collect_threshold() const noexcept { return _collect_every; }
+ template<typename T, typename... Xs>
+ requires requires { T{std::uint64_t(), std::declval<world&>(), entity_type(), std::declval<Xs>()...}; }
+ std::shared_ptr<T> make_entity(global_coords pos, Xs&&... xs)
+ {
+ static_assert(std::is_base_of_v<entity, T>);
+ auto ret = std::shared_ptr<T>(new T{++entity_counter, *this, entity_type_<T>::value, std::forward<Xs>(xs)...});
+ do_make_entity(std::static_pointer_cast<entity>(ret), pos);
+ return ret;
+ }
+
+ bool is_teardown() const { return _teardown; }
+
fm_DECLARE_DEPRECATED_COPY_ASSIGNMENT(world);
fm_DECLARE_DEFAULT_MOVE_ASSIGNMENT_(world);
};