yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_editor_behavior.cc
Go to the documentation of this file.
2
3#include <cstdio>
4
6#include "core/project.h"
7
8namespace yaze::editor {
10 const project::SpriteBehavior& behavior) {
12 return absl::FailedPreconditionError("Select a sprite asset");
13 if (!behavior.profile.empty()) {
16 if (!parsed.ok())
17 return parsed.status();
18 } else if (!behavior.actions.empty()) {
19 return absl::InvalidArgumentError(
20 "Actions require an explicit behavior profile");
21 }
24 behavior_candidate_.clear();
27 return absl::OkStatus();
28}
29
32 return absl::FailedPreconditionError("Select a sprite asset");
33 const auto root =
34 project() ? project()->GetAbsolutePath(project()->sprite_source_root)
35 : "";
36 auto hashes = sprite_behavior::ReadReviewedProfile(root);
37 if (!hashes.ok())
38 return hashes.status();
39 auto behavior = current_sprite_binding()->behavior;
40 behavior.profile = "oracle_actions_v1";
41 behavior.source_sha256 = *hashes;
42 if (behavior.actions.empty())
43 behavior.actions.emplace_back();
44 return SetSpriteBehavior(behavior);
45}
46
48 const std::string& prefix) {
50 return absl::FailedPreconditionError("Select a sprite asset");
51 const auto& asset = *current_sprite_binding();
54 const auto root =
55 project() ? project()->GetAbsolutePath(project()->sprite_source_root)
56 : "";
58 // A behavior candidate references this asset's frames; retain the draw-source fence.
59 if (!asset.source_path.empty())
61 return sprite_behavior::GenerateCandidate(asset.behavior,
62 *current_custom_sprite(), prefix);
63}
64
67 return;
68 auto behavior = current_sprite_binding()->behavior;
69 ImGui::TextWrapped(
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")) {
76 behavior = current_sprite_binding()->behavior;
78 }
79 if (!behavior_status_.ok())
80 ImGui::TextWrapped("%s", behavior_status_.ToString().c_str());
81 if (behavior.profile.empty())
82 return;
83 ImGui::TextWrapped(
84 "Entry sets action, frame, timers A/B and XY velocity. Caller supplies "
85 "active checks, drawing and subtype dispatch.");
86 bool changed = false;
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);
91 selected_behavior_action_ = behavior.actions.size() - 1;
92 changed = true;
93 }
95 std::clamp(selected_behavior_action_, 0,
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(),
101 selected_behavior_action_ == static_cast<int>(i)))
103 ImGui::PopID();
104 }
105 auto& action = behavior.actions[selected_behavior_action_];
106 char name[81];
107 std::snprintf(name, sizeof(name), "%s", action.name.c_str());
108 if (ImGui::InputText("Action name", name, sizeof(name))) {
109 action.name = name;
110 changed = true;
111 }
112 const auto& animations = current_custom_sprite()->animations;
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;
123 changed = true;
124 }
125 ImGui::PopID();
126 }
127 ImGui::EndCombo();
128 }
129 if (ImGui::Button("Preview action animation") &&
130 action.animation < static_cast<int>(animations.size())) {
131 current_animation_index_ = action.animation;
132 current_frame_ = animations[action.animation].frame_start;
133 frame_timer_ = 0;
134 animation_playing_ = true;
136 }
137 ImGui::TextDisabled(
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;
144 changed = true;
145 }
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()
150 : "Missing action";
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)) {
155 value = -1;
156 changed = true;
157 }
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))) {
162 value = i;
163 changed = true;
164 }
165 ImGui::PopID();
166 }
167 ImGui::EndCombo();
168 }
169 };
170 if (dialogue) {
171 if (ImGui::InputInt("Message ID (decimal)", &action.message_id)) {
172 action.message_id = std::clamp(action.message_id, 0, 65535);
173 changed = true;
174 }
175 target("On message accepted", action.message_next, true);
176 ImGui::TextWrapped(
177 "Transitions when the message opens, not when dialogue or a choice is "
178 "finished. Message IDs must exist in the game.");
179 }
180 if (ImGui::Checkbox("Move with XY velocity", &action.move)) {
181 if (!action.move) {
182 action.x_speed = action.y_speed = 0;
183 action.bounce_tiles = false;
184 }
185 changed = true;
186 }
187 if (action.move) {
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);
192 changed |=
193 ImGui::Checkbox("Bounce from tile collision", &action.bounce_tiles);
194 }
195 bool timed = action.timer_next >= 0;
196 if (ImGui::Checkbox("Timer transition", &timed)) {
197 action.timer_next = timed ? selected_behavior_action_ : -1;
198 if (!timed)
199 action.timer_ticks = 0;
200 changed = true;
201 }
202 if (timed) {
203 changed |=
204 ImGui::SliderInt("Timer A ticks on entry", &action.timer_ticks, 0, 255);
205 target("On timer zero", action.timer_next);
206 }
207 if (ImGui::Button("Delete action")) {
210 if (behavior_status_.ok())
211 changed = true;
212 }
213 if (changed) {
216 }
217 ImGui::Separator();
218 if (ImGui::InputText("ASM label prefix", behavior_prefix_,
219 sizeof(behavior_prefix_)))
220 behavior_candidate_.clear();
221 if (ImGui::Button("Build behavior candidate")) {
223 behavior_status_ = generated.status();
224 behavior_candidate_ = generated.ok() ? *generated : "";
225 }
226 ImGui::SameLine();
227 if (ImGui::Button("Copy behavior candidate")) {
228 // Revalidate source at the moment of copying, even if a preview is cached.
230 behavior_status_ = generated.status();
231 behavior_candidate_ = generated.ok() ? *generated : "";
232 if (generated.ok())
233 ImGui::SetClipboardText(generated->c_str());
234 }
235 if (!behavior_candidate_.empty()) {
236 if (ImGui::BeginChild("BehaviorCandidate", ImVec2(0, 220), true,
237 ImGuiWindowFlags_HorizontalScrollbar))
238 ImGui::TextUnformatted(behavior_candidate_.c_str());
239 ImGui::EndChild();
240 }
241}
242} // namespace yaze::editor
project::YazeProject * project() const
Definition editor.h:358
absl::Status SetSpriteBehavior(const project::SpriteBehavior &behavior)
std::vector< project::SpriteAssetBinding > custom_sprite_bindings_
const zsprite::ZSprite * current_custom_sprite() const
absl::StatusOr< std::string > ExportCurrentSpriteBehavior(const std::string &prefix)
const project::SpriteAssetBinding * current_sprite_binding() const
absl::StatusOr< std::array< std::string, 3 > > ReadReviewedProfile(const std::string &root)
absl::Status VerifyProfile(const std::string &root, const project::SpriteBehavior &behavior)
absl::Status RemoveAction(project::SpriteBehavior &behavior, int index)
absl::Status Validate(const project::SpriteBehavior &behavior, const zsprite::ZSprite &sprite)
absl::StatusOr< std::string > GenerateCandidate(const project::SpriteBehavior &behavior, const zsprite::ZSprite &sprite, const std::string &prefix)
Editors are the view controllers for the application.
absl::StatusOr< SpriteBehavior > SpriteBehaviorFromJson(const nlohmann::json &json)
nlohmann::json SpriteBehaviorToJson(const SpriteBehavior &behavior)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::vector< AnimationGroup > animations
Definition zsprite.h:445
std::vector< SpriteBehaviorAction > actions
std::string GetAbsolutePath(const std::string &relative_path) const
Definition project.cc:2028