yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_map.cc
Go to the documentation of this file.
1#include "overworld_map.h"
2
3#include <algorithm>
4#include <array>
5#include <cstddef>
6#include <cstdint>
7#include <unordered_map>
8#include <vector>
9
10#include "absl/status/status.h"
11#include "absl/status/statusor.h"
15#include "rom/rom.h"
16#include "core/features.h"
17#include "util/macro.h"
18#include "zelda3/common.h"
21
22namespace yaze::zelda3 {
23
24OverworldMap::OverworldMap(int index, Rom* rom, GameData* game_data)
25 : index_(index), parent_(index), rom_(rom), game_data_(game_data) {
27
28 // Load parent ID from ROM data for all versions
29 // This is critical for proper large map sibling coordination
31 if (version >= OverworldVersion::kZSCustomV3) {
32 // For v3+, parent ID is stored in expanded table (0x140998) with 160 entries
34 } else {
35 // For vanilla/v1/v2, parent ID table at 0x125EC only has 64 entries (LW only)
36 // DW maps mirror LW parents with +0x40 offset
37 // SW maps use hardcoded parent values based on vanilla game behavior
39 // Light World: direct lookup from 64-entry table
41 } else if (index_ < kSpecialWorldMapIdStart) {
42 // Dark World: mirror LW parent structure with +0x40 offset
43 // DW map 0x43's parent = LW map 0x03's parent (from table) + 0x40
44 uint8_t lw_equivalent = index_ - kDarkWorldMapIdStart;
45 uint8_t lw_parent = (*rom_)[kOverworldMapParentId + lw_equivalent];
46 parent_ = lw_parent + kDarkWorldMapIdStart;
47 } else {
48 // Special World: hardcoded parents for vanilla compatibility
49 // Zora's Domain (0x81, 0x82, 0x89, 0x8A) is a large area with parent 0x81
50 if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A) {
51 parent_ = 0x81;
52 } else {
53 // All other SW maps are small areas - parent is self
55 }
56 }
57 }
58
59 if (version != OverworldVersion::kVanilla) {
60 // For ALL custom ASM versions (v1-v3), read from custom arrays
61 // SetupCustomTileset checks enable flags and falls back to vanilla if disabled
63 } else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
64 // Pure vanilla ROM but flag enabled - set up hardcoded vanilla defaults
66 }
67 // For pure vanilla ROMs, LoadAreaInfo already handles everything
68}
69
70absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
71 std::vector<gfx::Tile16>& tiles16,
72 OverworldBlockset& world_blockset) {
73 // Delegate to cached version with no cache
74 return BuildMapWithCache(count, game_state, world, tiles16, world_blockset,
75 nullptr);
76}
77
79 int count, int game_state, int world, std::vector<gfx::Tile16>& tiles16,
80 OverworldBlockset& world_blockset,
81 const std::vector<uint8_t>* cached_tileset) {
83 world_ = world;
85
86 // For large maps in vanilla ROMs, we need to handle special world graphics
87 // This ensures proper rendering of special overworld areas like Zora's Domain
88 // NOTE: Callers (LoadOverworldMaps, EnsureMapBuilt) also handle this and call
89 // LoadAreaGraphics() before BuildMapWithCache. This block is kept for the
90 // native async path which calls BuildMap() -> BuildMapWithCache() directly.
91 if (large_map_ && (version == OverworldVersion::kVanilla)) {
92 if (parent_ != index_ && !initialized_) {
93 if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
94 index_ != 0x88) {
95 // Zora's Domain children - set sprite_graphics and area graphics
96 sprite_graphics_[0] = 0x0E;
97 sprite_graphics_[1] = 0x0E;
98 sprite_graphics_[2] = 0x0E;
102 } else if (index_ == 0x88) {
103 // Triforce room has special hardcoded values
104 area_graphics_ = 0x51;
105 area_palette_ = 0x00;
106 } else if (index_ < kSpecialWorldMapIdStart) {
107 // LW/DW large map child - use parent's graphics
110 }
111 // Note: Other SW maps (>0x8A) keep their LoadAreaInfo values
112
113 initialized_ = true;
114 }
115 }
116
118
119 // Use cached tileset if available, otherwise build from scratch
120 if (cached_tileset && !cached_tileset->empty()) {
121 UseCachedTileset(*cached_tileset);
122 } else {
124 }
125
126 RETURN_IF_ERROR(BuildTiles16Gfx(tiles16, count))
129 RETURN_IF_ERROR(BuildBitmap(world_blockset))
130 built_ = true;
131 return absl::OkStatus();
132}
133
136
137 // ZSCustomOverworld ASM Version System:
138 // 0x00-0x02: Legacy versions with limited features
139 // 0x03: Current version with full area size expansion and custom data support
140 // 0xFF: Pure vanilla ROM (no ASM applied)
141
142 // Load message ID and area size based on ASM version
143 if (version < OverworldVersion::kZSCustomV2 ||
144 version == OverworldVersion::kVanilla) {
145 // v2 and vanilla: use original message table
146 message_id_ = (*rom_)[kOverworldMessageIds + (parent_ * 2)] |
147 ((*rom_)[kOverworldMessageIds + (parent_ * 2) + 1] << 8);
148
149 // Load area size for v2/vanilla
150 if (index_ < 0x80) {
151 // For LW and DW, check the screen size byte
152 // Note: v2 had a bug where large/small values were swapped
153 uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
154 switch (size_byte) {
155 case 0:
157 break;
158 case 1:
159 default:
161 break;
162 case 2:
164 break;
165 case 3:
167 break;
168 }
169 } else {
170 // For SW, use hardcoded values for v2 compatibility
171 // Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas
172 area_size_ =
173 (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
176 }
177 } else {
178 // v3: use expanded message table and area size table
179 // All area sizes are now stored in the expanded table, supporting all size
180 // types
182 (*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] |
183 ((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8);
184 area_size_ =
185 static_cast<AreaSizeEnum>((*rom_)[kOverworldScreenSize + index_]);
186 }
187
188 // Update large_map_ based on area size
190
191 // Load area-specific data based on index range
193 // Light World (LW) areas
199
202
204 sprite_palette_[1] =
206 sprite_palette_[2] =
208
213
214 // For v2/vanilla, use original palette table
215 if (version < OverworldVersion::kZSCustomV2 ||
216 version == OverworldVersion::kVanilla) {
218 }
219 } else if (index_ < kSpecialWorldMapIdStart) {
220 // Dark World (DW) areas
227
230
231 sprite_palette_[0] =
233 sprite_palette_[1] =
235 sprite_palette_[2] =
237
238 area_music_[0] =
240
241 // For v2/vanilla, use original palette table
242 if (version < OverworldVersion::kZSCustomV2 ||
243 version == OverworldVersion::kVanilla) {
245 }
246 } else {
247 // Special World (SW) areas (index >= 0x80)
248 // Message ID already loaded above based on ASM version
249
250 // For v3, use expanded sprite tables with full customization support
251 if (version == OverworldVersion::kZSCustomV3) {
261
268 } else {
269 // For v2/vanilla, use original sprite tables
276
283 }
284
287
288 // For v2/vanilla, use original palette table and handle special cases
289 // These hardcoded cases are needed for vanilla compatibility
290 if (version < OverworldVersion::kZSCustomV2 ||
291 version == OverworldVersion::kVanilla) {
293
294 // Handle special world area cases based on ZScream documentation
295 if (index_ == 0x88 || index_ == 0x93) {
296 // Triforce room - special graphics and palette
297 area_graphics_ = 0x51;
298 area_palette_ = 0x00;
299 } else if (index_ == 0x80) {
300 // Master Sword area - use special graphics group
304 } else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
305 index_ == 0x8A) {
306 // Zora's Domain areas - use special sprite graphics and area graphics
307 // Note: These are the large area maps that were causing crashes
308 sprite_graphics_[0] = 0x0E;
309 sprite_graphics_[1] = 0x0E;
310 sprite_graphics_[2] = 0x0E;
311
315 } else if (index_ == 0x94) {
316 // Make this the same GFX as the true master sword area
318 (0x80 - kSpecialWorldMapIdStart)];
320 } else if (index_ == 0x95) {
321 // Make this the same GFX as the LW death mountain areas
322 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x03];
323 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x03];
324 } else if (index_ == 0x96) {
325 // Make this the same GFX as the pyramid areas
326 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x5B];
327 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x5B];
328 } else if (index_ == 0x9C) {
329 // Make this the same GFX as the DW death mountain areas
330 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
331 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
332 } else {
333 // Default case - use basic graphics
334 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
335 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
336 }
337 }
338 }
339}
340
342 // Set the main palette values based on ZScream logic
343 // Use parent_ to ensure all sibling maps in a large area share the same palette
344 if (parent_ < 0x40 || parent_ == 0x95) { // LW
345 main_palette_ = 0;
346 } else if ((parent_ >= 0x40 && parent_ < 0x80) || parent_ == 0x96) { // DW
347 main_palette_ = 1;
348 } else if (parent_ >= 0x80 && parent_ < 0xA0) { // SW
349 main_palette_ = 0;
350 }
351
352 // Death Mountain / special overrides - use parent_ so siblings get correct palette
353 if (parent_ == 0x03 || parent_ == 0x05 ||
354 parent_ == 0x07) { // LW Death Mountain
355 main_palette_ = 2;
356 } else if (parent_ == 0x43 || parent_ == 0x45 ||
357 parent_ == 0x47) { // DW Death Mountain
358 main_palette_ = 3;
359 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
360 main_palette_ = 4;
361 }
362
363 // Set the mosaic values based on ZScream logic
364 switch (index_) {
365 case 0x00: // Leaving Skull Woods / Lost Woods
366 case 0x40:
367 mosaic_expanded_ = {false, true, false, true};
368 break;
369 case 0x02: // Going into Skull woods / Lost Woods west
370 case 0x0A:
371 case 0x42:
372 case 0x4A:
373 mosaic_expanded_ = {false, false, true, false};
374 break;
375 case 0x0F: // Going into Zora's Domain North
376 case 0x10: // Going into Skull Woods / Lost Woods North
377 case 0x11:
378 case 0x50:
379 case 0x51:
380 mosaic_expanded_ = {true, false, false, false};
381 break;
382 case 0x80: // Leaving Zora's Domain, the Master Sword area, and the
383 // Triforce area
384 case 0x81:
385 case 0x88:
386 mosaic_expanded_ = {false, true, false, false};
387 break;
388 default:
389 mosaic_expanded_ = {false, false, false, false};
390 break;
391 }
392
393 // Set up world index for GFX groups
394 int index_world = 0x20;
397 index_world = 0x21;
398 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
399 index_world = 0x24;
400 }
401
402 const auto overworld_gfx_groups2 = version_constants().kOverworldGfxGroups2;
403
404 // Main Blocksets
405 for (int i = 0; i < 8; i++) {
406 custom_gfx_ids_[i] =
407 (uint8_t)(*rom_)[overworld_gfx_groups2 + (index_world * 8) + i];
408 }
409
410 const auto overworldgfxGroups = version_constants().kOverworldGfxGroups1;
411
412 // Replace the variable tiles with the variable ones
413 uint8_t temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4)];
414 if (temp != 0) {
415 custom_gfx_ids_[3] = temp;
416 } else {
417 custom_gfx_ids_[3] = 0xFF;
418 }
419
420 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 1];
421 if (temp != 0) {
422 custom_gfx_ids_[4] = temp;
423 } else {
424 custom_gfx_ids_[4] = 0xFF;
425 }
426
427 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 2];
428 if (temp != 0) {
429 custom_gfx_ids_[5] = temp;
430 } else {
431 custom_gfx_ids_[5] = 0xFF;
432 }
433
434 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 3];
435 if (temp != 0) {
436 custom_gfx_ids_[6] = temp;
437 } else {
438 custom_gfx_ids_[6] = 0xFF;
439 }
440
441 // Set the animated GFX values - use parent_ so siblings get correct animated sheet
442 // Death Mountain uses sheet 0x59, others use 0x5B
443 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 || parent_ == 0x43 ||
444 parent_ == 0x45 || parent_ == 0x47 || parent_ == 0x95) {
445 animated_gfx_ = 0x59;
446 } else {
447 animated_gfx_ = 0x5B;
448 }
449
450 // Set the subscreen overlay values
451 subscreen_overlay_ = 0x00FF;
452
453 if (index_ == 0x00 || index_ == 0x01 || index_ == 0x08 || index_ == 0x09 ||
454 index_ == 0x40 || index_ == 0x41 || index_ == 0x48 ||
455 index_ == 0x49) { // Add fog 2 to the lost woods and skull woods
456 subscreen_overlay_ = 0x009D;
457 } else if (index_ == 0x03 || index_ == 0x04 || index_ == 0x0B ||
458 index_ == 0x0C || index_ == 0x05 || index_ == 0x06 ||
459 index_ == 0x0D || index_ == 0x0E ||
460 index_ == 0x07) { // Add the sky BG to LW death mountain
461 subscreen_overlay_ = 0x0095;
462 } else if (index_ == 0x43 || index_ == 0x44 || index_ == 0x4B ||
463 index_ == 0x4C || index_ == 0x45 || index_ == 0x46 ||
464 index_ == 0x4D || index_ == 0x4E ||
465 index_ == 0x47) { // Add the lava to DW death mountain
466 subscreen_overlay_ = 0x009C;
467 } else if (index_ == 0x5B || index_ == 0x5C || index_ == 0x63 ||
468 index_ == 0x64) { // TODO: Might need this one too "index == 0x1B"
469 // but for now I don't think so
470 subscreen_overlay_ = 0x0096;
471 } else if (index_ == 0x80) { // Add fog 1 to the master sword area
472 subscreen_overlay_ = 0x0097;
473 } else if (index_ ==
474 0x88) { // Add the triforce room curtains to the triforce room
475 subscreen_overlay_ = 0x0093;
476 }
477}
478
480 uint8_t palette = 0;
481
482 // Base world selection - use parent_ to ensure siblings share the same palette
483 if (parent_ < 0x40 || parent_ == 0x95) { // LW
484 palette = 0;
485 } else if ((parent_ >= 0x40 && parent_ < 0x80) || parent_ == 0x96) { // DW
486 palette = 1;
487 } else if (parent_ >= 0x80 && parent_ < 0xA0) { // SW
488 palette = 0;
489 }
490
491 // Death Mountain / special overrides - use parent_ to ensure all siblings
492 // in a large area get the correct palette (brown grass for DM)
493 // LW DM parents: 0x03, 0x05, 0x07 (children: 0x04, 0x06, 0x0B-0x0E)
494 // DW DM parents: 0x43, 0x45, 0x47 (children: 0x44, 0x46, 0x4B-0x4E)
495 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07) {
496 palette = 2; // LW Death Mountain
497 } else if (parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47) {
498 palette = 3; // DW Death Mountain
499 } else if (parent_ == 0x88 || parent_ == 0x93) {
500 palette = 4; // Triforce room
501 }
502
503 return palette;
504}
505
506void OverworldMap::SetupCustomTileset(uint8_t asm_version) {
507 // Load area size for v3
510 uint8_t size_byte = (*rom_)[kOverworldScreenSize + index_];
511 area_size_ = static_cast<AreaSizeEnum>(size_byte);
513 }
514
515 // Main Palette - check enable flag before reading from custom array
516 if ((*rom_)[OverworldCustomMainPaletteEnabled] != 0x00) {
518 } else {
519 // Fall back to world-based hardcoded logic
521 }
522
523 // Mosaic - check enable flag before reading from custom array
524 if ((*rom_)[OverworldCustomMosaicEnabled] != 0x00) {
525 mosaic_ = (*rom_)[OverworldCustomMosaicArray + index_] != 0x00;
526 uint8_t mosaicByte = (*rom_)[OverworldCustomMosaicArray + index_];
527 mosaic_expanded_ = {(mosaicByte & 0x08) != 0x00,
528 (mosaicByte & 0x04) != 0x00,
529 (mosaicByte & 0x02) != 0x00,
530 (mosaicByte & 0x01) != 0x00};
531 } else {
532 // Fall back to hardcoded vanilla mosaic values
533 mosaic_ = false;
534 mosaic_expanded_ = {false, false, false, false};
535 switch (index_) {
536 case 0x00: // Leaving Skull Woods / Lost Woods
537 case 0x40:
538 mosaic_expanded_ = {false, true, false, true};
539 break;
540 case 0x02: // Going into Skull woods / Lost Woods west
541 case 0x0A:
542 case 0x42:
543 case 0x4A:
544 mosaic_expanded_ = {false, false, true, false};
545 break;
546 case 0x0F: // Going into Zora's Domain North
547 case 0x10: // Going into Skull Woods / Lost Woods North
548 case 0x11:
549 case 0x50:
550 case 0x51:
551 mosaic_expanded_ = {true, false, false, false};
552 break;
553 case 0x80: // Leaving Zora's Domain, Master Sword area, Triforce area
554 case 0x81:
555 case 0x88:
556 mosaic_expanded_ = {false, true, false, false};
557 break;
558 }
559 }
560
561 // Animated GFX - check enable flag before reading from custom array
562 if ((*rom_)[OverworldCustomAnimatedGFXEnabled] != 0x00) {
564 } else {
565 // Death mountain uses 0x59, others use 0x5B
566 // Use parent_ to ensure siblings in large areas share the same animated sheet
567 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 || parent_ == 0x43 ||
568 parent_ == 0x45 || parent_ == 0x47 || parent_ == 0x95) {
569 animated_gfx_ = 0x59;
570 } else {
571 animated_gfx_ = 0x5B;
572 }
573 }
574
575 // Tile GFX Groups - check enable flag before reading from custom array
577 for (int i = 0; i < 8; i++) {
578 custom_gfx_ids_[i] =
579 (*rom_)[OverworldCustomTileGFXGroupArray + (index_ * 8) + i];
580 }
581 } else {
582 // Fall back to world-based GFX groups
583 int index_world = 0x20;
586 index_world = 0x21;
587 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
588 index_world = 0x24;
589 }
590
591 // Main Blocksets
592 for (int i = 0; i < 8; i++) {
593 custom_gfx_ids_[i] =
595 (index_world * 8) + i];
596 }
597
598 const auto overworldgfxGroups =
600
601 // Replace the variable tiles with the variable ones
602 // If the variable is 00 set it to 0xFF which is the new "don't load
603 // anything" value
604 uint8_t temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4)];
605 if (temp != 0x00) {
606 custom_gfx_ids_[3] = temp;
607 } else {
608 custom_gfx_ids_[3] = 0xFF;
609 }
610
611 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 1];
612 if (temp != 0x00) {
613 custom_gfx_ids_[4] = temp;
614 } else {
615 custom_gfx_ids_[4] = 0xFF;
616 }
617
618 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 2];
619 if (temp != 0x00) {
620 custom_gfx_ids_[5] = temp;
621 } else {
622 custom_gfx_ids_[5] = 0xFF;
623 }
624
625 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 3];
626 if (temp != 0x00) {
627 custom_gfx_ids_[6] = temp;
628 } else {
629 custom_gfx_ids_[6] = 0xFF;
630 }
631 }
632
633 // Subscreen Overlay - check enable flag before reading from custom array
637 ((*rom_)[OverworldCustomSubscreenOverlayArray + (index_ * 2) + 1] << 8);
638 } else {
639 // Fall back to hardcoded overlay values
640 subscreen_overlay_ = 0x00FF;
641 if (index_ == 0x00 || index_ == 0x01 || index_ == 0x08 || index_ == 0x09 ||
642 index_ == 0x40 || index_ == 0x41 || index_ == 0x48 || index_ == 0x49) {
643 // Add fog 2 to the lost woods and skull woods
644 subscreen_overlay_ = 0x009D;
645 } else if (index_ == 0x03 || index_ == 0x04 || index_ == 0x0B ||
646 index_ == 0x0C || index_ == 0x05 || index_ == 0x06 ||
647 index_ == 0x0D || index_ == 0x0E || index_ == 0x07) {
648 // Add the sky BG to LW death mountain
649 subscreen_overlay_ = 0x0095;
650 } else if (index_ == 0x43 || index_ == 0x44 || index_ == 0x4B ||
651 index_ == 0x4C || index_ == 0x45 || index_ == 0x46 ||
652 index_ == 0x4D || index_ == 0x4E || index_ == 0x47) {
653 // Add the lava to DW death mountain
654 subscreen_overlay_ = 0x009C;
655 } else if (index_ == 0x5B || index_ == 0x5C || index_ == 0x63 ||
656 index_ == 0x64) {
657 subscreen_overlay_ = 0x0096;
658 } else if (index_ == 0x80) {
659 // Add fog 1 to the master sword area
660 subscreen_overlay_ = 0x0097;
661 } else if (index_ == 0x88) {
662 // Add the triforce room curtains to the triforce room
663 subscreen_overlay_ = 0x0093;
664 }
665 }
666}
667
670 main_gfx_id_ = 0x20;
671 } else if (parent_ >= kDarkWorldMapIdStart &&
673 main_gfx_id_ = 0x21;
674 } else if (parent_ >= kSpecialWorldMapIdStart) {
675 // Special world maps - use appropriate graphics ID based on the specific
676 // map
677 if (parent_ == 0x88) {
678 main_gfx_id_ = 0x24;
679 } else {
680 // Default special world graphics ID
681 main_gfx_id_ = 0x20;
682 }
683 }
684}
685
687 int static_graphics_base = 0x73;
688 static_graphics_[8] = static_graphics_base + 0x00;
689 static_graphics_[9] = static_graphics_base + 0x01;
690 static_graphics_[10] = static_graphics_base + 0x06;
691 static_graphics_[11] = static_graphics_base + 0x07;
692
693 for (int i = 0; i < 4; i++) {
694 static_graphics_[12 + i] =
696 (sprite_graphics_[game_state_] * 4) + i] +
697 static_graphics_base);
698 }
699}
700
702 for (int i = 0; i < 8; i++) {
705 (main_gfx_id_ * 8) + i];
706 }
707}
708
709// For animating water tiles on the overworld map.
710// We want to swap out static_graphics_[07] with the next sheet
711// Usually it is 5A, so we make it 5B instead.
712// There is a middle frame which contains tiles from the bottom half
713// of the 5A sheet, so this will need some special manipulation to make work
714// during the BuildBitmap step (or a new one specifically for animating).
716 if (static_graphics_[7] == 0x5B) {
717 static_graphics_[7] = 0x5A;
718 } else {
719 if (static_graphics_[7] == 0x59) {
720 static_graphics_[7] = 0x58;
721 }
722 static_graphics_[7] = 0x5B;
723 }
724}
725
727 for (int i = 0; i < 4; i++) {
728 uint8_t value = (*rom_)[version_constants().kOverworldGfxGroups1 +
729 (area_graphics_ * 4) + i];
730 if (value != 0) {
731 static_graphics_[3 + i] = value;
732 }
733 }
734}
735
736// TODO: Change the conditions for death mountain gfx
737// JaredBrian: This is how ZS did it, but in 3.0.4 I changed it to just check
738// for 03, 05, 07, and the DW ones as that's how it would appear in-game if
739// you were to make area 03 not a large area anymore for example, so you might
740// want to do the same.
742 // Match ZScream 3.0.4 behavior: only specific DM parents use animated GFX
743 const bool is_light_dm = (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07);
744 const bool is_dark_dm = (parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47);
745 static_graphics_[7] = (is_light_dm || is_dark_dm) ? 0x59 : 0x5B;
746}
747
754
755 // v3 custom tile GFX groups: override main sheets when enabled
759 for (int i = 0; i < 8; i++) {
760 uint8_t custom_sheet = custom_gfx_ids_[i];
761 if (custom_sheet == 0x00 || custom_sheet == 0xFF) {
762 continue; // 0/FF = don't load/override this slot
763 }
764 static_graphics_[i] = custom_sheet;
765 }
766 }
767}
768
769namespace palette_internal {
770
771absl::Status SetColorsPalette(Rom& rom, GameData* game_data, int index,
772 gfx::SnesPalette& current,
775 gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
777 // Palettes infos, color 0 of a palette is always transparent (the arrays
778 // contains 7 colors width wide) There is 16 color per line so 16*Y
779
780 // Left side of the palette - Main, Animated
781 std::array<gfx::SnesColor, 256> new_palette = {};
782
783 // Main Palette, Location 0,2 : 35 colors [7x5]
784 int k = 0;
785 for (int y = 2; y < 7; y++) {
786 for (int x = 1; x < 8; x++) {
787 new_palette[x + (16 * y)] = main[k];
788 k++;
789 }
790 }
791
792 // Animated Palette, Location 0,7 : 7colors
793 for (int x = 1; x < 8; x++) {
794 new_palette[(16 * 7) + (x)] = animated[(x - 1)];
795 }
796
797 // Right side of the palette - Aux1, Aux2
798
799 // Aux1 Palette, Location 8,2 : 21 colors [7x3]
800 k = 0;
801 for (int y = 2; y < 5; y++) {
802 for (int x = 9; x < 16; x++) {
803 new_palette[x + (16 * y)] = aux1[k];
804 k++;
805 }
806 }
807
808 // Aux2 Palette, Location 8,5 : 21 colors [7x3]
809 k = 0;
810 for (int y = 5; y < 8; y++) {
811 for (int x = 9; x < 16; x++) {
812 new_palette[x + (16 * y)] = aux2[k];
813 k++;
814 }
815 }
816
817 // Hud Palette, Location 0,0 : 32 colors [16x2]
818 for (int i = 0; i < 32; i++) {
819 new_palette[i] = hud[i];
820 }
821
822 // Hardcoded grass color (that might change to become invisible instead)
823 for (int i = 0; i < 8; i++) {
824 new_palette[(i * 16)] = bgrcolor;
825 new_palette[(i * 16) + 8] = bgrcolor;
826 }
827
828 // Sprite Palettes
829 k = 0;
830 for (int y = 8; y < 9; y++) {
831 for (int x = 1; x < 8; x++) {
832 new_palette[x + (16 * y)] = game_data->palette_groups.sprites_aux1[1][k];
833 k++;
834 }
835 }
836
837 // Sprite Palettes
838 k = 0;
839 for (int y = 8; y < 9; y++) {
840 for (int x = 9; x < 16; x++) {
841 new_palette[x + (16 * y)] = game_data->palette_groups.sprites_aux3[0][k];
842 k++;
843 }
844 }
845
846 // Sprite Palettes
847 k = 0;
848 for (int y = 9; y < 13; y++) {
849 for (int x = 1; x < 16; x++) {
850 new_palette[x + (16 * y)] = game_data->palette_groups.global_sprites[0][k];
851 k++;
852 }
853 }
854
855 // Sprite Palettes
856 k = 0;
857 for (int y = 13; y < 14; y++) {
858 for (int x = 1; x < 8; x++) {
859 new_palette[x + (16 * y)] = spr[k];
860 k++;
861 }
862 }
863
864 // Sprite Palettes
865 k = 0;
866 for (int y = 14; y < 15; y++) {
867 for (int x = 1; x < 8; x++) {
868 new_palette[x + (16 * y)] = spr2[k];
869 k++;
870 }
871 }
872
873 // Sprite Palettes
874 k = 0;
875 for (int y = 15; y < 16; y++) {
876 for (int x = 1; x < 16; x++) {
877 new_palette[x + (16 * y)] = game_data->palette_groups.armors[0][k];
878 k++;
879 }
880 }
881
882 for (int i = 0; i < 256; i++) {
883 current[i] = new_palette[i];
884 current[(i / 16) * 16].set_transparent(true);
885 }
886
887 current.set_size(256);
888 return absl::OkStatus();
889}
890} // namespace palette_internal
891
892absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
893 const gfx::PaletteGroup& palette_group, int index, int previous_index,
894 int limit) {
895 if (index == 255) {
897 (previous_index * 4)];
898 }
899 if (index >= limit) {
900 index = limit - 1;
901 }
902 return palette_group[index];
903}
904
906 if (!game_data_) {
908 return absl::OkStatus();
909 }
910
911 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
913
914 int previous_pal_id = 0;
915 int previous_spr_pal_id = 0;
916
917 if (index_ > 0) {
918 // Load previous palette ID based on ASM version
920 // v3 uses expanded palette table
921 previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
922 } else {
923 previous_pal_id = (*rom_)[kOverworldMapPaletteIds + parent_ - 1];
924 }
925
926 previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
927 }
928
929 area_palette_ = std::min((int)area_palette_, 0xA3);
930
931 uint8_t pal0 = 0;
932 uint8_t pal1 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
933 (area_palette_ * 4)];
934 uint8_t pal2 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
935 (area_palette_ * 4) + 1];
936 uint8_t pal3 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
937 (area_palette_ * 4) + 2];
938 uint8_t pal4 = (*rom_)[kOverworldSpritePaletteGroup +
940 uint8_t pal5 = (*rom_)[kOverworldSpritePaletteGroup +
941 (sprite_palette_[game_state_] * 2) + 1];
942
943 auto& grass_pal_group = game_data_->palette_groups.grass;
944 auto bgr = grass_pal_group[0][0];
945
946 // Handle 0xFF palette references (use previous palette)
947 if (pal1 == 0xFF) {
949 (previous_pal_id * 4)];
950 }
951
952 if (pal2 == 0xFF) {
954 (previous_pal_id * 4) + 1];
955 }
956
957 if (pal3 == 0xFF) {
959 (previous_pal_id * 4) + 2];
960 }
961
962 auto& ow_aux_pal_group = game_data_->palette_groups.overworld_aux;
964 GetPalette(ow_aux_pal_group, pal1, previous_pal_id, 20));
966 GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
967
968 // Set background color based on world type and area-specific settings
969 bool use_area_specific_bg =
972 if (use_area_specific_bg) {
973 // Use area-specific background color from custom array
977 << 8);
978 // Convert 15-bit SNES color to palette color
980 } else {
981 // Use default world-based background colors
983 bgr = grass_pal_group[0][0]; // LW
984 } else if (parent_ >= kDarkWorldMapIdStart &&
986 bgr = grass_pal_group[0][1]; // DW
987 } else if (parent_ >= 128 && parent_ < kNumOverworldMaps) {
988 bgr = grass_pal_group[0][2]; // SW
989 }
990 }
991
992 // Use main palette from the overworld map data (matches ZScream logic)
993 if (version == OverworldVersion::kVanilla) {
994 // Vanilla ROMs never write main_palette_ elsewhere; ensure world defaults
996 }
997 pal0 = main_palette_;
998
999 auto& ow_main_pal_group = game_data_->palette_groups.overworld_main;
1001 GetPalette(ow_main_pal_group, pal0, previous_pal_id, 255));
1002 auto& ow_animated_pal_group = game_data_->palette_groups.overworld_animated;
1004 GetPalette(ow_animated_pal_group, std::min((int)pal3, 13),
1005 previous_pal_id, 14));
1006
1007 auto& hud_pal_group = game_data_->palette_groups.hud;
1008 gfx::SnesPalette hud = hud_pal_group[0];
1009
1010 // Handle 0xFF sprite palette references (use previous sprite palette)
1011 if (pal4 == 0xFF) {
1012 pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
1013 }
1014
1015 if (pal4 == 0xFF) {
1016 pal4 = 0; // Fallback to 0 if still 0xFF
1017 }
1018
1019 if (pal5 == 0xFF) {
1020 pal5 =
1021 (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
1022 }
1023
1024 if (pal5 == 0xFF) {
1025 pal5 = 0; // Fallback to 0 if still 0xFF
1026 }
1027
1030 previous_spr_pal_id, 24));
1033 previous_spr_pal_id, 24));
1034
1036 *rom_, game_data_, parent_, current_palette_, main, animated, aux1, aux2,
1037 hud, bgr, spr, spr2));
1038
1039 if (palettesets_.count(area_palette_) == 0) {
1041 main, animated, aux1, aux2, bgr, hud, spr, spr2, current_palette_};
1042 }
1043
1044 return absl::OkStatus();
1045}
1046
1048 // Load overlays based on ROM version
1050 // Vanilla ROM - load overlay from overlay pointers
1051 return LoadVanillaOverlayData();
1052 }
1053
1054 // Custom overworld ROM - use overlay from custom data
1056 has_overlay_ = (overlay_id_ != 0x00FF);
1057 overlay_data_.clear();
1058 return absl::OkStatus();
1059}
1060
1062 // Load vanilla overlay for this map (interactive overlays for revealing
1063 // holes/changing elements)
1064 int address = (kOverlayPointersBank << 16) +
1065 ((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
1066 (*rom_)[kOverlayPointers + (index_ * 2)];
1067
1068 // Convert SNES address to PC address
1069 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1070
1071 // Check if custom overlay code is present
1072 if ((*rom_)[kOverlayData1] == 0x6B) {
1073 // Use custom overlay data pointer
1074 address = ((*rom_)[kOverlayData2 + 2 + (index_ * 3)] << 16) +
1075 ((*rom_)[kOverlayData2 + 1 + (index_ * 3)] << 8) +
1076 (*rom_)[kOverlayData2 + (index_ * 3)];
1077 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1078 }
1079
1080 // Validate address
1081 if (address >= rom_->size()) {
1082 has_overlay_ = false;
1083 overlay_id_ = 0;
1084 overlay_data_.clear();
1085 return absl::OkStatus();
1086 }
1087
1088 // Parse overlay data (interactive overlays)
1089 overlay_data_.clear();
1090 uint8_t b = (*rom_)[address];
1091
1092 // Parse overlay commands until we hit END (0x60)
1093 while (b != 0x60 && address < rom_->size()) {
1094 overlay_data_.push_back(b);
1095
1096 // Handle different overlay commands
1097 switch (b) {
1098 case 0xA9: // LDA #$
1099 if (address + 2 < rom_->size()) {
1100 overlay_data_.push_back((*rom_)[address + 1]);
1101 overlay_data_.push_back((*rom_)[address + 2]);
1102 address += 3;
1103 } else {
1104 address++;
1105 }
1106 break;
1107 case 0xA2: // LDX #$
1108 if (address + 2 < rom_->size()) {
1109 overlay_data_.push_back((*rom_)[address + 1]);
1110 overlay_data_.push_back((*rom_)[address + 2]);
1111 address += 3;
1112 } else {
1113 address++;
1114 }
1115 break;
1116 case 0x8D: // STA $xxxx
1117 if (address + 3 < rom_->size()) {
1118 overlay_data_.push_back((*rom_)[address + 1]);
1119 overlay_data_.push_back((*rom_)[address + 2]);
1120 overlay_data_.push_back((*rom_)[address + 3]);
1121 address += 4;
1122 } else {
1123 address++;
1124 }
1125 break;
1126 case 0x9D: // STA $xxxx,x
1127 if (address + 3 < rom_->size()) {
1128 overlay_data_.push_back((*rom_)[address + 1]);
1129 overlay_data_.push_back((*rom_)[address + 2]);
1130 overlay_data_.push_back((*rom_)[address + 3]);
1131 address += 4;
1132 } else {
1133 address++;
1134 }
1135 break;
1136 case 0x8F: // STA $xxxxxx
1137 if (address + 4 < rom_->size()) {
1138 overlay_data_.push_back((*rom_)[address + 1]);
1139 overlay_data_.push_back((*rom_)[address + 2]);
1140 overlay_data_.push_back((*rom_)[address + 3]);
1141 overlay_data_.push_back((*rom_)[address + 4]);
1142 address += 5;
1143 } else {
1144 address++;
1145 }
1146 break;
1147 case 0x1A: // INC A
1148 address++;
1149 break;
1150 case 0x4C: // JMP
1151 if (address + 3 < rom_->size()) {
1152 overlay_data_.push_back((*rom_)[address + 1]);
1153 overlay_data_.push_back((*rom_)[address + 2]);
1154 overlay_data_.push_back((*rom_)[address + 3]);
1155 address += 4;
1156 } else {
1157 address++;
1158 }
1159 break;
1160 default:
1161 address++;
1162 break;
1163 }
1164
1165 if (address < rom_->size()) {
1166 b = (*rom_)[address];
1167 } else {
1168 break;
1169 }
1170 }
1171
1172 // Add the END command if we found it
1173 if (b == 0x60) {
1174 overlay_data_.push_back(0x60);
1175 }
1176
1177 // Set overlay ID based on map index (simplified)
1179 has_overlay_ = !overlay_data_.empty();
1180
1181 return absl::OkStatus();
1182}
1183
1184void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
1185 int size, const uint8_t* all_gfx) {
1186 // Ensure we don't go out of bounds
1187 int max_offset = static_graphics_offset * size + size;
1188 if (!game_data_ || max_offset > game_data_->graphics_buffer.size()) {
1189 // Fill with zeros if out of bounds
1190 for (int i = 0; i < size; i++) {
1191 current_gfx_[(index * size) + i] = 0x00;
1192 }
1193 return;
1194 }
1195
1196 for (int i = 0; i < size; i++) {
1197 auto byte = all_gfx[i + (static_graphics_offset * size)];
1198 switch (index) {
1199 case 0:
1200 case 3:
1201 case 4:
1202 case 5:
1203 byte += 0x88;
1204 break;
1205 }
1206 current_gfx_[(index * size) + i] = byte;
1207 }
1208}
1209
1211 if (current_gfx_.size() == 0)
1212 current_gfx_.resize(0x10000, 0x00);
1213
1214 if (!game_data_) {
1215 // Headless/tests: allow map builds without graphics by keeping zeroed data.
1216 return absl::OkStatus();
1217 }
1218
1219 // Process the 8 main graphics sheets (slots 0-7)
1220 for (int i = 0; i < 8; i++) {
1221 if (static_graphics_[i] != 0) {
1223 game_data_->graphics_buffer.data());
1224 }
1225 }
1226
1227 // Process sprite graphics (slots 8-15)
1228 for (int i = 8; i < 16; i++) {
1229 if (static_graphics_[i] != 0) {
1231 game_data_->graphics_buffer.data());
1232 }
1233 }
1234
1235 // NOTE: Previously there was code here accessing static_graphics_[16], but
1236 // the array is only size 16 (indices 0-15). This was undefined behavior
1237 // that read random memory and sometimes corrupted the animated graphics
1238 // slot (7), causing flaky water/cloud rendering. The animated graphics
1239 // are already correctly set in static_graphics_[7] by LoadDeathMountainGFX().
1240
1241 return absl::OkStatus();
1242}
1243
1244absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16,
1245 int count) {
1246 if (current_blockset_.size() == 0)
1247 current_blockset_.resize(0x100000, 0x00);
1248
1249 const int offsets[] = {0x00, 0x08, 0x400, 0x408};
1250 auto yy = 0;
1251 auto xx = 0;
1252
1253 for (auto i = 0; i < count; i++) {
1254 for (auto tile = 0; tile < 0x04; tile++) {
1255 gfx::TileInfo info = tiles16[i].tiles_info[tile];
1256 int offset = offsets[tile];
1257 for (auto y = 0; y < 0x08; ++y) {
1258 for (auto x = 0; x < 0x08; ++x) {
1259 int mx = x;
1260 int my = y;
1261
1262 if (info.horizontal_mirror_ != 0) {
1263 mx = 0x07 - x;
1264 }
1265
1266 if (info.vertical_mirror_ != 0) {
1267 my = 0x07 - y;
1268 }
1269
1270 int xpos = ((info.id_ % 0x10) * 0x08);
1271 int ypos = (((info.id_ / 0x10)) * 0x400);
1272 int source = ypos + xpos + (x + (y * 0x80));
1273
1274 auto destination = xx + yy + offset + (mx + (my * 0x80));
1276 (current_gfx_[source] & 0x0F) + (info.palette_ * 0x10);
1277 }
1278 }
1279 }
1280
1281 xx += 0x10;
1282 if (xx >= 0x80) {
1283 yy += 0x800;
1284 xx = 0;
1285 }
1286 }
1287
1288 return absl::OkStatus();
1289}
1290
1291absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
1292 if (bitmap_data_.size() != 0) {
1293 bitmap_data_.clear();
1294 }
1295 bitmap_data_.reserve(0x40000);
1296 for (int i = 0; i < 0x40000; i++) {
1297 bitmap_data_.push_back(0x00);
1298 }
1299
1300 int superY = ((index_ - (world_ * 0x40)) / 0x08);
1301 int superX = index_ - (world_ * 0x40) - (superY * 0x08);
1302
1303 for (int y = 0; y < 0x20; y++) {
1304 for (int x = 0; x < 0x20; x++) {
1305 auto xt = x + (superX * 0x20);
1306 auto yt = y + (superY * 0x20);
1307 gfx::CopyTile8bpp16((x * 0x10), (y * 0x10), world_blockset[xt][yt],
1309 }
1310 }
1311 return absl::OkStatus();
1312}
1313
1314} // namespace yaze::zelda3
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
auto size() const
Definition rom.h:134
static Flags & get()
Definition features.h:92
static std::unordered_map< uint8_t, gfx::Paletteset > palettesets_
SNES Color container.
Definition snes_color.h:110
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void set_size(size_t size)
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size, const uint8_t *all_gfx)
zelda3_version_pointers version_constants() const
std::vector< uint8_t > current_gfx_
std::vector< uint8_t > current_blockset_
std::array< bool, 4 > mosaic_expanded_
absl::Status LoadVanillaOverlayData()
std::vector< uint8_t > bitmap_data_
absl::Status BuildMapWithCache(int count, int game_state, int world, std::vector< gfx::Tile16 > &tiles16, OverworldBlockset &world_blockset, const std::vector< uint8_t > *cached_tileset)
Build map with optional cached tileset for performance.
void SetupCustomTileset(uint8_t asm_version)
absl::StatusOr< gfx::SnesPalette > GetPalette(const gfx::PaletteGroup &group, int index, int previous_index, int limit)
absl::Status BuildTiles16Gfx(std::vector< gfx::Tile16 > &tiles16, int count)
absl::Status BuildMap(int count, int game_state, int world, std::vector< gfx::Tile16 > &tiles16, OverworldBlockset &world_blockset)
std::array< uint8_t, 3 > sprite_graphics_
uint8_t ComputeWorldBasedMainPalette() const
absl::Status BuildBitmap(OverworldBlockset &world_blockset)
std::array< uint8_t, 3 > sprite_palette_
std::array< uint8_t, 4 > area_music_
std::array< uint8_t, 16 > static_graphics_
std::array< uint8_t, 8 > custom_gfx_ids_
std::vector< uint8_t > overlay_data_
void UseCachedTileset(const std::vector< uint8_t > &cached_gfx)
Use a pre-computed tileset from cache instead of rebuilding.
gfx::SnesPalette current_palette_
static bool SupportsCustomBGColors(OverworldVersion version)
Check if ROM supports custom background colors per area (v2+)
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static uint8_t GetAsmVersion(const Rom &rom)
Get raw ASM version byte from ROM.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
static bool SupportsCustomTileGFX(OverworldVersion version)
Check if ROM supports custom tile GFX groups (v3+)
int main(int argc, char **argv)
Definition emu.cc:39
struct destination destination
Room transition destination.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
void CopyTile8bpp16(int x, int y, int tile, std::vector< uint8_t > &bitmap, std::vector< uint8_t > &blockset)
Definition snes_tile.cc:436
absl::Status SetColorsPalette(Rom &rom, GameData *game_data, int index, gfx::SnesPalette &current, gfx::SnesPalette main, gfx::SnesPalette animated, gfx::SnesPalette aux1, gfx::SnesPalette aux2, gfx::SnesPalette hud, gfx::SnesColor bgrcolor, gfx::SnesPalette spr, gfx::SnesPalette spr2)
Zelda 3 specific classes and functions.
Definition editor.h:35
constexpr int kAreaGfxIdPtr
Definition overworld.h:117
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldSpritePaletteGroup
Definition overworld.h:109
constexpr int kOverworldSpriteset
Definition overworld.h:110
constexpr int kOverworldScreenSize
Definition overworld.h:143
constexpr int kOverlayData1
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
Definition common.h:85
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldSpecialSpritePaletteExpandedTemp
constexpr int kOverworldMapParentIdExpanded
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp
constexpr int kOverworldMusicBeginning
Definition overworld.h:120
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int kOverworldMusicDarkWorld
Definition overworld.h:124
constexpr int kOverlayPointersBank
constexpr int kOverworldSpecialPalGroup
Definition overworld.h:112
constexpr int kOverworldSpritePaletteIds
Definition overworld.h:108
constexpr int OverworldCustomASMHasBeenApplied
Definition common.h:89
constexpr int kOverworldMusicAgahnim
Definition overworld.h:123
constexpr int kOverworldMapParentId
Definition overworld.h:140
@ kZSCustomV2
Parent system, BG colors, main palettes.
@ kVanilla
0xFF in ROM, no ZScream ASM applied
@ kZSCustomV3
Area enum, wide/tall areas, all features.
constexpr int kOverworldPalettesScreenToSetNew
constexpr int kOverworldMessagesExpanded
constexpr int kOverworldMusicMasterSword
Definition overworld.h:122
constexpr int kOverworldMusicZelda
Definition overworld.h:121
constexpr int kOverworldMessageIds
Definition overworld.h:118
constexpr int kOverlayData2
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
constexpr int OverworldCustomMosaicEnabled
constexpr int OverworldCustomTileGFXGroupArray
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
constexpr int kOverlayPointers
constexpr int kOverworldMapPaletteIds
Definition overworld.h:107
constexpr int kOverworldSpecialGfxGroup
Definition overworld.h:111
constexpr int OverworldCustomSubscreenOverlayArray
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Room transition destination.
Definition zelda.h:448
Represents a group of palettes.
Represents a set of palettes used in a SNES graphics system.
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89
std::vector< uint8_t > graphics_buffer
Definition game_data.h:82
uint32_t kOverworldMapPaletteGroup
Definition zelda.h:98
uint32_t kOverworldGfxGroups1
Definition zelda.h:94
uint32_t kSpriteBlocksetPointer
Definition zelda.h:109
uint32_t kOverworldGfxGroups2
Definition zelda.h:95