summaryrefslogtreecommitdiffhomepage
path: root/editor/editor.cpp
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-10-23 17:31:31 +0200
committerStanislaw Halik <sthalik@misaki.pl>2022-10-23 17:31:31 +0200
commitcce1f768e7399b838a2b865511915bdd576dbbf4 (patch)
tree4c6a8f2dc9112394fd329d56c0f628ce66b16467 /editor/editor.cpp
parent6b875a0919b9932eca9ed877552c34ecb220b7d8 (diff)
a
Diffstat (limited to 'editor/editor.cpp')
-rw-r--r--editor/editor.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/editor/editor.cpp b/editor/editor.cpp
new file mode 100644
index 00000000..f3c8b157
--- /dev/null
+++ b/editor/editor.cpp
@@ -0,0 +1,234 @@
+#include "editor.hpp"
+#include "serialize/json-helper.hpp"
+#include "serialize/tile-atlas.hpp"
+#include "src/loader.hpp"
+#include "random.hpp"
+#include "compat/assert.hpp"
+#include "compat/unreachable.hpp"
+#include "src/tile-defs.hpp"
+#include "src/world.hpp"
+#include <Corrade/Containers/StringStlView.h>
+#include <filesystem>
+#include <vector>
+
+namespace floormat {
+
+static const std::filesystem::path image_path{IMAGE_PATH, std::filesystem::path::generic_format};
+
+tile_type::tile_type(editor_mode mode, Containers::StringView name) : _name{name}, _mode{mode}
+{
+ load_atlases();
+}
+
+void tile_type::load_atlases()
+{
+ using atlas_array = std::vector<std::shared_ptr<tile_atlas>>;
+ for (auto& atlas : json_helper::from_json<atlas_array>(image_path/(_name + ".json")))
+ {
+ Containers::StringView name = atlas->name();
+ if (auto x = name.findLast('.'); x)
+ name = name.prefix(x.data());
+ auto& [_, vec] = _permutation;
+ vec.reserve((std::size_t)atlas->num_tiles());
+ _atlases[name] = std::move(atlas);
+ }
+}
+
+std::shared_ptr<tile_atlas> tile_type::maybe_atlas(Containers::StringView str)
+{
+ auto it = std::find_if(_atlases.begin(), _atlases.end(), [&](const auto& tuple) -> bool {
+ const auto& [x, _] = tuple;
+ return Containers::StringView{x} == str;
+ });
+ if (it == _atlases.end())
+ return nullptr;
+ else
+ return it->second;
+}
+
+std::shared_ptr<tile_atlas> tile_type::atlas(Containers::StringView str)
+{
+ if (auto ptr = maybe_atlas(str); ptr)
+ return ptr;
+ else
+ fm_abort("no such atlas: %s", str.cbegin());
+}
+
+void tile_type::clear_selection()
+{
+ _selected_tile = {};
+ _permutation = {};
+ _selection_mode = sel_none;
+}
+
+void tile_type::select_tile(const std::shared_ptr<tile_atlas>& atlas, std::size_t variant)
+{
+ fm_assert(atlas);
+ clear_selection();
+ _selection_mode = sel_tile;
+ _selected_tile = { atlas, variant % atlas->num_tiles() };
+}
+
+void tile_type::select_tile_permutation(const std::shared_ptr<tile_atlas>& atlas)
+{
+ fm_assert(atlas);
+ clear_selection();
+ _selection_mode = sel_perm;
+ _permutation = { atlas, {} };
+}
+
+bool tile_type::is_tile_selected(const std::shared_ptr<const tile_atlas>& atlas, std::size_t variant) const
+{
+ return atlas && _selection_mode == sel_tile && _selected_tile &&
+ atlas == _selected_tile.atlas && variant == _selected_tile.variant;
+}
+
+bool tile_type::is_permutation_selected(const std::shared_ptr<const tile_atlas>& atlas) const
+{
+ const auto& [perm, _] = _permutation;
+ return atlas && _selection_mode == sel_perm && perm == atlas;
+}
+
+bool tile_type::is_atlas_selected(const std::shared_ptr<const tile_atlas>& atlas) const
+{
+ switch (_selection_mode)
+ {
+ default:
+ case sel_none:
+ return false;
+ case sel_perm:
+ return is_permutation_selected(atlas);
+ case sel_tile:
+ return atlas && _selected_tile && atlas == _selected_tile.atlas;
+ }
+}
+
+template<std::random_access_iterator T>
+void fisher_yates(T begin, T end)
+{
+ const auto N = std::distance(begin, end);
+ for (auto i = N-1; i >= 1; i--)
+ {
+ const auto j = random(i+1);
+ using std::swap;
+ swap(begin[i], begin[j]);
+ }
+}
+
+tile_image tile_type::get_selected_perm()
+{
+ auto& [atlas, vec] = _permutation;
+ const std::size_t N = atlas->num_tiles();
+ if (N == 0)
+ return {};
+ if (vec.empty())
+ {
+ for (std::size_t i = 0; i < N; i++)
+ vec.push_back(i);
+ fisher_yates(vec.begin(), vec.end());
+ }
+ const auto idx = vec.back();
+ vec.pop_back();
+ return {atlas, idx};
+}
+
+tile_image tile_type::get_selected()
+{
+ switch (_selection_mode)
+ {
+ case sel_none:
+ return {};
+ case sel_tile:
+ return _selected_tile;
+ case sel_perm:
+ return get_selected_perm();
+ default:
+ fm_warn_once("invalid editor mode '%u'", (unsigned)_selection_mode);
+ break;
+ }
+}
+
+void tile_type::place_tile(world& world, global_coords pos, tile_image& img)
+{
+ const auto& [c, t] = world[pos];
+ const auto& [atlas, variant] = img;
+ switch (_mode)
+ {
+ default:
+ fm_warn_once("invalid editor mode '%u'", (unsigned)_mode);
+ break;
+ case editor_mode::select:
+ break;
+ case editor_mode::floor: {
+ const auto& [c, t] = world[pos];
+ t.ground_image = { atlas, variant };
+ break;
+ }
+ case editor_mode::walls: {
+ break; // todo
+ }
+ }
+}
+
+editor::editor()
+{
+ set_mode(editor_mode::floor); // TODO
+}
+
+void editor::set_mode(editor_mode mode)
+{
+ _mode = mode;
+ on_release();
+}
+
+const tile_type* editor::current() const
+{
+ switch (_mode)
+ {
+ case editor_mode::select:
+ return nullptr;
+ case editor_mode::floor:
+ return &_floor;
+ case editor_mode::walls:
+ return nullptr; // todo
+ default:
+ fm_warn_once("invalid editor mode '%u'", (unsigned)_mode);
+ return nullptr;
+ }
+}
+
+tile_type* editor::current()
+{
+ return const_cast<tile_type*>(static_cast<const editor&>(*this).current());
+}
+
+void editor::on_release()
+{
+ _last_pos = std::nullopt;
+}
+
+void editor::on_mouse_move(world& world, const global_coords pos)
+{
+ if (_last_pos && *_last_pos != pos)
+ {
+ _last_pos = pos;
+ on_click(world, pos);
+ }
+}
+
+void editor::on_click(world& world, global_coords pos)
+{
+ if (auto* mode = current(); mode)
+ {
+ auto opt = mode->get_selected();
+ if (opt)
+ {
+ _last_pos = pos;
+ mode->place_tile(world, pos, opt);
+ }
+ else
+ on_release();
+ }
+}
+
+} // namespace floormat