yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_canvas_overlays.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <array>
3#include <cstdint>
4#include <span>
5#include <string>
6#include <string_view>
7#include <vector>
8#include "util/i18n/tr.h"
9
10#include "absl/strings/str_format.h"
18#include "imgui/imgui.h"
26
27namespace yaze::editor {
28
29namespace {
30
32 const std::vector<uint8_t>& preview,
33 const SDL_Rect& bounds, int room_x, int room_y,
34 const std::array<SDL_Color, 256>& color_table) {
35 if (!rt.draw_list || bounds.w <= 0 || bounds.h <= 0) {
36 return false;
37 }
38 const size_t row_stride = static_cast<size_t>(bounds.w);
39 if (static_cast<size_t>(bounds.h) > preview.size() / row_stride) {
40 return false;
41 }
42
43 const float scale = std::max(rt.scale, 0.01f);
44 const ImVec2 origin(rt.canvas_p0.x + rt.scrolling.x,
45 rt.canvas_p0.y + rt.scrolling.y);
46 const ImVec2 canvas_max(rt.canvas_p0.x + rt.canvas_sz.x,
47 rt.canvas_p0.y + rt.canvas_sz.y);
48 bool drew_any_pixel = false;
49
50 rt.draw_list->PushClipRect(rt.canvas_p0, canvas_max, true);
51 for (int y = 0; y < bounds.h; ++y) {
52 int x = 0;
53 while (x < bounds.w) {
54 const uint8_t palette_index =
55 preview[static_cast<size_t>(y) * row_stride + x];
56 const SDL_Color& palette_color = color_table[palette_index];
57 // RenderPreviewGraphics reserves index 0 for transparency. Unlike the
58 // former 0xFF sentinel, index 0 cannot collide with a visible dungeon
59 // sprite pixel: the preview encoder emits CGRAM indices 0x71..0xFF.
60 if (palette_index == 0 || palette_color.a == 0) {
61 ++x;
62 continue;
63 }
64 const ImU32 color = IM_COL32(palette_color.r, palette_color.g,
65 palette_color.b, palette_color.a);
66
67 const int start_x = x;
68 while (x < bounds.w && preview[static_cast<size_t>(y) * row_stride + x] ==
69 palette_index) {
70 ++x;
71 }
72
73 const ImVec2 min(origin.x + (room_x + start_x) * scale,
74 origin.y + (room_y + y) * scale);
75 const ImVec2 max(origin.x + (room_x + x) * scale,
76 origin.y + (room_y + y + 1) * scale);
77 rt.draw_list->AddRectFilled(min, max, color);
78 drew_any_pixel = true;
79 }
80 }
81 rt.draw_list->PopClipRect();
82
83 return drew_any_pixel;
84}
85
86const char* GetStoredPlacementLabel(const zelda3::RoomObject& object) {
87 const int layer_value = object.GetLayerValue();
88 if (!zelda3::UsesRoomObjectStream(object)) {
89 if (layer_value == 0) {
90 return "Upper BG1";
91 }
92 return layer_value == 1 ? "Lower BG2" : "Invalid selector";
93 }
94 switch (layer_value) {
95 case 0:
96 return "Primary";
97 case 1:
98 return "BG2 overlay";
99 case 2:
100 return "BG1 overlay";
101 default:
102 return "Unknown";
103 }
104}
105
106} // namespace
107
109 const zelda3::Room& room) {
111 return;
112 }
113
114 const auto& theme = AgentUI::GetTheme();
115 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
116 const int entity_size = is_touch ? 24 : 16;
117 const auto sprite_colors =
119 const std::string_view hack_name =
120 project_ != nullptr && project_->hack_manifest.loaded()
121 ? std::string_view(project_->hack_manifest.hack_name())
122 : std::string_view{};
124 project_ ? project_->filepath : std::string{},
126 : std::string{},
127 hack_name)) {
128 sprite_preview_cache_.Clear();
129 }
130 const auto& room_gfx = room.get_gfx_buffer();
131 const std::span<const uint8_t> room_gfx_span(room_gfx.data(),
132 room_gfx.size());
133
134 for (const auto& sprite : room.GetSprites()) {
135 const int canvas_x = sprite.x() * 16;
136 const int canvas_y = sprite.y() * 16;
137
138 if (canvas_x >= -entity_size && canvas_y >= -entity_size &&
139 canvas_x < 512 + entity_size && canvas_y < 512 + entity_size) {
140 ImVec4 sprite_color = sprite.layer() == 0 ? theme.dungeon_sprite_layer0
141 : theme.dungeon_sprite_layer1;
142
143 const auto* preview_layout =
144 zelda3::SpriteOamRegistry::GetPreviewOverride(sprite.id(), hack_name);
145 const SpritePreviewKey preview_key{room.graphics_revision(), sprite.id(),
146 sprite.subtype(), sprite.IsOverlord()};
147 // Revision zero means the room has not assembled its graphics yet and
148 // is not a reusable graphics identity.
149 auto* preview = room.graphics_revision() != 0
150 ? sprite_preview_cache_.Get(preview_key)
151 : nullptr;
152 CachedSpritePreview uncached_preview;
153 if (preview == nullptr) {
154 zelda3::Sprite preview_sprite = sprite;
155 preview_sprite.RenderPreviewGraphics(
156 room_gfx_span, preview_layout,
157 sprite_preview_resources_.GetGraphics(preview_layout));
158 CachedSpritePreview rendered{*preview_sprite.preview_graphics(),
159 preview_sprite.preview_bounds()};
160 if (room.graphics_revision() != 0) {
161 preview =
162 sprite_preview_cache_.Insert(preview_key, std::move(rendered));
163 } else {
164 uncached_preview = std::move(rendered);
165 preview = &uncached_preview;
166 }
167 }
168 const SDL_Rect preview_bounds = preview->bounds;
169 const bool drew_preview = DrawSpritePreviewPixels(
170 rt, preview->pixels, preview_bounds, canvas_x + preview_bounds.x,
171 canvas_y + preview_bounds.y, sprite_colors);
172
173 if (drew_preview) {
174 gui::DrawOutline(rt, canvas_x, canvas_y, entity_size, entity_size,
175 ImGui::GetColorU32(sprite_color));
176 } else {
177 gui::DrawRect(rt, canvas_x, canvas_y, entity_size, entity_size,
178 sprite_color);
179 }
180
181 std::string full_name = preview_layout != nullptr
182 ? preview_layout->name
183 : zelda3::GetSpriteLabel(sprite.id());
184 std::string sprite_text;
185 if (full_name.length() > 12) {
186 sprite_text = absl::StrFormat("%02X %s..", sprite.id(),
187 full_name.substr(0, 8).c_str());
188 } else {
189 sprite_text =
190 absl::StrFormat("%02X %s", sprite.id(), full_name.c_str());
191 }
192
193 gui::DrawText(rt, sprite_text, canvas_x, canvas_y);
194 }
195 }
196}
197
199 const zelda3::Room& room) {
201 return;
202 }
203
204 const auto& pot_items = room.GetPotItems();
205 if (pot_items.empty()) {
206 return;
207 }
208
209 static const char* kPotItemNames[] = {
210 "Nothing", "Green Rupee", "Rock", "Bee", "Health",
211 "Bomb", "Heart", "Blue Rupee", "Key", "Arrow",
212 "Bomb", "Heart", "Magic", "Full Magic", "Cucco",
213 "Green Soldier", "Bush Stal", "Blue Soldier", "Landmine", "Heart",
214 "Fairy", "Heart", "Nothing", "Hole", "Warp",
215 "Staircase", "Bombable", "Switch"};
216 constexpr size_t kPotItemNameCount =
217 sizeof(kPotItemNames) / sizeof(kPotItemNames[0]);
218
219 for (const auto& pot_item : pot_items) {
220 const int pixel_x = pot_item.GetPixelX();
221 const int pixel_y = pot_item.GetPixelY();
222 auto [canvas_x, canvas_y] =
224 pixel_y / 8);
225
226 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
227 const int entity_size = is_touch ? 24 : 16;
228
229 if (canvas_x >= -entity_size && canvas_y >= -entity_size &&
230 canvas_x < 512 + entity_size && canvas_y < 512 + entity_size) {
231 const auto& theme = AgentUI::GetTheme();
232 ImVec4 pot_item_color;
233 if (pot_item.item == 0) {
234 pot_item_color = theme.status_inactive;
235 pot_item_color.w = 0.4f;
236 } else {
237 pot_item_color = theme.item_color;
238 pot_item_color.w = 0.75f;
239 }
240
241 gui::DrawRect(rt, canvas_x, canvas_y, entity_size, entity_size,
242 pot_item_color);
243
244 std::string item_name;
245 if (pot_item.item < kPotItemNameCount) {
246 item_name = kPotItemNames[pot_item.item];
247 } else {
248 item_name = absl::StrFormat("Unk%02X", pot_item.item);
249 }
250
251 std::string item_text =
252 absl::StrFormat("%02X %s", pot_item.item, item_name.c_str());
253 gui::DrawText(rt, item_text, canvas_x, canvas_y - 10);
254 }
255 }
256}
257
259 const zelda3::Room& room) {
260 RenderSprites(rt, room);
261 RenderPotItems(rt, room);
262}
263
265 const gui::CanvasRuntime& rt, const zelda3::Room& room) {
266 constexpr const char* kPopupId = "##TouchEntityContextMenu";
267 const ImGuiIO& io = ImGui::GetIO();
268 const bool touch_context_click =
269 rt.hovered && io.MouseSource == ImGuiMouseSource_TouchScreen &&
270 ImGui::IsMouseClicked(ImGuiMouseButton_Right);
271 const bool gesture_long_press = touch_handler_.WasLongPressed();
272 const bool should_open_context = gesture_long_press || touch_context_click;
273
274 if (should_open_context) {
275 const ImVec2 gesture_pos = gesture_long_press
277 : ImGui::GetMousePos();
278 const DungeonCanvasTransform transform(rt.canvas_p0, rt.scrolling,
279 rt.scale);
280 const ImVec2 room_pos = transform.ScreenToRoomPixels(gesture_pos);
281 const auto [room_x, room_y] =
282 transform.ScreenToRoomPixelCoordinates(gesture_pos);
283 if (!dungeon_coords::IsWithinBounds(room_x, room_y)) {
284 return;
285 }
286 const float rel_x = room_pos.x;
287 const float rel_y = room_pos.y;
288 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
289 const int hit_size = is_touch ? 24 : 16;
290
291 const auto& sprites = room.GetSprites();
292 for (size_t idx = 0; idx < sprites.size(); ++idx) {
293 const int sprite_px = sprites[idx].x() * 16;
294 const int sprite_py = sprites[idx].y() * 16;
295 if (rel_x >= sprite_px && rel_x < sprite_px + hit_size &&
296 rel_y >= sprite_py && rel_y < sprite_py + hit_size) {
298 ImGui::OpenPopup(kPopupId);
299 break;
300 }
301 }
302
303 if (!ImGui::IsPopupOpen(kPopupId)) {
304 const auto& pot_items = room.GetPotItems();
305 for (size_t idx = 0; idx < pot_items.size(); ++idx) {
306 const int item_px = pot_items[idx].GetPixelX();
307 const int item_py = pot_items[idx].GetPixelY();
308 if (rel_x >= item_px && rel_x < item_px + hit_size &&
309 rel_y >= item_py && rel_y < item_py + hit_size) {
311 ImGui::OpenPopup(kPopupId);
312 break;
313 }
314 }
315 }
316
317 if (!ImGui::IsPopupOpen(kPopupId)) {
318 const auto object_index =
320 .tile_handler()
321 .GetEntityAtPosition(static_cast<int>(rel_x),
322 static_cast<int>(rel_y));
323 if (object_index.has_value()) {
325 ImGui::OpenPopup(kPopupId);
326 }
327 }
328 }
329
330 if (ImGui::BeginPopup(kPopupId)) {
333 if (sel.type == EntityType::Sprite) {
334 const auto& sprites = room.GetSprites();
335 if (sel.index < sprites.size()) {
336 std::string label = zelda3::GetSpriteLabel(sprites[sel.index].id());
337 ImGui::TextDisabled(tr("Sprite: %02X %s"), sprites[sel.index].id(),
338 label.c_str());
339 ImGui::Separator();
340 }
341 if (ImGui::MenuItem(tr("Delete Sprite"))) {
343 }
344 } else if (sel.type == EntityType::Item) {
345 ImGui::TextDisabled(tr("Pot Item"));
346 ImGui::Separator();
347 if (ImGui::MenuItem(tr("Delete Item"))) {
349 }
350 }
351 } else if (object_interaction_.GetSelectionCount() > 0) {
352 const auto indices = object_interaction_.GetSelectedObjectIndices();
353 if (indices.size() == 1) {
354 const auto& objects = room.GetTileObjects();
355 if (indices[0] < objects.size()) {
356 std::string name = zelda3::GetObjectName(objects[indices[0]].id_);
357 ImGui::TextDisabled(tr("Object: %03X %s"), objects[indices[0]].id_,
358 name.c_str());
359 ImGui::Separator();
360 }
361 } else {
362 ImGui::TextDisabled(tr("%zu objects selected"),
364 ImGui::Separator();
365 }
366 if (ImGui::MenuItem(tr("Delete"))) {
368 }
369 if (ImGui::MenuItem(tr("Copy"))) {
371 }
372 ImGui::Separator();
373 if (ImGui::MenuItem(tr("Send to Front"))) {
375 }
376 if (ImGui::MenuItem(tr("Send to Back"))) {
378 }
379 }
380 ImGui::EndPopup();
381 }
382}
383
385 const gui::CanvasRuntime& rt, const zelda3::Room& room) {
386 const auto& theme = AgentUI::GetTheme();
387 const auto& objects = room.GetTileObjects();
388
389 for (const auto& obj : objects) {
390 bool show_this_type = true;
391 if (obj.id_ < 0x100) {
393 } else if (obj.id_ >= 0x100 && obj.id_ < 0x200) {
395 } else if (obj.id_ >= 0xF00) {
397 }
398
399 bool show_this_layer = true;
400 if (obj.GetLayerValue() == 0) {
402 } else if (obj.GetLayerValue() == 1) {
404 } else if (obj.GetLayerValue() == 2) {
406 }
407
408 if (!show_this_type || !show_this_layer) {
409 continue;
410 }
411
412 auto [canvas_x, canvas_y, width, height] =
414 width = std::min(width, 512);
415 height = std::min(height, 512);
416
417 ImVec4 outline_color;
418 if (obj.GetLayerValue() == 0) {
419 outline_color = theme.dungeon_outline_layer0;
420 } else if (obj.GetLayerValue() == 1) {
421 outline_color = theme.dungeon_outline_layer1;
422 } else {
423 outline_color = theme.dungeon_outline_layer2;
424 }
425
426 gui::DrawRect(rt, canvas_x, canvas_y, width, height, outline_color);
427
428 std::string name = zelda3::GetObjectName(obj.id_);
429 if (name.length() > 12) {
430 name = name.substr(0, 10) + "..";
431 }
432 std::string label;
433 if (obj.id_ >= 0x100) {
434 label = absl::StrFormat("0x%03X\n%s\n%s [%dx%d]", obj.id_, name.c_str(),
435 GetStoredPlacementLabel(obj), width, height);
436 } else {
437 label = absl::StrFormat("0x%02X\n%s\n%s [%dx%d]", obj.id_, name.c_str(),
438 GetStoredPlacementLabel(obj), width, height);
439 }
440 gui::DrawText(rt, label, canvas_x + 1, canvas_y + 1);
441 }
442}
443
446 auto it = collision_overlay_cache_.find(room_id);
447 if (it != collision_overlay_cache_.end()) {
448 return it->second;
449 }
450
452 cache.entries.clear();
453
454 if (!rom_ || !rom_->is_loaded()) {
455 collision_overlay_cache_.emplace(room_id, cache);
456 return collision_overlay_cache_.at(room_id);
457 }
458
459 auto map_or = zelda3::LoadCustomCollisionMap(rom_, room_id);
460 if (!map_or.ok()) {
461 collision_overlay_cache_.emplace(room_id, cache);
462 return collision_overlay_cache_.at(room_id);
463 }
464
465 const auto& map = map_or.value();
466 cache.has_data = map.has_data;
467 if (cache.has_data && !track_collision_config_.IsEmpty()) {
468 for (int y = 0; y < 64; ++y) {
469 for (int x = 0; x < 64; ++x) {
470 const uint8_t tile = map.tiles[static_cast<size_t>(y * 64 + x)];
471 if (tile < 256 && (track_collision_config_.track_tiles[tile] ||
474 cache.entries.push_back(
476 static_cast<uint8_t>(x), static_cast<uint8_t>(y), tile});
477 }
478 }
479 }
480 }
481
482 collision_overlay_cache_.emplace(room_id, std::move(cache));
483 return collision_overlay_cache_.at(room_id);
484}
485
487 gfx::Bitmap* composite =
489 zelda3::Room* room = rooms_ ? rooms_->GetIfMaterialized(room_id) : nullptr;
490 if (composite != nullptr && room != nullptr) {
492 composite, room->rendered_dungeon_palette(), room->rendered_palette());
493 }
494 return composite;
495}
496
498 int room_id) {
499 auto& entry = connected_composite_outputs_[room_id];
500 if (!entry.output) {
501 entry.output = std::make_unique<RoomCompositeOutput>();
502 }
503 entry.last_used_frame =
504 ImGui::GetCurrentContext() != nullptr ? ImGui::GetFrameCount() : 0;
505 return PrepareRoomCompositeWithOutput(room_id, *entry.output);
506}
507
509 int room_id, RoomCompositeOutput& output) {
510 if (room_id < 0 || room_id >= zelda3::kNumberOfRooms || !rooms_ || !rom_ ||
511 !rom_->is_loaded()) {
512 return nullptr;
513 }
514
515 auto* room_ptr = rooms_->TryEnsureRoom(room_id);
516 if (!room_ptr) {
517 return nullptr;
518 }
519 auto& room = *room_ptr;
520 auto& layer_mgr = GetRoomLayerManager(room_id);
521 auto& composite = output.Prepare(room, layer_mgr);
522 if (!composite.is_active() || composite.width() <= 0) {
523 return nullptr;
524 }
525
526 // Closes the A3 first-frame race for the dungeon room composite, mirroring
527 // the title-screen path (A4). The helper queues CREATE on first sight,
528 // queues UPDATE when RoomLayerManager::CompositeToOutput marked the bitmap
529 // modified, and stamps metadata().purpose so canvas_rendering's diagnostic
530 // log can identify the bitmap.
532 return &composite;
533}
534
536 if (room_id < 0 || room_id >= zelda3::kNumberOfRooms || !rooms_) {
537 return false;
538 }
539
540 const float scale = canvas_.global_scale();
541 gfx::Bitmap* composite = PrepareRoomCompositeBitmap(room_id);
542 if (!composite) {
543 return false;
544 }
545
546 // Single-room canvas rendering prepares the composite after the normal room
547 // state upload pass. Flush here too so workbench mode can draw the freshly
548 // queued room texture in the same frame.
550
551 if (composite->texture()) {
552 canvas_.DrawBitmap(*composite, 0, 0, scale, 255);
553 return true;
554 }
555
556 return false;
557}
558
560 const zelda3::Room& room) {
561 const auto& objects = room.GetTileObjects();
562 const ImVec4 mask_color(0.2f, 0.6f, 1.0f, 0.4f);
563
564 for (const auto& obj : objects) {
565 if (obj.GetLayerValue() != 1) {
566 continue;
567 }
568
569 auto [canvas_x, canvas_y] =
571 auto [width, height] =
573 width = std::min(width, 512);
574 height = std::min(height, 512);
575 gui::DrawRect(rt, canvas_x, canvas_y, width, height, mask_color);
576 }
577}
578
579} // namespace yaze::editor
bool is_loaded() const
Definition rom.h:155
const std::string & hack_name() const
bool loaded() const
Check if the manifest has been loaded.
std::pair< int, int > ScreenToRoomPixelCoordinates(ImVec2 screen) const
ImVec2 ScreenToRoomPixels(ImVec2 screen) const
gfx::Bitmap * PrepareConnectedRoomCompositeBitmap(int room_id)
void RenderPotItems(const gui::CanvasRuntime &rt, const zelda3::Room &room)
gfx::Bitmap * PrepareRoomCompositeWithOutput(int room_id, RoomCompositeOutput &output)
zelda3::SpritePreviewResourceCache sprite_preview_resources_
const DungeonRenderingHelpers::CollisionOverlayCache & GetCollisionOverlayCache(int room_id)
std::map< int, ConnectedRoomCompositeEntry > connected_composite_outputs_
DungeonObjectInteraction object_interaction_
const project::YazeProject * project_
std::unordered_map< int, DungeonRenderingHelpers::CollisionOverlayCache > collision_overlay_cache_
void RenderSprites(const gui::CanvasRuntime &rt, const zelda3::Room &room)
gfx::Bitmap * PrepareRoomCompositeBitmap(int room_id)
zelda3::RoomLayerManager & GetRoomLayerManager(int room_id)
void DrawMaskHighlights(const gui::CanvasRuntime &rt, const zelda3::Room &room)
util::LruCache< SpritePreviewKey, CachedSpritePreview > sprite_preview_cache_
void RenderEntityOverlay(const gui::CanvasRuntime &rt, const zelda3::Room &room)
std::tuple< uint64_t, uint8_t, int, bool > SpritePreviewKey
DungeonRenderingHelpers::TrackCollisionConfig track_collision_config_
void DrawObjectPositionOutlines(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void HandleTouchLongPressContextMenu(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void SetSelectedObjects(const std::vector< size_t > &indices)
std::vector< size_t > GetSelectedObjectIndices() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
void SelectEntity(EntityType type, size_t index)
static std::pair< int, int > RoomToCanvasCoordinates(int room_x, int room_y)
zelda3::Room * GetIfMaterialized(int room_id)
zelda3::Room * TryEnsureRoom(int room_id)
void DeleteSelectedEntity()
Delete currently selected entity.
gfx::Bitmap & Prepare(zelda3::Room &room, const zelda3::RoomLayerManager &layer_manager)
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:69
TextureHandle texture() const
Definition bitmap.h:403
bool WasLongPressed() const
Check if a long-press gesture occurred this frame.
ImVec2 GetGesturePosition() const
Get the position of the last tap/gesture.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1173
auto global_scale() const
Definition canvas.h:403
static DimensionService & Get()
std::tuple< int, int, int, int > GetSelectionBoundsPixels(const RoomObject &obj) const
std::pair< int, int > GetPixelDimensions(const RoomObject &obj) const
static PaletteDebugger & Get()
void SetCurrentPresentation(gfx::Bitmap *bitmap, const gfx::SnesPalette &palette, const std::vector< SDL_Color > &render_palette)
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
Definition room.h:1019
const std::vector< SDL_Color > & rendered_palette() const
Definition room.h:1034
const gfx::SnesPalette & rendered_dungeon_palette() const
Definition room.h:1031
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:276
uint64_t graphics_revision() const
Definition room.h:1024
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:405
const std::vector< PotItem > & GetPotItems() const
Definition room.h:389
static const SpriteOamLayout * GetPreviewOverride(uint8_t sprite_id, std::string_view hack_name)
std::span< const uint8_t > GetGraphics(const SpriteOamLayout *layout)
bool SetContext(std::string_view project_path, std::string_view assets_path, std::string_view hack_name)
A class for managing sprites in the overworld and underworld.
Definition sprite.h:39
void RenderPreviewGraphics(std::span< const uint8_t > graphics, const SpriteOamLayout *layout_override=nullptr, std::span< const uint8_t > graphics_resource={})
Definition sprite.cc:1194
auto preview_graphics() const
Definition sprite.h:103
SDL_Rect preview_bounds() const
Definition sprite.h:106
const AgentUITheme & GetTheme()
bool DrawSpritePreviewPixels(const gui::CanvasRuntime &rt, const std::vector< uint8_t > &preview, const SDL_Rect &bounds, int room_x, int room_y, const std::array< SDL_Color, 256 > &color_table)
bool IsWithinBounds(int canvas_x, int canvas_y, int margin=0)
Check if coordinates are within room bounds.
Editors are the view controllers for the application.
void EnsureCompositeBitmapTextureQueued(Bitmap &composite)
void DrawRect(const CanvasRuntime &rt, int x, int y, int w, int h, ImVec4 color)
void DrawOutline(const CanvasRuntime &rt, int x, int y, int w, int h, ImU32 color=IM_COL32(255, 255, 255, 200))
void DrawText(const CanvasRuntime &rt, const std::string &text, int x, int y)
std::string GetSpriteLabel(int id)
Convenience function to get a sprite label.
absl::StatusOr< CustomCollisionMap > LoadCustomCollisionMap(Rom *rom, int room_id)
bool UsesRoomObjectStream(const RoomObject &object)
std::string GetObjectName(int object_id)
constexpr int kNumberOfRooms
std::array< SDL_Color, 256 > BuildDungeonSpriteRenderPalette(const Room &room, const GameData *game_data)
Definition room.cc:192
core::HackManifest hack_manifest
Definition project.h:212
std::string assets_folder
Definition project.h:187
std::string GetAbsolutePath(const std::string &relative_path) const
Definition project.cc:1548