backend-infra-engineer: Pre-0.2.2 snapshot (2022)
This commit is contained in:
115
src/app/editor/assembly_editor.cc
Normal file
115
src/app/editor/assembly_editor.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "assembly_editor.h"
|
||||
|
||||
#include "core/constants.h"
|
||||
#include "gui/widgets.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
AssemblyEditor::AssemblyEditor() {
|
||||
text_editor_.SetLanguageDefinition(gui::widgets::GetAssemblyLanguageDef());
|
||||
text_editor_.SetPalette(TextEditor::GetDarkPalette());
|
||||
}
|
||||
|
||||
void AssemblyEditor::Update() {
|
||||
ImGui::Begin("Assembly Editor", &file_is_loaded_);
|
||||
MENU_BAR()
|
||||
DrawFileMenu();
|
||||
DrawEditMenu();
|
||||
END_MENU_BAR()
|
||||
|
||||
auto cpos = text_editor_.GetCursorPosition();
|
||||
SetEditorText();
|
||||
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
|
||||
cpos.mColumn + 1, text_editor_.GetTotalLines(),
|
||||
text_editor_.IsOverwrite() ? "Ovr" : "Ins",
|
||||
text_editor_.CanUndo() ? "*" : " ",
|
||||
text_editor_.GetLanguageDefinition().mName.c_str(),
|
||||
current_file_.c_str());
|
||||
|
||||
text_editor_.Render("##asm_editor");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AssemblyEditor::InlineUpdate() {
|
||||
ChangeActiveFile("assets/asm/template_song.asm");
|
||||
auto cpos = text_editor_.GetCursorPosition();
|
||||
SetEditorText();
|
||||
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
|
||||
cpos.mColumn + 1, text_editor_.GetTotalLines(),
|
||||
text_editor_.IsOverwrite() ? "Ovr" : "Ins",
|
||||
text_editor_.CanUndo() ? "*" : " ",
|
||||
text_editor_.GetLanguageDefinition().mName.c_str(),
|
||||
current_file_.c_str());
|
||||
|
||||
text_editor_.Render("##asm_editor", ImVec2(0, 0));
|
||||
}
|
||||
|
||||
void AssemblyEditor::ChangeActiveFile(const std::string& filename) {
|
||||
current_file_ = filename;
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawFileMenu() {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "Ctrl+O")) {
|
||||
ImGuiFileDialog::Instance()->OpenDialog(
|
||||
"ChooseASMFileDlg", "Open ASM file", ".asm,.txt", ".");
|
||||
}
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
// TODO: Implement this
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGuiFileDialog::Instance()->Display("ChooseASMFileDlg")) {
|
||||
if (ImGuiFileDialog::Instance()->IsOk()) {
|
||||
ChangeActiveFile(ImGuiFileDialog::Instance()->GetFilePathName());
|
||||
}
|
||||
ImGuiFileDialog::Instance()->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawEditMenu() {
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
if (ImGui::MenuItem("Undo", "Ctrl+Z")) {
|
||||
text_editor_.Undo();
|
||||
}
|
||||
if (ImGui::MenuItem("Redo", "Ctrl+Y")) {
|
||||
text_editor_.Redo();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Cut", "Ctrl+X")) {
|
||||
text_editor_.Cut();
|
||||
}
|
||||
if (ImGui::MenuItem("Copy", "Ctrl+C")) {
|
||||
text_editor_.Copy();
|
||||
}
|
||||
if (ImGui::MenuItem("Paste", "Ctrl+V")) {
|
||||
text_editor_.Paste();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Find", "Ctrl+F")) {
|
||||
// TODO: Implement this.
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyEditor::SetEditorText() {
|
||||
if (!file_is_loaded_) {
|
||||
std::ifstream t(current_file_);
|
||||
if (t.good()) {
|
||||
std::string str((std::istreambuf_iterator<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
text_editor_.SetText(str);
|
||||
file_is_loaded_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
38
src/app/editor/assembly_editor.h
Normal file
38
src/app/editor/assembly_editor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
|
||||
|
||||
#include <ImGuiColorTextEdit/TextEditor.h>
|
||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class AssemblyEditor {
|
||||
public:
|
||||
AssemblyEditor();
|
||||
|
||||
void Update();
|
||||
void InlineUpdate();
|
||||
void ChangeActiveFile(const std::string &);
|
||||
|
||||
private:
|
||||
void DrawFileMenu();
|
||||
void DrawEditMenu();
|
||||
void SetEditorText();
|
||||
|
||||
bool file_is_loaded_ = false;
|
||||
|
||||
std::string current_file_;
|
||||
TextEditor text_editor_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
61
src/app/editor/dungeon_editor.cc
Normal file
61
src/app/editor/dungeon_editor.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "dungeon_editor.h"
|
||||
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
void DungeonEditor::Update() {
|
||||
DrawToolset();
|
||||
canvas_.DrawBackground();
|
||||
canvas_.DrawContextMenu();
|
||||
canvas_.DrawGrid();
|
||||
canvas_.DrawOverlay();
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawToolset() {
|
||||
if (ImGui::BeginTable("DWToolset", 9, toolset_table_flags_, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#undoTool");
|
||||
ImGui::TableSetupColumn("#redoTool");
|
||||
ImGui::TableSetupColumn("#history");
|
||||
ImGui::TableSetupColumn("#separator");
|
||||
ImGui::TableSetupColumn("#bg1Tool");
|
||||
ImGui::TableSetupColumn("#bg2Tool");
|
||||
ImGui::TableSetupColumn("#bg3Tool");
|
||||
ImGui::TableSetupColumn("#itemTool");
|
||||
ImGui::TableSetupColumn("#spriteTool");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_UNDO);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_REDO);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_MANAGE_HISTORY);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ICON_MD_MORE_VERT);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_FILTER_1);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_FILTER_2);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_FILTER_3);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_GRASS);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_PEST_CONTROL_RODENT);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
26
src/app/editor/dungeon_editor.h
Normal file
26
src/app/editor/dungeon_editor.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
class DungeonEditor {
|
||||
public:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void DrawToolset();
|
||||
|
||||
gui::Canvas canvas_;
|
||||
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
|
||||
};
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
322
src/app/editor/master_editor.cc
Normal file
322
src/app/editor/master_editor.cc
Normal file
@@ -0,0 +1,322 @@
|
||||
#include "master_editor.h"
|
||||
|
||||
#include <ImGuiColorTextEdit/TextEditor.h>
|
||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||
#include <imgui_memory_editor.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/editor/assembly_editor.h"
|
||||
#include "app/editor/dungeon_editor.h"
|
||||
#include "app/editor/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/rom.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
#include "gui/input.h"
|
||||
#include "gui/widgets.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr ImGuiWindowFlags kMainEditorFlags =
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar;
|
||||
|
||||
void NewMasterFrame() {
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::NewFrame();
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImVec2 dimensions(io.DisplaySize.x, io.DisplaySize.y);
|
||||
ImGui::SetNextWindowSize(dimensions, ImGuiCond_Always);
|
||||
|
||||
if (!ImGui::Begin("##YazeMain", nullptr, kMainEditorFlags)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool BeginCentered(const char *name) {
|
||||
ImGuiIO const &io = ImGui::GetIO();
|
||||
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
|
||||
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
|
||||
return ImGui::Begin(name, nullptr, flags);
|
||||
}
|
||||
|
||||
void DisplayStatus(absl::Status &status) {
|
||||
if (BeginCentered("StatusWindow")) {
|
||||
ImGui::Text("%s", status.ToString().c_str());
|
||||
ImGui::Spacing();
|
||||
ImGui::NextColumn();
|
||||
ImGui::Columns(1);
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
ImGui::SameLine(270);
|
||||
if (ImGui::Button("OK", ImVec2(200, 0))) {
|
||||
status = absl::OkStatus();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer) {
|
||||
sdl_renderer_ = renderer;
|
||||
rom_.SetupRenderer(renderer);
|
||||
}
|
||||
|
||||
void MasterEditor::UpdateScreen() {
|
||||
NewMasterFrame();
|
||||
|
||||
DrawYazeMenu();
|
||||
DrawFileDialog();
|
||||
DrawStatusPopup();
|
||||
DrawAboutPopup();
|
||||
DrawInfoPopup();
|
||||
|
||||
TAB_BAR("##TabBar")
|
||||
DrawOverworldEditor();
|
||||
DrawDungeonEditor();
|
||||
DrawMusicEditor();
|
||||
DrawSpriteEditor();
|
||||
DrawScreenEditor();
|
||||
DrawPaletteEditor();
|
||||
END_TAB_BAR()
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MasterEditor::DrawFileDialog() {
|
||||
if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) {
|
||||
if (ImGuiFileDialog::Instance()->IsOk()) {
|
||||
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||
status_ = rom_.LoadFromFile(filePathName);
|
||||
overworld_editor_.SetupROM(rom_);
|
||||
screen_editor_.SetupROM(rom_);
|
||||
palette_editor_.SetupROM(rom_);
|
||||
}
|
||||
ImGuiFileDialog::Instance()->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawStatusPopup() {
|
||||
if (!status_.ok()) {
|
||||
DisplayStatus(status_);
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawAboutPopup() {
|
||||
if (about_) ImGui::OpenPopup("About");
|
||||
if (ImGui::BeginPopupModal("About", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Yet Another Zelda3 Editor - v0.02");
|
||||
ImGui::Text("Written by: scawful");
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Special Thanks: Zarby89, JaredBrian");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Close", ImVec2(200, 0))) {
|
||||
about_ = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawInfoPopup() {
|
||||
if (rom_info_) ImGui::OpenPopup("ROM Information");
|
||||
if (ImGui::BeginPopupModal("ROM Information", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Title: %s", rom_.GetTitle());
|
||||
ImGui::Text("ROM Size: %ld", rom_.size());
|
||||
|
||||
if (ImGui::Button("Close", ImVec2(200, 0))) {
|
||||
rom_info_ = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawYazeMenu() {
|
||||
MENU_BAR()
|
||||
DrawFileMenu();
|
||||
DrawEditMenu();
|
||||
DrawViewMenu();
|
||||
DrawHelpMenu();
|
||||
END_MENU_BAR()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawFileMenu() const {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "Ctrl+O")) {
|
||||
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
|
||||
".sfc,.smc", ".");
|
||||
}
|
||||
|
||||
MENU_ITEM2("Save", "Ctrl+S") {}
|
||||
MENU_ITEM("Save As..") {}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// TODO: Make these options matter
|
||||
if (ImGui::BeginMenu("Options")) {
|
||||
static bool enabled = true;
|
||||
ImGui::MenuItem("Enabled", "", &enabled);
|
||||
ImGui::BeginChild("child", ImVec2(0, 60), true);
|
||||
for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i);
|
||||
ImGui::EndChild();
|
||||
static float f = 0.5f;
|
||||
static int n = 0;
|
||||
ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
|
||||
ImGui::InputFloat("Input", &f, 0.1f);
|
||||
ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawEditMenu() {
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = overworld_editor_.Undo(); }
|
||||
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = overworld_editor_.Redo(); }
|
||||
ImGui::Separator();
|
||||
MENU_ITEM2("Cut", "Ctrl+X") { status_ = overworld_editor_.Cut(); }
|
||||
MENU_ITEM2("Copy", "Ctrl+C") { status_ = overworld_editor_.Copy(); }
|
||||
MENU_ITEM2("Paste", "Ctrl+V") { status_ = overworld_editor_.Paste(); }
|
||||
ImGui::Separator();
|
||||
MENU_ITEM2("Find", "Ctrl+F") {}
|
||||
ImGui::Separator();
|
||||
MENU_ITEM("ROM Information") rom_info_ = true;
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawViewMenu() {
|
||||
static bool show_imgui_metrics = false;
|
||||
static bool show_imgui_style_editor = false;
|
||||
static bool show_memory_editor = false;
|
||||
static bool show_asm_editor = false;
|
||||
static bool show_imgui_demo = false;
|
||||
static bool show_memory_viewer = false;
|
||||
|
||||
if (show_imgui_metrics) {
|
||||
ImGui::ShowMetricsWindow(&show_imgui_metrics);
|
||||
}
|
||||
|
||||
if (show_memory_editor) {
|
||||
static MemoryEditor mem_edit;
|
||||
mem_edit.DrawWindow("Memory Editor", (void *)&rom_, rom_.size());
|
||||
}
|
||||
|
||||
if (show_imgui_demo) {
|
||||
ImGui::ShowDemoWindow();
|
||||
}
|
||||
|
||||
if (show_asm_editor) {
|
||||
assembly_editor_.Update();
|
||||
}
|
||||
|
||||
if (show_imgui_style_editor) {
|
||||
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
|
||||
ImGui::ShowStyleEditor();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_memory_viewer) {
|
||||
ImGui::Begin("Memory Viewer (ImGui)", &show_memory_viewer);
|
||||
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
static ImGuiTableFlags flags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
|
||||
if (auto outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
|
||||
ImGui::BeginTable("table1", 3, flags, outer_size)) {
|
||||
for (int row = 0; row < 10; row++) {
|
||||
ImGui::TableNextRow();
|
||||
for (int column = 0; column < 3; column++) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Cell %d,%d", column, row);
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("HEX Editor", nullptr, &show_memory_editor);
|
||||
ImGui::MenuItem("ASM Editor", nullptr, &show_asm_editor);
|
||||
ImGui::MenuItem("Memory Viewer", nullptr, &show_memory_viewer);
|
||||
ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("GUI Tools")) {
|
||||
ImGui::MenuItem("Metrics (ImGui)", nullptr, &show_imgui_metrics);
|
||||
ImGui::MenuItem("Style Editor (ImGui)", nullptr,
|
||||
&show_imgui_style_editor);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawHelpMenu() {
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About")) about_ = true;
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawOverworldEditor() {
|
||||
TAB_ITEM("Overworld")
|
||||
status_ = overworld_editor_.Update();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawDungeonEditor() {
|
||||
TAB_ITEM("Dungeon")
|
||||
dungeon_editor_.Update();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawPaletteEditor() {
|
||||
TAB_ITEM("Palettes")
|
||||
status_ = palette_editor_.Update();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawScreenEditor() {
|
||||
TAB_ITEM("Screens")
|
||||
screen_editor_.Update();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawMusicEditor() {
|
||||
TAB_ITEM("Music")
|
||||
music_editor_.Update();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void MasterEditor::DrawSpriteEditor() {
|
||||
TAB_ITEM("Sprites")
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
72
src/app/editor/master_editor.h
Normal file
72
src/app/editor/master_editor.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
|
||||
#include <ImGuiColorTextEdit/TextEditor.h>
|
||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||
#include <imgui_memory_editor.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/editor/assembly_editor.h"
|
||||
#include "app/editor/dungeon_editor.h"
|
||||
#include "app/editor/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/palette_editor.h"
|
||||
#include "app/editor/screen_editor.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/rom.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
#include "gui/input.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class MasterEditor {
|
||||
public:
|
||||
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void UpdateScreen();
|
||||
|
||||
private:
|
||||
void DrawFileDialog();
|
||||
void DrawStatusPopup();
|
||||
void DrawAboutPopup();
|
||||
void DrawInfoPopup();
|
||||
|
||||
void DrawYazeMenu();
|
||||
void DrawFileMenu() const;
|
||||
void DrawEditMenu();
|
||||
void DrawViewMenu();
|
||||
void DrawHelpMenu();
|
||||
|
||||
void DrawOverworldEditor();
|
||||
void DrawDungeonEditor();
|
||||
void DrawPaletteEditor();
|
||||
void DrawMusicEditor();
|
||||
void DrawScreenEditor();
|
||||
void DrawSpriteEditor();
|
||||
|
||||
bool about_ = false;
|
||||
bool rom_info_ = false;
|
||||
|
||||
std::shared_ptr<SDL_Renderer> sdl_renderer_;
|
||||
absl::Status status_;
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
DungeonEditor dungeon_editor_;
|
||||
OverworldEditor overworld_editor_;
|
||||
PaletteEditor palette_editor_;
|
||||
ScreenEditor screen_editor_;
|
||||
MusicEditor music_editor_;
|
||||
ROM rom_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
292
src/app/editor/music_editor.cc
Normal file
292
src/app/editor/music_editor.cc
Normal file
@@ -0,0 +1,292 @@
|
||||
#include "music_editor.h"
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/assembly_editor.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
#include "gui/input.h"
|
||||
#include "snes_spc/demo/demo_util.h"
|
||||
#include "snes_spc/demo/wave_writer.h"
|
||||
#include "snes_spc/snes_spc/spc.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
namespace {
|
||||
|
||||
#define BUF_SIZE 2048
|
||||
|
||||
void PlaySPC() {
|
||||
/* Create emulator and filter */
|
||||
SNES_SPC* snes_spc = spc_new();
|
||||
SPC_Filter* filter = spc_filter_new();
|
||||
if (!snes_spc || !filter) error("Out of memory");
|
||||
|
||||
/* Load SPC */
|
||||
{
|
||||
/* Load file into memory */
|
||||
long spc_size;
|
||||
void* spc = load_file("assets/music/hyrule_field.spc", &spc_size);
|
||||
|
||||
/* Load SPC data into emulator */
|
||||
error(spc_load_spc(snes_spc, spc, spc_size));
|
||||
free(spc); /* emulator makes copy of data */
|
||||
|
||||
/* Most SPC files have garbage data in the echo buffer, so clear that */
|
||||
spc_clear_echo(snes_spc);
|
||||
|
||||
/* Clear filter before playing */
|
||||
spc_filter_clear(filter);
|
||||
}
|
||||
|
||||
/* Record 20 seconds to wave file */
|
||||
wave_open(spc_sample_rate, "out.wav");
|
||||
wave_enable_stereo();
|
||||
while (wave_sample_count() < 30 * spc_sample_rate * 2) {
|
||||
/* Play into buffer */
|
||||
short buf[BUF_SIZE];
|
||||
error(spc_play(snes_spc, BUF_SIZE, buf));
|
||||
|
||||
/* Filter samples */
|
||||
spc_filter_run(filter, buf, BUF_SIZE);
|
||||
|
||||
wave_write(buf, BUF_SIZE);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
spc_filter_delete(filter);
|
||||
spc_delete(snes_spc);
|
||||
wave_close();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MusicEditor::Update() {
|
||||
if (ImGui::BeginTable("MusicEditorColumns", 2, music_editor_flags_,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Assembly");
|
||||
ImGui::TableSetupColumn("Composition");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
assembly_editor_.InlineUpdate();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawToolset();
|
||||
DrawChannels();
|
||||
DrawPianoRoll();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicEditor::DrawChannels() {
|
||||
if (ImGui::BeginTabBar("MyTabBar", ImGuiTabBarFlags_None)) {
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
if (ImGui::BeginTabItem(absl::StrFormat("%d", i).data())) {
|
||||
DrawPianoStaff();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
static const int NUM_KEYS = 25;
|
||||
static bool keys[NUM_KEYS];
|
||||
|
||||
void MusicEditor::DrawPianoStaff() {
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
|
||||
ImGui::BeginChild(child_id, ImVec2(0, 170), false)) {
|
||||
const int NUM_LINES = 5;
|
||||
const int LINE_THICKNESS = 2;
|
||||
const int LINE_SPACING = 40;
|
||||
|
||||
// Get the draw list for the current window
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw the staff lines
|
||||
ImVec2 canvas_p0 =
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
|
||||
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + ImGui::GetContentRegionAvail().y);
|
||||
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(32, 32, 32, 255));
|
||||
for (int i = 0; i < NUM_LINES; i++) {
|
||||
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING);
|
||||
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + i * LINE_SPACING);
|
||||
draw_list->AddLine(line_start, line_end, IM_COL32(200, 200, 200, 255),
|
||||
LINE_THICKNESS);
|
||||
}
|
||||
|
||||
// Draw the ledger lines
|
||||
const int NUM_LEDGER_LINES = 3;
|
||||
for (int i = -NUM_LEDGER_LINES; i <= NUM_LINES + NUM_LEDGER_LINES; i++) {
|
||||
if (i % 2 == 0) continue; // skip every other line
|
||||
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING / 2);
|
||||
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + i * LINE_SPACING / 2);
|
||||
draw_list->AddLine(line_start, line_end, IM_COL32(150, 150, 150, 255),
|
||||
LINE_THICKNESS);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void MusicEditor::DrawPianoRoll() {
|
||||
// Render the piano roll
|
||||
float key_width = ImGui::GetContentRegionAvail().x / NUM_KEYS;
|
||||
float white_key_height = ImGui::GetContentRegionAvail().y * 0.8f;
|
||||
float black_key_height = ImGui::GetContentRegionAvail().y * 0.5f;
|
||||
ImGui::Text("Piano Roll");
|
||||
ImGui::Separator();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw the staff lines
|
||||
ImVec2 canvas_p0 =
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
|
||||
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + ImGui::GetContentRegionAvail().y);
|
||||
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(200, 200, 200, 255));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.f, 0.f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.f);
|
||||
for (int i = 0; i < NUM_KEYS; i++) {
|
||||
// Calculate the position and size of the key
|
||||
ImVec2 key_pos = ImVec2(i * key_width, 0.0f);
|
||||
ImVec2 key_size;
|
||||
ImVec4 key_color;
|
||||
ImVec4 text_color;
|
||||
if (i % 12 == 1 || i % 12 == 3 || i % 12 == 6 || i % 12 == 8 ||
|
||||
i % 12 == 10) {
|
||||
// This is a black key
|
||||
key_size = ImVec2(key_width * 0.6f, black_key_height);
|
||||
key_color = ImVec4(0, 0, 0, 255);
|
||||
text_color = ImVec4(255, 255, 255, 255);
|
||||
} else {
|
||||
// This is a white key
|
||||
key_size = ImVec2(key_width, white_key_height);
|
||||
key_color = ImVec4(255, 255, 255, 255);
|
||||
text_color = ImVec4(0, 0, 0, 255);
|
||||
}
|
||||
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.f, 0.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, key_color);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
|
||||
if (ImGui::Button(kSongNotes[i].data(), key_size)) {
|
||||
keys[i] ^= 1;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImVec2 button_pos = ImGui::GetItemRectMin();
|
||||
ImVec2 button_size = ImGui::GetItemRectSize();
|
||||
if (keys[i]) {
|
||||
ImVec2 dest;
|
||||
dest.x = button_pos.x + button_size.x;
|
||||
dest.y = button_pos.y + button_size.y;
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(button_pos, dest,
|
||||
IM_COL32(200, 200, 255, 200));
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void MusicEditor::DrawSongToolset() {
|
||||
if (ImGui::BeginTable("DWToolset", 8, toolset_table_flags_, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#1");
|
||||
ImGui::TableSetupColumn("#play");
|
||||
ImGui::TableSetupColumn("#rewind");
|
||||
ImGui::TableSetupColumn("#fastforward");
|
||||
ImGui::TableSetupColumn("volumeController");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicEditor::DrawToolset() {
|
||||
static bool is_playing = false;
|
||||
static int selected_option = 0;
|
||||
static int current_volume = 0;
|
||||
static bool has_loaded_song = false;
|
||||
const int MAX_VOLUME = 100;
|
||||
|
||||
if (is_playing) {
|
||||
if (!has_loaded_song) {
|
||||
PlaySPC();
|
||||
current_song_ = Mix_LoadMUS("out.wav");
|
||||
Mix_PlayMusic(current_song_, -1);
|
||||
has_loaded_song = true;
|
||||
}
|
||||
|
||||
// // If there is no music playing
|
||||
// if (Mix_PlayingMusic() == 0) {
|
||||
// Mix_PlayMusic(current_song_, -1);
|
||||
// }
|
||||
// // If music is being played
|
||||
// else {
|
||||
// // If the music is paused
|
||||
// if (Mix_PausedMusic() == 1) {
|
||||
// // Resume the music
|
||||
// Mix_ResumeMusic();
|
||||
// }
|
||||
// // If the music is playing
|
||||
// else {
|
||||
// // Pause the music
|
||||
// Mix_PauseMusic();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
gui::ItemLabel("Select a song to edit: ", gui::ItemLabelFlags::Left);
|
||||
ImGui::Combo("#songs_in_game", &selected_option, kGameSongs, 30);
|
||||
|
||||
gui::ItemLabel("Controls: ", gui::ItemLabelFlags::Left);
|
||||
if (ImGui::BeginTable("SongToolset", 5, toolset_table_flags_, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#play");
|
||||
ImGui::TableSetupColumn("#rewind");
|
||||
ImGui::TableSetupColumn("#fastforward");
|
||||
ImGui::TableSetupColumn("#volume");
|
||||
ImGui::TableSetupColumn("#slider");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(is_playing ? ICON_MD_STOP : ICON_MD_PLAY_ARROW)) {
|
||||
if (is_playing) {
|
||||
Mix_HaltMusic();
|
||||
has_loaded_song = false;
|
||||
}
|
||||
is_playing = !is_playing;
|
||||
}
|
||||
|
||||
BUTTON_COLUMN(ICON_MD_FAST_REWIND)
|
||||
BUTTON_COLUMN(ICON_MD_FAST_FORWARD)
|
||||
BUTTON_COLUMN(ICON_MD_VOLUME_UP)
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderInt("Volume", ¤t_volume, 0, 100);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
const int SONG_DURATION = 120; // duration of the song in seconds
|
||||
static int current_time = 0; // current time in the song in seconds
|
||||
|
||||
// Display the current time in the song
|
||||
gui::ItemLabel("Current Time: ", gui::ItemLabelFlags::Left);
|
||||
ImGui::Text("%d:%02d", current_time / 60, current_time % 60);
|
||||
ImGui::SameLine();
|
||||
// Display the song duration/progress using a progress bar
|
||||
ImGui::ProgressBar((float)current_time / SONG_DURATION);
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
86
src/app/editor/music_editor.h
Normal file
86
src/app/editor/music_editor.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/assembly_editor.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
#include "gui/input.h"
|
||||
#include "snes_spc/demo/demo_util.h"
|
||||
#include "snes_spc/demo/wave_writer.h"
|
||||
#include "snes_spc/snes_spc/spc.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
static const char* kGameSongs[] = {"Title",
|
||||
"Light World",
|
||||
"Beginning",
|
||||
"Rabbit",
|
||||
"Forest",
|
||||
"Intro",
|
||||
"Town",
|
||||
"Warp",
|
||||
"Dark world",
|
||||
"Master sword",
|
||||
"File select",
|
||||
"Soldier",
|
||||
"Mountain",
|
||||
"Shop",
|
||||
"Fanfare",
|
||||
"Castle",
|
||||
"Palace (Pendant)",
|
||||
"Cave (Same as Secret Way)",
|
||||
"Clear (Dungeon end)",
|
||||
"Church",
|
||||
"Boss",
|
||||
"Dungeon (Crystal)",
|
||||
"Psychic",
|
||||
"Secret Way (Same as Cave)",
|
||||
"Rescue",
|
||||
"Crystal",
|
||||
"Fountain",
|
||||
"Pyramid",
|
||||
"Kill Agahnim",
|
||||
"Ganon Room",
|
||||
"Last Boss"};
|
||||
|
||||
static constexpr absl::string_view kSongNotes[] = {
|
||||
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
|
||||
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
|
||||
class MusicEditor {
|
||||
public:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void DrawChannels();
|
||||
void DrawPianoStaff();
|
||||
void DrawPianoRoll();
|
||||
void DrawSongToolset();
|
||||
void DrawToolset();
|
||||
|
||||
Mix_Music* current_song_ = NULL;
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
|
||||
ImGuiTableFlags music_editor_flags_ = ImGuiTableFlags_SizingFixedFit |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable;
|
||||
|
||||
ImGuiTableFlags channel_table_flags_ =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
352
src/app/editor/overworld_editor.cc
Normal file
352
src/app/editor/overworld_editor.cc
Normal file
@@ -0,0 +1,352 @@
|
||||
#include "overworld_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
absl::Status OverworldEditor::Update() {
|
||||
// Initialize overworld graphics, maps, and palettes
|
||||
if (rom_.isLoaded() && !all_gfx_loaded_) {
|
||||
RETURN_IF_ERROR(LoadGraphics())
|
||||
all_gfx_loaded_ = true;
|
||||
}
|
||||
|
||||
// Draws the toolset for editing the Overworld.
|
||||
RETURN_IF_ERROR(DrawToolset())
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("#owEditTable", 2, ow_edit_flags, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TableSetupColumn("Tile Selector");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
DrawOverworldCanvas();
|
||||
ImGui::TableNextColumn();
|
||||
DrawTileSelector();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
absl::Status OverworldEditor::DrawToolset() {
|
||||
if (ImGui::BeginTable("OWToolset", 17, toolset_table_flags, ImVec2(0, 0))) {
|
||||
for (const auto &name : kToolsetColumnNames)
|
||||
ImGui::TableSetupColumn(name.data());
|
||||
|
||||
BUTTON_COLUMN(ICON_MD_UNDO) // Undo
|
||||
BUTTON_COLUMN(ICON_MD_REDO) // Redo
|
||||
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
|
||||
BUTTON_COLUMN(ICON_MD_ZOOM_OUT) // Zoom Out
|
||||
BUTTON_COLUMN(ICON_MD_ZOOM_IN) // Zoom In
|
||||
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
|
||||
BUTTON_COLUMN(ICON_MD_DRAW) // Draw Tile
|
||||
BUTTON_COLUMN(ICON_MD_DOOR_FRONT) // Entrances
|
||||
BUTTON_COLUMN(ICON_MD_DOOR_BACK) // Exits
|
||||
BUTTON_COLUMN(ICON_MD_GRASS) // Items
|
||||
BUTTON_COLUMN(ICON_MD_PEST_CONTROL_RODENT) // Sprites
|
||||
BUTTON_COLUMN(ICON_MD_ADD_LOCATION) // Transports
|
||||
BUTTON_COLUMN(ICON_MD_MUSIC_NOTE) // Music
|
||||
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
|
||||
ImGui::TableNextColumn(); // Palette
|
||||
palette_editor_.DisplayPalette(palette_, overworld_.isLoaded());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void OverworldEditor::DrawOverworldMapSettings() {
|
||||
if (ImGui::BeginTable("#mapSettings", 8, ow_map_flags, ImVec2(0, 0), -1)) {
|
||||
for (const auto &name : kOverworldSettingsColumnNames)
|
||||
ImGui::TableSetupColumn(name.data());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(50.f);
|
||||
ImGui::InputInt("Current Map", ¤t_map_);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
ImGui::Combo("##world", ¤t_world_,
|
||||
"Light World\0Dark World\0Extra World\0");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("GFX");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(kInputFieldSize);
|
||||
ImGui::InputText("##mapGFX", map_gfx_, kByteSize);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Palette");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(kInputFieldSize);
|
||||
ImGui::InputText("##mapPal", map_palette_, kByteSize);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Spr GFX");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(kInputFieldSize);
|
||||
ImGui::InputText("##sprGFX", spr_gfx_, kByteSize);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Spr Palette");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(kInputFieldSize);
|
||||
ImGui::InputText("##sprPal", spr_palette_, kByteSize);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Msg ID");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(50.f);
|
||||
ImGui::InputText("##msgid", spr_palette_, kMessageIdSize);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void OverworldEditor::DrawOverworldEntrances() {
|
||||
for (const auto &each : overworld_.Entrances()) {
|
||||
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||
each.map_id_ >= (current_world_ * 0x40)) {
|
||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
|
||||
ImVec4(210, 24, 210, 150));
|
||||
std::string str = absl::StrFormat("%#x", each.entrance_id_);
|
||||
ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void OverworldEditor::DrawOverworldMaps() {
|
||||
int xx = 0;
|
||||
int yy = 0;
|
||||
for (int i = 0; i < 0x40; i++) {
|
||||
int world_index = i + (current_world_ * 0x40);
|
||||
int map_x = (xx * 0x200);
|
||||
int map_y = (yy * 0x200);
|
||||
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y);
|
||||
xx++;
|
||||
if (xx >= 8) {
|
||||
yy++;
|
||||
xx = 0;
|
||||
}
|
||||
DrawOverworldEntrances();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Overworld Editor canvas
|
||||
// Allows the user to make changes to the overworld map.
|
||||
void OverworldEditor::DrawOverworldCanvas() {
|
||||
DrawOverworldMapSettings();
|
||||
ImGui::Separator();
|
||||
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)7);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
|
||||
ow_map_canvas_.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8));
|
||||
ow_map_canvas_.DrawContextMenu();
|
||||
if (overworld_.isLoaded()) {
|
||||
DrawOverworldMaps();
|
||||
// User has selected a tile they want to draw from the blockset.
|
||||
if (!blockset_canvas_.Points().empty()) {
|
||||
int x = blockset_canvas_.Points().front().x / 32;
|
||||
int y = blockset_canvas_.Points().front().y / 32;
|
||||
std::cout << x << " " << y << std::endl;
|
||||
current_tile16_ = x + (y * 8);
|
||||
std::cout << current_tile16_ << std::endl;
|
||||
ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], 16);
|
||||
}
|
||||
}
|
||||
ow_map_canvas_.DrawGrid(64.0f);
|
||||
ow_map_canvas_.DrawOverlay();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Tile 16 Selector
|
||||
// Displays all the tiles in the game.
|
||||
void OverworldEditor::DrawTile16Selector() {
|
||||
blockset_canvas_.DrawBackground(ImVec2(0x100 + 1, (8192 * 2) + 1));
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 2, map_blockset_loaded_);
|
||||
blockset_canvas_.DrawTileSelector(32);
|
||||
blockset_canvas_.DrawGrid(32.0f);
|
||||
blockset_canvas_.DrawOverlay();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Tile 8 Selector
|
||||
// Displays all the individual tiles that make up a tile16.
|
||||
void OverworldEditor::DrawTile8Selector() {
|
||||
graphics_bin_canvas_.DrawBackground(
|
||||
ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1));
|
||||
graphics_bin_canvas_.DrawContextMenu();
|
||||
if (all_gfx_loaded_) {
|
||||
for (const auto &[key, value] : graphics_bin_) {
|
||||
int offset = 0x40 * (key + 1);
|
||||
int top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 2;
|
||||
if (key >= 1) {
|
||||
top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 0x40 * key;
|
||||
}
|
||||
graphics_bin_canvas_.GetDrawList()->AddImage(
|
||||
(void *)value.GetTexture(),
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, top_left_y),
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 0x100,
|
||||
graphics_bin_canvas_.GetZeroPoint().y + offset));
|
||||
}
|
||||
}
|
||||
graphics_bin_canvas_.DrawGrid(16.0f);
|
||||
graphics_bin_canvas_.DrawOverlay();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Displays the graphics tilesheets that are available on the current selected
|
||||
// overworld map.
|
||||
void OverworldEditor::DrawAreaGraphics() {
|
||||
current_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
|
||||
current_gfx_canvas_.DrawContextMenu();
|
||||
current_gfx_canvas_.DrawTileSelector(32);
|
||||
current_gfx_canvas_.DrawBitmap(current_gfx_bmp_, 2, overworld_.isLoaded());
|
||||
current_gfx_canvas_.DrawGrid(32.0f);
|
||||
current_gfx_canvas_.DrawOverlay();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void OverworldEditor::DrawTileSelector() {
|
||||
if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
|
||||
if (ImGui::BeginTabItem("Tile16")) {
|
||||
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)2);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
DrawTile16Selector();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Tile8")) {
|
||||
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)1);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
DrawTile8Selector();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Area Graphics")) {
|
||||
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)3);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
DrawAreaGraphics();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
absl::Status OverworldEditor::LoadGraphics() {
|
||||
// Load all of the graphics data from the game.
|
||||
PRINT_IF_ERROR(rom_.LoadAllGraphicsData())
|
||||
graphics_bin_ = rom_.GetGraphicsBin();
|
||||
|
||||
// Load the Link to the Past overworld.
|
||||
RETURN_IF_ERROR(overworld_.Load(rom_))
|
||||
palette_ = overworld_.AreaPalette();
|
||||
current_gfx_bmp_.Create(0x80, 0x200, 0x40, overworld_.AreaGraphics());
|
||||
current_gfx_bmp_.ApplyPalette(palette_);
|
||||
rom_.RenderBitmap(¤t_gfx_bmp_);
|
||||
|
||||
// Create the tile16 blockset image
|
||||
tile16_blockset_bmp_.Create(0x80, 8192, 0x80, overworld_.Tile16Blockset());
|
||||
tile16_blockset_bmp_.ApplyPalette(palette_);
|
||||
rom_.RenderBitmap(&tile16_blockset_bmp_);
|
||||
map_blockset_loaded_ = true;
|
||||
|
||||
// Copy the tile16 data into individual tiles.
|
||||
auto tile16_data = overworld_.Tile16Blockset();
|
||||
|
||||
// Loop through the tiles and copy their pixel data into separate vectors
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
// Create a new vector for the pixel data of the current tile
|
||||
Bytes tile_data;
|
||||
for (int j = 0; j < 32 * 32; j++) tile_data.push_back(0x00);
|
||||
|
||||
// Copy the pixel data for the current tile into the vector
|
||||
for (int ty = 0; ty < 32; ty++) {
|
||||
for (int tx = 0; tx < 32; tx++) {
|
||||
int position = (tx + (ty * 0x20));
|
||||
uchar value = tile16_data[i + tx + (ty * 0x80)];
|
||||
tile_data[position] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the vector for the current tile to the vector of tile pixel data
|
||||
tile16_individual_data_.push_back(tile_data);
|
||||
}
|
||||
|
||||
// Render the bitmaps of each tile.
|
||||
for (int id = 0; id < 4096; id++) {
|
||||
gfx::Bitmap new_tile16;
|
||||
tile16_individual_.emplace_back(new_tile16);
|
||||
tile16_individual_[id].Create(0x10, 0x10, 0x80,
|
||||
tile16_individual_data_[id]);
|
||||
tile16_individual_[id].ApplyPalette(palette_);
|
||||
rom_.RenderBitmap(&tile16_individual_[id]);
|
||||
}
|
||||
|
||||
// Render the overworld maps loaded from the ROM.
|
||||
for (int i = 0; i < core::kNumOverworldMaps; ++i) {
|
||||
overworld_.SetCurrentMap(i);
|
||||
auto palette = overworld_.AreaPalette();
|
||||
maps_bmp_[i].Create(0x200, 0x200, 0x200, overworld_.BitmapData());
|
||||
maps_bmp_[i].ApplyPalette(palette);
|
||||
rom_.RenderBitmap(&(maps_bmp_[i]));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
118
src/app/editor/overworld_editor.h
Normal file
118
src/app/editor/overworld_editor.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
static constexpr uint k4BPP = 4;
|
||||
static constexpr uint kByteSize = 3;
|
||||
static constexpr uint kMessageIdSize = 5;
|
||||
static constexpr uint kNumSheetsToLoad = 223;
|
||||
static constexpr uint kTile8DisplayHeight = 64;
|
||||
static constexpr float kInputFieldSize = 30.f;
|
||||
|
||||
static constexpr absl::string_view kToolsetColumnNames[] = {
|
||||
"#undoTool", "#redoTool", "#drawTool", "#separator2",
|
||||
"#zoomOutTool", "#zoomInTool", "#separator", "#history",
|
||||
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
|
||||
"#transportTool", "#musicTool"};
|
||||
|
||||
static constexpr absl::string_view kOverworldSettingsColumnNames[] = {
|
||||
"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
|
||||
"##sprpalCol", "##msgidCol", "##2ndCol"};
|
||||
|
||||
class OverworldEditor {
|
||||
public:
|
||||
absl::Status Update();
|
||||
absl::Status Undo() const { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() const { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() const { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() const { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() const { return absl::UnimplementedError("Paste"); }
|
||||
void SetupROM(ROM &rom) { rom_ = rom; }
|
||||
|
||||
private:
|
||||
absl::Status DrawToolset();
|
||||
void DrawOverworldMapSettings();
|
||||
|
||||
void DrawOverworldEntrances();
|
||||
void DrawOverworldMaps();
|
||||
void DrawOverworldCanvas();
|
||||
|
||||
void DrawTile16Selector();
|
||||
void DrawTile8Selector();
|
||||
void DrawAreaGraphics();
|
||||
void DrawTileSelector();
|
||||
absl::Status LoadGraphics();
|
||||
|
||||
int current_world_ = 0;
|
||||
int current_map_ = 0;
|
||||
int current_tile16_ = 0;
|
||||
int selected_tile_ = 0;
|
||||
char map_gfx_[3] = "";
|
||||
char map_palette_[3] = "";
|
||||
char spr_gfx_[3] = "";
|
||||
char spr_palette_[3] = "";
|
||||
char message_id_[5] = "";
|
||||
char staticgfx[16];
|
||||
|
||||
bool opt_enable_grid = true;
|
||||
bool all_gfx_loaded_ = false;
|
||||
bool map_blockset_loaded_ = false;
|
||||
bool selected_tile_loaded_ = false;
|
||||
bool update_selected_tile_ = true;
|
||||
|
||||
ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit;
|
||||
ImGuiTableFlags ow_map_flags = ImGuiTableFlags_Borders;
|
||||
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
Bytes selected_tile_data_;
|
||||
std::unordered_map<int, gfx::Bitmap> graphics_bin_;
|
||||
std::unordered_map<int, gfx::Bitmap> current_graphics_set_;
|
||||
std::unordered_map<int, gfx::Bitmap> maps_bmp_;
|
||||
std::unordered_map<int, gfx::Bitmap> sprite_previews_;
|
||||
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
|
||||
ROM rom_;
|
||||
PaletteEditor palette_editor_;
|
||||
zelda3::Overworld overworld_;
|
||||
|
||||
gfx::SNESPalette palette_;
|
||||
gfx::Bitmap selected_tile_bmp_;
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
gfx::Bitmap current_gfx_bmp_;
|
||||
gfx::Bitmap all_gfx_bmp;
|
||||
|
||||
gui::Canvas ow_map_canvas_;
|
||||
gui::Canvas current_gfx_canvas_;
|
||||
gui::Canvas blockset_canvas_;
|
||||
gui::Canvas graphics_bin_canvas_;
|
||||
};
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
128
src/app/editor/palette_editor.cc
Normal file
128
src/app/editor/palette_editor.cc
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
if (ImGui::TreeNode(kPaletteCategoryNames[i].data())) {
|
||||
auto size = rom_.GetPaletteGroup(kPaletteGroupNames[i].data()).size;
|
||||
auto palettes = rom_.GetPaletteGroup(kPaletteGroupNames[i].data());
|
||||
for (int j = 0; j < size; j++) {
|
||||
ImGui::Text("%d", j);
|
||||
auto palette = palettes[j];
|
||||
for (int n = 0; n < size; n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0)
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGuiColorEditFlags palette_button_flags =
|
||||
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker;
|
||||
if (ImGui::ColorButton("##palette", palette[n].RGB(),
|
||||
palette_button_flags, ImVec2(20, 20)))
|
||||
current_color_ =
|
||||
ImVec4(palette[n].rgb.x, palette[n].rgb.y, palette[n].rgb.z,
|
||||
current_color_.w); // Preserve alpha!
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) {
|
||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||
ImGuiColorEditFlags_NoDragDrop |
|
||||
ImGuiColorEditFlags_NoOptions;
|
||||
|
||||
// Generate a default palette. The palette will persist and can be edited.
|
||||
static bool init = false;
|
||||
static ImVec4 saved_palette[256] = {};
|
||||
if (loaded && !init) {
|
||||
for (int n = 0; n < palette.size_; n++) {
|
||||
saved_palette[n].x = palette.GetColor(n).rgb.x / 255;
|
||||
saved_palette[n].y = palette.GetColor(n).rgb.y / 255;
|
||||
saved_palette[n].z = palette.GetColor(n).rgb.z / 255;
|
||||
saved_palette[n].w = 255; // Alpha
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags);
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= ImGui::Button("Palette");
|
||||
if (open_popup) {
|
||||
ImGui::OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
if (ImGui::BeginPopup("mypicker")) {
|
||||
ImGui::Text("Current Overworld Palette");
|
||||
ImGui::Separator();
|
||||
ImGui::ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup(); // Lock X position
|
||||
ImGui::Text("Current ==>");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Previous");
|
||||
|
||||
ImGui::ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip;
|
||||
if (ImGui::ColorButton("##palette", saved_palette[n],
|
||||
palette_button_flags, ImVec2(20, 20)))
|
||||
color = ImVec4(saved_palette[n].x, saved_palette[n].y,
|
||||
saved_palette[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
42
src/app/editor/palette_editor.h
Normal file
42
src/app/editor/palette_editor.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
static constexpr absl::string_view kPaletteCategoryNames[] = {
|
||||
"Sword", "Shield", "Clothes", "World Colors",
|
||||
"Area Colors", "Enemies", "Dungeons", "World Map",
|
||||
"Dungeon Map", "Triforce", "Crystal"};
|
||||
|
||||
static constexpr absl::string_view kPaletteGroupNames[] = {
|
||||
"swords", "shields", "armors", "ow_main",
|
||||
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
|
||||
"ow_mini_map", "3d_object", "3d_object"};
|
||||
|
||||
class PaletteEditor {
|
||||
public:
|
||||
absl::Status Update();
|
||||
void DisplayPalette(gfx::SNESPalette& palette, bool loaded);
|
||||
|
||||
auto SetupROM(ROM& rom) { rom_ = rom; }
|
||||
|
||||
private:
|
||||
ImVec4 current_color_;
|
||||
ROM rom_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
201
src/app/editor/screen_editor.cc
Normal file
201
src/app/editor/screen_editor.cc
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "app/editor/screen_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/icons.h"
|
||||
#include "gui/input.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
ScreenEditor::ScreenEditor() { screen_canvas_.SetCanvasSize(ImVec2(512, 512)); }
|
||||
|
||||
void ScreenEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
DrawInventoryMenuEditor();
|
||||
DrawTitleScreenEditor();
|
||||
DrawNamingScreenEditor();
|
||||
DrawOverworldMapEditor();
|
||||
DrawDungeonMapsEditor();
|
||||
DrawMosaicEditor();
|
||||
END_TAB_BAR()
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawWorldGrid(int world, int h, int w) {
|
||||
const float time = (float)ImGui::GetTime();
|
||||
|
||||
int i = 0;
|
||||
if (world == 1) {
|
||||
i = 64;
|
||||
} else if (world == 2) {
|
||||
i = 128;
|
||||
}
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++) {
|
||||
if (x > 0) ImGui::SameLine();
|
||||
ImGui::PushID(y * 4 + x);
|
||||
std::string label = absl::StrCat(" #", absl::StrFormat("%x", i));
|
||||
if (ImGui::Selectable(label.c_str(), mosaic_tiles_[i] != 0, 0,
|
||||
ImVec2(35, 25))) {
|
||||
mosaic_tiles_[i] ^= 1;
|
||||
}
|
||||
ImGui::PopID();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
TAB_ITEM("Inventory Menu")
|
||||
|
||||
static bool create = false;
|
||||
if (!create && rom_.isLoaded()) {
|
||||
inventory_.Create();
|
||||
palette_ = inventory_.Palette();
|
||||
create = true;
|
||||
}
|
||||
|
||||
DrawInventoryToolset();
|
||||
|
||||
if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("Canvas");
|
||||
ImGui::TableSetupColumn("Tiles");
|
||||
ImGui::TableSetupColumn("Palette");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
screen_canvas_.DrawBackground();
|
||||
screen_canvas_.DrawContextMenu();
|
||||
screen_canvas_.DrawBitmap(inventory_.Bitmap(), 2, create);
|
||||
screen_canvas_.DrawGrid(32.0f);
|
||||
screen_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
|
||||
tilesheet_canvas_.DrawContextMenu();
|
||||
tilesheet_canvas_.DrawBitmap(inventory_.Tilesheet(), 2, create);
|
||||
tilesheet_canvas_.DrawGrid(16.0f);
|
||||
tilesheet_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
gui::DisplayPalette(palette_, create);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Separator();
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawTitleScreenEditor() {
|
||||
TAB_ITEM("Title Screen")
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
void ScreenEditor::DrawNamingScreenEditor() {
|
||||
TAB_ITEM("Naming Screen")
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
void ScreenEditor::DrawOverworldMapEditor() {
|
||||
TAB_ITEM("Overworld Map")
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
void ScreenEditor::DrawDungeonMapsEditor() {
|
||||
TAB_ITEM("Dungeon Maps")
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawMosaicEditor() {
|
||||
TAB_ITEM("Mosaic Transitions")
|
||||
|
||||
if (ImGui::BeginTable("Worlds", 3, ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("Light World");
|
||||
ImGui::TableSetupColumn("Dark World");
|
||||
ImGui::TableSetupColumn("Special World");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawWorldGrid(0);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawWorldGrid(1);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawWorldGrid(2, 4);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
gui::InputHex("Routine Location", &overworldCustomMosaicASM);
|
||||
|
||||
if (ImGui::Button("Generate Mosaic Assembly")) {
|
||||
auto mosaic =
|
||||
rom_.PatchOverworldMosaic(mosaic_tiles_, overworldCustomMosaicASM);
|
||||
if (!mosaic.ok()) {
|
||||
std::cout << mosaic;
|
||||
}
|
||||
}
|
||||
|
||||
END_TAB_ITEM()
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawToolset() {
|
||||
static bool show_bg1 = true;
|
||||
static bool show_bg2 = true;
|
||||
static bool show_bg3 = true;
|
||||
|
||||
static bool drawing_bg1 = true;
|
||||
static bool drawing_bg2 = false;
|
||||
static bool drawing_bg3 = false;
|
||||
|
||||
ImGui::Checkbox("Show BG1", &show_bg1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Show BG2", &show_bg2);
|
||||
|
||||
ImGui::Checkbox("Draw BG1", &drawing_bg1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Draw BG2", &drawing_bg2);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Draw BG3", &drawing_bg3);
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryToolset() {
|
||||
if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#drawTool");
|
||||
ImGui::TableSetupColumn("#sep1");
|
||||
ImGui::TableSetupColumn("#zoomOut");
|
||||
ImGui::TableSetupColumn("#zoomIN");
|
||||
ImGui::TableSetupColumn("#sep2");
|
||||
ImGui::TableSetupColumn("#bg2Tool");
|
||||
ImGui::TableSetupColumn("#bg3Tool");
|
||||
ImGui::TableSetupColumn("#itemTool");
|
||||
|
||||
BUTTON_COLUMN(ICON_MD_UNDO)
|
||||
BUTTON_COLUMN(ICON_MD_REDO)
|
||||
TEXT_COLUMN(ICON_MD_MORE_VERT)
|
||||
BUTTON_COLUMN(ICON_MD_ZOOM_OUT)
|
||||
BUTTON_COLUMN(ICON_MD_ZOOM_IN)
|
||||
TEXT_COLUMN(ICON_MD_MORE_VERT)
|
||||
BUTTON_COLUMN(ICON_MD_DRAW)
|
||||
BUTTON_COLUMN(ICON_MD_BUILD)
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
60
src/app/editor/screen_editor.h
Normal file
60
src/app/editor/screen_editor.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/inventory.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/color.h"
|
||||
#include "gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using MosaicArray = std::array<int, core::kNumOverworldMaps>;
|
||||
static int overworldCustomMosaicASM = 0x1301D0;
|
||||
|
||||
class ScreenEditor {
|
||||
public:
|
||||
ScreenEditor();
|
||||
void SetupROM(ROM &rom) {
|
||||
rom_ = rom;
|
||||
inventory_.SetupROM(rom_);
|
||||
}
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void DrawMosaicEditor();
|
||||
void DrawTitleScreenEditor();
|
||||
void DrawNamingScreenEditor();
|
||||
void DrawOverworldMapEditor();
|
||||
void DrawDungeonMapsEditor();
|
||||
void DrawInventoryMenuEditor();
|
||||
|
||||
void DrawToolset();
|
||||
void DrawInventoryToolset();
|
||||
void DrawWorldGrid(int world, int h = 8, int w = 8);
|
||||
|
||||
char mosaic_tiles_[core::kNumOverworldMaps];
|
||||
|
||||
ROM rom_;
|
||||
Bytes all_gfx_;
|
||||
zelda3::Inventory inventory_;
|
||||
gfx::SNESPalette palette_;
|
||||
gui::Canvas screen_canvas_;
|
||||
gui::Canvas tilesheet_canvas_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user