yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_stream_ordering.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_OBJECT_STREAM_ORDERING_H_
2#define YAZE_ZELDA3_DUNGEON_OBJECT_STREAM_ORDERING_H_
3
4#include <algorithm>
5#include <array>
6#include <cstddef>
7#include <numeric>
8#include <utility>
9#include <vector>
10
11#include "absl/status/status.h"
12#include "absl/status/statusor.h"
15
16namespace yaze::zelda3 {
17
19 bool changed = false;
20 std::vector<size_t> old_to_new_index;
21 std::vector<size_t> changed_old_indices;
22 std::vector<size_t> selected_indices;
23};
24
25// Reassigns the selected objects' stored placement value. Room-stream objects
26// are removed from their old stream and appended to the target stream, matching
27// the editor's z-order operation. Torches and pushable blocks stay in place and
28// interpret values 0/1 as their special-table draw-layer selector. A value of
29// 2 is rejected atomically when any selected object uses a special table.
30inline absl::StatusOr<ObjectStorageMutationResult> ReassignObjectStorage(
31 std::vector<RoomObject>& objects, const std::vector<size_t>& indices,
32 int target_value) {
33 if (target_value < 0 || target_value > 2) {
34 return absl::InvalidArgumentError("Invalid object stream target");
35 }
36
38 result.old_to_new_index.resize(objects.size());
39 std::iota(result.old_to_new_index.begin(), result.old_to_new_index.end(), 0);
40
41 std::vector<bool> selected(objects.size(), false);
42 for (size_t index : indices) {
43 if (index < objects.size()) {
44 selected[index] = true;
45 }
46 }
47
48 bool room_stream_changed = false;
49 for (size_t index = 0; index < objects.size(); ++index) {
50 if (selected[index] && UsesSpecialLayerSelector(objects[index]) &&
51 target_value == 2) {
52 return absl::InvalidArgumentError(
53 "Torches and pushable blocks only support upper/lower draw-layer "
54 "selector values 0/1");
55 }
56 if (selected[index] && objects[index].GetLayerValue() != target_value) {
57 result.changed_old_indices.push_back(index);
58 room_stream_changed |= UsesRoomObjectStream(objects[index]);
59 }
60 }
61 if (result.changed_old_indices.empty()) {
62 return result;
63 }
64 result.changed = true;
65
66 struct Entry {
67 RoomObject object;
68 size_t old_index = 0;
69 };
70
71 std::vector<RoomObject> reordered = objects;
72 for (size_t index = 0; index < objects.size(); ++index) {
73 if (selected[index] && UsesSpecialLayerSelector(objects[index])) {
74 reordered[index].layer_ =
75 static_cast<RoomObject::LayerType>(target_value);
76 }
77 }
78
79 std::vector<size_t> stream_slots;
80 std::array<std::vector<Entry>, 3> stream_buckets;
81 std::vector<Entry> moved_to_target;
82
83 if (room_stream_changed) {
84 for (size_t index = 0; index < objects.size(); ++index) {
85 RoomObject object = objects[index];
86 if (UsesSpecialLayerSelector(object)) {
87 continue;
88 }
89
90 stream_slots.push_back(index);
91 if (selected[index] && object.GetLayerValue() != target_value) {
92 object.layer_ = static_cast<RoomObject::LayerType>(target_value);
93 moved_to_target.push_back(Entry{std::move(object), index});
94 continue;
95 }
96
97 const int stream =
98 std::clamp(static_cast<int>(object.GetLayerValue()), 0, 2);
99 stream_buckets[stream].push_back(Entry{std::move(object), index});
100 }
101
102 for (auto& entry : moved_to_target) {
103 stream_buckets[target_value].push_back(std::move(entry));
104 }
105
106 size_t stream_slot_index = 0;
107 for (auto& bucket : stream_buckets) {
108 for (auto& entry : bucket) {
109 const size_t new_index = stream_slots[stream_slot_index++];
110 result.old_to_new_index[entry.old_index] = new_index;
111 reordered[new_index] = std::move(entry.object);
112 }
113 }
114 }
115
116 for (size_t old_index = 0; old_index < selected.size(); ++old_index) {
117 if (selected[old_index]) {
118 result.selected_indices.push_back(result.old_to_new_index[old_index]);
119 }
120 }
121 std::sort(result.selected_indices.begin(), result.selected_indices.end());
122
123 objects = std::move(reordered);
124 return result;
125}
126
127} // namespace yaze::zelda3
128
129#endif // YAZE_ZELDA3_DUNGEON_OBJECT_STREAM_ORDERING_H_
Zelda 3 specific classes and functions.
bool UsesRoomObjectStream(const RoomObject &object)
bool UsesSpecialLayerSelector(const RoomObject &object)
absl::StatusOr< ObjectStorageMutationResult > ReassignObjectStorage(std::vector< RoomObject > &objects, const std::vector< size_t > &indices, int target_value)