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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user