Enhance overworld editor with music editing features and custom overworld support

- Added a new Music tab in the Map Properties panel for editing music tracks associated with different game states.
- Implemented functionality to save music data for both Light and Dark World maps.
- Updated feature flags to enable custom overworld features based on ASM version, improving flexibility for ROM modifications.
- Enhanced UI elements with tooltips and popups for better user guidance on custom overworld settings.
This commit is contained in:
scawful
2025-09-27 11:32:42 -04:00
parent 8fe90c4c50
commit c6490314f3
10 changed files with 337 additions and 44 deletions

View File

@@ -155,6 +155,12 @@ void MapPropertiesSystem::DrawMapPropertiesPanel(int current_map, bool& show_map
ImGui::EndTabItem();
}
// Music Tab
if (ImGui::BeginTabItem("Music")) {
DrawMusicTab(current_map);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
@@ -521,6 +527,43 @@ void MapPropertiesSystem::DrawBasicPropertiesTab(int current_map) {
}
HOVER_HINT("Enable Mosaic effect for the current map");
// Add music editing controls
TableNextColumn(); ImGui::Text("Music (Beginning)");
TableNextColumn();
if (gui::InputHexByte("##Music0",
overworld_->mutable_overworld_map(current_map)->mutable_area_music(0),
kInputFieldSize)) {
RefreshMapProperties();
}
HOVER_HINT("Music track for game beginning state");
TableNextColumn(); ImGui::Text("Music (Zelda)");
TableNextColumn();
if (gui::InputHexByte("##Music1",
overworld_->mutable_overworld_map(current_map)->mutable_area_music(1),
kInputFieldSize)) {
RefreshMapProperties();
}
HOVER_HINT("Music track for Zelda rescued state");
TableNextColumn(); ImGui::Text("Music (Master Sword)");
TableNextColumn();
if (gui::InputHexByte("##Music2",
overworld_->mutable_overworld_map(current_map)->mutable_area_music(2),
kInputFieldSize)) {
RefreshMapProperties();
}
HOVER_HINT("Music track for Master Sword obtained state");
TableNextColumn(); ImGui::Text("Music (Agahnim)");
TableNextColumn();
if (gui::InputHexByte("##Music3",
overworld_->mutable_overworld_map(current_map)->mutable_area_music(3),
kInputFieldSize)) {
RefreshMapProperties();
}
HOVER_HINT("Music track for Agahnim defeated state");
ImGui::EndTable();
}
}
@@ -667,6 +710,74 @@ void MapPropertiesSystem::DrawTileGraphicsTab(int current_map) {
}
}
void MapPropertiesSystem::DrawMusicTab(int current_map) {
ImGui::Text("Music Settings for Different Game States:");
Separator();
if (BeginTable("MusicSettings", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Game State", ImGuiTableColumnFlags_WidthFixed, 150);
ImGui::TableSetupColumn("Music Track ID", ImGuiTableColumnFlags_WidthStretch);
const char* music_state_names[] = {
"Beginning (Pre-Zelda)",
"Zelda Rescued",
"Master Sword Obtained",
"Agahnim Defeated"
};
const char* music_descriptions[] = {
"Music before rescuing Zelda",
"Music after rescuing Zelda from Hyrule Castle",
"Music after obtaining the Master Sword",
"Music after defeating Agahnim (Dark World)"
};
for (int i = 0; i < 4; i++) {
TableNextColumn();
ImGui::Text("%s", music_state_names[i]);
TableNextColumn();
if (gui::InputHexByte(absl::StrFormat("##Music%d", i).c_str(),
overworld_->mutable_overworld_map(current_map)->mutable_area_music(i),
kInputFieldSize)) {
RefreshMapProperties();
// Update the ROM directly since music is not automatically saved
int music_address = 0;
switch (i) {
case 0: music_address = zelda3::kOverworldMusicBeginning + current_map; break;
case 1: music_address = zelda3::kOverworldMusicZelda + current_map; break;
case 2: music_address = zelda3::kOverworldMusicMasterSword + current_map; break;
case 3: music_address = zelda3::kOverworldMusicAgahnim + current_map; break;
}
if (music_address > 0) {
(*rom_)[music_address] = *overworld_->mutable_overworld_map(current_map)->mutable_area_music(i);
}
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", music_descriptions[i]);
}
}
ImGui::EndTable();
}
Separator();
ImGui::Text("Music tracks control the background music for different");
ImGui::Text("game progression states on this overworld map.");
// Show common music track IDs for reference
Separator();
ImGui::Text("Common Music Track IDs:");
ImGui::BulletText("0x02 - Overworld Theme");
ImGui::BulletText("0x05 - Kakariko Village");
ImGui::BulletText("0x07 - Lost Woods");
ImGui::BulletText("0x09 - Dark World Theme");
ImGui::BulletText("0x0F - Ganon's Tower");
ImGui::BulletText("0x11 - Death Mountain");
}
void MapPropertiesSystem::RefreshMapProperties() {
// Implementation would refresh map properties
}

View File

@@ -64,6 +64,7 @@ class MapPropertiesSystem {
void DrawSpritePropertiesTab(int current_map);
void DrawCustomFeaturesTab(int current_map);
void DrawTileGraphicsTab(int current_map);
void DrawMusicTab(int current_map);
// Utility methods
void RefreshMapProperties();

View File

@@ -802,6 +802,8 @@ void OverworldEditor::CheckForOverworldEdits() {
// Calculate the index within the overall map structure
int index_x = local_map_x * tiles_per_local_map + tile16_x;
int index_y = local_map_y * tiles_per_local_map + tile16_y;
overworld_.set_current_world(current_world_);
overworld_.set_current_map(current_map_);
int tile16_id = overworld_.GetTileFromPosition(
ow_map_canvas_.selected_tiles()[i]);
selected_world[index_x][index_y] = tile16_id;
@@ -818,6 +820,8 @@ void OverworldEditor::CheckForSelectRectangle() {
// Single tile case
if (ow_map_canvas_.selected_tile_pos().x != -1) {
overworld_.set_current_world(current_world_);
overworld_.set_current_map(current_map_);
current_tile16_ =
overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos());
ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1));
@@ -831,13 +835,18 @@ void OverworldEditor::CheckForSelectRectangle() {
}
if (ow_map_canvas_.selected_tiles().size() > 0) {
// Set the current world and map in overworld for proper tile lookup
overworld_.set_current_world(current_world_);
overworld_.set_current_map(current_map_);
for (auto &each : ow_map_canvas_.selected_tiles()) {
tile16_ids.push_back(overworld_.GetTileFromPosition(each));
}
}
}
// Create a composite image of all the tile16s selected
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_blockset_, 0x10);
if (!tile16_ids.empty()) {
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_blockset_, 0x10, ow_map_canvas_.global_scale());
}
}
absl::Status OverworldEditor::Copy() {
@@ -847,6 +856,8 @@ absl::Status OverworldEditor::Copy() {
!ow_map_canvas_.selected_tiles().empty()) {
std::vector<int> ids;
ids.reserve(ow_map_canvas_.selected_tiles().size());
overworld_.set_current_world(current_world_);
overworld_.set_current_map(current_map_);
for (const auto &pos : ow_map_canvas_.selected_tiles()) {
ids.push_back(overworld_.GetTileFromPosition(pos));
}
@@ -972,8 +983,22 @@ absl::Status OverworldEditor::CheckForCurrentMap() {
parent_map_y * kOverworldMapSize, large_map_size,
large_map_size);
} else {
const int current_map_x = current_highlighted_map % 8;
const int current_map_y = current_highlighted_map / 8;
// Calculate map coordinates accounting for world offset
int current_map_x, current_map_y;
if (current_world_ == 0) {
// Light World (0x00-0x3F)
current_map_x = current_highlighted_map % 8;
current_map_y = current_highlighted_map / 8;
} else if (current_world_ == 1) {
// Dark World (0x40-0x7F)
current_map_x = (current_highlighted_map - 0x40) % 8;
current_map_y = (current_highlighted_map - 0x40) / 8;
} else {
// Special World (0x80-0x9F) - use display coordinates based on current_world_
// The special world maps are displayed in the same 8x8 grid as LW/DW
current_map_x = (current_highlighted_map - 0x80) % 8;
current_map_y = (current_highlighted_map - 0x80) / 8;
}
ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
current_map_y * kOverworldMapSize,
kOverworldMapSize, kOverworldMapSize);
@@ -1012,7 +1037,12 @@ void OverworldEditor::CheckForMousePan() {
void OverworldEditor::DrawOverworldCanvas() {
if (all_gfx_loaded_) {
if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
// Use ASM version with flag as override to determine UI
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
bool use_custom_overworld = (asm_version != 0xFF) ||
core::FeatureFlags::get().overworld.kLoadCustomOverworld;
if (use_custom_overworld) {
map_properties_system_->DrawSimplifiedMapSettings(
current_world_, current_map_, current_map_lock_,
show_map_properties_panel_, show_custom_bg_color_editor_,
@@ -1434,6 +1464,7 @@ absl::Status OverworldEditor::Save() {
}
if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
RETURN_IF_ERROR(overworld_.SaveMapProperties());
RETURN_IF_ERROR(overworld_.SaveMusic());
}
return absl::OkStatus();
}