15 if (plan_)
return std::unexpected(
"piece move playback already active");
16 if (
plan.instructions.empty())
17 return std::unexpected(
"piece move plan has no TokenAnimStack instructions");
20 if (!
plan.loopBegin || !
plan.loopEnd ||
22 *
plan.loopEnd >
plan.instructions.size())
23 return std::unexpected(
"victory TokenAnimStack loop bounds are invalid");
25 plan_ = std::move(
plan);
28 desiredCamera_.reset();
32 std::expected<void, std::string> PieceMovePlayback::startInstruction(
38 return std::unexpected(
"sequence TokenAnimStack item has an empty DataID");
41 if (!
pose)
return std::unexpected(
"TokenAnimStack start square is invalid");
42 const auto& startPose = *
pose;
44 const std::optional<data::DataId>
previous =
46 std::optional<data::DataId>(currentSequence_);
49 startPose.yaw, 1.0F, startPose.x, startPose.z);
51 update.stoppedSequence =
previous.has_value();
61 if (!plan_)
return update;
63 const auto top =
plan.instructions.size() - 1U;
68 index_ =
plan.instructions.size();
79 const auto info =
playback.runtime().info(
81 if (info && info->endTime > info->sequenceClock)
88 plan.destinationSquare != 0)
92 update.passedGo =
true;
96 plan.instructions[index_].cameraOnly)
105 if (!started)
return std::unexpected(started.error());
113 plan.loopBegin &&
plan.loopEnd && index_ == *
plan.loopEnd)
115 index_ = *
plan.loopBegin;
116 update.looped =
true;
126 if (!stopped)
return std::unexpected(stopped.error());
128 update.stoppedSequence =
true;
132 update.completed =
true;
133 update.active =
false;
137 update.active =
true;
decision::EconomicActionPlan plan
std::expected< PieceMovePlaybackUpdate, std::string > tick(bool boardVisible, engine::SequencePlayback &playback)
std::expected< void, std::string > begin(PieceMovePlan plan)
constexpr DataId EmptyDataId
std::optional< TokenPose > tokenAnimationStartOrientation(std::uint8_t boardSquare, data::DataId sequenceId) noexcept