73 int room_id,
const std::vector<zelda3::RoomObject>& before,
74 const std::vector<zelda3::RoomObject>& after,
75 std::unique_ptr<UndoAction> object_action) {
82 if (!room || room->rom() !=
rom_) {
83 return absl::FailedPreconditionError(
"Track room is no longer loaded");
85 const auto collision_before = room->custom_collision();
92 before, after, collision_before, ownership_before));
93 if (edit.collision == collision_before &&
94 edit.ownership == ownership_before) {
98 auto* source_rom =
rom_;
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 {
108 if (!current || current->rom() != source_rom ||
rom_ != source_rom ||
110 return absl::FailedPreconditionError(
111 "Rail edit belongs to a different project generation");
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");
120 forward ? collision_after : collision_before);
122 forward ? ownership_after : ownership_before;
123 return absl::OkStatus();
129 return std::make_unique<RailObjectsAction>(std::move(object_action),
130 std::move(restore), bytes);
137 return absl::FailedPreconditionError(
"Track document is unavailable");
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;
146 return absl::InvalidArgumentError(
147 "Move a start within its existing room");
150 return absl::FailedPreconditionError(
"Load the start room first");
153 sprite_index = index;
154 old_sprite = room->GetSprites()[index];
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");
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;
171 auto restore = [
this, generation, before, after, sprite_index, old_sprite,
172 new_sprite](
bool forward) -> absl::Status {
175 return absl::FailedPreconditionError(
"Track document closed");
176 const auto& expected = forward ? before : after;
177 const auto& replacement = forward ? after : before;
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");
195 current_panel->RestoreTrackDraft(generation, expected, replacement));
197 room->
GetSprites()[*sprite_index] = forward ? *new_sprite : *old_sprite;
199 current_panel->MarkPairedTrackDraft();
203 return absl::OkStatus();
207 return absl::OkStatus();