75 const std::vector<chest_data>& chests) {
76 const auto indices = StreamObjectIndices(objects);
77 if (chests.size() != indices.size()) {
78 return absl::FailedPreconditionError(absl::StrFormat(
79 "Room has %zu stateful chest objects but %zu contents records; "
80 "resolve the mapping before adding, deleting, or reordering chests",
81 indices.size(), chests.size()));
83 for (
size_t ordinal = 0; ordinal < indices.size(); ++ordinal) {
84 if (chests[ordinal].size != (objects[indices[ordinal]].id_ == 0xFB1)) {
85 return absl::FailedPreconditionError(absl::StrFormat(
86 "Chest %zu contents type does not match its small/big object; "
87 "resolve the mapping before changing chest objects",
91 return absl::OkStatus();
95 const std::vector<RoomObject>& before_objects,
96 const std::vector<chest_data>& before_chests,
97 const std::vector<RoomObject>& after_objects,
98 const std::vector<std::optional<size_t>>& source_indices,
99 const std::vector<std::optional<chest_data>>& chest_overrides) {
100 if (source_indices.size() != after_objects.size()) {
101 return absl::InvalidArgumentError(
102 "Chest edit source indices must match the candidate objects");
104 if (!chest_overrides.empty() &&
105 chest_overrides.size() != after_objects.size()) {
106 return absl::InvalidArgumentError(
107 "Chest overrides must match the candidate objects");
109 for (
size_t index = 0; index < after_objects.size(); ++index) {
110 if (source_indices[index] &&
111 *source_indices[index] >= before_objects.size()) {
112 return absl::InvalidArgumentError(
"Chest edit source index is invalid");
114 const auto&
object = after_objects[index];
116 (IsChest(
object) ||
object.id_ == 0xF98) &&
117 object.GetLayerValue() > 2) {
118 return absl::InvalidArgumentError(
119 "Chest or big-key lock object-list index must be in range 0..2");
121 if (!chest_overrides.empty() && chest_overrides[index]) {
122 if (!IsChest(
object)) {
123 return absl::InvalidArgumentError(
124 "Only stateful chest objects can have contents overrides");
126 if (chest_overrides[index]->size != (
object.id_ == 0xFB1)) {
127 return absl::InvalidArgumentError(
128 "Chest contents override must match the small/big object type");
133 const auto before_indices = StreamObjectIndices(before_objects);
134 const auto after_indices = StreamObjectIndices(after_objects);
135 const auto before_events = StreamObjectIndices(before_objects,
true);
136 const auto after_events = StreamObjectIndices(after_objects,
true);
137 bool unchanged = before_events.size() == after_events.size();
138 for (
size_t ordinal = 0; unchanged && ordinal < after_events.size();
140 const size_t index = after_events[ordinal];
142 source_indices[index] == before_events[ordinal] &&
143 after_objects[index].id_ == before_objects[before_events[ordinal]].id_;
145 for (
size_t ordinal = 0; unchanged && ordinal < after_indices.size();
147 const size_t index = after_indices[ordinal];
148 if (!chest_overrides.empty() && chest_overrides[index]) {
149 const auto& replacement = *chest_overrides[index];
150 unchanged = unchanged && ordinal < before_chests.size() &&
151 replacement.id == before_chests[ordinal].id &&
152 replacement.size == before_chests[ordinal].size;
156 return before_chests;
159 const absl::Status mapping_status =
161 if (!mapping_status.ok()) {
162 return mapping_status;
164 std::vector<std::optional<size_t>> before_ordinals(before_objects.size());
165 for (
size_t ordinal = 0; ordinal < before_indices.size(); ++ordinal) {
166 before_ordinals[before_indices[ordinal]] = ordinal;
168 const absl::Status slots_status = ValidateRoomEventSlots(after_objects);
169 if (!slots_status.ok()) {
173 std::vector<chest_data> after_chests;
174 after_chests.reserve(after_indices.size());
175 for (
size_t index : after_indices) {
177 if (source_indices[index]) {
178 if (
const auto ordinal = before_ordinals[*source_indices[index]]) {
179 chest.id = before_chests[*ordinal].id;
182 if (!chest_overrides.empty() && chest_overrides[index]) {
183 chest = *chest_overrides[index];
185 after_chests.push_back(
chest);