1#ifndef YAZE_APP_EDITOR_SPRITE_SPRITE_BEHAVIOR_H_
2#define YAZE_APP_EDITOR_SPRITE_SPRITE_BEHAVIOR_H_
8#include "absl/strings/str_format.h"
16 "Core/sprite_macros.asm",
"Core/sprite_functions.asm",
"Core/symbols.asm"};
19 "07a4aca7fab35159c10abe0804f5de6f2455c32ed1acc7f00abe69b4ad09acd8",
20 "d727ced1802805fbebada4d5bcffca4f366adcb23c14ef9bb1985afa48b93f39",
21 "5914fb1de28a4612cfac189d20d22586a04526f5f6d0d3a2307489413dee22ab"};
24 std::istringstream stream(text);
25 std::string result, line;
26 while (std::getline(stream, line)) {
27 line = line.substr(0, line.find(
';'));
28 for (
unsigned char c : line)
36 const std::string& root) {
37 std::array<std::string, 3> hashes;
41 return source.status();
44 return absl::FailedPreconditionError(
45 std::string(
"Oracle action contract changed; review required: ") +
54 if (behavior.
profile !=
"oracle_actions_v1")
55 return absl::InvalidArgumentError(
"Unsupported behavior profile");
58 return current.status();
60 return absl::FailedPreconditionError(
61 "Behavior source changed since profile binding; review and rebind the "
63 return absl::OkStatus();
71 return checked.status();
72 for (
const auto& action : behavior.
actions) {
73 if (action.animation >=
static_cast<int>(sprite.
animations.size()))
74 return absl::FailedPreconditionError(
75 "Action references a missing animation");
76 const auto& animation = sprite.
animations[action.animation];
77 if (animation.frame_start > animation.frame_end ||
79 animation.frame_end == 255 || animation.frame_speed == 0)
80 return absl::FailedPreconditionError(
81 "Oracle actions require valid animation ranges ending below 255 and "
84 return absl::OkStatus();
89 if (index < 0 || index >=
static_cast<int>(behavior.
actions.size()) ||
91 return absl::InvalidArgumentError(
"Cannot delete this action");
92 for (
size_t i = 0; i < behavior.
actions.size(); ++i) {
93 if (
static_cast<int>(i) == index)
95 const auto& action = behavior.
actions[i];
96 if (action.message_next == index || action.timer_next == index)
97 return absl::FailedPreconditionError(
98 "Redirect incoming transitions before deleting this action");
101 for (
auto& action : behavior.
actions) {
102 if (action.message_next > index)
103 --action.message_next;
104 if (action.timer_next > index)
107 return absl::OkStatus();
114 const std::string& prefix) {
116 if (prefix.empty() || prefix.size() > 64 ||
117 !(std::isalpha(
static_cast<unsigned char>(prefix[0])) ||
119 prefix.find_first_not_of(
120 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") !=
122 return absl::InvalidArgumentError(
123 "Use an ASM identifier as the behavior label prefix");
124 auto label = [&](
const char* type,
int index) {
125 return prefix +
"_" + type + std::to_string(index);
127 auto byte = [](
int value) {
128 return absl::StrFormat(
"$%02X", value & 255);
130 std::ostringstream out;
131 out <<
"; Oracle action candidate. Review bank placement and RAM ownership "
132 "before integration.\n"
133 <<
"; Same-bank JSR entry/RTS return; A/X/Y 8-bit, X=sprite slot, "
134 "DBR must map low WRAM.\n"
135 <<
"; Caller: Init once; Main only when Sprite_CheckActive succeeds. "
136 "Keep draw frame base zero.\n"
137 <<
"; Owns SprAction, SprFrame, SprTimerA/B, SprXSpeed/YSpeed. Helpers "
138 "clobber scratch/flags.\n"
139 <<
"; Engine decrements timers. Dialogue transition means message "
140 "accepted, not dialogue completed.\n"
141 <<
"; No subtype selection, registration, or draw routine is "
143 << prefix <<
"_Init:\n JMP " << label(
"Enter", 0) <<
"\n\n"
144 << prefix <<
"_Main:\n LDA.w SprAction, X\n CMP.b #"
145 << byte(behavior.
actions.size())
146 <<
"\n BCC .dispatch\n RTS\n.dispatch\n JSL JumpTableLocal\n";
147 for (
size_t i = 0; i < behavior.
actions.size(); ++i)
148 out <<
" dw " << label(
"Action", i) <<
"\n";
149 for (
size_t i = 0; i < behavior.
actions.size(); ++i) {
150 const auto& action = behavior.
actions[i];
151 const auto& animation = sprite.
animations[action.animation];
153 << label(
"Action", i) <<
":\n %PlayAnimation("
154 << int(animation.frame_start) <<
", " << int(animation.frame_end)
155 <<
", " << int(animation.frame_speed) <<
")\n";
156 if (action.block_player)
157 out <<
" JSL Sprite_PlayerCantPassThrough\n";
158 if (action.message_id >= 0) {
159 out <<
" %ShowSolicitedMessage("
160 << absl::StrFormat(
"$%04X", action.message_id)
161 <<
")\n BCC .no_message\n";
162 if (action.message_next >= 0)
163 out <<
" JMP " << label(
"Enter", action.message_next) <<
"\n";
166 out <<
".no_message\n";
168 if (action.timer_next >= 0)
169 out <<
" LDA.w SprTimerA, X\n BNE .timer_pending\n JMP "
170 << label(
"Enter", action.timer_next) <<
"\n.timer_pending\n";
171 if (action.bounce_tiles)
172 out <<
" JSL Sprite_BounceFromTileCollision\n";
174 out <<
" JSL Sprite_Move\n";
175 out <<
" RTS\n\n" << label(
"Enter", i) <<
":\n";
176 for (
const auto& entry : std::vector<std::pair<const char*, int>>{
177 {
"SprAction",
static_cast<int>(i)},
178 {
"SprFrame", animation.frame_start},
179 {
"SprTimerB", animation.frame_speed},
180 {
"SprTimerA", action.timer_ticks},
181 {
"SprXSpeed", action.x_speed},
182 {
"SprYSpeed", action.y_speed}})
183 out <<
" LDA.b #" <<
byte(entry.second) <<
" : STA.w " << entry.first
std::string ComputeSourceArtifactSha256(std::string_view content)
std::string NormalizeContractSource(const std::string &text)
absl::StatusOr< std::array< std::string, 3 > > ReadReviewedProfile(const std::string &root)
constexpr std::array< const char *, 3 > kSourcePaths
absl::Status VerifyProfile(const std::string &root, const project::SpriteBehavior &behavior)
absl::Status RemoveAction(project::SpriteBehavior &behavior, int index)
constexpr std::array< const char *, 3 > kReviewedCodeSha256
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)
absl::StatusOr< SpriteBehavior > SpriteBehaviorFromJson(const nlohmann::json &json)
nlohmann::json SpriteBehaviorToJson(const SpriteBehavior &behavior)
absl::StatusOr< SpriteSourceDocument > ReadSpriteSourceFile(const std::string &source_root, const std::string &relative_path)
#define RETURN_IF_ERROR(expr)
std::vector< Frame > Frames
std::vector< AnimationGroup > animations
std::vector< SpriteBehaviorAction > actions
std::array< std::string, 3 > source_sha256