6#include "absl/strings/str_cat.h"
7#include "nlohmann/json.hpp"
26 auto room_lookup_const = [&room](
int room_id) ->
const Room* {
27 return room_id == room.
id() ? &room :
nullptr;
29 auto room_lookup_mut = [&room](
int room_id) ->
Room* {
30 return room_id == room.
id() ? &room :
nullptr;
37 return absl::OkStatus();
43 : rom_(rom), game_data_(game_data) {}
53 if (
rom_ ==
nullptr) {
54 return absl::InvalidArgumentError(
"ROM is null");
61 return absl::OkStatus();
68 return absl::OkStatus();
73 return absl::InvalidArgumentError(
"ROM is null");
75 std::vector<int> room_ids;
76 room_ids.reserve(
rooms_.size() + 1);
80 for (
const auto& [room_id, room] :
rooms_) {
82 if (std::find(room_ids.begin(), room_ids.end(), room_id) ==
84 room_ids.push_back(room_id);
88 for (
int room_id : room_ids) {
90 if (room ==
nullptr) {
94 if (room ==
nullptr || !room->
IsLoaded()) {
103 auto room_lookup_const = [
this](
int room_id) ->
const Room* {
106 auto room_lookup_mut = [
this](
int room_id) ->
Room* {
118 return absl::OkStatus();
131 return absl::InvalidArgumentError(
"Invalid room ID");
147 return absl::InvalidArgumentError(
"Invalid room ID");
168 return absl::FailedPreconditionError(
"Nothing to undo");
171 return absl::OkStatus();
180 return absl::FailedPreconditionError(
"Nothing to redo");
183 return absl::OkStatus();
232 return absl::OkStatus();
236 return absl::OkStatus();
240 if (room ==
nullptr) {
244 if (room ==
nullptr) {
245 return absl::NotFoundError(
"Current room is not available");
248 return absl::OkStatus();
255 auto it =
rooms_.find(room_id);
263 return SaveSingleRoomState(
rom_, room);
276 return absl::InvalidArgumentError(
"ROM is null");
279 return absl::InvalidArgumentError(
"Invalid room ID");
291 return absl::OkStatus();
296 return absl::InvalidArgumentError(
"ROM is null");
299 return absl::InvalidArgumentError(
"Invalid room ID");
303 if (room ==
nullptr) {
307 if (room ==
nullptr) {
308 return absl::NotFoundError(
"Room is not available");
311 return absl::OkStatus();
319 return std::make_unique<DungeonEditorSystem>(rom, game_data);
323 using json = nlohmann::json;
327 tmpl[
"room_id"] = room.
id();
331 props[
"blockset"] = room.
blockset();
332 props[
"palette"] = room.
palette();
335 tmpl[
"properties"] = props;
338 json objects_arr = json::array();
341 obj_json[
"id"] = obj.id_;
342 obj_json[
"x"] = obj.x_;
343 obj_json[
"y"] = obj.y_;
344 obj_json[
"size"] = obj.size_;
345 obj_json[
"layer"] = obj.GetLayerValue();
346 objects_arr.push_back(obj_json);
348 tmpl[
"objects"] = objects_arr;
351 json sprites_arr = json::array();
352 for (
const auto& sprite : room.
GetSprites()) {
354 spr_json[
"id"] = sprite.id();
355 spr_json[
"x"] = sprite.x();
356 spr_json[
"y"] = sprite.y();
357 spr_json[
"subtype"] = sprite.subtype();
358 spr_json[
"layer"] = sprite.layer();
359 sprites_arr.push_back(spr_json);
361 tmpl[
"sprites"] = sprites_arr;
364 json items_arr = json::array();
367 item_json[
"position"] = item.position;
368 item_json[
"item"] = item.item;
369 items_arr.push_back(item_json);
371 tmpl[
"pot_items"] = items_arr;
377 bool apply_properties) {
378 using json = nlohmann::json;
382 tmpl = json::parse(json_str);
383 }
catch (
const json::parse_error& err) {
384 return absl::InvalidArgumentError(
385 absl::StrCat(
"Invalid JSON: ", err.what()));
388 if (!tmpl.contains(
"version") || tmpl[
"version"].get<
int>() != 1) {
389 return absl::InvalidArgumentError(
"Unsupported template version");
393 if (apply_properties && tmpl.contains(
"properties")) {
394 const auto& props = tmpl[
"properties"];
395 if (props.contains(
"blockset"))
397 if (props.contains(
"palette"))
399 if (props.contains(
"layout"))
401 if (props.contains(
"spriteset"))
406 if (tmpl.contains(
"objects")) {
409 for (
const auto& obj_json : tmpl[
"objects"]) {
410 int16_t obj_id = obj_json.value(
"id",
static_cast<int16_t
>(0));
411 uint8_t obj_x = obj_json.value(
"x",
static_cast<uint8_t
>(0));
412 uint8_t obj_y = obj_json.value(
"y",
static_cast<uint8_t
>(0));
413 uint8_t obj_size = obj_json.value(
"size",
static_cast<uint8_t
>(0));
414 uint8_t obj_layer = obj_json.value(
"layer",
static_cast<uint8_t
>(0));
415 objects.emplace_back(obj_id, obj_x, obj_y, obj_size, obj_layer);
420 if (tmpl.contains(
"sprites")) {
423 for (
const auto& spr_json : tmpl[
"sprites"]) {
424 uint8_t spr_id = spr_json.value(
"id",
static_cast<uint8_t
>(0));
425 uint8_t spr_x = spr_json.value(
"x",
static_cast<uint8_t
>(0));
426 uint8_t spr_y = spr_json.value(
"y",
static_cast<uint8_t
>(0));
427 uint8_t spr_sub = spr_json.value(
"subtype",
static_cast<uint8_t
>(0));
428 uint8_t spr_layer = spr_json.value(
"layer",
static_cast<uint8_t
>(0));
429 sprites.emplace_back(spr_id, spr_x, spr_y, spr_sub, spr_layer);
434 if (tmpl.contains(
"pot_items")) {
437 for (
const auto& item_json : tmpl[
"pot_items"]) {
439 item.
position = item_json.value(
"position",
static_cast<uint16_t
>(0));
440 item.
item = item_json.value(
"item",
static_cast<uint8_t
>(0));
441 items.push_back(item);
445 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
int GetCurrentRoom() const
absl::Status SaveRoom(int room_id)
absl::Status InitializeObjectEditor()
void SetExternalRoom(Room *room)
RoomChangedCallback room_changed_callback_
absl::Status SaveDungeon()
Room * GetManagedRoom(int room_id)
absl::Status SaveManagedRoom(Room &room)
void SetRoomChangedCallback(RoomChangedCallback callback)
absl::Status SaveRoomData(int room_id)
std::shared_ptr< DungeonObjectEditor > object_editor_
DungeonEditorSystem(Rom *rom, GameData *game_data=nullptr)
std::shared_ptr< DungeonObjectEditor > GetObjectEditor()
absl::Status SetCurrentRoom(int room_id)
std::unordered_map< int, Room > rooms_
EditorState editor_state_
std::vector< UndoPoint > redo_history_
absl::Status Initialize()
absl::Status LoadRoomData(int room_id)
absl::Status ReloadRoom(int room_id)
std::vector< UndoPoint > undo_history_
absl::Status LoadDungeon(int dungeon_id)
std::function< void(int room_id)> RoomChangedCallback
absl::Status BindObjectEditorToCurrentRoom()
EditorState GetEditorState() const
absl::StatusOr< Room > GetRoom(int room_id)
void SetLayoutId(uint8_t id)
absl::Status SaveObjects(const DungeonStreamLayout *layout=nullptr)
absl::Status SaveRoomHeader()
absl::Status SaveObjectStreamHeader(const DungeonStreamLayout *layout=nullptr)
uint8_t spriteset() const
const std::vector< zelda3::Sprite > & GetSprites() const
absl::Status SaveSprites(const DungeonStreamLayout *layout=nullptr)
const std::vector< RoomObject > & GetTileObjects() const
void SetSpriteset(uint8_t ss)
void SetBlockset(uint8_t bs)
void SetPalette(uint8_t pal)
uint8_t layout_id() const
const std::vector< PotItem > & GetPotItems() const
absl::Status SaveSingleRoomState(Rom *rom, Room &room)
absl::Status SaveAllChests(Rom *rom, absl::Span< const Room > rooms)
absl::Status SaveAllPotItems(Rom *rom, absl::Span< const Room > rooms)
absl::Status ApplyRoomLayoutTemplate(Room &room, const std::string &json_str, bool apply_properties)
Apply a previously exported layout template to a room.
absl::Status SaveAllTorches(Rom *rom, absl::Span< const Room > rooms)
absl::StatusOr< std::string > ExportRoomLayoutTemplate(const Room &room)
Export a room's layout as a JSON template string.
Room LoadRoomFromRom(Rom *rom, int room_id)
constexpr int kNumberOfRooms
absl::Status SaveAllCollision(Rom *rom, absl::Span< Room > rooms)
std::unique_ptr< DungeonEditorSystem > CreateDungeonEditorSystem(Rom *rom, GameData *game_data)
Factory function to create dungeon editor system.
#define RETURN_IF_ERROR(expr)
std::chrono::steady_clock::time_point last_save_time