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();
161 if (Button(tr(
"Clear"))) {
175 " Overworld selection active! Click in scratch space to stamp.");
177 Text(tr(
"Left-click to paint with current tile."));
178 Text(tr(
"Right-click to select tiles."));
185 std::vector<uint8_t> empty_data(bitmap_width * bitmap_height, 0);
215 scratch_content_size);
229 if (canvas_rt.hovered) {
232 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
236 ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
245 if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
249 return absl::OkStatus();
256 int tile_x =
static_cast<int>(mouse_position.x) / grid_size;
257 int tile_y =
static_cast<int>(mouse_position.y) / grid_size;
262 if (tile_x >= 0 && tile_x < max_width && tile_y >= 0 && tile_y < max_height) {
263 bool changed =
false;
264 if (tile_x < 32 && tile_y < 32) {
288 int start_tile_x =
static_cast<int>(mouse_position.x) / 32;
289 int start_tile_y =
static_cast<int>(mouse_position.y) / 32;
300 if (tile_ids.empty())
307 for (
int py = 0; py < pattern_height && (start_tile_y + py) < max_height;
309 for (
int px = 0; px < pattern_width && (start_tile_x + px) < max_width;
311 if (idx <
static_cast<int>(tile_ids.size())) {
312 int tile_id = tile_ids[idx];
313 int scratch_x = start_tile_x + px;
314 int scratch_y = start_tile_y + py;
316 if (scratch_x >= 0 && scratch_x < 32 && scratch_y >= 0 &&
329 absl::StrFormat(
"Pattern %dx%d", pattern_width, pattern_height);
339 if (tile_data.empty())
342 const int grid_size = 32;
346 for (
int y = 0; y < 16; ++y) {
347 for (
int x = 0; x < 16; ++x) {
348 int src_index = y * 16 + x;
350 int dst_x = tile_x * grid_size + x + x;
351 int dst_y = tile_y * grid_size + y + y;
353 if (dst_x >= 0 && dst_x < scratch_bitmap_width && dst_y >= 0 &&
354 dst_y < scratch_bitmap_height &&
355 src_index <
static_cast<int>(tile_data.size())) {
356 for (
int py = 0; py < 2 && (dst_y + py) < scratch_bitmap_height; ++py) {
357 for (
int px = 0; px < 2 && (dst_x + px) < scratch_bitmap_width;
359 int dst_index = (dst_y + py) * scratch_bitmap_width + (dst_x + px);
361 tile_data[src_index]);
380 if (selected_points.size() >= 2) {
382 const auto start = selected_points[0];
383 const auto end = selected_points[1];
385 int selection_width =
386 std::abs(
static_cast<int>((end.x - start.x) / 16)) + 1;
387 int selection_height =
388 std::abs(
static_cast<int>((end.y - start.y) / 16)) + 1;
398 std::vector<uint8_t> empty_data(bitmap_width * bitmap_height, 0);
424 if (x < 32 && y < 32) {
442 return absl::OkStatus();
447 return absl::FailedPreconditionError(
"Scratch space is empty");
452 return absl::OkStatus();
467 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()
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()
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)
auto selected_points() const
void set_current_world(int world)
int GetTileFromPosition(ImVec2 position) const
const gfx::SnesPalette & current_area_palette() const
void set_current_map(int i)
#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