yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_editor_v2_context.cc
Go to the documentation of this file.
3
4#include <string>
5#include <utility>
6
7#include "absl/strings/str_format.h"
13#include "core/project.h"
14#include "zelda3/dungeon/room.h"
15
16namespace yaze::editor {
17
18absl::StatusOr<zelda3::RoomsetUsageReport>
20 if (rom_ == nullptr || !rom_->is_loaded() || game_data_ == nullptr) {
21 return absl::FailedPreconditionError(
22 "Load a ROM to inspect shared graphics.");
23 }
24 auto room_headers = zelda3::CollectRoomGfx(*rom_);
25 // A materialized Room may still have default header values. Only loaded
26 // rooms can replace the ROM census with unsaved editor selections.
27 rooms_.ForEachLoaded([&](int id, const zelda3::Room& room) {
28 room_headers[id] = {room.blockset(), room.spriteset()};
29 });
30 return zelda3::DescribeRoomsetUsage(*rom_, *game_data_, roomset, room_headers,
33 : nullptr);
34}
35
37 if (!status_bar)
38 return;
39 const int room_id = current_room_id();
40
41 if (room_id >= 0) {
43 room_opts.tooltip = "Click to refocus viewer on this room";
44 room_opts.on_click = [this, room_id]() {
48 }
49 };
50 status_bar->SetCustomSegment("Room", absl::StrFormat("0x%03X", room_id),
51 std::move(room_opts));
52 }
53
54 const int loaded = LoadedRoomCount();
55 const int total = TotalRoomCount();
56 if (total > 0) {
57 StatusBarSegmentOptions rooms_opts;
58 rooms_opts.tooltip =
59 absl::StrFormat("%d rooms loaded of %d total", loaded, total);
60 status_bar->SetCustomSegment("Rooms",
61 absl::StrFormat("%d/%d", loaded, total),
62 std::move(rooms_opts));
63 }
64
65 if (room_id >= 0 && CurrentRoomHasPendingChanges()) {
66 StatusBarSegmentOptions current_room_opts;
67 current_room_opts.tooltip =
68 "This room has pending editor changes that are not yet applied to the "
69 "ROM buffer.";
70 status_bar->SetCustomSegment("RoomState", "Pending",
71 std::move(current_room_opts));
72 }
73
75 mode_opts.tooltip = "Click to toggle Workbench / Standalone workflow";
76 mode_opts.on_click = [this]() {
78 };
82 std::move(mode_opts));
83}
84
85// Builds the shared editor-context snapshot for the current dungeon room:
86// identity (owner id, title, workflow mode), room metadata and object counts,
87// diagnostics, and the capabilities/actions the right sidebar can offer.
88// Graphics fields include the loader's default entrance when census ownership
89// cannot resolve an entrance. Rooms that
90// are not materialized yet only get a "room_not_loaded" diagnostic.
92 // Identity: stable owner id, room label title, workflow mode subtitle.
93 EditorContextSnapshot snapshot;
94 snapshot.category = "Dungeon";
95 snapshot.semantic_owner =
96 absl::StrFormat("dungeon.room.%03X", current_room_id_);
97 snapshot.title =
98 absl::StrFormat("Room 0x%03X ยท %s", current_room_id_,
104
107 : nullptr;
108 if (room == nullptr) {
109 snapshot.diagnostics.push_back({
110 .id = "room_not_loaded",
112 .message = "Room data has not been materialized yet.",
113 });
114 } else {
115 // The room header selects secondary sheets, not necessarily the main set.
116 const DungeonRenderContext render_context =
118 const auto& fallback = render_context.default_entrance;
119 snapshot.metadata = {
120 {.id = "graphics_entrance",
121 .label = "Graphics entrance",
122 .value =
123 render_context.uses_entrance()
125 : fallback.entrance_id >= 0
126 ? DungeonEntrancePreviewLabel(fallback.entrance_id +
128 " (default)"
129 : "None"},
130 {.id = "graphics_source",
131 .label = "Graphics source",
132 .value = DungeonRenderContextSourceName(render_context)},
133 {.id = "dungeon_owner",
134 .label = "Dungeon owner",
135 .value = render_context.owner_name.empty()
136 ? "Unresolved"
137 : render_context.owner_name},
138 {.id = "blockset",
139 .label = "Blockset",
140 .value = absl::StrFormat("main 0x%02X / room 0x%02X",
141 render_context.effective_blockset(),
142 room->blockset())},
143 {.id = "spriteset",
144 .label = "Spriteset",
145 .value = absl::StrFormat("0x%02X", room->spriteset())},
146 {.id = "palette",
147 .label = "Palette",
148 .value = absl::StrFormat("0x%02X", room->palette())},
149 };
151 if (dependencies_.project != nullptr) {
153 }
155 snapshot, zelda3::DescribeDungeonGraphicsContext(*room, identity));
156 snapshot.counts = {
157 {.id = "objects",
158 .label = "Objects",
159 .value = std::to_string(room->GetTileObjectCount())},
160 {.id = "sprites",
161 .label = "Sprites",
162 .value = std::to_string(room->GetSprites().size())},
163 {.id = "doors",
164 .label = "Doors",
165 .value = std::to_string(room->GetDoors().size())},
166 {.id = "stairs",
167 .label = "Stairs",
168 .value = std::to_string(room->GetStairs().size())},
169 {.id = "chests",
170 .label = "Chests",
171 .value = std::to_string(room->GetChests().size())},
172 {.id = "pot_items",
173 .label = "Pot items",
174 .value = std::to_string(room->GetPotItems().size())},
175 };
176 // A default entrance is a preview inference, not proof of a gameplay route.
177 snapshot.has_pending_changes = room->HasUnsavedChanges();
178 snapshot.pending_label =
179 snapshot.has_pending_changes ? "Room has unapplied changes" : "";
180 if (render_context.source == DungeonRenderContextSource::kAmbiguous) {
181 snapshot.diagnostics.push_back({
182 .id = "ambiguous_dungeon_graphics_owner",
184 .message = absl::StrFormat(
185 "Owner entrances disagree on main GFX. Preview: %s (0x%02X). "
186 "Select the entrance used in game to verify the walls.",
187 DungeonRenderContextSourceName(render_context),
188 render_context.effective_blockset()),
189 .action_id = "open_entrance",
190 });
191 } else if (!dungeon_render_context_census_error_.empty()) {
192 snapshot.diagnostics.push_back({
193 .id = "dungeon_owner_unavailable",
195 .message = absl::StrFormat("Dungeon ownership is unavailable: %s",
197 });
198 }
199 // Entrance camera: unsafe geometry is an error; a safe camera that differs
200 // from the derived values is a warning (repair stays an explicit action).
201 if (const auto camera = GetEntranceCameraState(current_entrance_id_);
202 camera.has_value()) {
203 const auto validation = ValidateDungeonEntranceCamera(*camera);
204 snapshot.metadata.push_back({
205 .id = "entrance_camera",
206 .label = "Entrance camera",
207 .value = !validation.geometry_valid() ? "Unsafe geometry"
208 : validation.matches_derived()
209 ? "Matches player position"
210 : absl::StrFormat("%zu derived difference(s)",
211 validation.differences.size()),
212 });
213 if (!validation.geometry_valid()) {
214 snapshot.diagnostics.push_back({
215 .id = "entrance_camera_invalid",
217 .message = validation.errors.front(),
218 .action_id = "open_entrance",
219 });
220 } else if (!validation.matches_derived()) {
221 snapshot.diagnostics.push_back({
222 .id = "entrance_camera_custom",
224 .message =
225 "Entrance camera is safe but differs from derived values.",
226 .action_id = "open_entrance",
227 });
228 }
229 }
230 }
231
232 // Capabilities and sidebar actions; minecart tracks only when the project's
233 // hack manifest declares a track layout.
234 snapshot.capabilities = {
235 "dungeon.room_matrix",
236 "dungeon.entrances",
237 "dungeon.room_graphics",
238 };
239 if (dependencies_.project != nullptr &&
242 .source.has_value()) {
243 snapshot.capabilities.push_back("minecart_tracks");
244 }
245 snapshot.actions = {
246 {.id = "open_matrix",
247 .label = "Room Matrix",
248 .target = "dungeon.room_matrix"},
249 {.id = "open_entrance",
250 .label = "Entrances",
251 .target = "dungeon.entrance_properties"},
252 {.id = "open_graphics",
253 .label = "Room Graphics",
254 .target = "dungeon.room_graphics"},
255 };
256 if (snapshot.HasCapability("minecart_tracks")) {
257 snapshot.actions.push_back({
258 .id = "open_minecart",
259 .label = "Minecart Tracks",
260 .target = kMinecartTrackEditorId,
261 });
262 }
263 return snapshot;
264}
265
266} // namespace yaze::editor
void Publish(const T &event)
Definition event_bus.h:35
bool is_loaded() const
Definition rom.h:187
const MinecartTrackLayout & minecart_track_layout() const
bool loaded() const
Check if the manifest has been loaded.
EditorContextSnapshot BuildContextSnapshot() const override
DungeonRenderContext ResolveDungeonRenderContextForRoom(int room_id) const
std::function< const zelda3::Overworld *()> room_graphics_overworld_provider_
void ToggleWorkbenchWorkflowMode(bool show_toast=true)
void ContributeStatus(StatusBar *status_bar) override
absl::StatusOr< zelda3::RoomsetUsageReport > GetRoomGraphicsUsage(int roomset) const
std::optional< DungeonEntranceCameraState > GetEntranceCameraState(int slot_index) const
static bool IsValidRoomId(int room_id)
static constexpr const char * kMinecartTrackEditorId
zelda3::Room * GetIfMaterialized(int room_id)
EditorDependencies dependencies_
Definition editor.h:370
A session-aware status bar displayed at the bottom of the application.
Definition status_bar.h:57
void SetCustomSegment(const std::string &key, const std::string &value)
Set a custom segment with key-value pair.
void SetEditorMode(const std::string &mode)
Set the current editor mode or tool.
const std::vector< chest_data > & GetChests() const
Definition room.h:323
uint8_t blockset() const
Definition room.h:1015
bool HasUnsavedChanges() const
Definition room.h:662
const std::vector< Door > & GetDoors() const
Definition room.h:413
size_t GetTileObjectCount() const
Definition room.h:530
const std::vector< staircase > & GetStairs() const
Definition room.h:330
uint8_t palette() const
Definition room.h:1019
uint8_t spriteset() const
Definition room.h:1018
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:316
const std::vector< PotItem > & GetPotItems() const
Definition room.h:429
std::string GetRoomLabel(const project::YazeProject *project, int room_id)
constexpr const char * kStandalone
constexpr const char * kWorkbench
Editors are the view controllers for the application.
std::string DungeonEntrancePreviewLabel(int slot)
const char * DungeonRenderContextSourceName(DungeonRenderContextSource source)
void AppendResolvedGraphicsContext(EditorContextSnapshot &snapshot, const zelda3::ResolvedGraphicsContext &context)
DungeonEntranceCameraValidation ValidateDungeonEntranceCamera(const DungeonEntranceCameraState &state)
absl::StatusOr< RoomsetUsageReport > DescribeRoomsetUsage(Rom &rom, GameData &game_data, int roomset, const std::map< int, RoomGfxInfo > &rooms, const Overworld *live_overworld)
ResolvedGraphicsContext DescribeDungeonGraphicsContext(const Room &room, const GraphicsContextIdentity &identity)
std::map< int, RoomGfxInfo > CollectRoomGfx(const Rom &rom)
constexpr int kNumDungeonSpawnPoints
std::optional< Source > source
zelda3::RoomDefaultEntrance default_entrance
std::vector< EditorContextValue > counts
std::vector< EditorContextAction > actions
bool HasCapability(const std::string &capability) const
std::vector< EditorContextDiagnostic > diagnostics
std::vector< EditorContextValue > metadata
project::YazeProject * project
Definition editor.h:174
GlobalEditorContext * global_context
Definition editor.h:176
static JumpToRoomRequestEvent Create(int room, size_t session=0)
Optional behavior for an interactive status bar segment.
Definition status_bar.h:27
std::function< void()> on_click
Definition status_bar.h:28
core::HackManifest hack_manifest
Definition project.h:360