yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tile16_editor.cc
Go to the documentation of this file.
1#include "tile16_editor.h"
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <array>
6#include <memory>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
14#include "app/gfx/core/bitmap.h"
19#include "app/gui/core/input.h"
20#include "app/gui/core/style.h"
24#include "imgui/imgui.h"
25#include "rom/rom.h"
26#include "util/hex.h"
27#include "util/log.h"
28#include "util/macro.h"
29#include "zelda3/game_data.h"
36
37namespace yaze {
38namespace editor {
39
40using namespace ImGui;
41
43 gfx::Bitmap& tile16_blockset_bmp, gfx::Bitmap& current_gfx_bmp,
44 std::array<uint8_t, 0x200>& all_tiles_types) {
46 [this](int tile_id) { blockset_selector_.SetSelectedTile(tile_id); });
47 session_.set_on_tile8_sheet_resized([this](float width, float height) {
48 tile8_source_canvas_.SetCanvasSize(ImVec2(width, height));
49 });
50
51 RETURN_IF_ERROR(session_.InitializeBitmaps(tile16_blockset_bmp,
52 current_gfx_bmp, all_tiles_types));
53
56
59
62
63 if (session_.rom()) {
66 }
67
68 SetCollisionTypes(all_tiles_types);
71
72 gui::AddTableColumn(tile_edit_table_, "##tile16ID", [&]() {
73 Text(tr("Tile16: %02X"), session_.current_tile16());
74 });
75 gui::AddTableColumn(tile_edit_table_, "##tile8ID", [&]() {
76 Text(tr("Tile8: %02X"), session_.current_tile8());
77 });
78 gui::AddTableColumn(tile_edit_table_, "##tile16Flip", [&]() {
79 Checkbox(tr("X Flip"), session_.mutable_x_flip());
80 Checkbox(tr("Y Flip"), session_.mutable_y_flip());
81 Checkbox(tr("Priority"), session_.mutable_priority_tile());
82 });
83
84 return absl::OkStatus();
85}
86
87void Tile16Editor::SetCollisionTypes(const std::array<uint8_t, 0x200>& types) {
89 ImVector<std::string> labels;
90 labels.reserve(static_cast<int>(types.size()));
91 for (size_t i = 0; i < types.size(); ++i) {
93 labels.push_back(collision_label_storage_[i]);
94 }
96}
97
100 ImVector<std::string> labels;
101 labels.reserve(0x200);
102 for (auto& label : collision_label_storage_) {
103 label = "??";
104 labels.push_back(label);
105 }
107}
108
109absl::Status Tile16Editor::Update() {
110 // Legacy MenuBar shell removed — single chrome path is UpdateAsPanel().
111 return UpdateAsPanel();
112}
113
117
120 return absl::InvalidArgumentError("Blockset not initialized, open a ROM.");
121 }
122
125 TextDisabled("Collision labels unavailable for this map/layout.");
126 } else if (session_.rom() &&
129 TextDisabled("Collision labels: static full-load sheet preview.");
130 }
131 }
132
133 if (Button("Edit Graphics")) {
135 }
136 SameLine();
137 TextDisabled("Start at quadrant %d", session_.active_quadrant() + 1);
138 if (!graphics_open_status_.ok()) {
139 ImGui::TextWrapped("Graphics edit unavailable: %s",
140 graphics_open_status_.message().data());
141 }
142
143 // Handle keyboard shortcuts (shared implementation)
145
147 if (!status_.ok()) {
148 ImGui::TextWrapped("Tile16 edit failed: %s", status_.message().data());
149 }
152
153 return absl::OkStatus();
154}
155
158 gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
159
160 // Tile ID search/jump bar
163 }
164
165 // Configure canvas frame options for blockset view
166 gui::CanvasFrameOptions frame_opts;
167 // The selector draws its own grid aligned to its tile cells.
168 frame_opts.draw_grid = false;
169 frame_opts.draw_context_menu = true;
170 frame_opts.draw_overlay = true;
171 frame_opts.render_popups = true;
172 frame_opts.use_child_window = false;
173
174 auto canvas_rt = gui::BeginCanvas(blockset_canvas_, frame_opts);
176
177 // Ensure selector is synced with current selection
180 }
181
182 if (session_.tile16_blockset_bmp() == nullptr) {
183 return absl::FailedPreconditionError(
184 "Tile16 blockset bitmap not initialized");
185 }
186
187 // Render the selector widget (handles bitmap, grid, highlights, interaction)
188 auto result =
190
191 if (result.selection_changed) {
192 // Selection does not create an edit or interrupt document history.
193 RequestTileSwitch(result.selected_tile);
194 util::logf("Selected Tile16 from blockset: %d", result.selected_tile);
195 }
196
197 gui::EndCanvas(blockset_canvas_, canvas_rt, frame_opts);
198 EndChild();
199
200 return absl::OkStatus();
201}
202
204 // Child windows of this panel only. RootAndChildWindows is true whenever
205 // the overworld map canvas (a sibling under the same root) is focused, so
206 // Command+Z was consumed as a Tile16 undo while hovering the map.
207 if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows) &&
208 !ImGui::IsAnyItemActive()) {
209 const ImGuiIO& io = ImGui::GetIO();
210#if defined(__APPLE__)
211 const bool platform_primary_held = io.KeyCtrl || io.KeySuper;
212#else
213 const bool platform_primary_held = io.KeyCtrl;
214#endif
215 const bool ctrl_held = platform_primary_held ||
216 ImGui::IsKeyDown(ImGuiKey_LeftCtrl) ||
217 ImGui::IsKeyDown(ImGuiKey_RightCtrl);
218
219 // Editing shortcuts (only fire without Ctrl to avoid conflicts)
220 if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
222 }
223 if (ImGui::IsKeyPressed(ImGuiKey_H) && !ctrl_held) {
225 }
226 if (ImGui::IsKeyPressed(ImGuiKey_V) && !ctrl_held) {
228 }
229 if (ImGui::IsKeyPressed(ImGuiKey_R) && !ctrl_held) {
231 }
232 if (ImGui::IsKeyPressed(ImGuiKey_F) && !ctrl_held) {
233 if (session_.current_tile8() >= 0 &&
235 static_cast<int>(
238 }
239 }
240
241 if (!ctrl_held) {
242 if (ImGui::IsKeyPressed(ImGuiKey_P)) {
244 }
245 if (ImGui::IsKeyPressed(ImGuiKey_I)) {
247 }
248 if (ImGui::IsKeyPressed(ImGuiKey_U)) {
250 }
251 }
252
253 // Palette shortcuts
254 if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
255 status_ = CyclePalette(false);
256 }
257 if (ImGui::IsKeyPressed(ImGuiKey_E)) {
258 status_ = CyclePalette(true);
259 }
260
261 // Numeric shortcuts:
262 // - 1..4 focus tile16 quadrants
263 // - Ctrl+1..8 switch brush palette rows
264 for (int i = 0; i < 8; ++i) {
265 if (!ImGui::IsKeyPressed(static_cast<ImGuiKey>(ImGuiKey_1 + i))) {
266 continue;
267 }
268 const Tile16NumericShortcutResult shortcut =
269 ResolveTile16NumericShortcut(ctrl_held, i);
270 if (shortcut.quadrant_focus.has_value()) {
272 }
273 if (shortcut.palette_id.has_value()) {
276 }
277 }
278
279 // Ctrl-modified shortcuts
280 if (ctrl_held) {
281 if (!session_.has_shared_history() && ImGui::IsKeyPressed(ImGuiKey_Z)) {
282 status_ = io.KeyShift ? Redo() : Undo();
283 }
284 if (!session_.has_shared_history() && ImGui::IsKeyPressed(ImGuiKey_Y)) {
285 status_ = Redo();
286 }
287 if (ImGui::IsKeyPressed(ImGuiKey_C)) {
289 }
290 if (ImGui::IsKeyPressed(ImGuiKey_V)) {
292 }
293 }
294 }
295}
296
297absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 pos,
298 const gfx::Bitmap* source_tile) {
299 return session_.DrawToCurrentTile16({pos.x, pos.y}, source_tile);
300}
301
302absl::Status Tile16Editor::HandleTile16CanvasClick(const ImVec2& tile_position,
303 bool left_click,
304 bool right_click) {
305 return session_.HandleTile16CanvasClick({tile_position.x, tile_position.y},
306 left_click, right_click);
307}
308
310 const ImVec2& display_position) {
311 const Tile16LocalPos local =
313 {display_position.x, display_position.y});
314 return ImVec2(local.x, local.y);
315}
316
317absl::Status Tile16Editor::PickTile8FromTile16(const ImVec2& position) {
318 return session_.PickTile8FromTile16({position.x, position.y});
319}
320
324 selected_tiles_.clear();
325 selected_tiles_.reserve(64);
326 for (int y = 0; y < 8; ++y) {
327 for (int x = 0; x < 8; ++x) {
328 const int tile_id = session_.layout_scratch(slot).tile_layout[y][x];
329 if (tile_id >= 0) {
330 selected_tiles_.push_back(tile_id);
331 }
332 }
333 }
335 return absl::OkStatus();
336}
337
338} // namespace editor
339} // namespace yaze
absl::Status HandleTile16CanvasClick(Tile16LocalPos tile_position, bool left_click, bool right_click)
void SetCollisionTypes(const std::array< uint8_t, 0x200 > &types)
absl::Status InitializeBitmaps(gfx::Bitmap &tile16_blockset_bmp, gfx::Bitmap &current_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
absl::Status PickTile8FromTile16(Tile16LocalPos position)
absl::Status DrawToCurrentTile16(Tile16LocalPos pos, const gfx::Bitmap *source_tile=nullptr)
void set_on_tile_selection_changed(std::function< void(int)> callback)
Optional: UI can sync blockset selector when current tile changes.
absl::Status LoadLayoutFromScratch(int slot)
Tile16LayoutScratch & layout_scratch(int i)
std::vector< zelda3::Tile8PixelData > & mutable_current_gfx_individual()
static Tile16LocalPos Tile16PreviewDisplayPixelToTilePosition(Tile16LocalPos display_position)
void set_on_tile8_sheet_resized(std::function< void(float width, float height)> callback)
Optional: UI updates Tile8 source canvas size after LoadTile8.
void set_edit_mode(Tile16EditMode mode)
absl::Status LoadLayoutFromScratch(int slot)
static ImVec2 Tile16PreviewDisplayPixelToTilePosition(const ImVec2 &display_position)
absl::Status CyclePalette(bool forward=true)
absl::Status HandleTile16CanvasClick(const ImVec2 &tile_position, bool left_click, bool right_click)
absl::Status FillTile16WithTile8(int tile8_id)
absl::Status PasteTile16FromClipboard()
gui::TileSelectorWidget blockset_selector_
absl::Status FlipTile16Horizontal()
absl::Status OpenGraphicsForActiveQuadrant()
std::array< std::string, 0x200 > collision_label_storage_
void SetCollisionTypes(const std::array< uint8_t, 0x200 > &types)
absl::Status RefreshAllPalettes()
void RequestTileSwitch(int target_tile_id)
absl::Status DrawToCurrentTile16(ImVec2 pos, const gfx::Bitmap *source_tile=nullptr)
absl::Status UpdateLivePreview()
absl::Status Initialize(gfx::Bitmap &tile16_blockset_bmp, gfx::Bitmap &current_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
absl::Status CopyTile16ToClipboard(int tile_id)
absl::Status FlipTile16Vertical()
Tile16EditSession session_
absl::Status PickTile8FromTile16(const ImVec2 &position)
std::vector< int > selected_tiles_
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
auto custom_labels_enabled()
Definition canvas.h:414
void SetCanvasSize(ImVec2 canvas_size)
Definition canvas.h:388
auto mutable_labels(int i)
Definition canvas.h:457
void InitializePaletteEditor(Rom *rom)
Definition canvas.cc:434
void InitializeDefaults()
Definition canvas.cc:260
void SetAutoResize(bool auto_resize)
Definition canvas.h:291
RenderResult Render(gfx::Bitmap &atlas, bool atlas_ready)
Definition input.cc:30
Tile16NumericShortcutResult ResolveTile16NumericShortcut(bool ctrl_held, int number_index)
void EndCanvas(Canvas &canvas)
void BeginPadding(int i)
Definition style.cc:299
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
void AddTableColumn(Table &table, const std::string &label, GuiElement element)
Definition input.cc:760
void EndPadding()
Definition style.cc:303
void BeginChildWithScrollbar(const char *str_id)
Definition style.cc:315
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:33
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
Definition log.h:146
LayoutDialect DetectLayout(const Rom &rom)
constexpr int kNumTile16Individual
Definition overworld.h:244
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::array< std::array< int, 8 >, 8 > tile_layout