24 const auto* active = ImGui::GetDragDropPayload();
27 const bool rail_drag =
32 if (!node_drag && !rail_drag && !panel->placement_brush()) {
35 if (!node_drag && !rail_drag && panel->placement_brush()->collision_only) {
39 panel->CancelNodePlacement();
44 (ImGui::IsKeyPressed(ImGuiKey_Escape) ||
45 ImGui::IsMouseDown(ImGuiMouseButton_Right) ||
46 ImGui::IsMouseDown(ImGuiMouseButton_Middle))) {
47 panel->CancelNodePlacement();
50 std::optional<MinecartToolPayload> brush = panel->placement_brush();
63 if (!brush || brush->collision_only ||
72 if (!sprite.deleted() && sprite.layer() == 0 && sprite.id() == wanted &&
73 sprite.x() * 2 == x && sprite.y() * 2 == y) {
81 if (!rail_drag && brush && !brush->collision_only &&
82 ImGui::IsMouseDragging(ImGuiMouseButton_Left) &&
83 !ImGui::IsMouseDown(ImGuiMouseButton_Right) &&
84 !ImGui::IsMouseDown(ImGuiMouseButton_Middle)) {
85 const auto click = ImGui::GetIO().MouseClickedPos[ImGuiMouseButton_Left];
86 auto [source_x, source_y] = ImGui::IsMousePosValid(&click)
91 if (source_x >= 0 && source_y >= 0 && source_x <= 62 && source_y <= 62) {
92 if (
const auto* sprite = actor_at(source_x, source_y);
93 sprite && ImGui::BeginDragDropSource()) {
95 moving.node.id = sprite->subtype();
96 moving.node.source_x = source_x;
97 moving.node.source_y = source_y;
98 moving.node.source_key_drop =
static_cast<uint8_t
>(sprite->key_drop());
100 if (sprite->id() == 0xB0) {
101 moving.node.collision =
104 moving.owner = panel;
105 moving.source_viewer =
this;
106 moving.revision = panel->canvas_revision();
107 moving.room_id = room_id;
109 sizeof(moving), ImGuiCond_Once);
110 ImGui::TextUnformatted(sprite->id() == 0xAF ?
"Move switch"
111 :
"Move switch track");
112 ImGui::EndDragDropSource();
116 active = ImGui::GetDragDropPayload();
124 std::memcpy(&incoming, active->Data,
sizeof(incoming));
127 auto [x, y] = ImGui::IsMousePosValid()
130 if (rail_drag || !brush || !brush->collision_only || brush->size == 2) {
136 (node_drag || rail_drag)
137 ? ImGui::IsWindowHovered(
138 ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
139 ImGui::IsMouseHoveringRect(target.Min, target.Max)
141 const bool inside = hovered && x >= 0 && y >= 0 && x < 64 && y < 64;
142 absl::Status validity =
143 absl::InvalidArgumentError(
"Keep the node inside this room");
144 std::optional<zelda3::RoomObject> rail;
146 const auto& payload =
148 rail.emplace(0x31, x, y, payload.size, 0);
150 objects.push_back(*rail);
152 panel->automatic_rail_collision()
157 if (x < 0 || y < 0 || x > 62 || y > 62 || payload.size > 15) {
159 absl::InvalidArgumentError(
"Place a complete rail inside this room");
161 }
else if (brush && inside) {
162 if (brush->owner != panel || brush->revision != panel->canvas_revision() ||
163 brush->room_id != room_id) {
164 validity = absl::FailedPreconditionError(
165 "Track tool belongs to an earlier room or project; select it again");
167 validity = brush->collision_only
170 {x, y, brush->size, brush->collision})
177 bool delivered =
false;
178 if (node_drag || rail_drag) {
179 if (ImGui::BeginDragDropTargetCustom(target,
180 ImGui::GetID(
"##MinecartNodeDrop"))) {
181 if (
const auto* payload = ImGui::AcceptDragDropPayload(
184 ImGuiDragDropFlags_AcceptBeforeDelivery |
185 ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) {
186 delivered = payload->IsDelivery();
188 ImGui::EndDragDropTarget();
191 const bool clicked = !node_drag && !rail_drag && inside && !actor_at(x, y) &&
192 ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
193 !ImGui::IsMouseDown(ImGuiMouseButton_Right) &&
194 !ImGui::IsMouseDown(ImGuiMouseButton_Middle);
197 const auto color = ImGui::GetColorU32(validity.ok() ? theme.status_success
198 : theme.status_error);
200 rail_drag || !brush || !brush->collision_only ? 16 : brush->size * 8;
209 rt.
draw_list->AddRectFilled(p, q, (color & 0x00FFFFFF) | 0x44000000);
210 rt.
draw_list->AddRect(p, q, color, 0.0f, 2.0f);
212 if (!validity.ok()) {
213 ImGui::SetTooltip(
"%s", std::string(validity.message()).c_str());
216 if ((delivered || clicked) && inside) {
217 auto status = validity;
218 if (status.ok() && rail) {
220 if (!handler.PlaceObjectAt(room_id, *rail, x, y)) {
223 }
else if (status.ok() && brush) {
224 if (brush->collision_only && panel->collision_stamp_callback()) {
225 status = panel->collision_stamp_callback()(
227 {x, y, brush->size, brush->collision});
228 }
else if (!brush->collision_only && panel->node_placement_callback()) {
229 status = panel->node_placement_callback()(room_id, x, y, brush->node);
232 absl::FailedPreconditionError(
"Track placement is unavailable");
235 panel->ReportToolStatus(status);
236 if (status.ok() && brush && !brush->collision_only &&
238 panel->CancelNodePlacement();