yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_editor_v2_minecart.cc
Go to the documentation of this file.
2
5#include "core/features.h"
6#include "util/macro.h"
7
8namespace yaze::editor {
9namespace {
10class CartStartAction final : public UndoAction {
11 public:
12 explicit CartStartAction(std::function<absl::Status(bool)> restore)
13 : restore_(std::move(restore)) {}
14 absl::Status Undo() override { return restore_(false); }
15 absl::Status Redo() override { return restore_(true); }
16 std::string Description() const override { return "Edit cart start"; }
17
18 private:
19 std::function<absl::Status(bool)> restore_;
20};
21
22class RailObjectsAction final : public UndoAction {
23 public:
24 using Restore = std::function<absl::Status(bool, UndoAction&)>;
25 RailObjectsAction(std::unique_ptr<UndoAction> objects, Restore restore,
26 size_t collision_bytes)
27 : objects_(std::move(objects)),
28 restore_(std::move(restore)),
29 collision_bytes_(collision_bytes) {}
30 absl::Status Undo() override { return restore_(false, *objects_); }
31 absl::Status Redo() override { return restore_(true, *objects_); }
32 std::string Description() const override { return objects_->Description(); }
33 size_t MemoryUsage() const override {
34 return objects_->MemoryUsage() + collision_bytes_;
35 }
36
37 private:
38 std::unique_ptr<UndoAction> objects_;
41};
42} // namespace
43
45 int room_id, const std::vector<zelda3::RoomObject>& objects) const {
49 return absl::OkStatus();
50 }
51 const auto* room = rooms_.GetIfLoaded(room_id);
52 if (!room) {
53 return absl::FailedPreconditionError("Track room is no longer loaded");
54 }
55 const auto* before = &room->GetTileObjects();
57 pending_selection_undo_.plan->room_id == room_id) {
58 before = &pending_selection_undo_.plan->before.objects;
59 } else if (pending_undo_.room_id == room_id) {
61 }
62 const auto found = minecart_collision_ownership_.find(room_id);
63 const auto ownership = found == minecart_collision_ownership_.end()
65 : found->second;
67 *before, objects, room->custom_collision(), ownership)
68 .status();
69}
70
71absl::StatusOr<std::unique_ptr<UndoAction>>
73 int room_id, const std::vector<zelda3::RoomObject>& before,
74 const std::vector<zelda3::RoomObject>& after,
75 std::unique_ptr<UndoAction> object_action) {
79 return object_action;
80 }
81 auto* room = rooms_.GetIfLoaded(room_id);
82 if (!room || room->rom() != rom_) {
83 return absl::FailedPreconditionError("Track room is no longer loaded");
84 }
85 const auto collision_before = room->custom_collision();
86 const auto found = minecart_collision_ownership_.find(room_id);
87 const auto ownership_before = found == minecart_collision_ownership_.end()
89 : found->second;
90 ASSIGN_OR_RETURN(auto edit,
92 before, after, collision_before, ownership_before));
93 if (edit.collision == collision_before &&
94 edit.ownership == ownership_before) {
95 return object_action;
96 }
97 const uint64_t generation = minecart_collision_generation_;
98 auto* source_rom = rom_;
99 const size_t bytes =
100 sizeof(collision_before) + sizeof(edit.collision) +
101 (ownership_before.size() + edit.ownership.size()) *
102 sizeof(zelda3::MinecartTrackCollisionOwnership::value_type);
103 auto restore = [this, room_id, generation, source_rom, collision_before,
104 collision_after = edit.collision, ownership_before,
105 ownership_after = edit.ownership](
106 bool forward, UndoAction& objects) -> absl::Status {
107 auto* current = rooms_.GetIfLoaded(room_id);
108 if (!current || current->rom() != source_rom || rom_ != source_rom ||
109 minecart_collision_generation_ != generation) {
110 return absl::FailedPreconditionError(
111 "Rail edit belongs to a different project generation");
112 }
113 const auto& expected = forward ? collision_before : collision_after;
114 if (!(current->custom_collision() == expected)) {
115 return absl::FailedPreconditionError(
116 "Rail collision changed outside Undo; repeat the edit");
117 }
118 RETURN_IF_ERROR(forward ? objects.Redo() : objects.Undo());
120 forward ? collision_after : collision_before);
122 forward ? ownership_after : ownership_before;
123 return absl::OkStatus();
124 };
125 // The ordinary object mutation has already completed. Collision joins it
126 // only after the complete generated candidate has passed validation.
127 RestoreRoomCustomCollision(room_id, edit.collision);
128 minecart_collision_ownership_[room_id] = std::move(edit.ownership);
129 return std::make_unique<RailObjectsAction>(std::move(object_action),
130 std::move(restore), bytes);
131}
132
134 const MinecartTrack& before, const MinecartTrack& after, bool move_cart) {
135 auto* panel = minecart_track_editor_panel_;
136 if (!panel)
137 return absl::FailedPreconditionError("Track document is unavailable");
138 if (before == after)
139 return absl::OkStatus();
141 const uint64_t generation = panel->track_generation();
142 std::optional<size_t> sprite_index;
143 std::optional<zelda3::Sprite> old_sprite, new_sprite;
144 if (move_cart) {
145 if (before.room_id != after.room_id || before.id != after.id)
146 return absl::InvalidArgumentError(
147 "Move a start within its existing room");
148 auto* room = rooms_.GetIfLoaded(before.room_id);
149 if (!room)
150 return absl::FailedPreconditionError("Load the start room first");
151 ASSIGN_OR_RETURN(const size_t index, FindCartStartSprite(*room, before));
153 sprite_index = index;
154 old_sprite = room->GetSprites()[index];
155 const auto local = *MinecartLocalPosition(after);
156 for (size_t other = 0; other < room->GetSprites().size(); ++other) {
157 const auto& sprite = room->GetSprites()[other];
158 if (other != index && !sprite.deleted() && sprite.id() == 0xA3 &&
159 sprite.layer() == 0 && sprite.x() * 16 == local.first &&
160 sprite.y() * 16 == local.second)
161 return absl::FailedPreconditionError("Another cart occupies this stop");
162 }
163 new_sprite.emplace(old_sprite->id(), local.first / 16, local.second / 16,
164 old_sprite->subtype(), old_sprite->layer());
165 new_sprite->set_key_drop(old_sprite->key_drop());
166 new_sprite->set_deleted(old_sprite->deleted());
167 auto candidates = room->GetSprites();
168 candidates[index] = *new_sprite;
170 }
171 auto restore = [this, generation, before, after, sprite_index, old_sprite,
172 new_sprite](bool forward) -> absl::Status {
173 auto* current_panel = minecart_track_editor_panel_;
174 if (!current_panel)
175 return absl::FailedPreconditionError("Track document closed");
176 const auto& expected = forward ? before : after;
177 const auto& replacement = forward ? after : before;
178 zelda3::Room* room = nullptr;
179 if (sprite_index) {
180 room = rooms_.GetIfLoaded(before.room_id);
181 if (!room || *sprite_index >= room->GetSprites().size())
182 return absl::FailedPreconditionError(
183 "The paired cart is no longer loaded");
184 const auto& current = room->GetSprites()[*sprite_index];
185 const auto& wanted = forward ? *old_sprite : *new_sprite;
186 if (current.id() != wanted.id() || current.x() != wanted.x() ||
187 current.y() != wanted.y() || current.subtype() != wanted.subtype() ||
188 current.layer() != wanted.layer() ||
189 current.deleted() != wanted.deleted() ||
190 current.key_drop() != wanted.key_drop())
191 return absl::FailedPreconditionError(
192 "The paired cart changed; repeat the edit");
193 }
195 current_panel->RestoreTrackDraft(generation, expected, replacement));
196 if (room) {
197 room->GetSprites()[*sprite_index] = forward ? *new_sprite : *old_sprite;
198 room->MarkSpritesDirty();
199 current_panel->MarkPairedTrackDraft();
200 if (rom_)
201 rom_->set_dirty(true);
202 }
203 return absl::OkStatus();
204 };
205 RETURN_IF_ERROR(restore(true));
206 undo_manager_.Push(std::make_unique<CartStartAction>(std::move(restore)));
207 return absl::OkStatus();
208}
209} // namespace yaze::editor
void set_dirty(bool dirty)
Definition rom.h:201
static Flags & get()
Definition features.h:122
class MinecartTrackEditorPanel * minecart_track_editor_panel_
absl::Status PreflightSpriteMutation(int room_id, const std::vector< zelda3::Sprite > &sprites)
absl::Status ApplyMinecartTrackEdit(const MinecartTrack &before, const MinecartTrack &after, bool move_cart)
absl::StatusOr< std::unique_ptr< UndoAction > > PairMinecartObjectCollision(int room_id, const std::vector< zelda3::RoomObject > &before, const std::vector< zelda3::RoomObject > &after, std::unique_ptr< UndoAction > object_action)
PendingSelectionUndo pending_selection_undo_
absl::Status PreflightMinecartObjectCollision(int room_id, const std::vector< zelda3::RoomObject > &objects) const
void RestoreRoomCustomCollision(int room_id, const zelda3::CustomCollisionMap &map)
std::unordered_map< int, zelda3::MinecartTrackCollisionOwnership > minecart_collision_ownership_
zelda3::Room * GetIfLoaded(int room_id)
UndoManager undo_manager_
Definition editor.h:383
Abstract base for all undoable actions (Command pattern)
Definition undo_action.h:20
void Push(std::unique_ptr< UndoAction > action)
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
RailObjectsAction(std::unique_ptr< UndoAction > objects, Restore restore, size_t collision_bytes)
void MarkSpritesDirty()
Definition room.h:319
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:316
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
Editors are the view controllers for the application.
std::optional< std::pair< int, int > > MinecartLocalPosition(const MinecartTrack &track)
absl::StatusOr< size_t > FindCartStartSprite(const zelda3::Room &room, const MinecartTrack &start)
absl::Status ValidateCartStartDestination(const zelda3::Room &room, const MinecartTrack &start)
std::map< size_t, MinecartTrackCollisionCellOwner > MinecartTrackCollisionOwnership
absl::StatusOr< MinecartTrackCollisionEdit > SynchronizeMinecartTrackCollision(std::span< const RoomObject > before_objects, std::span< const RoomObject > after_objects, const CustomCollisionMap &before_collision, const MinecartTrackCollisionOwnership &before_ownership)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::optional< DungeonSelectionEditPlan > plan
std::vector< zelda3::RoomObject > before_objects