Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
IBarScoreStripPlayback.cpp
Go to the documentation of this file.
2
3#include <memory>
4#include <utility>
5#include <variant>
6#include <vector>
7
8namespace monopoly::ibar
9{
10 namespace
11 {
13 {
15 }
16
17 [[nodiscard]] std::uint16_t colourPriority(std::size_t player) noexcept
18 {
19 return static_cast<std::uint16_t>(ScoreGeneralPriority + 1 + player);
20 }
21
22 [[nodiscard]] std::uint16_t tokenPriority(std::size_t player) noexcept
23 {
24 return static_cast<std::uint16_t>(ScoreBoxPriority + player);
25 }
26
27 [[nodiscard]] std::uint16_t jailPriority(std::size_t player) noexcept
28 {
29 return static_cast<std::uint16_t>(ScoreBoxPriority + 1 + player);
30 }
31 [[nodiscard]] std::expected<std::shared_ptr<const sequence::SequenceProgram>, std::string>
32 loadProgram(engine::SequencePlayback& playback, data::DataId id)
33 {
34 auto loaded = sequence::SequenceProgram::load(playback.resources(), id);
35 if (!loaded)
36 return std::unexpected(loaded.error().detail);
37 return std::move(*loaded);
38 }
39
40 void addStartXY(
41 std::vector<sequence::SequenceCommand>& commands,
42 std::shared_ptr<const sequence::SequenceProgram> program,
43 data::DataId id,
44 std::uint16_t priority,
45 int x,
46 int y)
47 {
48 commands.push_back(sequence::StartSequenceCommand{
49 std::move(program), priority, {}});
50 commands.push_back(sequence::makeMoveXY(id, priority, x, y));
51 }
52 }
53
54 std::expected<ScoreStripPlan, std::string> planScoreStrip(
57 {
58 if (state.numberOfPlayers > rules::MaxPlayers)
59 return std::unexpected("IBar score strip player count exceeds legacy maximum");
60 ScoreStripPlan result{};
61 result.tick = inputs.tick;
62 const int count = static_cast<int>(state.numberOfPlayers);
63 if (count == 0) return result;
64
66 const data::DataTag colourBase = count <= 4
68
69 for (int player = 0; player < count; ++player)
70 {
71 if (!inputs.visiblePlayers[static_cast<std::size_t>(player)])
72 continue;
73
74 const auto& source = state.players[static_cast<std::size_t>(player)];
75 if (source.token >= rules::MaxTokens)
76 return std::unexpected("IBar score token is outside legacy token range");
77 if (source.colour >= ScorePlayerColourCount)
78 return std::unexpected("IBar score colour is outside legacy 0..5 range");
79
80 auto& target = result.players[static_cast<std::size_t>(player)];
81 target.visible = true;
82 target.token = mainData(static_cast<data::DataTag>(ScoreTokenBaseTag + source.token));
83 target.colourBar = mainData(static_cast<data::DataTag>(colourBase + source.colour));
84 target.jailBars = inputs.gameInProgress && source.currentSquare == 40;
85 target.x = layout::scoreX(player, count, width);
86 target.width = width;
87 target.hovered = inputs.hoveredPlayer == player;
88 target.cash = source.cash;
89 target.name = source.name;
90 }
91 return result;
92 }
93
94 std::expected<void, std::string> ScoreStripPlayback::sync(
95 const ScoreStripPlan& plan,
97 {
98 for (std::size_t player = 0; player < players_.size(); ++player)
99 {
100 const auto& desired = plan.players[player];
101 auto current = players_[player];
102 const auto& text = text_[player];
103 auto nextText = text;
104 nextText.redrawRequested = false;
105 nextText.lastCashChange = ScoreCashChange::None;
106
107 if (!desired.visible)
108 {
109 if (!current.visible)
110 {
111 text_[player] = std::move(nextText);
112 continue;
113 }
114 std::vector<sequence::SequenceCommand> commands;
115 if (current.colourBar != data::EmptyDataId)
116 commands.push_back(sequence::StopSequenceCommand{
117 current.colourBar, colourPriority(player), false});
118 if (current.token != data::EmptyDataId)
119 commands.push_back(sequence::StopSequenceCommand{
120 current.token, tokenPriority(player), false});
121 if (current.jailBars)
122 commands.push_back(sequence::StopSequenceCommand{
123 mainData(ScoreJailBarsTag), jailPriority(player), false});
124
125 if (commands.size() > sequence::SequenceCommandQueue::Capacity -
126 playback.commands().pendingCount())
127 return std::unexpected("sequence command queue cannot fit IBar score hide transition");
128
129 for (auto& command : commands)
130 {
131 const auto queued = std::visit(
132 [&](auto value)
133 {
134 return playback.commands().enqueue(std::move(value));
135 },
136 std::move(command));
137 if (!queued)
138 return std::unexpected("validated IBar score hide command rejected");
139 }
140 players_[player] = {};
141 text_[player] = std::move(nextText);
142 continue;
143 }
144
145 const bool changed = !current.visible ||
146 current.token != desired.token ||
147 current.colourBar != desired.colourBar ||
148 current.jailBars != desired.jailBars ||
149 current.x != desired.x ||
150 current.hovered != desired.hovered;
151 const bool textChanged = desired.cash != text.displayedCash ||
152 desired.name != text.printedName;
153 if (changed || textChanged)
154 {
155 if (desired.cash != text.displayedCash &&
156 text.lastCashUpdateTick <= plan.tick - 20u)
157 {
158 nextText.lastCashChange = desired.cash > text.displayedCash
160 nextText.displayedCash = desired.cash;
161 nextText.lastCashUpdateTick = plan.tick;
162 }
163 nextText.printedName = desired.name;
164 nextText.redrawRequested = true;
165 }
166 if (!changed)
167 {
168 text_[player] = std::move(nextText);
169 continue;
170 }
171 const auto tokenProgram = loadProgram(playback, desired.token);
172 if (!tokenProgram) return std::unexpected(tokenProgram.error());
173 const auto colourProgram = loadProgram(playback, desired.colourBar);
174 if (!colourProgram) return std::unexpected(colourProgram.error());
175
176 std::shared_ptr<const sequence::SequenceProgram> jailProgram;
177 if (desired.jailBars)
178 {
179 const auto loadedJail = loadProgram(playback, mainData(ScoreJailBarsTag));
180 if (!loadedJail) return std::unexpected(loadedJail.error());
182 }
183
184 std::vector<sequence::SequenceCommand> commands;
185 if (current.colourBar != data::EmptyDataId)
186 commands.push_back(sequence::StopSequenceCommand{
187 current.colourBar, colourPriority(player), false});
188 if (current.token != data::EmptyDataId)
189 commands.push_back(sequence::StopSequenceCommand{
190 current.token, tokenPriority(player), false});
191
192 const int hoverOffset = desired.hovered ? 1 : 0;
193 addStartXY(commands, *tokenProgram, desired.token,
194 tokenPriority(player), desired.x + 5,
196 addStartXY(commands, *colourProgram, desired.colourBar,
197 colourPriority(player), desired.x,
199 commands.push_back(sequence::StopSequenceCommand{
200 mainData(ScoreJailBarsTag), jailPriority(player), false});
201 if (desired.jailBars)
202 {
204 jailPriority(player), desired.x + 1,
206 }
207
208 if (commands.size() > sequence::SequenceCommandQueue::Capacity -
209 playback.commands().pendingCount())
210 return std::unexpected("sequence command queue cannot fit IBar score transition");
211
212 for (auto& command : commands)
213 {
214 const auto queued = std::visit(
215 [&](auto value)
216 {
217 return playback.commands().enqueue(std::move(value));
218 },
219 std::move(command));
220 if (!queued)
221 return std::unexpected("validated IBar score command rejected");
222 }
223
224 players_[player] = ScoreStripPlayerRuntime{
225 true, desired.token, desired.colourBar,
226 desired.jailBars, desired.x, desired.hovered};
227 text_[player] = std::move(nextText);
228 }
229 return {};
230 }
231}
std::size_t count
decision::EconomicActionPlan plan
std::string_view text
std::uint16_t priority
std::shared_ptr< const data::HmdTextureImage > source
std::uint32_t width
unsigned index
std::uint16_t value
Definition Timers.cpp:16
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
int scoreX(int playerIndex, int numberOfPlayers, int playerWidth) noexcept
int scoreBoxWidth(int numberOfPlayers) noexcept
constexpr std::uint16_t ScoreBoxPriority
constexpr data::DataTag ScoreJailBarsTag
constexpr data::DataTag ScoreLargeColourBaseTag
constexpr std::uint8_t ScorePlayerColourCount
constexpr std::uint16_t ScoreGeneralPriority
constexpr data::DataTag ScoreTokenBaseTag
constexpr data::DataTag ScoreSmallColourBaseTag
State & state()
Definition IBar.cpp:1285
std::expected< ScoreStripPlan, std::string > planScoreStrip(const rules::GameState &state, const ScoreStripInputs &inputs)
constexpr std::size_t MaxTokens
Definition RuleTypes.hpp:30
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
std::array< PlayerDisplay, rules::MaxPlayers > players
Definition IBar.hpp:34