70 "Oracle actions are a new authoring model, not an import of this "
71 "sprite's existing behavior.");
72 if (ImGui::Button(behavior.profile.empty()
73 ?
"Use reviewed Oracle action profile"
74 :
"Recheck and bind reviewed sources")) {
81 if (behavior.profile.empty())
84 "Entry sets action, frame, timers A/B and XY velocity. Caller supplies "
85 "active checks, drawing and subtype dispatch.");
87 if (ImGui::Button(
"Add action") && behavior.actions.size() < 16) {
89 action.
name =
"Action " + std::to_string(behavior.actions.size());
90 behavior.actions.push_back(action);
96 static_cast<int>(behavior.actions.size()) - 1);
97 for (
size_t i = 0; i < behavior.actions.size(); ++i) {
98 ImGui::PushID(
static_cast<int>(i));
99 const auto label = std::to_string(i) +
": " + behavior.actions[i].name;
100 if (ImGui::Selectable(label.c_str(),
107 std::snprintf(name,
sizeof(name),
"%s", action.name.c_str());
108 if (ImGui::InputText(
"Action name", name,
sizeof(name))) {
113 const char* animation_name =
114 action.animation <
static_cast<int>(animations.size())
115 ? animations[action.animation].frame_name.c_str()
116 :
"Missing animation";
117 if (ImGui::BeginCombo(
"Animation", animation_name)) {
118 for (
size_t i = 0; i < animations.size(); ++i) {
119 ImGui::PushID(
static_cast<int>(i));
120 if (ImGui::Selectable(animations[i].frame_name.c_str(),
121 action.animation ==
static_cast<int>(i))) {
122 action.animation = i;
129 if (ImGui::Button(
"Preview action animation") &&
130 action.animation <
static_cast<int>(animations.size())) {
138 "Animation only; movement, dialogue and collision need game testing.");
139 changed |= ImGui::Checkbox(
"Block player", &action.block_player);
140 bool dialogue = action.message_id >= 0;
141 if (ImGui::Checkbox(
"Solicited dialogue", &dialogue)) {
142 action.message_id = dialogue ? 0 : -1;
143 action.message_next = -1;
146 auto target = [&](
const char* label,
int& value,
bool allow_stay =
false) {
147 const char* display =
148 value >= 0 && value < static_cast<int>(behavior.actions.size())
149 ? behavior.actions[value].name.c_str()
151 if (allow_stay && value == -1)
152 display =
"Stay (no re-entry)";
153 if (ImGui::BeginCombo(label, display)) {
154 if (allow_stay && ImGui::Selectable(
"Stay (no re-entry)", value == -1)) {
158 for (
size_t i = 0; i < behavior.actions.size(); ++i) {
159 ImGui::PushID(
static_cast<int>(i));
160 if (ImGui::Selectable(behavior.actions[i].name.c_str(),
161 value ==
static_cast<int>(i))) {
171 if (ImGui::InputInt(
"Message ID (decimal)", &action.message_id)) {
172 action.message_id = std::clamp(action.message_id, 0, 65535);
175 target(
"On message accepted", action.message_next,
true);
177 "Transitions when the message opens, not when dialogue or a choice is "
178 "finished. Message IDs must exist in the game.");
180 if (ImGui::Checkbox(
"Move with XY velocity", &action.move)) {
182 action.x_speed = action.y_speed = 0;
183 action.bounce_tiles =
false;
188 changed |= ImGui::SliderInt(
"X speed (1/16 pixel per tick)",
189 &action.x_speed, -127, 127);
190 changed |= ImGui::SliderInt(
"Y speed (1/16 pixel per tick)",
191 &action.y_speed, -127, 127);
193 ImGui::Checkbox(
"Bounce from tile collision", &action.bounce_tiles);
195 bool timed = action.timer_next >= 0;
196 if (ImGui::Checkbox(
"Timer transition", &timed)) {
199 action.timer_ticks = 0;
204 ImGui::SliderInt(
"Timer A ticks on entry", &action.timer_ticks, 0, 255);
205 target(
"On timer zero", action.timer_next);
207 if (ImGui::Button(
"Delete action")) {
221 if (ImGui::Button(
"Build behavior candidate")) {
227 if (ImGui::Button(
"Copy behavior candidate")) {
233 ImGui::SetClipboardText(generated->c_str());
236 if (ImGui::BeginChild(
"BehaviorCandidate", ImVec2(0, 220),
true,
237 ImGuiWindowFlags_HorizontalScrollbar))