yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_object.h
Go to the documentation of this file.
1#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H
2#define YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H
3
4#include <cstdint>
5#include <string>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
11#include "rom/rom.h"
13
14namespace yaze {
15namespace zelda3 {
16
18
19enum Sorting {
20 All = 0,
21 Wall = 1,
26 Floors = 32,
27 SortStairs = 64
28};
29
30enum class ObjectOption {
31 Nothing = 0,
32 Door = 1,
33 Chest = 2,
34 Block = 4,
35 Torch = 8,
36 Bgr = 16,
37 Stairs = 32
38};
39
44
45constexpr int kRoomObjectSubtype1 = 0x8000; // JP = Same
46constexpr int kRoomObjectSubtype2 = 0x83F0; // JP = Same
47constexpr int kRoomObjectSubtype3 = 0x84F0; // JP = Same
48constexpr int kRoomObjectTileAddress = 0x1B52; // JP = Same
49constexpr int kRoomObjectTileAddressFloor = 0x1B5A; // JP = Same
50
52 public:
53 enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
54
55 // ROM room object stream index (0=primary, 1=BG2 overlay, 2=BG1 overlay).
56 // Same numeric values as LayerType for encode buckets; use only when the
57 // object came from the room object stream (see `layer_` comment below).
58 enum ListIndex : uint8_t {
62 };
63
64 RoomObject(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer = 0)
65 : id_(id),
66 x_(x),
67 y_(y),
68 size_(size),
69 layer_(static_cast<LayerType>(layer)),
70 nx_(x),
71 ny_(y),
72 ox_(x),
73 oy_(y),
74 width_(16),
75 height_(16),
76 rom_(nullptr) {}
77
78 void SetRom(Rom* rom) { rom_ = rom; }
79 Rom* rom() const { return rom_; }
80 auto mutable_rom() { return rom_; }
81
82 // Position setters and getters
83 void set_id(int16_t id);
84 void set_x(uint8_t x) { x_ = x; }
85 void set_y(uint8_t y) { y_ = y; }
86 void set_size(uint8_t size) { size_ = size; }
87 uint8_t x() const { return x_; }
88 uint8_t y() const { return y_; }
89 uint8_t size() const { return size_; }
90
91 // Ensures tiles_ is populated with a basic set based on ROM tables so we can
92 // preview/draw objects without needing full emulator execution.
93 void EnsureTilesLoaded();
94
95 // Load tiles using the new ObjectParser
96 absl::Status LoadTilesWithParser();
97
98 // Getter for tiles
99 const std::vector<gfx::TileInfo>& tiles() const {
100 const_cast<RoomObject*>(this)->EnsureTilesLoaded();
101 return tiles_;
102 }
103 std::vector<gfx::TileInfo>& mutable_tiles() {
105 return tiles_;
106 }
107
108 // Get tile data through Arena system - returns references, not copies
109 absl::StatusOr<std::span<const gfx::TileInfo>> GetTiles() const;
110
111 // Get individual tile by index - uses Arena lookup
112 absl::StatusOr<const gfx::TileInfo*> GetTile(int index) const;
113
114 // Get tile count without loading all tiles
115 int GetTileCount() const;
116
117 // ============================================================================
118 // Object Encoding/Decoding (Phase 1, Task 1.1)
119 // ============================================================================
120
121 // 3-byte object encoding structure
122 struct ObjectBytes {
123 uint8_t b1;
124 uint8_t b2;
125 uint8_t b3;
126 };
127
128 // Decode object from 3-byte ROM format
129 // Type1: xxxxxxss yyyyyyss iiiiiiii (ID 0x000-0x0F7)
130 // Type2: 111111xx xxxxyyyy yyiiiiii (ID 0x100-0x13F)
131 // Type3: xxxxxxii yyyyyyii 11111iii (ID 0xF80-0xFFF)
132 static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
133 uint8_t layer);
134
135 // Encode object to 3-byte ROM format
137
138 // Determine object type from bytes (1, 2, or 3)
139 static int DetermineObjectType(uint8_t b1, uint8_t b3);
140
141 // Get layer from LayerType enum
142 uint8_t GetLayerValue() const { return static_cast<uint8_t>(layer_); }
143
144 // ============================================================================
145
146 // NOTE: Legacy ZScream methods removed. Modern rendering uses:
147 // - ObjectParser for loading tiles from ROM
148 // - ObjectDrawer for rendering tiles to BackgroundBuffer
149
150 auto options() const { return options_; }
152
153 // For `ObjectOption::Block` objects only. Holds the current committed
154 // 0-based index of the corresponding 4-byte global pushable-block entry.
155 // LoadBlocks initializes it from ROM; a successful structural save rebases
156 // it after deleted entries are compacted. This preserves vanilla's
157 // interleaved authoring order on later no-op saves. User-added blocks use
158 // kBlockLoadOrderNew until their first successful save assigns a committed
159 // tail slot. Ignored for non-block objects.
160 int block_load_order() const { return block_load_order_; }
161 void set_block_load_order(int order) { block_load_order_ = order; }
162
163 // Pushable-block bit 14 is independent of `layer_`: `layer_` stores the
164 // bit-13 draw target, while this selector is retained for runtime pit and
165 // collision behavior. It is preserved by edits and copies unless changed
166 // explicitly by a future dedicated behavior control.
167 uint8_t block_behavior_layer() const { return block_behavior_layer_; }
168 void set_block_behavior_layer(uint8_t layer) {
169 block_behavior_layer_ = layer & 0x01;
170 }
171
172 // Lightable-torch bit 14 is reserved by the runtime table. Keep it separate
173 // from `layer_`, which stores the bit-13 BG1/BG2 draw target, so ordinary
174 // edits and copies do not discard authoring data that yaze does not expose.
175 uint8_t torch_reserved_bit() const { return torch_reserved_bit_; }
176 void set_torch_reserved_bit(uint8_t reserved) {
177 torch_reserved_bit_ = reserved & 0x01;
178 }
179
180 // Copying an editor entity must not claim the source block's ROM table slot.
181 // Ordinary C++ copies intentionally preserve load order for move validation,
182 // undo snapshots, and rendering; creation paths call this method explicitly.
184 RoomObject copy = *this;
186 return copy;
187 }
188
189 // Explicit editor/custom-object override. Built-in objects derive their
190 // layer routing from DrawRoutineRegistry instead of duplicating ID lists.
191 bool all_bgs_ = false;
192 bool lit_ = false;
193
194 int16_t id_;
195 uint8_t x_;
196 uint8_t y_;
197 uint8_t size_;
198 uint8_t nx_;
199 uint8_t ny_;
200 uint8_t ox_;
201 uint8_t oy_;
202 uint8_t z_ = 0;
203 uint8_t previous_size_ = 0;
204 // Size nibble bits captured from object encoding (0..3 each) for heuristic
205 // orientation and sizing decisions.
206 uint8_t size_x_bits_ = 0;
207 uint8_t size_y_bits_ = 0;
208
211 int offset_x_ = 0;
212 int offset_y_ = 0;
213
214 std::string name_;
215
216 std::vector<uint8_t> preview_object_data_;
217
218 // Tile data storage - using Arena system for efficient memory management
219 // Instead of copying Tile16 vectors, we store references to Arena-managed
220 // data
221 mutable std::vector<gfx::TileInfo> tiles_; // Individual tiles like ZScream
222 mutable bool tiles_loaded_ = false;
223 mutable int tile_count_ = 0;
224 mutable int tile_data_ptr_ = -1; // Pointer to tile data in ROM
225
226 // For room-stream objects this is the object *list* index (0=primary, 1=BG2
227 // overlay, 2=BG1 overlay), matching `Room::EncodeObjects` buckets; map it
228 // through MapRoomObjectListIndexToDrawLayer() before drawing. For torches and
229 // pushable blocks, values 0/1 are the special-table draw target. Pushable
230 // block behavior and the torch reserved bit use independent fields.
233
234 // Sentinel for blocks that were not loaded from ROM (user-added in the
235 // editor). `SaveAllBlocks` keeps these at the tail of the encoded
236 // buffer in creation order so they don't get sorted in front of the
237 // vanilla entries.
238 static constexpr int kBlockLoadOrderNew = -1;
242
244
245 private:
246 void InvalidateTileCache();
247 bool IsTrackedTileCacheCurrent() const;
250
251 // Only parser-populated caches are tracked. Tests, custom previews, and
252 // other callers that inject tiles directly retain their cache across ROM
253 // revisions because no ROM provenance was claimed for those tiles.
254 mutable const Rom* tile_cache_rom_identity_ = nullptr;
255 mutable uint64_t tile_cache_revision_ = 0;
256 mutable bool tile_cache_tracked_ = false;
257};
258
259// Room-object size is persisted only for Type 1 objects. Type 2 has no size
260// field, while Type 3 reuses those bits as part of the object ID; its canonical
261// value mirrors DecodeObjectFromBytes rather than the ID's raw low nibble.
262bool IsRoomObjectSizeEditable(int object_id);
263uint8_t CanonicalRoomObjectSize(int object_id, uint8_t requested_size);
264uint8_t DefaultRoomObjectSizeForPlacement(int object_id);
265
266// Geometric resizing is distinct from the persisted size field: active custom
267// objects use that field as a variant selector, not as width/height.
268bool IsRoomObjectResizable(int object_id);
269// Tile stride for each packed two-bit axis, or zero for scalar/fixed objects.
270int RoomObjectSizeAxisStep(int object_id);
271// Physical tile extent for a packed axis, including fixed borders. Returns zero
272// for scalar/fixed objects and active custom variants.
273int RoomObjectSizeAxisTiles(int object_id, uint8_t size,
274 bool horizontal = false);
275// Packed objects resize height by default, width when horizontal is true.
276uint8_t ResizeRoomObjectByDelta(int object_id, uint8_t size, int delta,
277 bool horizontal = false);
278
279// Stateful small and big chests advance the engine's per-room chest-event
280// index. Fixed-open chest graphics (F9A/FB2) and the FF5 minigame chest do not.
281inline constexpr bool IsStatefulChestObjectId(int object_id) {
282 return object_id == 0xF99 || object_id == 0xFB1;
283}
284
285// Validates that an ordinary room-object stream entry can be encoded without
286// changing its identity or colliding with the stream's list/door markers.
287// Torches and pushable blocks use separate tables; callers must exclude them
288// with UsesRoomObjectStream().
289absl::Status ValidateRoomObjectStreamEntryForSave(const RoomObject& object);
290
291// USDASM (LoadAndBuildRoom): three room-object lists separated by $FFFF after the
292// floor/layout header. `RoomObject::layer_` stores that list index (0, 1, 2) for
293// encode round-trip — not the same as “which SNES buffer”. After
294// `RoomDraw_DrawFloors`, the layout pass and the primary room-object list both
295// still target the upper/BG1 tilemap pointers; only the post-$FFFF overlay pass
296// explicitly swaps to lower/BG2, and the final pass swaps back to upper/BG1.
297// Use this when building draw lists.
299 uint8_t list_index) {
300 if (list_index > 2) {
301 list_index = 2;
302 }
303 if (list_index == 1) {
305 }
306 return list_index == 0 ? RoomObject::LayerType::BG1
308}
309
310// NOTE: Legacy Subtype1, Subtype2, Subtype3 classes removed.
311// These were ported from ZScream but are no longer used.
312// Modern code uses: ObjectParser + ObjectDrawer + ObjectRenderer
313
314constexpr static inline const char* Type1RoomObjectNames[] = {
315 "Ceiling ↔",
316 "Wall (top, north) ↔",
317 "Wall (top, south) ↔",
318 "Wall (bottom, north) ↔",
319 "Wall (bottom, south) ↔",
320 "Wall columns (north) ↔",
321 "Wall columns (south) ↔",
322 "Deep wall (north) ↔",
323 "Deep wall (south) ↔",
324 "Diagonal wall A ◤ (top) ↔",
325 "Diagonal wall A ◣ (top) ↔",
326 "Diagonal wall A ◥ (top) ↔",
327 "Diagonal wall A ◢ (top) ↔",
328 "Diagonal wall B ◤ (top) ↔",
329 "Diagonal wall B ◣ (top) ↔",
330 "Diagonal wall B ◥ (top) ↔",
331 "Diagonal wall B ◢ (top) ↔",
332 "Diagonal wall C ◤ (top) ↔",
333 "Diagonal wall C ◣ (top) ↔",
334 "Diagonal wall C ◥ (top) ↔",
335 "Diagonal wall C ◢ (top) ↔",
336 "Diagonal wall A ◤ (bottom) ↔",
337 "Diagonal wall A ◣ (bottom) ↔",
338 "Diagonal wall A ◥ (bottom) ↔",
339 "Diagonal wall A ◢ (bottom) ↔",
340 "Diagonal wall B ◤ (bottom) ↔",
341 "Diagonal wall B ◣ (bottom) ↔",
342 "Diagonal wall B ◥ (bottom) ↔",
343 "Diagonal wall B ◢ (bottom) ↔",
344 "Diagonal wall C ◤ (bottom) ↔",
345 "Diagonal wall C ◣ (bottom) ↔",
346 "Diagonal wall C ◥ (bottom) ↔",
347 "Diagonal wall C ◢ (bottom) ↔",
348 "Platform stairs ↔",
349 "Rail ↔",
350 "Pit edge ┏━┓ A (north) ↔",
351 "Pit edge ┏━┓ B (north) ↔",
352 "Pit edge ┏━┓ C (north) ↔",
353 "Pit edge ┏━┓ D (north) ↔",
354 "Pit edge ┏━┓ E (north) ↔",
355 "Pit edge ┗━┛ (south) ↔",
356 "Pit edge ━━━ (south) ↔",
357 "Pit edge ━━━ (north) ↔",
358 "Pit edge ━━┛ (south) ↔",
359 "Pit edge ┗━━ (south) ↔",
360 "Pit edge ━━┓ (north) ↔",
361 "Pit edge ┏━━ (north) ↔",
362 "Rail wall (north) ↔",
363 "Rail wall (south) ↔",
364 "Nothing",
365 "Nothing",
366 "Carpet ↔",
367 "Carpet trim ↔",
368 "Hole in wall", // Generic opening or doorway
369 "Drapes (north) ↔",
370 "Drapes (west, odd) ↔",
371 "Statues ↔",
372 "Columns ↔",
373 "Wall decors (north) ↔",
374 "Wall decors (south) ↔",
375 "Chairs in pairs ↔",
376 "Tall torches ↔",
377 "Supports (north) ↔",
378 "Water edge ┏━┓ (concave) ↔",
379 "Water edge ┗━┛ (concave) ↔",
380 "Water edge ┏━┓ (convex) ↔",
381 "Water edge ┗━┛ (convex) ↔",
382 "Water edge ┏━┛ (concave) ↔",
383 "Water edge ┗━┓ (concave) ↔",
384 "Water edge ┗━┓ (convex) ↔",
385 "Water edge ┏━┛ (convex) ↔",
386 "Waterfall A ↔",
387 "Waterfall B ↔",
388 "Floor tiles 4x2 A ↔",
389 "Floor tiles 4x2 B ↔",
390 "Supports (south) ↔",
391 "Bar ↔",
392 "Shelf A ↔",
393 "Shelf B ↔",
394 "Shelf C ↔",
395 "Somaria path ↔",
396 "Cannon hole A (north) ↔",
397 "Cannon hole A (south) ↔",
398 "Pipe path ↔",
399 "Nothing",
400 "Wall torches (north) ↔",
401 "Wall torches (south) ↔",
402 "Nothing",
403 "Nothing",
404 "Nothing",
405 "Nothing",
406 "Cannon hole B (north) ↔",
407 "Cannon hole B (south) ↔",
408 "Thick rail ↔",
409 "Blocks ↔",
410 "Long rail ↔",
411 "Ceiling ↕",
412 "Wall (top, west) ↕",
413 "Wall (top, east) ↕",
414 "Wall (bottom, west) ↕",
415 "Wall (bottom, east) ↕",
416 "Wall columns (west) ↕",
417 "Wall columns (east) ↕",
418 "Deep wall (west) ↕",
419 "Deep wall (east) ↕",
420 "Rail ↕",
421 "Pit edge (west) ↕",
422 "Pit edge (east) ↕",
423 "Rail wall (west) ↕",
424 "Rail wall (east) ↕",
425 "Nothing",
426 "Nothing",
427 "Carpet ↕",
428 "Carpet trim ↕",
429 "Nothing",
430 "Drapes (west) ↕",
431 "Drapes (east) ↕",
432 "Columns ↕",
433 "Wall decors (west) ↕",
434 "Wall decors (east) ↕",
435 "Supports (west) ↕",
436 "Water edge (west) ↕",
437 "Water edge (east) ↕",
438 "Supports (east) ↕",
439 "Somaria path ↕",
440 "Pipe path ↕",
441 "Nothing",
442 "Wall torches (west) ↕",
443 "Wall torches (east) ↕",
444 "Wall decors tight A (west) ↕",
445 "Wall decors tight A (east) ↕",
446 "Wall decors tight B (west) ↕",
447 "Wall decors tight B (east) ↕",
448 "Cannon hole (west) ↕",
449 "Cannon hole (east) ↕",
450 "Tall torches ↕",
451 "Thick rail ↕",
452 "Blocks ↕",
453 "Long rail ↕",
454 "Jump ledge (west) ↕",
455 "Jump ledge (east) ↕",
456 "Rug trim (west) ↕",
457 "Rug trim (east) ↕",
458 "Bar ↕",
459 "Wall flair (west) ↕",
460 "Wall flair (east) ↕",
461 "Blue pegs ↕",
462 "Orange pegs ↕",
463 "Invisible floor ↕",
464 "Fake pots ↕",
465 "Hammer pegs ↕",
466 "Nothing",
467 "Nothing",
468 "Nothing",
469 "Nothing",
470 "Nothing",
471 "Nothing",
472 "Nothing",
473 "Nothing",
474 "Nothing",
475 "Diagonal ceiling A ◤",
476 "Diagonal ceiling A ◣",
477 "Diagonal ceiling A ◥",
478 "Diagonal ceiling A ◢",
479 "Pit ⇲",
480 "Diagonal layer 2 mask A ◤",
481 "Diagonal layer 2 mask A ◣",
482 "Diagonal layer 2 mask A ◥",
483 "Diagonal layer 2 mask A ◢",
484 "Diagonal layer 2 mask B ◤", // TODO: VERIFY
485 "Diagonal layer 2 mask B ◣", // TODO: VERIFY
486 "Diagonal layer 2 mask B ◥", // TODO: VERIFY
487 "Diagonal layer 2 mask B ◢", // TODO: VERIFY
488 "Nothing",
489 "Nothing",
490 "Nothing",
491 "Jump ledge (north) ↔",
492 "Jump ledge (south) ↔",
493 "Rug ↔",
494 "Rug trim (north) ↔",
495 "Rug trim (south) ↔",
496 "Archery game curtains ↔",
497 "Wall flair (north) ↔",
498 "Wall flair (south) ↔",
499 "Blue pegs ↔",
500 "Orange pegs ↔",
501 "Invisible floor ↔",
502 "Fake pressure plates ↔",
503 "Fake pots ↔",
504 "Hammer pegs ↔",
505 "Nothing",
506 "Nothing",
507 "Ceiling (large) ⇲",
508 "Chest platform (tall) ⇲",
509 "Layer 2 pit mask (large) ⇲",
510 "Layer 2 pit mask (medium) ⇲",
511 "Floor 1 ⇲",
512 "Floor 3 ⇲",
513 "Layer 2 mask (large) ⇲",
514 "Floor 4 ⇲",
515 "Water floor ⇲ ",
516 "Flood water (medium) ⇲ ",
517 "Conveyor floor ⇲ ",
518 "Nothing",
519 "Nothing",
520 "Moving wall (west) ⇲",
521 "Moving wall (east) ⇲",
522 "Nothing",
523 "Nothing",
524 "Icy floor A ⇲",
525 "Icy floor B ⇲",
526 "Wall moved check A (logic)",
527 "Wall moved check B (logic)",
528 "Wall moved check C (logic)",
529 "Wall moved check D (logic)",
530 "Layer 2 mask (medium) ⇲",
531 "Flood water (large) ⇲",
532 "Layer 2 swim mask ⇲",
533 "Flood water B (large) ⇲",
534 "Floor 2 ⇲",
535 "Chest platform (short) ⇲",
536 "Table / rock ⇲",
537 "Spike blocks ⇲",
538 "Spiked floor ⇲",
539 "Floor 7 ⇲",
540 "Tiled floor ⇲",
541 "Rupee floor ⇲",
542 "Conveyor upwards ⇲",
543 "Conveyor downwards ⇲",
544 "Conveyor leftwards ⇲",
545 "Conveyor rightwards ⇲",
546 "Heavy current water ⇲",
547 "Floor 10 ⇲",
548 "Nothing",
549 "Nothing",
550 "Nothing",
551 "Nothing",
552 "Nothing",
553 "Nothing",
554 "Nothing",
555 "Nothing",
556 "Nothing",
557 "Nothing",
558 "Nothing",
559 "Nothing",
560 "Nothing",
561 "Nothing",
562 "Nothing",
563};
564
565constexpr static inline const char* Type2RoomObjectNames[] = {
566 "Corner (top, concave) ▛",
567 "Corner (top, concave) ▙",
568 "Corner (top, concave) ▜",
569 "Corner (top, concave) ▟",
570 "Corner (top, convex) ▟",
571 "Corner (top, convex) ▜",
572 "Corner (top, convex) ▙",
573 "Corner (top, convex) ▛",
574 "Corner (bottom, concave) ▛",
575 "Corner (bottom, concave) ▙",
576 "Corner (bottom, concave) ▜",
577 "Corner (bottom, concave) ▟",
578 "Corner (bottom, convex) ▟",
579 "Corner (bottom, convex) ▜",
580 "Corner (bottom, convex) ▙",
581 "Corner (bottom, convex) ▛",
582 "Kinked corner north (bottom) ▜",
583 "Kinked corner south (bottom) ▟",
584 "Kinked corner north (bottom) ▛",
585 "Kinked corner south (bottom) ▙",
586 "Kinked corner west (bottom) ▙",
587 "Kinked corner west (bottom) ▛",
588 "Kinked corner east (bottom) ▟",
589 "Kinked corner east (bottom) ▜",
590 "Deep corner (concave) ▛",
591 "Deep corner (concave) ▙",
592 "Deep corner (concave) ▜",
593 "Deep corner (concave) ▟",
594 "Large brazier",
595 "Statue",
596 "Star tile (disabled)",
597 "Star tile (enabled)",
598 "Small torch (lit)",
599 "Barrel",
600 "Statue (back)",
601 "Table",
602 "Fairy statue",
603 "Potted plant",
604 "Stool",
605 "Chair",
606 "Bed",
607 "Fireplace",
608 "Mario portrait",
609 "Rightwards 2x2 decor",
610 "Rightwards 6x3 decor",
611 "Interroom stairs (up)",
612 "Interroom stairs (down)",
613 "Interroom stairs B (down)",
614 "Intraroom stairs north B", // TODO: VERIFY LAYER HANDLING
615 "Intraroom stairs north (separate layers)",
616 "Intraroom stairs north (merged layers)",
617 "Intraroom stairs north (swim layer)",
618 "Block",
619 "Water-hop stairs A",
620 "Water-hop stairs B",
621 "Dam floodgate",
622 "Interroom spiral stairs up (top)",
623 "Interroom spiral stairs down (top)",
624 "Interroom spiral stairs up (bottom)",
625 "Interroom spiral stairs down (bottom)",
626 "Sanctuary wall (north)",
627 "Sanctuary pew (right end)",
628 "Pew",
629 "Magic bat altar",
630};
631
632constexpr static inline const char* Type3RoomObjectNames[] = {
633 "Waterfall face (empty)",
634 "Waterfall face (short)",
635 "Waterfall face (long)",
636 "Somaria path endpoint",
637 "Somaria path intersection ╋",
638 "Somaria path corner ┏",
639 "Somaria path corner ┗",
640 "Somaria path corner ┓",
641 "Somaria path corner ┛",
642 "Somaria path intersection ┳",
643 "Somaria path intersection ┻",
644 "Somaria path intersection ┣",
645 "Somaria path intersection ┫",
646 "Prison cell bars (alt)",
647 "Somaria path 2-way endpoint",
648 "Somaria path crossover",
649 "Babasu hole (north)",
650 "Babasu hole (south)",
651 "9 blue rupees",
652 "Telepathy tile",
653 "Unused / Warp door",
654 "Kholdstare's shell",
655 "Hammer peg",
656 "Prison cell",
657 "Big key lock",
658 "Chest",
659 "Chest (open)",
660 "Intraroom stairs south", // TODO: VERIFY LAYER HANDLING
661 "Intraroom stairs south (separate layers)",
662 "Intraroom stairs south (merged layers)",
663 "Interroom straight stairs up (north, top)",
664 "Interroom straight stairs down (north, top)",
665 "Interroom straight stairs up (south, top)",
666 "Interroom straight stairs down (south, top)",
667 "Deep corner (convex) ▟",
668 "Deep corner (convex) ▜",
669 "Deep corner (convex) ▙",
670 "Deep corner (convex) ▛",
671 "Interroom straight stairs up (north, bottom)",
672 "Interroom straight stairs down (north, bottom)",
673 "Interroom straight stairs up (south, bottom)",
674 "Interroom straight stairs down (south, bottom)",
675 "Lamp cones",
676 "Single 2x2 decor A",
677 "Liftable large block",
678 "Agahnim's altar",
679 "Agahnim's boss room",
680 "Pot",
681 "Single 2x2 decor B",
682 "Big chest",
683 "Big chest (open)",
684 "Intraroom stairs south (swim layer)",
685 "Intraroom stairs south (long)",
686 "Ladder (north)",
687 "Ladder (south)",
688 "4x4 decor A ↔",
689 "4x4 decor B ↔",
690 "4x4 decor C ↔",
691 "Pipe end (south)",
692 "Pipe end (north)",
693 "Pipe end (east)",
694 "Pipe end (west)",
695 "Pipe corner ▛",
696 "Pipe corner ▙",
697 "Pipe corner ▜",
698 "Pipe corner ▟",
699 "Pipe-rock intersection ⯊",
700 "Pipe-rock intersection ⯋",
701 "Pipe-rock intersection ◖",
702 "Pipe-rock intersection ◗",
703 "Pipe crossover",
704 "Bombable floor",
705 "Fake bombable floor",
706 "Single 2x2 decor C",
707 "Warp tile",
708 "Tool rack",
709 "Furnace",
710 "Tub (wide)",
711 "Anvil",
712 "Warp tile (disabled)",
713 "Pressure plate",
714 "Single 2x2 decor D",
715 "Blue peg",
716 "Orange peg",
717 "Fortune teller room",
718 "Utility 3x5 decor",
719 "Bar corner ▛",
720 "Bar corner ▙",
721 "Bar corner ▜",
722 "Bar corner ▟",
723 "Decorative bowl",
724 "Tub (tall)",
725 "Bookcase",
726 "Range",
727 "Suitcase",
728 "Bar bottles",
729 "Arrow game hole (west)",
730 "Arrow game hole (east)",
731 "Vitreous goo gfx",
732 "Fake pressure plate",
733 "Medusa head",
734 "4-way shooter block",
735 "Pit",
736 "Wall crack (north)",
737 "Wall crack (south)",
738 "Wall crack (west)",
739 "Wall crack (east)",
740 "Large decor",
741 "Water grate (north)",
742 "Water grate (south)",
743 "Water grate (west)",
744 "Water grate (east)",
745 "Window sunlight",
746 "Floor sunlight",
747 "Trinexx's shell",
748 "Layer 2 mask (full)",
749 "Boss entrance",
750 "Minigame chest",
751 "Ganon door",
752 "Triforce wall ornament",
753 "Triforce floor tiles",
754 "Freezor hole",
755 "Pile of bones",
756 "Vitreous goo damage",
757 "Arrow tile ↑",
758 "Arrow tile ↓",
759 "Arrow tile →",
760 "Nothing",
761};
762
763// Helper function to get object name from ID
764// Works across all three object subtypes
765inline std::string GetObjectName(int object_id) {
766 if (object_id < 0x100) {
767 // Type 1: Subtype 1 objects (0x00-0xFF)
768 constexpr size_t kType1Count =
769 sizeof(Type1RoomObjectNames) / sizeof(Type1RoomObjectNames[0]);
770 if (object_id >= 0 && object_id < static_cast<int>(kType1Count)) {
771 return Type1RoomObjectNames[object_id];
772 }
773 return absl::StrFormat("Unknown Type1 (0x%02X)", object_id);
774 } else if (object_id >= 0x100 && object_id < 0x200) {
775 // Type 2: Subtype 2 objects (0x100-0x1FF)
776 int idx = object_id - 0x100;
777 constexpr size_t kType2Count =
778 sizeof(Type2RoomObjectNames) / sizeof(Type2RoomObjectNames[0]);
779 if (idx >= 0 && idx < static_cast<int>(kType2Count)) {
780 return Type2RoomObjectNames[idx];
781 }
782 return absl::StrFormat("Unknown Type2 (0x%03X)", object_id);
783 } else if (object_id >= 0xF80) {
784 // Type 3: Special objects (0xF80-0xFFF, decoded from ASM 0x200-0x27F)
785 int idx = object_id - 0xF80;
786 constexpr size_t kType3Count =
787 sizeof(Type3RoomObjectNames) / sizeof(Type3RoomObjectNames[0]);
788 if (idx >= 0 && idx < static_cast<int>(kType3Count)) {
789 return Type3RoomObjectNames[idx];
790 }
791 return absl::StrFormat("Unknown Type3 (0x%03X)", object_id);
792 }
793 return absl::StrFormat("Unknown (0x%03X)", object_id);
794}
795
796// Helper to get object type/subtype from ID
797inline int GetObjectSubtype(int object_id) {
798 if (object_id < 0x100)
799 return 1;
800 if (object_id < 0x200)
801 return 2;
802 if (object_id >= 0xF80)
803 return 3;
804 return 0; // Unknown
805}
806
807} // namespace zelda3
808} // namespace yaze
809
810#endif // YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H
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
absl::StatusOr< const gfx::TileInfo * > GetTile(int index) const
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t layer)
std::vector< uint8_t > preview_object_data_
std::vector< gfx::TileInfo > tiles_
void set_size(uint8_t size)
Definition room_object.h:86
std::vector< gfx::TileInfo > & mutable_tiles()
bool IsTrackedTileCacheCurrent() const
ObjectBytes EncodeObjectToBytes() const
const std::vector< gfx::TileInfo > & tiles() const
Definition room_object.h:99
void set_x(uint8_t x)
Definition room_object.h:84
void set_id(int16_t id)
static int DetermineObjectType(uint8_t b1, uint8_t b3)
absl::Status LoadTilesWithParser()
void set_block_behavior_layer(uint8_t layer)
void SetRom(Rom *rom)
Definition room_object.h:78
uint8_t size() const
Definition room_object.h:89
static constexpr int kBlockLoadOrderNew
void set_torch_reserved_bit(uint8_t reserved)
absl::StatusOr< std::span< const gfx::TileInfo > > GetTiles() const
uint8_t block_behavior_layer() const
const Rom * tile_cache_rom_identity_
RoomObject(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer=0)
Definition room_object.h:64
uint8_t GetLayerValue() const
void set_block_load_order(int order)
uint8_t torch_reserved_bit() const
RoomObject CopyForNewPlacement() const
void set_y(uint8_t y)
Definition room_object.h:85
void set_options(ObjectOption options)
uint8_t ResizeRoomObjectByDelta(int object_id, uint8_t size, int delta, bool horizontal)
uint8_t DefaultRoomObjectSizeForPlacement(int object_id)
bool IsRoomObjectSizeEditable(int object_id)
constexpr int kRoomObjectSubtype3
Definition room_object.h:47
RoomObject::LayerType MapRoomObjectListIndexToDrawLayer(uint8_t list_index)
ObjectOption operator|(ObjectOption lhs, ObjectOption rhs)
int GetObjectSubtype(int object_id)
ObjectOption operator^(ObjectOption lhs, ObjectOption rhs)
uint8_t CanonicalRoomObjectSize(int object_id, uint8_t requested_size)
constexpr int kRoomObjectSubtype1
Definition room_object.h:45
constexpr int kRoomObjectSubtype2
Definition room_object.h:46
int RoomObjectSizeAxisStep(int object_id)
constexpr int kRoomObjectTileAddress
Definition room_object.h:48
ObjectOption operator~(ObjectOption option)
std::string GetObjectName(int object_id)
constexpr bool IsStatefulChestObjectId(int object_id)
ObjectOption operator&(ObjectOption lhs, ObjectOption rhs)
absl::Status ValidateRoomObjectStreamEntryForSave(const RoomObject &object)
constexpr int kRoomObjectTileAddressFloor
Definition room_object.h:49
bool IsRoomObjectResizable(int object_id)
int RoomObjectSizeAxisTiles(int object_id, uint8_t size, bool horizontal)