145 if (!owner || binding.owner != owner)
146 return absl::FailedPreconditionError(
"Palette address session mismatch");
147 if (binding.colors[0] || target.
palette.
size() != 8 || binding.row < 2 ||
149 (binding.half_offset != 0 && binding.half_offset != 8)) {
150 return absl::FailedPreconditionError(
"Invalid raw palette address mapping");
152 for (
int raw = 1; raw < 8; ++raw) {
153 if (!binding.colors[raw])
154 return absl::FailedPreconditionError(
"Nonzero palette index is unmapped");
157 return color.status();
158 const auto expected = color->rom_color();
159 const auto displayed = target.
palette[raw].rom_color();
160 if (expected.red != displayed.red || expected.green != displayed.green ||
161 expected.blue != displayed.blue) {
162 return absl::FailedPreconditionError(
163 "Displayed palette differs from its writable source; refresh the "
167 return absl::OkStatus();
203 return absl::FailedPreconditionError(
204 "Graphics source changed; reopen Edit Graphics from the source.");
206 for (
size_t i = 0; i < context.
targets.size(); ++i) {
207 const auto& old = context.
targets[i];
208 const auto& next = refreshed.
targets[i];
209 if (old.sheet_id != next.sheet_id || old.tile_index != next.tile_index ||
210 old.label != next.label ||
211 old.source_orientation_mask != next.source_orientation_mask ||
212 next.palette.size() != 8) {
213 return absl::FailedPreconditionError(
214 "Graphics source changed; reopen Edit Graphics from the source.");
216 if (next.writable_palette) {
223 for (
size_t i = 0; i < context.
targets.size(); ++i) {
224 context.
targets[i].writable_palette =
225 std::move(refreshed.
targets[i].writable_palette);
226 context.
targets[i].palette_address_limitation =
227 std::move(refreshed.
targets[i].palette_address_limitation);
228 context.
targets[i].palette = std::move(refreshed.
targets[i].palette);
231 return absl::OkStatus();
237 if (!owner || room.
game_data() != owner) {
238 return absl::FailedPreconditionError(
"Dungeon graphics session mismatch");
240 auto tiles =
object.GetTiles();
242 return tiles.status();
246 return patterns.status();
247 std::vector<gfx::TileInfo> source_tiles;
252 return parsed.status();
253 source_tiles = std::move(*parsed);
254 tiles = std::span<const gfx::TileInfo>(source_tiles);
257 context.
owner = owner;
259 absl::StrFormat(
"Room %03X / Object %03X / Main %02X", room.
id(),
264 auto traced = TraceObjectPattern(room,
object, context.
limitation);
266 context.
limitation = std::string(traced.status().message());
271 std::vector<gfx::TileInfo> drawn_tiles;
272 if (traced.ok() && !traced->empty()) {
273 for (
const auto& cell : *traced) {
275 tile.
id_ = cell.tile_id;
276 tile.
palette_ = (cell.flags >> 3) & 7;
279 tile.
over_ = (cell.flags & 4) != 0;
280 drawn_tiles.push_back(tile);
282 tiles = std::span<const gfx::TileInfo>(drawn_tiles);
285 const auto blocks = room.
blocks();
286 std::map<std::pair<uint16_t, uint8_t>,
size_t> target_indices;
287 for (
const auto& tile : *tiles) {
288 if (tile.id_ >= 512 || tile.palette_ >= 8 ||
289 (tile.id_ >= 0x1B0 && tile.id_ < 0x1D0)) {
294 "Animated or unmapped tiles are omitted from pixel editing.";
297 const uint8_t orientation =
298 (tile.horizontal_mirror_ ? 1 : 0) | (tile.vertical_mirror_ ? 2 : 0);
299 const uint8_t orientation_bit = 1 << orientation;
300 const auto [entry, inserted] = target_indices.emplace(
301 std::make_pair(tile.id_, tile.palette_), context.
targets.size());
303 context.
targets[entry->second].source_orientation_mask |= orientation_bit;
306 const int slot = tile.id_ / 64;
307 const int offset = tile.palette_ * 16;
308 if (colors.size() <
static_cast<size_t>(offset + 16)) {
309 return absl::FailedPreconditionError(
"Room palette is not rendered");
312 std::vector<gfx::SnesColor> palette;
313 for (
int index = 0; index < 8; ++index) {
314 const auto& color = colors[offset + (index == 0 ? 0 : half + index)];
315 palette.emplace_back(color.r, color.g, color.b);
318 .sheet_id = blocks[slot],
319 .tile_index =
static_cast<uint16_t
>(tile.id_ % 64),
320 .label = absl::StrFormat(
"Tile %03X / slot %d / sheet %02X / row %d %s",
321 tile.id_, slot, blocks[slot], tile.palette_,
322 half == 8 ?
"right" :
"left"),
324 .source_orientation_mask = orientation_bit,
328 tile.palette_, half);
331 return absl::UnimplementedError(
332 "This object's tiles do not yet have an editable graphics source");
334 if (traced.ok() && !traced->empty()) {
335 int min_x = std::numeric_limits<int>::max();
337 int max_x = std::numeric_limits<int>::min();
339 for (
const auto& cell : *traced) {
340 min_x = std::min(min_x,
static_cast<int>(cell.x_tile));
341 min_y = std::min(min_y,
static_cast<int>(cell.y_tile));
342 max_x = std::max(max_x,
static_cast<int>(cell.x_tile));
343 max_y = std::max(max_y,
static_cast<int>(cell.y_tile));
347 std::set<std::tuple<int, int, size_t, bool, bool>> seen;
348 for (
const auto& cell : *traced) {
349 const uint8_t palette = (cell.flags >> 3) & 7;
350 const auto target = target_indices.find({cell.tile_id, palette});
351 if (target == target_indices.end())
353 const int x = cell.x_tile - min_x;
354 const int y = cell.y_tile - min_y;
355 const bool h = (cell.flags & 1) != 0;
356 const bool v = (cell.flags & 2) != 0;
357 if (seen.emplace(x, y, target->second, h, v).second) {
358 context.
pattern_cells.push_back({x, y, target->second, h, v});
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr, gfx::BackgroundBuffer *layout_bg2=nullptr)
Draw a room object to background buffers.
auto ObjectIdentity(const zelda3::RoomObject &object)
absl::StatusOr< std::vector< zelda3::ObjectDrawer::TileTrace > > TraceObjectPattern(const zelda3::Room &room, const zelda3::RoomObject &object, std::string &limitation)
zelda3::ResolvedGraphicsContext resolved
std::vector< GraphicsEditTarget > targets
std::vector< GraphicsSourceCell > pattern_cells
const zelda3::GameData * owner