30 return absl::InvalidArgumentError(
"Start is outside its room");
31 std::optional<size_t> match;
33 for (
size_t i = 0; i < sprites.size(); ++i) {
34 const auto& sprite = sprites[i];
35 if (sprite.deleted() || sprite.id() != 0xA3 ||
36 sprite.subtype() != start.
id || sprite.x() * 16 != local->first ||
37 sprite.y() * 16 != local->second)
40 return absl::FailedPreconditionError(
"Multiple carts share this start");
41 if (sprite.layer() != 0)
42 return absl::FailedPreconditionError(
43 "Lower-layer stop collision cannot yet be verified");
47 return absl::FailedPreconditionError(
"No matching cart at this start");
54 if (!local || local->first % 16 || local->second % 16)
55 return absl::InvalidArgumentError(
56 "Place the cart on the 16 pixel sprite grid");
58 return absl::FailedPreconditionError(
59 "This room has no verified custom stop collision");
61 .
tiles[(local->second / 8) * 64 + local->first / 8];
62 if (tile < 0xB7 || tile > 0xBA)
63 return absl::FailedPreconditionError(
64 "Place the cart on a stop, not a rail or floor tile");
66 if (!sprite.deleted() && sprite.id() == 0xA3 &&
67 sprite.subtype() != start.
id && sprite.layer() == 0 &&
68 sprite.x() * 16 == local->first && sprite.y() * 16 == local->second)
69 return absl::FailedPreconditionError(
"Another cart occupies this stop");
71 return absl::OkStatus();
81 bool editable,
bool shortcut_focus,
96 const auto mouse = ImGui::GetMousePos();
98 const bool inside = rt.
hovered && room_point.x >= 0 && room_point.x < 512 &&
99 room_point.y >= 0 && room_point.y < 512;
103 for (
const auto& start : binding.
tracks) {
104 if (start.room_id != room_id)
110 ImVec2(local->first + 8, local->second + 8));
111 const float radius = std::max(8.0f, 8.0f * rt.
scale);
112 const float dx = mouse.x - center.x, dy = mouse.y - center.y;
113 if (inside && dx * dx + dy * dy <= radius * radius)
115 const auto color = ImGui::GetColorU32(start.id == binding.
selected
116 ? theme.status_warning
117 : theme.selection_primary);
118 rt.
draw_list->AddCircleFilled(center, radius, color);
119 rt.
draw_list->AddText(ImVec2(center.x + radius + 3, center.y - 7),
120 ImGui::GetColorU32(theme.text_primary),
121 absl::StrFormat(
"Cart %d", start.id).c_str());
128 if (ImGui::IsMouseDown(ImGuiMouseButton_Right) ||
129 ImGui::IsMouseDown(ImGuiMouseButton_Middle)) {
134 for (
const auto& start : binding.
tracks)
135 if (start.id ==
id && start.room_id == room_id)
139 if ((shortcut_focus ||
dragging()) && ImGui::IsKeyPressed(ImGuiKey_Escape)) {
146 if (inside && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
148 if (
const auto* start = selected(
id)) {
157 message_ = std::string(sprite.status().message());
163 const int x =
static_cast<int>(std::round((room_point.x - 8) / 16)) * 16;
164 const int y =
static_cast<int>(std::round((room_point.y - 8) / 16)) * 16;
166 preview_->start_x = (room_id & 15) * 512 + x;
167 preview_->start_y = (room_id >> 4) * 512 + y;
168 const auto validity =
170 : absl::InvalidArgumentError(
"Keep the start inside this room");
171 message_ = validity.ok() ?
"Release to move cart and start"
172 : std::string(validity.message());
179 ImGui::GetColorU32(validity.ok() ? theme.status_success
180 : theme.status_error),
184 if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
185 if (validity.ok() && binding.
move) {
189 const auto status = binding.
move(from, to);
191 ?
"Cart start moved; source and room edits are pending"
192 : std::string(status.message());
198 }
else if (shortcut_focus && !ImGui::GetIO().WantTextInput &&
199 !ImGui::GetIO().KeyCtrl && !ImGui::GetIO().KeySuper) {
201 if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
203 if (ImGui::IsKeyPressed(ImGuiKey_RightArrow))
205 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow))
207 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow))
209 if (
const auto* start = selected(binding.
selected); start && (dx || dy)) {
214 if (status.ok() && binding.
move)
215 status = binding.
move(*start, next);
217 status.ok() ?
"Cart start moved" : std::string(status.message());
221 ImGui::SetTooltip(
"%s",
message_.c_str());