summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2023-03-19 16:18:13 +0100
committerStanislaw Halik <sthalik@misaki.pl>2023-03-19 16:18:13 +0100
commit86bd6a4411badecfc76fe3a9d29b4aa30c8fdba7 (patch)
tree468c23d67bab654f98126c1fa7ab58e9043b3f6a
parentf1da751349fb52a8a88b10bc3289288a4fcd2396 (diff)
work on entity reodering
-rw-r--r--editor/imgui-inspect.cpp8
-rw-r--r--editor/imgui.cpp8
-rw-r--r--editor/update.cpp27
-rw-r--r--scenery/control-panel.json20
-rw-r--r--scenery/control-panel.pngbin1370 -> 1563 bytes
-rw-r--r--scenery/scenery.json5
-rw-r--r--src/character.cpp34
-rw-r--r--src/character.hpp3
-rw-r--r--src/entity.cpp90
-rw-r--r--src/entity.hpp13
-rw-r--r--src/scenery.cpp38
-rw-r--r--src/scenery.hpp3
-rw-r--r--src/world.cpp1
-rw-r--r--src/world.hpp3
-rw-r--r--test/serializer.cpp1
15 files changed, 183 insertions, 71 deletions
diff --git a/editor/imgui-inspect.cpp b/editor/imgui-inspect.cpp
index 7ef24913..d95bc441 100644
--- a/editor/imgui-inspect.cpp
+++ b/editor/imgui-inspect.cpp
@@ -21,6 +21,7 @@ void app::draw_inspector()
{
auto end = inspectors.begin() + (ptrdiff_t)size - max_inspectors;
inspectors.erase(inspectors.begin(), end);
+ fm_assert(inspectors.size() <= max_inspectors);
}
const auto dpi = M->dpi_scale();
@@ -46,7 +47,12 @@ void app::draw_inspector()
{
auto& s2 = static_cast<scenery&>(s);
if (auto b2 = begin_window(buf, &is_open))
- entities::inspect_type(s2);
+ {
+ auto idx = s.index();
+ bool ret = entities::inspect_type(s2);
+ if (ret)
+ e->reposition(idx);
+ }
}
else
is_open = false;
diff --git a/editor/imgui.cpp b/editor/imgui.cpp
index 9700a8f7..696460a7 100644
--- a/editor/imgui.cpp
+++ b/editor/imgui.cpp
@@ -164,13 +164,17 @@ void app::do_popup_menu()
void app::kill_popups(bool hard)
{
+ const bool imgui = _imgui.context() != nullptr;
+
_popup_target = {};
- ImGui::CloseCurrentPopup();
+ if (imgui)
+ ImGui::CloseCurrentPopup();
if (hard)
inspectors.clear();
- ImGui::FocusWindow(nullptr);
+ if (imgui)
+ ImGui::FocusWindow(nullptr);
}
} // namespace floormat
diff --git a/editor/update.cpp b/editor/update.cpp
index 2393c6ce..15ffe89e 100644
--- a/editor/update.cpp
+++ b/editor/update.cpp
@@ -6,6 +6,7 @@
#include "floormat/events.hpp"
#include "floormat/main.hpp"
#include "character.hpp"
+#include <cmath>
namespace floormat {
@@ -107,8 +108,10 @@ void app::do_rotate(bool backward)
else if (auto* cl = find_clickable_scenery(*cursor.pixel))
{
auto& e = *cl->e;
+ auto i = e.index();
auto r = backward ? e.atlas->prev_rotation_from(e.r) : e.atlas->next_rotation_from(e.r);
- e.rotate(e.index(), r);
+ e.rotate(i, r);
+ e.reposition(i);
}
}
}
@@ -176,6 +179,7 @@ void app::apply_commands(const key_set& keys)
void app::update_world(float dt)
{
auto& world = M->world();
+ const auto curframe = world.increment_frame_no();
auto [minx, maxx, miny, maxy] = M->get_draw_bounds();
minx--; miny--; maxx++; maxy++;
for (int16_t y = miny; y <= maxy; y++)
@@ -184,10 +188,27 @@ void app::update_world(float dt)
auto& c = world[chunk_coords{x, y}];
const auto& es = c.entities();
const auto size = es.size();
- for (auto i = size-1; i != (size_t)-1; i--)
+
+start: for (auto i = size-1; i != (size_t)-1; i--)
{
auto& e = *es[i];
- e.update(i, dt);
+ fm_debug_assert(!(e.last_update > curframe));
+ if (curframe > e.last_update) [[likely]]
+ {
+ auto off = e.ordinal_offset({});
+ e.last_update = curframe;
+ auto status = e.update(i, dt);
+ if (status == entity_update_status::updated_repositioning)
+ {
+ //Debug{} << "reposition after update" << e.ordinal_offset({}) << off;
+ e.reposition(i);
+ }
+ if (status >= entity_update_status::updated_repositioned)
+ {
+ //Debug{} << "goto start";
+ goto start;
+ }
+ }
}
}
}
diff --git a/scenery/control-panel.json b/scenery/control-panel.json
index 78045d2d..2ef2e2f0 100644
--- a/scenery/control-panel.json
+++ b/scenery/control-panel.json
@@ -3,22 +3,28 @@
{
"frames": [
{
- "ground": "11 x 46",
- "size": "21 x 54"
+ "ground": "11 x 45",
+ "size": "21 x 52"
}
],
"ground": "11 x 46",
"name": "n",
- "offset": [ 0, -32, 22 ]
+ "offset": [
+ 0,
+ 0,
+ 22
+ ]
},
{
"mirror-from": "n",
- "name": "w",
- "offset": [ -32, 0, 22 ]
+ "name": "w"
}
],
"nframes": 1,
"object_name": "control-panel",
- "pixel_size": "21 x 54",
- "scale": [ "height", 54 ]
+ "pixel_size": "21 x 52",
+ "scale": [
+ "factor",
+ 1.0
+ ]
}
diff --git a/scenery/control-panel.png b/scenery/control-panel.png
index 8a422400..c1f0f5f1 100644
--- a/scenery/control-panel.png
+++ b/scenery/control-panel.png
Binary files differ
diff --git a/scenery/scenery.json b/scenery/scenery.json
index 0f2826b7..4d563b07 100644
--- a/scenery/scenery.json
+++ b/scenery/scenery.json
@@ -4,13 +4,14 @@
"type": "door",
"atlas-name": "door-close",
"bbox-offset": "0 x -32",
- "bbox-size": "32 x 16"
+ "bbox-size": "64 x 16"
},
{
"name": "control panel (wall) 1",
"type": "generic",
"atlas-name": "control-panel",
- "pass-mode": "pass"
+ "pass-mode": "pass",
+ "offset": "0 x -32"
},
{
"name": "bench1",
diff --git a/src/character.cpp b/src/character.cpp
index 110d2031..10ef4a34 100644
--- a/src/character.cpp
+++ b/src/character.cpp
@@ -96,26 +96,36 @@ void character::set_keys(bool L, bool R, bool U, bool D)
b_D = D;
}
-bool character::update(size_t i, float dt)
+Vector2 character::ordinal_offset(Vector2b offset) const
+{
+ return Vector2(offset);
+}
+
+entity_update_status character::update(size_t i, float dt)
{
auto [lr, ud, new_r] = arrows_to_dir(b_L, b_R, b_U, b_D);
if (!lr & !ud)
{
delta = 0;
- return false;
+ return entity_update_status::not_updated;
}
int nframes = allocate_frame_time(dt);
- rotate(i, new_r);
-
- if (!nframes)
- return false;
+ if (nframes == 0)
+ {
+ if (r == new_r) [[unlikely]]
+ return entity_update_status::not_updated;
+ rotate(i, new_r);
+ return entity_update_status::updated;
+ }
const auto vec = move_vec(lr, ud);
c->ensure_passability();
+ entity_update_status ret = entity_update_status::updated;
+
for (int k = 0; k < nframes; k++)
{
constexpr auto frac = Vector2(32767);
@@ -123,12 +133,22 @@ bool character::update(size_t i, float dt)
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_);
+ const auto old_i = i;
if (can_move_to(off_i))
+ {
i = move_to(i, off_i, new_r);
+ if (i != old_i)
+ {
+ //Debug{} << "move_to repositioned" << i;
+ ret = entity_update_status::updated_repositioned;
+ }
+ }
+ else
+ break;
++frame %= atlas->info().nframes;
}
//Debug{} << "pos" << Vector2i(pos.local());
- return true;
+ return ret;
}
entity_type character::type() const noexcept { return entity_type::character; }
diff --git a/src/character.hpp b/src/character.hpp
index 24e0e173..17915df7 100644
--- a/src/character.hpp
+++ b/src/character.hpp
@@ -26,8 +26,9 @@ struct character final : entity
entity_type type() const noexcept override;
explicit operator character_proto() const;
+ entity_update_status update(size_t i, float dt) override;
void set_keys(bool L, bool R, bool U, bool D);
- bool update(size_t i, float dt) override;
+ Vector2 ordinal_offset(Vector2b offset) const override;
String name;
Vector2s offset_frac;
diff --git a/src/entity.cpp b/src/entity.cpp
index 8822326e..766b0093 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -4,6 +4,7 @@
#include "anim-atlas.hpp"
#include "RTree.hpp"
#include "compat/exception.hpp"
+#include <cmath>
#include <algorithm>
namespace floormat {
@@ -39,36 +40,17 @@ entity::~entity() noexcept
const_cast<object_id&>(id) = 0;
}
-Vector2b entity::ordinal_offset_for_type(entity_type type, Vector2b offset)
-{
- switch (type)
- {
- default:
- fm_warn_once("unknown entity type '%zu'", size_t(type));
- [[fallthrough]];
- case entity_type::scenery:
- return offset;
- case entity_type::character:
- return {};
- }
-}
-
-float entity_proto::ordinal(local_coords local) const
-{
- return entity::ordinal(local, offset, type);
-}
-
float entity::ordinal() const
{
- return ordinal(coord.local(), offset, type());
+ return ordinal(coord.local(), offset);
}
-float entity::ordinal(local_coords xy, Vector2b offset, entity_type type)
+float entity::ordinal(local_coords xy, Vector2b offset) const
{
constexpr auto inv_tile_size = 1.f/TILE_SIZE2;
constexpr float width = TILE_MAX_DIM+1;
- offset = ordinal_offset_for_type(type, offset);
- auto vec = Vector2(xy) + Vector2(offset)*inv_tile_size;
+ auto offset_ = ordinal_offset(offset);
+ auto vec = Vector2(xy) + offset_*inv_tile_size;
return vec[1]*width + vec[0];
}
@@ -82,8 +64,15 @@ size_t entity::index() const
auto& c = chunk();
auto& es = c._entities;
auto it = std::lower_bound(es.cbegin(), es.cend(), nullptr, [ord = ordinal()](const auto& a, const auto&) { return a->ordinal() < ord; });
- fm_assert(it != es.cend());
+ if (it == es.cend())
+ goto slow;
it = std::find_if(it, es.cend(), [id = id](const auto& x) { return x->id == id; });
+ if (it == es.cend())
+ goto slow;
+ return (size_t)std::distance(es.cbegin(), it);
+slow:
+ fm_warn_once("BUG: wrong entity sort order");
+ it = std::find_if(es.cbegin(), es.cend(), [id = id](const auto& x) { return x->id == id; });
fm_assert(it != es.cend());
return (size_t)std::distance(es.cbegin(), it);
}
@@ -181,26 +170,17 @@ size_t entity::move_to(size_t i, Vector2i delta, rotation new_r)
const auto bb_size = rotate_size(bbox_size, r, new_r);
bool b0 = c->_bbox_for_scenery(*this, bb0),
b1 = c->_bbox_for_scenery(*this, coord_.local(), offset_, bb_offset, bb_size, bb1);
- const auto ord = ordinal(coord_.local(), offset_, type());
+ const auto ord = ordinal(coord_.local(), offset_);
if (coord_.chunk() == coord.chunk())
{
c->_replace_bbox(bb0, bb1, b0, b1);
- auto it_ = std::lower_bound(es.cbegin(), es.cend(), e_, [=](const auto& a, const auto&) { return a->ordinal() < ord; });
const_cast<global_coords&>(coord) = coord_;
set_bbox_(offset_, bb_offset, bb_size, pass);
const_cast<rotation&>(r) = new_r;
- auto pos1 = std::distance(es.cbegin(), it_);
- if ((size_t)pos1 > i)
- pos1--;
//for (auto i = 0_uz; const auto& x : es) fm_debug("%zu %s %f", i++, x->atlas->name().data(), x->ordinal());
- if ((size_t)pos1 != i)
- {
- //fm_debug("insert (%hd;%hd|%hhd;%hhd) %td -> %zu | %f", coord_.chunk().x, coord_.chunk().y, coord_.local().x, coord_.local().y, pos1, es.size(), e.ordinal());
- es.erase(es.cbegin() + (ptrdiff_t)i);
- es.insert(es.cbegin() + pos1, std::move(e_));
- }
- return size_t(pos1);
+ //fm_debug("insert (%hd;%hd|%hhd;%hhd) %td -> %zu | %f", coord_.chunk().x, coord_.chunk().y, coord_.local().x, coord_.local().y, pos1, es.size(), e.ordinal());
+ return reposition(i);
}
else
{
@@ -265,6 +245,44 @@ bool entity::is_dynamic() const
return atlas->info().fps > 0;
}
+size_t entity::reposition(size_t i)
+{
+ const auto ord = ordinal();
+ const auto fn = [=](const auto& a, const auto&) { return a->ordinal() < ord; };
+ auto& es = c->_entities;
+ fm_assert(i < es.size());
+ auto e_ = es[i];
+ fm_assert(&*e_ == this);
+ const auto old_it = es.cbegin() + ptrdiff_t(i);
+ if (auto it = std::lower_bound(es.cbegin(), old_it, e_, fn);
+ it != old_it)
+ {
+ auto pos = std::distance(es.cbegin(), it);
+ //Debug{} << "repos: backward to" << std::distance(es.cbegin(), it) << "from" << i;
+ es.erase(old_it);
+ es.insert(es.cbegin() + pos, e_);
+ i = (size_t)pos;
+ goto done;
+ }
+ if (auto it = std::lower_bound(old_it, es.cend(), e_, fn);
+ it != old_it)
+ {
+ --it;
+ auto pos = std::distance(es.cbegin(), it);
+ //Debug{} << "repos: forward to" << std::distance(es.cbegin(), it) << "from" << i;
+ es.erase(old_it);
+ es.insert(es.cbegin() + pos, e_);
+ goto done;
+ }
+
+ //Debug{} << "repos: no need to move" << i;
+ return i;
+done:
+ if (!is_dynamic())
+ c->mark_scenery_modified();
+ return (size_t)i;
+}
+
entity_type entity::type_of() const noexcept
{
return type();
diff --git a/src/entity.hpp b/src/entity.hpp
index d759cbd1..ade975e4 100644
--- a/src/entity.hpp
+++ b/src/entity.hpp
@@ -25,7 +25,6 @@ struct entity_proto
rotation r : rotation_BITS = rotation::N;
pass_mode pass : pass_mode_BITS = pass_mode::see_through;
- float ordinal(local_coords coord) const;
entity_proto& operator=(const entity_proto&);
entity_proto();
entity_proto(const entity_proto&);
@@ -36,11 +35,16 @@ struct entity_proto
entity_type type_of() const noexcept;
};
+enum class entity_update_status : unsigned char {
+ not_updated, updated, updated_repositioned, updated_repositioning,
+};
+
struct entity
{
fm_DECLARE_DELETED_COPY_ASSIGNMENT(entity);
const object_id id = 0;
+ uint64_t last_update = 0;
struct chunk* const c;
std::shared_ptr<anim_atlas> atlas;
const global_coords coord;
@@ -52,9 +56,9 @@ struct entity
virtual ~entity() noexcept;
- static Vector2b ordinal_offset_for_type(entity_type type, Vector2b offset);
+ virtual Vector2 ordinal_offset(Vector2b offset) const = 0;
float ordinal() const;
- static float ordinal(local_coords xy, Vector2b offset, entity_type type);
+ float ordinal(local_coords xy, Vector2b offset) const;
struct chunk& chunk() const;
size_t index() const;
@@ -63,7 +67,7 @@ struct entity
virtual entity_type type() const noexcept = 0;
virtual bool can_activate(size_t i) const;
virtual bool activate(size_t i);
- virtual bool update(size_t i, float dt) = 0;
+ virtual entity_update_status update(size_t i, float dt) = 0;
virtual void rotate(size_t i, rotation r);
virtual bool can_rotate(global_coords coord, rotation new_r, rotation old_r, Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size);
virtual bool can_move_to(Vector2i delta, global_coords coord, Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_aize);
@@ -73,6 +77,7 @@ struct entity
static Pair<global_coords, Vector2b> normalize_coords(global_coords coord, Vector2b cur_offset, Vector2i delta);
bool is_dynamic() const;
+ size_t reposition(size_t i);
bool can_rotate(rotation new_r);
bool can_move_to(Vector2i delta);
size_t move_to(size_t i, Vector2i delta, rotation new_r);
diff --git a/src/scenery.cpp b/src/scenery.cpp
index bfa69142..fe1618af 100644
--- a/src/scenery.cpp
+++ b/src/scenery.cpp
@@ -3,6 +3,7 @@
#include "chunk.hpp"
#include "compat/assert.hpp"
#include "world.hpp"
+#include "src/rotation.inl"
#include <algorithm>
namespace floormat {
@@ -22,19 +23,19 @@ bool scenery::can_activate(size_t) const
return atlas && interactive;
}
-bool scenery::update(size_t, float dt)
+entity_update_status scenery::update(size_t, float dt)
{
auto& s = *this;
if (!s.active)
- return false;
+ return entity_update_status::not_updated;
switch (s.sc_type)
{
default:
case scenery_type::none:
case scenery_type::generic:
- return false;
- case scenery_type::door:
+ return entity_update_status::not_updated;
+ case scenery_type::door: {
fm_assert(atlas);
auto& anim = *atlas;
const auto hz = uint8_t(atlas->info().fps);
@@ -47,10 +48,12 @@ bool scenery::update(size_t, float dt)
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 entity_update_status::not_updated;
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;
+ pass_mode old_pass = pass, p;
if (fr <= 0)
p = pass_mode::pass;
else if (fr >= nframes-1)
@@ -58,11 +61,32 @@ bool scenery::update(size_t, float dt)
else
p = pass_mode::see_through;
set_bbox(offset, bbox_offset, bbox_size, p);
- s.frame = (uint16_t)std::clamp(fr, 0, nframes-1);
+ 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;
- return true;
+ //if ((p == pass_mode::pass) != (old_pass == pass_mode::pass)) Debug{} << "update: need reposition" << (s.frame == 0 ? "-1" : "1");
+ return (p == pass_mode::pass) != (old_pass == pass_mode::pass)
+ ? entity_update_status::updated_repositioning
+ : entity_update_status::updated;
+ }
+ }
+}
+
+Vector2 scenery::ordinal_offset(Vector2b offset) const
+{
+ if (sc_type == scenery_type::door)
+ {
+ constexpr auto vec0 = Vector2b(-iTILE_SIZE2[0], -iTILE_SIZE[1]/2+1);
+ constexpr auto vec1 = Vector2b(0, -iTILE_SIZE[1]/2+1);
+ const auto vec0_ = rotate_point(vec0, rotation::N, r);
+ const auto vec1_ = rotate_point(vec1, rotation::N, r);
+ const auto vec = frame == 0 ? vec0_ : vec1_;
+ return Vector2(offset) + Vector2(vec);
}
+ else
+ return Vector2(offset);
}
bool scenery::activate(size_t)
diff --git a/src/scenery.hpp b/src/scenery.hpp
index b52ddded..b21a416e 100644
--- a/src/scenery.hpp
+++ b/src/scenery.hpp
@@ -41,7 +41,8 @@ struct scenery final : entity
unsigned char closing : 1 = false;
unsigned char interactive : 1 = false;
- bool update(size_t i, float dt) override;
+ entity_update_status update(size_t i, float dt) override;
+ Vector2 ordinal_offset(Vector2b offset) const override;
bool can_activate(size_t i) const override;
bool activate(size_t i) override;
diff --git a/src/world.cpp b/src/world.cpp
index 8c564007..9d669e62 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -21,6 +21,7 @@ world& world::operator=(world&& w) noexcept
_chunks = std::move(w._chunks);
_entities = std::move(w._entities);
_entity_counter = w._entity_counter;
+ _current_frame = w._current_frame;
w._entity_counter = 0;
for (auto& [id, c] : _chunks)
diff --git a/src/world.hpp b/src/world.hpp
index d269acf6..b057b854 100644
--- a/src/world.hpp
+++ b/src/world.hpp
@@ -33,6 +33,7 @@ private:
size_t _collect_every = 64;
std::shared_ptr<char> _unique_id = std::make_shared<char>('A');
object_id _entity_counter = 0;
+ uint64_t _current_frame = 1; // zero is special for struct entity
bool _teardown : 1 = false;
explicit world(size_t capacity);
@@ -66,6 +67,8 @@ public:
static world deserialize(StringView filename);
void set_collect_threshold(size_t value) { _collect_every = value; }
size_t collect_threshold() const noexcept { return _collect_every; }
+ auto frame_no() const { return _current_frame; }
+ auto increment_frame_no() { return _current_frame++; }
template<typename T, bool sorted = true, typename... Xs>
requires requires(chunk& c) {
diff --git a/test/serializer.cpp b/test/serializer.cpp
index 236003cf..090034db 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -39,6 +39,7 @@ chunk& make_test_chunk(world& w, chunk_coords ch)
auto i = e.index();
e.activate(i);
e.update(i, 1.f/60);
+ e.reposition(i);
fm_assert(e.active);
fm_assert(e.frame != 0 && e.frame != e.atlas->info().nframes - 1);
}