Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
AuctionPennyBagsPlayback.cpp
Go to the documentation of this file.
2
3#include <array>
4#include <cstdlib>
5#include <memory>
6#include <utility>
7#include <variant>
8#include <vector>
9
11{
12 namespace
13 {
15 {
17 }
18
19 [[nodiscard]] std::uint8_t randomVariant() noexcept
20 {
21 return static_cast<std::uint8_t>(std::rand() % 3);
22 }
23 }
24
25 std::expected<data::DataId, std::string> pennyBagsSequenceDataId(
27 std::uint8_t variant)
28 {
29 const auto choose = [variant](const std::array<data::DataTag, 3>& tags)
30 -> std::expected<data::DataId, std::string>
31 {
32 if (variant >= tags.size())
33 return std::unexpected("auction Pennybags variant is outside legacy 0..2 range");
34 return pattern(tags[variant]);
35 };
36
37 switch (state)
38 {
59 return data::EmptyDataId;
60 }
61 return std::unexpected("auction Pennybags state is outside the legacy enum");
62 }
63
64 std::expected<PennyBagsUpdate, std::string> PennyBagsPlayback::sync(
65 State& state,
70 {
71 if (gameState.numberOfPlayers > rules::MaxPlayers)
72 return std::unexpected("auction Pennybags player count exceeds legacy maximum");
73
75 auto nextCurrent = currentSequence_;
76 auto nextDesired = desiredSequence_;
77 bool nextReachedEnd = animationReachedEnd_;
78 bool nextHeardIntro = heardIntro_;
79 PennyBagsUpdate update{};
80 auto nextSound = currentSound_;
81 bool sendReady = false;
82 std::uint32_t readyMask{};
83 std::int64_t readySerial{};
84
85 const auto generalSound = [&](udsound::PennybagsVoice voice)
86 {
87 nextSound = PennyBagsSoundRequest{voice, std::nullopt};
88 };
89 const auto specificSound = [&](udsound::PennybagsVoice voice, std::uint8_t offset)
90 {
92 };
93
94 const auto chooseSequence = [&](PennyBagsState next)
95 -> std::expected<void, std::string>
96 {
98 if (!id)
99 return std::unexpected(id.error());
100 nextDesired = *id;
101 return {};
102 };
103
105 {
106 if (planned.pennyBagsSwitch)
107 {
108 planned.pennyBagsSwitch = false;
109 switch (planned.nextPennyBags)
110 {
112 if (nextHeardIntro)
113 {
114 planned.pennyBagsSwitch = true;
116 }
117 else
118 {
119 nextHeardIntro = true;
120 if (auto selected = chooseSequence(PennyBagsState::Intro); !selected)
121 return std::unexpected(selected.error());
124 // Mirrors AuctionPennyBagsAnimReachedEnd=TRUE before the
125 // new intro ID is committed below.
126 nextReachedEnd = true;
127 }
128 break;
129
131 if (auto selected = chooseSequence(PennyBagsState::Instructions); !selected)
132 return std::unexpected(selected.error());
135 break;
136
138 if (auto selected = chooseSequence(PennyBagsState::StartBidding); !selected)
139 return std::unexpected(selected.error());
141 planned.nextPennyBags = PennyBagsState::Begin;
142 break;
143
145 if (planned.rollCallSerial != 0)
146 {
147 sendReady = true;
148 readyMask = planned.rollCallPlayers;
149 readySerial = planned.rollCallSerial;
150 }
151 planned.nextPennyBags = PennyBagsState::Idle;
152 break;
153
155 break;
156
158 if (planned.highestBidder && *planned.highestBidder < rules::MaxPlayers)
159 {
160 if (auto selected = chooseSequence(PennyBagsState::NameHighestBidder); !selected)
161 return std::unexpected(selected.error());
162 const auto token = gameState.players[*planned.highestBidder].token;
163 if (token >= rules::MaxTokens)
164 return std::unexpected("auction highest bidder token is out of range");
166 }
167 planned.nextPennyBags = PennyBagsState::Idle;
168 break;
169
171 if (auto selected = chooseSequence(PennyBagsState::GoingOnce); !selected)
172 return std::unexpected(selected.error());
174 planned.nextPennyBags = PennyBagsState::Idle;
175 break;
176
178 if (auto selected = chooseSequence(PennyBagsState::GoingTwice); !selected)
179 return std::unexpected(selected.error());
181 planned.nextPennyBags = PennyBagsState::Idle;
182 break;
183
185 if (auto selected = chooseSequence(PennyBagsState::Sold); !selected)
186 return std::unexpected(selected.error());
187 if (planned.highestBid > 0)
189 else
191 planned.nextPennyBags = planned.highestBid > 0
194 break;
195
197 if (auto selected = chooseSequence(PennyBagsState::Congrats); !selected)
198 return std::unexpected(selected.error());
199 if (planned.propertyForSale >= 28)
201 else
203 planned.nextPennyBags = PennyBagsState::EndAuction;
204 break;
205
208 planned.nextPennyBags = PennyBagsState::None;
209 update.requestedBackdrop = planned.formerView;
210 break;
211
213 break;
214 }
215 }
216 }
217 else
218 {
220 }
221
222 // UDAuct.cpp performs this test after processing the requested state and
223 // before replacing the displayed ID. Keep that one-frame handoff exact.
225 {
226 const auto info = playback.runtime().info(
228 if (!info)
229 return std::unexpected("auction Pennybags displayed sequence has no runtime info");
230 if (info->endTime <= info->sequenceClock)
231 {
232 planned.pennyBagsSwitch =
233 planned.nextPennyBags != PennyBagsState::Idle;
234 nextReachedEnd = true;
235 }
236 }
237
238 std::shared_ptr<const sequence::SequenceProgram> program;
240 {
242 playback.resources(), nextDesired);
243 if (!loaded)
244 return std::unexpected(loaded.error().detail);
245 program = std::move(*loaded);
246 }
247
248 std::vector<sequence::SequenceCommand> commands;
250 {
252 {
253 commands.push_back(sequence::StopSequenceCommand{
255 }
257 {
258 commands.push_back(sequence::StartSequenceCommand{
259 std::move(program), AuctionPennyBagsPriority, {}});
260 }
261 }
262
263 if (commands.size() > sequence::SequenceCommandQueue::Capacity -
264 playback.commands().pendingCount())
265 {
266 return std::unexpected(
267 "sequence command queue cannot fit auction Pennybags transition");
268 }
269
270 if (sendReady)
271 {
272 if (!readySender)
273 return std::unexpected("auction Pennybags ready sender is unavailable");
274 const auto sent = readySender(readyMask, readySerial);
275 if (!sent)
276 return std::unexpected(sent.error());
277 planned.rollCallSerial = 0;
278 }
279
280 for (auto& command : commands)
281 {
282 const auto queued = std::visit(
283 [&](auto value)
284 {
285 return playback.commands().enqueue(std::move(value));
286 },
287 std::move(command));
288 if (!queued)
289 return std::unexpected("validated auction Pennybags command rejected");
290 }
291
293 {
296 }
297
298 if (nextSound != currentSound_)
299 update.sound = nextSound;
300 state = std::move(planned);
301 currentSequence_ = nextCurrent;
302 desiredSequence_ = nextDesired;
303 animationReachedEnd_ = nextReachedEnd;
304 heardIntro_ = nextHeardIntro;
305 currentSound_ = nextSound;
306 return update;
307 }
308
310 {
311 currentSequence_ = data::EmptyDataId;
312 desiredSequence_ = data::EmptyDataId;
313 animationReachedEnd_ = true;
314 currentSound_.reset();
315 // heardIntro_ intentionally survives: source udauct_DoneAnAuctionBefore
316 // is static and is not reset by DISPLAY_UDAUCT_Initialize/Destroy.
317 }
318}
std::size_t offset
data::DataId id
unsigned state
unsigned index
int token
std::string error
std::uint16_t value
Definition Timers.cpp:16
std::expected< PennyBagsUpdate, std::string > sync(State &state, const rules::GameState &gameState, display::Screen2D desiredView, engine::SequencePlayback &playback, const AuctionReadySender &readySender)
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > load(const data::DataSource &source, data::DataId id, std::size_t offset=0, DescriptionLimits limits={})
constexpr data::DataTag PennyBagsAn04Tag
constexpr data::DataTag PennyBagsAn14Tag
constexpr data::DataTag PennyBagsAn07Tag
constexpr data::DataTag PennyBagsAn12Tag
constexpr data::DataTag PennyBagsAn03Tag
std::expected< data::DataId, std::string > pennyBagsSequenceDataId(PennyBagsState state, std::uint8_t variant)
constexpr data::DataTag PennyBagsAn15Tag
constexpr data::DataTag PennyBagsAn16Tag
constexpr data::DataTag PennyBagsAn05Tag
constexpr data::DataTag PennyBagsAn17Tag
constexpr data::DataTag PennyBagsAn13Tag
constexpr data::DataTag PennyBagsAn02Tag
constexpr std::uint16_t AuctionPennyBagsPriority
constexpr data::DataTag PennyBagsAn01Tag
constexpr data::DataTag PennyBagsAn06Tag
constexpr data::DataTag PennyBagsAn10Tag
constexpr data::DataTag PennyBagsAn08Tag
constexpr data::DataTag PennyBagsAn11Tag
std::function< std::expected< void, std::string >(std::uint32_t playerMask, std::int64_t serial)> AuctionReadySender
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::size_t MaxTokens
Definition RuleTypes.hpp:30
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14