Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
IBarBackdropPlayback.cpp
Go to the documentation of this file.
2#include "RuntimeState.hpp"
3
4#include <array>
5#include <utility>
6#include <variant>
7#include <vector>
8
9namespace monopoly::ibar
10{
11 std::optional<std::uint8_t> BackdropPlayback::takeCardReadRequest() noexcept
12 {
13 return std::exchange(pendingCardRead_, std::nullopt);
14 }
15
16 std::expected<data::DataId, std::string> desiredBackdrop(
18 bool visible,
20 {
21 if (!visible)
22 {
23 return data::EmptyDataId;
24 }
25
27 {
28 return data::packDataId(
31 }
32
34 activePlayer >= state.numberOfPlayers)
35 {
36 return data::EmptyDataId;
37 }
38
39 const auto colour = state.players[activePlayer].colour;
41 {
42 return std::unexpected("IBar player colour is outside the legacy 0..5 range");
43 }
44
45 return data::packDataId(
47 static_cast<data::DataTag>(
49 }
50
52 bool visible,
54 const ActionButtonInputs& inputs) noexcept
55 {
56 RuleActionHitState result{};
57 if (!visible || activePlayer > rules::BankPlayer)
58 return result;
59
60 const auto add = [&](layout::ActionButtonSlot slot)
61 {
63 };
64
65 switch (inputs.ruleMode)
66 {
71 break;
76 break;
82 break;
87 break;
91 break;
95 break;
98 break;
102 if (inputs.raiseCashCanBankrupt) add(layout::ActionButtonSlot::Main);
103 break;
106 break;
111 break;
115 break;
121 if (inputs.rollDiceDesired) add(layout::ActionButtonSlot::Main);
122 break;
131 break;
138 break;
139 case RuleMode::Build:
140 case RuleMode::Sell:
145 break;
146 default:
147 break;
148 }
149 if (inputs.gameInProgress)
151 if (inputs.gameInProgress && inputs.tradeEligible &&
152 inputs.desired2DView != display::Screen2D::Trade)
153 {
155 }
156 return result;
157 }
158
159 std::expected<void, std::string> BackdropPlayback::sync(
160 const rules::GameState& state,
161 bool visible,
165 {
166 consumedPressedButton_.reset();
167 const auto resolved = desiredBackdrop(state, visible, activePlayer);
168 if (!resolved)
169 {
170 return std::unexpected(resolved.error());
171 }
172
174 if (desired != currentBackdrop_)
175 {
176 std::shared_ptr<const sequence::SequenceProgram> program;
178 {
180 playback.resources(), desired);
181 if (!loaded)
182 {
183 return std::unexpected(loaded.error().detail);
184 }
185 program = std::move(*loaded);
186 }
187
188 std::vector<sequence::SequenceCommand> commands;
189 if (currentBackdrop_ != data::EmptyDataId)
190 {
191 commands.push_back(sequence::StopSequenceCommand{
192 currentBackdrop_, BackdropPriority, false});
193 }
194
196 {
197 commands.push_back(sequence::StartSequenceCommand{
198 std::move(program), BackdropPriority});
199 commands.push_back(sequence::makeMoveXY(
201 }
202
203 if (commands.size() >
205 playback.commands().pendingCount())
206 {
207 return std::unexpected(
208 "sequence command queue cannot fit IBar backdrop transition");
209 }
210
211 for (auto& command : commands)
212 {
213 const auto queued = std::visit(
214 [&](auto value)
215 {
216 return playback.commands().enqueue(std::move(value));
217 },
218 std::move(command));
219
220 if (!queued)
221 {
222 return std::unexpected(
223 "validated IBar backdrop command rejected");
224 }
225 }
226
227 currentBackdrop_ = desired;
228 }
229
230 // UDIBar.cpp computes IBarIsStable once before iterating buttons.
231 // Off/Idle are stable; In/Out/Pressed block ordinary fly-in/fly-out
232 // transitions until the next stable cycle.
233 const auto stable = [](CameraButtonVisualState state) noexcept
234 {
237 };
238
239 const auto wantsDone = [](RuleMode mode) noexcept
240 {
241 switch (mode)
242 {
243 case RuleMode::Build:
244 case RuleMode::Sell:
253 return true;
254 default:
255 return false;
256 }
257 };
258 const auto wantsUseCard = [](RuleMode mode) noexcept
259 {
260 return mode == RuleMode::JailExitPCR ||
261 mode == RuleMode::JailExitPCX;
262 };
263 const auto wantsJailRoll = [](RuleMode mode) noexcept
264 {
265 return mode == RuleMode::JailExitPCR ||
266 mode == RuleMode::JailExitPXR;
267 };
268 const auto wantsPay = [](RuleMode mode) noexcept
269 {
270 return mode == RuleMode::JailExitPCR ||
271 mode == RuleMode::JailExitPXR ||
272 mode == RuleMode::JailExitPCX ||
273 mode == RuleMode::JailExitPXX;
274 };
275
276 const bool gameInProgress = runtime::state().gameInProgress;
277 const bool buyAuction = inputs.ruleMode == RuleMode::BuyAuction;
278 const bool taxDecision = inputs.ruleMode == RuleMode::TaxDecision;
279 const bool raiseMoney = inputs.ruleMode == RuleMode::RaiseMoney;
280 const bool bssmRulePlayerActive = activePlayer == inputs.rulePlayer;
281 const bool trading =
283 const bool hotelDecomposition =
285 const bool placeHouse =
287 const bool placeHotel =
289 const bool housingShort =
291 const bool hotelShort =
293 const bool gameOver = inputs.ruleMode == RuleMode::GameOver;
294 const bool bssmNormal =
295 inputs.ruleMode == RuleMode::StartTurn ||
296 inputs.ruleMode == RuleMode::OtherPlayer ||
297 inputs.ruleMode == RuleMode::DoneTurn ||
299 const bool bssmRaiseMoney = inputs.ruleMode == RuleMode::RaiseMoney;
300 const bool deedActive = inputs.ruleMode == RuleMode::DeedActive;
301 const bool buildDesired = (bssmNormal || deedActive) && inputs.canBuild;
302 const bool sellDesired =
303 (bssmNormal || bssmRaiseMoney || deedActive) && inputs.canSell;
304 const bool mortgageDesired =
305 (bssmNormal || bssmRaiseMoney || deedActive) && inputs.canMortgage;
306 const bool unmortgageDesired =
307 (bssmNormal || deedActive) && inputs.canUnmortgage;
308
309 struct ButtonRequest
310 {
312 bool desired;
313 bool useGrey;
314 };
315
316 // Preserve the original t=0..IBAR_BUTTON_DISTINCT_MAX iteration order
317 // for the subset whose predicates are now ported.
318 const std::array requests{
319 ButtonRequest{&auctionButton_, visible && buyAuction, inputs.aiButtonRemoteState},
320 ButtonRequest{&buyButton_, visible && buyAuction, inputs.aiButtonRemoteState},
321 ButtonRequest{&cameraButton_, visible, false},
322 ButtonRequest{&doneButton_, visible && wantsDone(inputs.ruleMode), inputs.aiButtonRemoteState},
323 ButtonRequest{&sellButton_, visible && (hotelDecomposition || sellDesired), inputs.aiButtonRemoteState},
324 ButtonRequest{&flatTaxButton_, visible && taxDecision, inputs.aiButtonRemoteState},
325 ButtonRequest{&percentageButton_, visible && taxDecision, inputs.aiButtonRemoteState},
326 ButtonRequest{&buildButton_, visible && buildDesired, inputs.aiButtonRemoteState},
327 ButtonRequest{&tradeAcceptButton_, visible && trading, inputs.aiButtonRemoteState},
328 ButtonRequest{&tradeCounterButton_, visible && trading, inputs.aiButtonRemoteState},
329 ButtonRequest{&bankruptButton_, visible && raiseMoney &&
330 inputs.raiseCashCanBankrupt, inputs.aiButtonRemoteState},
331 ButtonRequest{&tradeRejectButton_, visible && trading, inputs.aiButtonRemoteState},
332 ButtonRequest{&mortgageButton_, visible && mortgageDesired, inputs.aiButtonRemoteState},
333 ButtonRequest{&mainButton_, visible && gameInProgress &&
334 (inputs.desired2DView == display::Screen2D::Portfolio ||
335 inputs.desired2DView == display::Screen2D::Trade), false},
336 ButtonRequest{&optionsButton_, visible && gameInProgress, false},
337 ButtonRequest{&payButton_, visible && wantsPay(inputs.ruleMode), inputs.aiButtonRemoteState},
338 ButtonRequest{&newGameButton_, visible && gameOver, false},
339 ButtonRequest{&rollDiceButton_, visible &&
340 (inputs.rollDiceDesired || wantsJailRoll(inputs.ruleMode)),
341 inputs.aiButtonRemoteState},
342 ButtonRequest{&statusButton_, visible && gameInProgress &&
343 inputs.desired2DView == display::Screen2D::Main, false},
344 ButtonRequest{&tradeButton_, visible && gameInProgress &&
345 inputs.tradeEligible && inputs.desired2DView != display::Screen2D::Trade, false},
346 ButtonRequest{&unmortgageButton_, visible && unmortgageDesired, inputs.aiButtonRemoteState},
347 ButtonRequest{&exitButton_, visible && gameOver, false},
348 ButtonRequest{&useCardButton_, visible && wantsUseCard(inputs.ruleMode),
349 inputs.aiButtonRemoteState},
350 ButtonRequest{&auctionHouseButton_, visible && housingShort, inputs.aiButtonRemoteState},
351 ButtonRequest{&auctionHotelButton_, visible && hotelShort, inputs.aiButtonRemoteState},
352 ButtonRequest{&placeHouseButton_, visible && placeHouse, inputs.aiButtonRemoteState},
353 ButtonRequest{&placeHotelButton_, visible && placeHotel, inputs.aiButtonRemoteState}
354 };
355
356 bool actionButtonsStable = true;
357 for (const auto& request : requests)
359 stable(request.button->visualState());
360
361 const bool justChanged = inputs.trackRules &&
362 (trackedRuleMode_ != inputs.ruleMode ||
363 trackedRulePlayer_ != inputs.rulePlayer);
364 if (inputs.trackRules)
365 {
366 trackedRuleMode_ = inputs.ruleMode;
367 trackedRulePlayer_ = inputs.rulePlayer;
368 }
369
370 // IBAR_JustChanged makes this pass outgoing-only. It is cleared at the
371 // end of the original show routine, so the next sync may fly buttons in
372 // once every existing animation is stable again.
373 std::optional<std::uint8_t> consumedPressed;
374 for (const auto& request : requests)
375 {
376 const auto before = request.button->visualState();
377 const auto buttonIndex = request.button->buttonIndex();
378 const bool requestPressed = inputs.pressedButtonIndex == buttonIndex;
379 const auto result = request.button->sync(
380 request.desired, playback, request.useGrey,
382 if (!result)
383 return result;
384 if (requestPressed &&
386 request.button->visualState() == CameraButtonVisualState::Pressed))
387 {
388 consumedPressed = buttonIndex;
389 }
390 }
391
392 const auto desiredCard = inputs.ruleMode == RuleMode::ViewingCard
393 ? inputs.desiredCardIndex : std::optional<std::uint8_t>{};
394 const auto previousCardState = card_.visualState();
395 const auto card = card_.sync(
396 desiredCard, visible, inputs.desired2DView,
397 inputs.desiredBoardCamera, playback);
398 if (!card)
399 return card;
400 // UDIBar reads lastCardIn when the face appears, after the incoming
401 // animation, including its accelerated path while the IBar is hidden.
404 pendingCardRead_ = card_.lastCard();
405
406 const auto propertyTitles = propertyTitles_.sync(
407 inputs.propertyTitles, playback);
408 if (!propertyTitles)
409 return propertyTitles;
410
411 const bool propertyBarAvailable = visible &&
412 (inputs.desired2DView == display::Screen2D::Main ||
413 inputs.desired2DView == display::Screen2D::Trade);
414 const auto jailCards = jailCards_.sync(
416 if (!jailCards)
417 return jailCards;
418
419 const int ruleCurrentSquare =
420 state.currentPlayer < state.numberOfPlayers &&
421 state.currentPlayer < rules::MaxPlayers
422 ? state.players[state.currentPlayer].currentSquare
423 : -1;
424 const auto buyAuctionPopup = buyAuctionPopup_.sync(
425 inputs.desiredBuyAuctionSquare,
426 inputs.desired2DView,
428 playback);
429 if (!buyAuctionPopup)
430 return buyAuctionPopup;
431
432 const auto propertyHover = propertyHover_.sync(
434 inputs.tick, playback);
435 if (!propertyHover)
436 return propertyHover;
437
438 const auto scoreStrip = scoreStrip_.sync(inputs.scoreStrip, playback);
439 if (!scoreStrip)
440 return scoreStrip;
441
442 // UDIBar.cpp shows the bank during DISPLAY_UDIBAR_Show(), before
443 // DISPLAY_UDPIECES_Show() starts the dice at the same priority.
444 // Bank hover follows IBarPlayerCurrentMouseOver == RULE_MAX_PLAYERS
445 // and moves the existing priority-256 sequence down by one pixel.
446 const auto bank = bank_.sync(visible, inputs.bankHovered, playback);
447 if (!bank)
448 {
449 return bank;
450 }
451
452 const auto currentPlayer = currentPlayer_.sync(state, visible, playback);
453 if (!currentPlayer)
454 return currentPlayer;
455
456 consumedPressedButton_ = consumedPressed;
457 return {};
458 }
459}
PlayerNumber slot
unsigned index
int colour
Button button
std::uint16_t value
Definition Timers.cpp:16
std::expected< void, std::string > sync(const rules::GameState &state, bool visible, rules::PlayerNumber activePlayer, engine::SequencePlayback &playback, ActionButtonInputs inputs={})
std::optional< std::uint8_t > takeCardReadRequest() noexcept
std::expected< void, std::string > sync(bool visible, bool hovered, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(std::optional< std::uint8_t > desiredSquare, display::Screen2D view, int currentPlayerSquare, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(std::optional< std::uint8_t > desiredCard, bool iBarVisible, display::Screen2D view, pieces::BoardCameraView camera, engine::SequencePlayback &playback)
std::optional< std::uint8_t > lastCard() const noexcept
CardVisualState visualState() const noexcept
std::expected< void, std::string > sync(const rules::GameState &state, bool visible, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(const rules::GameState &state, bool propertyBarAvailable, rules::PlayerNumber activePlayer, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(const rules::GameState &state, const PropertyTitlePlan &titles, int currentMouseOver, std::uint64_t tick, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(const PropertyTitlePlan &plan, engine::SequencePlayback &playback)
std::expected< void, std::string > sync(const ScoreStripPlan &plan, engine::SequencePlayback &playback)
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > load(const data::DataSource &source, data::DataId id, std::size_t offset=0, DescriptionLimits limits={})
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 ActionButtonMask actionButtonBit(ActionButtonSlot slot) noexcept
RuleActionHitState ruleActionHitState(bool visible, rules::PlayerNumber activePlayer, const ActionButtonInputs &inputs) noexcept
constexpr std::uint16_t BackdropPriority
constexpr std::int32_t BackdropX
constexpr std::int32_t BackdropY
constexpr data::DataTag BankBackdropTag
std::expected< data::DataId, std::string > desiredBackdrop(const rules::GameState &state, bool visible, rules::PlayerNumber activePlayer)
constexpr data::DataTag PlayerBackdropBaseTag
State & state()
Definition IBar.cpp:1285
constexpr std::uint8_t PlayerColourCount
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
MoveSequenceCommand makeMoveXY(data::DataId dataId, std::uint16_t priority, std::int32_t x, std::int32_t y) noexcept
int propertyCurrentMouseOver
Definition IBar.hpp:51
std::array< PlayerDisplay, rules::MaxPlayers > players
Definition IBar.hpp:34