backend-infra-engineer: Pre-0.2.2 2024 Q3 snapshot
This commit is contained in:
21
src/app/editor/CMakeLists.txt
Normal file
21
src/app/editor/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
set(
|
||||
YAZE_APP_EDITOR_SRC
|
||||
app/editor/dungeon/dungeon_editor.cc
|
||||
app/editor/master_editor.cc
|
||||
app/editor/master_editor_test.cc
|
||||
app/editor/settings_editor.cc
|
||||
app/editor/overworld_editor.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/music/music_editor.cc
|
||||
app/editor/message/message_editor.cc
|
||||
app/editor/message/message_editor_test.cc
|
||||
app/editor/code/assembly_editor.cc
|
||||
app/editor/graphics/screen_editor.cc
|
||||
app/editor/graphics/graphics_editor.cc
|
||||
app/editor/graphics/palette_editor.cc
|
||||
app/editor/graphics/tile16_editor.cc
|
||||
app/editor/graphics/gfx_group_editor.cc
|
||||
app/editor/utils/gfx_context.cc
|
||||
app/editor/overworld/refresh.cc
|
||||
app/editor/overworld/entity.cc
|
||||
)
|
||||
367
src/app/editor/code/assembly_editor.cc
Normal file
367
src/app/editor/code/assembly_editor.cc
Normal file
@@ -0,0 +1,367 @@
|
||||
#include "assembly_editor.h"
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string> RemoveIgnoredFiles(
|
||||
const std::vector<std::string>& files,
|
||||
const std::vector<std::string>& ignored_files) {
|
||||
std::vector<std::string> filtered_files;
|
||||
for (const auto& file : files) {
|
||||
// Remove subdirectory files
|
||||
if (file.find('/') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
// Make sure the file has an extension
|
||||
if (file.find('.') == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(ignored_files.begin(), ignored_files.end(), file) ==
|
||||
ignored_files.end()) {
|
||||
filtered_files.push_back(file);
|
||||
}
|
||||
}
|
||||
return filtered_files;
|
||||
}
|
||||
|
||||
core::FolderItem LoadFolder(const std::string& folder) {
|
||||
// Check if .gitignore exists in the folder
|
||||
std::ifstream gitignore(folder + "/.gitignore");
|
||||
std::vector<std::string> ignored_files;
|
||||
if (gitignore.good()) {
|
||||
std::string line;
|
||||
while (std::getline(gitignore, line)) {
|
||||
if (line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
if (line[0] == '!') {
|
||||
// Ignore the file
|
||||
continue;
|
||||
}
|
||||
ignored_files.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
core::FolderItem current_folder;
|
||||
current_folder.name = folder;
|
||||
auto root_files = FileDialogWrapper::GetFilesInFolder(current_folder.name);
|
||||
current_folder.files = RemoveIgnoredFiles(root_files, ignored_files);
|
||||
|
||||
for (const auto& folder :
|
||||
FileDialogWrapper::GetSubdirectoriesInFolder(current_folder.name)) {
|
||||
core::FolderItem folder_item;
|
||||
folder_item.name = folder;
|
||||
std::string full_folder = current_folder.name + "/" + folder;
|
||||
auto folder_files = FileDialogWrapper::GetFilesInFolder(full_folder);
|
||||
for (const auto& files : folder_files) {
|
||||
// Remove subdirectory files
|
||||
if (files.find('/') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
// Make sure the file has an extension
|
||||
if (files.find('.') == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(ignored_files.begin(), ignored_files.end(), files) !=
|
||||
ignored_files.end()) {
|
||||
continue;
|
||||
}
|
||||
folder_item.files.push_back(files);
|
||||
}
|
||||
|
||||
for (const auto& subdir :
|
||||
FileDialogWrapper::GetSubdirectoriesInFolder(full_folder)) {
|
||||
core::FolderItem subfolder_item;
|
||||
subfolder_item.name = subdir;
|
||||
subfolder_item.files = FileDialogWrapper::GetFilesInFolder(subdir);
|
||||
folder_item.subfolders.push_back(subfolder_item);
|
||||
}
|
||||
current_folder.subfolders.push_back(folder_item);
|
||||
}
|
||||
|
||||
return current_folder;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void AssemblyEditor::OpenFolder(const std::string& folder_path) {
|
||||
current_folder_ = LoadFolder(folder_path);
|
||||
}
|
||||
|
||||
void AssemblyEditor::Update(bool& is_loaded) {
|
||||
ImGui::Begin("Assembly Editor", &is_loaded);
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
DrawFileMenu();
|
||||
DrawEditMenu();
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
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::UpdateCodeView() {
|
||||
ImGui::BeginTable("##table_view", 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable);
|
||||
|
||||
// Table headers
|
||||
ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthFixed, 256.0f);
|
||||
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Table data
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (current_folder_.name != "") {
|
||||
DrawCurrentFolder();
|
||||
} else {
|
||||
if (ImGui::Button("Open Folder")) {
|
||||
current_folder_ = LoadFolder(FileDialogWrapper::ShowOpenFolderDialog());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
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::EndTable();
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawCurrentFolder() {
|
||||
if (ImGui::BeginChild("##current_folder", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (ImGui::BeginTable("##file_table", 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Sortable)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 256.0f);
|
||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& file : current_folder_.files) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(file.c_str())) {
|
||||
ChangeActiveFile(absl::StrCat(current_folder_.name, "/", file));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("File");
|
||||
}
|
||||
|
||||
for (const auto& subfolder : current_folder_.subfolders) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::TreeNode(subfolder.name.c_str())) {
|
||||
for (const auto& file : subfolder.files) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(file.c_str())) {
|
||||
ChangeActiveFile(absl::StrCat(current_folder_.name, "/",
|
||||
subfolder.name, "/", file));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("File");
|
||||
}
|
||||
ImGui::TreePop();
|
||||
} else {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Folder");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawFileTabView() {
|
||||
static int next_tab_id = 0;
|
||||
|
||||
if (ImGui::BeginTabBar("AssemblyFileTabBar", ImGuiTabBarFlags_None)) {
|
||||
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_None)) {
|
||||
if (std::find(active_files_.begin(), active_files_.end(),
|
||||
current_file_id_) != active_files_.end()) {
|
||||
// Room is already open
|
||||
next_tab_id++;
|
||||
}
|
||||
active_files_.push_back(next_tab_id++); // Add new tab
|
||||
}
|
||||
|
||||
// Submit our regular tabs
|
||||
for (int n = 0; n < active_files_.Size;) {
|
||||
bool open = true;
|
||||
|
||||
if (ImGui::BeginTabItem(files_[active_files_[n]].data(), &open,
|
||||
ImGuiTabItemFlags_None)) {
|
||||
auto cpos = text_editor_.GetCursorPosition();
|
||||
{
|
||||
std::ifstream t(current_file_);
|
||||
if (t.good()) {
|
||||
std::string str((std::istreambuf_iterator<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
text_editor_.SetText(str);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error opening file: %s\n", current_file_.c_str());
|
||||
}
|
||||
}
|
||||
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());
|
||||
|
||||
open_files_[active_files_[n]].Render("##asm_editor");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (!open)
|
||||
active_files_.erase(active_files_.Data + n);
|
||||
else
|
||||
n++;
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error opening file: %s\n",
|
||||
current_file_.c_str());
|
||||
}
|
||||
file_is_loaded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Cut() {
|
||||
text_editor_.Cut();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Copy() {
|
||||
text_editor_.Copy();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Paste() {
|
||||
text_editor_.Paste();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Undo() {
|
||||
text_editor_.Undo();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Redo() {
|
||||
text_editor_.Redo();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Update() { return absl::OkStatus(); }
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
79
src/app/editor/code/assembly_editor.h
Normal file
79
src/app/editor/code/assembly_editor.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#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>
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gui/style.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @class AssemblyEditor
|
||||
* @brief Text editor for modifying assembly code.
|
||||
*/
|
||||
class AssemblyEditor : public Editor {
|
||||
public:
|
||||
AssemblyEditor() {
|
||||
text_editor_.SetLanguageDefinition(gui::GetAssemblyLanguageDef());
|
||||
text_editor_.SetPalette(TextEditor::GetDarkPalette());
|
||||
text_editor_.SetShowWhitespaces(false);
|
||||
type_ = EditorType::kAssembly;
|
||||
}
|
||||
void ChangeActiveFile(const std::string_view &filename) {
|
||||
current_file_ = filename;
|
||||
file_is_loaded_ = false;
|
||||
}
|
||||
|
||||
void Update(bool &is_loaded);
|
||||
void InlineUpdate();
|
||||
|
||||
void UpdateCodeView();
|
||||
|
||||
absl::Status Cut() override;
|
||||
absl::Status Copy() override;
|
||||
absl::Status Paste() override;
|
||||
|
||||
absl::Status Undo() override;
|
||||
absl::Status Redo() override;
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
void OpenFolder(const std::string &folder_path);
|
||||
|
||||
private:
|
||||
void DrawFileMenu();
|
||||
void DrawEditMenu();
|
||||
|
||||
void SetEditorText();
|
||||
|
||||
void DrawCurrentFolder();
|
||||
|
||||
void DrawFileTabView();
|
||||
|
||||
bool file_is_loaded_ = false;
|
||||
|
||||
std::vector<std::string> files_;
|
||||
std::vector<TextEditor> open_files_;
|
||||
ImVector<int> active_files_;
|
||||
int current_file_id_ = 0;
|
||||
|
||||
std::string current_file_;
|
||||
core::FolderItem current_folder_;
|
||||
TextEditor text_editor_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
88
src/app/editor/code/memory_editor.h
Normal file
88
src/app/editor/code/memory_editor.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_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/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/code/memory_editor.h"
|
||||
#include "app/editor/dungeon/dungeon_editor.h"
|
||||
#include "app/editor/graphics/graphics_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/graphics/screen_editor.h"
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/editor/utils/recent_files.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Text;
|
||||
|
||||
struct MemoryEditorWithDiffChecker : public SharedRom {
|
||||
void Update(bool &show_memory_editor) {
|
||||
static MemoryEditor mem_edit;
|
||||
static MemoryEditor comp_edit;
|
||||
static bool show_compare_rom = false;
|
||||
static Rom comparison_rom;
|
||||
ImGui::Begin("Hex Editor", &show_memory_editor);
|
||||
if (ImGui::Button("Compare Rom")) {
|
||||
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||
PRINT_IF_ERROR(comparison_rom.LoadFromFile(file_name));
|
||||
show_compare_rom = true;
|
||||
}
|
||||
|
||||
static uint64_t convert_address = 0;
|
||||
gui::InputHex("SNES to PC", (int *)&convert_address, 6, 200.f);
|
||||
SameLine();
|
||||
Text("%x", core::SnesToPc(convert_address));
|
||||
|
||||
// mem_edit.DrawWindow("Memory Editor", (void*)&(*rom()), rom()->size());
|
||||
BEGIN_TABLE("Memory Comparison", 2, ImGuiTableFlags_Resizable);
|
||||
SETUP_COLUMN("Source")
|
||||
SETUP_COLUMN("Dest")
|
||||
|
||||
NEXT_COLUMN()
|
||||
Text("%s", rom()->filename().data());
|
||||
mem_edit.DrawContents((void *)&(*rom()), rom()->size());
|
||||
|
||||
NEXT_COLUMN()
|
||||
if (show_compare_rom) {
|
||||
comp_edit.SetComparisonData((void *)&(*rom()));
|
||||
ImGui::BeginGroup();
|
||||
ImGui::BeginChild("Comparison ROM");
|
||||
Text("%s", comparison_rom.filename().data());
|
||||
comp_edit.DrawContents((void *)&(comparison_rom), comparison_rom.size());
|
||||
ImGui::EndChild();
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
END_TABLE()
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||
#define YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
namespace context {
|
||||
|
||||
class EntranceContext {
|
||||
public:
|
||||
absl::Status LoadEntranceTileTypes(Rom& rom) {
|
||||
int offset_low = 0xDB8BF;
|
||||
int offset_high = 0xDB917;
|
||||
|
||||
for (int i = 0; i < 0x2C; i++) {
|
||||
// Load entrance tile types
|
||||
ASSIGN_OR_RETURN(auto value_low, rom.ReadWord(offset_low + i));
|
||||
entrance_tile_types_low_.push_back(value_low);
|
||||
ASSIGN_OR_RETURN(auto value_high, rom.ReadWord(offset_high + i));
|
||||
entrance_tile_types_low_.push_back(value_high);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint16_t> entrance_tile_types_low_;
|
||||
std::vector<uint16_t> entrance_tile_types_high_;
|
||||
};
|
||||
|
||||
} // namespace context
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "dungeon_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/labeling.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/dungeon/object_names.h"
|
||||
#include "app/zelda3/dungeon/room_names.h"
|
||||
@@ -18,15 +18,26 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
constexpr ImGuiTableFlags kDungeonObjectTableFlags =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV;
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndTabBar;
|
||||
using ImGui::EndTabItem;
|
||||
using ImGui::RadioButton;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
constexpr ImGuiTableFlags kDungeonObjectTableFlags =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV;
|
||||
|
||||
absl::Status DungeonEditor::Update() {
|
||||
if (!is_loaded_ && rom()->is_loaded()) {
|
||||
@@ -41,7 +52,7 @@ absl::Status DungeonEditor::Update() {
|
||||
|
||||
TAB_BAR("##DungeonEditorTabBar")
|
||||
TAB_ITEM("Room Editor")
|
||||
UpdateDungeonRoomView();
|
||||
status_ = UpdateDungeonRoomView();
|
||||
END_TAB_ITEM()
|
||||
TAB_ITEM("Usage Statistics")
|
||||
if (is_loaded_) {
|
||||
@@ -82,7 +93,14 @@ absl::Status DungeonEditor::Initialize() {
|
||||
}
|
||||
|
||||
LoadDungeonRoomSize();
|
||||
LoadRoomEntrances();
|
||||
// LoadRoomEntrances
|
||||
for (int i = 0; i < 0x07; ++i) {
|
||||
entrances_.emplace_back(zelda3::dungeon::RoomEntrance(*rom(), i, true));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0x85; ++i) {
|
||||
entrances_.emplace_back(zelda3::dungeon::RoomEntrance(*rom(), i, false));
|
||||
}
|
||||
|
||||
// Load the palette group and palette for the dungeon
|
||||
full_palette_ = dungeon_man_pal_group[current_palette_group_id_];
|
||||
@@ -92,7 +110,7 @@ absl::Status DungeonEditor::Initialize() {
|
||||
graphics_bin_ = *rom()->mutable_bitmap_manager();
|
||||
// Create a vector of pointers to the current block bitmaps
|
||||
for (int block : rooms_[current_room_id_].blocks()) {
|
||||
room_gfx_sheets_.emplace_back(graphics_bin_[block].get());
|
||||
room_gfx_sheets_.emplace_back(&graphics_bin_[block]);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -100,16 +118,16 @@ absl::Status DungeonEditor::Initialize() {
|
||||
absl::Status DungeonEditor::RefreshGraphics() {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int block = rooms_[current_room_id_].blocks()[i];
|
||||
RETURN_IF_ERROR(graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
||||
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0));
|
||||
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
||||
rom()->UpdateBitmap(&graphics_bin_[block], true);
|
||||
}
|
||||
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
|
||||
for (int i = 9; i < 16; i++) {
|
||||
int block = rooms_[current_room_id_].blocks()[i];
|
||||
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
||||
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0));
|
||||
rom()->UpdateBitmap(&graphics_bin_[block], true);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -168,8 +186,7 @@ absl::Status DungeonEditor::UpdateDungeonRoomView() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("#DungeonEditTable", 3, kDungeonTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
if (BeginTable("#DungeonEditTable", 3, kDungeonTableFlags, ImVec2(0, 0))) {
|
||||
TableSetupColumn("Room Selector");
|
||||
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
@@ -198,8 +215,8 @@ absl::Status DungeonEditor::UpdateDungeonRoomView() {
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawToolset() {
|
||||
if (ImGui::BeginTable("DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
if (BeginTable("DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("#undoTool");
|
||||
TableSetupColumn("#redoTool");
|
||||
TableSetupColumn("#separator");
|
||||
@@ -215,47 +232,43 @@ void DungeonEditor::DrawToolset() {
|
||||
TableSetupColumn("#blockTool");
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_UNDO)) {
|
||||
if (Button(ICON_MD_UNDO)) {
|
||||
PRINT_IF_ERROR(Undo());
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_REDO)) {
|
||||
if (Button(ICON_MD_REDO)) {
|
||||
PRINT_IF_ERROR(Redo());
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::Text(ICON_MD_MORE_VERT);
|
||||
Text(ICON_MD_MORE_VERT);
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_FILTER_NONE,
|
||||
background_type_ == kBackgroundAny)) {
|
||||
if (RadioButton(ICON_MD_FILTER_NONE, background_type_ == kBackgroundAny)) {
|
||||
background_type_ = kBackgroundAny;
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_FILTER_1,
|
||||
background_type_ == kBackground1)) {
|
||||
if (RadioButton(ICON_MD_FILTER_1, background_type_ == kBackground1)) {
|
||||
background_type_ = kBackground1;
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_FILTER_2,
|
||||
background_type_ == kBackground2)) {
|
||||
if (RadioButton(ICON_MD_FILTER_2, background_type_ == kBackground2)) {
|
||||
background_type_ = kBackground2;
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_FILTER_3,
|
||||
background_type_ == kBackground3)) {
|
||||
if (RadioButton(ICON_MD_FILTER_3, background_type_ == kBackground3)) {
|
||||
background_type_ = kBackground3;
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::Text(ICON_MD_MORE_VERT);
|
||||
Text(ICON_MD_MORE_VERT);
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_PEST_CONTROL, placement_type_ == kSprite)) {
|
||||
if (RadioButton(ICON_MD_PEST_CONTROL, placement_type_ == kSprite)) {
|
||||
placement_type_ = kSprite;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -263,7 +276,7 @@ void DungeonEditor::DrawToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_GRASS, placement_type_ == kItem)) {
|
||||
if (RadioButton(ICON_MD_GRASS, placement_type_ == kItem)) {
|
||||
placement_type_ = kItem;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -271,7 +284,7 @@ void DungeonEditor::DrawToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_SENSOR_DOOR, placement_type_ == kDoor)) {
|
||||
if (RadioButton(ICON_MD_SENSOR_DOOR, placement_type_ == kDoor)) {
|
||||
placement_type_ = kDoor;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -279,7 +292,7 @@ void DungeonEditor::DrawToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::RadioButton(ICON_MD_SQUARE, placement_type_ == kBlock)) {
|
||||
if (RadioButton(ICON_MD_SQUARE, placement_type_ == kBlock)) {
|
||||
placement_type_ = kBlock;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -287,7 +300,7 @@ void DungeonEditor::DrawToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_PALETTE)) {
|
||||
if (Button(ICON_MD_PALETTE)) {
|
||||
palette_showing_ = !palette_showing_;
|
||||
}
|
||||
|
||||
@@ -301,8 +314,8 @@ void DungeonEditor::DrawRoomSelector() {
|
||||
gui::InputHex("Palette ID", ¤t_palette_id_);
|
||||
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
int i = 0;
|
||||
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
@@ -318,86 +331,66 @@ void DungeonEditor::DrawRoomSelector() {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
using ImGui::Separator;
|
||||
|
||||
void DungeonEditor::DrawEntranceSelector() {
|
||||
if (rom()->is_loaded()) {
|
||||
gui::InputHexWord("Entrance ID",
|
||||
&entrances_[current_entrance_id_].entrance_id_);
|
||||
auto current_entrance = entrances_[current_entrance_id_];
|
||||
gui::InputHexWord("Entrance ID", ¤t_entrance.entrance_id_);
|
||||
gui::InputHexWord("Room ID", ¤t_entrance.room_, 50.f, true);
|
||||
SameLine();
|
||||
|
||||
gui::InputHexWord("Room ID", &entrances_[current_entrance_id_].room_, 50.f,
|
||||
gui::InputHexByte("Dungeon ID", ¤t_entrance.dungeon_id_, 50.f, true);
|
||||
gui::InputHexByte("Blockset", ¤t_entrance.blockset_, 50.f, true);
|
||||
SameLine();
|
||||
|
||||
gui::InputHexByte("Music", ¤t_entrance.music_, 50.f, true);
|
||||
SameLine();
|
||||
gui::InputHexByte("Floor", ¤t_entrance.floor_);
|
||||
Separator();
|
||||
|
||||
gui::InputHexWord("Player X ", ¤t_entrance.x_position_);
|
||||
SameLine();
|
||||
gui::InputHexWord("Player Y ", ¤t_entrance.y_position_);
|
||||
|
||||
gui::InputHexWord("Camera X", ¤t_entrance.camera_trigger_x_);
|
||||
SameLine();
|
||||
gui::InputHexWord("Camera Y", ¤t_entrance.camera_trigger_y_);
|
||||
|
||||
gui::InputHexWord("Scroll X ", ¤t_entrance.camera_x_);
|
||||
SameLine();
|
||||
gui::InputHexWord("Scroll Y ", ¤t_entrance.camera_y_);
|
||||
|
||||
gui::InputHexWord("Exit", ¤t_entrance.exit_, 50.f, true);
|
||||
|
||||
Separator();
|
||||
Text("Camera Boundaries");
|
||||
Separator();
|
||||
Text("\t\t\t\t\tNorth East South West");
|
||||
gui::InputHexByte("Quadrant", ¤t_entrance.camera_boundary_qn_, 50.f,
|
||||
true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("Dungeon ID",
|
||||
&entrances_[current_entrance_id_].dungeon_id_, 50.f,
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_qe_, 50.f, true);
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_qs_, 50.f, true);
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_qw_, 50.f, true);
|
||||
|
||||
gui::InputHexByte("Full room", ¤t_entrance.camera_boundary_fn_, 50.f,
|
||||
true);
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_fe_, 50.f, true);
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_fs_, 50.f, true);
|
||||
SameLine();
|
||||
gui::InputHexByte("", ¤t_entrance.camera_boundary_fw_, 50.f, true);
|
||||
|
||||
gui::InputHexByte("Blockset", &entrances_[current_entrance_id_].blockset_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
|
||||
gui::InputHexByte("Music", &entrances_[current_entrance_id_].music_, 50.f,
|
||||
true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("Floor", &entrances_[current_entrance_id_].floor_);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
gui::InputHexWord("Player X ",
|
||||
&entrances_[current_entrance_id_].x_position_);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexWord("Player Y ",
|
||||
&entrances_[current_entrance_id_].y_position_);
|
||||
|
||||
gui::InputHexWord("Camera X",
|
||||
&entrances_[current_entrance_id_].camera_trigger_x_);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexWord("Camera Y",
|
||||
&entrances_[current_entrance_id_].camera_trigger_y_);
|
||||
|
||||
gui::InputHexWord("Scroll X ",
|
||||
&entrances_[current_entrance_id_].camera_x_);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexWord("Scroll Y ",
|
||||
&entrances_[current_entrance_id_].camera_y_);
|
||||
|
||||
gui::InputHexWord("Exit", &entrances_[current_entrance_id_].exit_, 50.f,
|
||||
true);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Camera Boundaries");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("\t\t\t\t\tNorth East South West");
|
||||
gui::InputHexByte("Quadrant",
|
||||
&entrances_[current_entrance_id_].camera_boundary_qn_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_qe_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_qs_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_qw_,
|
||||
50.f, true);
|
||||
|
||||
gui::InputHexByte("Full room",
|
||||
&entrances_[current_entrance_id_].camera_boundary_fn_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_fe_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_fs_,
|
||||
50.f, true);
|
||||
ImGui::SameLine();
|
||||
gui::InputHexByte("", &entrances_[current_entrance_id_].camera_boundary_fw_,
|
||||
50.f, true);
|
||||
|
||||
if (ImGui::BeginChild("EntranceSelector", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (BeginChild("EntranceSelector", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
for (int i = 0; i < 0x85 + 7; i++) {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
current_entrance_id_ == i, "Dungeon Entrance Names",
|
||||
@@ -412,15 +405,15 @@ void DungeonEditor::DrawEntranceSelector() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawDungeonTabView() {
|
||||
static int next_tab_id = 0;
|
||||
|
||||
if (ImGui::BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
|
||||
if (ImGui::TabItemButton("+", kDungeonTabFlags)) {
|
||||
if (BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
|
||||
if (ImGui::TabItemButton(ICON_MD_ADD, kDungeonTabFlags)) {
|
||||
if (std::find(active_rooms_.begin(), active_rooms_.end(),
|
||||
current_room_id_) != active_rooms_.end()) {
|
||||
// Room is already open
|
||||
@@ -438,11 +431,10 @@ void DungeonEditor::DrawDungeonTabView() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(
|
||||
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
|
||||
ImGuiTabItemFlags_None)) {
|
||||
if (BeginTabItem(zelda3::dungeon::kRoomNames[active_rooms_[n]].data(),
|
||||
&open, ImGuiTabItemFlags_None)) {
|
||||
DrawDungeonCanvas(active_rooms_[n]);
|
||||
ImGui::EndTabItem();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
if (!open)
|
||||
@@ -451,33 +443,33 @@ void DungeonEditor::DrawDungeonTabView() {
|
||||
n++;
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
EndTabBar();
|
||||
}
|
||||
ImGui::Separator();
|
||||
Separator();
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawDungeonCanvas(int room_id) {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
gui::InputHexByte("Layout", &rooms_[room_id].layout);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
gui::InputHexByte("Palette", &rooms_[room_id].palette);
|
||||
|
||||
gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -492,7 +484,8 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
|
||||
|
||||
void DungeonEditor::DrawRoomGraphics() {
|
||||
const auto height = 0x40;
|
||||
room_gfx_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x10 * 0x40 + 1));
|
||||
const int num_sheets = 0x10;
|
||||
room_gfx_canvas_.DrawBackground(ImVec2(0x100 + 1, num_sheets * height + 1));
|
||||
room_gfx_canvas_.DrawContextMenu();
|
||||
room_gfx_canvas_.DrawTileSelector(32);
|
||||
if (is_loaded_) {
|
||||
@@ -505,7 +498,7 @@ void DungeonEditor::DrawRoomGraphics() {
|
||||
top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
|
||||
}
|
||||
room_gfx_canvas_.draw_list()->AddImage(
|
||||
(void*)graphics_bin_[block].get()->texture(),
|
||||
(void*)graphics_bin_[block].texture(),
|
||||
ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
|
||||
ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
|
||||
room_gfx_canvas_.zero_point().y + offset));
|
||||
@@ -517,34 +510,34 @@ void DungeonEditor::DrawRoomGraphics() {
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawTileSelector() {
|
||||
if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
|
||||
if (ImGui::BeginTabItem("Room Graphics")) {
|
||||
if (BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
|
||||
if (BeginTabItem("Room Graphics")) {
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)3);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
DrawRoomGraphics();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
EndChild();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Object Renderer")) {
|
||||
if (BeginTabItem("Object Renderer")) {
|
||||
DrawObjectRenderer();
|
||||
ImGui::EndTabItem();
|
||||
EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonEditor::DrawObjectRenderer() {
|
||||
if (ImGui::BeginTable("DungeonObjectEditorTable", 2, kDungeonObjectTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
if (BeginTable("DungeonObjectEditorTable", 2, kDungeonObjectTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
TableSetupColumn("Canvas");
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("DungeonObjectButtons", ImVec2(250, 0), true);
|
||||
BeginChild("DungeonObjectButtons", ImVec2(250, 0), true);
|
||||
|
||||
int selected_object = 0;
|
||||
int i = 0;
|
||||
@@ -560,12 +553,11 @@ void DungeonEditor::DrawObjectRenderer() {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
// Right side of the table - Canvas
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1),
|
||||
true);
|
||||
BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1), true);
|
||||
|
||||
object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
|
||||
object_canvas_.DrawContextMenu();
|
||||
@@ -576,7 +568,7 @@ void DungeonEditor::DrawObjectRenderer() {
|
||||
object_canvas_.DrawGrid(32.0f);
|
||||
object_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
@@ -589,16 +581,6 @@ void DungeonEditor::DrawObjectRenderer() {
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonEditor::LoadRoomEntrances() {
|
||||
for (int i = 0; i < 0x07; ++i) {
|
||||
entrances_.emplace_back(zelda3::dungeon::RoomEntrance(*rom(), i, true));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0x85; ++i) {
|
||||
entrances_.emplace_back(zelda3::dungeon::RoomEntrance(*rom(), i, false));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
void DungeonEditor::CalculateUsageStats() {
|
||||
@@ -663,16 +645,16 @@ void RenderUnusedSets(const absl::flat_hash_map<T, int>& usage_map, int max_set,
|
||||
}
|
||||
for (const auto& set : unused_sets) {
|
||||
if (spriteset_offset != 0x00) {
|
||||
ImGui::Text("%#02x, %#02x", set, (set + spriteset_offset));
|
||||
Text("%#02x, %#02x", set, (set + spriteset_offset));
|
||||
} else {
|
||||
ImGui::Text("%#02x", set);
|
||||
Text("%#02x", set);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DungeonEditor::DrawUsageStats() {
|
||||
if (ImGui::Button("Refresh")) {
|
||||
if (Button("Refresh")) {
|
||||
selected_blockset_ = 0xFFFF;
|
||||
selected_spriteset_ = 0xFFFF;
|
||||
selected_palette_ = 0xFFFF;
|
||||
@@ -684,9 +666,9 @@ void DungeonEditor::DrawUsageStats() {
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
if (ImGui::BeginTable("DungeonUsageStatsTable", 8,
|
||||
kDungeonTableFlags | ImGuiTableFlags_SizingFixedFit,
|
||||
ImGui::GetContentRegionAvail())) {
|
||||
if (BeginTable("DungeonUsageStatsTable", 8,
|
||||
kDungeonTableFlags | ImGuiTableFlags_SizingFixedFit,
|
||||
ImGui::GetContentRegionAvail())) {
|
||||
TableSetupColumn("Blockset Usage");
|
||||
TableSetupColumn("Unused Blockset");
|
||||
TableSetupColumn("Palette Usage");
|
||||
@@ -699,51 +681,50 @@ void DungeonEditor::DrawUsageStats() {
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("BlocksetUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("BlocksetUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderSetUsage(blockset_usage_, selected_blockset_);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("UnusedBlocksetScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("UnusedBlocksetScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderUnusedSets(blockset_usage_, 0x25);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("PaletteUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("PaletteUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderSetUsage(palette_usage_, selected_palette_);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("UnusedPaletteScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("UnusedPaletteScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderUnusedSets(palette_usage_, 0x48);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
|
||||
ImGui::BeginChild("SpritesetUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("SpritesetUsageScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderSetUsage(spriteset_usage_, selected_spriteset_, 0x40);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
RenderUnusedSets(spriteset_usage_, 0x90, 0x40);
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
ImGui::BeginChild("UsageGrid", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::Text("%s",
|
||||
absl::StrFormat("Total size of all rooms: %d hex format: %#06x",
|
||||
total_room_size_, total_room_size_)
|
||||
.c_str());
|
||||
BeginChild("UsageGrid", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
Text("%s", absl::StrFormat("Total size of all rooms: %d hex format: %#06x",
|
||||
total_room_size_, total_room_size_)
|
||||
.c_str());
|
||||
DrawUsageGrid();
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
if (selected_blockset_ < 0x25) {
|
||||
@@ -801,10 +782,10 @@ void DungeonEditor::DrawUsageGrid() {
|
||||
ImGuiCol_Button,
|
||||
ImVec4(1.0f, 0.5f, 0.0f, 1.0f)); // Or any highlight color
|
||||
}
|
||||
if (ImGui::Button(absl::StrFormat(
|
||||
"%#x", rooms_[row * squaresWide + col].room_size())
|
||||
.c_str(),
|
||||
ImVec2(55, 30))) {
|
||||
if (Button(absl::StrFormat("%#x",
|
||||
rooms_[row * squaresWide + col].room_size())
|
||||
.c_str(),
|
||||
ImVec2(55, 30))) {
|
||||
// Switch over to the room editor tab
|
||||
// and add a room tab by the ID of the square
|
||||
// that was clicked
|
||||
@@ -826,20 +807,20 @@ void DungeonEditor::DrawUsageGrid() {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
// Display a tooltip with all the room properties
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("Room ID: %d", row * squaresWide + col);
|
||||
ImGui::Text("Blockset: %#02x", room.blockset);
|
||||
ImGui::Text("Spriteset: %#02x", room.spriteset);
|
||||
ImGui::Text("Palette: %#02x", room.palette);
|
||||
ImGui::Text("Floor1: %#02x", room.floor1);
|
||||
ImGui::Text("Floor2: %#02x", room.floor2);
|
||||
ImGui::Text("Message ID: %#04x", room.message_id_);
|
||||
ImGui::Text("Size: %#06x", room.room_size());
|
||||
ImGui::Text("Size Pointer: %#06x", room.room_size_ptr());
|
||||
Text("Room ID: %d", row * squaresWide + col);
|
||||
Text("Blockset: %#02x", room.blockset);
|
||||
Text("Spriteset: %#02x", room.spriteset);
|
||||
Text("Palette: %#02x", room.palette);
|
||||
Text("Floor1: %#02x", room.floor1);
|
||||
Text("Floor2: %#02x", room.floor2);
|
||||
Text("Message ID: %#04x", room.message_id_);
|
||||
Text("Size: %#016llx", room.room_size());
|
||||
Text("Size Pointer: %#016llx", room.room_size_ptr());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// Keep squares in the same line
|
||||
ImGui::SameLine();
|
||||
SameLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/core/labeling.h"
|
||||
#include "app/editor/modules/gfx_group_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
@@ -46,12 +46,15 @@ class DungeonEditor : public Editor,
|
||||
public SharedRom,
|
||||
public core::ExperimentFlags {
|
||||
public:
|
||||
DungeonEditor() { type_ = EditorType::kDungeon; }
|
||||
|
||||
absl::Status Update() override;
|
||||
absl::Status Cut() override { return absl::OkStatus(); }
|
||||
absl::Status Copy() override { return absl::OkStatus(); }
|
||||
absl::Status Paste() override { return absl::OkStatus(); }
|
||||
absl::Status Undo() override { return absl::OkStatus(); }
|
||||
absl::Status Redo() override { return absl::OkStatus(); }
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
void add_room(int i) { active_rooms_.push_back(i); }
|
||||
|
||||
@@ -74,8 +77,6 @@ class DungeonEditor : public Editor,
|
||||
void DrawTileSelector();
|
||||
void DrawObjectRenderer();
|
||||
|
||||
void LoadRoomEntrances();
|
||||
|
||||
void CalculateUsageStats();
|
||||
void DrawUsageStats();
|
||||
void DrawUsageGrid();
|
||||
@@ -141,6 +142,8 @@ class DungeonEditor : public Editor,
|
||||
|
||||
std::unordered_map<int, int> room_size_addresses_;
|
||||
std::unordered_map<int, ImVec4> room_palette_;
|
||||
|
||||
absl::Status status_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
311
src/app/editor/graphics/gfx_group_editor.cc
Normal file
311
src/app/editor/graphics/gfx_group_editor.cc
Normal file
@@ -0,0 +1,311 @@
|
||||
#include "gfx_group_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginGroup;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::ColorButton;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndGroup;
|
||||
using ImGui::EndTabBar;
|
||||
using ImGui::EndTabItem;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::GetContentRegionAvail;
|
||||
using ImGui::GetStyle;
|
||||
using ImGui::IsItemClicked;
|
||||
using ImGui::PopID;
|
||||
using ImGui::PushID;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Separator;
|
||||
using ImGui::SetNextItemWidth;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
using gfx::kPaletteGroupNames;
|
||||
using gfx::PaletteCategory;
|
||||
|
||||
absl::Status GfxGroupEditor::Update() {
|
||||
if (BeginTabBar("GfxGroupEditor")) {
|
||||
if (BeginTabItem("Main")) {
|
||||
gui::InputHexByte("Selected Blockset", &selected_blockset_,
|
||||
(uint8_t)0x24);
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "blockset", "0x" + std::to_string(selected_blockset_),
|
||||
"Blockset " + std::to_string(selected_blockset_));
|
||||
DrawBlocksetViewer();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
if (BeginTabItem("Rooms")) {
|
||||
gui::InputHexByte("Selected Blockset", &selected_roomset_, (uint8_t)81);
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "roomset", "0x" + std::to_string(selected_roomset_),
|
||||
"Roomset " + std::to_string(selected_roomset_));
|
||||
DrawRoomsetViewer();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
if (BeginTabItem("Sprites")) {
|
||||
gui::InputHexByte("Selected Spriteset", &selected_spriteset_,
|
||||
(uint8_t)143);
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "spriteset", "0x" + std::to_string(selected_spriteset_),
|
||||
"Spriteset " + std::to_string(selected_spriteset_));
|
||||
Text("Values");
|
||||
DrawSpritesetViewer();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
if (BeginTabItem("Palettes")) {
|
||||
DrawPaletteViewer();
|
||||
EndTabItem();
|
||||
}
|
||||
|
||||
EndTabBar();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
|
||||
if (BeginTable("##BlocksetTable", sheet_only ? 1 : 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
|
||||
ImVec2(0, 0))) {
|
||||
if (!sheet_only) {
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
GetContentRegionAvail().x);
|
||||
}
|
||||
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
if (!sheet_only) {
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->main_blockset_ids[selected_blockset_][i]);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[sheet_id];
|
||||
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 22);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawRoomsetViewer() {
|
||||
Text("Values - Overwrites 4 of main blockset");
|
||||
if (BeginTable("##Roomstable", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("List", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
GetContentRegionAvail().x);
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginChild("##RoomsetList");
|
||||
for (int i = 0; i < 0x51; i++) {
|
||||
BeginGroup();
|
||||
std::string roomset_label = absl::StrFormat("0x%02X", i);
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "roomset", roomset_label, "Roomset " + roomset_label);
|
||||
if (IsItemClicked()) {
|
||||
selected_roomset_ = i;
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->room_blockset_ids[selected_roomset_][i]);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[sheet_id];
|
||||
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 23);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
|
||||
if (BeginTable("##SpritesTable", sheet_only ? 1 : 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
|
||||
ImVec2(0, 0))) {
|
||||
if (!sheet_only) {
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
GetContentRegionAvail().x);
|
||||
}
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
if (!sheet_only) {
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->spriteset_ids[selected_spriteset_][i]);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[115 + sheet_id];
|
||||
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 24);
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
|
||||
if (palette.empty()) {
|
||||
return;
|
||||
}
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SnesColorButton(absl::StrCat("Palette", n), palette[n],
|
||||
ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip)) {
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GfxGroupEditor::DrawPaletteViewer() {
|
||||
gui::InputHexByte("Selected Paletteset", &selected_paletteset_);
|
||||
if (selected_paletteset_ >= 71) {
|
||||
selected_paletteset_ = 71;
|
||||
}
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "paletteset", "0x" + std::to_string(selected_paletteset_),
|
||||
"Paletteset " + std::to_string(selected_paletteset_));
|
||||
|
||||
uint8_t &dungeon_main_palette_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][0];
|
||||
uint8_t &dungeon_spr_pal_1_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][1];
|
||||
uint8_t &dungeon_spr_pal_2_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][2];
|
||||
uint8_t &dungeon_spr_pal_3_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][3];
|
||||
|
||||
gui::InputHexByte("Dungeon Main", &dungeon_main_palette_val);
|
||||
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, kPaletteGroupNames[PaletteCategory::kDungeons].data(),
|
||||
std::to_string(dungeon_main_palette_val), "Unnamed dungeon palette");
|
||||
auto &palette = *rom()->mutable_palette_group()->dungeon_main.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][0]);
|
||||
DrawPaletteFromPaletteGroup(palette);
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 1", &dungeon_spr_pal_1_val);
|
||||
auto &spr_aux_pal1 =
|
||||
*rom()->mutable_palette_group()->sprites_aux1.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][1]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal1);
|
||||
SameLine();
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, kPaletteGroupNames[PaletteCategory::kSpritesAux1].data(),
|
||||
std::to_string(dungeon_spr_pal_1_val), "Dungeon Spr Pal 1");
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 2", &dungeon_spr_pal_2_val);
|
||||
auto &spr_aux_pal2 =
|
||||
*rom()->mutable_palette_group()->sprites_aux2.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][2]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal2);
|
||||
SameLine();
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, kPaletteGroupNames[PaletteCategory::kSpritesAux2].data(),
|
||||
std::to_string(dungeon_spr_pal_2_val), "Dungeon Spr Pal 2");
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 3", &dungeon_spr_pal_3_val);
|
||||
auto &spr_aux_pal3 =
|
||||
*rom()->mutable_palette_group()->sprites_aux3.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][3]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal3);
|
||||
SameLine();
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, kPaletteGroupNames[PaletteCategory::kSpritesAux3].data(),
|
||||
std::to_string(dungeon_spr_pal_3_val), "Dungeon Spr Pal 3");
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,21 +1,19 @@
|
||||
#ifndef YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/gui/widgets.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -42,7 +40,9 @@ class GfxGroupEditor : public SharedRom {
|
||||
selected_spriteset_ = spriteset;
|
||||
}
|
||||
|
||||
void InitBlockset(gfx::Bitmap tile16_blockset);
|
||||
void InitBlockset(gfx::Bitmap* tile16_blockset) {
|
||||
tile16_blockset_bmp_ = tile16_blockset;
|
||||
}
|
||||
|
||||
private:
|
||||
int preview_palette_id_ = 0;
|
||||
@@ -50,8 +50,7 @@ class GfxGroupEditor : public SharedRom {
|
||||
uint8_t selected_blockset_ = 0;
|
||||
uint8_t selected_roomset_ = 0;
|
||||
uint8_t selected_spriteset_ = 0;
|
||||
|
||||
PaletteEditor palette_editor_;
|
||||
uint8_t selected_paletteset_ = 0;
|
||||
|
||||
gui::Canvas blockset_canvas_;
|
||||
gui::Canvas roomset_canvas_;
|
||||
@@ -59,7 +58,7 @@ class GfxGroupEditor : public SharedRom {
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
gfx::PaletteGroup palette_group_;
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
gfx::Bitmap* tile16_blockset_bmp_;
|
||||
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
@@ -1,22 +1,23 @@
|
||||
#include "app/editor/graphics_editor.h"
|
||||
#include "graphics_editor.h"
|
||||
|
||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||
#include <imgui_memory_editor.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 "absl/status/statusor.h"
|
||||
#include "app/core/platform/clipboard.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/compression.h"
|
||||
#include "app/gfx/scad_format.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/asset_browser.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
@@ -24,12 +25,18 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using gfx::kPaletteGroupAddressesKeys;
|
||||
using ImGui::Button;
|
||||
using ImGui::InputInt;
|
||||
using ImGui::InputText;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::TableNextColumn;
|
||||
|
||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||
"#memoryEditor",
|
||||
"##separator_gfx1",
|
||||
};
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
|
||||
@@ -40,9 +47,20 @@ constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton;
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
status_ = UpdateGfxEdit();
|
||||
TAB_ITEM("Sheet Browser")
|
||||
if (asset_browser_.Initialized == false) {
|
||||
asset_browser_.Initialize(rom()->mutable_bitmap_manager());
|
||||
}
|
||||
asset_browser_.Draw(rom()->mutable_bitmap_manager());
|
||||
|
||||
END_TAB_ITEM()
|
||||
status_ = UpdateScadView();
|
||||
status_ = UpdateLinkGfxView();
|
||||
END_TAB_BAR()
|
||||
@@ -109,7 +127,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
TableNextColumn();
|
||||
if (Button(ICON_MD_CONTENT_COPY)) {
|
||||
std::vector<uint8_t> png_data =
|
||||
rom()->bitmap_manager().shared_bitmap(current_sheet_)->GetPngData();
|
||||
rom()->bitmap_manager().shared_bitmap(current_sheet_).GetPngData();
|
||||
CopyImageToClipboard(png_data);
|
||||
}
|
||||
HOVER_HINT("Copy to Clipboard");
|
||||
@@ -124,10 +142,8 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
->mutable_bitmap_manager()
|
||||
->mutable_bitmap(current_sheet_)
|
||||
->Create(width, height, 8, png_data);
|
||||
rom()->UpdateBitmap(rom()
|
||||
->mutable_bitmap_manager()
|
||||
->mutable_bitmap(current_sheet_)
|
||||
.get());
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_));
|
||||
}
|
||||
}
|
||||
HOVER_HINT("Paste from Clipboard");
|
||||
@@ -148,7 +164,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
|
||||
TableNextColumn();
|
||||
auto bitmap = rom()->bitmap_manager()[current_sheet_];
|
||||
auto palette = bitmap->palette();
|
||||
auto palette = bitmap.palette();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImGui::SameLine();
|
||||
auto color =
|
||||
@@ -161,7 +177,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexByte("Tile Size", &tile_size_, 0x01);
|
||||
gui::InputHexByte("Tile Size", &tile_size_);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
@@ -172,30 +188,38 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
"##GfxEditChild", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
// TODO: Update the interaction for multi select on sheets
|
||||
static ImGuiSelectionBasicStorage selection;
|
||||
ImGuiMultiSelectFlags flags =
|
||||
ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(
|
||||
flags, selection.Size, rom()->bitmap_manager().size());
|
||||
selection.ApplyRequests(ms_io);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(rom()->bitmap_manager().size());
|
||||
if (ms_io->RangeSrcItem != -1)
|
||||
clipper.IncludeItemByIndex(
|
||||
(int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
|
||||
|
||||
for (auto& [key, value] : rom()->bitmap_manager()) {
|
||||
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
|
||||
ImVec2(0x100 + 1, 0x40 + 1), true,
|
||||
ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::PopStyleVar();
|
||||
gui::Canvas graphics_bin_canvas_;
|
||||
// auto select_tile_event = [&]() {
|
||||
// };
|
||||
// graphics_bin_canvas_.UpdateEvent(
|
||||
// select_tile_event, ImVec2(0x100 + 1, 0x40 + 1), 0x20, sheet_scale_,
|
||||
// /*grid_size=*/16.0f);
|
||||
|
||||
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
|
||||
graphics_bin_canvas_.DrawContextMenu();
|
||||
if (value.get()->is_active()) {
|
||||
auto texture = value.get()->texture();
|
||||
if (value.is_active()) {
|
||||
auto texture = value.texture();
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(void*)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
value.get()->width() * sheet_scale_,
|
||||
value.width() * sheet_scale_,
|
||||
graphics_bin_canvas_.zero_point().y +
|
||||
value.get()->height() * sheet_scale_));
|
||||
value.height() * sheet_scale_));
|
||||
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
@@ -224,6 +248,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ms_io = ImGui::EndMultiSelect();
|
||||
selection.ApplyRequests(ms_io);
|
||||
ImGui::EndChild();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -232,8 +258,8 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
static int next_tab_id = 0;
|
||||
|
||||
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
|
||||
if (ImGui::TabItemButton(
|
||||
"+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) {
|
||||
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
|
||||
ImGuiTabItemFlags_NoTooltip)) {
|
||||
open_sheets_.insert(next_tab_id++);
|
||||
}
|
||||
|
||||
@@ -269,7 +295,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
*rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
|
||||
rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
|
||||
tile_size_, current_scale_);
|
||||
|
||||
ImGui::EndChild();
|
||||
@@ -302,7 +328,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_ = id;
|
||||
// ImVec2(0x100, 0x40),
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
*rom()->bitmap_manager()[id], current_color_,
|
||||
rom()->bitmap_manager()[id], current_color_,
|
||||
[&]() {
|
||||
|
||||
},
|
||||
@@ -322,12 +348,10 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
auto palette_group = *rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
|
||||
auto palette = palette_group.palette(edit_palette_index_);
|
||||
|
||||
if (rom()->is_loaded()) {
|
||||
auto palette_group = *rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
auto palette = palette_group.palette(edit_palette_index_);
|
||||
gui::TextWithSeparators("ROM Palette");
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
|
||||
@@ -335,21 +359,20 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHex("Palette Group Index", &edit_palette_index_);
|
||||
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
||||
palette);
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
RETURN_IF_ERROR(
|
||||
rom()->bitmap_manager()[current_sheet_].ApplyPaletteWithTransparent(
|
||||
palette, edit_palette_sub_index_));
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_),
|
||||
true);
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
||||
palette);
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
RETURN_IF_ERROR(
|
||||
rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent(
|
||||
palette, edit_palette_sub_index_));
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get(),
|
||||
true);
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -366,10 +389,12 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
||||
NEXT_COLUMN();
|
||||
link_canvas_.DrawBackground();
|
||||
link_canvas_.DrawGrid(16.0f);
|
||||
|
||||
int i = 0;
|
||||
for (auto [key, link_sheet] : rom()->link_graphics()) {
|
||||
for (auto [key, link_sheet] : *rom()->mutable_link_graphics()) {
|
||||
int x_offset = 0;
|
||||
int y_offset = core::kTilesheetHeight * i * 4;
|
||||
link_canvas_.DrawContextMenu(&link_sheet);
|
||||
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
|
||||
i++;
|
||||
}
|
||||
@@ -435,7 +460,7 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
if (super_donkey_) {
|
||||
if (refresh_graphics_) {
|
||||
for (int i = 0; i < graphics_bin_.size(); i++) {
|
||||
graphics_bin_[i].ApplyPalette(
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
rom()->UpdateBitmap(&graphics_bin_[i]);
|
||||
}
|
||||
@@ -498,19 +523,21 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
||||
is_open_ = true;
|
||||
cgx_loaded_ = true;
|
||||
});
|
||||
gui::ButtonPipe("Copy CGX Path",
|
||||
[this]() { ImGui::SetClipboardText(cgx_file_path_); });
|
||||
|
||||
gui::ButtonPipe("Load CGX Data", [this]() {
|
||||
if (ImGui::Button("Copy CGX Path")) {
|
||||
ImGui::SetClipboardText(cgx_file_path_);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Load CGX Data")) {
|
||||
status_ = gfx::scad_format::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
|
||||
decoded_cgx_, extra_cgx_data_);
|
||||
|
||||
cgx_bitmap_.InitializeFromData(0x80, 0x200, 8, decoded_cgx_);
|
||||
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
|
||||
if (col_file_) {
|
||||
cgx_bitmap_.ApplyPalette(decoded_col_);
|
||||
rom()->RenderBitmap(&cgx_bitmap_);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -532,7 +559,7 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
|
||||
InputInt("SCR Mod", &scr_mod_value_);
|
||||
|
||||
gui::ButtonPipe("Load Scr Data", [this]() {
|
||||
if (ImGui::Button("Load Scr Data")) {
|
||||
status_ =
|
||||
gfx::scad_format::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
|
||||
|
||||
@@ -540,12 +567,12 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
status_ = gfx::scad_format::DrawScrWithCgx(current_bpp_, scr_data_,
|
||||
decoded_scr_data_, decoded_cgx_);
|
||||
|
||||
scr_bitmap_.InitializeFromData(0x100, 0x100, 8, decoded_scr_data_);
|
||||
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
|
||||
if (scr_loaded_) {
|
||||
scr_bitmap_.ApplyPalette(decoded_col_);
|
||||
rom()->RenderBitmap(&scr_bitmap_);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -582,8 +609,9 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
|
||||
is_open_ = true;
|
||||
});
|
||||
|
||||
gui::ButtonPipe("Copy COL Path",
|
||||
[this]() { ImGui::SetClipboardText(col_file_path_); });
|
||||
if (ImGui::Button("Copy Col Path")) {
|
||||
ImGui::SetClipboardText(col_file_path_);
|
||||
}
|
||||
|
||||
if (rom()->is_loaded()) {
|
||||
gui::TextWithSeparators("ROM Palette");
|
||||
@@ -655,8 +683,9 @@ absl::Status GraphicsEditor::DrawFileImport() {
|
||||
is_open_ = true;
|
||||
});
|
||||
|
||||
gui::ButtonPipe("Copy File Path",
|
||||
[this]() { ImGui::SetClipboardText(file_path_); });
|
||||
if (Button("Copy File Path")) {
|
||||
ImGui::SetClipboardText(file_path_);
|
||||
}
|
||||
|
||||
gui::InputHex("BIN Offset", ¤t_offset_);
|
||||
gui::InputHex("BIN Size", &bin_size_);
|
||||
@@ -675,7 +704,7 @@ absl::Status GraphicsEditor::DrawFileImport() {
|
||||
|
||||
absl::Status GraphicsEditor::DrawClipboardImport() {
|
||||
gui::TextWithSeparators("Clipboard Import");
|
||||
gui::ButtonPipe("Paste from Clipboard", [this]() {
|
||||
if (Button("Paste From Clipboard")) {
|
||||
const char* text = ImGui::GetClipboardText();
|
||||
if (text) {
|
||||
const auto clipboard_data = Bytes(text, text + strlen(text));
|
||||
@@ -684,12 +713,12 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
|
||||
is_open_ = true;
|
||||
open_memory_editor_ = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
gui::InputHex("Offset", &clipboard_offset_);
|
||||
gui::InputHex("Size", &clipboard_size_);
|
||||
gui::InputHex("Num Sheets", &num_sheets_to_load_);
|
||||
|
||||
gui::ButtonPipe("Decompress Clipboard Data", [this]() {
|
||||
if (Button("Decompress Clipboard Data")) {
|
||||
if (temp_rom_.is_loaded()) {
|
||||
status_ = DecompressImportData(0x40000);
|
||||
} else {
|
||||
@@ -697,7 +726,7 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
|
||||
"Please paste data into the clipboard before "
|
||||
"decompressing.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -740,9 +769,9 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
auto palette_group = rom()->palette_group().overworld_animated;
|
||||
z3_rom_palette_ = palette_group[current_palette_];
|
||||
if (col_file_) {
|
||||
bin_bitmap_.ApplyPalette(col_file_palette_);
|
||||
status_ = bin_bitmap_.ApplyPalette(col_file_palette_);
|
||||
} else {
|
||||
bin_bitmap_.ApplyPalette(z3_rom_palette_);
|
||||
status_ = bin_bitmap_.ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,7 +794,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
|
||||
core::kTilesheetDepth, converted_sheet);
|
||||
if (col_file_) {
|
||||
graphics_bin_[i].ApplyPalette(
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
} else {
|
||||
// ROM palette
|
||||
@@ -773,7 +802,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
auto palette_group = rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
rom()->RenderBitmap(&graphics_bin_[i]);
|
||||
@@ -791,14 +820,14 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
|
||||
core::kTilesheetDepth, converted_sheet);
|
||||
if (col_file_) {
|
||||
graphics_bin_[i].ApplyPalette(
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
} else {
|
||||
// ROM palette
|
||||
auto palette_group = rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
rom()->RenderBitmap(&graphics_bin_[i]);
|
||||
@@ -812,4 +841,4 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
@@ -1,19 +1,20 @@
|
||||
#ifndef YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
|
||||
#include <ImGuiFileDialog/ImGuiFileDialog.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||
#include <imgui_memory_editor.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 "absl/status/statusor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/asset_browser.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -43,22 +44,6 @@ const std::string kSuperDonkeySprites[] = {
|
||||
"BE115", "BE5C2", "BEB63", "BF0CB", "BF607", "BFA55", "BFD71", "C017D",
|
||||
"C0567", "C0981", "C0BA7", "C116D", "C166A", "C1FE0", "C24CE", "C2B19"};
|
||||
|
||||
constexpr const char* kPaletteGroupAddressesKeys[] = {
|
||||
"ow_main", "ow_aux", "ow_animated", "hud",
|
||||
"global_sprites", "armors", "swords", "shields",
|
||||
"sprites_aux1", "sprites_aux2", "sprites_aux3", "dungeon_main",
|
||||
"grass", "3d_object", "ow_mini_map",
|
||||
};
|
||||
|
||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||
"#memoryEditor",
|
||||
"##separator_gfx1",
|
||||
};
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
/**
|
||||
* @class GraphicsEditor
|
||||
* @brief Allows the user to edit graphics sheets from the game or view
|
||||
@@ -73,9 +58,18 @@ constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
||||
* drawing toolsets, palette controls, clipboard imports, experimental features,
|
||||
* and memory editor.
|
||||
*/
|
||||
class GraphicsEditor : public SharedRom {
|
||||
class GraphicsEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
absl::Status Update();
|
||||
GraphicsEditor() { type_ = EditorType::kGraphics; }
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
private:
|
||||
enum class GfxEditMode {
|
||||
@@ -163,6 +157,8 @@ class GraphicsEditor : public SharedRom {
|
||||
char tilemap_file_path_[256] = "";
|
||||
char tilemap_file_name_[256] = "";
|
||||
|
||||
gui::GfxSheetAssetBrowser asset_browser_;
|
||||
|
||||
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
|
||||
|
||||
Rom temp_rom_;
|
||||
@@ -193,9 +189,10 @@ class GraphicsEditor : public SharedRom {
|
||||
gui::Canvas import_canvas_;
|
||||
gui::Canvas scr_canvas_;
|
||||
gui::Canvas super_donkey_canvas_;
|
||||
gui::Canvas current_sheet_canvas_{ImVec2(0x80, 0x20),
|
||||
gui::Canvas current_sheet_canvas_{"CurrentSheetCanvas", ImVec2(0x80, 0x20),
|
||||
gui::CanvasGridSize::k8x8};
|
||||
gui::Canvas link_canvas_{
|
||||
"LinkCanvas",
|
||||
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||
gui::CanvasGridSize::k16x16};
|
||||
absl::Status status_;
|
||||
@@ -205,4 +202,4 @@ class GraphicsEditor : public SharedRom {
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
#endif // YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
469
src/app/editor/graphics/palette_editor.cc
Normal file
469
src/app/editor/graphics/palette_editor.cc
Normal file
@@ -0,0 +1,469 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::AcceptDragDropPayload;
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginDragDropTarget;
|
||||
using ImGui::BeginGroup;
|
||||
using ImGui::BeginPopup;
|
||||
using ImGui::BeginPopupContextItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::ColorButton;
|
||||
using ImGui::ColorPicker4;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndDragDropTarget;
|
||||
using ImGui::EndGroup;
|
||||
using ImGui::EndPopup;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::GetContentRegionAvail;
|
||||
using ImGui::GetStyle;
|
||||
using ImGui::OpenPopup;
|
||||
using ImGui::PopID;
|
||||
using ImGui::PushID;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Separator;
|
||||
using ImGui::SetClipboardText;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetColumnIndex;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
using ImGui::TreeNode;
|
||||
using ImGui::TreePop;
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
constexpr ImGuiTableFlags kPaletteTableFlags =
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Hideable;
|
||||
|
||||
constexpr ImGuiColorEditFlags kPalNoAlpha = ImGuiColorEditFlags_NoAlpha;
|
||||
|
||||
constexpr ImGuiColorEditFlags kPalButtonFlags2 = ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip;
|
||||
|
||||
constexpr ImGuiColorEditFlags kColorPopupFlags =
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha;
|
||||
|
||||
namespace {
|
||||
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#ifdef IMGUI_USE_STB_SPRINTF
|
||||
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
|
||||
#else
|
||||
int w = vsnprintf(buf, buf_size, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
if (buf == nullptr) return w;
|
||||
if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1;
|
||||
buf[w] = 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline float color_saturate(float f) {
|
||||
return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
|
||||
}
|
||||
|
||||
#define F32_TO_INT8_SAT(_VAL) \
|
||||
((int)(color_saturate(_VAL) * 255.0f + \
|
||||
0.5f)) // Saturated, always output 0..255
|
||||
} // namespace
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
if (rom()->is_loaded()) {
|
||||
// Initialize the labels
|
||||
for (int i = 0; i < kNumPalettes; i++) {
|
||||
rom()->resource_label()->CreateOrGetLabel(
|
||||
"Palette Group Name", std::to_string(i),
|
||||
std::string(kPaletteGroupNames[i]));
|
||||
}
|
||||
} else {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
|
||||
if (BeginTable("paletteEditorTable", 2, kPaletteTableFlags, ImVec2(0, 0))) {
|
||||
TableSetupColumn("Palette Groups", ImGuiTableColumnFlags_WidthStretch,
|
||||
GetContentRegionAvail().x);
|
||||
TableSetupColumn("Palette Sets and Metadata",
|
||||
ImGuiTableColumnFlags_WidthStretch,
|
||||
GetContentRegionAvail().x);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
DrawModifiedColors();
|
||||
|
||||
DrawCustomPalette();
|
||||
Separator();
|
||||
gui::SnesColorEdit4("Current Color Picker", ¤t_color_,
|
||||
ImGuiColorEditFlags_NoAlpha);
|
||||
Separator();
|
||||
DisplayCategoryTable();
|
||||
|
||||
TableNextColumn();
|
||||
gfx_group_editor_.DrawPaletteViewer();
|
||||
Separator();
|
||||
static bool in_use = false;
|
||||
ImGui::Checkbox("Palette in use? ", &in_use);
|
||||
Separator();
|
||||
static std::string palette_notes = "Notes about the palette";
|
||||
ImGui::InputTextMultiline("Notes", palette_notes.data(), 1024,
|
||||
ImVec2(-1, ImGui::GetTextLineHeight() * 4),
|
||||
ImGuiInputTextFlags_AllowTabInput);
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
CLEAR_AND_RETURN_STATUS(status_)
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawCustomPalette() {
|
||||
if (BeginChild("ColorPalette", ImVec2(0, 40), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
for (int i = 0; i < custom_palette_.size(); i++) {
|
||||
PushID(i);
|
||||
SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
gui::SnesColorEdit4("##customPalette", &custom_palette_[i],
|
||||
ImGuiColorEditFlags_NoInputs);
|
||||
// Accept a drag drop target which adds a color to the custom_palette_
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) {
|
||||
ImVec4 color = ImVec4(0, 0, 0, 1.0f);
|
||||
memcpy((float*)&color, payload->Data, sizeof(float));
|
||||
custom_palette_.push_back(SnesColor(color));
|
||||
}
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
SameLine();
|
||||
if (ImGui::Button("Add Color")) {
|
||||
custom_palette_.push_back(SnesColor(0x7FFF));
|
||||
}
|
||||
SameLine();
|
||||
if (ImGui::Button("Export to Clipboard")) {
|
||||
std::string clipboard;
|
||||
for (const auto& color : custom_palette_) {
|
||||
clipboard += absl::StrFormat("$%04X,", color.snes());
|
||||
}
|
||||
SetClipboardText(clipboard.c_str());
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
|
||||
void PaletteEditor::DisplayCategoryTable() {
|
||||
if (BeginTable("Category Table", 8,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_SizingStretchSame |
|
||||
ImGuiTableFlags_Hideable,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Weapons and Gear");
|
||||
TableSetupColumn("Overworld and Area Colors");
|
||||
TableSetupColumn("Global Sprites");
|
||||
TableSetupColumn("Sprites Aux1");
|
||||
TableSetupColumn("Sprites Aux2");
|
||||
TableSetupColumn("Sprites Aux3");
|
||||
TableSetupColumn("Maps and Items");
|
||||
TableSetupColumn("Dungeons");
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
TableSetColumnIndex(0);
|
||||
if (TreeNode("Sword")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kSword);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Shield")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kShield);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Clothes")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kClothes, true);
|
||||
TreePop();
|
||||
}
|
||||
|
||||
TableSetColumnIndex(1);
|
||||
gui::BeginChildWithScrollbar("##WorldPaletteScrollRegion");
|
||||
if (TreeNode("World Colors")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kWorldColors);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Area Colors")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kAreaColors);
|
||||
TreePop();
|
||||
}
|
||||
EndChild();
|
||||
|
||||
TableSetColumnIndex(2);
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kGlobalSprites, true);
|
||||
|
||||
TableSetColumnIndex(3);
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux1);
|
||||
|
||||
TableSetColumnIndex(4);
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux2);
|
||||
|
||||
TableSetColumnIndex(5);
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux3);
|
||||
|
||||
TableSetColumnIndex(6);
|
||||
gui::BeginChildWithScrollbar("##MapPaletteScrollRegion");
|
||||
if (TreeNode("World Map")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kWorldMap, true);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Dungeon Map")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kDungeonMap);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Triforce")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kTriforce);
|
||||
TreePop();
|
||||
}
|
||||
if (TreeNode("Crystal")) {
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kCrystal);
|
||||
TreePop();
|
||||
}
|
||||
EndChild();
|
||||
|
||||
TableSetColumnIndex(7);
|
||||
gui::BeginChildWithScrollbar("##DungeonPaletteScrollRegion");
|
||||
status_ = DrawPaletteGroup(PaletteCategory::kDungeons, true);
|
||||
EndChild();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::DrawPaletteGroup(int category, bool right_side) {
|
||||
if (!rom()->is_loaded()) {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
|
||||
auto palette_group_name = kPaletteGroupNames[category];
|
||||
gfx::PaletteGroup* palette_group =
|
||||
rom()->mutable_palette_group()->get_group(palette_group_name.data());
|
||||
const auto size = palette_group->size();
|
||||
|
||||
static bool edit_color = false;
|
||||
for (int j = 0; j < size; j++) {
|
||||
gfx::SnesPalette* palette = palette_group->mutable_palette(j);
|
||||
auto pal_size = palette->size();
|
||||
|
||||
for (int n = 0; n < pal_size; n++) {
|
||||
PushID(n);
|
||||
if (!right_side) {
|
||||
if ((n % 7) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
} else {
|
||||
if ((n % 15) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
}
|
||||
|
||||
auto popup_id =
|
||||
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
|
||||
kPalNoAlpha)) {
|
||||
ASSIGN_OR_RETURN(current_color_, palette->GetColor(n));
|
||||
}
|
||||
|
||||
if (BeginPopupContextItem(popup_id.c_str())) {
|
||||
RETURN_IF_ERROR(HandleColorPopup(*palette, category, j, n))
|
||||
}
|
||||
PopID();
|
||||
}
|
||||
SameLine();
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, palette_group_name.data(), /*key=*/std::to_string(j),
|
||||
"Unnamed Palette");
|
||||
if (right_side) Separator();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawModifiedColors() {
|
||||
if (BeginChild("ModifiedColors", ImVec2(0, 100), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
for (int i = 0; i < history_.size(); i++) {
|
||||
PushID(i);
|
||||
gui::SnesColorEdit4("Original ", &history_.GetOriginalColor(i),
|
||||
ImGuiColorEditFlags_NoInputs);
|
||||
SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
gui::SnesColorEdit4("Modified ", &history_.GetModifiedColor(i),
|
||||
ImGuiColorEditFlags_NoInputs);
|
||||
PopID();
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
||||
int j, int n) {
|
||||
auto col = gfx::ToFloatArray(palette[n]);
|
||||
auto original_color = palette[n];
|
||||
if (gui::SnesColorEdit4("Edit Color", &palette[n], kColorPopupFlags)) {
|
||||
history_.RecordChange(/*group_name=*/std::string(kPaletteGroupNames[i]),
|
||||
/*palette_index=*/j, /*color_index=*/n,
|
||||
original_color, palette[n]);
|
||||
palette[n].set_modified(true);
|
||||
}
|
||||
|
||||
if (Button("Copy as..", ImVec2(-1, 0))) OpenPopup("Copy");
|
||||
if (BeginPopup("Copy")) {
|
||||
int cr = F32_TO_INT8_SAT(col[0]);
|
||||
int cg = F32_TO_INT8_SAT(col[1]);
|
||||
int cb = F32_TO_INT8_SAT(col[2]);
|
||||
char buf[64];
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0],
|
||||
col[1], col[2]);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
// SNES Format
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "$%04X",
|
||||
ConvertRGBtoSNES(ImVec4(col[0], col[1], col[2], 1.0f)));
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
EndPopup();
|
||||
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;
|
||||
if (loaded && !init) {
|
||||
status_ = InitializeSavedPalette(palette);
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
|
||||
SameLine(0, GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= Button("Palette");
|
||||
if (open_popup) {
|
||||
OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
|
||||
if (BeginPopup("mypicker")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
SameLine();
|
||||
|
||||
BeginGroup(); // Lock X position
|
||||
Text("Current ==>");
|
||||
SameLine();
|
||||
Text("Previous");
|
||||
|
||||
if (Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
SameLine();
|
||||
|
||||
if (ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
Separator();
|
||||
Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ColorButton("##palette", saved_palette_[n], kPalButtonFlags2,
|
||||
ImVec2(20, 20)))
|
||||
color = ImVec4(saved_palette_[n].x, saved_palette_[n].y,
|
||||
saved_palette_[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
EndGroup();
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
|
||||
int index) {
|
||||
if (index >= palette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
|
||||
// Get the current color
|
||||
ASSIGN_OR_RETURN(auto color, palette.GetColor(index));
|
||||
auto currentColor = color.rgb();
|
||||
if (ColorPicker4("Color Picker", (float*)&palette[index])) {
|
||||
// The color was modified, update it in the palette
|
||||
palette(index, currentColor);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::ResetColorToOriginal(
|
||||
gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette) {
|
||||
if (index >= palette.size() || index >= originalPalette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
ASSIGN_OR_RETURN(auto color, originalPalette.GetColor(index));
|
||||
auto originalColor = color.rgb();
|
||||
palette(index, originalColor);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
@@ -13,18 +15,6 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
constexpr int kNumPalettes = 11;
|
||||
|
||||
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"};
|
||||
|
||||
namespace palette_internal {
|
||||
struct PaletteChange {
|
||||
std::string group_name;
|
||||
@@ -56,9 +46,9 @@ class PaletteEditorHistory {
|
||||
}
|
||||
|
||||
// Restore the original color
|
||||
gfx::SnesColor GetOriginalColor(const std::string& groupName,
|
||||
size_t paletteIndex,
|
||||
size_t colorIndex) const {
|
||||
gfx::SnesColor RestoreOriginalColor(const std::string& groupName,
|
||||
size_t paletteIndex,
|
||||
size_t colorIndex) const {
|
||||
for (const auto& change : recentChanges) {
|
||||
if (change.group_name == groupName &&
|
||||
change.palette_index == paletteIndex &&
|
||||
@@ -71,6 +61,15 @@ class PaletteEditorHistory {
|
||||
return gfx::SnesColor();
|
||||
}
|
||||
|
||||
auto size() const { return recentChanges.size(); }
|
||||
|
||||
gfx::SnesColor& GetModifiedColor(size_t index) {
|
||||
return recentChanges[index].new_color;
|
||||
}
|
||||
gfx::SnesColor& GetOriginalColor(size_t index) {
|
||||
return recentChanges[index].original_color;
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<PaletteChange> recentChanges;
|
||||
static const size_t maxHistorySize = 50; // or any other number you deem fit
|
||||
@@ -81,21 +80,36 @@ class PaletteEditorHistory {
|
||||
* @class PaletteEditor
|
||||
* @brief Allows the user to view and edit in game palettes.
|
||||
*/
|
||||
class PaletteEditor : public SharedRom {
|
||||
class PaletteEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
absl::Status Update();
|
||||
absl::Status DrawPaletteGroups();
|
||||
PaletteEditor() {
|
||||
type_ = EditorType::kPalette;
|
||||
custom_palette_.push_back(gfx::SnesColor(0x7FFF));
|
||||
}
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Cut() override { return absl::OkStatus(); }
|
||||
absl::Status Copy() override { return absl::OkStatus(); }
|
||||
absl::Status Paste() override { return absl::OkStatus(); }
|
||||
absl::Status Undo() override { return absl::OkStatus(); }
|
||||
absl::Status Redo() override { return absl::OkStatus(); }
|
||||
absl::Status Find() override { return absl::OkStatus(); }
|
||||
|
||||
void DisplayCategoryTable();
|
||||
|
||||
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
|
||||
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette);
|
||||
void DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
||||
void DrawPortablePalette(gfx::SnesPalette& palette);
|
||||
absl::Status DrawPaletteGroup(int category);
|
||||
absl::Status DrawPaletteGroup(int category, bool right_side = false);
|
||||
|
||||
void DrawCustomPalette();
|
||||
|
||||
void DrawModifiedColors();
|
||||
|
||||
private:
|
||||
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
|
||||
|
||||
absl::Status InitializeSavedPalette(const gfx::SnesPalette& palette) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
|
||||
@@ -108,18 +122,15 @@ class PaletteEditor : public SharedRom {
|
||||
}
|
||||
|
||||
absl::Status status_;
|
||||
|
||||
palette_internal::PaletteEditorHistory history_;
|
||||
|
||||
ImVec4 saved_palette_[256] = {};
|
||||
gfx::SnesColor current_color_;
|
||||
|
||||
ImGuiColorEditFlags color_popup_flags =
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha;
|
||||
ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha;
|
||||
ImGuiColorEditFlags palette_button_flags_2 = ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip;
|
||||
GfxGroupEditor gfx_group_editor_;
|
||||
|
||||
std::vector<gfx::SnesColor> custom_palette_;
|
||||
|
||||
ImVec4 saved_palette_[256] = {};
|
||||
|
||||
palette_internal::PaletteEditorHistory history_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "app/editor/screen_editor.h"
|
||||
#include "screen_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
@@ -25,9 +25,7 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
ScreenEditor::ScreenEditor() { screen_canvas_.SetCanvasSize(ImVec2(512, 512)); }
|
||||
|
||||
void ScreenEditor::Update() {
|
||||
absl::Status ScreenEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
TAB_ITEM("Dungeon Maps")
|
||||
if (rom()->is_loaded()) {
|
||||
@@ -39,6 +37,8 @@ void ScreenEditor::Update() {
|
||||
DrawTitleScreenEditor();
|
||||
DrawNamingScreenEditor();
|
||||
END_TAB_BAR()
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
@@ -46,7 +46,7 @@ void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
|
||||
static bool create = false;
|
||||
if (!create && rom()->is_loaded()) {
|
||||
inventory_.Create();
|
||||
status_ = inventory_.Create();
|
||||
palette_ = inventory_.Palette();
|
||||
create = true;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
tilesheet_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
gui::DisplayPalette(palette_, create);
|
||||
status_ = gui::DisplayPalette(palette_, create);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
@@ -233,8 +233,8 @@ absl::Status ScreenEditor::LoadDungeonMapTile16() {
|
||||
tile16_sheet_.ComposeTile16(rom()->graphics_buffer(), t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
tile16_sheet_.mutable_bitmap()->ApplyPalette(
|
||||
*rom()->mutable_dungeon_palette(3));
|
||||
RETURN_IF_ERROR(tile16_sheet_.mutable_bitmap()->ApplyPalette(
|
||||
*rom()->mutable_dungeon_palette(3)));
|
||||
rom()->RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
|
||||
|
||||
for (int i = 0; i < tile16_sheet_.num_tiles(); ++i) {
|
||||
@@ -293,13 +293,7 @@ void ScreenEditor::DrawDungeonMapsTabs() {
|
||||
std::string label =
|
||||
dungeon_map_labels_[selected_dungeon][floor_number][j];
|
||||
screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
|
||||
// GFX.drawText(
|
||||
// e.Graphics, 16 + ((i % 5) * 32), 20 + ((i / 5) * 32),
|
||||
}
|
||||
|
||||
// if (dungmapSelectedTile == i)
|
||||
// Constants.AzurePen2,
|
||||
// 10 + ((i % 5) * 32), 12 + ((i / 5) * 32), 32, 32));
|
||||
}
|
||||
|
||||
screen_canvas_.DrawGrid(64.f, 5);
|
||||
@@ -369,6 +363,9 @@ void ScreenEditor::DrawDungeonMapsEditor() {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
selected_dungeon == i, "Dungeon Names", absl::StrFormat("%d", i),
|
||||
dungeon_names[i]);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
selected_dungeon = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Map column
|
||||
@@ -437,4 +434,4 @@ void ScreenEditor::DrawToolset() {
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
@@ -1,12 +1,13 @@
|
||||
#ifndef YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -36,10 +37,21 @@ namespace editor {
|
||||
*
|
||||
* The class inherits from the SharedRom class.
|
||||
*/
|
||||
class ScreenEditor : public SharedRom {
|
||||
class ScreenEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
ScreenEditor();
|
||||
void Update();
|
||||
ScreenEditor() {
|
||||
screen_canvas_.SetCanvasSize(ImVec2(512, 512));
|
||||
type_ = EditorType::kScreen;
|
||||
}
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
absl::Status SaveDungeonMaps();
|
||||
|
||||
@@ -83,10 +95,12 @@ class ScreenEditor : public SharedRom {
|
||||
gfx::BitmapTable sheets_;
|
||||
|
||||
gfx::Tilesheet tile16_sheet_;
|
||||
|
||||
absl::Status status_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "tile16_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
@@ -15,9 +16,7 @@
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/gui/widgets.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -26,50 +25,73 @@ namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginMenu;
|
||||
using ImGui::BeginMenuBar;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::Checkbox;
|
||||
using ImGui::Combo;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndMenu;
|
||||
using ImGui::EndMenuBar;
|
||||
using ImGui::EndTabBar;
|
||||
using ImGui::EndTabItem;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::GetContentRegionAvail;
|
||||
using ImGui::Separator;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
absl::Status Tile16Editor::InitBlockset(
|
||||
gfx::Bitmap* tile16_blockset_bmp, gfx::Bitmap current_gfx_bmp,
|
||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||
uint8_t all_tiles_types[0x200]) {
|
||||
all_tiles_types_ = all_tiles_types;
|
||||
tile16_blockset_bmp_ = tile16_blockset_bmp;
|
||||
tile16_individual_ = tile16_individual;
|
||||
current_gfx_bmp_ = current_gfx_bmp;
|
||||
tile8_gfx_data_ = current_gfx_bmp_.vector();
|
||||
RETURN_IF_ERROR(LoadTile8());
|
||||
ImVector<std::string> tile16_names;
|
||||
for (int i = 0; i < 0x200; ++i) {
|
||||
std::string str = core::UppercaseHexByte(all_tiles_types_[i]);
|
||||
tile16_names.push_back(str);
|
||||
}
|
||||
|
||||
*tile8_source_canvas_.mutable_labels(0) = tile16_names;
|
||||
*tile8_source_canvas_.custom_labels_enabled() = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::Update() {
|
||||
if (rom()->is_loaded() && !map_blockset_loaded_) {
|
||||
RETURN_IF_ERROR(LoadTile8());
|
||||
ImVector<std::string> tile16_names;
|
||||
for (int i = 0; i < 0x200; ++i) {
|
||||
std::string str = core::UppercaseHexByte(all_tiles_types_[i]);
|
||||
tile16_names.push_back(str);
|
||||
}
|
||||
|
||||
*tile8_source_canvas_.mutable_labels(0) = tile16_names;
|
||||
*tile8_source_canvas_.custom_labels_enabled() = true;
|
||||
if (!map_blockset_loaded_) {
|
||||
return absl::InvalidArgumentError("Blockset not initialized, open a ROM.");
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(DrawMenu());
|
||||
if (BeginTabBar("Tile16 Editor Tabs")) {
|
||||
RETURN_IF_ERROR(DrawTile16Editor());
|
||||
RETURN_IF_ERROR(UpdateTile16Transfer());
|
||||
ImGui::EndTabBar();
|
||||
EndTabBar();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::DrawMenu() {
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::Checkbox("Show Collision Types",
|
||||
tile8_source_canvas_.custom_labels_enabled());
|
||||
ImGui::EndMenu();
|
||||
if (BeginMenuBar()) {
|
||||
if (BeginMenu("View")) {
|
||||
Checkbox("Show Collision Types",
|
||||
tile8_source_canvas_.custom_labels_enabled());
|
||||
EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
EndMenuBar();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -80,9 +102,9 @@ absl::Status Tile16Editor::DrawTile16Editor() {
|
||||
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
GetContentRegionAvail().x);
|
||||
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
GetContentRegionAvail().x);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
@@ -92,10 +114,10 @@ absl::Status Tile16Editor::DrawTile16Editor() {
|
||||
RETURN_IF_ERROR(UpdateTile16Edit());
|
||||
RETURN_IF_ERROR(DrawTileEditControls());
|
||||
|
||||
ImGui::EndTable();
|
||||
EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
EndTabItem();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -108,26 +130,23 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
||||
{
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
blockset_canvas_.DrawTileSelector(32);
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
||||
blockset_canvas_.DrawBitmap(*tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
||||
blockset_canvas_.DrawGrid();
|
||||
blockset_canvas_.DrawOverlay();
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
}
|
||||
|
||||
if (!blockset_canvas_.points().empty()) {
|
||||
uint16_t x = blockset_canvas_.points().front().x / 32;
|
||||
uint16_t y = blockset_canvas_.points().front().y / 32;
|
||||
|
||||
// notify_tile16.mutable_get() = x + (y * 8);
|
||||
notify_tile16.mutable_get() = blockset_canvas_.GetTileIdFromMousePos();
|
||||
notify_tile16.apply_changes();
|
||||
|
||||
if (notify_tile16.modified()) {
|
||||
current_tile16_ = notify_tile16.get();
|
||||
current_tile16_bmp_ = tile16_individual_[notify_tile16];
|
||||
current_tile16_bmp_ = &tile16_individual_[notify_tile16];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(current_tile16_bmp_.ApplyPalette(
|
||||
RETURN_IF_ERROR(current_tile16_bmp_->ApplyPalette(
|
||||
ow_main_pal_group[current_palette_]));
|
||||
rom()->RenderBitmap(¤t_tile16_bmp_);
|
||||
rom()->RenderBitmap(current_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +177,7 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
int pixel_index =
|
||||
(start_position.y + y) * tile16_size + ((start_position.x) + x);
|
||||
int gfx_pixel_index = y * tile8_size + x;
|
||||
current_tile16_bmp_.WriteToPixel(
|
||||
current_tile16_bmp_->WriteToPixel(
|
||||
pixel_index,
|
||||
current_gfx_individual_[current_tile8_].data()[gfx_pixel_index]);
|
||||
}
|
||||
@@ -170,9 +189,8 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
|
||||
if (ImGui::BeginChild("Tile8 Selector",
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0x175),
|
||||
true)) {
|
||||
if (BeginChild("Tile8 Selector", ImVec2(GetContentRegionAvail().x, 0x175),
|
||||
true)) {
|
||||
tile8_source_canvas_.DrawBackground();
|
||||
tile8_source_canvas_.DrawContextMenu(¤t_gfx_bmp_);
|
||||
if (tile8_source_canvas_.DrawTileSelector(32)) {
|
||||
@@ -185,7 +203,7 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
tile8_source_canvas_.DrawGrid();
|
||||
tile8_source_canvas_.DrawOverlay();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
|
||||
// The user selected a tile8
|
||||
if (!tile8_source_canvas_.points().empty()) {
|
||||
@@ -199,31 +217,31 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
||||
}
|
||||
|
||||
if (ImGui::BeginChild("Tile16 Editor Options",
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0x50), true)) {
|
||||
if (BeginChild("Tile16 Editor Options",
|
||||
ImVec2(GetContentRegionAvail().x, 0x50), true)) {
|
||||
tile16_edit_canvas_.DrawBackground();
|
||||
tile16_edit_canvas_.DrawContextMenu(¤t_tile16_bmp_);
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
|
||||
tile16_edit_canvas_.DrawContextMenu(current_tile16_bmp_);
|
||||
tile16_edit_canvas_.DrawBitmap(*current_tile16_bmp_, 0, 0, 4.0f);
|
||||
if (!tile8_source_canvas_.points().empty()) {
|
||||
if (tile16_edit_canvas_.DrawTilePainter(
|
||||
current_gfx_individual_[current_tile8_], 16, 2.0f)) {
|
||||
RETURN_IF_ERROR(
|
||||
DrawToCurrentTile16(tile16_edit_canvas_.drawn_tile_position()));
|
||||
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
||||
rom()->UpdateBitmap(current_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
tile16_edit_canvas_.DrawGrid();
|
||||
tile16_edit_canvas_.DrawOverlay();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
EndChild();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::DrawTileEditControls() {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Tile16 ID: %d", current_tile16_);
|
||||
ImGui::Text("Tile8 ID: %d", current_tile8_);
|
||||
ImGui::Text("Options:");
|
||||
Separator();
|
||||
Text("Tile16 ID: %d", current_tile16_);
|
||||
Text("Tile8 ID: %d", current_tile8_);
|
||||
Text("Options:");
|
||||
gui::InputHexByte("Palette", ¬ify_palette.mutable_get());
|
||||
notify_palette.apply_changes();
|
||||
if (notify_palette.modified()) {
|
||||
@@ -241,15 +259,15 @@ absl::Status Tile16Editor::DrawTileEditControls() {
|
||||
RETURN_IF_ERROR(
|
||||
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||
current_tile16_bmp_->ApplyPaletteWithTransparent(palette, value));
|
||||
rom()->UpdateBitmap(¤t_gfx_bmp_);
|
||||
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
||||
rom()->UpdateBitmap(current_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Checkbox("X Flip", &x_flip);
|
||||
ImGui::Checkbox("Y Flip", &y_flip);
|
||||
ImGui::Checkbox("Priority Tile", &priority_tile);
|
||||
Checkbox("X Flip", &x_flip);
|
||||
Checkbox("Y Flip", &y_flip);
|
||||
Checkbox("Priority Tile", &priority_tile);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -308,9 +326,9 @@ absl::Status Tile16Editor::UpdateTile16Transfer() {
|
||||
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
||||
ImGui::GetContentRegionAvail().x / 2);
|
||||
GetContentRegionAvail().x / 2);
|
||||
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
||||
ImGui::GetContentRegionAvail().x / 2);
|
||||
GetContentRegionAvail().x / 2);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
@@ -320,17 +338,17 @@ absl::Status Tile16Editor::UpdateTile16Transfer() {
|
||||
TableNextColumn();
|
||||
RETURN_IF_ERROR(UpdateTransferTileCanvas());
|
||||
|
||||
ImGui::EndTable();
|
||||
EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
EndTabItem();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||
// Create a button for loading another ROM
|
||||
if (ImGui::Button("Load ROM")) {
|
||||
if (Button("Load ROM")) {
|
||||
ImGuiFileDialog::Instance()->OpenDialog(
|
||||
"ChooseTransferFileDlgKey", "Open Transfer ROM", ".sfc,.smc", ".");
|
||||
}
|
||||
@@ -353,9 +371,9 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||
palette_ = transfer_overworld_.AreaPalette();
|
||||
|
||||
// Create the tile16 blockset image
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x80,
|
||||
transfer_overworld_.Tile16Blockset(),
|
||||
transfer_blockset_bmp_, palette_));
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
0x80, 0x2000, 0x80, transfer_overworld_.Tile16Blockset(),
|
||||
transfer_blockset_bmp_, palette_));
|
||||
transfer_blockset_loaded_ = true;
|
||||
}
|
||||
|
||||
@@ -367,6 +385,16 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::SetCurrentTile(int id) {
|
||||
current_tile16_ = id;
|
||||
current_tile16_bmp_ = &tile16_individual_[id];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_->ApplyPalette(ow_main_pal_group[current_palette_]));
|
||||
rom()->RenderBitmap(current_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
@@ -1,22 +1,21 @@
|
||||
#ifndef YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
#define YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/context/gfx_context.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gfx/tilesheet.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -29,6 +28,11 @@ namespace editor {
|
||||
*/
|
||||
class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
public:
|
||||
absl::Status InitBlockset(gfx::Bitmap* tile16_blockset_bmp,
|
||||
gfx::Bitmap current_gfx_bmp,
|
||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||
uint8_t all_tiles_types[0x200]);
|
||||
|
||||
absl::Status Update();
|
||||
absl::Status DrawMenu();
|
||||
|
||||
@@ -44,28 +48,9 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
|
||||
absl::Status UpdateTransferTileCanvas();
|
||||
|
||||
void InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
|
||||
gfx::Bitmap current_gfx_bmp,
|
||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||
uint8_t all_tiles_types[0x200]) {
|
||||
all_tiles_types_ = all_tiles_types;
|
||||
tile16_blockset_bmp_ = tile16_blockset_bmp;
|
||||
tile16_individual_ = tile16_individual;
|
||||
current_gfx_bmp_ = current_gfx_bmp;
|
||||
tile8_gfx_data_ = current_gfx_bmp_.vector();
|
||||
}
|
||||
|
||||
absl::Status LoadTile8();
|
||||
|
||||
absl::Status set_tile16(int id) {
|
||||
current_tile16_ = id;
|
||||
current_tile16_bmp_ = tile16_individual_[id];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_.ApplyPalette(ow_main_pal_group[current_palette_]));
|
||||
rom()->RenderBitmap(¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
absl::Status SetCurrentTile(int id);
|
||||
|
||||
private:
|
||||
bool map_blockset_loaded_ = false;
|
||||
@@ -79,13 +64,6 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
core::NotifyValue<uint32_t> notify_tile16;
|
||||
core::NotifyValue<uint8_t> notify_palette;
|
||||
|
||||
// Canvas dimensions
|
||||
int canvas_width;
|
||||
int canvas_height;
|
||||
|
||||
// Texture ID for the canvas
|
||||
int texture_id;
|
||||
|
||||
// Various options for the Tile16 Editor
|
||||
bool x_flip;
|
||||
bool y_flip;
|
||||
@@ -95,26 +73,24 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
uint8_t* all_tiles_types_;
|
||||
|
||||
// Tile16 blockset for selecting the tile to edit
|
||||
gui::Canvas blockset_canvas_{ImVec2(0x100, 0x4000),
|
||||
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100, 0x4000),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
gfx::Bitmap* tile16_blockset_bmp_;
|
||||
|
||||
// Canvas for editing the selected tile
|
||||
gui::Canvas tile16_edit_canvas_{ImVec2(0x40, 0x40),
|
||||
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(0x40, 0x40),
|
||||
gui::CanvasGridSize::k64x64};
|
||||
gfx::Bitmap current_tile16_bmp_;
|
||||
gfx::Bitmap current_tile8_bmp_;
|
||||
gfx::Bitmap* current_tile16_bmp_;
|
||||
|
||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||
gui::Canvas tile8_source_canvas_{
|
||||
"Tile8SourceCanvas",
|
||||
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gfx::Bitmap current_gfx_bmp_;
|
||||
std::vector<gfx::Tilesheet> current_tilesheets_;
|
||||
|
||||
gui::Canvas transfer_canvas_;
|
||||
gfx::Bitmap transfer_blockset_bmp_;
|
||||
gfx::Bitmap transfer_current_bmp_;
|
||||
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,36 @@
|
||||
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS 1
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#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 "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/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/editor/context/gfx_context.h"
|
||||
#include "app/editor/dungeon_editor.h"
|
||||
#include "app/editor/graphics_editor.h"
|
||||
#include "app/editor/modules/assembly_editor.h"
|
||||
#include "app/editor/modules/music_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/code/memory_editor.h"
|
||||
#include "app/editor/dungeon/dungeon_editor.h"
|
||||
#include "app/editor/graphics/graphics_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/graphics/screen_editor.h"
|
||||
#include "app/editor/message/message_editor.h"
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/screen_editor.h"
|
||||
#include "app/editor/sprite_editor.h"
|
||||
#include "app/editor/settings_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -56,14 +59,29 @@ class MasterEditor : public SharedRom,
|
||||
public context::GfxContext,
|
||||
public core::ExperimentFlags {
|
||||
public:
|
||||
MasterEditor() { current_editor_ = &overworld_editor_; }
|
||||
MasterEditor() {
|
||||
current_editor_ = &overworld_editor_;
|
||||
active_editors_.push_back(&overworld_editor_);
|
||||
active_editors_.push_back(&dungeon_editor_);
|
||||
active_editors_.push_back(&graphics_editor_);
|
||||
active_editors_.push_back(&palette_editor_);
|
||||
active_editors_.push_back(&sprite_editor_);
|
||||
active_editors_.push_back(&message_editor_);
|
||||
}
|
||||
|
||||
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer,
|
||||
std::string filename = "");
|
||||
absl::Status Update();
|
||||
|
||||
void Shutdown() { overworld_editor_.Shutdown(); }
|
||||
auto emulator() -> emu::Emulator& { return emulator_; }
|
||||
auto quit() { return quit_; }
|
||||
auto overworld_editor() -> OverworldEditor& { return overworld_editor_; }
|
||||
|
||||
private:
|
||||
void ManageActiveEditors();
|
||||
void ManageKeyboardShortcuts();
|
||||
void OpenRomOrProject(const std::string& filename);
|
||||
|
||||
void DrawFileDialog();
|
||||
void DrawStatusPopup();
|
||||
void DrawAboutPopup();
|
||||
@@ -73,10 +91,16 @@ class MasterEditor : public SharedRom,
|
||||
void DrawFileMenu();
|
||||
void DrawEditMenu();
|
||||
void DrawViewMenu();
|
||||
void DrawTestMenu();
|
||||
void DrawProjectMenu();
|
||||
void DrawHelpMenu();
|
||||
|
||||
void LoadRom();
|
||||
void SaveRom();
|
||||
|
||||
absl::Status OpenProject();
|
||||
|
||||
bool quit_ = false;
|
||||
bool about_ = false;
|
||||
bool rom_info_ = false;
|
||||
bool backup_rom_ = false;
|
||||
@@ -91,6 +115,8 @@ class MasterEditor : public SharedRom,
|
||||
|
||||
emu::Emulator emulator_;
|
||||
|
||||
Project current_project_;
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
DungeonEditor dungeon_editor_;
|
||||
GraphicsEditor graphics_editor_;
|
||||
@@ -99,12 +125,17 @@ class MasterEditor : public SharedRom,
|
||||
PaletteEditor palette_editor_;
|
||||
ScreenEditor screen_editor_;
|
||||
SpriteEditor sprite_editor_;
|
||||
SettingsEditor settings_editor_;
|
||||
MessageEditor message_editor_;
|
||||
MemoryEditorWithDiffChecker memory_editor_;
|
||||
|
||||
Editor *current_editor_ = nullptr;
|
||||
ImVector<int> active_tabs_;
|
||||
std::vector<Editor*> active_editors_;
|
||||
Editor* current_editor_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
|
||||
10
src/app/editor/master_editor_test.cc
Normal file
10
src/app/editor/master_editor_test.cc
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "master_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
177
src/app/editor/message/message_data.h
Normal file
177
src/app/editor/message/message_data.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#ifndef YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
#define YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
const uint8_t MESSAGETERMINATOR = 0x7F;
|
||||
|
||||
static std::string AddNewLinesToCommands(std::string str);
|
||||
static std::string ReplaceAllDictionaryWords(std::string str);
|
||||
static std::vector<uint8_t> ParseMessageToData(std::string str);
|
||||
|
||||
const std::string CHEESE = "\uBEBE"; // Inserted into commands to protect
|
||||
// them from dictionary replacements.
|
||||
|
||||
struct MessageData {
|
||||
int ID;
|
||||
int Address;
|
||||
std::string RawString;
|
||||
std::string ContentsParsed;
|
||||
std::vector<uint8_t> Data;
|
||||
std::vector<uint8_t> DataParsed;
|
||||
|
||||
MessageData() = default;
|
||||
MessageData(int id, int address, const std::string& rawString,
|
||||
const std::vector<uint8_t>& rawData,
|
||||
const std::string& parsedString,
|
||||
const std::vector<uint8_t>& parsedData)
|
||||
: ID(id),
|
||||
Address(address),
|
||||
RawString(rawString),
|
||||
Data(rawData),
|
||||
DataParsed(parsedData),
|
||||
ContentsParsed(parsedString) {}
|
||||
|
||||
// Copy constructor
|
||||
MessageData(const MessageData& other) {
|
||||
ID = other.ID;
|
||||
Address = other.Address;
|
||||
RawString = other.RawString;
|
||||
Data = other.Data;
|
||||
DataParsed = other.DataParsed;
|
||||
ContentsParsed = other.ContentsParsed;
|
||||
}
|
||||
|
||||
void SetMessage(std::string messageString) {
|
||||
ContentsParsed = messageString;
|
||||
RawString = OptimizeMessageForDictionary(messageString);
|
||||
RecalculateData();
|
||||
}
|
||||
|
||||
std::string ToString() {
|
||||
return absl::StrFormat("%0X - %s", ID, ContentsParsed);
|
||||
}
|
||||
|
||||
std::string GetReadableDumpedContents() {
|
||||
std::stringstream stringBuilder;
|
||||
for (const auto& b : Data) {
|
||||
stringBuilder << absl::StrFormat("%0X ", b);
|
||||
}
|
||||
stringBuilder << absl::StrFormat("%00X", MESSAGETERMINATOR);
|
||||
|
||||
return absl::StrFormat(
|
||||
"[[[[\r\nMessage "
|
||||
"%000X]]]]\r\n[Contents]\r\n%s\r\n\r\n[Data]\r\n%s"
|
||||
"\r\n\r\n\r\n\r\n",
|
||||
ID, AddNewLinesToCommands(ContentsParsed), stringBuilder.str());
|
||||
}
|
||||
|
||||
std::string GetDumpedContents() {
|
||||
return absl::StrFormat("%000X : %s\r\n\r\n", ID, ContentsParsed);
|
||||
}
|
||||
|
||||
std::string OptimizeMessageForDictionary(std::string messageString) {
|
||||
std::stringstream protons;
|
||||
bool command = false;
|
||||
for (const auto& c : messageString) {
|
||||
if (c == '[') {
|
||||
command = true;
|
||||
} else if (c == ']') {
|
||||
command = false;
|
||||
}
|
||||
|
||||
protons << c;
|
||||
if (command) {
|
||||
protons << CHEESE;
|
||||
}
|
||||
}
|
||||
|
||||
std::string protonsString = protons.str();
|
||||
std::string replacedString = ReplaceAllDictionaryWords(protonsString);
|
||||
std::string finalString =
|
||||
absl::StrReplaceAll(replacedString, {{CHEESE, ""}});
|
||||
|
||||
return finalString;
|
||||
}
|
||||
|
||||
void RecalculateData() {
|
||||
Data = ParseMessageToData(RawString);
|
||||
DataParsed = ParseMessageToData(ContentsParsed);
|
||||
}
|
||||
};
|
||||
|
||||
struct TextElement {
|
||||
uint8_t ID;
|
||||
std::string Token;
|
||||
std::string GenericToken;
|
||||
std::string Pattern;
|
||||
std::string StrictPattern;
|
||||
std::string Description;
|
||||
bool HasArgument;
|
||||
|
||||
TextElement() = default;
|
||||
TextElement(uint8_t id, std::string token, bool arg,
|
||||
std::string description) {
|
||||
ID = id;
|
||||
Token = token;
|
||||
if (arg) {
|
||||
GenericToken = absl::StrFormat("[%s:##]", Token);
|
||||
} else {
|
||||
GenericToken = absl::StrFormat("[%s]", Token);
|
||||
}
|
||||
HasArgument = arg;
|
||||
Description = description;
|
||||
Pattern =
|
||||
arg ? "\\[" + Token + ":?([0-9A-F]{1,2})\\]" : "\\[" + Token + "\\]";
|
||||
Pattern = absl::StrReplaceAll(Pattern, {{"[", "\\["}, {"]", "\\]"}});
|
||||
StrictPattern = absl::StrCat("^", Pattern, "$");
|
||||
StrictPattern = "^" + Pattern + "$";
|
||||
}
|
||||
|
||||
std::string GetParameterizedToken(uint8_t value = 0) {
|
||||
if (HasArgument) {
|
||||
return absl::StrFormat("[%s:%02X]", Token, value);
|
||||
} else {
|
||||
return absl::StrFormat("[%s]", Token);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ToString() {
|
||||
return absl::StrFormat("%s %s", GenericToken, Description);
|
||||
}
|
||||
|
||||
std::smatch MatchMe(std::string dfrag) const {
|
||||
std::regex pattern(StrictPattern);
|
||||
std::smatch match;
|
||||
std::regex_match(dfrag, match, pattern);
|
||||
return match;
|
||||
}
|
||||
|
||||
bool Empty() { return ID == 0; }
|
||||
};
|
||||
|
||||
struct ParsedElement {
|
||||
TextElement Parent;
|
||||
uint8_t Value;
|
||||
bool Active = false;
|
||||
|
||||
ParsedElement() = default;
|
||||
ParsedElement(TextElement textElement, uint8_t value) {
|
||||
Parent = textElement;
|
||||
Value = value;
|
||||
Active = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
770
src/app/editor/message/message_editor.cc
Normal file
770
src/app/editor/message/message_editor.cc
Normal file
@@ -0,0 +1,770 @@
|
||||
#include "message_editor.h"
|
||||
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::Begin;
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::End;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::InputText;
|
||||
using ImGui::InputTextMultiline;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Separator;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
using ImGui::TextWrapped;
|
||||
using ImGui::TreeNode;
|
||||
|
||||
static ParsedElement FindMatchingElement(string str) {
|
||||
std::smatch match;
|
||||
for (auto& textElement : TextCommands) {
|
||||
match = textElement.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
if (textElement.HasArgument) {
|
||||
return ParsedElement(textElement,
|
||||
std::stoi(match[1].str(), nullptr, 16));
|
||||
} else {
|
||||
return ParsedElement(textElement, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match = DictionaryElement.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
return ParsedElement(DictionaryElement,
|
||||
DICTOFF + std::stoi(match[1].str(), nullptr, 16));
|
||||
}
|
||||
return ParsedElement();
|
||||
}
|
||||
|
||||
static string ReplaceAllDictionaryWords(string str) {
|
||||
string temp = str;
|
||||
for (const auto& entry : AllDictionaries) {
|
||||
if (absl::StrContains(temp, entry.Contents)) {
|
||||
temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}});
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> ParseMessageToData(string str) {
|
||||
std::vector<uint8_t> bytes;
|
||||
string tempString = str;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < tempString.size()) {
|
||||
// Get next text fragment.
|
||||
if (tempString[pos] == '[') {
|
||||
int next = tempString.find(']', pos);
|
||||
if (next == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ParsedElement parsedElement =
|
||||
FindMatchingElement(tempString.substr(pos, next - pos + 1));
|
||||
if (!parsedElement.Active) {
|
||||
break; // TODO: handle badness.
|
||||
// } else if (parsedElement.Parent == DictionaryElement) {
|
||||
// bytes.push_back(parsedElement.Value);
|
||||
} else {
|
||||
bytes.push_back(parsedElement.Parent.ID);
|
||||
|
||||
if (parsedElement.Parent.HasArgument) {
|
||||
bytes.push_back(parsedElement.Value);
|
||||
}
|
||||
}
|
||||
|
||||
pos = next + 1;
|
||||
continue;
|
||||
} else {
|
||||
uint8_t bb = MessageEditor::FindMatchingCharacter(tempString[pos++]);
|
||||
|
||||
if (bb != 0xFF) {
|
||||
// TODO: handle badness.
|
||||
bytes.push_back(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Update() {
|
||||
if (rom()->is_loaded() && !data_loaded_) {
|
||||
RETURN_IF_ERROR(Initialize());
|
||||
CurrentMessage = ListOfTexts[1];
|
||||
data_loaded_ = true;
|
||||
}
|
||||
|
||||
if (BeginTable("##MessageEditor", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("List");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Commands");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextColumn();
|
||||
DrawMessageList();
|
||||
|
||||
TableNextColumn();
|
||||
DrawCurrentMessage();
|
||||
|
||||
TableNextColumn();
|
||||
DrawTextCommands();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawMessageList() {
|
||||
if (InputText("Search", &search_text_)) {
|
||||
DisplayedMessages.clear();
|
||||
for (const auto& message : ListOfTexts) {
|
||||
if (absl::StrContains(message.ContentsParsed, search_text_)) {
|
||||
DisplayedMessages.push_back(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BeginChild("##MessagesList", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (BeginTable("##MessagesTable", 3,
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("ID");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Data");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
TableNextColumn();
|
||||
if (Button(core::UppercaseHexWord(message.ID).c_str())) {
|
||||
CurrentMessage = message;
|
||||
DrawMessagePreview();
|
||||
}
|
||||
TableNextColumn();
|
||||
TextWrapped("%s", ParsedMessages[message.ID].c_str());
|
||||
TableNextColumn();
|
||||
TextWrapped(
|
||||
"%s",
|
||||
core::UppercaseHexLong(ListOfTexts[message.ID].Address).c_str());
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawCurrentMessage() {
|
||||
Button(absl::StrCat("Message ", CurrentMessage.ID).c_str());
|
||||
if (InputTextMultiline("##MessageEditor", &ParsedMessages[CurrentMessage.ID],
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
CurrentMessage.Data = ParseMessageToData(message_text_box_.text);
|
||||
DrawMessagePreview();
|
||||
}
|
||||
Separator();
|
||||
|
||||
Text("Font Graphics");
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("MessageEditorCanvas", ImVec2(0, 130));
|
||||
font_gfx_canvas_.DrawBackground();
|
||||
font_gfx_canvas_.DrawContextMenu();
|
||||
font_gfx_canvas_.DrawBitmap(font_gfx_bitmap_, 0, 0);
|
||||
font_gfx_canvas_.DrawGrid();
|
||||
font_gfx_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
gui::EndPadding();
|
||||
Separator();
|
||||
|
||||
Text("Message Preview");
|
||||
if (Button("Refresh Bitmap")) {
|
||||
rom()->UpdateBitmap(¤t_font_gfx16_bitmap_);
|
||||
}
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("CurrentGfxFont", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
current_font_gfx16_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
current_font_gfx16_canvas_.DrawContextMenu();
|
||||
current_font_gfx16_canvas_.DrawBitmap(current_font_gfx16_bitmap_, 0, 0);
|
||||
current_font_gfx16_canvas_.DrawGrid();
|
||||
current_font_gfx16_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawTextCommands() {
|
||||
if (BeginChild("##TextCommands", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
for (const auto& text_element : TextCommands) {
|
||||
if (Button(text_element.GenericToken.c_str())) {
|
||||
}
|
||||
SameLine();
|
||||
TextWrapped("%s", text_element.Description.c_str());
|
||||
Separator();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Initialize() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
width_array[i] = rom()->data()[kCharactersWidth + i];
|
||||
}
|
||||
|
||||
BuildDictionaryEntries();
|
||||
ReadAllTextData();
|
||||
|
||||
font_preview_colors_.AddColor(0x7FFF); // White
|
||||
font_preview_colors_.AddColor(0x7C00); // Red
|
||||
font_preview_colors_.AddColor(0x03E0); // Green
|
||||
font_preview_colors_.AddColor(0x001F); // Blue
|
||||
|
||||
std::vector<uint8_t> data(0x4000, 0);
|
||||
for (int i = 0; i < 0x4000; i++) {
|
||||
data[i] = rom()->data()[kGfxFont + i];
|
||||
}
|
||||
font_gfx16_data = gfx::SnesTo8bppSheet(data, /*bpp=*/2);
|
||||
|
||||
// 4bpp
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
128, 128, 8, font_gfx16_data, font_gfx_bitmap_, font_preview_colors_))
|
||||
|
||||
current_font_gfx16_data_.reserve(172 * 4096);
|
||||
for (int i = 0; i < 172 * 4096; i++) {
|
||||
current_font_gfx16_data_.push_back(0);
|
||||
}
|
||||
|
||||
// 8bpp
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
172, 4096, 64, current_font_gfx16_data_, current_font_gfx16_bitmap_,
|
||||
font_preview_colors_))
|
||||
|
||||
gfx::SnesPalette color_palette = font_gfx_bitmap_.palette();
|
||||
for (int i = 0; i < font_preview_colors_.size(); i++) {
|
||||
*color_palette.mutable_color(i) = font_preview_colors_[i];
|
||||
}
|
||||
|
||||
*font_gfx_bitmap_.mutable_palette() = color_palette;
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
DisplayedMessages.push_back(message);
|
||||
}
|
||||
|
||||
for (const auto& each_message : ListOfTexts) {
|
||||
// Each string has a [:XX] char encoded
|
||||
// The corresponding character is found in CharEncoder unordered_map
|
||||
std::string parsed_message = "";
|
||||
for (const auto& byte : each_message.Data) {
|
||||
// Find the char byte in the CharEncoder map
|
||||
if (CharEncoder.contains(byte)) {
|
||||
parsed_message.push_back(CharEncoder.at(byte));
|
||||
} else {
|
||||
// If the byte is not found in the CharEncoder map, it is a command
|
||||
// or a dictionary entry
|
||||
if (byte >= DICTOFF && byte < (DICTOFF + 97)) {
|
||||
// Dictionary entry
|
||||
auto dictionaryEntry = GetDictionaryFromID(byte - DICTOFF);
|
||||
parsed_message.append(dictionaryEntry.Contents);
|
||||
} else {
|
||||
// Command
|
||||
TextElement textElement = FindMatchingCommand(byte);
|
||||
if (!textElement.Empty()) {
|
||||
// If the element is line 2, 3 or V we add a newline
|
||||
if (textElement.ID == kScrollVertical || textElement.ID == kLine2 ||
|
||||
textElement.ID == kLine3)
|
||||
parsed_message.append("\n");
|
||||
|
||||
parsed_message.append(textElement.GenericToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ParsedMessages.push_back(parsed_message);
|
||||
}
|
||||
|
||||
DrawMessagePreview();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MessageEditor::BuildDictionaryEntries() {
|
||||
for (int i = 0; i < 97; i++) {
|
||||
std::vector<uint8_t> bytes;
|
||||
std::stringstream stringBuilder;
|
||||
|
||||
int address = core::SnesToPc(
|
||||
0x0E0000 + (rom()->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
|
||||
rom()->data()[kPointersDictionaries + (i * 2)]);
|
||||
|
||||
int temppush_backress = core::SnesToPc(
|
||||
0x0E0000 +
|
||||
(rom()->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
|
||||
rom()->data()[kPointersDictionaries + ((i + 1) * 2)]);
|
||||
|
||||
while (address < temppush_backress) {
|
||||
uint8_t uint8_tDictionary = rom()->data()[address++];
|
||||
bytes.push_back(uint8_tDictionary);
|
||||
stringBuilder << ParseTextDataByte(uint8_tDictionary);
|
||||
}
|
||||
|
||||
// AllDictionaries[i] = DictionaryEntry{(uint8_t)i, stringBuilder.str()};
|
||||
AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
|
||||
}
|
||||
|
||||
// AllDictionaries.OrderByDescending(dictionary = > dictionary.Length);
|
||||
AllDictionaries[0].Length = 0;
|
||||
}
|
||||
|
||||
void MessageEditor::ReadAllTextData() {
|
||||
int messageID = 0;
|
||||
uint8_t current_byte;
|
||||
int pos = kTextData;
|
||||
std::vector<uint8_t> temp_bytes_raw;
|
||||
std::vector<uint8_t> temp_bytes_parsed;
|
||||
|
||||
std::string current_message_raw;
|
||||
std::string current_message_parsed;
|
||||
TextElement text_element;
|
||||
|
||||
while (true) {
|
||||
current_byte = rom()->data()[pos++];
|
||||
|
||||
if (current_byte == MESSAGETERMINATOR) {
|
||||
auto message =
|
||||
MessageData(messageID++, pos, current_message_raw, temp_bytes_raw,
|
||||
current_message_parsed, temp_bytes_parsed);
|
||||
|
||||
ListOfTexts.push_back(message);
|
||||
|
||||
temp_bytes_raw.clear();
|
||||
temp_bytes_parsed.clear();
|
||||
current_message_raw.clear();
|
||||
current_message_parsed.clear();
|
||||
|
||||
continue;
|
||||
} else if (current_byte == 0xFF) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp_bytes_raw.push_back(current_byte);
|
||||
|
||||
// Check for command.
|
||||
text_element = FindMatchingCommand(current_byte);
|
||||
|
||||
if (!text_element.Empty()) {
|
||||
temp_bytes_parsed.push_back(current_byte);
|
||||
if (text_element.HasArgument) {
|
||||
current_byte = rom()->data()[pos++];
|
||||
temp_bytes_raw.push_back(current_byte);
|
||||
temp_bytes_parsed.push_back(current_byte);
|
||||
}
|
||||
|
||||
current_message_raw.append(
|
||||
text_element.GetParameterizedToken(current_byte));
|
||||
current_message_parsed.append(
|
||||
text_element.GetParameterizedToken(current_byte));
|
||||
|
||||
if (text_element.Token == BANKToken) {
|
||||
pos = kTextData2;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for special characters.
|
||||
text_element = FindMatchingSpecial(current_byte);
|
||||
|
||||
if (!text_element.Empty()) {
|
||||
current_message_raw.append(text_element.GetParameterizedToken());
|
||||
current_message_parsed.append(text_element.GetParameterizedToken());
|
||||
temp_bytes_parsed.push_back(current_byte);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for dictionary.
|
||||
int dictionary = FindDictionaryEntry(current_byte);
|
||||
|
||||
if (dictionary >= 0) {
|
||||
current_message_raw.append("[");
|
||||
current_message_raw.append(DICTIONARYTOKEN);
|
||||
current_message_raw.append(":");
|
||||
current_message_raw.append(core::UppercaseHexWord(dictionary));
|
||||
current_message_raw.append("]");
|
||||
|
||||
uint32_t address = core::Get24LocalFromPC(
|
||||
rom()->data(), kPointersDictionaries + (dictionary * 2));
|
||||
uint32_t address_end = core::Get24LocalFromPC(
|
||||
rom()->data(), kPointersDictionaries + ((dictionary + 1) * 2));
|
||||
|
||||
for (uint32_t i = address; i < address_end; i++) {
|
||||
temp_bytes_parsed.push_back(rom()->data()[i]);
|
||||
current_message_parsed.append(ParseTextDataByte(rom()->data()[i]));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything else.
|
||||
if (CharEncoder.contains(current_byte)) {
|
||||
std::string str = "";
|
||||
str.push_back(CharEncoder.at(current_byte));
|
||||
current_message_raw.append(str);
|
||||
current_message_parsed.append(str);
|
||||
temp_bytes_parsed.push_back(current_byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextElement MessageEditor::FindMatchingCommand(uint8_t b) {
|
||||
TextElement empty_element;
|
||||
for (const auto text_element : TextCommands) {
|
||||
if (text_element.ID == b) {
|
||||
return text_element;
|
||||
}
|
||||
}
|
||||
return empty_element;
|
||||
}
|
||||
|
||||
TextElement MessageEditor::FindMatchingSpecial(uint8_t value) {
|
||||
TextElement empty_element;
|
||||
for (const auto text_element : SpecialChars) {
|
||||
if (text_element.ID == value) {
|
||||
return text_element;
|
||||
}
|
||||
}
|
||||
return empty_element;
|
||||
}
|
||||
|
||||
MessageEditor::DictionaryEntry MessageEditor::GetDictionaryFromID(
|
||||
uint8_t value) {
|
||||
if (value < 0 || value >= AllDictionaries.size()) {
|
||||
return DictionaryEntry();
|
||||
}
|
||||
return AllDictionaries[value];
|
||||
}
|
||||
|
||||
uint8_t MessageEditor::FindDictionaryEntry(uint8_t value) {
|
||||
if (value < DICTOFF || value == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value - DICTOFF;
|
||||
}
|
||||
|
||||
uint8_t MessageEditor::FindMatchingCharacter(char value) {
|
||||
for (const auto [key, char_value] : CharEncoder) {
|
||||
if (value == char_value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
string MessageEditor::ParseTextDataByte(uint8_t value) {
|
||||
if (CharEncoder.contains(value)) {
|
||||
char c = CharEncoder.at(value);
|
||||
string str = "";
|
||||
str.push_back(c);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Check for command.
|
||||
TextElement textElement = FindMatchingCommand(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for special characters.
|
||||
textElement = FindMatchingSpecial(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for dictionary.
|
||||
int dictionary = FindDictionaryEntry(value);
|
||||
if (dictionary >= 0) {
|
||||
return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
int sizex, int sizey) {
|
||||
int drawid = srcx + (srcy * 32);
|
||||
for (int yl = 0; yl < sizey * 8; yl++) {
|
||||
for (int xl = 0; xl < 4; xl++) {
|
||||
int mx = xl;
|
||||
int my = yl;
|
||||
|
||||
// Formula information to get tile index position in the array.
|
||||
// ((ID / nbrofXtiles) * (imgwidth/2) + (ID - ((ID/16)*16) ))
|
||||
int tx = ((drawid / 16) * 512) + ((drawid - ((drawid / 16) * 16)) * 4);
|
||||
uint8_t pixel = font_gfx16_data[tx + (yl * 64) + xl];
|
||||
|
||||
// nx,ny = object position, xx,yy = tile position, xl,yl = pixel
|
||||
// position
|
||||
int index = x + (y * 172) + (mx * 2) + (my * 172);
|
||||
if ((pixel & 0x0F) != 0) {
|
||||
current_font_gfx16_data_[index + 1] =
|
||||
(uint8_t)((pixel & 0x0F) + (0 * 4));
|
||||
}
|
||||
|
||||
if (((pixel >> 4) & 0x0F) != 0) {
|
||||
current_font_gfx16_data_[index + 0] =
|
||||
(uint8_t)(((pixel >> 4) & 0x0F) + (0 * 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawStringToPreview(string str) {
|
||||
for (const auto c : str) {
|
||||
DrawCharacterToPreview(c);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawCharacterToPreview(char c) {
|
||||
DrawCharacterToPreview(FindMatchingCharacter(c));
|
||||
}
|
||||
|
||||
void MessageEditor::DrawCharacterToPreview(const std::vector<uint8_t>& text) {
|
||||
for (const uint8_t& value : text) {
|
||||
if (skip_next) {
|
||||
skip_next = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value < 100) {
|
||||
int srcy = value / 16;
|
||||
int srcx = value - (value & (~0xF));
|
||||
|
||||
if (text_pos >= 170) {
|
||||
text_pos = 0;
|
||||
text_line++;
|
||||
}
|
||||
|
||||
DrawTileToPreview(text_pos, text_line * 16, srcx, srcy, 0, 1, 2);
|
||||
text_pos += width_array[value];
|
||||
} else if (value == kLine1) {
|
||||
text_pos = 0;
|
||||
text_line = 0;
|
||||
} else if (value == kScrollVertical) {
|
||||
text_pos = 0;
|
||||
text_line += 1;
|
||||
} else if (value == kLine2) {
|
||||
text_pos = 0;
|
||||
text_line = 1;
|
||||
} else if (value == kLine3) {
|
||||
text_pos = 0;
|
||||
text_line = 2;
|
||||
} else if (value == 0x6B || value == 0x6D || value == 0x6E ||
|
||||
value == 0x77 || value == 0x78 || value == 0x79 ||
|
||||
value == 0x7A) {
|
||||
skip_next = true;
|
||||
|
||||
continue;
|
||||
} else if (value == 0x6C) // BCD numbers.
|
||||
{
|
||||
DrawCharacterToPreview('0');
|
||||
skip_next = true;
|
||||
|
||||
continue;
|
||||
} else if (value == 0x6A) {
|
||||
// Includes parentheses to be longer, since player names can be up to 6
|
||||
// characters.
|
||||
DrawStringToPreview("(NAME)");
|
||||
} else if (value >= DICTOFF && value < (DICTOFF + 97)) {
|
||||
auto dictionaryEntry = GetDictionaryFromID(value - DICTOFF);
|
||||
DrawCharacterToPreview(dictionaryEntry.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawMessagePreview() // From Parsing.
|
||||
{
|
||||
text_line = 0;
|
||||
for (int i = 0; i < (172 * 4096); i++) {
|
||||
current_font_gfx16_data_[i] = 0;
|
||||
}
|
||||
text_pos = 0;
|
||||
DrawCharacterToPreview(CurrentMessage.Data);
|
||||
shown_lines = 0;
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Cut() {
|
||||
// Ensure that text is currently selected in the text box.
|
||||
if (!message_text_box_.text.empty()) {
|
||||
// Cut the selected text in the control and paste it into the Clipboard.
|
||||
message_text_box_.Cut();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Paste() {
|
||||
// Determine if there is any text in the Clipboard to paste into the
|
||||
if (ImGui::GetClipboardText() != nullptr) {
|
||||
// Paste the text from the Clipboard into the text box.
|
||||
message_text_box_.Paste();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Copy() {
|
||||
// Ensure that text is selected in the text box.
|
||||
if (message_text_box_.selection_length > 0) {
|
||||
// Copy the selected text to the Clipboard.
|
||||
message_text_box_.Copy();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Undo() {
|
||||
// Determine if last operation can be undone in text box.
|
||||
if (message_text_box_.can_undo) {
|
||||
// Undo the last operation.
|
||||
message_text_box_.Undo();
|
||||
|
||||
// clear the undo buffer to prevent last action from being redone.
|
||||
message_text_box_.clearUndo();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Save() {
|
||||
std::vector<uint8_t> backup = rom()->vector();
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
RETURN_IF_ERROR(rom()->Write(kCharactersWidth + i, width_array[i]));
|
||||
}
|
||||
|
||||
int pos = kTextData;
|
||||
bool in_second_bank = false;
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
for (const auto value : message.Data) {
|
||||
RETURN_IF_ERROR(rom()->Write(pos, value));
|
||||
|
||||
if (value == kBlockTerminator) {
|
||||
// Make sure we didn't go over the space available in the first block.
|
||||
// 0x7FFF available.
|
||||
if ((!in_second_bank & pos) > kTextDataEnd) {
|
||||
return absl::InternalError(DisplayTextOverflowError(pos, true));
|
||||
}
|
||||
|
||||
// Switch to the second block.
|
||||
pos = kTextData2 - 1;
|
||||
in_second_bank = true;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(
|
||||
rom()->Write(pos++, MESSAGETERMINATOR)); // , true, "Terminator text"
|
||||
}
|
||||
|
||||
// Verify that we didn't go over the space available for the second block.
|
||||
// 0x14BF available.
|
||||
if ((in_second_bank & pos) > kTextData2End) {
|
||||
// rom()->data() = backup;
|
||||
return absl::InternalError(DisplayTextOverflowError(pos, false));
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(rom()->Write(pos, 0xFF)); // , true, "End of text"
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::string MessageEditor::DisplayTextOverflowError(int pos, bool bank) {
|
||||
int space = bank ? kTextDataEnd - kTextData : kTextData2End - kTextData2;
|
||||
string bankSTR = bank ? "1st" : "2nd";
|
||||
string posSTR = bank ? absl::StrFormat("%X4", pos & 0xFFFF)
|
||||
: absl::StrFormat("%X4", (pos - kTextData2) & 0xFFFF);
|
||||
std::string message = absl::StrFormat(
|
||||
"There is too much text data in the %s block to save.\n"
|
||||
"Available: %X4 | Used: %s",
|
||||
bankSTR, space, posSTR);
|
||||
return message;
|
||||
}
|
||||
|
||||
// push_backs a command to the text field when the push_back command button is
|
||||
// pressed or the command is double clicked in the list.
|
||||
void MessageEditor::InsertCommandButton_Click_1() {
|
||||
// InsertSelectedText(
|
||||
// TextCommands[TextCommandList.SelectedIndex].GetParameterizedToken(
|
||||
// (uint8_t)ParamsBox.HexValue));
|
||||
}
|
||||
|
||||
// push_backs a special character to the text field when the push_back command
|
||||
// button is pressed or the character is double clicked in the list.
|
||||
void MessageEditor::InsertSpecialButton_Click() {
|
||||
// InsertSelectedText(
|
||||
// SpecialChars[SpecialsList.SelectedIndex].GetParameterizedToken());
|
||||
}
|
||||
|
||||
void MessageEditor::InsertSelectedText(string str) {
|
||||
int textboxPos = message_text_box_.selection_start;
|
||||
from_form = true;
|
||||
// message_text_box_.Text = message_text_box_.Text.Insert(textboxPos, str);
|
||||
from_form = false;
|
||||
message_text_box_.selection_start = textboxPos + str.size();
|
||||
message_text_box_.Focus();
|
||||
}
|
||||
|
||||
void MessageEditor::Delete() {
|
||||
// Determine if any text is selected in the TextBox control.
|
||||
if (message_text_box_.selection_length == 0) {
|
||||
// clear all of the text in the textbox.
|
||||
message_text_box_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::SelectAll() {
|
||||
// Determine if any text is selected in the TextBox control.
|
||||
if (message_text_box_.selection_length == 0) {
|
||||
// Select all text in the text box.
|
||||
message_text_box_.SelectAll();
|
||||
|
||||
// Move the cursor to the text box.
|
||||
message_text_box_.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
366
src/app/editor/message/message_editor.h
Normal file
366
src/app/editor/message/message_editor.h
Normal file
@@ -0,0 +1,366 @@
|
||||
#ifndef YAZE_APP_EDITOR_MESSAGE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MESSAGE_EDITOR_H
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "app/editor/message/message_data.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using std::string;
|
||||
|
||||
// TEXT EDITOR RELATED CONSTANTS
|
||||
const int kGfxFont = 0x70000; // 2bpp format
|
||||
const int kTextData = 0xE0000;
|
||||
const int kTextDataEnd = 0xE7FFF;
|
||||
const int kTextData2 = 0x75F40;
|
||||
const int kTextData2End = 0x773FF;
|
||||
const int kPointersDictionaries = 0x74703;
|
||||
const int kCharactersWidth = 0x74ADF;
|
||||
|
||||
const string DICTIONARYTOKEN = "D";
|
||||
const uint8_t DICTOFF = 0x88;
|
||||
const string BANKToken = "BANK";
|
||||
const uint8_t BANKID = 0x80;
|
||||
|
||||
constexpr uint8_t kBlockTerminator = 0x80;
|
||||
|
||||
static std::vector<uint8_t> ParseMessageToData(string str);
|
||||
|
||||
static ParsedElement FindMatchingElement(string str);
|
||||
|
||||
constexpr uint8_t kScrollVertical = 0x73;
|
||||
constexpr uint8_t kLine1 = 0x74;
|
||||
constexpr uint8_t kLine2 = 0x75;
|
||||
constexpr uint8_t kLine3 = 0x76;
|
||||
|
||||
static const TextElement TextCommands[] = {
|
||||
TextElement(0x6B, "W", true, "Window border"),
|
||||
TextElement(0x6D, "P", true, "Window position"),
|
||||
TextElement(0x6E, "SPD", true, "Scroll speed"),
|
||||
TextElement(0x7A, "S", true, "Text draw speed"),
|
||||
TextElement(0x77, "C", true, "Text color"),
|
||||
TextElement(0x6A, "L", false, "Player name"),
|
||||
TextElement(0x74, "1", false, "Line 1"),
|
||||
TextElement(0x75, "2", false, "Line 2"),
|
||||
TextElement(0x76, "3", false, "Line 3"),
|
||||
TextElement(0x7E, "K", false, "Wait for key"),
|
||||
TextElement(0x73, "V", false, "Scroll text"),
|
||||
TextElement(0x78, "WT", true, "Delay X"),
|
||||
TextElement(0x6C, "N", true, "BCD number"),
|
||||
TextElement(0x79, "SFX", true, "Sound effect"),
|
||||
TextElement(0x71, "CH3", false, "Choose 3"),
|
||||
TextElement(0x72, "CH2", false, "Choose 2 high"),
|
||||
TextElement(0x6F, "CH2L", false, "Choose 2 low"),
|
||||
TextElement(0x68, "CH2I", false, "Choose 2 indented"),
|
||||
TextElement(0x69, "CHI", false, "Choose item"),
|
||||
TextElement(0x67, "IMG", false, "Next attract image"),
|
||||
TextElement(0x80, BANKToken, false, "Bank marker (automatic)"),
|
||||
TextElement(0x70, "NONO", false, "Crash"),
|
||||
};
|
||||
|
||||
static std::vector<TextElement> SpecialChars = {
|
||||
TextElement(0x43, "...", false, "Ellipsis …"),
|
||||
TextElement(0x4D, "UP", false, "Arrow ↑"),
|
||||
TextElement(0x4E, "DOWN", false, "Arrow ↓"),
|
||||
TextElement(0x4F, "LEFT", false, "Arrow ←"),
|
||||
TextElement(0x50, "RIGHT", false, "Arrow →"),
|
||||
TextElement(0x5B, "A", false, "Button Ⓐ"),
|
||||
TextElement(0x5C, "B", false, "Button Ⓑ"),
|
||||
TextElement(0x5D, "X", false, "Button ⓧ"),
|
||||
TextElement(0x5E, "Y", false, "Button ⓨ"),
|
||||
TextElement(0x52, "HP1L", false, "1 HP left"),
|
||||
TextElement(0x53, "HP1R", false, "1 HP right"),
|
||||
TextElement(0x54, "HP2L", false, "2 HP left"),
|
||||
TextElement(0x55, "HP3L", false, "3 HP left"),
|
||||
TextElement(0x56, "HP3R", false, "3 HP right"),
|
||||
TextElement(0x57, "HP4L", false, "4 HP left"),
|
||||
TextElement(0x58, "HP4R", false, "4 HP right"),
|
||||
TextElement(0x47, "HY0", false, "Hieroglyph ☥"),
|
||||
TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"),
|
||||
TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"),
|
||||
TextElement(0x4A, "LFL", false, "Link face left"),
|
||||
TextElement(0x4B, "LFR", false, "Link face right"),
|
||||
};
|
||||
|
||||
static const std::unordered_map<uint8_t, wchar_t> CharEncoder = {
|
||||
{0x00, 'A'},
|
||||
{0x01, 'B'},
|
||||
{0x02, 'C'},
|
||||
{0x03, 'D'},
|
||||
{0x04, 'E'},
|
||||
{0x05, 'F'},
|
||||
{0x06, 'G'},
|
||||
{0x07, 'H'},
|
||||
{0x08, 'I'},
|
||||
{0x09, 'J'},
|
||||
{0x0A, 'K'},
|
||||
{0x0B, 'L'},
|
||||
{0x0C, 'M'},
|
||||
{0x0D, 'N'},
|
||||
{0x0E, 'O'},
|
||||
{0x0F, 'P'},
|
||||
{0x10, 'Q'},
|
||||
{0x11, 'R'},
|
||||
{0x12, 'S'},
|
||||
{0x13, 'T'},
|
||||
{0x14, 'U'},
|
||||
{0x15, 'V'},
|
||||
{0x16, 'W'},
|
||||
{0x17, 'X'},
|
||||
{0x18, 'Y'},
|
||||
{0x19, 'Z'},
|
||||
{0x1A, 'a'},
|
||||
{0x1B, 'b'},
|
||||
{0x1C, 'c'},
|
||||
{0x1D, 'd'},
|
||||
{0x1E, 'e'},
|
||||
{0x1F, 'f'},
|
||||
{0x20, 'g'},
|
||||
{0x21, 'h'},
|
||||
{0x22, 'i'},
|
||||
{0x23, 'j'},
|
||||
{0x24, 'k'},
|
||||
{0x25, 'l'},
|
||||
{0x26, 'm'},
|
||||
{0x27, 'n'},
|
||||
{0x28, 'o'},
|
||||
{0x29, 'p'},
|
||||
{0x2A, 'q'},
|
||||
{0x2B, 'r'},
|
||||
{0x2C, 's'},
|
||||
{0x2D, 't'},
|
||||
{0x2E, 'u'},
|
||||
{0x2F, 'v'},
|
||||
{0x30, 'w'},
|
||||
{0x31, 'x'},
|
||||
{0x32, 'y'},
|
||||
{0x33, 'z'},
|
||||
{0x34, '0'},
|
||||
{0x35, '1'},
|
||||
{0x36, '2'},
|
||||
{0x37, '3'},
|
||||
{0x38, '4'},
|
||||
{0x39, '5'},
|
||||
{0x3A, '6'},
|
||||
{0x3B, '7'},
|
||||
{0x3C, '8'},
|
||||
{0x3D, '9'},
|
||||
{0x3E, '!'},
|
||||
{0x3F, '?'},
|
||||
{0x40, '-'},
|
||||
{0x41, '.'},
|
||||
{0x42, ','},
|
||||
{0x44, '>'},
|
||||
{0x45, '('},
|
||||
{0x46, ')'},
|
||||
{0x4C, '"'},
|
||||
{0x51, '\''},
|
||||
{0x59, ' '},
|
||||
{0x5A, '<'},
|
||||
// {0x5F, '¡'}, {0x60, '¡'}, {0x61, '¡'}, {0x62, ' '}, {0x63, ' '}, {0x64,
|
||||
// ' '},
|
||||
{0x65, ' '},
|
||||
{0x66, '_'},
|
||||
};
|
||||
|
||||
static TextElement DictionaryElement =
|
||||
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
|
||||
|
||||
class MessageEditor : public Editor, public SharedRom {
|
||||
public:
|
||||
struct DictionaryEntry {
|
||||
uint8_t ID;
|
||||
std::string Contents;
|
||||
std::vector<uint8_t> Data;
|
||||
int Length;
|
||||
std::string Token;
|
||||
|
||||
DictionaryEntry() = default;
|
||||
DictionaryEntry(uint8_t i, std::string s)
|
||||
: Contents(s), ID(i), Length(s.length()) {
|
||||
Token = absl::StrFormat("[%s:%00X]", DICTIONARYTOKEN, ID);
|
||||
Data = ParseMessageToData(Contents);
|
||||
}
|
||||
|
||||
bool ContainedInString(std::string s) {
|
||||
return s.find(Contents) != std::string::npos;
|
||||
}
|
||||
|
||||
std::string ReplaceInstancesOfIn(std::string s) {
|
||||
std::string replacedString = s;
|
||||
size_t pos = replacedString.find(Contents);
|
||||
while (pos != std::string::npos) {
|
||||
replacedString.replace(pos, Contents.length(), Token);
|
||||
pos = replacedString.find(Contents, pos + Token.length());
|
||||
}
|
||||
return replacedString;
|
||||
}
|
||||
};
|
||||
|
||||
MessageEditor() { type_ = EditorType::kMessage; }
|
||||
|
||||
absl::Status Update() override;
|
||||
void DrawMessageList();
|
||||
void DrawCurrentMessage();
|
||||
void DrawTextCommands();
|
||||
|
||||
absl::Status Initialize();
|
||||
void ReadAllTextData();
|
||||
void BuildDictionaryEntries();
|
||||
|
||||
absl::Status Cut() override;
|
||||
absl::Status Copy() override;
|
||||
absl::Status Paste() override;
|
||||
absl::Status Undo() override;
|
||||
absl::Status Redo() override {
|
||||
return absl::UnimplementedError("Redo not implemented");
|
||||
}
|
||||
absl::Status Find() override {
|
||||
return absl::UnimplementedError("Find not implemented");
|
||||
}
|
||||
absl::Status Save();
|
||||
void Delete();
|
||||
void SelectAll();
|
||||
// void RegisterTests(ImGuiTestEngine* e) override;
|
||||
|
||||
TextElement FindMatchingCommand(uint8_t byte);
|
||||
TextElement FindMatchingSpecial(uint8_t value);
|
||||
string ParseTextDataByte(uint8_t value);
|
||||
DictionaryEntry GetDictionaryFromID(uint8_t value);
|
||||
|
||||
static uint8_t FindDictionaryEntry(uint8_t value);
|
||||
static uint8_t FindMatchingCharacter(char value);
|
||||
void DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
int sizex = 1, int sizey = 1);
|
||||
void DrawCharacterToPreview(char c);
|
||||
void DrawCharacterToPreview(const std::vector<uint8_t>& text);
|
||||
|
||||
void DrawStringToPreview(string str);
|
||||
void DrawMessagePreview();
|
||||
std::string DisplayTextOverflowError(int pos, bool bank);
|
||||
|
||||
void InsertCommandButton_Click_1();
|
||||
void InsertSpecialButton_Click();
|
||||
void InsertSelectedText(string str);
|
||||
|
||||
static const std::vector<DictionaryEntry> AllDicts;
|
||||
|
||||
uint8_t width_array[100];
|
||||
string romname = "";
|
||||
|
||||
int text_line = 0;
|
||||
int text_pos = 0;
|
||||
int shown_lines = 0;
|
||||
int selected_tile = 0;
|
||||
|
||||
bool skip_next = false;
|
||||
bool from_form = false;
|
||||
|
||||
std::vector<MessageData> ListOfTexts;
|
||||
std::vector<MessageData> DisplayedMessages;
|
||||
std::vector<std::string> ParsedMessages;
|
||||
|
||||
MessageData CurrentMessage;
|
||||
|
||||
private:
|
||||
static const TextElement DictionaryElement;
|
||||
|
||||
bool data_loaded_ = false;
|
||||
int current_message_id_ = 0;
|
||||
|
||||
std::string search_text_ = "";
|
||||
|
||||
gui::Canvas font_gfx_canvas_{"##FontGfxCanvas", ImVec2(128, 128)};
|
||||
gui::Canvas current_font_gfx16_canvas_{"##CurrentMessageGfx",
|
||||
ImVec2(172, 4096)};
|
||||
|
||||
gfx::Bitmap font_gfx_bitmap_;
|
||||
gfx::Bitmap current_font_gfx16_bitmap_;
|
||||
|
||||
Bytes font_gfx16_data;
|
||||
Bytes current_font_gfx16_data_;
|
||||
|
||||
gfx::SnesPalette font_preview_colors_;
|
||||
|
||||
struct TextBox {
|
||||
std::string text;
|
||||
std::string buffer;
|
||||
int cursor_pos = 0;
|
||||
int selection_start = 0;
|
||||
int selection_end = 0;
|
||||
int selection_length = 0;
|
||||
bool has_selection = false;
|
||||
bool has_focus = false;
|
||||
bool changed = false;
|
||||
bool can_undo = false;
|
||||
|
||||
void Undo() {
|
||||
text = buffer;
|
||||
cursor_pos = selection_start;
|
||||
has_selection = false;
|
||||
}
|
||||
void clearUndo() { can_undo = false; }
|
||||
void Copy() { ImGui::SetClipboardText(text.c_str()); }
|
||||
void Cut() {
|
||||
Copy();
|
||||
text.erase(selection_start, selection_end - selection_start);
|
||||
cursor_pos = selection_start;
|
||||
has_selection = false;
|
||||
changed = true;
|
||||
}
|
||||
void Paste() {
|
||||
text.erase(selection_start, selection_end - selection_start);
|
||||
text.insert(selection_start, ImGui::GetClipboardText());
|
||||
std::string str = ImGui::GetClipboardText();
|
||||
cursor_pos = selection_start + str.size();
|
||||
has_selection = false;
|
||||
changed = true;
|
||||
}
|
||||
void clear() {
|
||||
text.clear();
|
||||
buffer.clear();
|
||||
cursor_pos = 0;
|
||||
selection_start = 0;
|
||||
selection_end = 0;
|
||||
selection_length = 0;
|
||||
has_selection = false;
|
||||
has_focus = false;
|
||||
changed = false;
|
||||
can_undo = false;
|
||||
}
|
||||
void SelectAll() {
|
||||
selection_start = 0;
|
||||
selection_end = text.size();
|
||||
selection_length = text.size();
|
||||
has_selection = true;
|
||||
}
|
||||
void Focus() { has_focus = true; }
|
||||
};
|
||||
|
||||
TextBox message_text_box_;
|
||||
};
|
||||
|
||||
static std::vector<MessageEditor::DictionaryEntry> AllDictionaries;
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MESSAGE_EDITOR_H
|
||||
7
src/app/editor/message/message_editor_test.cc
Normal file
7
src/app/editor/message/message_editor_test.cc
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "message_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,152 +0,0 @@
|
||||
#include "assembly_editor.h"
|
||||
|
||||
#include "app/gui/widgets.h"
|
||||
#include "core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
AssemblyEditor::AssemblyEditor() {
|
||||
text_editor_.SetLanguageDefinition(gui::GetAssemblyLanguageDef());
|
||||
text_editor_.SetPalette(TextEditor::GetDarkPalette());
|
||||
}
|
||||
|
||||
void AssemblyEditor::Update(bool& is_loaded) {
|
||||
ImGui::Begin("Assembly Editor", &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_view& filename) {
|
||||
current_file_ = filename;
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawFileView() {
|
||||
ImGui::BeginTable("##table_view", 4,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg);
|
||||
|
||||
// Table headers
|
||||
ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
||||
ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_WidthFixed, 60.0f);
|
||||
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Table data
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
// TODO: Add tree view of files
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
// TODO: Add line number
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
// TODO: Add address per line
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
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::EndTable();
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,45 +0,0 @@
|
||||
#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
|
||||
* @brief Text editor for modifying assembly code.
|
||||
*/
|
||||
class AssemblyEditor {
|
||||
public:
|
||||
AssemblyEditor();
|
||||
|
||||
void Update(bool &is_loaded);
|
||||
void InlineUpdate();
|
||||
void ChangeActiveFile(const std::string_view &);
|
||||
|
||||
private:
|
||||
void DrawFileMenu();
|
||||
void DrawEditMenu();
|
||||
|
||||
void DrawFileView();
|
||||
|
||||
void SetEditorText();
|
||||
|
||||
bool file_is_loaded_ = false;
|
||||
|
||||
std::string current_file_;
|
||||
TextEditor text_editor_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
@@ -1,246 +0,0 @@
|
||||
#include "gfx_group_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/gui/widgets.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::SameLine;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
|
||||
absl::Status GfxGroupEditor::Update() {
|
||||
if (ImGui::BeginTabBar("GfxGroupEditor")) {
|
||||
if (ImGui::BeginTabItem("Main")) {
|
||||
gui::InputHexByte("Selected Blockset", &selected_blockset_);
|
||||
|
||||
DrawBlocksetViewer();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Rooms")) {
|
||||
gui::InputHexByte("Selected Blockset", &selected_roomset_);
|
||||
|
||||
DrawRoomsetViewer();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Sprites")) {
|
||||
gui::InputHexByte("Selected Spriteset", &selected_spriteset_);
|
||||
|
||||
ImGui::Text("Values");
|
||||
DrawSpritesetViewer();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Palettes")) {
|
||||
DrawPaletteViewer();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Palette: ");
|
||||
ImGui::InputInt("##PreviewPaletteID", &preview_palette_id_);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
|
||||
if (ImGui::BeginTable("##BlocksetTable", sheet_only ? 1 : 2,
|
||||
ImGuiTableFlags_Borders, ImVec2(0, 0))) {
|
||||
if (!sheet_only) {
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
}
|
||||
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
if (!sheet_only) {
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->main_blockset_ids[selected_blockset_][i]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
|
||||
auto &sheet = *rom()->bitmap_manager()[sheet_id];
|
||||
// if (sheet_id != last_sheet_id_) {
|
||||
// last_sheet_id_ = sheet_id;
|
||||
// auto palette_group = rom()->palette_group("ow_main");
|
||||
// auto palette = palette_group[preview_palette_id_];
|
||||
// sheet.ApplyPalette(palette);
|
||||
// rom()->UpdateBitmap(&sheet);
|
||||
// }
|
||||
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 22);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawRoomsetViewer() {
|
||||
ImGui::Text("Values - Overwrites 4 of main blockset");
|
||||
if (ImGui::BeginTable("##Roomstable", 2, ImGuiTableFlags_Borders,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->room_blockset_ids[selected_roomset_][i]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
|
||||
auto &sheet = *rom()->bitmap_manager()[sheet_id];
|
||||
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 23);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
|
||||
if (ImGui::BeginTable("##SpritesTable", sheet_only ? 1 : 2,
|
||||
ImGuiTableFlags_Borders, ImVec2(0, 0))) {
|
||||
if (!sheet_only) {
|
||||
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
}
|
||||
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
if (!sheet_only) {
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHexByte(("0x" + std::to_string(i)).c_str(),
|
||||
&rom()->spriteset_ids[selected_spriteset_][i]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
TableNextColumn();
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
|
||||
auto sheet = *rom()->bitmap_manager()[115 + sheet_id];
|
||||
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 24);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
auto popup_id = absl::StrCat("Palette", n);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SnesColorButton(popup_id, palette[n],
|
||||
ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip)) {
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GfxGroupEditor::DrawPaletteViewer() {
|
||||
static uint8_t selected_paletteset = 0;
|
||||
|
||||
gui::InputHexByte("Selected Paletteset", &selected_paletteset);
|
||||
|
||||
auto dungeon_main_palette_val = rom()->paletteset_ids[selected_paletteset][0];
|
||||
auto dungeon_spr_pal_1_val = rom()->paletteset_ids[selected_paletteset][1];
|
||||
auto dungeon_spr_pal_2_val = rom()->paletteset_ids[selected_paletteset][2];
|
||||
auto dungeon_spr_pal_3_val = rom()->paletteset_ids[selected_paletteset][3];
|
||||
|
||||
gui::InputHexByte("Dungeon Main", &dungeon_main_palette_val);
|
||||
gui::InputHexByte("Dungeon Spr Pal 1", &dungeon_spr_pal_1_val);
|
||||
gui::InputHexByte("Dungeon Spr Pal 2", &dungeon_spr_pal_2_val);
|
||||
gui::InputHexByte("Dungeon Spr Pal 3", &dungeon_spr_pal_3_val);
|
||||
|
||||
auto &palette = *rom()->mutable_palette_group()->dungeon_main.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset][0]);
|
||||
DrawPaletteFromPaletteGroup(palette);
|
||||
auto &spr_aux_pal1 =
|
||||
*rom()->mutable_palette_group()->sprites_aux1.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset][1]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal1);
|
||||
auto &spr_aux_pal2 =
|
||||
*rom()->mutable_palette_group()->sprites_aux2.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset][2]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal2);
|
||||
auto &spr_aux_pal3 =
|
||||
*rom()->mutable_palette_group()->sprites_aux3.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset][3]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal3);
|
||||
}
|
||||
|
||||
void GfxGroupEditor::InitBlockset(gfx::Bitmap tile16_blockset) {
|
||||
tile16_blockset_bmp_ = tile16_blockset;
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,300 +0,0 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
static inline float ImSaturate(float f) {
|
||||
return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
|
||||
}
|
||||
|
||||
#define IM_F32_TO_INT8_SAT(_VAL) \
|
||||
((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
|
||||
|
||||
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#ifdef IMGUI_USE_STB_SPRINTF
|
||||
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
|
||||
#else
|
||||
int w = vsnprintf(buf, buf_size, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
if (buf == nullptr) return w;
|
||||
if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1;
|
||||
buf[w] = 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
if (rom()->is_loaded()) {
|
||||
// Initialize the labels
|
||||
for (int i = 0; i < kNumPalettes; i++) {
|
||||
rom()->resource_label()->CreateOrGetLabel(
|
||||
"Palette Group Name", std::to_string(i),
|
||||
std::string(kPaletteGroupNames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("paletteEditorTable", 2,
|
||||
ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Palette Groups",
|
||||
ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
for (int category = 0; category < kNumPalettes; ++category) {
|
||||
if (ImGui::TreeNode(kPaletteCategoryNames[category].data())) {
|
||||
status_ = DrawPaletteGroup(category);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (gui::SnesColorEdit4("Color Picker", current_color_,
|
||||
ImGuiColorEditFlags_NoAlpha)) {
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
CLEAR_AND_RETURN_STATUS(status_)
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
|
||||
int index) {
|
||||
if (index >= palette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
|
||||
// Get the current color
|
||||
ASSIGN_OR_RETURN(auto color, palette.GetColor(index));
|
||||
auto currentColor = color.rgb();
|
||||
if (ImGui::ColorPicker4("Color Picker", (float*)&palette[index])) {
|
||||
// The color was modified, update it in the palette
|
||||
palette(index, currentColor);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::ResetColorToOriginal(
|
||||
gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette) {
|
||||
if (index >= palette.size() || index >= originalPalette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
ASSIGN_OR_RETURN(auto color, originalPalette.GetColor(index));
|
||||
auto originalColor = color.rgb();
|
||||
palette(index, originalColor);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
||||
if (!rom()->is_loaded()) {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
|
||||
std::string group_name = kPaletteGroupNames[category].data();
|
||||
auto palette_group = *rom()->palette_group().get_group(group_name);
|
||||
const auto size = palette_group.size();
|
||||
|
||||
static bool edit_color = false;
|
||||
for (int j = 0; j < size; j++) {
|
||||
// ImGui::Text("%d", j);
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, "Palette Group Name", std::to_string(j),
|
||||
std::string(kPaletteGroupNames[category]));
|
||||
auto palette = palette_group.mutable_palette(j);
|
||||
auto pal_size = palette->size();
|
||||
|
||||
for (int n = 0; n < pal_size; n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 7) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
auto popup_id =
|
||||
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
|
||||
palette_button_flags)) {
|
||||
ASSIGN_OR_RETURN(current_color_, palette->GetColor(n));
|
||||
// EditColorInPalette(*palette, n);
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem(popup_id.c_str())) {
|
||||
RETURN_IF_ERROR(HandleColorPopup(*palette, category, j, n))
|
||||
}
|
||||
|
||||
// if (gui::SnesColorEdit4(popup_id.c_str(), (*palette)[n],
|
||||
// palette_button_flags)) {
|
||||
// EditColorInPalette(*palette, n);
|
||||
// }
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
||||
int j, int n) {
|
||||
auto col = gfx::ToFloatArray(palette[n]);
|
||||
if (gui::SnesColorEdit4("Edit Color", palette[n], color_popup_flags)) {
|
||||
// TODO: Implement new update color function
|
||||
}
|
||||
|
||||
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");
|
||||
if (ImGui::BeginPopup("Copy")) {
|
||||
int cr = IM_F32_TO_INT8_SAT(col[0]);
|
||||
int cg = IM_F32_TO_INT8_SAT(col[1]);
|
||||
int cb = IM_F32_TO_INT8_SAT(col[2]);
|
||||
char buf[64];
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0],
|
||||
col[1], col[2]);
|
||||
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb);
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
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;
|
||||
if (loaded && !init) {
|
||||
InitializeSavedPalette(palette);
|
||||
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")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
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");
|
||||
|
||||
if (ImGui::Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
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);
|
||||
|
||||
if (ImGui::ColorButton("##palette", saved_palette_[n],
|
||||
palette_button_flags_2, 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();
|
||||
}
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawPortablePalette(gfx::SnesPalette& palette) {
|
||||
static bool init = false;
|
||||
if (!init) {
|
||||
InitializeSavedPalette(palette);
|
||||
init = true;
|
||||
}
|
||||
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
ImGui::BeginGroup(); // Lock X position
|
||||
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);
|
||||
|
||||
if (ImGui::ColorButton("##palette", saved_palette_[n],
|
||||
palette_button_flags_2, ImVec2(20, 20)))
|
||||
ImVec4(saved_palette_[n].x, saved_palette_[n].y, saved_palette_[n].z,
|
||||
1.0f); // 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::EndChild();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,71 +1,18 @@
|
||||
#include "music_editor.h"
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/modules/assembly_editor.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/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() {
|
||||
absl::Status MusicEditor::Update() {
|
||||
if (ImGui::BeginTable("MusicEditorColumns", 2, music_editor_flags_,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Assembly");
|
||||
@@ -83,6 +30,8 @@ void MusicEditor::Update() {
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MusicEditor::DrawChannels() {
|
||||
@@ -221,31 +170,8 @@ void MusicEditor::DrawToolset() {
|
||||
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();
|
||||
// }
|
||||
// }
|
||||
if (is_playing && !has_loaded_song) {
|
||||
has_loaded_song = true;
|
||||
}
|
||||
|
||||
gui::ItemLabel("Select a song to edit: ", gui::ItemLabelFlags::Left);
|
||||
@@ -264,7 +190,6 @@ void MusicEditor::DrawToolset() {
|
||||
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;
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/modules/assembly_editor.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
@@ -59,9 +59,18 @@ static constexpr absl::string_view kSongNotes[] = {
|
||||
* @class MusicEditor
|
||||
* @brief A class for editing music data in a Rom.
|
||||
*/
|
||||
class MusicEditor : public SharedRom {
|
||||
class MusicEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
void Update();
|
||||
MusicEditor() { type_ = EditorType::kMusic; }
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
private:
|
||||
void DrawChannels();
|
||||
490
src/app/editor/overworld/entity.cc
Normal file
490
src/app/editor/overworld/entity.cc
Normal file
@@ -0,0 +1,490 @@
|
||||
#include "app/editor/overworld/entity.h"
|
||||
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginGroup;
|
||||
using ImGui::Button;
|
||||
using ImGui::Checkbox;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Text;
|
||||
|
||||
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) {
|
||||
// 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);
|
||||
|
||||
// Calculate the new position on the 16x16 grid
|
||||
int new_x = static_cast<int>(mouse_pos.x) / 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 entity position
|
||||
entity->set_x(new_x);
|
||||
entity->set_y(new_y);
|
||||
}
|
||||
|
||||
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) {
|
||||
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";
|
||||
}
|
||||
const auto is_hovering =
|
||||
IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling);
|
||||
|
||||
const auto drag_or_clicked = ImGui::IsMouseDragging(ImGuiMouseButton_Left) ||
|
||||
ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
|
||||
if (is_hovering && drag_or_clicked && !is_dragging_entity) {
|
||||
dragged_entity = entity;
|
||||
is_dragging_entity = true;
|
||||
} else if (is_hovering && 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_);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawEntranceInserterPopup() {
|
||||
bool set_done = false;
|
||||
if (set_done) {
|
||||
set_done = false;
|
||||
}
|
||||
if (ImGui::BeginPopup("Entrance Inserter")) {
|
||||
static int entrance_id = 0;
|
||||
gui::InputHex("Entrance ID", &entrance_id);
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
set_done = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return set_done;
|
||||
}
|
||||
|
||||
// TODO: Implement deleting OverworldEntrance objects, currently only hides them
|
||||
bool DrawOverworldEntrancePopup(
|
||||
zelda3::overworld::OverworldEntrance &entrance) {
|
||||
static bool set_done = false;
|
||||
if (set_done) {
|
||||
set_done = false;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Entrance editor", NULL,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
gui::InputHex("Map ID", &entrance.map_id_);
|
||||
gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
|
||||
kInputFieldSize + 20);
|
||||
gui::InputHex("X", &entrance.x_);
|
||||
gui::InputHex("Y", &entrance.y_);
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
set_done = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DELETE)) {
|
||||
entrance.deleted = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return set_done;
|
||||
}
|
||||
|
||||
// TODO: Implement deleting OverworldExit objects
|
||||
void DrawExitInserterPopup() {
|
||||
if (ImGui::BeginPopup("Exit Inserter")) {
|
||||
static int exit_id = 0;
|
||||
gui::InputHex("Exit ID", &exit_id);
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
|
||||
static bool set_done = false;
|
||||
if (set_done) {
|
||||
set_done = false;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Exit editor", NULL,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
// Normal door: None = 0, Wooden = 1, Bombable = 2
|
||||
static int doorType = exit.door_type_1_;
|
||||
// Fancy door: None = 0, Sanctuary = 1, Palace = 2
|
||||
static int fancyDoorType = exit.door_type_2_;
|
||||
|
||||
static int xPos = 0;
|
||||
static int yPos = 0;
|
||||
|
||||
// Special overworld exit properties
|
||||
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;
|
||||
|
||||
gui::InputHexWord("Room", &exit.room_id_);
|
||||
SameLine();
|
||||
gui::InputHex("Entity ID", &exit.entity_id_, 4);
|
||||
gui::InputHex("Map", &exit.map_id_);
|
||||
SameLine();
|
||||
Checkbox("Automatic", &exit.is_automatic_);
|
||||
|
||||
gui::InputHex("X Positon", &exit.x_);
|
||||
SameLine();
|
||||
gui::InputHex("Y Position", &exit.y_);
|
||||
|
||||
gui::InputHexByte("X Camera", &exit.x_camera_);
|
||||
SameLine();
|
||||
gui::InputHexByte("Y Camera", &exit.y_camera_);
|
||||
|
||||
gui::InputHexWord("X Scroll", &exit.x_scroll_);
|
||||
SameLine();
|
||||
gui::InputHexWord("Y Scroll", &exit.y_scroll_);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
static bool show_properties = false;
|
||||
Checkbox("Show properties", &show_properties);
|
||||
if (show_properties) {
|
||||
Text("Deleted? %s", exit.deleted_ ? "true" : "false");
|
||||
Text("Hole? %s", exit.is_hole_ ? "true" : "false");
|
||||
Text("Large Map? %s", exit.large_map_ ? "true" : "false");
|
||||
}
|
||||
|
||||
gui::TextWithSeparators("Unimplemented below");
|
||||
|
||||
ImGui::RadioButton("None", &doorType, 0);
|
||||
SameLine();
|
||||
ImGui::RadioButton("Wooden", &doorType, 1);
|
||||
SameLine();
|
||||
ImGui::RadioButton("Bombable", &doorType, 2);
|
||||
// If door type is not None, input positions
|
||||
if (doorType != 0) {
|
||||
gui::InputHex("Door X pos", &xPos);
|
||||
gui::InputHex("Door Y pos", &yPos);
|
||||
}
|
||||
|
||||
ImGui::RadioButton("None##Fancy", &fancyDoorType, 0);
|
||||
SameLine();
|
||||
ImGui::RadioButton("Sanctuary", &fancyDoorType, 1);
|
||||
SameLine();
|
||||
ImGui::RadioButton("Palace", &fancyDoorType, 2);
|
||||
// If fancy door type is not None, input positions
|
||||
if (fancyDoorType != 0) {
|
||||
// Placeholder for fancy door's X position
|
||||
gui::InputHex("Fancy Door X pos", &xPos);
|
||||
// Placeholder for fancy door's Y position
|
||||
gui::InputHex("Fancy Door Y pos", &yPos);
|
||||
}
|
||||
|
||||
static bool special_exit = false;
|
||||
Checkbox("Special exit", &special_exit);
|
||||
if (special_exit) {
|
||||
gui::InputHex("Center X", ¢erX);
|
||||
|
||||
gui::InputHex("Center Y", ¢erY);
|
||||
gui::InputHex("Unk1", &unk1);
|
||||
gui::InputHex("Unk2", &unk2);
|
||||
|
||||
gui::InputHex("Link's posture", &linkPosture);
|
||||
gui::InputHex("Sprite GFX", &spriteGFX);
|
||||
gui::InputHex("BG GFX", &bgGFX);
|
||||
gui::InputHex("Palette", &palette);
|
||||
gui::InputHex("Spr Pal", &sprPal);
|
||||
|
||||
gui::InputHex("Top", &top);
|
||||
gui::InputHex("Bottom", &bottom);
|
||||
gui::InputHex("Left", &left);
|
||||
gui::InputHex("Right", &right);
|
||||
|
||||
gui::InputHex("Left edge of map", &leftEdgeOfMap);
|
||||
}
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
set_done = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DELETE)) {
|
||||
exit.deleted_ = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return set_done;
|
||||
}
|
||||
|
||||
void DrawItemInsertPopup() {
|
||||
// Contents of the Context Menu
|
||||
if (ImGui::BeginPopup("Item Inserter")) {
|
||||
static int new_item_id = 0;
|
||||
Text("Add Item");
|
||||
BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||
i == new_item_id)) {
|
||||
new_item_id = i;
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
// Add the new item to the overworld
|
||||
new_item_id = 0;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement deleting OverworldItem objects, currently only hides them
|
||||
bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item) {
|
||||
static bool set_done = false;
|
||||
if (set_done) {
|
||||
set_done = false;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Item editor", NULL,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||
item.id == i)) {
|
||||
item.id = i;
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
EndChild();
|
||||
|
||||
if (Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup();
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CLOSE)) {
|
||||
set_done = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DELETE)) {
|
||||
item.deleted = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return set_done;
|
||||
}
|
||||
|
||||
const ImGuiTableSortSpecs *SpriteItem::s_current_sort_specs = nullptr;
|
||||
|
||||
void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
|
||||
static ImGuiTextFilter filter;
|
||||
static int selected_id = 0;
|
||||
static std::vector<SpriteItem> items;
|
||||
|
||||
// Initialize items if empty
|
||||
if (items.empty()) {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
items.push_back(SpriteItem{i, core::kSpriteDefaultNames[i].data()});
|
||||
}
|
||||
}
|
||||
|
||||
filter.Draw("Filter", 180);
|
||||
|
||||
if (ImGui::BeginTable("##sprites", 2,
|
||||
ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort, 0.0f,
|
||||
MyItemColumnID_ID);
|
||||
ImGui::TableSetupColumn("Name", 0, 0.0f, MyItemColumnID_Name);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Handle sorting
|
||||
if (ImGuiTableSortSpecs *sort_specs = ImGui::TableGetSortSpecs()) {
|
||||
if (sort_specs->SpecsDirty) {
|
||||
SpriteItem::SortWithSortSpecs(sort_specs, items);
|
||||
sort_specs->SpecsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Display filtered and sorted items
|
||||
for (const auto &item : items) {
|
||||
if (filter.PassFilter(item.name)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
Text("%d", item.id);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
|
||||
if (Selectable(item.name, selected_id == item.id,
|
||||
ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
selected_id = item.id;
|
||||
onSpriteSelect(item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement deleting OverworldSprite objects
|
||||
void DrawSpriteInserterPopup() {
|
||||
if (ImGui::BeginPopup("Sprite Inserter")) {
|
||||
static int new_sprite_id = 0;
|
||||
Text("Add Sprite");
|
||||
BeginChild("ScrollRegion", ImVec2(250, 250), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
DrawSpriteTable([](int selected_id) { new_sprite_id = selected_id; });
|
||||
EndChild();
|
||||
|
||||
if (Button(ICON_MD_DONE)) {
|
||||
// Add the new item to the overworld
|
||||
new_sprite_id = 0;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
|
||||
if (Button(ICON_MD_CANCEL)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite) {
|
||||
static bool set_done = false;
|
||||
if (set_done) {
|
||||
set_done = false;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Sprite editor", NULL,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
BeginChild("ScrollRegion", ImVec2(350, 350), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
ImGui::BeginGroup();
|
||||
Text("%s", sprite.name().c_str());
|
||||
|
||||
DrawSpriteTable([&sprite](int selected_id) {
|
||||
sprite.set_id(selected_id);
|
||||
sprite.UpdateMapProperties(sprite.map_id());
|
||||
});
|
||||
ImGui::EndGroup();
|
||||
EndChild();
|
||||
|
||||
if (Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup();
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CLOSE)) {
|
||||
set_done = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DELETE)) {
|
||||
sprite.set_deleted(true);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return set_done;
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
88
src/app/editor/overworld/entity.h
Normal file
88
src/app/editor/overworld/entity.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/zelda3/common.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
|
||||
ImVec2 canvas_p0, ImVec2 scrolling);
|
||||
|
||||
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, bool free_movement = false);
|
||||
|
||||
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);
|
||||
|
||||
bool DrawEntranceInserterPopup();
|
||||
bool DrawOverworldEntrancePopup(zelda3::overworld::OverworldEntrance &entrance);
|
||||
|
||||
void DrawExitInserterPopup();
|
||||
bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit);
|
||||
|
||||
void DrawItemInsertPopup();
|
||||
|
||||
bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item);
|
||||
|
||||
enum MyItemColumnID {
|
||||
MyItemColumnID_ID,
|
||||
MyItemColumnID_Name,
|
||||
MyItemColumnID_Action,
|
||||
MyItemColumnID_Quantity,
|
||||
MyItemColumnID_Description
|
||||
};
|
||||
|
||||
struct SpriteItem {
|
||||
int id;
|
||||
const char *name;
|
||||
static const ImGuiTableSortSpecs *s_current_sort_specs;
|
||||
|
||||
static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs,
|
||||
std::vector<SpriteItem> &items) {
|
||||
s_current_sort_specs =
|
||||
sort_specs; // Store for access by the compare function.
|
||||
if (items.size() > 1)
|
||||
std::sort(items.begin(), items.end(), SpriteItem::CompareWithSortSpecs);
|
||||
s_current_sort_specs = nullptr;
|
||||
}
|
||||
|
||||
static bool CompareWithSortSpecs(const SpriteItem &a, const SpriteItem &b) {
|
||||
for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) {
|
||||
const ImGuiTableColumnSortSpecs *sort_spec =
|
||||
&s_current_sort_specs->Specs[n];
|
||||
int delta = 0;
|
||||
switch (sort_spec->ColumnUserID) {
|
||||
case MyItemColumnID_ID:
|
||||
delta = (a.id - b.id);
|
||||
break;
|
||||
case MyItemColumnID_Name:
|
||||
delta = strcmp(a.name + 2, b.name + 2);
|
||||
break;
|
||||
}
|
||||
if (delta != 0)
|
||||
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending)
|
||||
? delta < 0
|
||||
: delta > 0;
|
||||
}
|
||||
return a.id < b.id; // Fallback
|
||||
}
|
||||
};
|
||||
|
||||
void DrawSpriteTable(std::function<void(int)> onSpriteSelect);
|
||||
void DrawSpriteInserterPopup();
|
||||
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite);
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
|
||||
157
src/app/editor/overworld/refresh.cc
Normal file
157
src/app/editor/overworld/refresh.cc
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "app/editor/overworld_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
void OverworldEditor::RefreshChildMap(int map_index) {
|
||||
overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
|
||||
PRINT_IF_ERROR(status_);
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
|
||||
overworld_.tiles16().size());
|
||||
PRINT_IF_ERROR(status_);
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
|
||||
overworld_.GetMapTiles(current_world_));
|
||||
maps_bmp_[map_index].set_data(
|
||||
overworld_.mutable_overworld_map(map_index)->bitmap_data());
|
||||
maps_bmp_[map_index].set_modified(true);
|
||||
PRINT_IF_ERROR(status_);
|
||||
}
|
||||
|
||||
void OverworldEditor::RefreshOverworldMap() {
|
||||
std::vector<std::future<void>> futures;
|
||||
int indices[4];
|
||||
|
||||
auto refresh_map_async = [this](int map_index) {
|
||||
RefreshChildMap(map_index);
|
||||
};
|
||||
|
||||
int source_map_id = current_map_;
|
||||
bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
|
||||
if (is_large) {
|
||||
source_map_id = current_parent_;
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
|
||||
if (i >= 2) sibling_index += 6;
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, refresh_map_async, sibling_index));
|
||||
indices[i] = sibling_index;
|
||||
}
|
||||
}
|
||||
indices[0] = source_map_id;
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, refresh_map_async, source_map_id));
|
||||
|
||||
for (auto &each : futures) {
|
||||
each.get();
|
||||
}
|
||||
int n = is_large ? 4 : 1;
|
||||
// We do texture updating on the main thread
|
||||
for (int i = 0; i < n; ++i) {
|
||||
rom()->UpdateBitmap(&maps_bmp_[indices[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status OverworldEditor::RefreshMapPalette() {
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(current_map_)->LoadPalette());
|
||||
const auto current_map_palette = overworld_.AreaPalette();
|
||||
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
||||
if (i >= 2) sibling_index += 6;
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
RETURN_IF_ERROR(
|
||||
maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void OverworldEditor::RefreshMapProperties() {
|
||||
auto ¤t_ow_map = *overworld_.mutable_overworld_map(current_map_);
|
||||
if (current_ow_map.is_large_map()) {
|
||||
// We need to copy the properties from the parent map to the children
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = current_ow_map.parent() + i;
|
||||
if (i >= 2) {
|
||||
sibling_index += 6;
|
||||
}
|
||||
auto &map = *overworld_.mutable_overworld_map(sibling_index);
|
||||
map.set_area_graphics(current_ow_map.area_graphics());
|
||||
map.set_area_palette(current_ow_map.area_palette());
|
||||
map.set_sprite_graphics(game_state_,
|
||||
current_ow_map.sprite_graphics(game_state_));
|
||||
map.set_sprite_palette(game_state_,
|
||||
current_ow_map.sprite_palette(game_state_));
|
||||
map.set_message_id(current_ow_map.message_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status OverworldEditor::RefreshTile16Blockset() {
|
||||
if (current_blockset_ ==
|
||||
overworld_.overworld_map(current_map_)->area_graphics()) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
|
||||
|
||||
overworld_.set_current_map(current_map_);
|
||||
palette_ = overworld_.AreaPalette();
|
||||
// Create the tile16 blockset image
|
||||
rom()->UpdateBitmap(&tile16_blockset_bmp_);
|
||||
RETURN_IF_ERROR(tile16_blockset_bmp_.ApplyPalette(palette_));
|
||||
|
||||
// Copy the tile16 data into individual tiles.
|
||||
auto tile16_data = overworld_.Tile16Blockset();
|
||||
|
||||
std::vector<std::future<void>> futures;
|
||||
// Loop through the tiles and copy their pixel data into separate vectors
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
futures.push_back(std::async(
|
||||
std::launch::async,
|
||||
[&](int index) {
|
||||
// Create a new vector for the pixel data of the current tile
|
||||
Bytes tile_data(16 * 16, 0x00); // More efficient initialization
|
||||
|
||||
// Copy the pixel data for the current tile into the vector
|
||||
for (int ty = 0; ty < 16; ty++) {
|
||||
for (int tx = 0; tx < 16; tx++) {
|
||||
int position = tx + (ty * 0x10);
|
||||
uint8_t value =
|
||||
tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
|
||||
(ty * 0x80) + tx];
|
||||
tile_data[position] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the vector for the current tile to the vector of tile pixel
|
||||
// data
|
||||
tile16_individual_[index].set_data(tile_data);
|
||||
},
|
||||
i));
|
||||
}
|
||||
|
||||
for (auto &future : futures) {
|
||||
future.get();
|
||||
}
|
||||
|
||||
// Render the bitmaps of each tile.
|
||||
for (int id = 0; id < 4096; id++) {
|
||||
RETURN_IF_ERROR(tile16_individual_[id].ApplyPalette(palette_));
|
||||
rom()->UpdateBitmap(&tile16_individual_[id]);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/imgui_internal.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
@@ -12,18 +11,18 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/context/entrance_context.h"
|
||||
#include "app/editor/context/gfx_context.h"
|
||||
#include "app/editor/modules/gfx_group_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/editor/modules/tile16_editor.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/graphics/tile16_editor.h"
|
||||
#include "app/editor/overworld/entity.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/gui/zeml.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -62,6 +61,28 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
|
||||
constexpr absl::string_view kOWEditTable = "##OWEditTable";
|
||||
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
|
||||
|
||||
class EntranceContext {
|
||||
public:
|
||||
absl::Status LoadEntranceTileTypes(Rom& rom) {
|
||||
int offset_low = 0xDB8BF;
|
||||
int offset_high = 0xDB917;
|
||||
|
||||
for (int i = 0; i < 0x2C; i++) {
|
||||
// Load entrance tile types
|
||||
ASSIGN_OR_RETURN(auto value_low, rom.ReadWord(offset_low + i));
|
||||
entrance_tile_types_low_.push_back(value_low);
|
||||
ASSIGN_OR_RETURN(auto value_high, rom.ReadWord(offset_high + i));
|
||||
entrance_tile_types_low_.push_back(value_high);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint16_t> entrance_tile_types_low_;
|
||||
std::vector<uint16_t> entrance_tile_types_high_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class OverworldEditor
|
||||
* @brief Manipulates the Overworld and OverworldMap data in a Rom.
|
||||
@@ -81,15 +102,20 @@ constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
|
||||
class OverworldEditor : public Editor,
|
||||
public SharedRom,
|
||||
public context::GfxContext,
|
||||
public context::EntranceContext,
|
||||
public EntranceContext,
|
||||
public core::ExperimentFlags {
|
||||
public:
|
||||
OverworldEditor() { type_ = EditorType::kOverworld; }
|
||||
|
||||
void InitializeZeml();
|
||||
|
||||
absl::Status Update() final;
|
||||
absl::Status Undo() { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() { return absl::UnimplementedError("Find Unused Tiles"); }
|
||||
|
||||
auto overworld() { return &overworld_; }
|
||||
|
||||
@@ -99,25 +125,6 @@ class OverworldEditor : public Editor,
|
||||
int jump_to_tab() { return jump_to_tab_; }
|
||||
int jump_to_tab_ = -1;
|
||||
|
||||
void Shutdown() {
|
||||
for (auto& bmp : tile16_individual_) {
|
||||
bmp.Cleanup();
|
||||
}
|
||||
for (auto& [i, bmp] : maps_bmp_) {
|
||||
bmp.Cleanup();
|
||||
}
|
||||
for (auto& [i, bmp] : graphics_bin_) {
|
||||
bmp.Cleanup();
|
||||
}
|
||||
for (auto& [i, bmp] : current_graphics_set_) {
|
||||
bmp.Cleanup();
|
||||
}
|
||||
maps_bmp_.clear();
|
||||
overworld_.Destroy();
|
||||
all_gfx_loaded_ = false;
|
||||
map_blockset_loaded_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the Bitmap objects for each OverworldMap.
|
||||
*
|
||||
@@ -128,7 +135,6 @@ class OverworldEditor : public Editor,
|
||||
absl::Status LoadGraphics();
|
||||
|
||||
private:
|
||||
absl::Status UpdateOverworldEdit();
|
||||
absl::Status UpdateFullscreenCanvas();
|
||||
|
||||
absl::Status DrawToolset();
|
||||
@@ -259,14 +265,15 @@ class OverworldEditor : public Editor,
|
||||
PaletteEditor palette_editor_;
|
||||
zelda3::overworld::Overworld overworld_;
|
||||
|
||||
gui::Canvas ow_map_canvas_{ImVec2(0x200 * 8, 0x200 * 8),
|
||||
gui::Canvas ow_map_canvas_{"owMapCanvas", ImVec2(0x200 * 8, 0x200 * 8),
|
||||
gui::CanvasGridSize::k64x64};
|
||||
gui::Canvas current_gfx_canvas_{ImVec2(0x100 + 1, 0x10 * 0x40 + 1),
|
||||
gui::Canvas current_gfx_canvas_{"customGfxCanvas",
|
||||
ImVec2(0x100 + 1, 0x10 * 0x40 + 1),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas blockset_canvas_{ImVec2(0x100 + 1, 0x2000 + 1),
|
||||
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100 + 1, 0x2000 + 1),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas graphics_bin_canvas_{
|
||||
ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1),
|
||||
"graphicsBinCanvas", ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1),
|
||||
gui::CanvasGridSize::k16x16};
|
||||
gui::Canvas properties_canvas_;
|
||||
|
||||
@@ -277,12 +284,13 @@ class OverworldEditor : public Editor,
|
||||
gfx::Bitmap all_gfx_bmp;
|
||||
|
||||
gfx::BitmapTable maps_bmp_;
|
||||
gfx::BitmapTable graphics_bin_;
|
||||
gfx::BitmapTable current_graphics_set_;
|
||||
gfx::BitmapTable sprite_previews_;
|
||||
|
||||
gfx::BitmapTable animated_maps_;
|
||||
|
||||
OWBlockset refresh_blockset_;
|
||||
|
||||
gui::zeml::Node layout_node_;
|
||||
absl::Status status_;
|
||||
};
|
||||
} // namespace editor
|
||||
|
||||
85
src/app/editor/settings_editor.cc
Normal file
85
src/app/editor/settings_editor.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/utils/flags.h"
|
||||
#include "app/editor/utils/keyboard_shortcuts.h"
|
||||
#include "app/editor/settings_editor.h"
|
||||
#include "app/editor/utils/flags.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginMenu;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Checkbox;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndMenu;
|
||||
using ImGui::EndTabBar;
|
||||
using ImGui::EndTabItem;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::TableHeader;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetBgColor;
|
||||
using ImGui::TableSetColumnIndex;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
absl::Status SettingsEditor::Update() {
|
||||
if (BeginTabBar("Settings", ImGuiTabBarFlags_None)) {
|
||||
if (BeginTabItem("General")) {
|
||||
DrawGeneralSettings();
|
||||
EndTabItem();
|
||||
}
|
||||
if (BeginTabItem("Keyboard Shortcuts")) {
|
||||
EndTabItem();
|
||||
}
|
||||
EndTabBar();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void SettingsEditor::DrawGeneralSettings() {
|
||||
if (BeginTable("##SettingsTable", 2,
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("Experiment Flags", ImGuiTableColumnFlags_WidthFixed,
|
||||
250.0f);
|
||||
TableSetupColumn("General Setting", ImGuiTableColumnFlags_WidthStretch,
|
||||
0.0f);
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextColumn();
|
||||
if (BeginChild("##GeneralSettingsStyleWrapper", ImVec2(0, 0),
|
||||
ImGuiChildFlags_FrameStyle)) {
|
||||
static FlagsMenu flags;
|
||||
flags.Draw();
|
||||
EndChild();
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (BeginChild("##GeneralSettingsWrapper", ImVec2(0, 0),
|
||||
ImGuiChildFlags_FrameStyle)) {
|
||||
Text("TODO: Add some settings here");
|
||||
EndChild();
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status SettingsEditor::DrawKeyboardShortcuts() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
232
src/app/editor/settings_editor.h
Normal file
232
src/app/editor/settings_editor.h
Normal file
@@ -0,0 +1,232 @@
|
||||
#ifndef YAZE_APP_EDITOR_SETTINGS_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SETTINGS_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
// Simple representation for a tree
|
||||
// (this is designed to be simple to understand for our demos, not to be
|
||||
// efficient etc.)
|
||||
struct ExampleTreeNode {
|
||||
char Name[28];
|
||||
ImGuiID UID = 0;
|
||||
ExampleTreeNode* Parent = NULL;
|
||||
ImVector<ExampleTreeNode*> Childs;
|
||||
|
||||
// Data
|
||||
bool HasData = false; // All leaves have data
|
||||
bool DataIsEnabled = false;
|
||||
int DataInt = 128;
|
||||
ImVec2 DataVec2 = ImVec2(0.0f, 3.141592f);
|
||||
};
|
||||
|
||||
// Simple representation of struct metadata/serialization data.
|
||||
// (this is a minimal version of what a typical advanced application may
|
||||
// provide)
|
||||
struct ExampleMemberInfo {
|
||||
const char* Name;
|
||||
ImGuiDataType DataType;
|
||||
int DataCount;
|
||||
int Offset;
|
||||
};
|
||||
|
||||
// Metadata description of ExampleTreeNode struct.
|
||||
static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]{
|
||||
{"Enabled", ImGuiDataType_Bool, 1,
|
||||
offsetof(ExampleTreeNode, DataIsEnabled)},
|
||||
{"MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataInt)},
|
||||
{"MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataVec2)},
|
||||
};
|
||||
|
||||
static ExampleTreeNode* ExampleTree_CreateNode(const char* name,
|
||||
const ImGuiID uid,
|
||||
ExampleTreeNode* parent) {
|
||||
ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
|
||||
snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name);
|
||||
node->UID = uid;
|
||||
node->Parent = parent;
|
||||
if (parent) parent->Childs.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Create example tree data
|
||||
static ExampleTreeNode* ExampleTree_CreateDemoTree() {
|
||||
static const char* root_names[] = {"Apple", "Banana", "Cherry",
|
||||
"Kiwi", "Mango", "Orange",
|
||||
"Pineapple", "Strawberry", "Watermelon"};
|
||||
char name_buf[32];
|
||||
ImGuiID uid = 0;
|
||||
ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
|
||||
for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) {
|
||||
snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2);
|
||||
ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
|
||||
const int number_of_childs = (int)strlen(node_L1->Name);
|
||||
for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) {
|
||||
snprintf(name_buf, 32, "Child %d", idx_L1);
|
||||
ExampleTreeNode* node_L2 =
|
||||
ExampleTree_CreateNode(name_buf, ++uid, node_L1);
|
||||
node_L2->HasData = true;
|
||||
if (idx_L1 == 0) {
|
||||
snprintf(name_buf, 32, "Sub-child %d", 0);
|
||||
ExampleTreeNode* node_L3 =
|
||||
ExampleTree_CreateNode(name_buf, ++uid, node_L2);
|
||||
node_L3->HasData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return node_L0;
|
||||
}
|
||||
|
||||
struct ExampleAppPropertyEditor {
|
||||
ImGuiTextFilter Filter;
|
||||
|
||||
void Draw(ExampleTreeNode* root_node) {
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F,
|
||||
ImGuiInputFlags_Tooltip);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
|
||||
if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf,
|
||||
IM_ARRAYSIZE(Filter.InputBuf),
|
||||
ImGuiInputTextFlags_EscapeClearsAll))
|
||||
Filter.Build();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_ScrollY |
|
||||
ImGuiTableFlags_RowBg;
|
||||
if (ImGui::BeginTable("##split", 2, table_flags)) {
|
||||
ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch,
|
||||
1.0f);
|
||||
ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch,
|
||||
2.0f); // Default twice larger
|
||||
// ImGui::TableSetupScrollFreeze(0, 1);
|
||||
// ImGui::TableHeadersRow();
|
||||
|
||||
for (ExampleTreeNode* node : root_node->Childs)
|
||||
if (Filter.PassFilter(node->Name)) // Filter root node
|
||||
DrawTreeNode(node);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTreeNode(ExampleTreeNode* node) {
|
||||
// Object tree node
|
||||
ImGui::PushID((int)node->UID);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
|
||||
tree_flags |=
|
||||
ImGuiTreeNodeFlags_SpanAllColumns |
|
||||
ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility
|
||||
tree_flags |=
|
||||
ImGuiTreeNodeFlags_OpenOnArrow |
|
||||
ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we
|
||||
// are likely to want to add
|
||||
// selection afterwards
|
||||
tree_flags |=
|
||||
ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
|
||||
bool node_open =
|
||||
ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextDisabled("UID: 0x%08X", node->UID);
|
||||
|
||||
// Display child and data
|
||||
if (node_open)
|
||||
for (ExampleTreeNode* child : node->Childs) DrawTreeNode(child);
|
||||
if (node_open && node->HasData) {
|
||||
// In a typical application, the structure description would be derived
|
||||
// from a data-driven system.
|
||||
// - We try to mimic this with our ExampleMemberInfo structure and the
|
||||
// ExampleTreeNodeMemberInfos[] array.
|
||||
// - Limits and some details are hard-coded to simplify the demo.
|
||||
// - Text and Selectable are less high than framed widgets, using
|
||||
// AlignTextToFramePadding() we add vertical spacing to make the
|
||||
// selectable lines equal high.
|
||||
for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav,
|
||||
true);
|
||||
ImGui::Selectable(field_desc.Name, false,
|
||||
ImGuiSelectableFlags_SpanAllColumns |
|
||||
ImGuiSelectableFlags_AllowOverlap);
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushID(field_desc.Name);
|
||||
void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
|
||||
switch (field_desc.DataType) {
|
||||
case ImGuiDataType_Bool: {
|
||||
IM_ASSERT(field_desc.DataCount == 1);
|
||||
ImGui::Checkbox("##Editor", (bool*)field_ptr);
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32: {
|
||||
int v_min = INT_MIN, v_max = INT_MAX;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr,
|
||||
field_desc.DataCount, 1.0f, &v_min, &v_max);
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float: {
|
||||
float v_min = 0.0f, v_max = 1.0f;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr,
|
||||
field_desc.DataCount, &v_min, &v_max);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
if (node_open) ImGui::TreePop();
|
||||
ImGui::PopID();
|
||||
}
|
||||
};
|
||||
|
||||
// Demonstrate creating a simple property editor.
|
||||
static void ShowExampleAppPropertyEditor(bool* p_open) {
|
||||
ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Example: Property editor", p_open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
static ExampleAppPropertyEditor property_editor;
|
||||
static ExampleTreeNode* tree_data = ExampleTree_CreateDemoTree();
|
||||
property_editor.Draw(tree_data);
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
class SettingsEditor : public Editor {
|
||||
public:
|
||||
SettingsEditor() : Editor() { type_ = EditorType::kSettings; }
|
||||
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
private:
|
||||
void DrawGeneralSettings();
|
||||
|
||||
absl::Status DrawKeyboardShortcuts();
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SETTINGS_EDITOR_H_
|
||||
278
src/app/editor/sprite/sprite_editor.cc
Normal file
278
src/app/editor/sprite/sprite_editor.cc
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "sprite_editor.h"
|
||||
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/editor/sprite/zsprite.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Separator;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
absl::Status SpriteEditor::Update() {
|
||||
if (rom()->is_loaded() && !sheets_loaded_) {
|
||||
// Load the values for current_sheets_ array
|
||||
sheets_loaded_ = true;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabBar("##SpriteEditorTabs")) {
|
||||
if (ImGui::BeginTabItem("Vanilla")) {
|
||||
DrawVanillaSpriteEditor();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Custom")) {
|
||||
DrawCustomSprites();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
return status_.ok() ? absl::OkStatus() : status_;
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawVanillaSpriteEditor() {
|
||||
if (ImGui::BeginTable("##SpriteCanvasTable", 3, ImGuiTableFlags_Resizable,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Sprites List", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
TableSetupColumn("Tile Selector", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
TableNextColumn();
|
||||
DrawSpritesList();
|
||||
|
||||
TableNextColumn();
|
||||
static int next_tab_id = 0;
|
||||
|
||||
if (ImGui::BeginTabBar("SpriteTabBar", kSpriteTabBarFlags)) {
|
||||
if (ImGui::TabItemButton(ICON_MD_ADD, kSpriteTabBarFlags)) {
|
||||
if (std::find(active_sprites_.begin(), active_sprites_.end(),
|
||||
current_sprite_id_) != active_sprites_.end()) {
|
||||
// Room is already open
|
||||
next_tab_id++;
|
||||
}
|
||||
active_sprites_.push_back(next_tab_id++); // Add new tab
|
||||
}
|
||||
|
||||
// Submit our regular tabs
|
||||
for (int n = 0; n < active_sprites_.Size;) {
|
||||
bool open = true;
|
||||
|
||||
if (active_sprites_[n] > sizeof(core::kSpriteDefaultNames) / 4) {
|
||||
active_sprites_.erase(active_sprites_.Data + n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(
|
||||
core::kSpriteDefaultNames[active_sprites_[n]].data(), &open,
|
||||
ImGuiTabItemFlags_None)) {
|
||||
DrawSpriteCanvas();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (!open)
|
||||
active_sprites_.erase(active_sprites_.Data + n);
|
||||
else
|
||||
n++;
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (sheets_loaded_) {
|
||||
DrawCurrentSheets();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawSpriteCanvas() {
|
||||
static bool flip_x = false;
|
||||
static bool flip_y = false;
|
||||
if (ImGui::BeginChild(gui::GetID("##SpriteCanvas"),
|
||||
ImGui::GetContentRegionAvail(), true)) {
|
||||
sprite_canvas_.DrawBackground();
|
||||
sprite_canvas_.DrawContextMenu();
|
||||
sprite_canvas_.DrawGrid();
|
||||
sprite_canvas_.DrawOverlay();
|
||||
|
||||
// Draw a table with OAM configuration
|
||||
// X, Y, Tile, Palette, Priority, Flip X, Flip Y
|
||||
if (ImGui::BeginTable("##OAMTable", 7, ImGuiTableFlags_Resizable,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("X", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Y", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Tile", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Palette", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Priority", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Flip X", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Flip Y", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexWord("", &oam_config_.x);
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexWord("", &oam_config_.y);
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexByte("", &oam_config_.tile);
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexByte("", &oam_config_.palette);
|
||||
|
||||
TableNextColumn();
|
||||
gui::InputHexByte("", &oam_config_.priority);
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::Checkbox("##XFlip", &flip_x)) {
|
||||
oam_config_.flip_x = flip_x;
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
if (ImGui::Checkbox("##YFlip", &flip_y)) {
|
||||
oam_config_.flip_y = flip_y;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
DrawAnimationFrames();
|
||||
|
||||
DrawCustomSpritesMetadata();
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawCurrentSheets() {
|
||||
if (ImGui::BeginChild(gui::GetID("sheet_label"),
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration)) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
std::string sheet_label = absl::StrFormat("Sheet %d", i);
|
||||
gui::InputHexByte(sheet_label.c_str(), ¤t_sheets_[i]);
|
||||
if (i % 2 == 0) ImGui::SameLine();
|
||||
}
|
||||
|
||||
graphics_sheet_canvas_.DrawBackground();
|
||||
graphics_sheet_canvas_.DrawContextMenu();
|
||||
graphics_sheet_canvas_.DrawTileSelector(32);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
graphics_sheet_canvas_.DrawBitmap(
|
||||
rom()->bitmap_manager()[current_sheets_[i]], 1, (i * 0x40) + 1, 2);
|
||||
}
|
||||
graphics_sheet_canvas_.DrawGrid();
|
||||
graphics_sheet_canvas_.DrawOverlay();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawSpritesList() {
|
||||
if (ImGui::BeginChild(gui::GetID("##SpritesList"),
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration)) {
|
||||
int i = 0;
|
||||
for (const auto each_sprite_name : core::kSpriteDefaultNames) {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
current_sprite_id_ == i, "Sprite Names", core::UppercaseHexByte(i),
|
||||
core::kSpriteDefaultNames[i].data());
|
||||
if (ImGui::IsItemClicked()) {
|
||||
current_sprite_id_ = i;
|
||||
if (!active_sprites_.contains(i)) {
|
||||
active_sprites_.push_back(i);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawAnimationFrames() {
|
||||
if (ImGui::Button("Add Frame")) {
|
||||
// Add a new frame
|
||||
}
|
||||
if (ImGui::Button("Remove Frame")) {
|
||||
// Remove the current frame
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawCustomSprites() {
|
||||
if (BeginTable("##CustomSpritesTable", 3,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Metadata", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
TableSetupColumn("TIlesheets", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
|
||||
Separator();
|
||||
DrawCustomSpritesMetadata();
|
||||
|
||||
TableNextColumn();
|
||||
DrawSpriteCanvas();
|
||||
|
||||
TableNextColumn();
|
||||
DrawCurrentSheets();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawCustomSpritesMetadata() {
|
||||
// ZSprite Maker format open file dialog
|
||||
if (ImGui::Button("Open ZSprite")) {
|
||||
// Open ZSprite file
|
||||
std::string file_path = FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!file_path.empty()) {
|
||||
zsprite::ZSprite zsprite;
|
||||
status_ = zsprite.Load(file_path);
|
||||
if (status_.ok()) {
|
||||
custom_sprites_.push_back(zsprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto custom_sprite : custom_sprites_) {
|
||||
Selectable("%s", custom_sprite.sprName.c_str());
|
||||
if (ImGui::IsItemClicked()) {
|
||||
current_sprite_id_ = 256 + stoi(custom_sprite.property_sprid.Text);
|
||||
if (!active_sprites_.contains(current_sprite_id_)) {
|
||||
active_sprites_.push_back(current_sprite_id_);
|
||||
}
|
||||
}
|
||||
Separator();
|
||||
}
|
||||
|
||||
for (const auto custom_sprite : custom_sprites_) {
|
||||
// Draw the custom sprite metadata
|
||||
Text("Sprite ID: %s", custom_sprite.property_sprid.Text.c_str());
|
||||
Text("Sprite Name: %s", custom_sprite.property_sprname.Text.c_str());
|
||||
Text("Sprite Palette: %s", custom_sprite.property_palette.Text.c_str());
|
||||
Separator();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
117
src/app/editor/sprite/sprite_editor.h
Normal file
117
src/app/editor/sprite/sprite_editor.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/sprite/zsprite.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
constexpr ImGuiTabItemFlags kSpriteTabFlags =
|
||||
ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip;
|
||||
|
||||
constexpr ImGuiTabBarFlags kSpriteTabBarFlags =
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton;
|
||||
|
||||
constexpr ImGuiTableFlags kSpriteTableFlags =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV;
|
||||
|
||||
/**
|
||||
* @class SpriteEditor
|
||||
* @brief Allows the user to edit sprites.
|
||||
*
|
||||
* This class provides functionality for updating the sprite editor, drawing the
|
||||
* editor table, drawing the sprite canvas, and drawing the current sheets.
|
||||
*/
|
||||
class SpriteEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
SpriteEditor() { type_ = EditorType::kSprite; }
|
||||
|
||||
/**
|
||||
* @brief Updates the sprite editor.
|
||||
*
|
||||
* @return An absl::Status indicating the success or failure of the update.
|
||||
*/
|
||||
absl::Status Update() override;
|
||||
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
private:
|
||||
void DrawVanillaSpriteEditor();
|
||||
|
||||
/**
|
||||
* @brief Draws the sprites list.
|
||||
*/
|
||||
void DrawSpritesList();
|
||||
|
||||
/**
|
||||
* @brief Draws the sprite canvas.
|
||||
*/
|
||||
void DrawSpriteCanvas();
|
||||
|
||||
/**
|
||||
* @brief Draws the current sheets.
|
||||
*/
|
||||
void DrawCurrentSheets();
|
||||
|
||||
void DrawCustomSprites();
|
||||
|
||||
void DrawCustomSpritesMetadata();
|
||||
|
||||
/**
|
||||
* @brief Draws the animation frames manager.
|
||||
*/
|
||||
void DrawAnimationFrames();
|
||||
|
||||
ImVector<int> active_sprites_; /**< Active sprites. */
|
||||
|
||||
int current_sprite_id_; /**< Current sprite ID. */
|
||||
uint8_t current_sheets_[8] = {0x00, 0x0A, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00};
|
||||
bool sheets_loaded_ =
|
||||
false; /**< Flag indicating whether the sheets are loaded or not. */
|
||||
|
||||
// OAM Configuration
|
||||
struct OAMConfig {
|
||||
uint16_t x; /**< X offset. */
|
||||
uint16_t y; /**< Y offset. */
|
||||
uint8_t tile; /**< Tile number. */
|
||||
uint8_t palette; /**< Palette number. */
|
||||
uint8_t priority; /**< Priority. */
|
||||
bool flip_x; /**< Flip X. */
|
||||
bool flip_y; /**< Flip Y. */
|
||||
};
|
||||
|
||||
OAMConfig oam_config_; /**< OAM configuration. */
|
||||
gui::Bitmap oam_bitmap_; /**< OAM bitmap. */
|
||||
|
||||
gui::Canvas sprite_canvas_{
|
||||
"SpriteCanvas", ImVec2(0x200, 0x200),
|
||||
gui::CanvasGridSize::k32x32}; /**< Sprite canvas. */
|
||||
|
||||
gui::Canvas graphics_sheet_canvas_{
|
||||
"GraphicsSheetCanvas", ImVec2(0x80 * 2 + 2, 0x40 * 8 + 2),
|
||||
gui::CanvasGridSize::k16x16}; /**< Graphics sheet canvas. */
|
||||
|
||||
std::vector<zsprite::ZSprite> custom_sprites_; /**< Sprites. */
|
||||
|
||||
absl::Status status_; /**< Status. */
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
396
src/app/editor/sprite/zsprite.h
Normal file
396
src/app/editor/sprite/zsprite.h
Normal file
@@ -0,0 +1,396 @@
|
||||
#ifndef YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
|
||||
#define YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
/**
|
||||
* @brief Namespace for the ZSprite format from Zarby's ZSpriteMaker.
|
||||
*/
|
||||
namespace zsprite {
|
||||
|
||||
struct OamTile {
|
||||
OamTile(uint8_t x, uint8_t y, bool mx, bool my, uint16_t id, uint8_t pal,
|
||||
bool s, uint8_t p)
|
||||
: x(x),
|
||||
y(y),
|
||||
mirror_x(mx),
|
||||
mirror_y(my),
|
||||
id(id),
|
||||
palette(pal),
|
||||
size(s),
|
||||
priority(p) {}
|
||||
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
bool mirror_x;
|
||||
bool mirror_y;
|
||||
uint16_t id;
|
||||
uint8_t palette;
|
||||
bool size;
|
||||
uint8_t priority;
|
||||
uint8_t z;
|
||||
};
|
||||
|
||||
struct AnimationGroup {
|
||||
AnimationGroup() = default;
|
||||
AnimationGroup(uint8_t fs, uint8_t fe, uint8_t fsp, std::string fn)
|
||||
: frame_start(fs), frame_end(fe), frame_speed(fsp), frame_name(fn) {}
|
||||
|
||||
std::string frame_name;
|
||||
uint8_t frame_start;
|
||||
uint8_t frame_end;
|
||||
uint8_t frame_speed;
|
||||
std::vector<OamTile> Tiles;
|
||||
};
|
||||
|
||||
struct UserRoutine {
|
||||
UserRoutine(std::string n, std::string c) : name(n), code(c) {}
|
||||
|
||||
std::string name;
|
||||
std::string code;
|
||||
int Count;
|
||||
};
|
||||
|
||||
struct SubEditor {
|
||||
std::vector<AnimationGroup> Frames;
|
||||
std::vector<UserRoutine> user_routines;
|
||||
};
|
||||
|
||||
struct SpriteProperty {
|
||||
bool IsChecked;
|
||||
std::string Text;
|
||||
};
|
||||
|
||||
struct ZSprite {
|
||||
public:
|
||||
absl::Status Load(const std::string& filename) {
|
||||
std::ifstream fs(filename, std::ios::binary);
|
||||
if (!fs.is_open()) {
|
||||
return absl::NotFoundError("File not found");
|
||||
}
|
||||
|
||||
std::vector<char> buffer(std::istreambuf_iterator<char>(fs), {});
|
||||
|
||||
int animation_count = *reinterpret_cast<int32_t*>(&buffer[0]);
|
||||
int offset = sizeof(int);
|
||||
|
||||
for (int i = 0; i < animation_count; i++) {
|
||||
std::string aname = std::string(&buffer[offset]);
|
||||
offset += aname.size() + 1;
|
||||
uint8_t afs = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
uint8_t afe = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
uint8_t afspeed = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
animations.push_back(AnimationGroup(afs, afe, afspeed, aname));
|
||||
}
|
||||
// RefreshAnimations();
|
||||
|
||||
int frame_count = *reinterpret_cast<int32_t*>(&buffer[offset]);
|
||||
offset += sizeof(int);
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
// editor.Frames[i] = new Frame();
|
||||
editor.Frames.emplace_back();
|
||||
// editor.AddUndo(i);
|
||||
int tCount = *reinterpret_cast<int*>(&buffer[offset]);
|
||||
offset += sizeof(int);
|
||||
|
||||
for (int j = 0; j < tCount; j++) {
|
||||
ushort tid = *reinterpret_cast<ushort*>(&buffer[offset]);
|
||||
offset += sizeof(ushort);
|
||||
uint8_t tpal = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
bool tmx = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
bool tmy = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
uint8_t tprior = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
bool tsize = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
uint8_t tx = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
uint8_t ty = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
uint8_t tz = *reinterpret_cast<uint8_t*>(&buffer[offset]);
|
||||
offset += sizeof(uint8_t);
|
||||
OamTile to(tx, ty, tmx, tmy, tid, tpal, tsize, tprior);
|
||||
to.z = tz;
|
||||
editor.Frames[i].Tiles.push_back(to);
|
||||
}
|
||||
}
|
||||
|
||||
// all sprites properties
|
||||
property_blockable.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_canfall.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_collisionlayer.IsChecked =
|
||||
*reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_customdeath.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_damagesound.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_deflectarrows.IsChecked =
|
||||
*reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_deflectprojectiles.IsChecked =
|
||||
*reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_fast.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_harmless.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_impervious.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_imperviousarrow.IsChecked =
|
||||
*reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_imperviousmelee.IsChecked =
|
||||
*reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_interaction.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_isboss.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_persist.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_shadow.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_smallshadow.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_statis.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_statue.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
property_watersprite.IsChecked = *reinterpret_cast<bool*>(&buffer[offset]);
|
||||
offset += sizeof(bool);
|
||||
|
||||
property_prize.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
property_palette.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
property_oamnbr.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
property_hitbox.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
property_health.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
property_damage.Text =
|
||||
std::to_string(*reinterpret_cast<uint8_t*>(&buffer[offset]));
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
if (offset != buffer.size()) {
|
||||
property_sprname.Text = std::string(&buffer[offset]);
|
||||
offset += property_sprname.Text.size() + 1;
|
||||
|
||||
int actionL = buffer[offset];
|
||||
offset += sizeof(int);
|
||||
for (int i = 0; i < actionL; i++) {
|
||||
std::string a = std::string(&buffer[offset]);
|
||||
offset += a.size() + 1;
|
||||
std::string b = std::string(&buffer[offset]);
|
||||
offset += b.size() + 1;
|
||||
userRoutines.push_back(UserRoutine(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != buffer.size()) {
|
||||
property_sprid.Text = std::string(&buffer[offset]);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// UpdateUserRoutines();
|
||||
// userroutinesListbox.SelectedIndex = 0;
|
||||
// RefreshScreen();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Save(const std::string& filename) {
|
||||
std::ofstream fs(filename, std::ios::binary);
|
||||
if (fs.is_open()) {
|
||||
// Write data to the file
|
||||
fs.write(reinterpret_cast<const char*>(animations.size()), sizeof(int));
|
||||
for (const AnimationGroup& anim : animations) {
|
||||
fs.write(anim.frame_name.c_str(), anim.frame_name.size() + 1);
|
||||
fs.write(reinterpret_cast<const char*>(&anim.frame_start),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&anim.frame_end),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&anim.frame_speed),
|
||||
sizeof(uint8_t));
|
||||
}
|
||||
|
||||
fs.write(reinterpret_cast<const char*>(editor.Frames.size()),
|
||||
sizeof(int));
|
||||
for (int i = 0; i < editor.Frames.size(); i++) {
|
||||
fs.write(reinterpret_cast<const char*>(editor.Frames[i].Tiles.size()),
|
||||
sizeof(int));
|
||||
|
||||
for (int j = 0; j < editor.Frames[i].Tiles.size(); j++) {
|
||||
fs.write(reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].id),
|
||||
sizeof(ushort));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].palette),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(
|
||||
&editor.Frames[i].Tiles[j].mirror_x),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(
|
||||
&editor.Frames[i].Tiles[j].mirror_y),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(
|
||||
&editor.Frames[i].Tiles[j].priority),
|
||||
sizeof(uint8_t));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].size),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].x),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].y),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&editor.Frames[i].Tiles[j].z),
|
||||
sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
|
||||
// Write other properties
|
||||
fs.write(reinterpret_cast<const char*>(&property_blockable.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_canfall.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&property_collisionlayer.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_customdeath.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_damagesound.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_deflectarrows.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&property_deflectprojectiles.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_fast.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_harmless.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_impervious.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&property_imperviousarrow.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(
|
||||
reinterpret_cast<const char*>(&property_imperviousmelee.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_interaction.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_isboss.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_persist.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_shadow.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_smallshadow.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_statis.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_statue.IsChecked),
|
||||
sizeof(bool));
|
||||
fs.write(reinterpret_cast<const char*>(&property_watersprite.IsChecked),
|
||||
sizeof(bool));
|
||||
|
||||
fs.write(reinterpret_cast<const char*>(&property_prize.Text),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&property_palette.Text),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&property_oamnbr.Text),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&property_hitbox.Text),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&property_health.Text),
|
||||
sizeof(uint8_t));
|
||||
fs.write(reinterpret_cast<const char*>(&property_damage.Text),
|
||||
sizeof(uint8_t));
|
||||
|
||||
fs.write(sprName.c_str(), sprName.size() + 1);
|
||||
|
||||
fs.write(reinterpret_cast<const char*>(userRoutines.size()), sizeof(int));
|
||||
for (const UserRoutine& userR : userRoutines) {
|
||||
fs.write(userR.name.c_str(), userR.name.size() + 1);
|
||||
fs.write(userR.code.c_str(), userR.code.size() + 1);
|
||||
}
|
||||
|
||||
fs.write(reinterpret_cast<const char*>(&property_sprid.Text),
|
||||
sizeof(property_sprid.Text));
|
||||
|
||||
fs.close();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::string sprName;
|
||||
std::vector<AnimationGroup> animations;
|
||||
std::vector<UserRoutine> userRoutines;
|
||||
SubEditor editor;
|
||||
|
||||
SpriteProperty property_blockable;
|
||||
SpriteProperty property_canfall;
|
||||
SpriteProperty property_collisionlayer;
|
||||
SpriteProperty property_customdeath;
|
||||
SpriteProperty property_damagesound;
|
||||
SpriteProperty property_deflectarrows;
|
||||
SpriteProperty property_deflectprojectiles;
|
||||
SpriteProperty property_fast;
|
||||
SpriteProperty property_harmless;
|
||||
SpriteProperty property_impervious;
|
||||
SpriteProperty property_imperviousarrow;
|
||||
SpriteProperty property_imperviousmelee;
|
||||
SpriteProperty property_interaction;
|
||||
SpriteProperty property_isboss;
|
||||
SpriteProperty property_persist;
|
||||
SpriteProperty property_shadow;
|
||||
SpriteProperty property_smallshadow;
|
||||
SpriteProperty property_statis;
|
||||
SpriteProperty property_statue;
|
||||
SpriteProperty property_watersprite;
|
||||
SpriteProperty property_sprname;
|
||||
|
||||
SpriteProperty property_prize;
|
||||
SpriteProperty property_palette;
|
||||
SpriteProperty property_oamnbr;
|
||||
SpriteProperty property_hitbox;
|
||||
SpriteProperty property_health;
|
||||
SpriteProperty property_damage;
|
||||
|
||||
SpriteProperty property_sprid;
|
||||
};
|
||||
|
||||
} // namespace zsprite
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "sprite_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::Button;
|
||||
using ImGui::Separator;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
absl::Status SpriteEditor::Update() {
|
||||
if (rom()->is_loaded() && !sheets_loaded_) {
|
||||
// Load the values for current_sheets_ array
|
||||
|
||||
sheets_loaded_ = true;
|
||||
}
|
||||
|
||||
// if (ImGui::BeginTable({"Canvas", "Graphics"}, 2, nullptr, ImVec2(0, 0))) {
|
||||
// TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
|
||||
// ImGui::GetContentRegionAvail().x);
|
||||
// TableSetupColumn("Tile Selector", ImGuiTableColumnFlags_WidthFixed, 256);
|
||||
// TableHeadersRow();
|
||||
// TableNextRow();
|
||||
// TableNextColumn();
|
||||
// DrawSpriteCanvas();
|
||||
|
||||
// TableNextColumn();
|
||||
// if (sheets_loaded_) {
|
||||
// DrawCurrentSheets();
|
||||
// }
|
||||
|
||||
// ImGui::EndTable();
|
||||
// }
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void SpriteEditor::DrawEditorTable() {}
|
||||
|
||||
void SpriteEditor::DrawSpriteCanvas() {}
|
||||
|
||||
void SpriteEditor::DrawCurrentSheets() {
|
||||
static gui::Canvas graphics_sheet_canvas;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)7);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
|
||||
graphics_sheet_canvas.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8));
|
||||
ImGui::PopStyleVar(2);
|
||||
graphics_sheet_canvas.DrawContextMenu();
|
||||
graphics_sheet_canvas.DrawBitmap(
|
||||
*rom()->bitmap_manager()[current_sheets_[i]], 2, 2);
|
||||
graphics_sheet_canvas.DrawGrid(64.0f);
|
||||
graphics_sheet_canvas.DrawOverlay();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @class SpriteEditor
|
||||
* @brief Allows the user to edit sprites.
|
||||
*
|
||||
* This class provides functionality for updating the sprite editor, drawing the
|
||||
* editor table, drawing the sprite canvas, and drawing the current sheets.
|
||||
*/
|
||||
class SpriteEditor : public SharedRom {
|
||||
public:
|
||||
/**
|
||||
* @brief Updates the sprite editor.
|
||||
*
|
||||
* @return An absl::Status indicating the success or failure of the update.
|
||||
*/
|
||||
absl::Status Update();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Draws the editor table.
|
||||
*/
|
||||
void DrawEditorTable();
|
||||
|
||||
/**
|
||||
* @brief Draws the sprite canvas.
|
||||
*/
|
||||
void DrawSpriteCanvas();
|
||||
|
||||
/**
|
||||
* @brief Draws the current sheets.
|
||||
*/
|
||||
void DrawCurrentSheets();
|
||||
|
||||
uint8_t current_sheets_[8]; /**< Array to store the current sheets. */
|
||||
bool sheets_loaded_ =
|
||||
false; /**< Flag indicating whether the sheets are loaded or not. */
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SPRITE_EDITOR_H
|
||||
@@ -12,6 +12,24 @@ namespace app {
|
||||
*/
|
||||
namespace editor {
|
||||
|
||||
enum class EditorType {
|
||||
kAssembly,
|
||||
kDungeon,
|
||||
kGraphics,
|
||||
kMusic,
|
||||
kOverworld,
|
||||
kPalette,
|
||||
kScreen,
|
||||
kSprite,
|
||||
kMessage,
|
||||
kSettings,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 10> kEditorNames = {
|
||||
"Assembly", "Dungeon", "Graphics", "Music", "Overworld",
|
||||
"Palette", "Screen", "Sprite", "Message", "Settings",
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Editor
|
||||
* @brief Interface for editor classes.
|
||||
@@ -31,6 +49,13 @@ class Editor {
|
||||
virtual absl::Status Redo() = 0;
|
||||
|
||||
virtual absl::Status Update() = 0;
|
||||
|
||||
virtual absl::Status Find() = 0;
|
||||
|
||||
EditorType type() const { return type_; }
|
||||
|
||||
protected:
|
||||
EditorType type_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
68
src/app/editor/utils/flags.h
Normal file
68
src/app/editor/utils/flags.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_FLAGS_H
|
||||
#define YAZE_APP_EDITOR_UTILS_FLAGS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "core/common.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginMenu;
|
||||
using ImGui::Checkbox;
|
||||
using ImGui::EndMenu;
|
||||
using ImGui::MenuItem;
|
||||
using ImGui::Separator;
|
||||
|
||||
struct FlagsMenu : public core::ExperimentFlags {
|
||||
void Draw() {
|
||||
if (BeginMenu("Overworld Flags")) {
|
||||
Checkbox("Enable Overworld Sprites",
|
||||
&mutable_flags()->overworld.kDrawOverworldSprites);
|
||||
Separator();
|
||||
Checkbox("Save Overworld Maps",
|
||||
&mutable_flags()->overworld.kSaveOverworldMaps);
|
||||
Checkbox("Save Overworld Entrances",
|
||||
&mutable_flags()->overworld.kSaveOverworldEntrances);
|
||||
Checkbox("Save Overworld Exits",
|
||||
&mutable_flags()->overworld.kSaveOverworldExits);
|
||||
Checkbox("Save Overworld Items",
|
||||
&mutable_flags()->overworld.kSaveOverworldItems);
|
||||
Checkbox("Save Overworld Properties",
|
||||
&mutable_flags()->overworld.kSaveOverworldProperties);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (BeginMenu("Dungeon Flags")) {
|
||||
Checkbox("Draw Dungeon Room Graphics",
|
||||
&mutable_flags()->kDrawDungeonRoomGraphics);
|
||||
Separator();
|
||||
Checkbox("Save Dungeon Maps", &mutable_flags()->kSaveDungeonMaps);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (BeginMenu("Emulator Flags")) {
|
||||
Checkbox("Load Audio Device", &mutable_flags()->kLoadAudioDevice);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
Checkbox("Use built-in file dialog",
|
||||
&mutable_flags()->kNewFileDialogWrapper);
|
||||
Checkbox("Enable Console Logging", &mutable_flags()->kLogToConsole);
|
||||
Checkbox("Enable Texture Streaming",
|
||||
&mutable_flags()->kLoadTexturesAsStreaming);
|
||||
Checkbox("Use Bitmap Manager", &mutable_flags()->kUseBitmapManager);
|
||||
Checkbox("Log Instructions to Debugger",
|
||||
&mutable_flags()->kLogInstructions);
|
||||
Checkbox("Save All Palettes", &mutable_flags()->kSaveAllPalettes);
|
||||
Checkbox("Save Gfx Groups", &mutable_flags()->kSaveGfxGroups);
|
||||
Checkbox("Use New ImGui Input", &mutable_flags()->kUseNewImGuiInput);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UTILS_FLAGS_H_
|
||||
@@ -1,17 +1,16 @@
|
||||
#include "app/editor/context/gfx_context.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -1,19 +1,17 @@
|
||||
#ifndef YAZE_APP_EDITOR_VRAM_CONTEXT_H
|
||||
#define YAZE_APP_EDITOR_VRAM_CONTEXT_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/pipeline.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -25,9 +23,6 @@ namespace context {
|
||||
* @brief Shared graphical context across editors.
|
||||
*/
|
||||
class GfxContext {
|
||||
public:
|
||||
absl::Status Update();
|
||||
|
||||
protected:
|
||||
// Palettesets for the tile16 individual tiles
|
||||
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
|
||||
25
src/app/editor/utils/keyboard_shortcuts.h
Normal file
25
src/app/editor/utils/keyboard_shortcuts.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
|
||||
#define YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
struct KeyboardShortcuts {
|
||||
enum class ShortcutType {
|
||||
kCut,
|
||||
kCopy,
|
||||
kPaste,
|
||||
kUndo,
|
||||
kRedo,
|
||||
kFind,
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H_
|
||||
64
src/app/editor/utils/recent_files.h
Normal file
64
src/app/editor/utils/recent_files.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
#define YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class RecentFilesManager {
|
||||
public:
|
||||
RecentFilesManager(const std::string& filename) : filename_(filename) {}
|
||||
|
||||
void AddFile(const std::string& filePath) {
|
||||
// Add a file to the list, avoiding duplicates
|
||||
auto it = std::find(recentFiles_.begin(), recentFiles_.end(), filePath);
|
||||
if (it == recentFiles_.end()) {
|
||||
recentFiles_.push_back(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void Save() {
|
||||
std::ofstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return; // Handle the error appropriately
|
||||
}
|
||||
|
||||
for (const auto& filePath : recentFiles_) {
|
||||
file << filePath << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Load() {
|
||||
std::ifstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return; // Handle the error appropriately
|
||||
}
|
||||
|
||||
recentFiles_.clear();
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (!line.empty()) {
|
||||
recentFiles_.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetRecentFiles() const {
|
||||
return recentFiles_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
std::vector<std::string> recentFiles_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
Reference in New Issue
Block a user