yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_editor_system.cc
Go to the documentation of this file.
2
3#include <chrono>
4
5#include "absl/strings/str_cat.h"
6#include "nlohmann/json.hpp"
8
9namespace yaze {
10namespace zelda3 {
11
13 : rom_(rom), game_data_(game_data) {}
14
16 if (rom_ == nullptr) {
17 return absl::InvalidArgumentError("ROM is null");
18 }
19
20 // Initialize object editor
21 object_editor_ = std::make_shared<DungeonObjectEditor>(rom_);
22 RETURN_IF_ERROR(object_editor_->InitializeEditor());
23
24 return absl::OkStatus();
25}
26
27absl::Status DungeonEditorSystem::LoadDungeon(int dungeon_id) {
29 editor_state_.is_dirty = false;
30 editor_state_.last_save_time = std::chrono::steady_clock::now();
31 return absl::OkStatus();
32}
33
35 if (!rom_)
36 return absl::InvalidArgumentError("ROM is null");
37
38 for (int i = 0; i < kNumberOfRooms; ++i) {
39 auto status = SaveRoomData(i);
40 if (!status.ok()) {
41 return status;
42 }
43 }
44
45 editor_state_.is_dirty = false;
46 editor_state_.last_save_time = std::chrono::steady_clock::now();
47
48 return absl::OkStatus();
49}
50
51absl::Status DungeonEditorSystem::SaveRoom(int room_id) {
52 return SaveRoomData(room_id);
53}
54
55absl::Status DungeonEditorSystem::ReloadRoom(int room_id) {
56 return LoadRoomData(room_id);
57}
58
59absl::Status DungeonEditorSystem::SetCurrentRoom(int room_id) {
60 if (room_id < 0 || room_id >= kNumberOfRooms) {
61 return absl::InvalidArgumentError("Invalid room ID");
62 }
63
65 return absl::OkStatus();
66}
67
71
72absl::StatusOr<Room> DungeonEditorSystem::GetRoom(int room_id) {
73 if (room_id < 0 || room_id >= kNumberOfRooms) {
74 return absl::InvalidArgumentError("Invalid room ID");
75 }
76
77 return Room(room_id, rom_, game_data_);
78}
79
80std::shared_ptr<DungeonObjectEditor> DungeonEditorSystem::GetObjectEditor() {
81 if (!object_editor_) {
82 object_editor_ = std::make_shared<DungeonObjectEditor>(rom_);
83 }
84 return object_editor_;
85}
86
88 if (object_editor_) {
89 return object_editor_->Undo();
90 }
91
92 if (!CanUndo()) {
93 return absl::FailedPreconditionError("Nothing to undo");
94 }
95
96 return absl::OkStatus();
97}
98
100 if (object_editor_) {
101 return object_editor_->Redo();
102 }
103
104 if (!CanRedo()) {
105 return absl::FailedPreconditionError("Nothing to redo");
106 }
107
108 return absl::OkStatus();
109}
110
112 return !undo_history_.empty();
113}
114
116 return !redo_history_.empty();
117}
118
120 undo_history_.clear();
121 redo_history_.clear();
122}
123
127
131
133 return rom_;
134}
135
139
141 rom_ = rom;
142 if (object_editor_) {
143 object_editor_->SetROM(rom);
144 }
145}
146
148 if (object_editor_) {
149 object_editor_->SetExternalRoom(room);
150 }
151}
152
154 if (!object_editor_) {
155 object_editor_ = std::make_shared<DungeonObjectEditor>(rom_);
156 }
157 return object_editor_->InitializeEditor();
158}
159
160absl::Status DungeonEditorSystem::LoadRoomData(int room_id) {
161 if (!rom_)
162 return absl::InvalidArgumentError("ROM is null");
163
164 // Room loading is handled by the Room class itself
165 // This method exists for consistency with the API
166 return absl::OkStatus();
167}
168
169absl::Status DungeonEditorSystem::SaveRoomData(int room_id) {
170 if (!rom_)
171 return absl::InvalidArgumentError("ROM is null");
172
173 // Check if this is the currently edited room in the ObjectEditor
174 if (object_editor_ && object_editor_->GetRoom().id() == room_id) {
175 Room* room = object_editor_->GetMutableRoom();
176 if (room) {
177 return room->SaveObjects();
178 }
179 }
180
181 return absl::OkStatus();
182}
183
184std::unique_ptr<DungeonEditorSystem> CreateDungeonEditorSystem(
185 Rom* rom, GameData* game_data) {
186 return std::make_unique<DungeonEditorSystem>(rom, game_data);
187}
188
189absl::StatusOr<std::string> ExportRoomLayoutTemplate(const Room& room) {
190 using json = nlohmann::json;
191
192 json tmpl;
193 tmpl["version"] = 1;
194 tmpl["room_id"] = room.id();
195
196 // Room properties
197 json props;
198 props["blockset"] = room.blockset();
199 props["palette"] = room.palette();
200 props["layout"] = room.layout_id();
201 props["spriteset"] = room.spriteset();
202 tmpl["properties"] = props;
203
204 // Tile objects
205 json objects_arr = json::array();
206 for (const auto& obj : room.GetTileObjects()) {
207 json obj_json;
208 obj_json["id"] = obj.id_;
209 obj_json["x"] = obj.x_;
210 obj_json["y"] = obj.y_;
211 obj_json["size"] = obj.size_;
212 obj_json["layer"] = obj.GetLayerValue();
213 objects_arr.push_back(obj_json);
214 }
215 tmpl["objects"] = objects_arr;
216
217 // Sprites
218 json sprites_arr = json::array();
219 for (const auto& sprite : room.GetSprites()) {
220 json spr_json;
221 spr_json["id"] = sprite.id();
222 spr_json["x"] = sprite.x();
223 spr_json["y"] = sprite.y();
224 spr_json["subtype"] = sprite.subtype();
225 spr_json["layer"] = sprite.layer();
226 sprites_arr.push_back(spr_json);
227 }
228 tmpl["sprites"] = sprites_arr;
229
230 // Pot items
231 json items_arr = json::array();
232 for (const auto& item : room.GetPotItems()) {
233 json item_json;
234 item_json["position"] = item.position;
235 item_json["item"] = item.item;
236 items_arr.push_back(item_json);
237 }
238 tmpl["pot_items"] = items_arr;
239
240 return tmpl.dump(2);
241}
242
243absl::Status ApplyRoomLayoutTemplate(Room& room, const std::string& json_str,
244 bool apply_properties) {
245 using json = nlohmann::json;
246
247 json tmpl;
248 try {
249 tmpl = json::parse(json_str);
250 } catch (const json::parse_error& err) {
251 return absl::InvalidArgumentError(
252 absl::StrCat("Invalid JSON: ", err.what()));
253 }
254
255 if (!tmpl.contains("version") || tmpl["version"].get<int>() != 1) {
256 return absl::InvalidArgumentError("Unsupported template version");
257 }
258
259 // Apply room properties if requested
260 if (apply_properties && tmpl.contains("properties")) {
261 const auto& props = tmpl["properties"];
262 if (props.contains("blockset"))
263 room.SetBlockset(props["blockset"]);
264 if (props.contains("palette"))
265 room.SetPalette(props["palette"]);
266 if (props.contains("layout"))
267 room.SetLayoutId(props["layout"]);
268 if (props.contains("spriteset"))
269 room.SetSpriteset(props["spriteset"]);
270 }
271
272 // Apply objects
273 if (tmpl.contains("objects")) {
274 auto& objects = room.GetTileObjects();
275 objects.clear();
276 for (const auto& obj_json : tmpl["objects"]) {
277 int16_t obj_id = obj_json.value("id", static_cast<int16_t>(0));
278 uint8_t obj_x = obj_json.value("x", static_cast<uint8_t>(0));
279 uint8_t obj_y = obj_json.value("y", static_cast<uint8_t>(0));
280 uint8_t obj_size = obj_json.value("size", static_cast<uint8_t>(0));
281 uint8_t obj_layer = obj_json.value("layer", static_cast<uint8_t>(0));
282 objects.emplace_back(obj_id, obj_x, obj_y, obj_size, obj_layer);
283 }
284 }
285
286 // Apply sprites
287 if (tmpl.contains("sprites")) {
288 auto& sprites = room.GetSprites();
289 sprites.clear();
290 for (const auto& spr_json : tmpl["sprites"]) {
291 uint8_t spr_id = spr_json.value("id", static_cast<uint8_t>(0));
292 uint8_t spr_x = spr_json.value("x", static_cast<uint8_t>(0));
293 uint8_t spr_y = spr_json.value("y", static_cast<uint8_t>(0));
294 uint8_t spr_sub = spr_json.value("subtype", static_cast<uint8_t>(0));
295 uint8_t spr_layer = spr_json.value("layer", static_cast<uint8_t>(0));
296 sprites.emplace_back(spr_id, spr_x, spr_y, spr_sub, spr_layer);
297 }
298 }
299
300 // Apply pot items
301 if (tmpl.contains("pot_items")) {
302 auto& items = room.GetPotItems();
303 items.clear();
304 for (const auto& item_json : tmpl["pot_items"]) {
305 PotItem item;
306 item.position = item_json.value("position", static_cast<uint16_t>(0));
307 item.item = item_json.value("item", static_cast<uint8_t>(0));
308 items.push_back(item);
309 }
310 }
311
312 return absl::OkStatus();
313}
314
315} // namespace zelda3
316} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
void SetRoomChangedCallback(RoomChangedCallback callback)
std::shared_ptr< DungeonObjectEditor > object_editor_
DungeonEditorSystem(Rom *rom, GameData *game_data=nullptr)
std::shared_ptr< DungeonObjectEditor > GetObjectEditor()
absl::Status SetCurrentRoom(int room_id)
absl::Status LoadDungeon(int dungeon_id)
std::function< void(int room_id)> RoomChangedCallback
absl::StatusOr< Room > GetRoom(int room_id)
uint8_t blockset() const
Definition room.h:569
void SetLayoutId(uint8_t id)
Definition room.h:577
absl::Status SaveObjects()
Definition room.cc:1681
uint8_t palette() const
Definition room.h:571
uint8_t spriteset() const
Definition room.h:570
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:214
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:314
void SetSpriteset(uint8_t ss)
Definition room.h:487
void SetBlockset(uint8_t bs)
Definition room.h:481
void SetPalette(uint8_t pal)
Definition room.h:475
uint8_t layout_id() const
Definition room.h:572
const std::vector< PotItem > & GetPotItems() const
Definition room.h:308
int id() const
Definition room.h:566
nlohmann::json json
absl::Status ApplyRoomLayoutTemplate(Room &room, const std::string &json_str, bool apply_properties)
Apply a previously exported layout template to a room.
absl::StatusOr< std::string > ExportRoomLayoutTemplate(const Room &room)
Export a room's layout as a JSON template string.
constexpr int kNumberOfRooms
std::unique_ptr< DungeonEditorSystem > CreateDungeonEditorSystem(Rom *rom, GameData *game_data)
Factory function to create dungeon editor system.
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::chrono::steady_clock::time_point last_save_time
uint16_t position
Definition room.h:95