1#ifndef YAZE_APP_EDITOR_OVERWORLD_UNDO_ACTIONS_H_
2#define YAZE_APP_EDITOR_OVERWORLD_UNDO_ACTIONS_H_
11#include <unordered_map>
15#include "absl/status/status.h"
16#include "absl/strings/str_format.h"
64 std::function<
void()> refresh_fn,
65 std::function<
void(
int)> refresh_map_fn = {},
66 bool allow_merge =
true)
74 timestamp_(std::chrono::steady_clock::now()) {}
76 absl::Status
Undo()
override {
78 return absl::InternalError(
"Overworld pointer is null");
82 world_tiles[change.x][change.y] = change.old_tile_id;
85 return absl::OkStatus();
88 absl::Status
Redo()
override {
90 return absl::InternalError(
"Overworld pointer is null");
94 world_tiles[change.x][change.y] = change.new_tile_id;
97 return absl::OkStatus();
101 return absl::StrFormat(
"Paint %d tile%s on map %d",
tile_changes_.size(),
112 const auto* prev_paint =
116 if (!prev_paint->allow_merge_)
120 if (prev_paint->world_ !=
world_)
123 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
134 std::unordered_map<uint64_t, size_t> coord_index;
137 coord_index[key] = i;
140 for (
const auto& prev_change : prev_paint.tile_changes_) {
142 auto it = coord_index.find(key);
143 if (it != coord_index.end()) {
145 tile_changes_[it->second].old_tile_id = prev_change.old_tile_id;
166 return (
static_cast<uint64_t
>(
static_cast<uint32_t
>(change.
x)) << 32) |
167 static_cast<uint32_t
>(change.
y);
171 std::vector<OverworldTileChange> changes) {
172 std::unordered_map<uint64_t, size_t> coordinate_indices;
173 std::vector<OverworldTileChange> normalized;
174 normalized.reserve(changes.size());
175 for (
const auto& change : changes) {
176 const auto [it, inserted] =
177 coordinate_indices.emplace(
CoordinateKey(change), normalized.size());
179 normalized.push_back(change);
183 normalized[it->second].new_tile_id = change.new_tile_id;
196 std::array<bool, zelda3::kNumOverworldMaps> changed_maps{};
198 const int map_id =
world_ * 0x40 + change.x / 32 + (change.y / 32) * 8;
200 changed_maps[
map_id] =
true;
224 std::vector<zelda3::OverworldItem>
items;
240 std::string description)
248 return absl::InternalError(
249 "OverworldItemsEditAction: no restore callback");
252 return absl::OkStatus();
257 return absl::InternalError(
258 "OverworldItemsEditAction: no restore callback");
261 return absl::OkStatus();
267 const size_t before_size =
269 const size_t after_size =
271 return sizeof(*this) + before_size + after_size;
289 std::string description)
297 return absl::InternalError(
298 "OverworldMapPropertyEditAction: no apply callback");
305 return absl::InternalError(
306 "OverworldMapPropertyEditAction: no apply callback");
332 std::vector<OverworldPropertyEdit> after,
333 ApplyFn apply, std::string description)
341 return absl::InternalError(
342 "OverworldMapPropertyBatchEditAction: no apply callback");
347 return absl::OkStatus();
352 return absl::InternalError(
353 "OverworldMapPropertyBatchEditAction: no apply callback");
355 for (
const auto& edit :
after_) {
358 return absl::OkStatus();
365 for (
const auto& edit :
before_) {
366 total +=
sizeof(edit) + edit.description.size();
368 for (
const auto& edit :
after_) {
369 total +=
sizeof(edit) + edit.description.size();
378 std::vector<OverworldPropertyEdit>
after_;
385 using ApplyFn = std::function<absl::Status(
const std::string&)>;
388 ApplyFn apply, std::string description)
396 return absl::InternalError(
397 "OverworldProjectLabelEditAction: no apply callback");
404 return absl::InternalError(
405 "OverworldProjectLabelEditAction: no apply callback");
Undoable action for overworld item mutations.
bool CanMergeWith(const UndoAction &) const override
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
OverworldItemsEditAction(OverworldItemsSnapshot before, OverworldItemsSnapshot after, RestoreFn restore, std::string description)
absl::Status Redo() override
absl::Status Undo() override
std::function< void(const OverworldItemsSnapshot &)> RestoreFn
OverworldItemsSnapshot before_
OverworldItemsSnapshot after_
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
std::vector< OverworldPropertyEdit > after_
std::vector< OverworldPropertyEdit > before_
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
absl::Status Undo() override
std::function< absl::Status(const OverworldPropertyEdit &)> ApplyFn
absl::Status Redo() override
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
OverworldMapPropertyBatchEditAction(std::vector< OverworldPropertyEdit > before, std::vector< OverworldPropertyEdit > after, ApplyFn apply, std::string description)
bool CanMergeWith(const UndoAction &) const override
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
bool CanMergeWith(const UndoAction &) const override
OverworldPropertyEdit before_
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
OverworldPropertyEdit after_
absl::Status Redo() override
OverworldMapPropertyEditAction(OverworldPropertyEdit before, OverworldPropertyEdit after, ApplyFn apply, std::string description)
absl::Status Undo() override
std::function< absl::Status(const OverworldPropertyEdit &)> ApplyFn
OverworldProjectLabelEditAction(std::string before, std::string after, ApplyFn apply, std::string description)
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
std::function< absl::Status(const std::string &)> ApplyFn
absl::Status Redo() override
bool CanMergeWith(const UndoAction &) const override
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
absl::Status Undo() override
Undoable action for painting tiles on the overworld map.
std::function< void(int)> refresh_map_fn_
std::function< void()> refresh_fn_
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
void MergeWith(UndoAction &prev) override
bool CanMergeWith(const UndoAction &prev) const override
static constexpr int kMergeWindowMs
Merge window: consecutive paints within this duration become one step.
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
std::vector< OverworldTileChange > tile_changes_
const std::vector< OverworldTileChange > & tile_changes() const
absl::Status Redo() override
void RefreshChangedMaps() const
static uint64_t CoordinateKey(const OverworldTileChange &change)
zelda3::Overworld * overworld_
static std::vector< OverworldTileChange > NormalizeChanges(std::vector< OverworldTileChange > changes)
std::chrono::steady_clock::time_point timestamp_
OverworldTilePaintAction(int map_id, int world, std::vector< OverworldTileChange > tile_changes, zelda3::Overworld *overworld, std::function< void()> refresh_fn, std::function< void(int)> refresh_map_fn={}, bool allow_merge=true)
absl::Status Undo() override
Abstract base for all undoable actions (Command pattern)
OverworldBlockset & GetMapTiles(int world_type)
constexpr int kNumOverworldMaps
#define RETURN_IF_ERROR(expr)
Snapshot of overworld item list + current item selection.
std::vector< zelda3::OverworldItem > items
std::optional< zelda3::OverworldItem > selected_item_identity
A single tile coordinate + old/new value pair for undo/redo.