backend-infra-engineer: Pre-0.2.2 2024 Q2 snapshot

This commit is contained in:
scawful
2024-04-14 15:49:57 -05:00
parent 546093360f
commit 92cc574e15
113 changed files with 5631 additions and 1872 deletions

View File

@@ -0,0 +1,42 @@
#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_

View File

@@ -4,23 +4,24 @@
#include <cmath>
#include "app/core/editor.h"
#include "app/gui/pipeline.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 {
namespace app {
namespace editor {
namespace context {
std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -6,7 +6,7 @@
#include <cmath>
#include <vector>
#include "app/core/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"
@@ -19,7 +19,11 @@
namespace yaze {
namespace app {
namespace editor {
namespace context {
/**
* @brief Shared graphical context across editors.
*/
class GfxContext {
public:
absl::Status Update();
@@ -29,6 +33,7 @@ class GfxContext {
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
};
} // namespace context
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -30,60 +30,12 @@ using ImGui::TableSetupColumn;
absl::Status DungeonEditor::Update() {
if (!is_loaded_ && rom()->is_loaded()) {
for (int i = 0; i < 0x100 + 40; i++) {
rooms_.emplace_back(zelda3::dungeon::Room(i));
rooms_[i].LoadHeader();
rooms_[i].LoadRoomFromROM();
if (flags()->kDrawDungeonRoomGraphics) {
rooms_[i].LoadRoomGraphics();
}
room_size_pointers_.push_back(rooms_[i].room_size_ptr());
if (rooms_[i].room_size_ptr() != 0x0A8000) {
room_size_addresses_[i] = rooms_[i].room_size_ptr();
}
auto dungeon_palette_ptr = rom()->paletteset_ids[rooms_[i].palette][0];
ASSIGN_OR_RETURN(auto palette_id,
rom()->ReadWord(0xDEC4B + dungeon_palette_ptr));
int p_id = palette_id / 180;
auto color = rom()->palette_group("dungeon_main")[p_id][3];
room_palette_[rooms_[i].palette] = color.rgb();
}
LoadDungeonRoomSize();
LoadRoomEntrances();
// Load the palette group and palette for the dungeon
full_palette_ =
rom()->palette_group("dungeon_main")[current_palette_group_id_];
ASSIGN_OR_RETURN(current_palette_group_,
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
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());
}
RETURN_IF_ERROR(Initialize());
is_loaded_ = true;
}
if (refresh_graphics_) {
for (int i = 0; i < 8; i++) {
int block = rooms_[current_room_id_].blocks()[i];
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
current_palette_group_[current_palette_id_], 0);
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
}
for (int i = 9; i < 16; i++) {
int block = rooms_[current_room_id_].blocks()[i];
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
rom()->palette_group("sprites_aux1")[current_palette_id_], 0);
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
}
RETURN_IF_ERROR(RefreshGraphics());
refresh_graphics_ = false;
}
@@ -106,6 +58,62 @@ absl::Status DungeonEditor::Update() {
return absl::OkStatus();
}
absl::Status DungeonEditor::Initialize() {
auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;
for (int i = 0; i < 0x100 + 40; i++) {
rooms_.emplace_back(zelda3::dungeon::Room(i));
rooms_[i].LoadHeader();
rooms_[i].LoadRoomFromROM();
if (flags()->kDrawDungeonRoomGraphics) {
rooms_[i].LoadRoomGraphics();
}
room_size_pointers_.push_back(rooms_[i].room_size_ptr());
if (rooms_[i].room_size_ptr() != 0x0A8000) {
room_size_addresses_[i] = rooms_[i].room_size_ptr();
}
auto dungeon_palette_ptr = rom()->paletteset_ids[rooms_[i].palette][0];
ASSIGN_OR_RETURN(auto palette_id,
rom()->ReadWord(0xDEC4B + dungeon_palette_ptr));
int p_id = palette_id / 180;
auto color = dungeon_man_pal_group[p_id][3];
room_palette_[rooms_[i].palette] = color.rgb();
}
LoadDungeonRoomSize();
LoadRoomEntrances();
// Load the palette group and palette for the dungeon
full_palette_ = dungeon_man_pal_group[current_palette_group_id_];
ASSIGN_OR_RETURN(current_palette_group_,
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
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());
}
return absl::OkStatus();
}
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(
current_palette_group_[current_palette_id_], 0));
rom()->UpdateBitmap(graphics_bin_[block].get(), 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 absl::OkStatus();
}
void DungeonEditor::LoadDungeonRoomSize() {
std::map<int, std::vector<int>> rooms_by_bank;
for (const auto& room : room_size_addresses_) {
@@ -148,13 +156,13 @@ void DungeonEditor::LoadDungeonRoomSize() {
}
}
void DungeonEditor::UpdateDungeonRoomView() {
absl::Status DungeonEditor::UpdateDungeonRoomView() {
DrawToolset();
if (palette_showing_) {
ImGui::Begin("Palette Editor", &palette_showing_, 0);
current_palette_ =
rom()->palette_group("dungeon_main")[current_palette_group_id_];
auto dungeon_main_pal_group = rom()->palette_group().dungeon_main;
current_palette_ = dungeon_main_pal_group[current_palette_group_id_];
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
current_palette_);
ImGui::End();
@@ -186,6 +194,7 @@ void DungeonEditor::UpdateDungeonRoomView() {
DrawTileSelector();
ImGui::EndTable();
}
return absl::OkStatus();
}
void DungeonEditor::DrawToolset() {
@@ -483,7 +492,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
void DungeonEditor::DrawRoomGraphics() {
const auto height = 0x40;
room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
room_gfx_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x10 * 0x40 + 1));
room_gfx_canvas_.DrawContextMenu();
room_gfx_canvas_.DrawTileSelector(32);
if (is_loaded_) {
@@ -593,26 +602,19 @@ void DungeonEditor::LoadRoomEntrances() {
// ============================================================================
void DungeonEditor::CalculateUsageStats() {
// Create a hash map of the usage for elements of each Dungeon Room such as
// the blockset, spriteset, palette, etc. This is so we can keep track of
// which graphics sets and palette sets are in use and which are not.
for (const auto& room : rooms_) {
// Blockset
if (blockset_usage_.find(room.blockset) == blockset_usage_.end()) {
blockset_usage_[room.blockset] = 1;
} else {
blockset_usage_[room.blockset] += 1;
}
// Spriteset
if (spriteset_usage_.find(room.spriteset) == spriteset_usage_.end()) {
spriteset_usage_[room.spriteset] = 1;
} else {
spriteset_usage_[room.spriteset] += 1;
}
// Palette
if (palette_usage_.find(room.palette) == palette_usage_.end()) {
palette_usage_[room.palette] = 1;
} else {
@@ -756,22 +758,14 @@ void DungeonEditor::DrawUsageStats() {
}
void DungeonEditor::DrawUsageGrid() {
// Create a grid of 295 small squares which is 16 squares wide
// Each square represents a room in the game
// When you hover a square it should show a hover tooltip with the properties
// of the room such as the blockset, spriteset, palette, etc. Calculate the
// number of rows
int totalSquares = 296;
int squaresWide = 16;
int squaresTall = (totalSquares + squaresWide - 1) /
squaresWide; // Ceiling of totalSquares/squaresWide
// Loop through each row
for (int row = 0; row < squaresTall; ++row) {
// Start a new line for each row
ImGui::NewLine();
// Loop through each column in the row
for (int col = 0; col < squaresWide; ++col) {
// Check if we have reached 295 squares
if (row * squaresWide + col >= totalSquares) {

View File

@@ -4,7 +4,7 @@
#include <imgui/imgui.h>
#include "app/core/common.h"
#include "app/core/editor.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"
@@ -32,8 +32,18 @@ constexpr ImGuiTableFlags kDungeonTableFlags =
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV;
/**
* @brief DungeonEditor class for editing dungeons.
*
* This class is currently a work in progress and is used for editing dungeons.
* It provides various functions for updating, cutting, copying, pasting,
* undoing, and redoing. It also includes methods for drawing the toolset, room
* selector, entrance selector, dungeon tab view, dungeon canvas, room graphics,
* tile selector, and object renderer. Additionally, it handles loading room
* entrances, calculating usage statistics, and rendering set usage.
*/
class DungeonEditor : public Editor,
public SharedROM,
public SharedRom,
public core::ExperimentFlags {
public:
absl::Status Update() override;
@@ -46,9 +56,12 @@ class DungeonEditor : public Editor,
void add_room(int i) { active_rooms_.push_back(i); }
private:
absl::Status Initialize();
absl::Status RefreshGraphics();
void LoadDungeonRoomSize();
void UpdateDungeonRoomView();
absl::Status UpdateDungeonRoomView();
void DrawToolset();
void DrawRoomSelector();

View File

@@ -51,12 +51,13 @@ absl::Status GraphicsEditor::Update() {
}
absl::Status GraphicsEditor::UpdateGfxEdit() {
TAB_ITEM("Graphics Editor")
TAB_ITEM("Sheet Editor")
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) {
for (const auto& name : kGfxEditColumnNames)
ImGui::TableSetupColumn(name.data());
for (const auto& name :
{"Tilesheets", "Current Graphics", "Palette Controls"})
ImGui::TableSetupColumn(name);
ImGui::TableHeadersRow();
@@ -321,10 +322,10 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
}
absl::Status GraphicsEditor::UpdatePaletteColumn() {
auto palette_group = rom()->palette_group(
auto palette_group = *rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
auto palette = palette_group[edit_palette_index_];
auto palette = palette_group.palette(edit_palette_index_);
if (rom()->is_loaded()) {
gui::TextWithSeparators("ROM Palette");
@@ -339,11 +340,13 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
palette);
if (refresh_graphics_) {
rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent(
palette, edit_palette_sub_index_);
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());
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get(),
true);
refresh_graphics_ = false;
}
@@ -353,22 +356,44 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
absl::Status GraphicsEditor::UpdateLinkGfxView() {
TAB_ITEM("Player Animations")
const auto link_gfx_offset = 0x80000;
const auto link_gfx_length = 0x7000;
if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) {
for (const auto& name : {"Canvas", "Animation Steps", "Properties"})
ImGui::TableSetupColumn(name);
// TODO: Finish Rom::LoadLinkGraphics and implement this
if (ImGui::Button("Load Link Graphics (Experimental)")) {
if (rom()->is_loaded()) {
// Load Links graphics from the ROM
rom()->LoadLinkGraphics();
ImGui::TableHeadersRow();
// Split it into the pose data frames
// Create an animation step display for the poses
// Allow the user to modify the frames used in an anim step
// LinkOAM_AnimationSteps:
// #_0D85FB
NEXT_COLUMN();
link_canvas_.DrawBackground();
link_canvas_.DrawGrid(16.0f);
int i = 0;
for (auto [key, link_sheet] : rom()->link_graphics()) {
int x_offset = 0;
int y_offset = core::kTilesheetHeight * i * 4;
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
i++;
}
link_canvas_.DrawOverlay();
link_canvas_.DrawGrid();
NEXT_COLUMN();
ImGui::Text("Placeholder");
NEXT_COLUMN();
if (ImGui::Button("Load Link Graphics (Experimental)")) {
if (rom()->is_loaded()) {
// Load Links graphics from the ROM
RETURN_IF_ERROR(rom()->LoadLinkGraphics());
// Split it into the pose data frames
// Create an animation step display for the poses
// Allow the user to modify the frames used in an anim step
// LinkOAM_AnimationSteps:
// #_0D85FB
}
}
}
ImGui::EndTable();
END_TAB_ITEM()
return absl::OkStatus();
@@ -477,8 +502,8 @@ absl::Status GraphicsEditor::DrawCgxImport() {
[this]() { ImGui::SetClipboardText(cgx_file_path_); });
gui::ButtonPipe("Load CGX Data", [this]() {
status_ = gfx::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
decoded_cgx_, extra_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_);
if (col_file_) {
@@ -508,11 +533,12 @@ absl::Status GraphicsEditor::DrawScrImport() {
InputInt("SCR Mod", &scr_mod_value_);
gui::ButtonPipe("Load Scr Data", [this]() {
status_ = gfx::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
status_ =
gfx::scad_format::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
decoded_scr_data_.resize(0x100 * 0x100);
status_ = gfx::DrawScrWithCgx(current_bpp_, scr_data_, decoded_scr_data_,
decoded_cgx_);
status_ = gfx::scad_format::DrawScrWithCgx(current_bpp_, scr_data_,
decoded_scr_data_, decoded_cgx_);
scr_bitmap_.InitializeFromData(0x100, 0x100, 8, decoded_scr_data_);
if (scr_loaded_) {
@@ -551,7 +577,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
col_file_palette_ = gfx::SnesPalette(col_data_);
// gigaleak dev format based code
decoded_col_ = gfx::DecodeColFile(col_file_path_);
decoded_col_ = gfx::scad_format::DecodeColFile(col_file_path_);
col_file_ = true;
is_open_ = true;
});
@@ -711,7 +737,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
converted_sheet);
if (rom()->is_loaded()) {
auto palette_group = rom()->palette_group("ow_main");
auto palette_group = rom()->palette_group().overworld_animated;
z3_rom_palette_ = palette_group[current_palette_];
if (col_file_) {
bin_bitmap_.ApplyPalette(col_file_palette_);
@@ -743,9 +769,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group =
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = palette_group[current_palette_index_];
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_);
}
@@ -768,9 +795,9 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group =
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = palette_group[current_palette_index_];
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_);
}

View File

@@ -15,7 +15,7 @@
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -50,9 +50,6 @@ constexpr const char* kPaletteGroupAddressesKeys[] = {
"grass", "3d_object", "ow_mini_map",
};
static constexpr std::string_view kGfxEditColumnNames[] = {
"Tilesheets", "Current Graphics", "Palette Controls"};
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
"#memoryEditor",
"##separator_gfx1",
@@ -62,7 +59,21 @@ constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
class GraphicsEditor : public SharedROM {
/**
* @class GraphicsEditor
* @brief Allows the user to edit graphics sheets from the game or view
* prototype graphics.
*
* The GraphicsEditor class is responsible for providing functionality to edit
* graphics sheets from the game or view prototype graphics of Link to the Past
* from the CGX, SCR, and OBJ formats. It provides various methods to update
* different components of the graphics editor, such as the graphics edit tab,
* link graphics view, and prototype graphics viewer. It also includes import
* functions for different file formats, as well as other utility functions for
* drawing toolsets, palette controls, clipboard imports, experimental features,
* and memory editor.
*/
class GraphicsEditor : public SharedRom {
public:
absl::Status Update();
@@ -154,9 +165,9 @@ class GraphicsEditor : public SharedROM {
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
ROM temp_rom_;
ROM tilemap_rom_;
zelda3::Overworld overworld_;
Rom temp_rom_;
Rom tilemap_rom_;
zelda3::overworld::Overworld overworld_;
MemoryEditor cgx_memory_editor_;
MemoryEditor col_memory_editor_;
PaletteEditor palette_editor_;
@@ -184,6 +195,9 @@ class GraphicsEditor : public SharedROM {
gui::Canvas super_donkey_canvas_;
gui::Canvas current_sheet_canvas_{ImVec2(0x80, 0x20),
gui::CanvasGridSize::k8x8};
gui::Canvas link_canvas_{
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
gui::CanvasGridSize::k16x16};
absl::Status status_;
};

View File

@@ -151,6 +151,8 @@ absl::Status MasterEditor::Update() {
DrawInfoPopup();
if (rom()->is_loaded() && !rom_assets_loaded_) {
// Load all of the graphics data from the game.
RETURN_IF_ERROR(rom()->LoadAllGraphicsData())
// Initialize overworld graphics, maps, and palettes
RETURN_IF_ERROR(overworld_editor_.LoadGraphics());
rom_assets_loaded_ = true;
@@ -464,7 +466,43 @@ void MasterEditor::DrawViewMenu() {
if (show_memory_editor) {
static MemoryEditor mem_edit;
mem_edit.DrawWindow("Memory Editor", (void*)&(*rom()), rom()->size());
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);
ImGui::SameLine();
ImGui::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()
ImGui::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");
ImGui::Text("%s", comparison_rom.filename().data());
comp_edit.DrawContents((void*)&(comparison_rom), comparison_rom.size());
ImGui::EndChild();
ImGui::EndGroup();
}
END_TABLE()
ImGui::End();
}
if (show_imgui_demo) {

View File

@@ -12,7 +12,6 @@
#include "absl/status/status.h"
#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/gui/pipeline.h"
#include "app/editor/context/gfx_context.h"
#include "app/editor/dungeon_editor.h"
#include "app/editor/graphics_editor.h"
@@ -28,14 +27,33 @@
#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 {
namespace app {
namespace editor {
class MasterEditor : public SharedROM,
public GfxContext,
/**
* @class MasterEditor
* @brief The MasterEditor class represents the main editor for a Rom in the
* Yaze application.
*
* This class inherits from SharedRom, GfxContext, and ExperimentFlags, and
* provides functionality for setting up the screen, updating the editor, and
* shutting down the editor. It also includes methods for drawing various menus
* and popups, saving the Rom, and managing editor-specific flags.
*
* The MasterEditor class contains instances of various editor classes such as
* AssemblyEditor, DungeonEditor, GraphicsEditor, MusicEditor, OverworldEditor,
* PaletteEditor, ScreenEditor, and SpriteEditor. The current_editor_ member
* variable points to the currently active editor in the tab view.
*
* @note This class assumes the presence of an SDL_Renderer object for rendering
* graphics.
*/
class MasterEditor : public SharedRom,
public context::GfxContext,
public core::ExperimentFlags {
public:
MasterEditor() { current_editor_ = &overworld_editor_; }

View File

@@ -12,6 +12,10 @@ namespace yaze {
namespace app {
namespace editor {
/**
* @class AssemblyEditor
* @brief Text editor for modifying assembly code.
*/
class AssemblyEditor {
public:
AssemblyEditor();

View File

@@ -6,8 +6,8 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.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"
@@ -18,7 +18,7 @@
#include "app/gui/pipeline.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -220,29 +220,20 @@ void GfxGroupEditor::DrawPaletteViewer() {
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")[rom()->paletteset_ids[selected_paletteset][0]]
.mutable_palette(0);
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")[rom()->paletteset_ids[selected_paletteset][1]]
.mutable_palette(0);
*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")[rom()->paletteset_ids[selected_paletteset][2]]
.mutable_palette(0);
*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")[rom()->paletteset_ids[selected_paletteset][3]]
.mutable_palette(0);
*rom()->mutable_palette_group()->sprites_aux3.mutable_palette(
rom()->paletteset_ids[selected_paletteset][3]);
DrawPaletteFromPaletteGroup(spr_aux_pal3);
}

View File

@@ -7,7 +7,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/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"
@@ -17,13 +17,17 @@
#include "app/gui/pipeline.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
namespace editor {
class GfxGroupEditor : public SharedROM {
/**
* @class GfxGroupEditor
* @brief Manage graphics group configurations in a Rom.
*/
class GfxGroupEditor : public SharedRom {
public:
absl::Status Update();
@@ -61,7 +65,7 @@ class GfxGroupEditor : public SharedROM {
std::vector<gfx::Bitmap> tile16_individual_;
gui::BitmapViewer gfx_group_viewer_;
zelda3::Overworld overworld_;
zelda3::overworld::Overworld overworld_;
};
} // namespace editor

View File

@@ -54,7 +54,12 @@ static const char* kGameSongs[] = {"Title",
static constexpr absl::string_view kSongNotes[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
class MusicEditor : public SharedROM {
/**
* @class MusicEditor
* @brief A class for editing music data in a Rom.
*/
class MusicEditor : public SharedRom {
public:
void Update();
@@ -65,7 +70,7 @@ class MusicEditor : public SharedROM {
void DrawSongToolset();
void DrawToolset();
zelda3::Tracker music_tracker_;
zelda3::music::Tracker music_tracker_;
// Mix_Music* current_song_ = NULL;

View File

@@ -75,30 +75,32 @@ absl::Status PaletteEditor::Update() {
return absl::OkStatus();
}
void PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette, int index) {
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
int index) {
if (index >= palette.size()) {
// Handle error: the index is out of bounds
return;
return absl::InvalidArgumentError("Index out of bounds");
}
// Get the current color
auto currentColor = palette.GetColor(index).rgb();
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();
}
void PaletteEditor::ResetColorToOriginal(
absl::Status PaletteEditor::ResetColorToOriginal(
gfx::SnesPalette& palette, int index,
const gfx::SnesPalette& originalPalette) {
if (index >= palette.size() || index >= originalPalette.size()) {
// Handle error: the index is out of bounds
return;
return absl::InvalidArgumentError("Index out of bounds");
}
auto originalColor = originalPalette.GetColor(index).rgb();
ASSIGN_OR_RETURN(auto color, originalPalette.GetColor(index));
auto originalColor = color.rgb();
palette(index, originalColor);
return absl::OkStatus();
}
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
@@ -106,17 +108,17 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category) {
return absl::NotFoundError("ROM not open, no palettes to display");
}
const auto size =
rom()->palette_group(kPaletteGroupNames[category].data()).size();
auto palettes =
rom()->mutable_palette_group(kPaletteGroupNames[category].data());
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 = palettes->mutable_palette(j);
auto palette = palette_group.mutable_palette(j);
auto pal_size = palette->size();
for (int n = 0; n < pal_size; n++) {
@@ -129,7 +131,7 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category) {
// Small icon of the color in the palette
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
palette_button_flags)) {
current_color_ = palette->GetColor(n);
ASSIGN_OR_RETURN(current_color_, palette->GetColor(n));
// EditColorInPalette(*palette, n);
}
@@ -152,8 +154,7 @@ 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)) {
RETURN_IF_ERROR(rom()->UpdatePaletteColor(kPaletteGroupNames[i].data(), j,
n, palette[n]))
// TODO: Implement new update color function
}
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");

View File

@@ -25,6 +25,7 @@ static constexpr absl::string_view kPaletteGroupNames[] = {
"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;
size_t palette_index;
@@ -74,15 +75,20 @@ class PaletteEditorHistory {
std::deque<PaletteChange> recentChanges;
static const size_t maxHistorySize = 50; // or any other number you deem fit
};
} // namespace palette_internal
class PaletteEditor : public SharedROM {
/**
* @class PaletteEditor
* @brief Allows the user to view and edit in game palettes.
*/
class PaletteEditor : public SharedRom {
public:
absl::Status Update();
absl::Status DrawPaletteGroups();
void EditColorInPalette(gfx::SnesPalette& palette, int index);
void ResetColorToOriginal(gfx::SnesPalette& palette, int index,
const gfx::SnesPalette& originalPalette);
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);
@@ -90,18 +96,20 @@ class PaletteEditor : public SharedROM {
private:
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
void InitializeSavedPalette(const gfx::SnesPalette& palette) {
absl::Status InitializeSavedPalette(const gfx::SnesPalette& palette) {
for (int n = 0; n < palette.size(); n++) {
saved_palette_[n].x = palette.GetColor(n).rgb().x / 255;
saved_palette_[n].y = palette.GetColor(n).rgb().y / 255;
saved_palette_[n].z = palette.GetColor(n).rgb().z / 255;
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
saved_palette_[n].x = color.rgb().x / 255;
saved_palette_[n].y = color.rgb().y / 255;
saved_palette_[n].z = color.rgb().z / 255;
saved_palette_[n].w = 255; // Alpha
}
return absl::OkStatus();
}
absl::Status status_;
PaletteEditorHistory history_;
palette_internal::PaletteEditorHistory history_;
ImVec4 saved_palette_[256] = {};
gfx::SnesColor current_color_;

View File

@@ -6,11 +6,12 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.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/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
@@ -18,7 +19,7 @@
#include "app/gui/style.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -50,6 +51,7 @@ absl::Status Tile16Editor::Update() {
*tile8_source_canvas_.custom_labels_enabled() = true;
}
RETURN_IF_ERROR(DrawMenu());
if (BeginTabBar("Tile16 Editor Tabs")) {
RETURN_IF_ERROR(DrawTile16Editor());
RETURN_IF_ERROR(UpdateTile16Transfer());
@@ -59,11 +61,25 @@ absl::Status Tile16Editor::Update() {
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();
}
ImGui::EndMenuBar();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::DrawTile16Editor() {
if (BeginTabItem("Tile16 Editing")) {
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Tiles", ImGuiTableColumnFlags_WidthFixed,
TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
@@ -74,31 +90,7 @@ absl::Status Tile16Editor::DrawTile16Editor() {
TableNextColumn();
RETURN_IF_ERROR(UpdateTile16Edit());
ImGui::EndTable();
}
ImGui::EndTabItem();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateTile16Transfer() {
if (BeginTabItem("Tile16 Transfer")) {
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
TableNextColumn();
RETURN_IF_ERROR(UpdateTransferTileCanvas());
RETURN_IF_ERROR(DrawTileEditControls());
ImGui::EndTable();
}
@@ -113,12 +105,14 @@ absl::Status Tile16Editor::UpdateBlockset() {
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
blockset_canvas_.DrawBackground();
gui::EndPadding();
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
ImGui::EndChild();
{
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
ImGui::EndChild();
}
if (!blockset_canvas_.points().empty()) {
uint16_t x = blockset_canvas_.points().front().x / 32;
@@ -130,8 +124,9 @@ absl::Status Tile16Editor::UpdateBlockset() {
if (notify_tile16.modified()) {
current_tile16_ = notify_tile16.get();
current_tile16_bmp_ = tile16_individual_[notify_tile16];
current_tile16_bmp_.ApplyPalette(
rom()->palette_group("ow_main")[current_palette_]);
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_);
}
}
@@ -173,40 +168,41 @@ 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)) {
tile8_source_canvas_.DrawBackground(
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4));
tile8_source_canvas_.DrawContextMenu();
tile8_source_canvas_.DrawBackground();
tile8_source_canvas_.DrawContextMenu(&current_gfx_bmp_);
if (tile8_source_canvas_.DrawTileSelector(32)) {
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
rom()->palette_group("ow_main")[0], current_palette_);
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->UpdateBitmap(&current_gfx_individual_[current_tile8_]);
}
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
tile8_source_canvas_.DrawGrid(32.0f);
tile8_source_canvas_.DrawGrid();
tile8_source_canvas_.DrawOverlay();
}
ImGui::EndChild();
// The user selected a tile8
if (!tile8_source_canvas_.points().empty()) {
uint16_t x = tile8_source_canvas_.points().front().x / 16;
uint16_t y = tile8_source_canvas_.points().front().y / 16;
current_tile8_ = x + (y * 8);
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
rom()->palette_group("ow_main")[0], current_palette_);
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->UpdateBitmap(&current_gfx_individual_[current_tile8_]);
}
ImGui::Text("Tile16 ID: %d", current_tile16_);
ImGui::Text("Tile8 ID: %d", current_tile8_);
if (ImGui::BeginChild("Tile16 Editor Options",
ImVec2(ImGui::GetContentRegionAvail().x, 0x50), true)) {
tile16_edit_canvas_.DrawBackground(ImVec2(0x40, 0x40));
tile16_edit_canvas_.DrawContextMenu();
tile16_edit_canvas_.DrawBackground();
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(
@@ -216,17 +212,17 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
rom()->UpdateBitmap(&current_tile16_bmp_);
}
}
tile16_edit_canvas_.DrawGrid(64.0f);
tile16_edit_canvas_.DrawGrid();
tile16_edit_canvas_.DrawOverlay();
}
ImGui::EndChild();
DrawTileEditControls();
return absl::OkStatus();
}
void Tile16Editor::DrawTileEditControls() {
absl::Status Tile16Editor::DrawTileEditControls() {
ImGui::Separator();
ImGui::Text("Tile16 ID: %d", current_tile16_);
ImGui::Text("Tile8 ID: %d", current_tile8_);
ImGui::Text("Options:");
gui::InputHexByte("Palette", &notify_palette.mutable_get());
notify_palette.apply_changes();
@@ -242,8 +238,10 @@ void Tile16Editor::DrawTileEditControls() {
}
if (value > 0x00) {
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value);
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value);
RETURN_IF_ERROR(
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
RETURN_IF_ERROR(
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
rom()->UpdateBitmap(&current_gfx_bmp_);
rom()->UpdateBitmap(&current_tile16_bmp_);
}
@@ -252,6 +250,82 @@ void Tile16Editor::DrawTileEditControls() {
ImGui::Checkbox("X Flip", &x_flip);
ImGui::Checkbox("Y Flip", &y_flip);
ImGui::Checkbox("Priority Tile", &priority_tile);
return absl::OkStatus();
}
absl::Status Tile16Editor::LoadTile8() {
auto ow_main_pal_group = rom()->palette_group().overworld_main;
current_gfx_individual_.reserve(1024);
for (int index = 0; index < 1024; index++) {
std::vector<uint8_t> tile_data(0x40, 0x00);
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 8; ty++) {
for (int tx = 0; tx < 8; tx++) {
// Current Gfx Data is 16 sheets of 8x8 tiles ordered 16 wide by 4 tall
// Calculate the position in the tile data vector
int position = tx + (ty * 0x08);
// Calculate the position in the current gfx data
int num_columns = current_gfx_bmp_.width() / 8;
int num_rows = current_gfx_bmp_.height() / 8;
int x = (index % num_columns) * 8 + tx;
int y = (index / num_columns) * 8 + ty;
int gfx_position = x + (y * 0x100);
// Get the pixel value from the current gfx data
uint8_t value = current_gfx_bmp_.data()[gfx_position];
if (value & 0x80) {
value -= 0x88;
}
tile_data[position] = value;
}
}
current_gfx_individual_.emplace_back();
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
RETURN_IF_ERROR(current_gfx_individual_[index].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->RenderBitmap(&current_gfx_individual_[index]);
}
map_blockset_loaded_ = true;
return absl::OkStatus();
}
// ============================================================================
// Tile16 Transfer
absl::Status Tile16Editor::UpdateTile16Transfer() {
if (BeginTabItem("Tile16 Transfer")) {
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
TableNextColumn();
RETURN_IF_ERROR(UpdateTransferTileCanvas());
ImGui::EndTable();
}
ImGui::EndTabItem();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
@@ -279,9 +353,9 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
palette_ = transfer_overworld_.AreaPalette();
// Create the tile16 blockset image
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80,
transfer_overworld_.Tile16Blockset(),
*rom(), transfer_blockset_bmp_, palette_);
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x80,
transfer_overworld_.Tile16Blockset(),
transfer_blockset_bmp_, palette_));
transfer_blockset_loaded_ = true;
}
@@ -293,62 +367,6 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
return absl::OkStatus();
}
absl::Status Tile16Editor::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();
return absl::OkStatus();
}
absl::Status Tile16Editor::LoadTile8() {
current_gfx_individual_.reserve(1024);
for (int index = 0; index < 1024; index++) {
std::vector<uint8_t> tile_data(0x40, 0x00);
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 8; ty++) {
for (int tx = 0; tx < 8; tx++) {
// Current Gfx Data is 16 sheets of 8x8 tiles ordered 16 wide by 4 tall
// Calculate the position in the tile data vector
int position = tx + (ty * 0x08);
// Calculate the position in the current gfx data
int num_columns = current_gfx_bmp_.width() / 8;
int num_rows = current_gfx_bmp_.height() / 8;
int x = (index % num_columns) * 8 + tx;
int y = (index / num_columns) * 8 + ty;
int gfx_position = x + (y * 0x100);
// Get the pixel value from the current gfx data
uint8_t value = tile8_gfx_data_[gfx_position];
if (value & 0x80) {
value -= 0x88;
}
tile_data[position] = value;
}
}
current_gfx_individual_.emplace_back();
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
current_gfx_individual_[index].ApplyPaletteWithTransparent(
rom()->palette_group("ow_main")[0], current_palette_);
rom()->RenderBitmap(&current_gfx_individual_[index]);
}
map_blockset_loaded_ = true;
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -7,25 +7,31 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/editor/context/gfx_context.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/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.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
namespace editor {
class Tile16Editor : public GfxContext, public SharedROM {
/**
* @brief Popup window to edit Tile16 data
*/
class Tile16Editor : public context::GfxContext, public SharedRom {
public:
absl::Status Update();
absl::Status DrawMenu();
absl::Status DrawTile16Editor();
absl::Status UpdateTile16Transfer();
absl::Status UpdateBlockset();
@@ -34,23 +40,31 @@ class Tile16Editor : public GfxContext, public SharedROM {
absl::Status UpdateTile16Edit();
void DrawTileEditControls();
absl::Status DrawTileEditControls();
absl::Status UpdateTransferTileCanvas();
absl::Status 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]);
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();
auto set_tile16(int id) {
absl::Status set_tile16(int id) {
current_tile16_ = id;
current_tile16_bmp_ = tile16_individual_[id];
current_tile16_bmp_.ApplyPalette(
rom()->palette_group("ow_main")[current_palette_]);
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();
}
private:
@@ -78,7 +92,7 @@ class Tile16Editor : public GfxContext, public SharedROM {
bool priority_tile;
int tile_size;
uint8_t *all_tiles_types_;
uint8_t* all_tiles_types_;
// Tile16 blockset for selecting the tile to edit
gui::Canvas blockset_canvas_{ImVec2(0x100, 0x4000),
@@ -86,13 +100,17 @@ class Tile16Editor : public GfxContext, public SharedROM {
gfx::Bitmap tile16_blockset_bmp_;
// Canvas for editing the selected tile
gui::Canvas tile16_edit_canvas_;
gui::Canvas tile16_edit_canvas_{ImVec2(0x40, 0x40),
gui::CanvasGridSize::k64x64};
gfx::Bitmap current_tile16_bmp_;
gfx::Bitmap current_tile8_bmp_;
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
gui::Canvas tile8_source_canvas_;
gui::Canvas tile8_source_canvas_{
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_;
@@ -110,14 +128,12 @@ class Tile16Editor : public GfxContext, public SharedROM {
PaletteEditor palette_editor_;
gfx::SnesPalette palette_;
zelda3::Overworld transfer_overworld_;
zelda3::overworld::Overworld transfer_overworld_;
gfx::BitmapTable graphics_bin_;
ROM transfer_rom_;
Rom transfer_rom_;
absl::Status transfer_status_;
core::TaskManager<std::function<void(int)>> task_manager_;
};
} // namespace editor

View File

@@ -23,7 +23,7 @@
#include "app/gui/style.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -44,52 +44,17 @@ using ImGui::Text;
absl::Status OverworldEditor::Update() {
if (rom()->is_loaded() && !all_gfx_loaded_) {
RETURN_IF_ERROR(tile16_editor_.InitBlockset(
tile16_blockset_bmp_, current_gfx_bmp_, tile16_individual_,
*overworld_.mutable_all_tiles_types()));
tile16_editor_.InitBlockset(tile16_blockset_bmp_, current_gfx_bmp_,
tile16_individual_,
*overworld_.mutable_all_tiles_types());
gfx_group_editor_.InitBlockset(tile16_blockset_bmp_);
RETURN_IF_ERROR(LoadEntranceTileTypes(*rom()));
all_gfx_loaded_ = true;
} else if (!rom()->is_loaded() && all_gfx_loaded_) {
// TODO: Destroy the overworld graphics canvas.
Shutdown();
overworld_.Destroy();
all_gfx_loaded_ = false;
map_blockset_loaded_ = false;
}
// TODO: Setup pan tool with middle mouse button
// if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
// previous_mode = current_mode;
// current_mode = EditingMode::PAN;
// ow_map_canvas_.set_draggable(true);
// middle_mouse_dragging_ = true;
// }
// if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
// current_mode == EditingMode::PAN && middle_mouse_dragging_) {
// current_mode = previous_mode;
// ow_map_canvas_.set_draggable(false);
// middle_mouse_dragging_ = false;
// }
if (overworld_canvas_fullscreen_) {
static bool use_work_area = true;
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings;
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
: viewport->Size);
if (ImGui::Begin("Example: Fullscreen window",
&overworld_canvas_fullscreen_, flags)) {
// Draws the toolset for editing the Overworld.
RETURN_IF_ERROR(DrawToolset())
DrawOverworldCanvas();
}
ImGui::End();
return absl::OkStatus();
}
RETURN_IF_ERROR(UpdateFullscreenCanvas());
TAB_BAR("##OWEditorTabBar")
TAB_ITEM("Map Editor")
@@ -117,12 +82,35 @@ absl::Status OverworldEditor::UpdateOverworldEdit() {
TableNextColumn();
DrawOverworldCanvas();
TableNextColumn();
DrawTileSelector();
RETURN_IF_ERROR(DrawTileSelector());
ImGui::EndTable();
}
return absl::OkStatus();
}
absl::Status OverworldEditor::UpdateFullscreenCanvas() {
if (overworld_canvas_fullscreen_) {
static bool use_work_area = true;
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings;
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
: viewport->Size);
if (ImGui::Begin("Example: Fullscreen window",
&overworld_canvas_fullscreen_, flags)) {
// Draws the toolset for editing the Overworld.
RETURN_IF_ERROR(DrawToolset())
DrawOverworldCanvas();
}
ImGui::End();
return absl::OkStatus();
}
return absl::OkStatus();
}
absl::Status OverworldEditor::DrawToolset() {
static bool show_gfx_group = false;
static bool show_properties = false;
@@ -258,7 +246,8 @@ absl::Status OverworldEditor::DrawToolset() {
if (show_tile16_editor_) {
// Create a table in ImGui for the Tile16 Editor
ImGui::Begin("Tile16 Editor", &show_tile16_editor_);
ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
ImGuiWindowFlags_MenuBar);
RETURN_IF_ERROR(tile16_editor_.Update())
ImGui::End();
}
@@ -358,21 +347,24 @@ void OverworldEditor::RefreshOverworldMap() {
}
// TODO: Palette throws out of bounds error unexpectedly.
void OverworldEditor::RefreshMapPalette() {
absl::Status OverworldEditor::RefreshMapPalette() {
const auto current_map_palette =
overworld_.overworld_map(current_map_)->current_palette();
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;
overworld_.mutable_overworld_map(sibling_index)->LoadPalette();
maps_bmp_[sibling_index].ApplyPalette(
*overworld_.mutable_overworld_map(sibling_index)
->mutable_current_palette());
RETURN_IF_ERROR(
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
RETURN_IF_ERROR(
maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
}
}
maps_bmp_[current_map_].ApplyPalette(
*overworld_.mutable_overworld_map(current_map_)
->mutable_current_palette());
RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
return absl::OkStatus();
}
void OverworldEditor::RefreshMapProperties() {
@@ -396,17 +388,13 @@ void OverworldEditor::RefreshMapProperties() {
}
}
// TODO: Add @JaredBrian per map mosaic feature
void OverworldEditor::DrawOverworldMapSettings() {
if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
for (const auto &name :
{"##mapIdCol", "##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
"##sprpalCol", "##msgidCol", "##2ndCol"})
if (BeginTable(kOWMapTable.data(), 7, kOWMapFlags, ImVec2(0, 0), -1)) {
for (const auto &name : {"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
"##sprpalCol", "##msgidCol", "##2ndCol"})
ImGui::TableSetupColumn(name);
TableNextColumn();
ImGui::Text("Parent/Map ID:%#x, %#x",
overworld_.overworld_map(current_map_)->parent(), current_map_);
TableNextColumn();
ImGui::SetNextItemWidth(120.f);
ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
@@ -429,7 +417,8 @@ void OverworldEditor::DrawOverworldMapSettings() {
->mutable_area_palette(),
kInputFieldSize)) {
RefreshMapProperties();
RefreshMapPalette();
status_ = RefreshMapPalette();
RefreshOverworldMap();
}
ImGui::EndGroup();
@@ -643,7 +632,7 @@ void OverworldEditor::CheckForSelectRectangle() {
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10);
}
void OverworldEditor::CheckForCurrentMap() {
absl::Status OverworldEditor::CheckForCurrentMap() {
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
auto mouse_position = ImGui::GetIO().MousePos;
constexpr int small_map_size = 512;
@@ -686,28 +675,48 @@ void OverworldEditor::CheckForCurrentMap() {
if (maps_bmp_[current_map_].modified() ||
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
RefreshOverworldMap();
RefreshTile16Blockset();
RETURN_IF_ERROR(RefreshTile16Blockset());
rom()->UpdateBitmap(&maps_bmp_[current_map_]);
maps_bmp_[current_map_].set_modified(false);
}
return absl::OkStatus();
}
// Overworld Editor canvas
// Allows the user to make changes to the overworld map.
void OverworldEditor::CheckForMousePan() {
if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
previous_mode = current_mode;
current_mode = EditingMode::PAN;
ow_map_canvas_.set_draggable(true);
middle_mouse_dragging_ = true;
}
if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
current_mode == EditingMode::PAN && middle_mouse_dragging_) {
current_mode = previous_mode;
ow_map_canvas_.set_draggable(false);
middle_mouse_dragging_ = false;
}
}
// TODO: Add @JaredBrian ZSCustomOverworld features to OverworldEditor
void OverworldEditor::DrawOverworldCanvas() {
if (all_gfx_loaded_) {
DrawOverworldMapSettings();
Separator();
}
gui::BeginNoPadding();
gui::BeginChildBothScrollbars(7);
ow_map_canvas_.DrawBackground();
gui::EndNoPadding();
CheckForMousePan();
if (current_mode == EditingMode::PAN) {
ow_map_canvas_.DrawContextMenu();
} else {
ow_map_canvas_.set_draggable(false);
}
if (overworld_.is_loaded()) {
DrawOverworldMaps();
DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
@@ -716,40 +725,47 @@ void OverworldEditor::DrawOverworldCanvas() {
DrawOverworldItems();
DrawOverworldSprites();
CheckForOverworldEdits();
if (ImGui::IsItemHovered()) CheckForCurrentMap();
if (ImGui::IsItemHovered()) status_ = CheckForCurrentMap();
}
ow_map_canvas_.DrawGrid();
ow_map_canvas_.DrawOverlay();
ImGui::EndChild();
}
void OverworldEditor::DrawTile16Selector() {
absl::Status OverworldEditor::DrawTile16Selector() {
gui::BeginPadding(3);
ImGui::BeginGroup();
gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
blockset_canvas_.DrawBackground();
gui::EndNoPadding();
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
map_blockset_loaded_);
if (blockset_canvas_.DrawTileSelector(32.0f)) {
// Open the tile16 editor to the tile
auto tile_pos = blockset_canvas_.points().front();
int grid_x = static_cast<int>(tile_pos.x / 32);
int grid_y = static_cast<int>(tile_pos.y / 32);
int id = grid_x + grid_y * 8;
tile16_editor_.set_tile16(id);
show_tile16_editor_ = true;
{
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
map_blockset_loaded_);
if (blockset_canvas_.DrawTileSelector(32.0f)) {
// Open the tile16 editor to the tile
auto tile_pos = blockset_canvas_.points().front();
int grid_x = static_cast<int>(tile_pos.x / 32);
int grid_y = static_cast<int>(tile_pos.y / 32);
int id = grid_x + grid_y * 8;
RETURN_IF_ERROR(tile16_editor_.set_tile16(id));
show_tile16_editor_ = true;
}
if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
int x = blockset_canvas_.points().front().x / 32;
int y = blockset_canvas_.points().front().y / 32;
current_tile16_ = x + (y * 8);
}
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
}
if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
int x = blockset_canvas_.points().front().x / 32;
int y = blockset_canvas_.points().front().y / 32;
current_tile16_ = x + (y * 8);
}
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
ImGui::EndChild();
ImGui::EndGroup();
return absl::OkStatus();
}
void OverworldEditor::DrawTile8Selector() {
@@ -774,15 +790,14 @@ void OverworldEditor::DrawTile8Selector() {
graphics_bin_canvas_.DrawOverlay();
}
void OverworldEditor::DrawAreaGraphics() {
absl::Status OverworldEditor::DrawAreaGraphics() {
if (overworld_.is_loaded()) {
if (current_graphics_set_.count(current_map_) == 0) {
overworld_.set_current_map(current_map_);
palette_ = overworld_.AreaPalette();
gfx::Bitmap bmp;
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x08,
overworld_.current_graphics(), *rom(),
bmp, palette_);
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
0x80, 0x200, 0x08, overworld_.current_graphics(), bmp, palette_));
current_graphics_set_[current_map_] = bmp;
}
}
@@ -792,21 +807,24 @@ void OverworldEditor::DrawAreaGraphics() {
gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
current_gfx_canvas_.DrawBackground();
gui::EndPadding();
current_gfx_canvas_.DrawContextMenu();
current_gfx_canvas_.DrawBitmap(current_graphics_set_[current_map_],
/*border_offset=*/2, overworld_.is_loaded());
current_gfx_canvas_.DrawTileSelector(32.0f);
current_gfx_canvas_.DrawGrid();
current_gfx_canvas_.DrawOverlay();
{
current_gfx_canvas_.DrawContextMenu();
current_gfx_canvas_.DrawBitmap(current_graphics_set_[current_map_],
/*border_offset=*/2, overworld_.is_loaded());
current_gfx_canvas_.DrawTileSelector(32.0f);
current_gfx_canvas_.DrawGrid();
current_gfx_canvas_.DrawOverlay();
}
ImGui::EndChild();
ImGui::EndGroup();
return absl::OkStatus();
}
void OverworldEditor::DrawTileSelector() {
absl::Status OverworldEditor::DrawTileSelector() {
if (BeginTabBar(kTileSelectorTab.data(),
ImGuiTabBarFlags_FittingPolicyScroll)) {
if (BeginTabItem("Tile16")) {
DrawTile16Selector();
RETURN_IF_ERROR(DrawTile16Selector());
EndTabItem();
}
if (BeginTabItem("Tile8")) {
@@ -823,6 +841,7 @@ void OverworldEditor::DrawTileSelector() {
}
EndTabBar();
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------
@@ -940,7 +959,9 @@ bool DrawEntranceInserterPopup() {
return set_done;
}
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance) {
// 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;
@@ -1045,6 +1066,7 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
namespace exit_internal {
// TODO: Implement deleting OverworldExit objects
void DrawExitInserterPopup() {
if (ImGui::BeginPopup("Exit Inserter")) {
static int exit_id = 0;
@@ -1063,7 +1085,7 @@ void DrawExitInserterPopup() {
}
}
bool DrawExitEditorPopup(zelda3::OverworldExit &exit) {
bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
static bool set_done = false;
if (set_done) {
set_done = false;
@@ -1259,8 +1281,8 @@ void DrawItemInsertPopup() {
ImGui::Text("Add Item");
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) {
if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(),
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
if (ImGui::Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
i == new_item_id)) {
new_item_id = i;
}
@@ -1282,7 +1304,8 @@ void DrawItemInsertPopup() {
}
}
bool DrawItemEditorPopup(zelda3::OverworldItem &item) {
// 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;
@@ -1292,8 +1315,8 @@ bool DrawItemEditorPopup(zelda3::OverworldItem &item) {
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
ImGui::BeginGroup();
for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) {
if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(),
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
if (ImGui::Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
item.id == i)) {
item.id = i;
}
@@ -1326,7 +1349,7 @@ void OverworldEditor::DrawOverworldItems() {
// Get the item's bitmap and real X and Y positions
if (item.room_map_id < 0x40 + (current_world_ * 0x40) &&
item.room_map_id >= (current_world_ * 0x40) && !item.deleted) {
std::string item_name = zelda3::kSecretItemNames[item.id];
std::string item_name = zelda3::overworld::kSecretItemNames[item.id];
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
@@ -1461,6 +1484,7 @@ void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
}
}
// TODO: Implement deleting OverworldSprite objects
void DrawSpriteInserterPopup() {
if (ImGui::BeginPopup("Sprite Inserter")) {
static int new_sprite_id = 0;
@@ -1576,8 +1600,6 @@ void OverworldEditor::DrawOverworldSprites() {
// ----------------------------------------------------------------------------
absl::Status OverworldEditor::LoadGraphics() {
// Load all of the graphics data from the game.
PRINT_IF_ERROR(rom()->LoadAllGraphicsData())
graphics_bin_ = rom()->graphics_bin();
// Load the Link to the Past overworld.
@@ -1585,14 +1607,12 @@ absl::Status OverworldEditor::LoadGraphics() {
palette_ = overworld_.AreaPalette();
// Create the area graphics image
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40,
overworld_.current_graphics(), *rom(),
current_gfx_bmp_, palette_);
rom()->CreateAndRenderBitmap(0x80, 0x200, 0x40, overworld_.current_graphics(),
current_gfx_bmp_, palette_);
// Create the tile16 blockset image
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x08,
overworld_.Tile16Blockset(), *rom(),
tile16_blockset_bmp_, palette_);
rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x08, overworld_.Tile16Blockset(),
tile16_blockset_bmp_, palette_);
map_blockset_loaded_ = true;
// Copy the tile16 data into individual tiles.
@@ -1620,18 +1640,17 @@ absl::Status OverworldEditor::LoadGraphics() {
// Render the bitmaps of each tile.
for (int id = 0; id < 4096; id++) {
tile16_individual_.emplace_back();
gui::BuildAndRenderBitmapPipeline(0x10, 0x10, 0x80,
tile16_individual_data_[id], *rom(),
tile16_individual_[id], palette_);
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
0x10, 0x10, 0x80, tile16_individual_data_[id], tile16_individual_[id],
palette_));
}
// Render the overworld maps loaded from the ROM.
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
for (int i = 0; i < zelda3::overworld::kNumOverworldMaps; ++i) {
overworld_.set_current_map(i);
auto palette = overworld_.AreaPalette();
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200,
overworld_.BitmapData(), *rom(),
maps_bmp_[i], palette);
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
0x200, 0x200, 0x200, overworld_.BitmapData(), maps_bmp_[i], palette));
}
if (flags()->overworld.kDrawOverworldSprites) {
@@ -1641,19 +1660,19 @@ absl::Status OverworldEditor::LoadGraphics() {
return absl::OkStatus();
}
void OverworldEditor::RefreshTile16Blockset() {
absl::Status OverworldEditor::RefreshTile16Blockset() {
if (current_blockset_ ==
overworld_.overworld_map(current_map_)->area_graphics()) {
return;
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
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x08,
overworld_.Tile16Blockset(), *rom(),
tile16_blockset_bmp_, palette_);
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x08,
overworld_.Tile16Blockset(),
tile16_blockset_bmp_, palette_));
// Copy the tile16 data into individual tiles.
auto tile16_data = overworld_.Tile16Blockset();
@@ -1691,9 +1710,11 @@ void OverworldEditor::RefreshTile16Blockset() {
// Render the bitmaps of each tile.
for (int id = 0; id < 4096; id++) {
tile16_individual_[id].ApplyPalette(palette_);
RETURN_IF_ERROR(tile16_individual_[id].ApplyPalette(palette_));
rom()->UpdateBitmap(&tile16_individual_[id]);
}
return absl::OkStatus();
}
absl::Status OverworldEditor::LoadSpriteGraphics() {
@@ -1705,7 +1726,7 @@ absl::Status OverworldEditor::LoadSpriteGraphics() {
int depth = 0x40;
auto spr_gfx = sprite.PreviewGraphics();
sprite_previews_[sprite.id()].Create(width, height, depth, spr_gfx);
sprite_previews_[sprite.id()].ApplyPalette(palette_);
RETURN_IF_ERROR(sprite_previews_[sprite.id()].ApplyPalette(palette_));
rom()->RenderBitmap(&(sprite_previews_[sprite.id()]));
}
return absl::OkStatus();
@@ -1880,15 +1901,15 @@ void OverworldEditor::DrawUsageGrid() {
}
}
void OverworldEditor::LoadAnimatedMaps() {
absl::Status OverworldEditor::LoadAnimatedMaps() {
int world_index = 0;
static std::vector<bool> animated_built(0x40, false);
if (!animated_built[world_index]) {
animated_maps_[world_index] = maps_bmp_[world_index];
auto &map = *overworld_.mutable_overworld_map(world_index);
map.DrawAnimatedTiles();
map.BuildTileset();
map.BuildTiles16Gfx(overworld_.tiles16().size());
RETURN_IF_ERROR(map.BuildTileset());
RETURN_IF_ERROR(map.BuildTiles16Gfx(overworld_.tiles16().size()));
OWBlockset blockset;
if (current_world_ == 0) {
blockset = overworld_.map_tiles().light_world;
@@ -1897,14 +1918,16 @@ void OverworldEditor::LoadAnimatedMaps() {
} else {
blockset = overworld_.map_tiles().special_world;
}
map.BuildBitmap(blockset);
RETURN_IF_ERROR(map.BuildBitmap(blockset));
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200, map.bitmap_data(),
*rom(), animated_maps_[world_index],
*map.mutable_current_palette());
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
0x200, 0x200, 0x200, map.bitmap_data(), animated_maps_[world_index],
*map.mutable_current_palette()));
animated_built[world_index] = true;
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------

View File

@@ -12,11 +12,12 @@
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/core/common.h"
#include "app/core/editor.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/utils/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -24,7 +25,7 @@
#include "app/gui/icons.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -61,9 +62,26 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
constexpr absl::string_view kOWEditTable = "##OWEditTable";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
/**
* @class OverworldEditor
* @brief Manipulates the Overworld and OverworldMap data in a Rom.
*
* The `OverworldEditor` class is responsible for managing the editing and
* manipulation of the overworld in a game. The user can drag and drop tiles,
* modify OverworldEntrance, OverworldExit, Sprite, and OverworldItem
* as well as change the gfx and palettes used in each overworld map.
*
* The Overworld itself is a series of bitmap images which exist inside each
* OverworldMap object. The drawing of the overworld is done using the Canvas
* class in conjunction with these underlying Bitmap objects.
*
* Provides access to the GfxGroupEditor and Tile16Editor through popup windows.
*
*/
class OverworldEditor : public Editor,
public SharedROM,
public GfxContext,
public SharedRom,
public context::GfxContext,
public context::EntranceContext,
public core::ExperimentFlags {
public:
absl::Status Update() final;
@@ -75,6 +93,9 @@ class OverworldEditor : public Editor,
auto overworld() { return &overworld_; }
/**
* @brief
*/
int jump_to_tab() { return jump_to_tab_; }
int jump_to_tab_ = -1;
@@ -91,21 +112,33 @@ class OverworldEditor : public Editor,
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.
*
* Calls the Overworld class to load the image data and palettes from the Rom,
* then renders the area graphics and tile16 blockset Bitmap objects before
* assembling the OverworldMap Bitmap objects.
*/
absl::Status LoadGraphics();
private:
absl::Status UpdateOverworldEdit();
absl::Status UpdateFullscreenCanvas();
absl::Status DrawToolset();
void DrawOverworldMapSettings();
void RefreshChildMap(int i);
void RefreshOverworldMap();
void RefreshMapPalette();
absl::Status RefreshMapPalette();
void RefreshMapProperties();
void RefreshTile16Blockset();
absl::Status RefreshTile16Blockset();
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling,
bool holes = false);
@@ -118,14 +151,19 @@ class OverworldEditor : public Editor,
void RenderUpdatedMapBitmap(const ImVec2& click_position,
const Bytes& tile_data);
void CheckForOverworldEdits();
void CheckForCurrentMap();
void CheckForSelectRectangle();
absl::Status CheckForCurrentMap();
void CheckForMousePan();
/**
* @brief Allows the user to make changes to the overworld map.
*/
void DrawOverworldCanvas();
void DrawTile16Selector();
absl::Status DrawTile16Selector();
void DrawTile8Selector();
void DrawAreaGraphics();
void DrawTileSelector();
absl::Status DrawAreaGraphics();
absl::Status DrawTileSelector();
absl::Status LoadSpriteGraphics();
@@ -137,7 +175,7 @@ class OverworldEditor : public Editor,
void DrawUsageGrid();
void CalculateUsageStats();
void LoadAnimatedMaps();
absl::Status LoadAnimatedMaps();
void DrawDebugWindow();
auto gfx_group_editor() const { return gfx_group_editor_; }
@@ -197,11 +235,11 @@ class OverworldEditor : public Editor,
zelda3::OverworldEntity* current_entity_;
int current_entrance_id_ = 0;
zelda3::OverworldEntrance current_entrance_;
zelda3::overworld::OverworldEntrance current_entrance_;
int current_exit_id_ = 0;
zelda3::OverworldExit current_exit_;
zelda3::overworld::OverworldExit current_exit_;
int current_item_id_ = 0;
zelda3::OverworldItem current_item_;
zelda3::overworld::OverworldItem current_item_;
int current_sprite_id_ = 0;
zelda3::Sprite current_sprite_;
@@ -219,7 +257,7 @@ class OverworldEditor : public Editor,
Tile16Editor tile16_editor_;
GfxGroupEditor gfx_group_editor_;
PaletteEditor palette_editor_;
zelda3::Overworld overworld_;
zelda3::overworld::Overworld overworld_;
gui::Canvas ow_map_canvas_{ImVec2(0x200 * 8, 0x200 * 8),
gui::CanvasGridSize::k64x64};

View File

@@ -117,25 +117,30 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
for (int d = 0; d < 14; d++) {
current_floor_rooms_d.clear();
current_floor_gfx_d.clear();
ASSIGN_OR_RETURN(int ptr,
rom()->ReadWord(zelda3::kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(int ptrGFX,
rom()->ReadWord(zelda3::kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(
int ptr,
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(
int ptrGFX,
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
ptr |= 0x0A0000; // Add bank to the short ptr
ptrGFX |= 0x0A0000; // Add bank to the short ptr
int pcPtr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
int pcPtrGFX =
core::SnesToPc(ptrGFX); // Contains data for the next 25 rooms
ASSIGN_OR_RETURN(ushort bossRoomD,
rom()->ReadWord(zelda3::kDungeonMapBossRooms + (d * 2)));
ASSIGN_OR_RETURN(
ushort bossRoomD,
rom()->ReadWord(zelda3::screen::kDungeonMapBossRooms + (d * 2)));
ASSIGN_OR_RETURN(nbr_basement_d,
rom()->ReadByte(zelda3::kDungeonMapFloors + (d * 2)));
ASSIGN_OR_RETURN(
nbr_basement_d,
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
nbr_basement_d &= 0x0F;
ASSIGN_OR_RETURN(nbr_floor_d,
rom()->ReadByte(zelda3::kDungeonMapFloors + (d * 2)));
ASSIGN_OR_RETURN(
nbr_floor_d,
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
nbr_floor_d &= 0xF0;
nbr_floor_d = nbr_floor_d >> 4;
@@ -179,8 +184,8 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
absl::Status ScreenEditor::SaveDungeonMaps() {
for (int d = 0; d < 14; d++) {
int ptr = zelda3::kDungeonMapRoomsPtr + (d * 2);
int ptrGFX = zelda3::kDungeonMapGfxPtr + (d * 2);
int ptr = zelda3::screen::kDungeonMapRoomsPtr + (d * 2);
int ptrGFX = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
int pcPtr = core::SnesToPc(ptr);
int pcPtrGFX = core::SnesToPc(ptrGFX);
@@ -208,9 +213,9 @@ absl::Status ScreenEditor::LoadDungeonMapTile16() {
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
for (int i = 0; i < 186; i++) {
int addr = zelda3::kDungeonMapTile16;
if (rom()->data()[zelda3::kDungeonMapExpCheck] != 0xB9) {
addr = zelda3::kDungeonMapTile16Expanded;
int addr = zelda3::screen::kDungeonMapTile16;
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
addr = zelda3::screen::kDungeonMapTile16Expanded;
}
ASSIGN_OR_RETURN(auto tl, rom()->ReadWord(addr + (i * 8)));

View File

@@ -22,7 +22,21 @@ namespace yaze {
namespace app {
namespace editor {
class ScreenEditor : public SharedROM {
/**
* @brief The ScreenEditor class allows the user to edit a variety of screens in
* the game or create a custom menu.
*
* This class is currently a work in progress (WIP) and provides functionality
* for updating the screens, saving dungeon maps, drawing different types of
* screens, loading dungeon maps, and managing various properties related to the
* editor.
*
* The screens that can be edited include the title screen, naming screen,
* overworld map, inventory menu, and more.
*
* The class inherits from the SharedRom class.
*/
class ScreenEditor : public SharedRom {
public:
ScreenEditor();
void Update();
@@ -43,7 +57,7 @@ class ScreenEditor : public SharedROM {
void DrawDungeonMapsTabs();
void DrawDungeonMapsEditor();
std::vector<zelda3::DungeonMap> dungeon_maps_;
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
@@ -60,7 +74,7 @@ class ScreenEditor : public SharedROM {
bool paste_button_pressed = false;
Bytes all_gfx_;
zelda3::Inventory inventory_;
zelda3::screen::Inventory inventory_;
gfx::SnesPalette palette_;
gui::Canvas screen_canvas_;
gui::Canvas tilesheet_canvas_;

View File

@@ -9,17 +9,41 @@ namespace yaze {
namespace app {
namespace editor {
class SpriteEditor : public SharedROM {
/**
* @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];
bool sheets_loaded_ = false;
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

View File

@@ -0,0 +1,40 @@
#ifndef YAZE_APP_CORE_EDITOR_H
#define YAZE_APP_CORE_EDITOR_H
#include "absl/status/status.h"
namespace yaze {
namespace app {
/**
* @namespace yaze::app::editor
* @brief Editors are the view controllers for the application.
*/
namespace editor {
/**
* @class Editor
* @brief Interface for editor classes.
*
* Provides basic editing operations that each editor should implement.
*/
class Editor {
public:
Editor() = default;
virtual ~Editor() = default;
virtual absl::Status Cut() = 0;
virtual absl::Status Copy() = 0;
virtual absl::Status Paste() = 0;
virtual absl::Status Undo() = 0;
virtual absl::Status Redo() = 0;
virtual absl::Status Update() = 0;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_CORE_EDITOR_H