8#include "absl/status/status.h"
9#include "absl/status/statusor.h"
10#include "absl/strings/str_format.h"
19#include "imgui/imgui.h"
20#include "nlohmann/json.hpp"
48 if (!std::filesystem::exists(path)) {
49 return absl::OkStatus();
52 std::ifstream input(path);
53 if (!input.is_open()) {
54 return absl::InternalError(
55 absl::StrFormat(
"Could not open %s", path.string()));
58 auto root = nlohmann::json::parse(input,
nullptr,
false);
59 if (root.is_discarded() || !root.is_object()) {
60 return absl::DataLossError(
61 absl::StrFormat(
"Invalid scratch pad JSON in %s", path.string()));
73 if (root.contains(
"tiles") && root[
"tiles"].is_array()) {
74 const auto& rows = root[
"tiles"];
75 for (
size_t y = 0; y < rows.size() && y < 32; ++y) {
76 if (!rows[y].is_array()) {
79 const auto& row = rows[y];
80 for (
size_t x = 0; x < row.size() && x < 32; ++x) {
94 root[
"version"] = kScratchPadVersion;
99 root[
"tiles"] = nlohmann::json::array();
101 for (
int y = 0; y < root[
"height"].get<
int>(); ++y) {
102 nlohmann::json row = nlohmann::json::array();
103 for (
int x = 0; x < root[
"width"].get<
int>(); ++x) {
106 root[
"tiles"].push_back(std::move(row));
109 std::ofstream output(path, std::ios::trunc);
110 if (!output.is_open()) {
111 return absl::InternalError(
112 absl::StrFormat(
"Could not write %s", path.string()));
114 output << root.dump(2) <<
'\n';
115 return absl::OkStatus();
120 return absl::OkStatus();
124 return absl::OkStatus();
134 std::vector<uint8_t> empty_data(bitmap_width * bitmap_height, 0);
154 return absl::OkStatus();
159 ImGui::TextDisabled(
"Lyra preview: scratch editing unavailable");
160 return absl::OkStatus();
165 if (Button(tr(
"Clear"))) {
179 " Overworld selection active! Click in scratch space to stamp.");
181 Text(tr(
"Left-click to paint with current tile."));
182 Text(tr(
"Right-click to select tiles."));
189 std::vector<uint8_t> empty_data(bitmap_width * bitmap_height, 0);
219 scratch_content_size);
233 if (canvas_rt.hovered) {
236 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
240 ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
249 if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
253 return absl::OkStatus();
260 int tile_x =
static_cast<int>(mouse_position.x) / grid_size;
261 int tile_y =
static_cast<int>(mouse_position.y) / grid_size;
266 if (tile_x >= 0 && tile_x < max_width && tile_y >= 0 && tile_y < max_height) {
267 bool changed =
false;
268 if (tile_x < 32 && tile_y < 32) {
292 int start_tile_x =
static_cast<int>(mouse_position.x) / 32;
293 int start_tile_y =
static_cast<int>(mouse_position.y) / 32;
304 if (tile_ids.empty())
311 for (
int py = 0; py < pattern_height && (start_tile_y + py) < max_height;
313 for (
int px = 0; px < pattern_width && (start_tile_x + px) < max_width;
315 if (idx <
static_cast<int>(tile_ids.size())) {
316 int tile_id = tile_ids[idx];
317 int scratch_x = start_tile_x + px;
318 int scratch_y = start_tile_y + py;
320 if (scratch_x >= 0 && scratch_x < 32 && scratch_y >= 0 &&
333 absl::StrFormat(
"Pattern %dx%d", pattern_width, pattern_height);
343 if (tile_data.empty())
346 const int grid_size = 32;
350 for (
int y = 0; y < 16; ++y) {
351 for (
int x = 0; x < 16; ++x) {
352 int src_index = y * 16 + x;
354 int dst_x = tile_x * grid_size + x + x;
355 int dst_y = tile_y * grid_size + y + y;
357 if (dst_x >= 0 && dst_x < scratch_bitmap_width && dst_y >= 0 &&
358 dst_y < scratch_bitmap_height &&
359 src_index <
static_cast<int>(tile_data.size())) {
360 for (
int py = 0; py < 2 && (dst_y + py) < scratch_bitmap_height; ++py) {
361 for (
int px = 0; px < 2 && (dst_x + px) < scratch_bitmap_width;
363 int dst_index = (dst_y + py) * scratch_bitmap_width + (dst_x + px);
365 tile_data[src_index]);
393 bitmap_width, bitmap_height, 8,
394 std::vector<uint8_t>(bitmap_width * bitmap_height, 0));
404 const int tile_id = brush->at(x, y);
418 return absl::OkStatus();
423 return absl::FailedPreconditionError(
"Scratch space is empty");
428 return absl::OkStatus();
443 std::fill(data.begin(), data.end(), 0);
EditorDependencies dependencies_
bool map_blockset_loaded_
absl::Status DrawScratchSpace()
absl::Status LoadScratchToSelection()
gfx::Tilemap tile16_blockset_
absl::Status ClearScratchSpace()
gui::Canvas ow_map_canvas_
ScratchSpace scratch_space_
void MarkScratchSpaceDirty()
bool is_lyra_preview() const
void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id)
void DrawScratchSpacePattern()
gui::Canvas scratch_canvas_
absl::Status FlushScratchPadIfDirty()
void DrawScratchSpaceEdits()
bool scratch_space_dirty_
absl::Status SaveCurrentSelectionToScratch()
absl::Status LoadScratchPad()
absl::Status RebuildScratchBitmapFromTileData()
std::unique_ptr< TilePaintingManager > tile_painting_
zelda3::Overworld overworld_
absl::Status SaveScratchPad() const
gfx::SnesPalette palette_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void set_modified(bool modified)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
std::vector< uint8_t > & mutable_data()
RAII timer for automatic timing management.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
auto select_rect_active() const
auto selected_tiles() const
auto drawn_tile_position() const
bool DrawTileSelector(int size, int size_y=0)
const gfx::SnesPalette & current_area_palette() const
#define ICON_MD_CONTENT_PASTE
#define ASSIGN_OR_RETURN(type_variable_name, expression)
#define HOVER_HINT(string)
constexpr int kScratchPadVersion
constexpr const char * kScratchPadFileName
absl::StatusOr< std::filesystem::path > GetScratchPadPath()
Editors are the view controllers for the application.
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
void EndCanvas(Canvas &canvas)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
void BeginChildWithScrollbar(const char *str_id)
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
#define RETURN_IF_ERROR(expr)
SharedClipboard * shared_clipboard
gfx::Bitmap scratch_bitmap
std::array< std::array< int, 32 >, 32 > tile_data
std::vector< int > overworld_tile16_ids
bool has_overworld_tile16
std::optional< float > grid_step