Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
DiceDisplay.cpp
Go to the documentation of this file.
1#include "DiceDisplay.hpp"
2
4
6{
7 namespace
8 {
9 constexpr std::uint8_t StayAtEnd = 2;
10 constexpr std::uint8_t LoopToBeginning = 3;
11
14 const std::array<std::uint8_t, 2>& values) noexcept
15 {
16 if (!validValues(values)) return data::EmptyDataId;
17 const auto offset = static_cast<std::uint32_t>(values[0] - 1U) * 12U +
18 static_cast<std::uint32_t>(values[1] - 1U) * 2U;
20 static_cast<data::DataTag>(base + offset));
21 }
22
24 {
26 sequence::moveRySTxzTransform(0.0F, 1.0F, 0.0F, 0.0F));
27 }
28 }
29
30 bool validValues(const std::array<std::uint8_t, 2>& values) noexcept
31 {
32 return values[0] >= 1 && values[0] <= 6 &&
33 values[1] >= 1 && values[1] <= 6;
34 }
35
37 const std::array<std::uint8_t, 2>& values) noexcept
38 {
39 return threeDSequence(Roll3DBaseTag, values);
40 }
41
43 const std::array<std::uint8_t, 2>& values) noexcept
44 {
45 return threeDSequence(Idle3DBaseTag, values);
46 }
47
48 TwoDPlan plan2D(const std::array<std::uint8_t, 2>& values,
49 bool rollAnimationDesired, bool iBarVisible) noexcept
50 {
51 TwoDPlan plan{};
53 {
54 plan.bobbing = true;
55 const auto bob = data::packDataId(
58 -35, 0, false, true};
59 plan.dice[1] = TwoDItem{bob,
60 static_cast<std::uint16_t>(IBarGeneralPriority + 1U),
61 -11, 0, true, true};
62 return plan;
63 }
64
65 if (!iBarVisible) return plan;
66 for (std::size_t index = 0; index < values.size(); ++index)
67 {
68 const auto face = values[index];
69 if (face < 1 || face > 6) continue;
71 static_cast<data::DataTag>(Fixed2DBaseTag + face - 1U));
72 plan.dice[index] = TwoDItem{id,
73 static_cast<std::uint16_t>(IBarGeneralPriority + index),
74 static_cast<std::int32_t>(index * 24) - 35, 0,
75 false, false};
76 }
77 return plan;
78 }
79
80 std::expected<void, std::string> TwoDPlayback::sync(
81 const std::array<std::uint8_t, 2>& values, bool rollAnimationDesired,
82 bool iBarVisible, bool& diceRollNotification, engine::SequencePlayback& playback)
83 {
84 const auto plan = plan2D(values, rollAnimationDesired, iBarVisible);
85 std::vector<sequence::SequenceCommand> commands;
86 auto fixed = currentDiceID_;
87 auto bob = currentBobDice_;
88 bool notification = diceRollNotification;
89 const auto start = [&](const TwoDItem& item) -> std::expected<void, std::string> {
90 auto program = sequence::SequenceProgram::load(playback.resources(), item.sequence);
91 if (!program) return std::unexpected(program.error().detail);
93 options.dropFrames = item.dropFrames;
94 commands.push_back(sequence::StartSequenceCommand{*program,item.priority,options});
95 commands.push_back(sequence::makeMoveXY(item.sequence,item.priority,item.x,item.y));
96 if (item.loop) commands.push_back(sequence::SetSequenceEndingActionCommand{
97 item.sequence,item.priority,LoopToBeginning,false});
98 return {};
99 };
100 for (std::size_t index=0;index<2;++index)
101 {
102 const auto desired = !plan.bobbing && plan.dice[index] ?
103 plan.dice[index]->sequence : data::EmptyDataId;
104 const auto priority=static_cast<std::uint16_t>(IBarGeneralPriority+index);
106 {
108 commands.push_back(sequence::StopSequenceCommand{fixed[index],priority,false});
111 {
112 const auto ready=start(*plan.dice[index]);
113 if (!ready) return ready;
114 }
115 }
116 notification=false; // Source consumes the notification inside the loop.
117 }
118 const auto desiredBob=plan.bobbing ? plan.dice[0]->sequence : data::EmptyDataId;
119 if (bob!=desiredBob)
120 {
122 {
123 commands.push_back(sequence::StopSequenceCommand{bob,IBarGeneralPriority,false});
124 commands.push_back(sequence::StopSequenceCommand{bob,IBarGeneralPriority+1,false});
125 }
128 for (const auto& item:plan.dice)
129 {
130 const auto ready=start(*item);
131 if (!ready) return ready;
132 }
133 }
134 // Load all programs before touching FIFO/state. Capacity is checked
135 // for the complete Stop/Start/Move/Loop operation, not per die.
136 if (commands.size()>sequence::SequenceCommandQueue::Capacity-playback.commands().pendingCount())
137 return std::unexpected("sequence command queue cannot fit dice 2D transition");
138 for (auto& command:commands)
139 {
140 const auto queued=std::visit([&](auto value) {
141 return playback.commands().enqueue(std::move(value));
142 },std::move(command));
143 if (!queued) return std::unexpected("validated dice 2D command rejected");
144 }
145 currentDiceID_=fixed;
146 currentBobDice_=bob;
147 diceRollNotification=notification;
148 return {};
149 }
150
151 std::expected<void, std::string> Playback::begin(RollRequest request)
152 {
153 if (request_)
154 return std::unexpected("dice roll playback already active");
155 request_ = request;
156 prepared_ = false;
157 rollStarted_ = false;
158 cameraReleased_ = false;
159 return {};
160 }
161
163 {
164 request_.reset();
165 current3D_ = data::EmptyDataId;
166 cameraSetTime_ = 0;
167 prepared_ = false;
168 rollStarted_ = false;
169 cameraReleased_ = false;
170 }
171
172 std::expected<bool, std::string> Playback::syncIdle(
173 bool boardVisible,
174 const rules::GameState& state,
176 {
177 const auto desired = boardVisible ? idle3DSequence(state.dice) :
178 data::EmptyDataId;
179 if (desired == current3D_) return false;
180
182 {
183 if (current3D_ != data::EmptyDataId)
184 {
185 const auto stopped = playback.stop(current3D_, Generic3DPriority);
186 if (!stopped) return std::unexpected(stopped.error());
187 current3D_ = data::EmptyDataId;
188 return true;
189 }
190 return false;
191 }
192
193 const std::optional<data::DataId> previous =
194 current3D_ == data::EmptyDataId ? std::nullopt :
195 std::optional<data::DataId>(current3D_);
196 const auto transitioned = playback.transitionMovedDrop(
199 if (!transitioned) return std::unexpected(transitioned.error());
200 current3D_ = desired;
201 return true;
202 }
203
204 std::expected<PlaybackUpdate, std::string> Playback::tick(
205 std::uint64_t tick,
206 bool boardVisible,
207 bool iBarVisible,
208 const rules::GameState& state,
210 {
211 PlaybackUpdate update{};
212 if (!request_)
213 {
214 const auto idle = syncIdle(boardVisible, state, playback);
215 if (!idle) return std::unexpected(idle.error());
216 update.idleChanged = *idle;
217 return update;
218 }
219
220 update.activeRoll = true;
221 const auto elapsed = tick >= request_->lockTick ?
222 tick - request_->lockTick : 0U;
223 const auto rollId = roll3DSequence(request_->values);
224
225 if (!prepared_)
226 {
227 if (cameraSetTime_ != request_->lockTick)
228 {
229 cameraSetTime_ = request_->lockTick;
230 update.cameraTakeover = true;
231 }
232 if (current3D_ != rollId &&
233 current3D_ != data::EmptyDataId)
234 {
235 const auto stopped = playback.stop(
236 current3D_, Generic3DPriority);
237 if (!stopped) return std::unexpected(stopped.error());
238 current3D_ = data::EmptyDataId;
239 }
240 prepared_ = true;
241 }
242
243 if (elapsed > 35U && rollId != data::EmptyDataId && !rollStarted_)
244 {
245 if (current3D_ != rollId)
246 {
247 const auto started = playback.transitionMovedDrop(
248 std::nullopt, rollId, Generic3DPriority,
250 if (!started) return std::unexpected(started.error());
251 current3D_ = rollId;
252 }
253 rollStarted_ = true;
254 update.startedRoll = true;
255 }
256
257 if (elapsed > 90U && !cameraReleased_)
258 {
259 cameraReleased_ = true;
260 update.cameraRelease = true;
261 update.announceRoll = true;
262 }
263
264 if (elapsed > 120U || !iBarVisible)
265 {
266 request_.reset();
267 prepared_ = false;
268 rollStarted_ = false;
269 cameraReleased_ = false;
270 update.activeRoll = false;
271 update.queueRelease = true;
272
273 const auto idle = syncIdle(boardVisible, state, playback);
274 if (!idle) return std::unexpected(idle.error());
275 update.idleChanged = *idle;
276 }
277 return update;
278 }
279}
decision::EconomicActionPlan plan
std::vector< std::uint8_t > data
std::size_t offset
data::DataId id
std::uint16_t priority
unsigned state
unsigned index
std::uint16_t value
Definition Timers.cpp:16
data::DataId idle
std::expected< PlaybackUpdate, std::string > tick(std::uint64_t tick, bool boardVisible, bool iBarVisible, const rules::GameState &state, engine::SequencePlayback &playback)
std::expected< void, std::string > begin(RollRequest request)
std::expected< void, std::string > sync(const std::array< std::uint8_t, 2 > &values, bool rollAnimationDesired, bool iBarVisible, bool &diceRollNotification, 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
data::DataId idle3DSequence(const std::array< std::uint8_t, 2 > &values) noexcept
constexpr data::DataTag Idle3DBaseTag
constexpr data::DataTag Roll3DBaseTag
data::DataId roll3DSequence(const std::array< std::uint8_t, 2 > &values) noexcept
constexpr std::uint16_t IBarGeneralPriority
bool validValues(const std::array< std::uint8_t, 2 > &values) noexcept
constexpr data::DataTag Bobbing2DTag
constexpr data::DataTag Fixed2DBaseTag
constexpr std::uint16_t Generic3DPriority
TwoDPlan plan2D(const std::array< std::uint8_t, 2 > &values, bool rollAnimationDesired, bool iBarVisible) noexcept
Matrix3D moveRySTxzTransform(float yaw, float scale, float x, float z) noexcept
std::variant< std::monostate, Matrix2D, Matrix3D > SequenceTransform
MoveSequenceCommand makeMoveXY(data::DataId dataId, std::uint16_t priority, std::int32_t x, std::int32_t y) noexcept