7#include "absl/strings/str_format.h"
17 for (
int q = 0; q < 4; ++q)
41 if (x.group_name != y.group_name || x.palette_index != y.palette_index ||
42 x.color_index != y.color_index ||
43 x.original_color.snes() != y.original_color.snes() ||
44 x.new_color.snes() != y.new_color.snes())
65 return owner && !owner->overworld_graphics_preview.empty()
66 ? owner->GetAbsolutePath(owner->overworld_graphics_preview)
78 return absl::FailedPreconditionError(
79 "Project graphics draft belongs to a different project binding");
83 for (
const auto& [parent, characters] : pending)
84 for (
const auto& [
id, pixels] : characters)
85 preview[parent][
id] = pixels;
97 return absl::OkStatus();
106 return absl::FailedPreconditionError(
"Open project graphics first");
112absl::StatusOr<ProjectGraphicsImportContext>
118 return absl::FailedPreconditionError(
119 "Select the native area bound to the open graphics source");
123 const auto [width, height] = AreaDimensions(*map);
124 const int parent = map->parent();
125 const int origin_x = (parent % 8) * 32;
126 const int origin_y = ((parent % 64) / 8) * 32;
128 std::vector<uint16_t> grid;
129 grid.reserve(width * height);
130 for (
int y = 0; y < height; ++y) {
131 for (
int x = 0; x < width; ++x) {
132 if (origin_x + x >=
static_cast<int>(tiles.size()) ||
133 origin_y + y >=
static_cast<int>(tiles[origin_x + x].size()))
134 return absl::OutOfRangeError(
"Area exceeds the loaded world grid");
135 grid.push_back(tiles[origin_x + x][origin_y + y]);
142absl::StatusOr<ProjectGraphicsImportPlan>
146 return absl::FailedPreconditionError(
"Open a project graphics resource");
149 return absl::FailedPreconditionError(
150 "Graphics resource changed; preview again");
151 const auto slice = std::find_if(
152 resource->slices.begin(), resource->slices.end(), [&](
const auto& s) {
153 return s.profile_id == graphics_source_->profile_id() &&
154 s.source_offset == 0 &&
155 s.byte_count == resource->pending_bytes.size() &&
156 s.first_character() == bindings.resource_first_character;
158 if (slice == resource->slices.end())
159 return absl::FailedPreconditionError(
160 "Import requires a complete, contiguous resource transfer in this "
169 std::set<int> changed;
170 for (
const auto& definition : plan.definitions)
172 definition.tile_data))
173 changed.insert(definition.tile_id);
175 const int ox = (parent % 8) * 32;
176 const int oy = ((parent % 64) / 8) * 32;
177 for (
int world = 0; world < 3; ++world) {
179 for (
int x = 0; x < static_cast<int>(tiles.size()); ++x) {
180 for (
int y = 0; y < static_cast<int>(tiles[x].size()); ++y) {
181 const bool inside = world == parent / 64 &&
184 x < ox + bindings.
anchor_x + asset.width &&
185 y < oy + bindings.
anchor_y + asset.height;
186 if (!inside && changed.contains(tiles[x][y]))
187 return absl::FailedPreconditionError(absl::StrFormat(
188 "Tile16 $%03X is placed outside the import at world %d (%d,%d)",
189 tiles[x][y], world, x, y));
193 std::set<int> shared_parents;
196 if (!other || other->parent() !=
id ||
id == parent)
200 for (
const auto& address : other->palette_addresses()) {
203 for (
const auto& change : plan.palette_changes) {
204 if (change.group_name ==
206 change.palette_index == address->palette_index &&
207 change.color_index == address->color_index)
208 shared_parents.insert(
id);
212 if (!shared_parents.empty()) {
214 for (
int id : shared_parents)
215 maps += absl::StrFormat(
"$%02X ",
id);
216 plan.notes.push_back(
"Shared palette records also load in parent maps: " +
227 return absl::FailedPreconditionError(
228 "Publish or undo pixel edits before importing a graphics pack");
234 return absl::FailedPreconditionError(
235 "The pack or destination changed after review; preview the import "
241 const auto slices = resource->slices;
244 const auto resolve = [
this, source, owner,
246 -> absl::StatusOr<std::string> {
248 return absl::FailedPreconditionError(
249 "Import history belongs to another project");
250 for (
const auto& candidate : document.resources()) {
251 if (candidate.slices.size() != slices.size())
254 for (
size_t i = 0; i < slices.size(); ++i) {
255 const auto& a = slices[i];
256 const auto& b = candidate.slices[i];
257 same &= a.profile_id == b.profile_id &&
258 a.parent_maps == b.parent_maps &&
259 a.source_offset == b.source_offset &&
260 a.byte_count == b.byte_count &&
261 a.vram_word_address == b.vram_word_address &&
262 a.bg_character_base_word == b.bg_character_base_word;
265 return candidate.source_path;
267 return absl::FailedPreconditionError(
268 "Imported resource consumer binding changed");
273 source, resolve, plan, std::move(definitions),
game_data_,
277 const auto applied = action->Redo();
289 return absl::OkStatus();
295 return absl::FailedPreconditionError(
"Import a graphics pack first");
300 bindings.anchor_x = world_x - (parent % 8) * 32;
301 bindings.anchor_y = world_y - ((parent % 64) / 8) * 32;
304 if (plan.resource_after != plan.resource_before ||
305 !plan.palette_changes.empty())
306 return absl::FailedPreconditionError(
307 "This background needs another graphics import with separately owned "
309 for (
const auto& definition : plan.definitions)
311 definition.tile_data))
312 return absl::FailedPreconditionError(
313 "This background needs different Tile16 definitions; preview a "
315 return tile_painting_->PlaceBrushAt(plan.stamp, world_x, world_y);
void set_dirty(bool dirty)
EditorContext context() const
UndoManager undo_manager_
ProjectGraphicsImportBindings graphics_import_applied_bindings_
bool IsProjectTerrainMap(int map_id) const
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > maps_bmp_
std::string graphics_import_pack_
void ForceRefreshGraphics(int map_index)
zelda3::GameData * game_data_
absl::Status ApplyProjectGraphicsImport(const std::string &pack, const ProjectGraphicsImportBindings &bindings)
zelda3::OverworldGraphicsPreview graphics_preview_baseline_
std::string ProjectGraphicsPath() const
Tile16Editor tile16_editor_
absl::Status SyncProjectGraphicsPreview(bool force=false)
absl::StatusOr< ProjectGraphicsImportPlan > PreviewProjectGraphicsImport(const std::string &pack, const ProjectGraphicsImportBindings &bindings)
absl::Status ReloadProjectGraphics()
bool is_lyra_preview() const
void RefreshOverworldMapOnDemand(int map_index)
absl::Status RefreshPendingProjectGraphics()
bool graphics_import_applied_
absl::Status RefreshTile16Blockset()
void FinalizePaintOperation()
std::shared_ptr< ProjectGraphicsSource > graphics_source_
bool graphics_import_ranges_reviewed_
const project::YazeProject * graphics_source_project_
project::YazeProject * TerrainProject() const
std::optional< ProjectGraphicsImportPlan > graphics_import_plan_
absl::Status PlaceProjectGraphicsImport(int world_x, int world_y)
std::unique_ptr< TilePaintingManager > tile_painting_
absl::Status PublishProjectGraphicsSource()
gfx::BitmapTable current_graphics_set_
zelda3::Overworld overworld_
ProjectGraphicsImportBindings graphics_import_bindings_
absl::StatusOr< ProjectGraphicsImportContext > GraphicsImportContext()
absl::StatusOr< std::unique_ptr< UndoAction > > PrepareDefinitionImport(const std::vector< Tile16Commit > &definitions)
void Push(std::unique_ptr< UndoAction > action)
Tile composition of four 8x8 tiles.
Represents a single Overworld map screen.
void SyncAreaProperties(int map_index)
Copy the area parent's render settings (main palette, animated GFX, tile GFX groups,...
const std::vector< gfx::Tile16 > & tiles16() const
auto overworld_map(int i) const
void SetGraphicsPreview(OverworldGraphicsPreview preview)
auto mutable_overworld_map(int i)
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
OverworldBlockset & GetMapTiles(int world_type)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
bool SameDefinition(const gfx::Tile16 &a, const gfx::Tile16 &b)
std::pair< int, int > AreaDimensions(const zelda3::OverworldMap &map)
bool SameReviewedPlan(const ProjectGraphicsImportPlan &a, const ProjectGraphicsImportPlan &b)
Editors are the view controllers for the application.
absl::StatusOr< ProjectGraphicsImportAsset > LoadProjectGraphicsImport(const std::filesystem::path &logical_pack)
absl::StatusOr< ProjectGraphicsImportContext > CaptureProjectGraphicsImportContext(const zelda3::OverworldMap &map, const std::vector< gfx::Tile16 > &definitions, const std::vector< uint16_t > &grid, int width, int height)
absl::StatusOr< ProjectGraphicsImportPlan > PlanProjectGraphicsImport(const ProjectGraphicsImportAsset &asset, const ProjectGraphicsImportBindings &bindings, const ProjectGraphicsImportContext &context)
absl::StatusOr< std::unique_ptr< UndoAction > > CreateProjectGraphicsImportAction(ProjectGraphicsImportResourceCallbacks resource, const ProjectGraphicsImportPlan &plan, std::unique_ptr< UndoAction > prepared_definitions, const zelda3::GameData *owner, ProjectGraphicsImportRefresh refresh, std::string description)
uint16_t TileInfoToShort(TileInfo tile_info)
constexpr int kNumOverworldMaps
const char * PaletteAddressGroupName(PaletteAddressGroup group)
const gfx::TileInfo & Tile16QuadrantInfo(const gfx::Tile16 &tile, int quadrant)
#define RETURN_IF_ERROR(expr)
std::vector< uint8_t > target_resource
std::vector< uint8_t > resource_before
std::vector< Tile16Commit > definitions
std::vector< uint8_t > resource_after
std::string source_sha256
std::vector< gfx::PaletteColorChange > palette_changes
std::vector< std::string > notes
std::vector< int > tile_ids
std::string GetAbsolutePath(const std::string &relative_path) const