Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
PieceJailPlayback.cpp
Go to the documentation of this file.
2
3#include "PiecePlacement.hpp"
5
6#include <algorithm>
7#include <cmath>
8
9namespace monopoly::pieces
10{
11 namespace
12 {
13 constexpr data::DataTag TokenIntoWagonBase = 0x0157;
14 constexpr data::DataTag TokenOutOfWagonBase = 0x0158;
15 constexpr data::DataTag PaddyDoorClose = 0x05C7;
16 constexpr data::DataTag PaddyDoorOpen = 0x05C8;
17 constexpr data::DataTag PaddyOpenIdle = 0x05C9;
18 constexpr data::DataTag PaddyPopIn = 0x05CA;
19 constexpr data::DataTag PaddyPopOut = 0x05CB;
20 constexpr data::DataTag PaddyRolling = 0x05CC;
21 constexpr std::uint8_t StayAtEnd = 2;
22 constexpr std::uint8_t LoopToBeginning = 3;
23
26
28 data::DataTag base, std::uint8_t token) noexcept
29 {
30 return threeD(static_cast<data::DataTag>(base +
31 static_cast<data::DataTag>(AnimationsPerToken * token)));
32 }
33
34 [[nodiscard]] sequence::Matrix3D rotateX(float angle) noexcept
35 {
37 matrix.values[5] = matrix.values[10] = std::cos(angle);
38 matrix.values[6] = std::sin(angle);
39 matrix.values[9] = -matrix.values[6];
40 return matrix;
41 }
42
43 [[nodiscard]] sequence::Matrix3D rotateY(float angle) noexcept
44 {
46 matrix.values[0] = matrix.values[10] = std::cos(angle);
47 matrix.values[2] = -std::sin(angle);
48 matrix.values[8] = -matrix.values[2];
49 return matrix;
50 }
51
52 [[nodiscard]] sequence::Matrix3D paddyMatrix(
53 const PieceInterpolationSample& sample) noexcept
54 {
55 const float yaw = std::atan2(sample.forward.x, sample.forward.z);
56 const float pitch = -std::asin(std::clamp(sample.forward.y, -1.0F, 1.0F));
57 auto result = rotateY(yaw);
58 result = sequence::multiply(result, rotateX(pitch));
60 sample.location.x, sample.location.y, sample.location.z));
61 return result;
62 }
63
65 const engine::SequencePlayback& playback, data::DataId id) noexcept
66 {
67 if (id == data::EmptyDataId) return false;
68 const auto info = const_cast<engine::SequencePlayback&>(playback)
69 .runtime().info(id, JailPlaybackPriority, false);
70 return info && info->endTime <= info->sequenceClock;
71 }
72
73 [[nodiscard]] InterpolationVec3 routeEnd(
74 const PieceJailRoutePlan& route) noexcept
75 {
76 if (route.path.used == 0) return {};
77 return route.path.segments[route.path.used - 1][3];
78 }
79
80 [[nodiscard]] std::expected<void, std::string> transitionPaddy(
81 engine::SequencePlayback& playback, data::DataId previous,
82 data::DataId next, const sequence::Matrix3D& matrix,
83 std::uint8_t endingAction)
84 {
85 const std::optional<data::DataId> old = previous == data::EmptyDataId ?
86 std::nullopt : std::optional<data::DataId>{previous};
87 return playback.transitionMovedDrop(old, next, JailPlaybackPriority,
88 sequence::SequenceTransform(matrix), endingAction);
89 }
90
91 [[nodiscard]] std::expected<void, std::string> startToken(
92 engine::SequencePlayback& playback, data::DataId id,
93 std::uint8_t square)
94 {
95 const auto pose = tokenOrientation(square);
96 if (!pose) return std::unexpected("missing token board orientation");
97 return playback.transitionRySTxzDropStayAtEnd(std::nullopt, id,
98 JailPlaybackPriority, pose->yaw, 1.0F, pose->x, pose->z);
99 }
100 }
101
102 std::expected<void, std::string> PieceJailPlayback::begin(
103 const PieceMoveSpecialRequest& request, std::uint64_t,
104 bool animationsEnabled, std::uint8_t randomBit)
105 {
106 if (active()) return std::unexpected("GoToJail playback already active");
107 if (request.special != PieceMoveSpecial::GoToJail ||
108 request.after != 40 || request.before < 0 || request.before >= 40 ||
109 request.player >= rules::MaxPlayers)
110 return std::unexpected("invalid GoToJail request");
111 request_ = request;
112 animationsEnabled_ = animationsEnabled;
113 randomBit_ = static_cast<std::uint8_t>(randomBit & 1U);
114 state_ = 1;
115 playerInPaddywagon_.reset();
116 outbound_.reset(); inbound_.reset();
117 paddySequence_ = tokenSequence_ = data::EmptyDataId;
118 paddyMatrix_ = sequence::identity3D();
119 return {};
120 }
121
122 std::expected<PieceJailPlaybackUpdate, std::string> PieceJailPlayback::tick(
125 {
127 if (!active()) return result;
128 if (request_.player >= rules::MaxPlayers)
129 return std::unexpected("GoToJail player no longer valid");
130
131 for (;;)
132 {
133 switch (state_)
134 {
135 case 1:
136 {
137 if (!animationsEnabled_) { state_ = 14; continue; }
138 result.playSiren = true;
139 auto route = planPaddyToToken(
140 static_cast<std::uint8_t>(request_.before), tickValue, randomBit_);
141 if (!route) return std::unexpected("failed to build paddy outbound route");
142 outbound_ = std::move(*route);
143 result.camera = outbound_->camera;
144 if (outbound_->skipMotion) { state_ = 14; continue; }
145 state_ = 2;
146 continue;
147 }
148
149 case 2:
150 {
151 const auto popIn = threeD(PaddyPopIn);
152 if (paddySequence_ != popIn)
153 {
154 const auto sample = samplePieceInterpolation(
155 outbound_->path, outbound_->path.startTick);
156 paddyMatrix_ = paddyMatrix(sample);
157 const auto started = transitionPaddy(playback, paddySequence_,
158 popIn, paddyMatrix_, StayAtEnd);
159 if (!started) return std::unexpected(started.error());
160 paddySequence_ = popIn;
161 return result;
162 }
163 if (!sequenceFinished(playback, paddySequence_)) return result;
164
165 const auto duration = outbound_->path.endTick - outbound_->path.startTick;
166 outbound_->path.startTick = tickValue;
167 outbound_->path.endTick = tickValue + duration;
169 outbound_->path, tickValue));
170 const auto rolling = threeD(PaddyRolling);
171 const auto started = transitionPaddy(playback, paddySequence_,
172 rolling, paddyMatrix_, LoopToBeginning);
173 if (!started) return std::unexpected(started.error());
174 paddySequence_ = rolling;
175 state_ = 3;
176 continue;
177 }
178
179 case 3:
180 {
181 const auto sample = samplePieceInterpolation(outbound_->path, tickValue);
182 paddyMatrix_ = paddyMatrix(sample);
183 const auto moved = playback.move(paddySequence_, JailPlaybackPriority,
184 sequence::SequenceTransform(paddyMatrix_));
185 if (!moved) return std::unexpected(moved.error());
186 const auto cameraTick = outbound_->path.endTick > 55U ?
187 outbound_->path.endTick - 55U : 0U;
188 if (tickValue >= cameraTick)
189 result.camera = pickCameraFor3Squares(outbound_->loadSquare);
190 if (tickValue < outbound_->path.endTick) return result;
191 state_ = 4;
192 continue;
193 }
194
195 case 4:
196 {
197 const auto doorOpen = threeD(PaddyDoorOpen);
198 if (paddySequence_ != doorOpen)
199 {
200 const auto started = transitionPaddy(playback, paddySequence_,
201 doorOpen, paddyMatrix_, StayAtEnd);
202 if (!started) return std::unexpected(started.error());
203 paddySequence_ = doorOpen;
204 return result;
205 }
206 if (!sequenceFinished(playback, paddySequence_)) return result;
207
208 const auto idle = threeD(PaddyOpenIdle);
209 const auto started = transitionPaddy(playback, paddySequence_,
210 idle, paddyMatrix_, LoopToBeginning);
211 if (!started) return std::unexpected(started.error());
212 paddySequence_ = idle;
213 tokenSequence_ = data::EmptyDataId;
214 state_ = 5;
215 continue;
216 }
217
218 case 5:
219 {
220 playerInPaddywagon_ = request_.player;
221 uiState.players[request_.player].currentSquare = 40;
222 const auto wanted = tokenSequence(TokenIntoWagonBase, request_.token);
223 if (tokenSequence_ != wanted)
224 {
225 const auto started = startToken(playback, wanted,
226 static_cast<std::uint8_t>(request_.before));
227 if (!started) return std::unexpected(started.error());
228 tokenSequence_ = wanted;
229 return result;
230 }
231 if (!sequenceFinished(playback, tokenSequence_)) return result;
232 state_ = 6;
233 continue;
234 }
235
236 case 6:
237 {
238 const auto doorClose = threeD(PaddyDoorClose);
239 if (paddySequence_ != doorClose)
240 {
241 const auto started = transitionPaddy(playback, paddySequence_,
242 doorClose, paddyMatrix_, StayAtEnd);
243 if (!started) return std::unexpected(started.error());
244 paddySequence_ = doorClose;
245 return result;
246 }
247 if (!sequenceFinished(playback, paddySequence_)) return result;
248
249 if (tokenSequence_ != data::EmptyDataId)
250 {
251 const auto stopped = playback.stop(
252 tokenSequence_, JailPlaybackPriority);
253 if (!stopped) return std::unexpected(stopped.error());
254 tokenSequence_ = data::EmptyDataId;
255 }
256 const auto rolling = threeD(PaddyRolling);
257 const auto started = transitionPaddy(playback, paddySequence_,
258 rolling, paddyMatrix_, LoopToBeginning);
259 if (!started) return std::unexpected(started.error());
260 paddySequence_ = rolling;
261 state_ = 7;
262 continue;
263 }
264
265 case 7:
266 {
267 result.playSiren = true;
268 auto route = planPaddyToJail(
269 static_cast<std::uint8_t>(request_.before),
270 routeEnd(*outbound_), tickValue);
271 if (!route) return std::unexpected("failed to build paddy return route");
272 inbound_ = std::move(*route);
273 result.camera = inbound_->camera;
274 state_ = 8;
275 continue;
276 }
277
278 case 8:
279 {
280 const auto sample = samplePieceInterpolation(inbound_->path, tickValue);
281 paddyMatrix_ = paddyMatrix(sample);
282 const auto moved = playback.move(paddySequence_, JailPlaybackPriority,
283 sequence::SequenceTransform(paddyMatrix_));
284 if (!moved) return std::unexpected(moved.error());
285 const auto cameraTick = inbound_->path.endTick > 55U ?
286 inbound_->path.endTick - 55U : 0U;
287 if (tickValue >= cameraTick)
288 result.camera = pickCameraFor3Squares(40);
289 if (tickValue < inbound_->path.endTick) return result;
290 state_ = 9;
291 continue;
292 }
293
294 case 9:
295 {
296 const auto doorOpen = threeD(PaddyDoorOpen);
297 if (paddySequence_ != doorOpen)
298 {
299 const auto started = transitionPaddy(playback, paddySequence_,
300 doorOpen, paddyMatrix_, StayAtEnd);
301 if (!started) return std::unexpected(started.error());
302 paddySequence_ = doorOpen;
303 return result;
304 }
305 if (!sequenceFinished(playback, paddySequence_)) return result;
306
307 const auto idle = threeD(PaddyOpenIdle);
308 const auto paddyStarted = transitionPaddy(playback, paddySequence_,
309 idle, paddyMatrix_, LoopToBeginning);
310 if (!paddyStarted) return std::unexpected(paddyStarted.error());
311 paddySequence_ = idle;
312
313 const auto wanted = tokenSequence(TokenOutOfWagonBase, request_.token);
314 const auto tokenStarted = startToken(playback, wanted, 10);
315 if (!tokenStarted) return std::unexpected(tokenStarted.error());
316 tokenSequence_ = wanted;
317 state_ = 10;
318 return result;
319 }
320
321 case 10:
322 if (!sequenceFinished(playback, tokenSequence_)) return result;
323 state_ = 11;
324 continue;
325
326 case 11:
327 {
328 const auto doorClose = threeD(PaddyDoorClose);
329 if (paddySequence_ != doorClose)
330 {
331 if (tokenSequence_ != data::EmptyDataId)
332 {
333 const auto stopped = playback.stop(
334 tokenSequence_, JailPlaybackPriority);
335 if (!stopped) return std::unexpected(stopped.error());
336 tokenSequence_ = data::EmptyDataId;
337 }
338 uiState.players[request_.player].currentSquare = 40;
339 playerInPaddywagon_.reset();
340 const auto started = transitionPaddy(playback, paddySequence_,
341 doorClose, paddyMatrix_, StayAtEnd);
342 if (!started) return std::unexpected(started.error());
343 paddySequence_ = doorClose;
344 return result;
345 }
346 if (!sequenceFinished(playback, paddySequence_)) return result;
347 state_ = 12;
348 continue;
349 }
350
351 case 12:
352 {
353 const auto popOut = threeD(PaddyPopOut);
354 const auto started = transitionPaddy(playback, paddySequence_,
355 popOut, paddyMatrix_, StayAtEnd);
356 if (!started) return std::unexpected(started.error());
357 paddySequence_ = popOut;
358 result.camera = pickCameraFor3Squares(40);
359 state_ = 13;
360 return result;
361 }
362
363 case 13:
364 if (!sequenceFinished(playback, paddySequence_)) return result;
365 state_ = 14;
366 continue;
367
368 case 14:
369 {
370 if (paddySequence_ != data::EmptyDataId)
371 {
372 const auto stopped = playback.stop(
373 paddySequence_, JailPlaybackPriority);
374 if (!stopped) return std::unexpected(stopped.error());
375 paddySequence_ = data::EmptyDataId;
376 }
377 if (tokenSequence_ != data::EmptyDataId)
378 {
379 const auto stopped = playback.stop(
380 tokenSequence_, JailPlaybackPriority);
381 if (!stopped) return std::unexpected(stopped.error());
382 tokenSequence_ = data::EmptyDataId;
383 }
384 uiState.players[request_.player].currentSquare = 40;
385 playerInPaddywagon_.reset();
386 outbound_.reset(); inbound_.reset();
387 state_ = 0;
388 result.completed = true;
389 result.camera = pickCameraFor3Squares(40);
390 return result;
391 }
392
393 default:
394 state_ = 0;
395 return std::unexpected("invalid GoToJail playback state");
396 }
397 }
398 }
399}
fonts::Runtime & runtime
unsigned index
int token
float yaw
std::uint16_t pitch
std::string path
data::DataId idle
std::expected< PieceJailPlaybackUpdate, std::string > tick(std::uint64_t tick, engine::SequencePlayback &playback, rules::GameState &uiState)
std::expected< void, std::string > begin(const PieceMoveSpecialRequest &request, std::uint64_t tick, bool animationsEnabled, std::uint8_t randomBit=0)
std::uint32_t DataId
Definition DataBanks.hpp:10
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
std::uint16_t DataTag
Definition DataBanks.hpp:11
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39
constexpr std::uint8_t LoopToBeginning
PieceInterpolationSample samplePieceInterpolation(const PieceInterpolationPath &path, std::uint64_t tick) noexcept
std::optional< TokenPose > tokenOrientation(std::uint8_t boardSquare) noexcept
BoardCameraView pickCameraFor3Squares(std::int32_t square) noexcept
constexpr std::uint16_t JailPlaybackPriority
constexpr data::DataTag AnimationsPerToken
std::expected< PieceJailRoutePlan, PieceJailPlanError > planPaddyToJail(std::uint8_t tokenSquare, InterpolationVec3 startLocation, std::uint64_t startTick)
std::expected< PieceJailRoutePlan, PieceJailPlanError > planPaddyToToken(std::uint8_t tokenSquare, std::uint64_t startTick, std::uint8_t randomBit)
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
std::variant< std::monostate, Matrix2D, Matrix3D > SequenceTransform
Matrix3D translate3D(float x, float y, float z) noexcept
Matrix3D identity3D() noexcept
Matrix2D multiply(const Matrix2D &left, const Matrix2D &right) noexcept