6#include "absl/strings/str_format.h"
12 : rom_(rom), game_data_(game_data) {}
15 if (
rom_ ==
nullptr) {
16 return absl::InvalidArgumentError(
"ROM is null");
36 return absl::OkStatus();
46 return absl::OkStatus();
50 if (!
rom_)
return absl::InvalidArgumentError(
"ROM is null");
63 return absl::OkStatus();
84 return absl::InvalidArgumentError(
"Invalid room ID");
88 return absl::OkStatus();
97 return absl::InvalidArgumentError(
"Invalid room ID");
105 const std::string&
name) {
107 return absl::OkStatus();
112 return absl::OkStatus();
116 int target_room_id) {
118 return absl::OkStatus();
130 return absl::OkStatus();
143 return absl::OkStatus();
149 return absl::NotFoundError(
"Sprite not found");
153 return absl::OkStatus();
160 return absl::NotFoundError(
"Sprite not found");
163 it->second = sprite_data;
170 return absl::OkStatus();
177 return absl::NotFoundError(
"Sprite not found");
183absl::StatusOr<std::vector<DungeonEditorSystem::SpriteData>>
185 std::vector<SpriteData> room_sprites;
187 for (
const auto& [
id, sprite] :
sprites_) {
188 if (sprite.x >= 0 && sprite.y >= 0) {
189 room_sprites.push_back(sprite);
196absl::StatusOr<std::vector<DungeonEditorSystem::SpriteData>>
198 std::vector<SpriteData> typed_sprites;
200 for (
const auto& [
id, sprite] :
sprites_) {
201 if (sprite.type == type) {
202 typed_sprites.push_back(sprite);
206 return typed_sprites;
213 return absl::NotFoundError(
"Sprite not found");
216 it->second.x = new_x;
217 it->second.y = new_y;
223 return absl::OkStatus();
229 return absl::NotFoundError(
"Sprite not found");
232 it->second.is_active = active;
238 return absl::OkStatus();
244 items_[item_id] = item_data;
251 return absl::OkStatus();
255 auto it =
items_.find(item_id);
257 return absl::NotFoundError(
"Item not found");
261 return absl::OkStatus();
266 auto it =
items_.find(item_id);
268 return absl::NotFoundError(
"Item not found");
271 it->second = item_data;
278 return absl::OkStatus();
283 auto it =
items_.find(item_id);
285 return absl::NotFoundError(
"Item not found");
291absl::StatusOr<std::vector<DungeonEditorSystem::ItemData>>
293 std::vector<ItemData> room_items;
295 for (
const auto& [
id, item] :
items_) {
296 if (item.room_id == room_id) {
297 room_items.push_back(item);
304absl::StatusOr<std::vector<DungeonEditorSystem::ItemData>>
306 std::vector<ItemData> typed_items;
308 for (
const auto& [
id, item] :
items_) {
309 if (item.type == type) {
310 typed_items.push_back(item);
318 auto it =
items_.find(item_id);
320 return absl::NotFoundError(
"Item not found");
323 it->second.x = new_x;
324 it->second.y = new_y;
330 return absl::OkStatus();
334 auto it =
items_.find(item_id);
336 return absl::NotFoundError(
"Item not found");
339 it->second.is_hidden = hidden;
345 return absl::OkStatus();
359 return absl::OkStatus();
365 return absl::NotFoundError(
"Entrance not found");
369 return absl::OkStatus();
376 return absl::NotFoundError(
"Entrance not found");
379 it->second = entrance_data;
386 return absl::OkStatus();
389absl::StatusOr<DungeonEditorSystem::EntranceData>
393 return absl::NotFoundError(
"Entrance not found");
399absl::StatusOr<std::vector<DungeonEditorSystem::EntranceData>>
401 std::vector<EntranceData> room_entrances;
403 for (
const auto& [
id, entrance] :
entrances_) {
404 if (entrance.source_room_id == room_id ||
405 entrance.target_room_id == room_id) {
406 room_entrances.push_back(entrance);
410 return room_entrances;
413absl::StatusOr<std::vector<DungeonEditorSystem::EntranceData>>
415 std::vector<EntranceData> typed_entrances;
417 for (
const auto& [
id, entrance] :
entrances_) {
418 if (entrance.type == type) {
419 typed_entrances.push_back(entrance);
423 return typed_entrances;
427 int x1,
int y1,
int x2,
int y2) {
444 const auto& entrance = it->second;
445 if ((entrance.source_room_id == room1_id &&
446 entrance.target_room_id == room2_id) ||
447 (entrance.source_room_id == room2_id &&
448 entrance.target_room_id == room1_id)) {
455 return absl::OkStatus();
461 doors_[door_id] = door_data;
468 return absl::OkStatus();
472 auto it =
doors_.find(door_id);
474 return absl::NotFoundError(
"Door not found");
478 return absl::OkStatus();
483 auto it =
doors_.find(door_id);
485 return absl::NotFoundError(
"Door not found");
488 it->second = door_data;
495 return absl::OkStatus();
500 auto it =
doors_.find(door_id);
502 return absl::NotFoundError(
"Door not found");
508absl::StatusOr<std::vector<DungeonEditorSystem::DoorData>>
510 std::vector<DoorData> room_doors;
512 for (
const auto& [
id, door] :
doors_) {
513 if (door.room_id == room_id) {
514 room_doors.push_back(door);
522 auto it =
doors_.find(door_id);
524 return absl::NotFoundError(
"Door not found");
527 it->second.is_locked = locked;
533 return absl::OkStatus();
539 auto it =
doors_.find(door_id);
541 return absl::NotFoundError(
"Door not found");
544 it->second.requires_key = requires_key;
545 it->second.key_type = key_type;
551 return absl::OkStatus();
558 chests_[chest_id].chest_id = chest_id;
564 return absl::OkStatus();
568 auto it =
chests_.find(chest_id);
570 return absl::NotFoundError(
"Chest not found");
574 return absl::OkStatus();
579 auto it =
chests_.find(chest_id);
581 return absl::NotFoundError(
"Chest not found");
585 it->second.chest_id = chest_id;
591 return absl::OkStatus();
596 auto it =
chests_.find(chest_id);
598 return absl::NotFoundError(
"Chest not found");
604absl::StatusOr<std::vector<DungeonEditorSystem::ChestData>>
606 std::vector<ChestData> room_chests;
609 if (
chest.room_id == room_id) {
610 room_chests.push_back(
chest);
619 auto it =
chests_.find(chest_id);
621 return absl::NotFoundError(
"Chest not found");
624 it->second.item_id = item_id;
625 it->second.item_quantity = quantity;
631 return absl::OkStatus();
635 auto it =
chests_.find(chest_id);
637 return absl::NotFoundError(
"Chest not found");
640 it->second.is_opened = opened;
646 return absl::OkStatus();
658 return absl::OkStatus();
661absl::StatusOr<DungeonEditorSystem::RoomProperties>
667 default_properties.
room_id = room_id;
668 default_properties.
name = absl::StrFormat(
"Room %d", room_id);
677 return default_properties;
687 return absl::OkStatus();
690absl::StatusOr<DungeonEditorSystem::DungeonSettings>
698 return absl::OkStatus();
703 return absl::OkStatus();
735 const std::string& file_path,
int room_id) {
737 return absl::OkStatus();
741 int room_id,
const std::string& file_path) {
743 return absl::OkStatus();
747 const std::string& file_path) {
749 return absl::OkStatus();
753 const std::string& file_path) {
755 return absl::OkStatus();
767 return absl::FailedPreconditionError(
"Nothing to undo");
771 return absl::OkStatus();
782 return absl::FailedPreconditionError(
"Nothing to redo");
786 return absl::OkStatus();
877 if (!
rom_)
return absl::InvalidArgumentError(
"ROM is null");
885 if (it->second.properties.count(
"room_id") && std::stoi(it->second.properties.at(
"room_id")) == room_id) {
893 for (
const auto& spr : room_sprites) {
898 data.
layer = spr.layer();
900 data.
name = absl::StrFormat(
"Sprite %02X", spr.id());
901 data.
properties[
"id"] = absl::StrFormat(
"%d", spr.id());
902 data.
properties[
"subtype"] = absl::StrFormat(
"%d", spr.subtype());
903 data.
properties[
"room_id"] = absl::StrFormat(
"%d", room_id);
911 if (it->second.room_id == room_id) {
918 const auto& room_chests = room.
GetChests();
919 for (
const auto&
chest : room_chests) {
928 return absl::OkStatus();
932 if (!
rom_)
return absl::InvalidArgumentError(
"ROM is null");
935 bool is_current_editor_room =
false;
937 is_current_editor_room =
true;
944 std::unique_ptr<Room> room_ptr;
945 Room* room =
nullptr;
947 if (is_current_editor_room) {
951 room = room_ptr.get();
954 if (!room)
return absl::InternalError(
"Failed to get Room object");
959 for (
const auto& [
id, sprite_data] :
sprites_) {
960 auto room_id_it = sprite_data.properties.find(
"room_id");
961 if (room_id_it != sprite_data.properties.end()) {
962 if (std::stoi(room_id_it->second) != room_id)
continue;
969 if (sprite_data.properties.count(
"id")) raw_id = std::stoi(sprite_data.properties.at(
"id"));
970 if (sprite_data.properties.count(
"subtype")) subtype = std::stoi(sprite_data.properties.at(
"subtype"));
972 zelda3::Sprite z3_sprite(raw_id, sprite_data.x, sprite_data.y, subtype, sprite_data.layer);
978 if (!status.ok())
return status;
984 if (is_current_editor_room) {
986 if (!status.ok())
return status;
989 return absl::OkStatus();
993 return absl::OkStatus();
997 return absl::OkStatus();
1001 return absl::OkStatus();
1005 return absl::OkStatus();
1009 return absl::OkStatus();
1013 return absl::OkStatus();
1017 return absl::OkStatus();
1021 return absl::OkStatus();
1025 return absl::OkStatus();
1029 return absl::OkStatus();
1041 return std::make_unique<DungeonEditorSystem>(rom);
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Represents a bitmap image optimized for SNES ROM hacking.
absl::StatusOr< DungeonSettings > GetDungeonSettings()
void SetSpriteChangedCallback(SpriteChangedCallback callback)
absl::Status UpdateItem(int item_id, const ItemData &item_data)
absl::Status DuplicateRoom(int source_room_id, int target_room_id)
absl::Status UpdateDoor(int door_id, const DoorData &door_data)
int GetCurrentRoom() const
absl::StatusOr< EntranceData > GetEntrance(int entrance_id)
absl::Status SaveRoom(int room_id)
std::unordered_map< int, DoorData > doors_
absl::Status ExportRoomToFile(int room_id, const std::string &file_path)
std::function< void(const std::vector< std::string > &errors)> ValidationCallback
std::unordered_map< int, EntranceData > entrances_
absl::Status ValidateRoom(int room_id)
void SetExternalRoom(Room *room)
absl::StatusOr< std::vector< SpriteData > > GetSpritesByType(DungeonEditorSystem::SpriteType type)
absl::Status SetRoomProperties(int room_id, const RoomProperties &properties)
absl::Status RemoveEntrance(int entrance_id)
RoomChangedCallback room_changed_callback_
absl::Status SetChestOpened(int chest_id, bool opened)
std::function< void(int sprite_id)> SpriteChangedCallback
absl::Status SaveDoorData()
std::unordered_map< int, SpriteData > sprites_
absl::Status SaveDungeon()
absl::StatusOr< std::vector< DoorData > > GetDoorsByRoom(int room_id)
std::function< void(int door_id)> DoorChangedCallback
absl::Status RemoveItem(int item_id)
absl::StatusOr< SpriteData > GetSprite(int sprite_id)
absl::Status SaveItemData()
void SetEditorMode(EditorMode mode)
DungeonSettings dungeon_settings_
SpriteChangedCallback sprite_changed_callback_
absl::Status MoveSprite(int sprite_id, int new_x, int new_y)
absl::Status LoadDoorData()
EntranceChangedCallback entrance_changed_callback_
absl::Status SetDoorKeyRequirement(int door_id, bool requires_key, int key_type)
absl::Status LoadSpriteData()
void SetRoomChangedCallback(RoomChangedCallback callback)
absl::Status SaveRoomData(int room_id)
void SetEntranceChangedCallback(EntranceChangedCallback callback)
std::vector< std::string > GetValidationErrors(int room_id)
ModeChangedCallback mode_changed_callback_
absl::StatusOr< RoomProperties > GetRoomProperties(int room_id)
std::shared_ptr< DungeonObjectEditor > object_editor_
absl::Status AddSprite(const SpriteData &sprite_data)
DungeonEditorSystem(Rom *rom, GameData *game_data=nullptr)
std::shared_ptr< DungeonObjectEditor > GetObjectEditor()
absl::Status LoadEntranceData()
absl::Status SaveSpriteData()
absl::Status SaveChestData()
absl::Status SetSpriteActive(int sprite_id, bool active)
void SetChestChangedCallback(ChestChangedCallback callback)
absl::Status SetCurrentRoom(int room_id)
absl::StatusOr< std::vector< ItemData > > GetItemsByType(DungeonEditorSystem::ItemType type)
absl::Status ConnectRooms(int room1_id, int room2_id, int x1, int y1, int x2, int y2)
absl::Status ValidateDungeon()
absl::StatusOr< gfx::Bitmap > RenderRoomPreview(int room_id, EditorMode mode)
absl::Status DeleteRoom(int room_id)
absl::Status RemoveDoor(int door_id)
std::vector< std::string > GetDungeonValidationErrors()
std::unordered_map< int, ChestData > chests_
std::unordered_map< int, ItemData > items_
absl::Status AddDoor(const DoorData &door_data)
absl::Status RemoveChest(int chest_id)
EditorMode GetEditorMode() const
std::function< void(int item_id)> ItemChangedCallback
absl::StatusOr< std::vector< EntranceData > > GetEntrancesByRoom(int room_id)
absl::Status SetDungeonSettings(const DungeonSettings &settings)
absl::Status ImportDungeonFromFile(const std::string &file_path)
absl::StatusOr< gfx::Bitmap > RenderRoom(int room_id)
ItemChangedCallback item_changed_callback_
EditorState editor_state_
absl::Status UpdateEntrance(int entrance_id, const EntranceData &entrance_data)
std::vector< UndoPoint > redo_history_
void SetModeChangedCallback(ModeChangedCallback callback)
absl::Status LoadItemData()
absl::Status Initialize()
absl::StatusOr< gfx::Bitmap > RenderDungeonMap()
absl::Status SetObjectEditorMode()
absl::Status DisconnectRooms(int room1_id, int room2_id)
ValidationCallback validation_callback_
std::unordered_map< int, RoomProperties > room_properties_
absl::Status ExportDungeonToFile(const std::string &file_path)
absl::Status LoadRoomData(int room_id)
absl::Status ReloadRoom(int room_id)
DoorChangedCallback door_changed_callback_
std::vector< UndoPoint > undo_history_
std::function< void(EditorMode mode)> ModeChangedCallback
absl::Status RemoveSprite(int sprite_id)
absl::StatusOr< std::vector< ChestData > > GetChestsByRoom(int room_id)
absl::Status LoadDungeon(int dungeon_id)
absl::StatusOr< std::vector< ItemData > > GetItemsByRoom(int room_id)
ChestChangedCallback chest_changed_callback_
std::function< void(int room_id)> RoomChangedCallback
absl::Status MoveItem(int item_id, int new_x, int new_y)
absl::StatusOr< ChestData > GetChest(int chest_id)
absl::StatusOr< DoorData > GetDoor(int door_id)
absl::Status SetChestItem(int chest_id, int item_id, int quantity)
absl::StatusOr< std::vector< SpriteData > > GetSpritesByRoom(int room_id)
absl::Status UpdateChest(int chest_id, const ChestData &chest_data)
void SetDoorChangedCallback(DoorChangedCallback callback)
std::function< void(int chest_id)> ChestChangedCallback
absl::Status LoadChestData()
absl::Status UpdateSprite(int sprite_id, const SpriteData &sprite_data)
absl::StatusOr< std::vector< EntranceData > > GetEntrancesByType(DungeonEditorSystem::EntranceType type)
absl::Status SetItemHidden(int item_id, bool hidden)
void SetItemChangedCallback(ItemChangedCallback callback)
absl::Status CreateRoom(int room_id, const std::string &name="")
absl::Status AddEntrance(const EntranceData &entrance_data)
absl::Status ImportRoomFromFile(const std::string &file_path, int room_id)
absl::Status SaveEntranceData()
absl::StatusOr< ItemData > GetItem(int item_id)
absl::Status AddChest(const ChestData &chest_data)
absl::StatusOr< Room > GetRoom(int room_id)
absl::Status SetDoorLocked(int door_id, bool locked)
void SetValidationCallback(ValidationCallback callback)
std::function< void(int entrance_id)> EntranceChangedCallback
absl::Status AddItem(const ItemData &item_data)
const std::vector< chest_data > & GetChests() const
absl::Status SaveObjects()
const std::vector< zelda3::Sprite > & GetSprites() const
absl::Status SaveSprites()
A class for managing sprites in the overworld and underworld.
struct chest_data chest_data
Legacy chest data structure.
constexpr int NumberOfRooms
Room LoadRoomFromRom(Rom *rom, int room_id)
std::unique_ptr< DungeonEditorSystem > CreateDungeonEditorSystem(Rom *rom, GameData *game_data)
Factory function to create dungeon editor system.
#define RETURN_IF_ERROR(expr)
Legacy chest data structure.
std::chrono::steady_clock::time_point last_save_time
DungeonEditorSystem::EntranceType type
DungeonEditorSystem::SpriteType type
std::unordered_map< std::string, std::string > properties