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