OverworldEntity update: Entrances, Exits, Items
This commit is contained in:
@@ -17,8 +17,12 @@ namespace core {
|
|||||||
|
|
||||||
std::shared_ptr<ExperimentFlags::Flags> ExperimentFlags::flags_;
|
std::shared_ptr<ExperimentFlags::Flags> ExperimentFlags::flags_;
|
||||||
|
|
||||||
std::string UppercaseHexByte(uint8_t byte) {
|
std::string UppercaseHexByte(uint8_t byte, bool leading) {
|
||||||
std::string result = absl::StrFormat("0x%02X", byte);
|
if (leading) {
|
||||||
|
std::string result = absl::StrFormat("0x%02X", byte);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
std::string result = absl::StrFormat("%02X", byte);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
std::string UppercaseHexWord(uint16_t word) {
|
std::string UppercaseHexWord(uint16_t word) {
|
||||||
|
|||||||
@@ -65,13 +65,16 @@ class ExperimentFlags {
|
|||||||
bool kSaveOverworldMaps = false;
|
bool kSaveOverworldMaps = false;
|
||||||
|
|
||||||
// Save overworld entrances to the ROM.
|
// Save overworld entrances to the ROM.
|
||||||
bool kSaveOverworldEntrances = false;
|
bool kSaveOverworldEntrances = true;
|
||||||
|
|
||||||
// Save overworld exits to the ROM.
|
// Save overworld exits to the ROM.
|
||||||
bool kSaveOverworldExits = false;
|
bool kSaveOverworldExits = true;
|
||||||
|
|
||||||
|
// Save overworld items to the ROM.
|
||||||
|
bool kSaveOverworldItems = true;
|
||||||
|
|
||||||
// Save overworld properties to the ROM.
|
// Save overworld properties to the ROM.
|
||||||
bool kSaveOverworldProperties = false;
|
bool kSaveOverworldProperties = true;
|
||||||
} overworld;
|
} overworld;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -232,7 +235,7 @@ class ImGuiIdIssuer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string UppercaseHexByte(uint8_t byte);
|
std::string UppercaseHexByte(uint8_t byte, bool leading = false);
|
||||||
std::string UppercaseHexWord(uint16_t word);
|
std::string UppercaseHexWord(uint16_t word);
|
||||||
std::string UppercaseHexLong(uint32_t dword);
|
std::string UppercaseHexLong(uint32_t dword);
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,15 @@
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define RETURN_VOID_IF_ERROR(expression) \
|
||||||
|
{ \
|
||||||
|
auto error = expression; \
|
||||||
|
if (!error.ok()) { \
|
||||||
|
std::cout << error.ToString() << std::endl; \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#define RETURN_IF_ERROR(expression) \
|
#define RETURN_IF_ERROR(expression) \
|
||||||
{ \
|
{ \
|
||||||
auto error = expression; \
|
auto error = expression; \
|
||||||
@@ -136,7 +145,7 @@ namespace app {
|
|||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
constexpr uint32_t kRedPen = 0xFF0000FF;
|
constexpr uint32_t kRedPen = 0xFF0000FF;
|
||||||
constexpr float kYazeVersion = 0.06;
|
constexpr float kYazeVersion = 0.07;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Magic numbers
|
// Magic numbers
|
||||||
|
|||||||
@@ -309,6 +309,11 @@ void DungeonEditor::DrawDungeonTabView() {
|
|||||||
for (int n = 0; n < active_rooms_.Size;) {
|
for (int n = 0; n < active_rooms_.Size;) {
|
||||||
bool open = true;
|
bool open = true;
|
||||||
|
|
||||||
|
if (active_rooms_[n] > sizeof(zelda3::dungeon::kRoomNames) / 4) {
|
||||||
|
active_rooms_.erase(active_rooms_.Data + n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::BeginTabItem(
|
if (ImGui::BeginTabItem(
|
||||||
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
|
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
|
||||||
ImGuiTabItemFlags_None)) {
|
ImGuiTabItemFlags_None)) {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class DungeonEditor : public Editor,
|
|||||||
absl::Status Undo() override { return absl::OkStatus(); }
|
absl::Status Undo() override { return absl::OkStatus(); }
|
||||||
absl::Status Redo() override { return absl::OkStatus(); }
|
absl::Status Redo() override { return absl::OkStatus(); }
|
||||||
|
|
||||||
|
void add_room(int i) { active_rooms_.push_back(i); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateDungeonRoomView();
|
void UpdateDungeonRoomView();
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
#include <imgui/misc/cpp/imgui_stdlib.h>
|
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
#include <imgui_memory_editor.h>
|
#include <imgui_memory_editor.h>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
@@ -122,6 +123,31 @@ void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer) {
|
|||||||
rom()->SetupRenderer(renderer);
|
rom()->SetupRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Function to switch the active tab in a tab bar
|
||||||
|
void SetTabBarTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) {
|
||||||
|
if (tab_bar == NULL) return;
|
||||||
|
|
||||||
|
// Find the tab item with the specified tab_id
|
||||||
|
// for (int i = 0; i < tab_bar->Tabs.Size; i++) {
|
||||||
|
ImGuiTabItem* tab_item = &tab_bar->Tabs[tab_id];
|
||||||
|
// if (tab_item->ID == tab_id) {
|
||||||
|
// Set the tab item as active
|
||||||
|
tab_item->LastFrameVisible = -1;
|
||||||
|
tab_item->LastFrameSelected = -1;
|
||||||
|
tab_bar->VisibleTabId = tab_id;
|
||||||
|
tab_bar->VisibleTabWasSubmitted = true;
|
||||||
|
tab_bar->SelectedTabId = tab_id;
|
||||||
|
tab_bar->NextSelectedTabId = tab_id;
|
||||||
|
tab_bar->ReorderRequestTabId = tab_id;
|
||||||
|
tab_bar->CurrFrameVisible = -1;
|
||||||
|
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
absl::Status MasterEditor::Update() {
|
absl::Status MasterEditor::Update() {
|
||||||
NewMasterFrame();
|
NewMasterFrame();
|
||||||
|
|
||||||
@@ -138,15 +164,22 @@ absl::Status MasterEditor::Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TAB_BAR("##TabBar")
|
TAB_BAR("##TabBar")
|
||||||
|
auto current_tab_bar = ImGui::GetCurrentContext()->CurrentTabBar;
|
||||||
|
|
||||||
gui::RenderTabItem("Overworld", [&]() {
|
if (overworld_editor_.jump_to_tab() == -1) {
|
||||||
current_editor_ = &overworld_editor_;
|
gui::RenderTabItem("Overworld", [&]() {
|
||||||
status_ = overworld_editor_.Update();
|
current_editor_ = &overworld_editor_;
|
||||||
});
|
status_ = overworld_editor_.Update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
gui::RenderTabItem("Dungeon", [&]() {
|
gui::RenderTabItem("Dungeon", [&]() {
|
||||||
current_editor_ = &dungeon_editor_;
|
current_editor_ = &dungeon_editor_;
|
||||||
status_ = dungeon_editor_.Update();
|
status_ = dungeon_editor_.Update();
|
||||||
|
if (overworld_editor_.jump_to_tab() != -1) {
|
||||||
|
dungeon_editor_.add_room(overworld_editor_.jump_to_tab());
|
||||||
|
overworld_editor_.jump_to_tab_ = -1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
gui::RenderTabItem("Graphics",
|
gui::RenderTabItem("Graphics",
|
||||||
@@ -336,6 +369,8 @@ void MasterEditor::DrawFileMenu() {
|
|||||||
&mutable_flags()->overworld.kSaveOverworldEntrances);
|
&mutable_flags()->overworld.kSaveOverworldEntrances);
|
||||||
Checkbox("Save Overworld Exits",
|
Checkbox("Save Overworld Exits",
|
||||||
&mutable_flags()->overworld.kSaveOverworldExits);
|
&mutable_flags()->overworld.kSaveOverworldExits);
|
||||||
|
Checkbox("Save Overworld Items",
|
||||||
|
&mutable_flags()->overworld.kSaveOverworldItems);
|
||||||
Checkbox("Save Overworld Properties",
|
Checkbox("Save Overworld Properties",
|
||||||
&mutable_flags()->overworld.kSaveOverworldProperties);
|
&mutable_flags()->overworld.kSaveOverworldProperties);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
@@ -537,16 +572,16 @@ void MasterEditor::DrawHelpMenu() {
|
|||||||
void MasterEditor::SaveRom() {
|
void MasterEditor::SaveRom() {
|
||||||
if (flags()->kSaveDungeonMaps) {
|
if (flags()->kSaveDungeonMaps) {
|
||||||
status_ = screen_editor_.SaveDungeonMaps();
|
status_ = screen_editor_.SaveDungeonMaps();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
}
|
}
|
||||||
if (flags()->overworld.kSaveOverworldMaps) {
|
if (flags()->overworld.kSaveOverworldMaps) {
|
||||||
if (overworld_editor_.overworld()->CreateTile32Tilemap()) {
|
if (overworld_editor_.overworld()->CreateTile32Tilemap()) {
|
||||||
status_ = overworld_editor_.overworld()->SaveMap16Tiles();
|
status_ = overworld_editor_.overworld()->SaveMap16Tiles();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
status_ = overworld_editor_.overworld()->SaveMap32Tiles();
|
status_ = overworld_editor_.overworld()->SaveMap32Tiles();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
status_ = overworld_editor_.overworld()->SaveOverworldMaps();
|
status_ = overworld_editor_.overworld()->SaveOverworldMaps();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
} else {
|
} else {
|
||||||
status_ = absl::InternalError(
|
status_ = absl::InternalError(
|
||||||
"Failed to save Overworld maps, aborting ROM save.");
|
"Failed to save Overworld maps, aborting ROM save.");
|
||||||
@@ -555,15 +590,19 @@ void MasterEditor::SaveRom() {
|
|||||||
}
|
}
|
||||||
if (flags()->overworld.kSaveOverworldEntrances) {
|
if (flags()->overworld.kSaveOverworldEntrances) {
|
||||||
status_ = overworld_editor_.overworld()->SaveEntrances();
|
status_ = overworld_editor_.overworld()->SaveEntrances();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
}
|
}
|
||||||
if (flags()->overworld.kSaveOverworldExits) {
|
if (flags()->overworld.kSaveOverworldExits) {
|
||||||
status_ = overworld_editor_.overworld()->SaveExits();
|
status_ = overworld_editor_.overworld()->SaveExits();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
|
}
|
||||||
|
if (flags()->overworld.kSaveOverworldItems) {
|
||||||
|
status_ = overworld_editor_.overworld()->SaveItems();
|
||||||
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
}
|
}
|
||||||
if (flags()->overworld.kSaveOverworldProperties) {
|
if (flags()->overworld.kSaveOverworldProperties) {
|
||||||
status_ = overworld_editor_.overworld()->SaveMapProperties();
|
status_ = overworld_editor_.overworld()->SaveMapProperties();
|
||||||
PRINT_IF_ERROR(status_);
|
RETURN_VOID_IF_ERROR(status_);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_ = rom()->SaveToFile(backup_rom_);
|
status_ = rom()->SaveToFile(backup_rom_);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
|
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||||
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
|
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||||
|
|
||||||
|
#define IMGUI_DEFINE_MATH_OPERATORS 1
|
||||||
|
|
||||||
#include <ImGuiColorTextEdit/TextEditor.h>
|
#include <ImGuiColorTextEdit/TextEditor.h>
|
||||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
|||||||
@@ -74,10 +74,6 @@ absl::Status OverworldEditor::Update() {
|
|||||||
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
|
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
|
||||||
ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags_NoMove |
|
||||||
ImGuiWindowFlags_NoSavedSettings;
|
ImGuiWindowFlags_NoSavedSettings;
|
||||||
|
|
||||||
// We demonstrate using the full viewport area or the work area (without
|
|
||||||
// menu-bars, task-bars etc.) Based on your use case you may want one or the
|
|
||||||
// other.
|
|
||||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||||
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
|
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
|
||||||
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
|
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
|
||||||
@@ -109,9 +105,7 @@ absl::Status OverworldEditor::Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldEditor::UpdateOverworldEdit() {
|
absl::Status OverworldEditor::UpdateOverworldEdit() {
|
||||||
// Draws the toolset for editing the Overworld.
|
|
||||||
RETURN_IF_ERROR(DrawToolset())
|
RETURN_IF_ERROR(DrawToolset())
|
||||||
|
|
||||||
if (BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) {
|
if (BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) {
|
||||||
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
||||||
ImGui::GetContentRegionAvail().x);
|
ImGui::GetContentRegionAvail().x);
|
||||||
@@ -127,100 +121,6 @@ absl::Status OverworldEditor::UpdateOverworldEdit() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldEditor::UpdateUsageStats() {
|
|
||||||
if (BeginTable("##UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
|
|
||||||
TableSetupColumn("Entrances");
|
|
||||||
TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
|
|
||||||
ImGui::GetContentRegionAvail().x);
|
|
||||||
TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
|
|
||||||
TableHeadersRow();
|
|
||||||
TableNextRow();
|
|
||||||
|
|
||||||
TableNextColumn();
|
|
||||||
ImGui::BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
|
|
||||||
ImGuiWindowFlags_HorizontalScrollbar);
|
|
||||||
for (int i = 0; i < 0x81; i++) {
|
|
||||||
std::string str = absl::StrFormat("%#x", i);
|
|
||||||
if (ImGui::Selectable(str.c_str(), selected_entrance_ == i,
|
|
||||||
overworld_.Entrances().at(i).deleted
|
|
||||||
? ImGuiSelectableFlags_Disabled
|
|
||||||
: 0)) {
|
|
||||||
selected_entrance_ = i;
|
|
||||||
selected_usage_map_ = overworld_.Entrances().at(i).map_id_;
|
|
||||||
properties_canvas_.set_highlight_tile_id(selected_usage_map_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
TableNextColumn();
|
|
||||||
DrawUsageGrid();
|
|
||||||
TableNextColumn();
|
|
||||||
DrawOverworldProperties();
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverworldEditor::CalculateUsageStats() {
|
|
||||||
absl::flat_hash_map<uint16_t, int> entrance_usage;
|
|
||||||
for (auto each_entrance : overworld_.Entrances()) {
|
|
||||||
if (each_entrance.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
|
||||||
each_entrance.map_id_ >= (current_world_ * 0x40)) {
|
|
||||||
entrance_usage[each_entrance.entrance_id_]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverworldEditor::DrawUsageGrid() {
|
|
||||||
// Create a grid of 8x8 squares
|
|
||||||
int totalSquares = 128;
|
|
||||||
int squaresWide = 8;
|
|
||||||
int squaresTall = (totalSquares + squaresWide - 1) /
|
|
||||||
squaresWide; // Ceiling of totalSquares/squaresWide
|
|
||||||
|
|
||||||
// Loop through each row
|
|
||||||
for (int row = 0; row < squaresTall; ++row) {
|
|
||||||
ImGui::NewLine();
|
|
||||||
|
|
||||||
for (int col = 0; col < squaresWide; ++col) {
|
|
||||||
if (row * squaresWide + col >= totalSquares) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Determine if this square should be highlighted
|
|
||||||
bool highlight = selected_usage_map_ == (row * squaresWide + col);
|
|
||||||
|
|
||||||
// Set highlight color if needed
|
|
||||||
if (highlight) {
|
|
||||||
ImGui::PushStyleColor(
|
|
||||||
ImGuiCol_Button,
|
|
||||||
ImVec4(1.0f, 0.5f, 0.0f, 1.0f)); // Or any highlight color
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a button or selectable for each square
|
|
||||||
if (ImGui::Button("##square", ImVec2(20, 20))) {
|
|
||||||
// Switch over to the room editor tab
|
|
||||||
// and add a room tab by the ID of the square
|
|
||||||
// that was clicked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset style if it was highlighted
|
|
||||||
if (highlight) {
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the square is hovered
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
// Display a tooltip with all the room properties
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep squares in the same line
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
absl::Status OverworldEditor::DrawToolset() {
|
absl::Status OverworldEditor::DrawToolset() {
|
||||||
static bool show_gfx_group = false;
|
static bool show_gfx_group = false;
|
||||||
static bool show_properties = false;
|
static bool show_properties = false;
|
||||||
@@ -369,35 +269,6 @@ absl::Status OverworldEditor::DrawToolset() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::DrawOverworldProperties() {
|
|
||||||
static bool init_properties = false;
|
|
||||||
|
|
||||||
if (!init_properties) {
|
|
||||||
for (int i = 0; i < 0x40; i++) {
|
|
||||||
std::string area_graphics_str = absl::StrFormat(
|
|
||||||
|
|
||||||
"0x%02hX", overworld_.overworld_map(i)->area_graphics());
|
|
||||||
properties_canvas_.mutable_labels(0)->push_back(area_graphics_str);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 0x40; i++) {
|
|
||||||
std::string area_palette_str = absl::StrFormat(
|
|
||||||
"0x%02hX", overworld_.overworld_map(i)->area_palette());
|
|
||||||
properties_canvas_.mutable_labels(1)->push_back(area_palette_str);
|
|
||||||
}
|
|
||||||
init_properties = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Area Graphics")) {
|
|
||||||
properties_canvas_.set_current_labels(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Area Palette")) {
|
|
||||||
properties_canvas_.set_current_labels(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
properties_canvas_.UpdateInfoGrid(ImVec2(512, 512), 16, 1.0f, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void OverworldEditor::RefreshChildMap(int map_index) {
|
void OverworldEditor::RefreshChildMap(int map_index) {
|
||||||
@@ -572,9 +443,25 @@ void OverworldEditor::DrawOverworldMapSettings() {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace {
|
namespace entity_internal {
|
||||||
void MoveEntranceOnGrid(zelda3::OverworldEntrance &entrance, ImVec2 canvas_p0,
|
|
||||||
ImVec2 scrolling) {
|
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
|
||||||
|
ImVec2 canvas_p0, ImVec2 scrolling) {
|
||||||
|
// Get the mouse position relative to the canvas
|
||||||
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
|
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
|
||||||
|
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
||||||
|
|
||||||
|
// Check if the mouse is hovering over the entity
|
||||||
|
if (mouse_pos.x >= entity.x_ && mouse_pos.x <= entity.x_ + 16 &&
|
||||||
|
mouse_pos.y >= entity.y_ && mouse_pos.y <= entity.y_ + 16) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, bool free_movement = false) {
|
||||||
// Get the mouse position relative to the canvas
|
// Get the mouse position relative to the canvas
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
|
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
|
||||||
@@ -583,151 +470,203 @@ void MoveEntranceOnGrid(zelda3::OverworldEntrance &entrance, ImVec2 canvas_p0,
|
|||||||
// Calculate the new position on the 16x16 grid
|
// Calculate the new position on the 16x16 grid
|
||||||
int new_x = static_cast<int>(mouse_pos.x) / 16 * 16;
|
int new_x = static_cast<int>(mouse_pos.x) / 16 * 16;
|
||||||
int new_y = static_cast<int>(mouse_pos.y) / 16 * 16;
|
int new_y = static_cast<int>(mouse_pos.y) / 16 * 16;
|
||||||
|
if (free_movement) {
|
||||||
|
new_x = static_cast<int>(mouse_pos.x) / 8 * 8;
|
||||||
|
new_y = static_cast<int>(mouse_pos.y) / 8 * 8;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the entrance position
|
// Update the entity position
|
||||||
entrance.x_ = new_x;
|
entity->set_x(new_x);
|
||||||
entrance.y_ = new_y;
|
entity->set_y(new_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance) {
|
void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, bool &is_dragging_entity,
|
||||||
|
zelda3::OverworldEntity *&dragged_entity,
|
||||||
|
zelda3::OverworldEntity *¤t_entity,
|
||||||
|
bool free_movement = false) {
|
||||||
|
std::string entity_type = "Entity";
|
||||||
|
if (entity->type_ == zelda3::OverworldEntity::EntityType::kExit) {
|
||||||
|
entity_type = "Exit";
|
||||||
|
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kEntrance) {
|
||||||
|
entity_type = "Entrance";
|
||||||
|
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kSprite) {
|
||||||
|
entity_type = "Sprite";
|
||||||
|
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kItem) {
|
||||||
|
entity_type = "Item";
|
||||||
|
}
|
||||||
|
if (IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling) &&
|
||||||
|
ImGui::IsMouseDragging(ImGuiMouseButton_Left) && !is_dragging_entity) {
|
||||||
|
dragged_entity = entity;
|
||||||
|
is_dragging_entity = true;
|
||||||
|
} else if (IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling) &&
|
||||||
|
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
|
current_entity = entity;
|
||||||
|
ImGui::OpenPopup(absl::StrFormat("%s editor", entity_type.c_str()).c_str());
|
||||||
|
} else if (is_dragging_entity && dragged_entity == entity &&
|
||||||
|
ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||||
|
MoveEntityOnGrid(dragged_entity, canvas_p0, scrolling, free_movement);
|
||||||
|
entity->UpdateMapProperties(entity->map_id_);
|
||||||
|
is_dragging_entity = false;
|
||||||
|
dragged_entity = nullptr;
|
||||||
|
} else if (is_dragging_entity && dragged_entity == entity) {
|
||||||
|
if (ImGui::BeginDragDropSource()) {
|
||||||
|
ImGui::SetDragDropPayload("ENTITY_PAYLOAD", &entity,
|
||||||
|
sizeof(zelda3::OverworldEntity));
|
||||||
|
Text("Moving %s ID: %s", entity_type.c_str(),
|
||||||
|
core::UppercaseHexByte(entity->entity_id_).c_str());
|
||||||
|
ImGui::EndDragDropSource();
|
||||||
|
}
|
||||||
|
MoveEntityOnGrid(dragged_entity, canvas_p0, scrolling, free_movement);
|
||||||
|
entity->x_ = dragged_entity->x_;
|
||||||
|
entity->y_ = dragged_entity->y_;
|
||||||
|
entity->UpdateMapProperties(entity->map_id_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace entity_internal
|
||||||
|
|
||||||
|
namespace entrance_internal {
|
||||||
|
|
||||||
|
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance) {
|
||||||
|
static bool set_done = false;
|
||||||
|
if (set_done) {
|
||||||
|
set_done = false;
|
||||||
|
}
|
||||||
if (ImGui::BeginPopupModal("Entrance editor", NULL,
|
if (ImGui::BeginPopupModal("Entrance editor", NULL,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
static uint16_t map_id = entrance.map_id_;
|
gui::InputHex("Map ID", &entrance.map_id_);
|
||||||
static int entrance_id = entrance.entrance_id_;
|
gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
|
||||||
static int x = entrance.x_;
|
kInputFieldSize + 20);
|
||||||
static int y = entrance.y_;
|
gui::InputHex("X", &entrance.x_);
|
||||||
|
gui::InputHex("Y", &entrance.y_);
|
||||||
|
|
||||||
gui::InputHexWord("Map ID", &map_id, kInputFieldSize + 20);
|
if (ImGui::Button(ICON_MD_DONE)) {
|
||||||
gui::InputHexByte("Entrance ID", &entrance.entrance_id_, kInputFieldSize);
|
|
||||||
ImGui::SetNextItemWidth(100.f);
|
|
||||||
ImGui::InputInt("X", &x);
|
|
||||||
ImGui::SetNextItemWidth(100.f);
|
|
||||||
ImGui::InputInt("Y", &y);
|
|
||||||
|
|
||||||
if (ImGui::Button("OK")) {
|
|
||||||
// Implement what happens when OK is pressed
|
|
||||||
entrance.map_id_ = map_id;
|
|
||||||
entrance.entrance_id_ = entrance_id;
|
|
||||||
entrance.x_ = x;
|
|
||||||
entrance.y_ = y;
|
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_CANCEL)) {
|
||||||
if (ImGui::Button("Cancel")) {
|
set_done = true;
|
||||||
// Implement what happens when Cancel is pressed
|
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
return set_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace entrance_internal
|
||||||
|
|
||||||
bool OverworldEditor::IsMouseHoveringOverEntrance(
|
void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
const zelda3::OverworldEntrance &entrance, ImVec2 canvas_p0,
|
bool holes) {
|
||||||
ImVec2 scrolling) {
|
int i = 0;
|
||||||
// Get the mouse position relative to the canvas
|
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
|
||||||
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
|
|
||||||
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
|
||||||
|
|
||||||
// Check if the mouse is hovering over the entrance
|
|
||||||
if (mouse_pos.x >= entrance.x_ && mouse_pos.x <= entrance.x_ + 16 &&
|
|
||||||
mouse_pos.y >= entrance.y_ && mouse_pos.y <= entrance.y_ + 16) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0,
|
|
||||||
ImVec2 scrolling) {
|
|
||||||
for (auto &each : overworld_.Entrances()) {
|
for (auto &each : overworld_.Entrances()) {
|
||||||
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||||
each.map_id_ >= (current_world_ * 0x40)) {
|
each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
|
||||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
|
// Make this yellow
|
||||||
ImVec4(210, 24, 210, 150));
|
auto color = ImVec4(255, 255, 0, 100);
|
||||||
|
if (each.is_hole_) {
|
||||||
|
color = ImVec4(255, 255, 255, 200);
|
||||||
|
}
|
||||||
|
if (each.deleted) {
|
||||||
|
color = ImVec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
|
||||||
std::string str = core::UppercaseHexByte(each.entrance_id_);
|
std::string str = core::UppercaseHexByte(each.entrance_id_);
|
||||||
|
|
||||||
// Check if this entrance is being clicked and dragged
|
|
||||||
if (current_mode == EditingMode::ENTRANCES) {
|
if (current_mode == EditingMode::ENTRANCES) {
|
||||||
if (IsMouseHoveringOverEntrance(each, canvas_p0, scrolling) &&
|
entity_internal::HandleEntityDragging(&each, canvas_p0, scrolling,
|
||||||
ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
is_dragging_entity_,
|
||||||
dragged_entrance_ = &each;
|
dragged_entity_, current_entity_);
|
||||||
is_dragging_entrance_ = true;
|
|
||||||
if (ImGui::BeginDragDropSource()) {
|
if (entity_internal::IsMouseHoveringOverEntity(each, canvas_p0,
|
||||||
ImGui::SetDragDropPayload("ENTRANCE_PAYLOAD", &each,
|
scrolling) &&
|
||||||
sizeof(zelda3::OverworldEntrance));
|
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||||
Text("Moving Entrance ID: %s", str.c_str());
|
jump_to_tab_ = each.entrance_id_;
|
||||||
ImGui::EndDragDropSource();
|
}
|
||||||
}
|
|
||||||
} else if (IsMouseHoveringOverEntrance(each, canvas_p0, scrolling) &&
|
if (entity_internal::IsMouseHoveringOverEntity(each, canvas_p0,
|
||||||
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
scrolling) &&
|
||||||
current_entrance_ = &each;
|
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
ImGui::OpenPopup("Entrance editor");
|
current_entrance_id_ = i;
|
||||||
} else if (is_dragging_entrance_ && dragged_entrance_ == &each &&
|
current_entrance_ = each;
|
||||||
ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
|
||||||
MoveEntranceOnGrid(*dragged_entrance_, canvas_p0, scrolling);
|
|
||||||
each.x_ = dragged_entrance_->x_;
|
|
||||||
each.y_ = dragged_entrance_->y_;
|
|
||||||
each.UpdateMapProperties(each.map_id_);
|
|
||||||
is_dragging_entrance_ = false;
|
|
||||||
dragged_entrance_ = nullptr;
|
|
||||||
} else if (is_dragging_entrance_ && dragged_entrance_ == &each) {
|
|
||||||
MoveEntranceOnGrid(*dragged_entrance_, canvas_p0, scrolling);
|
|
||||||
each.x_ = dragged_entrance_->x_;
|
|
||||||
each.y_ = dragged_entrance_->y_;
|
|
||||||
each.UpdateMapProperties(each.map_id_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2);
|
ow_map_canvas_.DrawText(str, each.x_, each.y_);
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawOverworldEntrancePopup(*current_entrance_);
|
if (entrance_internal::DrawOverworldEntrancePopup(
|
||||||
}
|
overworld_.Entrances()[current_entrance_id_])) {
|
||||||
|
overworld_.Entrances()[current_entrance_id_] = current_entrance_;
|
||||||
namespace {
|
|
||||||
bool IsMouseHoveringOverExit(const zelda3::OverworldExit &exit,
|
|
||||||
ImVec2 canvas_p0, ImVec2 scrolling) {
|
|
||||||
// Get the mouse position relative to the canvas
|
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
|
||||||
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
|
|
||||||
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
|
||||||
|
|
||||||
// Check if the mouse is hovering over the entrance
|
|
||||||
if (mouse_pos.x >= exit.x_ && mouse_pos.x <= exit.x_ + 16 &&
|
|
||||||
mouse_pos.y >= exit.y_ && mouse_pos.y <= exit.y_ + 16) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawExitEditorPopup(zelda3::OverworldExit &exit) {
|
namespace exit_internal {
|
||||||
|
|
||||||
|
bool DrawExitEditorPopup(zelda3::OverworldExit &exit) {
|
||||||
|
static bool set_done = false;
|
||||||
|
if (set_done) {
|
||||||
|
set_done = false;
|
||||||
|
}
|
||||||
if (ImGui::BeginPopupModal("Exit editor", NULL,
|
if (ImGui::BeginPopupModal("Exit editor", NULL,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
static int room = 385;
|
// Normal door: None = 0, Wooden = 1, Bombable = 2
|
||||||
static int scrollY = 0, centerY = 143, yPos = 128;
|
static int doorType = exit.door_type_1_;
|
||||||
static int scrollX = 256, centerX = 397, xPos = 496;
|
// Fancy door: None = 0, Sanctuary = 1, Palace = 2
|
||||||
static int doorType = 0; // Normal door: None = 0, Wooden = 1, Bombable = 2
|
static int fancyDoorType = exit.door_type_2_;
|
||||||
static int fancyDoorType =
|
|
||||||
0; // Fancy door: None = 0, Sanctuary = 1, Palace = 2
|
static int xPos = 0;
|
||||||
static int map = 128, unk1 = 0, unk2 = 0;
|
static int yPos = 0;
|
||||||
static int linkPosture = 2, spriteGFX = 12;
|
|
||||||
static int bgGFX = 47, palette = 10, sprPal = 8;
|
// Special overworld exit properties
|
||||||
static int top = 0, bottom = 32, left = 256, right = 256;
|
static int centerY = 0;
|
||||||
|
static int centerX = 0;
|
||||||
|
static int unk1 = 0;
|
||||||
|
static int unk2 = 0;
|
||||||
|
static int linkPosture = 0;
|
||||||
|
static int spriteGFX = 0;
|
||||||
|
static int bgGFX = 0;
|
||||||
|
static int palette = 0;
|
||||||
|
static int sprPal = 0;
|
||||||
|
static int top = 0;
|
||||||
|
static int bottom = 0;
|
||||||
|
static int left = 0;
|
||||||
|
static int right = 0;
|
||||||
static int leftEdgeOfMap = 0;
|
static int leftEdgeOfMap = 0;
|
||||||
|
|
||||||
ImGui::InputScalar("Room", ImGuiDataType_U8, &exit.room_id_);
|
gui::InputHexWord("Room", &exit.room_id_);
|
||||||
ImGui::InputInt("Scroll Y", &scrollY);
|
ImGui::SameLine();
|
||||||
ImGui::InputInt("Center Y", ¢erY);
|
gui::InputHex("Entity ID", &exit.entity_id_, 4);
|
||||||
ImGui::InputInt("Y pos", &yPos);
|
gui::InputHex("Map", &exit.map_id_);
|
||||||
ImGui::InputInt("Scroll X", &scrollX);
|
ImGui::SameLine();
|
||||||
ImGui::InputInt("Center X", ¢erX);
|
ImGui::Checkbox("Automatic", &exit.is_automatic_);
|
||||||
ImGui::InputInt("X pos", &xPos);
|
|
||||||
|
gui::InputHex("X Positon", &exit.x_);
|
||||||
|
ImGui::SameLine();
|
||||||
|
gui::InputHex("Y Position", &exit.y_);
|
||||||
|
|
||||||
|
gui::InputHexByte("X Camera", &exit.x_camera_);
|
||||||
|
ImGui::SameLine();
|
||||||
|
gui::InputHexByte("Y Camera", &exit.y_camera_);
|
||||||
|
|
||||||
|
gui::InputHexWord("X Scroll", &exit.x_scroll_);
|
||||||
|
ImGui::SameLine();
|
||||||
|
gui::InputHexWord("Y Scroll", &exit.y_scroll_);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
static bool show_properties = false;
|
||||||
|
ImGui::Checkbox("Show properties", &show_properties);
|
||||||
|
if (show_properties) {
|
||||||
|
ImGui::Text("Deleted? %s", exit.deleted ? "true" : "false");
|
||||||
|
ImGui::Text("Hole? %s", exit.is_hole_ ? "true" : "false");
|
||||||
|
ImGui::Text("Large Map? %s", exit.large_map_ ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::TextWithSeparators("Unimplemented below");
|
||||||
|
|
||||||
ImGui::RadioButton("None", &doorType, 0);
|
ImGui::RadioButton("None", &doorType, 0);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -736,102 +675,205 @@ void DrawExitEditorPopup(zelda3::OverworldExit &exit) {
|
|||||||
ImGui::RadioButton("Bombable", &doorType, 2);
|
ImGui::RadioButton("Bombable", &doorType, 2);
|
||||||
// If door type is not None, input positions
|
// If door type is not None, input positions
|
||||||
if (doorType != 0) {
|
if (doorType != 0) {
|
||||||
ImGui::InputInt("Door X pos",
|
gui::InputHex("Door X pos", &xPos);
|
||||||
&xPos); // Placeholder for door's X position
|
gui::InputHex("Door Y pos", &yPos);
|
||||||
ImGui::InputInt("Door Y pos",
|
|
||||||
&yPos); // Placeholder for door's Y position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::RadioButton("None##Fancy", &fancyDoorType, 0);
|
ImGui::RadioButton("None##Fancy", &fancyDoorType, 0);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::RadioButton("Sanctuary", &fancyDoorType
|
ImGui::RadioButton("Sanctuary", &fancyDoorType, 1);
|
||||||
|
|
||||||
,
|
|
||||||
1);
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::RadioButton("Palace", &fancyDoorType, 2);
|
ImGui::RadioButton("Palace", &fancyDoorType, 2);
|
||||||
// If fancy door type is not None, input positions
|
// If fancy door type is not None, input positions
|
||||||
if (fancyDoorType != 0) {
|
if (fancyDoorType != 0) {
|
||||||
// Placeholder for fancy door's X position
|
// Placeholder for fancy door's X position
|
||||||
ImGui::InputInt("Fancy Door X pos", &xPos);
|
gui::InputHex("Fancy Door X pos", &xPos);
|
||||||
// Placeholder for fancy door's Y position
|
// Placeholder for fancy door's Y position
|
||||||
ImGui::InputInt("Fancy Door Y pos", &yPos);
|
gui::InputHex("Fancy Door Y pos", &yPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::InputInt("Map", &map);
|
static bool special_exit = false;
|
||||||
ImGui::InputInt("Unk1", &unk1);
|
ImGui::Checkbox("Special exit", &special_exit);
|
||||||
ImGui::InputInt("Unk2", &unk2);
|
if (special_exit) {
|
||||||
|
gui::InputHex("Center X", ¢erX);
|
||||||
|
|
||||||
ImGui::InputInt("Link's posture", &linkPosture);
|
gui::InputHex("Center Y", ¢erY);
|
||||||
ImGui::InputInt("Sprite GFX", &spriteGFX);
|
gui::InputHex("Unk1", &unk1);
|
||||||
ImGui::InputInt("BG GFX", &bgGFX);
|
gui::InputHex("Unk2", &unk2);
|
||||||
ImGui::InputInt("Palette", &palette);
|
|
||||||
ImGui::InputInt("Spr Pal", &sprPal);
|
|
||||||
|
|
||||||
ImGui::InputInt("Top", &top);
|
gui::InputHex("Link's posture", &linkPosture);
|
||||||
ImGui::InputInt("Bottom", &bottom);
|
gui::InputHex("Sprite GFX", &spriteGFX);
|
||||||
ImGui::InputInt("Left", &left);
|
gui::InputHex("BG GFX", &bgGFX);
|
||||||
ImGui::InputInt("Right", &right);
|
gui::InputHex("Palette", &palette);
|
||||||
|
gui::InputHex("Spr Pal", &sprPal);
|
||||||
|
|
||||||
ImGui::InputInt("Left edge of map", &leftEdgeOfMap);
|
gui::InputHex("Top", &top);
|
||||||
|
gui::InputHex("Bottom", &bottom);
|
||||||
|
gui::InputHex("Left", &left);
|
||||||
|
gui::InputHex("Right", &right);
|
||||||
|
|
||||||
if (ImGui::Button("OK")) {
|
gui::InputHex("Left edge of map", &leftEdgeOfMap);
|
||||||
// Implement what happens when OK is pressed
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_DONE)) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (ImGui::Button("Cancel")) {
|
if (ImGui::Button(ICON_MD_CANCEL)) {
|
||||||
// Implement what happens when Cancel is pressed
|
set_done = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_DELETE)) {
|
||||||
|
exit.deleted = true;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return set_done;
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
} // namespace exit_internal
|
||||||
|
|
||||||
void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
|
void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto &each : *overworld_.exits()) {
|
for (auto &each : *overworld_.mutable_exits()) {
|
||||||
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||||
each.map_id_ >= (current_world_ * 0x40)) {
|
each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
|
||||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
|
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
|
||||||
ImVec4(255, 255, 255, 150));
|
ImVec4(255, 255, 255, 150));
|
||||||
std::string str = absl::StrFormat("%#x", each.entrance_id_);
|
if (current_mode == EditingMode::EXITS) {
|
||||||
ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2);
|
each.entity_id_ = i;
|
||||||
|
entity_internal::HandleEntityDragging(
|
||||||
|
&each, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(),
|
||||||
|
is_dragging_entity_, dragged_entity_, current_entity_, true);
|
||||||
|
|
||||||
// Check if this entrance is being clicked and dragged
|
if (entity_internal::IsMouseHoveringOverEntity(each, canvas_p0,
|
||||||
if (IsMouseHoveringOverExit(each, canvas_p0, scrolling) &&
|
scrolling) &&
|
||||||
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||||
current_exit_ = i;
|
jump_to_tab_ = each.room_id_;
|
||||||
ImGui::OpenPopup("Exit editor");
|
}
|
||||||
|
|
||||||
|
if (entity_internal::IsMouseHoveringOverEntity(each, canvas_p0,
|
||||||
|
scrolling) &&
|
||||||
|
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
|
current_exit_id_ = i;
|
||||||
|
current_exit_ = each;
|
||||||
|
current_entity_ = &each;
|
||||||
|
current_entity_->entity_id_ = i;
|
||||||
|
ImGui::OpenPopup("Exit editor");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string str = core::UppercaseHexByte(i);
|
||||||
|
ow_map_canvas_.DrawText(str, each.x_, each.y_);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawExitEditorPopup(overworld_.mutable_exits()->at(current_exit_));
|
if (exit_internal::DrawExitEditorPopup(
|
||||||
|
overworld_.mutable_exits()->at(current_exit_id_))) {
|
||||||
|
overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace item_internal {
|
||||||
|
|
||||||
|
bool DrawItemEditorPopup(zelda3::OverworldItem &item) {
|
||||||
|
static bool set_done = false;
|
||||||
|
if (set_done) {
|
||||||
|
set_done = false;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopupModal("Item editor", NULL,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::BeginChild("ScrollRegion", ImVec2(0, 150), true,
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) {
|
||||||
|
if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(),
|
||||||
|
item.id == i)) {
|
||||||
|
item.id = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_CLOSE)) {
|
||||||
|
set_done = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_DELETE)) {
|
||||||
|
item.deleted = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
return set_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace item_internal
|
||||||
|
|
||||||
|
void OverworldEditor::DrawOverworldItems() {
|
||||||
|
int i = 0;
|
||||||
|
for (auto &item : *overworld_.mutable_all_items()) {
|
||||||
|
// Get the item's bitmap and real X and Y positions
|
||||||
|
if (item.room_map_id < 0x40 + (current_world_ * 0x40) &&
|
||||||
|
item.room_map_id >= (current_world_ * 0x40) && !item.deleted) {
|
||||||
|
std::string item_name = zelda3::kSecretItemNames[item.id];
|
||||||
|
|
||||||
|
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
|
||||||
|
if (current_mode == EditingMode::ITEMS) {
|
||||||
|
if (entity_internal::IsMouseHoveringOverEntity(
|
||||||
|
item, ow_map_canvas_.zero_point(),
|
||||||
|
ow_map_canvas_.scrolling()) &&
|
||||||
|
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
|
current_item_id_ = i;
|
||||||
|
current_item_ = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this item is being clicked and dragged
|
||||||
|
entity_internal::HandleEntityDragging(
|
||||||
|
&item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(),
|
||||||
|
is_dragging_entity_, dragged_entity_, current_entity_);
|
||||||
|
}
|
||||||
|
ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item_internal::DrawItemEditorPopup(
|
||||||
|
overworld_.mutable_all_items()->at(current_item_id_))) {
|
||||||
|
overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void OverworldEditor::DrawOverworldSprites() {
|
void OverworldEditor::DrawOverworldSprites() {
|
||||||
for (const auto &sprite : overworld_.Sprites(game_state_)) {
|
for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
|
||||||
// Get the sprite's bitmap and real X and Y positions
|
int map_id = sprite.map_id();
|
||||||
auto id = sprite.id();
|
int map_x = sprite.map_x();
|
||||||
const gfx::Bitmap &sprite_bitmap = sprite_previews_[id];
|
int map_y = sprite.map_y();
|
||||||
int realX = sprite.GetRealX();
|
|
||||||
int realY = sprite.GetRealY();
|
|
||||||
|
|
||||||
// Draw the sprite's bitmap onto the canvas at its real X and Y positions
|
if (map_id < 0x40 + (current_world_ * 0x40) &&
|
||||||
ow_map_canvas_.DrawBitmap(sprite_bitmap, realX, realY);
|
map_id >= (current_world_ * 0x40) && !sprite.deleted()) {
|
||||||
ow_map_canvas_.DrawRect(realX, realY, sprite.Width(), sprite.Height(),
|
// auto globalx = (((map_id & 0x7) * 512) + (map_x * 16));
|
||||||
ImVec4(255, 0, 0, 150));
|
// auto globaly = (((map_id & 0x3F) / 8 * 512) + (map_y * 16));
|
||||||
std::string str = absl::StrFormat("%s", sprite.Name());
|
|
||||||
ow_map_canvas_.DrawText(str, realX - 4, realY - 2);
|
ow_map_canvas_.DrawRect(map_x, map_y, 16, 16,
|
||||||
|
/*magenta*/ ImVec4(255, 0, 255, 150));
|
||||||
|
ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.Name()), map_x,
|
||||||
|
map_y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -956,7 +998,7 @@ void OverworldEditor::CheckForOverworldEdits() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::CheckForCurrentMap() {
|
void OverworldEditor::CheckForCurrentMap() {
|
||||||
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
||||||
auto mouse_position = ImGui::GetIO().MousePos;
|
auto mouse_position = ImGui::GetIO().MousePos;
|
||||||
constexpr int small_map_size = 512;
|
constexpr int small_map_size = 512;
|
||||||
auto large_map_size = 1024;
|
auto large_map_size = 1024;
|
||||||
@@ -1008,9 +1050,8 @@ void OverworldEditor::DrawOverworldCanvas() {
|
|||||||
DrawOverworldEntrances(ow_map_canvas_.zero_point(),
|
DrawOverworldEntrances(ow_map_canvas_.zero_point(),
|
||||||
ow_map_canvas_.scrolling());
|
ow_map_canvas_.scrolling());
|
||||||
DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
|
DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
|
||||||
if (flags()->overworld.kDrawOverworldSprites) {
|
DrawOverworldItems();
|
||||||
DrawOverworldSprites();
|
DrawOverworldSprites();
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) CheckForCurrentMap();
|
if (ImGui::IsItemHovered()) CheckForCurrentMap();
|
||||||
CheckForOverworldEdits();
|
CheckForOverworldEdits();
|
||||||
}
|
}
|
||||||
@@ -1187,6 +1228,35 @@ absl::Status OverworldEditor::LoadSpriteGraphics() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverworldEditor::DrawOverworldProperties() {
|
||||||
|
static bool init_properties = false;
|
||||||
|
|
||||||
|
if (!init_properties) {
|
||||||
|
for (int i = 0; i < 0x40; i++) {
|
||||||
|
std::string area_graphics_str = absl::StrFormat(
|
||||||
|
|
||||||
|
"0x%02hX", overworld_.overworld_map(i)->area_graphics());
|
||||||
|
properties_canvas_.mutable_labels(0)->push_back(area_graphics_str);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 0x40; i++) {
|
||||||
|
std::string area_palette_str = absl::StrFormat(
|
||||||
|
"0x%02hX", overworld_.overworld_map(i)->area_palette());
|
||||||
|
properties_canvas_.mutable_labels(1)->push_back(area_palette_str);
|
||||||
|
}
|
||||||
|
init_properties = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Area Graphics")) {
|
||||||
|
properties_canvas_.set_current_labels(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Area Palette")) {
|
||||||
|
properties_canvas_.set_current_labels(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
properties_canvas_.UpdateInfoGrid(ImVec2(512, 512), 16, 1.0f, 64);
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status OverworldEditor::DrawExperimentalModal() {
|
absl::Status OverworldEditor::DrawExperimentalModal() {
|
||||||
ImGui::Begin("Experimental", &show_experimental);
|
ImGui::Begin("Experimental", &show_experimental);
|
||||||
|
|
||||||
@@ -1236,6 +1306,127 @@ absl::Status OverworldEditor::DrawExperimentalModal() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditor::UpdateUsageStats() {
|
||||||
|
if (BeginTable("##UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
|
||||||
|
TableSetupColumn("Entrances");
|
||||||
|
TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
|
||||||
|
ImGui::GetContentRegionAvail().x);
|
||||||
|
TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||||
|
TableHeadersRow();
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
ImGui::BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
|
||||||
|
ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
for (int i = 0; i < 0x81; i++) {
|
||||||
|
std::string str = absl::StrFormat("%#x", i);
|
||||||
|
if (ImGui::Selectable(str.c_str(), selected_entrance_ == i,
|
||||||
|
overworld_.Entrances().at(i).deleted
|
||||||
|
? ImGuiSelectableFlags_Disabled
|
||||||
|
: 0)) {
|
||||||
|
selected_entrance_ = i;
|
||||||
|
selected_usage_map_ = overworld_.Entrances().at(i).map_id_;
|
||||||
|
properties_canvas_.set_highlight_tile_id(selected_usage_map_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
DrawUsageGrid();
|
||||||
|
TableNextColumn();
|
||||||
|
DrawOverworldProperties();
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverworldEditor::CalculateUsageStats() {
|
||||||
|
absl::flat_hash_map<uint16_t, int> entrance_usage;
|
||||||
|
for (auto each_entrance : overworld_.Entrances()) {
|
||||||
|
if (each_entrance.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||||
|
each_entrance.map_id_ >= (current_world_ * 0x40)) {
|
||||||
|
entrance_usage[each_entrance.entrance_id_]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverworldEditor::DrawUsageGrid() {
|
||||||
|
// Create a grid of 8x8 squares
|
||||||
|
int totalSquares = 128;
|
||||||
|
int squaresWide = 8;
|
||||||
|
int squaresTall = (totalSquares + squaresWide - 1) /
|
||||||
|
squaresWide; // Ceiling of totalSquares/squaresWide
|
||||||
|
|
||||||
|
// Loop through each row
|
||||||
|
for (int row = 0; row < squaresTall; ++row) {
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
for (int col = 0; col < squaresWide; ++col) {
|
||||||
|
if (row * squaresWide + col >= totalSquares) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Determine if this square should be highlighted
|
||||||
|
bool highlight = selected_usage_map_ == (row * squaresWide + col);
|
||||||
|
|
||||||
|
// Set highlight color if needed
|
||||||
|
if (highlight) {
|
||||||
|
ImGui::PushStyleColor(
|
||||||
|
ImGuiCol_Button,
|
||||||
|
ImVec4(1.0f, 0.5f, 0.0f, 1.0f)); // Or any highlight color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a button or selectable for each square
|
||||||
|
if (ImGui::Button("##square", ImVec2(20, 20))) {
|
||||||
|
// Switch over to the room editor tab
|
||||||
|
// and add a room tab by the ID of the square
|
||||||
|
// that was clicked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset style if it was highlighted
|
||||||
|
if (highlight) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the square is hovered
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
// Display a tooltip with all the room properties
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep squares in the same line
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverworldEditor::LoadAnimatedMaps() {
|
||||||
|
int world_index = 0;
|
||||||
|
static std::vector<bool> animated_built(0x40, false);
|
||||||
|
if (!animated_built[world_index]) {
|
||||||
|
animated_maps_[world_index] = maps_bmp_[world_index];
|
||||||
|
auto &map = *overworld_.mutable_overworld_map(world_index);
|
||||||
|
map.DrawAnimatedTiles();
|
||||||
|
map.BuildTileset();
|
||||||
|
map.BuildTiles16Gfx(overworld_.tiles16().size());
|
||||||
|
OWBlockset blockset;
|
||||||
|
if (current_world_ == 0) {
|
||||||
|
blockset = overworld_.map_tiles().light_world;
|
||||||
|
} else if (current_world_ == 1) {
|
||||||
|
blockset = overworld_.map_tiles().dark_world;
|
||||||
|
} else {
|
||||||
|
blockset = overworld_.map_tiles().special_world;
|
||||||
|
}
|
||||||
|
map.BuildBitmap(blockset);
|
||||||
|
|
||||||
|
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200, map.BitmapData(),
|
||||||
|
*rom(), animated_maps_[world_index],
|
||||||
|
*map.mutable_current_palette());
|
||||||
|
|
||||||
|
animated_built[world_index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void OverworldEditor::DrawDebugWindow() {
|
void OverworldEditor::DrawDebugWindow() {
|
||||||
ImGui::Text("Current Map: %d", current_map_);
|
ImGui::Text("Current Map: %d", current_map_);
|
||||||
ImGui::Text("Current Tile16: %d", current_tile16_);
|
ImGui::Text("Current Tile16: %d", current_tile16_);
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ class OverworldEditor : public Editor,
|
|||||||
|
|
||||||
auto overworld() { return &overworld_; }
|
auto overworld() { return &overworld_; }
|
||||||
|
|
||||||
|
int jump_to_tab() { return jump_to_tab_; }
|
||||||
|
int jump_to_tab_ = -1;
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
for (auto &bmp : tile16_individual_) {
|
for (auto &bmp : tile16_individual_) {
|
||||||
bmp.Cleanup();
|
bmp.Cleanup();
|
||||||
@@ -90,10 +93,6 @@ class OverworldEditor : public Editor,
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
absl::Status UpdateOverworldEdit();
|
absl::Status UpdateOverworldEdit();
|
||||||
absl::Status UpdateUsageStats();
|
|
||||||
|
|
||||||
void DrawUsageGrid();
|
|
||||||
void CalculateUsageStats();
|
|
||||||
|
|
||||||
absl::Status DrawToolset();
|
absl::Status DrawToolset();
|
||||||
void DrawOverworldMapSettings();
|
void DrawOverworldMapSettings();
|
||||||
@@ -103,17 +102,16 @@ class OverworldEditor : public Editor,
|
|||||||
void RefreshMapPalette();
|
void RefreshMapPalette();
|
||||||
void RefreshMapProperties();
|
void RefreshMapProperties();
|
||||||
|
|
||||||
void DrawOverworldProperties();
|
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling,
|
||||||
|
bool holes = false);
|
||||||
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling);
|
|
||||||
void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling);
|
void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling);
|
||||||
void DrawOverworldMaps();
|
void DrawOverworldItems();
|
||||||
void DrawOverworldSprites();
|
void DrawOverworldSprites();
|
||||||
|
|
||||||
|
void DrawOverworldMaps();
|
||||||
void DrawOverworldEdits();
|
void DrawOverworldEdits();
|
||||||
void RenderUpdatedMapBitmap(const ImVec2 &click_position,
|
void RenderUpdatedMapBitmap(const ImVec2 &click_position,
|
||||||
const Bytes &tile_data);
|
const Bytes &tile_data);
|
||||||
|
|
||||||
void CheckForOverworldEdits();
|
void CheckForOverworldEdits();
|
||||||
void CheckForCurrentMap();
|
void CheckForCurrentMap();
|
||||||
void DrawOverworldCanvas();
|
void DrawOverworldCanvas();
|
||||||
@@ -124,8 +122,16 @@ class OverworldEditor : public Editor,
|
|||||||
void DrawTileSelector();
|
void DrawTileSelector();
|
||||||
|
|
||||||
absl::Status LoadSpriteGraphics();
|
absl::Status LoadSpriteGraphics();
|
||||||
|
|
||||||
|
void DrawOverworldProperties();
|
||||||
|
|
||||||
absl::Status DrawExperimentalModal();
|
absl::Status DrawExperimentalModal();
|
||||||
|
|
||||||
|
absl::Status UpdateUsageStats();
|
||||||
|
void DrawUsageGrid();
|
||||||
|
void CalculateUsageStats();
|
||||||
|
|
||||||
|
void LoadAnimatedMaps();
|
||||||
void DrawDebugWindow();
|
void DrawDebugWindow();
|
||||||
|
|
||||||
auto gfx_group_editor() const { return gfx_group_editor_; }
|
auto gfx_group_editor() const { return gfx_group_editor_; }
|
||||||
@@ -149,7 +155,6 @@ class OverworldEditor : public Editor,
|
|||||||
int current_tile16_ = 0;
|
int current_tile16_ = 0;
|
||||||
int selected_tile_ = 0;
|
int selected_tile_ = 0;
|
||||||
int game_state_ = 0;
|
int game_state_ = 0;
|
||||||
int current_exit_ = 0;
|
|
||||||
|
|
||||||
int selected_entrance_ = 0;
|
int selected_entrance_ = 0;
|
||||||
int selected_usage_map_ = 0xFFFF;
|
int selected_usage_map_ = 0xFFFF;
|
||||||
@@ -178,10 +183,16 @@ class OverworldEditor : public Editor,
|
|||||||
bool overworld_canvas_fullscreen_ = false;
|
bool overworld_canvas_fullscreen_ = false;
|
||||||
bool middle_mouse_dragging_ = false;
|
bool middle_mouse_dragging_ = false;
|
||||||
|
|
||||||
bool IsMouseHoveringOverEntrance(const zelda3::OverworldEntrance &entrance,
|
bool is_dragging_entity_ = false;
|
||||||
ImVec2 canvas_p, ImVec2 scrolling);
|
zelda3::OverworldEntity *dragged_entity_;
|
||||||
zelda3::OverworldEntrance *dragged_entrance_;
|
zelda3::OverworldEntity *current_entity_;
|
||||||
zelda3::OverworldEntrance *current_entrance_;
|
|
||||||
|
int current_entrance_id_ = 0;
|
||||||
|
zelda3::OverworldEntrance current_entrance_;
|
||||||
|
int current_exit_id_ = 0;
|
||||||
|
zelda3::OverworldExit current_exit_;
|
||||||
|
int current_item_id_ = 0;
|
||||||
|
zelda3::OverworldItem current_item_;
|
||||||
|
|
||||||
bool show_experimental = false;
|
bool show_experimental = false;
|
||||||
std::string ow_tilemap_filename_ = "";
|
std::string ow_tilemap_filename_ = "";
|
||||||
@@ -221,6 +232,8 @@ class OverworldEditor : public Editor,
|
|||||||
gfx::BitmapTable current_graphics_set_;
|
gfx::BitmapTable current_graphics_set_;
|
||||||
gfx::BitmapTable sprite_previews_;
|
gfx::BitmapTable sprite_previews_;
|
||||||
|
|
||||||
|
gfx::BitmapTable animated_maps_;
|
||||||
|
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
};
|
};
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -87,9 +87,8 @@ void Canvas::DrawBackground(ImVec2 canvas_size, bool can_drag) {
|
|||||||
// Pan (we use a zero mouse threshold when there's no context menu)
|
// Pan (we use a zero mouse threshold when there's no context menu)
|
||||||
if (const float mouse_threshold_for_pan =
|
if (const float mouse_threshold_for_pan =
|
||||||
enable_context_menu_ ? -1.0f : 0.0f;
|
enable_context_menu_ ? -1.0f : 0.0f;
|
||||||
is_active &&
|
is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right,
|
||||||
ImGui::IsMouseDragging(ImGuiMouseButton_Right,
|
mouse_threshold_for_pan)) {
|
||||||
mouse_threshold_for_pan)) {
|
|
||||||
scrolling_.x += io.MouseDelta.x;
|
scrolling_.x += io.MouseDelta.x;
|
||||||
scrolling_.y += io.MouseDelta.y;
|
scrolling_.y += io.MouseDelta.y;
|
||||||
}
|
}
|
||||||
@@ -459,9 +458,16 @@ void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
|
|||||||
canvas_p0_.y + scrolling_.y + y + h);
|
canvas_p0_.y + scrolling_.y + y + h);
|
||||||
draw_list_->AddRectFilled(origin, size,
|
draw_list_->AddRectFilled(origin, size,
|
||||||
IM_COL32(color.x, color.y, color.z, color.w));
|
IM_COL32(color.x, color.y, color.z, color.w));
|
||||||
|
// Add a black outline
|
||||||
|
ImVec2 outline_origin(origin.x - 1, origin.y - 1);
|
||||||
|
ImVec2 outline_size(size.x + 1, size.y + 1);
|
||||||
|
draw_list_->AddRect(outline_origin, outline_size, IM_COL32(0, 0, 0, 255));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawText(std::string text, int x, int y) {
|
void Canvas::DrawText(std::string text, int x, int y) {
|
||||||
|
draw_list_->AddText(ImVec2(canvas_p0_.x + scrolling_.x + x + 1,
|
||||||
|
canvas_p0_.y + scrolling_.y + y + 1),
|
||||||
|
IM_COL32(0, 0, 0, 255), text.data());
|
||||||
draw_list_->AddText(
|
draw_list_->AddText(
|
||||||
ImVec2(canvas_p0_.x + scrolling_.x + x, canvas_p0_.y + scrolling_.y + y),
|
ImVec2(canvas_p0_.x + scrolling_.x + x, canvas_p0_.y + scrolling_.y + y),
|
||||||
IM_COL32(255, 255, 255, 255), text.data());
|
IM_COL32(255, 255, 255, 255), text.data());
|
||||||
|
|||||||
@@ -135,6 +135,13 @@ bool InputHex(const char* label, uint64_t* data) {
|
|||||||
ImGuiInputTextFlags_CharsHexadecimal);
|
ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InputHex(const char* label, int* data, int num_digits, float input_width) {
|
||||||
|
const std::string format = "%0" + std::to_string(num_digits) + "X";
|
||||||
|
return ImGui::InputScalarLeft(label, ImGuiDataType_S32, data, &kStepOneHex,
|
||||||
|
&kStepFastHex, format.c_str(), input_width,
|
||||||
|
ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
|
}
|
||||||
|
|
||||||
bool InputHexShort(const char* label, uint32_t* data) {
|
bool InputHexShort(const char* label, uint32_t* data) {
|
||||||
return ImGui::InputScalar(label, ImGuiDataType_U32, data, &kStepOneHex,
|
return ImGui::InputScalar(label, ImGuiDataType_U32, data, &kStepOneHex,
|
||||||
&kStepFastHex, "%06X",
|
&kStepFastHex, "%06X",
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ IMGUI_API bool InputHexWithScrollwheel(const char* label, uint32_t* data,
|
|||||||
float input_width = 50.f);
|
float input_width = 50.f);
|
||||||
|
|
||||||
IMGUI_API bool InputHex(const char* label, uint64_t* data);
|
IMGUI_API bool InputHex(const char* label, uint64_t* data);
|
||||||
|
IMGUI_API bool InputHex(const char* label, int* data, int num_digits = 4,
|
||||||
|
float input_width = 50.f);
|
||||||
IMGUI_API bool InputHexShort(const char* label, uint32_t* data);
|
IMGUI_API bool InputHexShort(const char* label, uint32_t* data);
|
||||||
IMGUI_API bool InputHexWord(const char* label, uint16_t* data,
|
IMGUI_API bool InputHexWord(const char* label, uint16_t* data,
|
||||||
float input_width = 50.f);
|
float input_width = 50.f);
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ absl::Status ROM::LoadFromBytes(const Bytes& data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
||||||
|
absl::Status non_firing_status;
|
||||||
if (rom_data_.empty()) {
|
if (rom_data_.empty()) {
|
||||||
return absl::InternalError("ROM data is empty.");
|
return absl::InternalError("ROM data is empty.");
|
||||||
}
|
}
|
||||||
@@ -404,8 +405,13 @@ absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
|||||||
std::replace(backup_filename.begin(), backup_filename.end(), ' ', '_');
|
std::replace(backup_filename.begin(), backup_filename.end(), ' ', '_');
|
||||||
|
|
||||||
// Now, copy the original file to the backup file
|
// Now, copy the original file to the backup file
|
||||||
std::filesystem::copy(filename, backup_filename,
|
try {
|
||||||
std::filesystem::copy_options::overwrite_existing);
|
std::filesystem::copy(filename, backup_filename,
|
||||||
|
std::filesystem::copy_options::overwrite_existing);
|
||||||
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
|
non_firing_status = absl::InternalError(absl::StrCat(
|
||||||
|
"Could not create backup file: ", backup_filename, " - ", e.what()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the other save functions
|
// Run the other save functions
|
||||||
@@ -444,6 +450,10 @@ absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
|||||||
absl::StrCat("Error while writing to ROM file: ", filename));
|
absl::StrCat("Error while writing to ROM file: ", filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!non_firing_status.ok()) {
|
||||||
|
return non_firing_status;
|
||||||
|
}
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -417,6 +417,15 @@ class ROM : public core::ExperimentFlags {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status WriteWord(int addr, uint16_t value) {
|
||||||
|
if (addr + 1 >= rom_data_.size()) {
|
||||||
|
return absl::InvalidArgumentError("Address out of range");
|
||||||
|
}
|
||||||
|
rom_data_[addr] = (uint8_t)(value & 0xFF);
|
||||||
|
rom_data_[addr + 1] = (uint8_t)((value >> 8) & 0xFF);
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status WriteShort(uint32_t addr, uint16_t value) {
|
absl::Status WriteShort(uint32_t addr, uint16_t value) {
|
||||||
if (addr + 1 >= rom_data_.size()) {
|
if (addr + 1 >= rom_data_.size()) {
|
||||||
return absl::InvalidArgumentError("Address out of range");
|
return absl::InvalidArgumentError("Address out of range");
|
||||||
@@ -426,6 +435,16 @@ class ROM : public core::ExperimentFlags {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status WriteLong(uint32_t addr, uint32_t value) {
|
||||||
|
if (addr + 2 >= rom_data_.size()) {
|
||||||
|
return absl::InvalidArgumentError("Address out of range");
|
||||||
|
}
|
||||||
|
rom_data_[addr] = (uint8_t)(value & 0xFF);
|
||||||
|
rom_data_[addr + 1] = (uint8_t)((value >> 8) & 0xFF);
|
||||||
|
rom_data_[addr + 2] = (uint8_t)((value >> 16) & 0xFF);
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status WriteVector(int addr, std::vector<uint8_t> data) {
|
absl::Status WriteVector(int addr, std::vector<uint8_t> data) {
|
||||||
if (addr + data.size() > rom_data_.size()) {
|
if (addr + data.size() > rom_data_.size()) {
|
||||||
return absl::InvalidArgumentError("Address and data size out of range");
|
return absl::InvalidArgumentError("Address and data size out of range");
|
||||||
@@ -532,7 +551,8 @@ class ROM : public core::ExperimentFlags {
|
|||||||
const uchar* operator&() { return rom_data_.data(); }
|
const uchar* operator&() { return rom_data_.data(); }
|
||||||
|
|
||||||
ushort toint16(int offset) {
|
ushort toint16(int offset) {
|
||||||
return (ushort)((rom_data_[offset + 1]) << 8) | rom_data_[offset];
|
return (uint16_t)(rom_data_[offset] | (rom_data_[offset + 1] << 8));
|
||||||
|
// return (ushort)((rom_data_[offset + 1]) << 8) | rom_data_[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupRenderer(std::shared_ptr<SDL_Renderer> renderer) {
|
void SetupRenderer(std::shared_ptr<SDL_Renderer> renderer) {
|
||||||
|
|||||||
@@ -141,10 +141,9 @@ absl::Status Overworld::Load(ROM &rom) {
|
|||||||
FetchLargeMaps();
|
FetchLargeMaps();
|
||||||
LoadEntrances();
|
LoadEntrances();
|
||||||
LoadExits();
|
LoadExits();
|
||||||
|
LoadSprites();
|
||||||
|
RETURN_IF_ERROR(LoadItems());
|
||||||
RETURN_IF_ERROR(LoadOverworldMaps())
|
RETURN_IF_ERROR(LoadOverworldMaps())
|
||||||
if (flags()->overworld.kDrawOverworldSprites) {
|
|
||||||
LoadSprites();
|
|
||||||
}
|
|
||||||
|
|
||||||
is_loaded_ = true;
|
is_loaded_ = true;
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -213,10 +212,10 @@ absl::Status Overworld::SaveOverworldMaps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compress single_map_1 and single_map_2
|
// Compress single_map_1 and single_map_2
|
||||||
ASSIGN_OR_RETURN(
|
ASSIGN_OR_RETURN(auto a,
|
||||||
auto a, gfx::lc_lz2::CompressOverworld(single_map_1, 0, 256))
|
gfx::lc_lz2::CompressOverworld(single_map_1, 0, 256))
|
||||||
ASSIGN_OR_RETURN(
|
ASSIGN_OR_RETURN(auto b,
|
||||||
auto b, gfx::lc_lz2::CompressOverworld(single_map_2, 0, 256))
|
gfx::lc_lz2::CompressOverworld(single_map_2, 0, 256))
|
||||||
if (a.empty() || b.empty()) {
|
if (a.empty() || b.empty()) {
|
||||||
return absl::AbortedError("Error compressing map gfx.");
|
return absl::AbortedError("Error compressing map gfx.");
|
||||||
}
|
}
|
||||||
@@ -557,13 +556,17 @@ absl::Status Overworld::SaveMap16Tiles() {
|
|||||||
int tpos = kMap16Tiles;
|
int tpos = kMap16Tiles;
|
||||||
// 3760
|
// 3760
|
||||||
for (int i = 0; i < NumberOfMap16; i += 1) {
|
for (int i = 0; i < NumberOfMap16; i += 1) {
|
||||||
RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)))
|
RETURN_IF_ERROR(
|
||||||
|
rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)))
|
||||||
tpos += 2;
|
tpos += 2;
|
||||||
RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)))
|
RETURN_IF_ERROR(
|
||||||
|
rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)))
|
||||||
tpos += 2;
|
tpos += 2;
|
||||||
RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)))
|
RETURN_IF_ERROR(
|
||||||
|
rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)))
|
||||||
tpos += 2;
|
tpos += 2;
|
||||||
RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)))
|
RETURN_IF_ERROR(
|
||||||
|
rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)))
|
||||||
tpos += 2;
|
tpos += 2;
|
||||||
}
|
}
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -936,6 +939,85 @@ absl::Status Overworld::SaveExits() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status Overworld::SaveItems() {
|
||||||
|
std::vector<std::vector<OverworldItem>> roomItems(128);
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
roomItems[i] = std::vector<OverworldItem>();
|
||||||
|
for (const OverworldItem &item : all_items_) {
|
||||||
|
if (item.room_map_id == i) {
|
||||||
|
roomItems[i].push_back(item);
|
||||||
|
if (item.id == 0x86) {
|
||||||
|
rom()->WriteWord(0x16DC5 + (i * 2),
|
||||||
|
(item.game_x_ + (item.game_y_ * 64)) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataPos = overworldItemsPointers + 0x100;
|
||||||
|
|
||||||
|
std::vector<int> itemPointers(128);
|
||||||
|
std::vector<int> itemPointersReuse(128);
|
||||||
|
int emptyPointer = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
itemPointersReuse[i] = -1;
|
||||||
|
for (int ci = 0; ci < i; ci++) {
|
||||||
|
if (roomItems[i].empty()) {
|
||||||
|
itemPointersReuse[i] = -2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Unclear: this.compareItemsArrays(roomItems[i].ToArray(),
|
||||||
|
// roomItems[ci].ToArray()) Commenting out for now if
|
||||||
|
// (this.compareItemsArrays(roomItems[i].ToArray(),
|
||||||
|
// roomItems[ci].ToArray())) {
|
||||||
|
// itemPointersReuse[i] = ci;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
if (itemPointersReuse[i] == -1) {
|
||||||
|
itemPointers[i] = dataPos;
|
||||||
|
for (const OverworldItem &item : roomItems[i]) {
|
||||||
|
short mapPos =
|
||||||
|
static_cast<short>(((item.game_y_ << 6) + item.game_x_) << 1);
|
||||||
|
|
||||||
|
uint32_t data = static_cast<uint8_t>(mapPos & 0xFF) |
|
||||||
|
static_cast<uint8_t>(mapPos >> 8) |
|
||||||
|
static_cast<uint8_t>(item.id);
|
||||||
|
rom()->WriteLong(dataPos, data);
|
||||||
|
// WriteType::PotItemData);
|
||||||
|
|
||||||
|
dataPos += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyPointer = dataPos;
|
||||||
|
rom()->WriteWord(dataPos, 0xFFFF);
|
||||||
|
dataPos += 2;
|
||||||
|
} else if (itemPointersReuse[i] == -2) {
|
||||||
|
itemPointers[i] = emptyPointer;
|
||||||
|
} else {
|
||||||
|
itemPointers[i] = itemPointers[itemPointersReuse[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
int snesaddr = core::PcToSnes(itemPointers[i]);
|
||||||
|
rom()->WriteWord(overworldItemsPointers + (i * 2), snesaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataPos > overworldItemsEndData) {
|
||||||
|
return absl::AbortedError("Too many items");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags()->kLogToConsole) {
|
||||||
|
std::cout << "End of Items : " << dataPos << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
void Overworld::LoadExits() {
|
void Overworld::LoadExits() {
|
||||||
const int NumberOfOverworldExits = 0x4F;
|
const int NumberOfOverworldExits = 0x4F;
|
||||||
std::vector<OverworldExit> exits;
|
std::vector<OverworldExit> exits;
|
||||||
@@ -987,7 +1069,7 @@ void Overworld::LoadExits() {
|
|||||||
<< " DoorType2: " << exit_door_type_2 << std::endl;
|
<< " DoorType2: " << exit_door_type_2 << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (px == 0xFFFF && py == 0xFFFF) {
|
if ((px & py) == 0xFFFF) {
|
||||||
exit.deleted = true;
|
exit.deleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,6 +1078,55 @@ void Overworld::LoadExits() {
|
|||||||
all_exits_ = exits;
|
all_exits_ = exits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status Overworld::LoadItems() {
|
||||||
|
ASSIGN_OR_RETURN(int pointer, rom()->ReadLong(zelda3::overworldItemsAddress));
|
||||||
|
int oointerPC = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
int addr = (pointer & 0xFF0000) + // 1B
|
||||||
|
(rom()->data()[oointerPC + (i * 2) + 1] << 8) + // F9
|
||||||
|
rom()->data()[oointerPC + (i * 2)]; // 3C
|
||||||
|
|
||||||
|
addr = core::SnesToPc(addr);
|
||||||
|
|
||||||
|
if (overworld_maps_[i].IsLargeMap()) {
|
||||||
|
if (overworld_maps_[i].Parent() != (uint8_t)i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint8_t b1 = rom()->data()[addr];
|
||||||
|
uint8_t b2 = rom()->data()[addr + 1];
|
||||||
|
uint8_t b3 = rom()->data()[addr + 2];
|
||||||
|
|
||||||
|
if (b1 == 0xFF && b2 == 0xFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int p = (((b2 & 0x1F) << 8) + b1) >> 1;
|
||||||
|
|
||||||
|
int x = p % 64;
|
||||||
|
int y = p >> 6;
|
||||||
|
|
||||||
|
int fakeID = i;
|
||||||
|
if (fakeID >= 64) {
|
||||||
|
fakeID -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sy = fakeID / 8;
|
||||||
|
int sx = fakeID - (sy * 8);
|
||||||
|
|
||||||
|
all_items_.emplace_back(zelda3::OverworldItem(
|
||||||
|
b3, (ushort)i, (x * 16) + (sx * 512), (y * 16) + (sy * 512), false));
|
||||||
|
auto size = all_items_.size();
|
||||||
|
all_items_.at(size - 1).game_x = (uint8_t)x;
|
||||||
|
all_items_.at(size - 1).game_y = (uint8_t)y;
|
||||||
|
addr += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
void Overworld::LoadSprites() {
|
void Overworld::LoadSprites() {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
all_sprites_.emplace_back();
|
all_sprites_.emplace_back();
|
||||||
@@ -1025,7 +1156,7 @@ void Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count,
|
|||||||
|
|
||||||
int ptrPos = sprite_start + (i * 2);
|
int ptrPos = sprite_start + (i * 2);
|
||||||
int sprite_address =
|
int sprite_address =
|
||||||
core::SnesToPc((0x09 << 0x10) + rom()->toint16(ptrPos));
|
core::SnesToPc((0x09 << 0x10) | rom()->toint16(ptrPos));
|
||||||
while (true) {
|
while (true) {
|
||||||
uchar b1 = rom_[sprite_address];
|
uchar b1 = rom_[sprite_address];
|
||||||
uchar b2 = rom_[sprite_address + 1];
|
uchar b2 = rom_[sprite_address + 1];
|
||||||
@@ -1033,20 +1164,20 @@ void Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count,
|
|||||||
if (b1 == 0xFF) break;
|
if (b1 == 0xFF) break;
|
||||||
|
|
||||||
int editor_map_index = i;
|
int editor_map_index = i;
|
||||||
if (editor_map_index >= 128)
|
if (sprite_index != 0) {
|
||||||
editor_map_index -= 128;
|
if (editor_map_index >= 128)
|
||||||
else if (editor_map_index >= 64)
|
editor_map_index -= 128;
|
||||||
editor_map_index -= 64;
|
else if (editor_map_index >= 64)
|
||||||
|
editor_map_index -= 64;
|
||||||
|
}
|
||||||
int mapY = (editor_map_index / 8);
|
int mapY = (editor_map_index / 8);
|
||||||
int mapX = (editor_map_index % 8);
|
int mapX = (editor_map_index % 8);
|
||||||
|
|
||||||
int realX = ((b2 & 0x3F) * 16) + mapX * 512;
|
int realX = ((b2 & 0x3F) * 16) + mapX * 512;
|
||||||
int realY = ((b1 & 0x3F) * 16) + mapY * 512;
|
int realY = ((b1 & 0x3F) * 16) + mapY * 512;
|
||||||
auto graphics_bytes = overworld_maps_[i].AreaGraphics();
|
|
||||||
all_sprites_[sprite_index][i].InitSprite(
|
all_sprites_[sprite_index][i].InitSprite(
|
||||||
graphics_bytes, (uchar)i, b3, (uchar)(b2 & 0x3F), (uchar)(b1 & 0x3F),
|
overworld_maps_[i].AreaGraphics(), (uchar)i, b3, (uchar)(b2 & 0x3F),
|
||||||
realX, realY);
|
(uchar)(b1 & 0x3F), realX, realY);
|
||||||
all_sprites_[sprite_index][i].Draw();
|
all_sprites_[sprite_index][i].Draw();
|
||||||
|
|
||||||
sprite_address += 3;
|
sprite_address += 3;
|
||||||
@@ -1133,8 +1264,6 @@ absl::Status Overworld::LoadPrototype(ROM &rom,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadSprites();
|
|
||||||
|
|
||||||
is_loaded_ = true;
|
is_loaded_ = true;
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,143 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
|
class OverworldEntity {
|
||||||
|
public:
|
||||||
|
enum EntityType {
|
||||||
|
kEntrance = 0,
|
||||||
|
kExit = 1,
|
||||||
|
kItem = 2,
|
||||||
|
kSprite = 3,
|
||||||
|
kTransport = 4,
|
||||||
|
kMusic = 5,
|
||||||
|
kTilemap = 6,
|
||||||
|
kProperties = 7
|
||||||
|
} type_;
|
||||||
|
int x_;
|
||||||
|
int y_;
|
||||||
|
int game_x_;
|
||||||
|
int game_y_;
|
||||||
|
int entity_id_;
|
||||||
|
int map_id_;
|
||||||
|
|
||||||
|
auto set_x(int x) { x_ = x; }
|
||||||
|
auto set_y(int y) { y_ = y; }
|
||||||
|
|
||||||
|
OverworldEntity() = default;
|
||||||
|
|
||||||
|
virtual void UpdateMapProperties(short map_id) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// List of secret item names
|
||||||
|
const std::vector<std::string> kSecretItemNames = {
|
||||||
|
"Nothing", // 0
|
||||||
|
"Green Rupee", // 1
|
||||||
|
"Rock hoarder", // 2
|
||||||
|
"Bee", // 3
|
||||||
|
"Health pack", // 4
|
||||||
|
"Bomb", // 5
|
||||||
|
"Heart ", // 6
|
||||||
|
"Blue Rupee", // 7
|
||||||
|
"Key", // 8
|
||||||
|
"Arrow", // 9
|
||||||
|
"Bomb", // 10
|
||||||
|
"Heart", // 11
|
||||||
|
"Magic", // 12
|
||||||
|
"Full Magic", // 13
|
||||||
|
"Cucco", // 14
|
||||||
|
"Green Soldier", // 15
|
||||||
|
"Bush Stal", // 16
|
||||||
|
"Blue Soldier", // 17
|
||||||
|
"Landmine", // 18
|
||||||
|
"Heart", // 19
|
||||||
|
"Fairy", // 20
|
||||||
|
"Heart", // 21
|
||||||
|
"Nothing ", // 22
|
||||||
|
"Hole", // 23
|
||||||
|
"Warp", // 24
|
||||||
|
"Staircase", // 25
|
||||||
|
"Bombable", // 26
|
||||||
|
"Switch" // 27
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int overworldItemsPointers = 0xDC2F9;
|
||||||
|
constexpr int overworldItemsAddress = 0xDC8B9; // 1BC2F9
|
||||||
|
constexpr int overworldItemsBank = 0xDC8BF;
|
||||||
|
constexpr int overworldItemsEndData = 0xDC89C; // 0DC89E
|
||||||
|
|
||||||
|
class OverworldItem : public OverworldEntity {
|
||||||
|
public:
|
||||||
|
bool bg2 = false;
|
||||||
|
uint8_t game_x;
|
||||||
|
uint8_t game_y;
|
||||||
|
uint8_t id;
|
||||||
|
uint16_t room_map_id;
|
||||||
|
int unique_id = 0;
|
||||||
|
bool deleted = false;
|
||||||
|
OverworldItem() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OverworldItem"/>
|
||||||
|
/// class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"> The ID. </param>
|
||||||
|
/// <param name="room_map_id"> The dungeon room ID or overworld area ID.
|
||||||
|
/// </param> <param name="x"> The in editor X position. </param> <param
|
||||||
|
/// name="y"> The in editor Y position. </param> <param name="bg2"> Whether
|
||||||
|
/// the Item is on BG2 or not. </param>
|
||||||
|
OverworldItem(uint8_t id, uint16_t room_map_id, int x, int y, bool bg2) {
|
||||||
|
this->id = id;
|
||||||
|
this->x_ = x;
|
||||||
|
this->y_ = y;
|
||||||
|
this->bg2 = bg2;
|
||||||
|
this->room_map_id = room_map_id;
|
||||||
|
this->map_id_ = room_map_id;
|
||||||
|
this->entity_id_ = id;
|
||||||
|
this->type_ = kItem;
|
||||||
|
|
||||||
|
int map_x = room_map_id - ((room_map_id / 8) * 8);
|
||||||
|
int map_y = room_map_id / 8;
|
||||||
|
|
||||||
|
this->game_x = static_cast<uint8_t>(std::abs(x - (map_x * 512)) / 16);
|
||||||
|
this->game_y = static_cast<uint8_t>(std::abs(y - (map_y * 512)) / 16);
|
||||||
|
// this->unique_id = ROM.unique_item_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the item info when needed. Generally when moving items around
|
||||||
|
/// in editor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="room_map_id"> The dungeon room ID or overworld area ID where
|
||||||
|
/// the item was moved to. </param>
|
||||||
|
void UpdateMapProperties(int16_t room_map_id) override {
|
||||||
|
this->room_map_id = static_cast<uint16_t>(room_map_id);
|
||||||
|
|
||||||
|
if (room_map_id >= 64) {
|
||||||
|
room_map_id -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_x = room_map_id - ((room_map_id / 8) * 8);
|
||||||
|
int map_y = room_map_id / 8;
|
||||||
|
|
||||||
|
this->game_x =
|
||||||
|
static_cast<uint8_t>(std::abs(this->x_ - (map_x * 512)) / 16);
|
||||||
|
this->game_y =
|
||||||
|
static_cast<uint8_t>(std::abs(this->y_ - (map_y * 512)) / 16);
|
||||||
|
|
||||||
|
std::cout << "Item: " << std::hex << std::setw(2) << std::setfill('0')
|
||||||
|
<< static_cast<int>(this->id) << " MapId: " << std::hex
|
||||||
|
<< std::setw(2) << std::setfill('0')
|
||||||
|
<< static_cast<int>(this->room_map_id)
|
||||||
|
<< " X: " << static_cast<int>(this->game_x)
|
||||||
|
<< " Y: " << static_cast<int>(this->game_y) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverworldItem Copy() {
|
||||||
|
return OverworldItem(this->id, this->room_map_id, this->x_, this->y_,
|
||||||
|
this->bg2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
constexpr int OWExitRoomId = 0x15D8A; // 0x15E07 Credits sequences
|
constexpr int OWExitRoomId = 0x15D8A; // 0x15E07 Credits sequences
|
||||||
// 105C2 Ending maps
|
// 105C2 Ending maps
|
||||||
// 105E2 Sprite Group Table for Ending
|
// 105E2 Sprite Group Table for Ending
|
||||||
@@ -50,10 +187,8 @@ constexpr int OWExitUnk1Whirlpool = 0x16BF5; // JP = ;016E91
|
|||||||
constexpr int OWExitUnk2Whirlpool = 0x16C17; // JP = ;016EB3
|
constexpr int OWExitUnk2Whirlpool = 0x16C17; // JP = ;016EB3
|
||||||
constexpr int OWWhirlpoolPosition = 0x16CF8; // JP = ;016F94
|
constexpr int OWWhirlpoolPosition = 0x16CF8; // JP = ;016F94
|
||||||
|
|
||||||
class OverworldExit {
|
class OverworldExit : public OverworldEntity {
|
||||||
public:
|
public:
|
||||||
int x_;
|
|
||||||
int y_;
|
|
||||||
ushort y_scroll_;
|
ushort y_scroll_;
|
||||||
ushort x_scroll_;
|
ushort x_scroll_;
|
||||||
uchar y_player_;
|
uchar y_player_;
|
||||||
@@ -69,23 +204,21 @@ class OverworldExit {
|
|||||||
uchar entrance_id_;
|
uchar entrance_id_;
|
||||||
uchar area_x_;
|
uchar area_x_;
|
||||||
uchar area_y_;
|
uchar area_y_;
|
||||||
short map_id_;
|
|
||||||
bool is_hole_ = false;
|
bool is_hole_ = false;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
bool is_automatic_ = false;
|
bool is_automatic_ = false;
|
||||||
|
bool large_map_ = false;
|
||||||
|
|
||||||
|
OverworldExit() = default;
|
||||||
OverworldExit(ushort room_id, uchar map_id, ushort vram_location,
|
OverworldExit(ushort room_id, uchar map_id, ushort vram_location,
|
||||||
ushort y_scroll, ushort x_scroll, ushort player_y,
|
ushort y_scroll, ushort x_scroll, ushort player_y,
|
||||||
ushort player_x, ushort camera_y, ushort camera_x,
|
ushort player_x, ushort camera_y, ushort camera_x,
|
||||||
uchar scroll_mod_y, uchar scroll_mod_x, ushort door_type_1,
|
uchar scroll_mod_y, uchar scroll_mod_x, ushort door_type_1,
|
||||||
ushort door_type_2)
|
ushort door_type_2)
|
||||||
: x_(player_x),
|
: map_pos_(vram_location),
|
||||||
y_(player_y),
|
|
||||||
map_pos_(vram_location),
|
|
||||||
entrance_id_(0),
|
entrance_id_(0),
|
||||||
area_x_(0),
|
area_x_(0),
|
||||||
area_y_(0),
|
area_y_(0),
|
||||||
map_id_(map_id),
|
|
||||||
is_hole_(false),
|
is_hole_(false),
|
||||||
room_id_(room_id),
|
room_id_(room_id),
|
||||||
y_scroll_(y_scroll),
|
y_scroll_(y_scroll),
|
||||||
@@ -98,6 +231,12 @@ class OverworldExit {
|
|||||||
scroll_mod_x_(scroll_mod_x),
|
scroll_mod_x_(scroll_mod_x),
|
||||||
door_type_1_(door_type_1),
|
door_type_1_(door_type_1),
|
||||||
door_type_2_(door_type_2) {
|
door_type_2_(door_type_2) {
|
||||||
|
// Initialize entity variables
|
||||||
|
this->x_ = player_x;
|
||||||
|
this->y_ = player_y;
|
||||||
|
this->map_id_ = map_id;
|
||||||
|
this->type_ = kExit;
|
||||||
|
|
||||||
int mapX = (map_id_ - ((map_id_ / 8) * 8));
|
int mapX = (map_id_ - ((map_id_ / 8) * 8));
|
||||||
int mapY = (map_id_ / 8);
|
int mapY = (map_id_ / 8);
|
||||||
|
|
||||||
@@ -130,14 +269,14 @@ class OverworldExit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Overworld overworld
|
// Overworld overworld
|
||||||
void UpdateMapProperties(uchar map_id, bool large_map = false) {
|
void UpdateMapProperties(short map_id) override {
|
||||||
map_id_ = map_id;
|
map_id_ = map_id;
|
||||||
|
|
||||||
int large = 256;
|
int large = 256;
|
||||||
int mapid = map_id;
|
int mapid = map_id;
|
||||||
|
|
||||||
if (map_id < 128) {
|
if (map_id < 128) {
|
||||||
large = large_map ? 768 : 256;
|
large = large_map_ ? 768 : 256;
|
||||||
// if (overworld.overworld_map(map_id)->Parent() != map_id) {
|
// if (overworld.overworld_map(map_id)->Parent() != map_id) {
|
||||||
// mapid = overworld.overworld_map(map_id)->Parent();
|
// mapid = overworld.overworld_map(map_id)->Parent();
|
||||||
// }
|
// }
|
||||||
@@ -223,29 +362,27 @@ constexpr int OWHoleArea = 0xDB826;
|
|||||||
//(0x13 entries, 1 byte each) corresponding entrance numbers
|
//(0x13 entries, 1 byte each) corresponding entrance numbers
|
||||||
constexpr int OWHoleEntrance = 0xDB84C;
|
constexpr int OWHoleEntrance = 0xDB84C;
|
||||||
|
|
||||||
class OverworldEntrance {
|
class OverworldEntrance : public OverworldEntity {
|
||||||
public:
|
public:
|
||||||
int x_;
|
|
||||||
int y_;
|
|
||||||
ushort map_pos_;
|
ushort map_pos_;
|
||||||
uchar entrance_id_;
|
uchar entrance_id_;
|
||||||
uchar area_x_;
|
uchar area_x_;
|
||||||
uchar area_y_;
|
uchar area_y_;
|
||||||
short map_id_;
|
|
||||||
bool is_hole_ = false;
|
bool is_hole_ = false;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
|
|
||||||
|
OverworldEntrance() = default;
|
||||||
OverworldEntrance(int x, int y, uchar entrance_id, short map_id,
|
OverworldEntrance(int x, int y, uchar entrance_id, short map_id,
|
||||||
ushort map_pos, bool hole)
|
ushort map_pos, bool hole)
|
||||||
: x_(x),
|
: map_pos_(map_pos), entrance_id_(entrance_id), is_hole_(hole) {
|
||||||
y_(y),
|
x_ = x;
|
||||||
map_pos_(map_pos),
|
y_ = y;
|
||||||
entrance_id_(entrance_id),
|
map_id_ = map_id;
|
||||||
map_id_(map_id),
|
entity_id_ = entrance_id;
|
||||||
is_hole_(hole) {
|
type_ = kEntrance;
|
||||||
|
|
||||||
int mapX = (map_id_ - ((map_id_ / 8) * 8));
|
int mapX = (map_id_ - ((map_id_ / 8) * 8));
|
||||||
int mapY = (map_id_ / 8);
|
int mapY = (map_id_ / 8);
|
||||||
|
|
||||||
area_x_ = (uchar)((std::abs(x - (mapX * 512)) / 16));
|
area_x_ = (uchar)((std::abs(x - (mapX * 512)) / 16));
|
||||||
area_y_ = (uchar)((std::abs(y - (mapY * 512)) / 16));
|
area_y_ = (uchar)((std::abs(y - (mapY * 512)) / 16));
|
||||||
}
|
}
|
||||||
@@ -255,7 +392,7 @@ class OverworldEntrance {
|
|||||||
is_hole_);
|
is_hole_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateMapProperties(short map_id) {
|
void UpdateMapProperties(short map_id) override {
|
||||||
map_id_ = map_id;
|
map_id_ = map_id;
|
||||||
|
|
||||||
if (map_id_ >= 64) {
|
if (map_id_ >= 64) {
|
||||||
@@ -289,10 +426,6 @@ constexpr int overworldSpecialPALGroup = 0x16831;
|
|||||||
constexpr int overworldSpritesBegining = 0x4C881;
|
constexpr int overworldSpritesBegining = 0x4C881;
|
||||||
constexpr int overworldSpritesAgahnim = 0x4CA21;
|
constexpr int overworldSpritesAgahnim = 0x4CA21;
|
||||||
constexpr int overworldSpritesZelda = 0x4C901;
|
constexpr int overworldSpritesZelda = 0x4C901;
|
||||||
constexpr int overworldItemsPointers = 0xDC2F9;
|
|
||||||
constexpr int overworldItemsAddress = 0xDC8B9; // 1BC2F9
|
|
||||||
constexpr int overworldItemsBank = 0xDC8BF;
|
|
||||||
constexpr int overworldItemsEndData = 0xDC89C; // 0DC89E
|
|
||||||
constexpr int mapGfx = 0x7C9C;
|
constexpr int mapGfx = 0x7C9C;
|
||||||
constexpr int overlayPointers = 0x77664;
|
constexpr int overlayPointers = 0x77664;
|
||||||
constexpr int overlayPointersBank = 0x0E;
|
constexpr int overlayPointersBank = 0x0E;
|
||||||
@@ -353,6 +486,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
absl::Status SaveLargeMaps();
|
absl::Status SaveLargeMaps();
|
||||||
absl::Status SaveEntrances();
|
absl::Status SaveEntrances();
|
||||||
absl::Status SaveExits();
|
absl::Status SaveExits();
|
||||||
|
absl::Status SaveItems();
|
||||||
|
|
||||||
bool CreateTile32Tilemap(bool onlyShow = false);
|
bool CreateTile32Tilemap(bool onlyShow = false);
|
||||||
absl::Status SaveMap16Tiles();
|
absl::Status SaveMap16Tiles();
|
||||||
@@ -368,10 +502,14 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
std::vector<gfx::Tile16> tiles16() const { return tiles16_; }
|
std::vector<gfx::Tile16> tiles16() const { return tiles16_; }
|
||||||
|
|
||||||
auto Sprites(int state) const { return all_sprites_[state]; }
|
auto Sprites(int state) const { return all_sprites_[state]; }
|
||||||
|
auto mutable_sprites(int state) { return &all_sprites_[state]; }
|
||||||
auto AreaGraphics() const {
|
auto AreaGraphics() const {
|
||||||
return overworld_maps_[current_map_].AreaGraphics();
|
return overworld_maps_[current_map_].AreaGraphics();
|
||||||
}
|
}
|
||||||
auto &Entrances() { return all_entrances_; }
|
auto &Entrances() { return all_entrances_; }
|
||||||
|
auto mutable_entrances() { return &all_entrances_; }
|
||||||
|
auto &holes() { return all_holes_; }
|
||||||
|
auto mutable_holes() { return &all_holes_; }
|
||||||
auto AreaPalette() const {
|
auto AreaPalette() const {
|
||||||
return overworld_maps_[current_map_].AreaPalette();
|
return overworld_maps_[current_map_].AreaPalette();
|
||||||
}
|
}
|
||||||
@@ -387,6 +525,9 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
|
|
||||||
auto map_tiles() const { return map_tiles_; }
|
auto map_tiles() const { return map_tiles_; }
|
||||||
auto mutable_map_tiles() { return &map_tiles_; }
|
auto mutable_map_tiles() { return &map_tiles_; }
|
||||||
|
auto all_items() const { return all_items_; }
|
||||||
|
auto mutable_all_items() { return &all_items_; }
|
||||||
|
auto &ref_all_items() { return all_items_; }
|
||||||
|
|
||||||
absl::Status LoadPrototype(ROM &rom_, const std::string &tilemap_filename);
|
absl::Status LoadPrototype(ROM &rom_, const std::string &tilemap_filename);
|
||||||
|
|
||||||
@@ -410,6 +551,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
void FetchLargeMaps();
|
void FetchLargeMaps();
|
||||||
void LoadEntrances();
|
void LoadEntrances();
|
||||||
void LoadExits();
|
void LoadExits();
|
||||||
|
absl::Status LoadItems();
|
||||||
void LoadSprites();
|
void LoadSprites();
|
||||||
void LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex);
|
void LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex);
|
||||||
|
|
||||||
@@ -429,6 +571,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
std::vector<OverworldEntrance> all_entrances_;
|
std::vector<OverworldEntrance> all_entrances_;
|
||||||
std::vector<OverworldEntrance> all_holes_;
|
std::vector<OverworldEntrance> all_holes_;
|
||||||
std::vector<OverworldExit> all_exits_;
|
std::vector<OverworldExit> all_exits_;
|
||||||
|
std::vector<OverworldItem> all_items_;
|
||||||
std::vector<std::vector<Sprite>> all_sprites_;
|
std::vector<std::vector<Sprite>> all_sprites_;
|
||||||
|
|
||||||
std::vector<absl::flat_hash_map<uint16_t, int>> usage_stats_;
|
std::vector<absl::flat_hash_map<uint16_t, int>> usage_stats_;
|
||||||
|
|||||||
@@ -301,6 +301,37 @@ void OverworldMap::LoadMainBlocksets() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For animating water tiles on the overworld map.
|
||||||
|
// We want to swap out static_graphics_[07] with the next sheet
|
||||||
|
// Usually it is 5A, so we make it 5B instead.
|
||||||
|
// There is a middle frame which contains tiles from the bottom half
|
||||||
|
// of the 5A sheet, so this will need some special manipulation to make work
|
||||||
|
// during the BuildBitmap step (or a new one specifically for animating).
|
||||||
|
void OverworldMap::DrawAnimatedTiles() {
|
||||||
|
std::cout << "static_graphics_[6] = "
|
||||||
|
<< core::UppercaseHexByte(static_graphics_[6]) << std::endl;
|
||||||
|
std::cout << "static_graphics_[7] = "
|
||||||
|
<< core::UppercaseHexByte(static_graphics_[7]) << std::endl;
|
||||||
|
std::cout << "static_graphics_[8] = "
|
||||||
|
<< core::UppercaseHexByte(static_graphics_[8]) << std::endl;
|
||||||
|
if (static_graphics_[7] == 0x5B) {
|
||||||
|
static_graphics_[7] = 0x5A;
|
||||||
|
} else {
|
||||||
|
if (static_graphics_[7] == 0x59) {
|
||||||
|
static_graphics_[7] = 0x58;
|
||||||
|
}
|
||||||
|
static_graphics_[7] = 0x5B;
|
||||||
|
}
|
||||||
|
// if (static_graphics_[7] == 0x5A) {
|
||||||
|
// static_graphics_[7] = 0x5B;
|
||||||
|
// } else {
|
||||||
|
// if (static_graphics_[7] == 0x58) {
|
||||||
|
// static_graphics_[7] = 0x59;
|
||||||
|
// }
|
||||||
|
// static_graphics_[7] = 0x5A;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
void OverworldMap::LoadAreaGraphicsBlocksets() {
|
void OverworldMap::LoadAreaGraphicsBlocksets() {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
uchar value = rom_[rom_.version_constants().kOverworldGfxGroups1 +
|
uchar value = rom_[rom_.version_constants().kOverworldGfxGroups1 +
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class OverworldMap {
|
|||||||
absl::Status BuildTiles16Gfx(int count);
|
absl::Status BuildTiles16Gfx(int count);
|
||||||
absl::Status BuildBitmap(OWBlockset& world_blockset);
|
absl::Status BuildBitmap(OWBlockset& world_blockset);
|
||||||
|
|
||||||
|
void DrawAnimatedTiles();
|
||||||
|
|
||||||
auto Tile16Blockset() const { return current_blockset_; }
|
auto Tile16Blockset() const { return current_blockset_; }
|
||||||
auto AreaGraphics() const { return current_gfx_; }
|
auto AreaGraphics() const { return current_gfx_; }
|
||||||
auto AreaPalette() const { return current_palette_; }
|
auto AreaPalette() const { return current_palette_; }
|
||||||
|
|||||||
@@ -43,13 +43,18 @@ class Sprite {
|
|||||||
auto y() const { return y_; }
|
auto y() const { return y_; }
|
||||||
auto nx() const { return nx_; }
|
auto nx() const { return nx_; }
|
||||||
auto ny() const { return ny_; }
|
auto ny() const { return ny_; }
|
||||||
|
auto map_id() const { return map_id_; }
|
||||||
|
auto map_x() const { return map_x_; }
|
||||||
|
auto map_y() const { return map_y_; }
|
||||||
|
|
||||||
auto layer() const { return layer_; }
|
auto layer() const { return layer_; }
|
||||||
auto subtype() const { return subtype_; }
|
auto subtype() const { return subtype_; }
|
||||||
auto& keyDrop() const { return key_drop_; }
|
auto& keyDrop() const { return key_drop_; }
|
||||||
|
|
||||||
auto Width() const { return bounding_box_.w; }
|
auto Width() const { return bounding_box_.w; }
|
||||||
auto Height() const { return bounding_box_.h; }
|
auto Height() const { return bounding_box_.h; }
|
||||||
std::string Name() const { return name_; }
|
std::string& Name() { return name_; }
|
||||||
|
auto deleted() const { return deleted_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Bytes current_gfx_;
|
Bytes current_gfx_;
|
||||||
@@ -80,6 +85,8 @@ class Sprite {
|
|||||||
int height_ = 16;
|
int height_ = 16;
|
||||||
|
|
||||||
int key_drop_;
|
int key_drop_;
|
||||||
|
|
||||||
|
bool deleted_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
|
|||||||
Reference in New Issue
Block a user