yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_collision.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_ROOM_COLLISION_H
2#define YAZE_ZELDA3_DUNGEON_ROOM_COLLISION_H
3
4#include <array>
5#include <cstddef>
6#include <cstdint>
7#include <optional>
8#include <vector>
9
11#include "zelda3/dungeon/room.h"
13
14namespace yaze {
15
16class Rom;
17
18namespace zelda3 {
19
20// Vanilla underworld collision ("tile attribute") maps.
21//
22// After a room is drawn, Underworld_LoadAttributeTable (usdasm bank_01
23// #_01B8BF) builds one attribute byte per tilemap tile:
24// COLMAPA $7F2000 (64x64) from TILEMAPA $7E2000 (BG1, the upper layer)
25// COLMAPB $7F3000 (64x64) from TILEMAPB $7E4000 (BG2, the lower layer)
26// in four passes:
27// 1. Underworld_LoadBasicAttribute_full: TILEATTR[tile number], plus the
28// tile's flip bits for attributes 0x10-0x1B.
29// 2. Underworld_LoadObjectAttribute: stars, stairs, layer stairs, water
30// stairs, pots/blocks/pegs, torches, chests and big key locks, from lists
31// the object draw routines recorded while the room was drawn.
32// 3. Underworld_LoadDoorAttribute: doorways, locked doors, layer and
33// dungeon toggle doors, the door switch, from the door slot tables.
34// 4. Underworld_FlipCrystalPegAttribute when $7EC172 != 0.
35// yaze reproduces the recorded lists by replaying the list bookkeeping of
36// each RoomDraw_* routine over the room's own objects, doors, pushable blocks
37// and torches, in the game's draw order.
38
39inline constexpr size_t kTileAttributeTableSize = 0x200; // $7EFE00
40inline constexpr size_t kCollisionMapTiles = 64 * 64;
41// COLMAPA then COLMAPB, indexed like the game: tile = y * 64 + x, plus
42// 0x1000 for the lower (BG2) map.
43inline constexpr size_t kRoomCollisionBytes = 2 * kCollisionMapTiles;
44
45// Which pass last wrote a collision byte.
46enum class CollisionSource : uint8_t {
47 kTilemap = 0, // basic pass (tile attribute table)
48 kStar, // star tiles
49 kStairs, // in-room, spiral and straight stairs
50 kLayerStairs, // auto multi-layer / merged stairs
51 kWater, // water hop stairs, water overlay stairs, water ladders
52 kManipulable, // pots, pegs, blocks, big gray rocks
53 kTorch, // lightable torches
54 kChest, // chests and big chests
55 kBigKeyLock, // big key locks
56 kDoor, // doorway properties
57 kLockedDoor, // closed doors (F0 | slot)
58 kDoorLayerToggle, // layer / dungeon toggle doors (OR 0x10 / 0x20)
59 kDoorSwitch, // the door switcher object
60 kCrystalPeg, // crystal peg swap
61};
62
63const char* CollisionSourceName(CollisionSource source);
64
66 std::array<uint8_t, kRoomCollisionBytes> attributes{};
67 std::array<CollisionSource, kRoomCollisionBytes> sources{};
68
69 uint8_t upper(int x, int y) const { return attributes[y * 64 + x]; }
70 uint8_t lower(int x, int y) const {
71 return attributes[kCollisionMapTiles + y * 64 + x];
72 }
73};
74
75// Save-file and runtime state that changes the maps. The default is a room
76// entered for the first time on a new file: no room flags, pegs not swapped.
78 // The room's save word ($7EF000 + room * 2). Underworld_LoadHeader derives
79 // $0400 (= word & 0xF000), $068C (= $0400 | 0x0F00) and
80 // $0402 (= (word & 0x0FF0) << 4) from it.
81 uint16_t room_save_flags = 0;
82 // $068C, the open-door mask, when it differs from the one LoadHeader
83 // derives (a room whose shutters opened after it loaded).
84 std::optional<uint16_t> door_open_mask;
85 // $0468: shutter doors stay closed while it is nonzero. LoadHeader sets 1.
86 uint16_t shutter_controller = 1;
87 // $7EC172 != 0: Underworld_FlipCrystalPegAttribute swaps 0x66 and 0x67.
89};
90
91// TILEATTR ($7EFE00, 512 bytes): LoadDefaultTileTypes (#_0E97D9) copies
92// UnderworldTileTypes ($0E:9659) entries 0x000-0x13F to 0x000 and
93// 0x140-0x17F to 0x1C0; Underworld_LoadCustomTileTypes (#_0E942A) copies 0x80
94// bytes from CustomUnderworldTileTypes ($0E:902A) +
95// CustomTileTypesOffset[blockset] ($0E:9000) to 0x140. `blockset` is room
96// header byte 2 (Room::blockset()).
97std::array<uint8_t, kTileAttributeTableSize> LoadUnderworldTileAttributeTable(
98 const Rom& rom, uint8_t blockset);
99
100// Pass 1 only: the attribute of each tile word in `tilemaps`.
102 const std::array<uint8_t, kTileAttributeTableSize>& table,
103 const RoomTilemaps& tilemaps);
104
105// Everything the passes read from a room.
107 int room_id = 0;
108 uint8_t blockset = 0;
109 uint8_t tag1 = 0; // $AE
110 uint8_t tag2 = 0; // $AF
111 // Room objects as Room holds them: stream objects (list = GetLayerValue())
112 // plus pushable blocks and torches (ObjectOption::Block / ::Torch).
113 std::vector<RoomObject> objects;
114 std::vector<Room::Door> doors; // stream order
115 RoomTilemaps tilemaps; // BG1 / BG2 words after drawing
116};
117
118// All four passes. Tables (tile types, door positions, doorway properties)
119// are read from `rom`.
121 const Rom& rom, const RoomCollisionInput& input,
122 const UnderworldRoomLoadState& state = {});
123
131
137
138// Resolves room-stream stairs from the shared vanilla collision replay, not
139// vector order. Results are a model preview, not custom-engine/runtime proof.
140// Ambiguous triggers and more than four stairs never produce editable slots.
141std::vector<StaircaseSlotResolution> ResolveVanillaStaircaseSlots(
142 const Rom& rom, const RoomCollisionInput& input,
143 const UnderworldRoomLoadState& state = {});
144
145// Convenience for a loaded, rendered room: its objects, doors and header, and
146// the tilemaps yaze composed (ComposeYazeRoomTilemaps) unless `tilemaps` is
147// given.
148RoomCollisionInput MakeRoomCollisionInput(const Room& room);
149RoomCollisionInput MakeRoomCollisionInput(const Room& room,
150 const RoomTilemaps& tilemaps);
151
152} // namespace zelda3
153} // namespace yaze
154
155#endif // YAZE_ZELDA3_DUNGEON_ROOM_COLLISION_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:49
constexpr size_t kRoomCollisionBytes
constexpr size_t kTileAttributeTableSize
std::array< uint8_t, kTileAttributeTableSize > LoadUnderworldTileAttributeTable(const Rom &rom, uint8_t blockset)
constexpr size_t kCollisionMapTiles
RoomCollisionMaps ComputeBasicCollisionMaps(const std::array< uint8_t, kTileAttributeTableSize > &table, const RoomTilemaps &tilemaps)
RoomCollisionMaps ComputeRoomCollisionMaps(const Rom &rom, const RoomCollisionInput &input, const UnderworldRoomLoadState &state)
RoomCollisionInput MakeRoomCollisionInput(const Room &room, const RoomTilemaps &tilemaps)
std::vector< StaircaseSlotResolution > ResolveVanillaStaircaseSlots(const Rom &rom, const RoomCollisionInput &input, const UnderworldRoomLoadState &state)
const char * CollisionSourceName(CollisionSource source)
std::vector< RoomObject > objects
std::vector< Room::Door > doors
uint8_t lower(int x, int y) const
std::array< CollisionSource, kRoomCollisionBytes > sources
uint8_t upper(int x, int y) const
std::array< uint8_t, kRoomCollisionBytes > attributes
std::optional< uint16_t > door_open_mask