Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
StatsDeedValueTextPlayback.cpp
Go to the documentation of this file.
2
4#include "FontRuntime.hpp"
5#include "MoneyFormat.hpp"
8
9#include <algorithm>
10#include <array>
11
12namespace monopoly::statsui
13{
14 namespace
15 {
16 constexpr std::uint32_t TextColour = 0x00C8C8C8U;
17
18 struct PlannedRow
19 {
20 int x{};
21 int y{};
22 std::string text;
23 };
24
25 [[nodiscard]] std::expected<std::vector<PlannedRow>, std::string>
27 const State& state,
28 const rules::GameState& gameState,
29 const PlayerPlaybackInputs& inputs,
30 int monetarySystem,
31 data::BoardEdition edition)
32 {
33 std::vector<PlannedRow> rows;
34 if (state.activeSort == 1) return rows;
35 if (state.activeSort != 0 &&
36 state.activeSort != 2 &&
37 state.activeSort != 3)
38 return std::unexpected(
39 "UDStats Deed value-text sort is outside retail 0..3 range");
40
41 const auto grid = planDeedGrid(state, gameState, inputs);
42 if (!grid) return std::unexpected(grid.error());
43
44 std::array<std::int64_t, rules::SquareCount> metricBySquare{};
45 std::array<bool, rules::SquareCount> metricKnown{};
46 for (std::size_t rank = 0; rank < rules::SquareCount; ++rank)
47 {
48 const auto square =
49 static_cast<std::size_t>(state.deedOrder[rank]);
50 if (square >= rules::SquareCount)
51 return std::unexpected(
52 "UDStats Deed value-text order references invalid square");
53 metricBySquare[square] = state.deedMetric[rank];
54 metricKnown[square] = true;
55 }
56
57 rows.reserve(grid->size());
58 for (const auto& deed : *grid)
59 {
60 if (deed.square < 0 ||
61 deed.square >= static_cast<int>(rules::SquareCount))
62 return std::unexpected(
63 "UDStats Deed value-text grid references invalid square");
64 const auto square =
65 static_cast<std::size_t>(deed.square);
66 if (!metricKnown[square])
67 return std::unexpected(
68 "UDStats Deed value-text metric is unavailable");
69 const auto formatted = money::format(
70 metricBySquare[square], monetarySystem, false, edition);
71 if (!formatted) return std::unexpected(formatted.error());
72
73 rows.push_back({
74 deed.x + DeedOwnerBarOffsetX,
75 deed.y + DeedOwnerBarOffsetY,
76 *formatted});
77 }
78 return rows;
79 }
80
81 [[nodiscard]] data::LegacyBitmapRGBA8 blankValueImage(int scale)
82 {
83 data::LegacyBitmapRGBA8 image{
84 DeedValueTextWidth * scale, DeedValueTextHeight * scale, {}};
85 image.pixels.assign(
86 static_cast<std::size_t>(DeedValueTextWidth) *
87 static_cast<std::size_t>(DeedValueTextHeight) * scale * scale * 4U, 0U);
88 return image;
89 }
90
91 struct RestoreFont final
92 {
93 fonts::Runtime& font;
94 std::optional<fonts::Settings> caller;
96 {
97 if (!caller) (void)font.restoreSettings(0);
98 else
99 {
100 if (font.settings().fontPath != caller->fontPath || font.settings().familyName != caller->familyName)
101 (void)font.setFont(caller->fontPath, caller->familyName);
102 (void)font.setSize(caller->size); font.setWeight(caller->weight);
103 font.setItalic(caller->italic); font.setUnderline(caller->underline);
104 font.setStrikeOut(caller->strikeOut);
105 }
106 }
107 };
108 }
109
110 std::expected<void, std::string> DeedValueTextPlayback::sync(
111 const State& state,
114 int monetarySystem,
118 {
119 std::vector<PlannedRow> rows;
121 state.screen == Screen::Deed)
122 {
123 const auto resources = playback.resources();
124 if (!resources)
125 return std::unexpected(
126 "UDStats Deed value-text resources are unavailable");
127 const auto planned = planRows(
128 state, gameState, inputs, monetarySystem,
129 resources->context().board);
130 if (!planned) return std::unexpected(planned.error());
131 rows = *planned;
132 }
133
134 bool layoutChanged = rows.size() != published_.size();
135 const auto callerSettings = fontRuntime && fontRuntime->ready() ?
136 std::optional{fontRuntime->settings()} : std::nullopt;
137 std::optional<fonts::Settings> effectiveSettings;
138 if (!rows.empty() && callerSettings)
139 {
140 // Values always use saved slot0 at size8/weight500. A read-only
141 // snapshot leaves TTF caches untouched on unchanged frames.
142 const auto* savedSettings=fontRuntime->savedSettings(0);
143 if(!savedSettings)return std::unexpected("font setting slot is unavailable");
144 effectiveSettings=*savedSettings;
145 effectiveSettings->size=8;effectiveSettings->weight=500;
146 }
147 const bool presentationChanged = modernAA != modernAA_ || effectiveSettings != fontSettings_;
148 if (!layoutChanged)
149 {
150 for (std::size_t index = 0; index < rows.size(); ++index)
151 {
152 if (published_[index].x != rows[index].x ||
153 published_[index].y != rows[index].y)
154 {
155 layoutChanged = true;
156 break;
157 }
158 }
159 }
160
161 if (layoutChanged)
162 {
163 const std::size_t required =
164 published_.size() + rows.size();
165 if (required >
167 playback.commands().pendingCount())
168 return std::unexpected(
169 "sequence command queue cannot fit UDStats Deed value-text transition");
170 }
171
172 if (!layoutChanged)
173 {
174 std::size_t redraws{};
175 for (std::size_t index = 0; index < rows.size(); ++index)
176 if (presentationChanged || index >= content_.size() || content_[index] != rows[index].text) ++redraws;
177 if (redraws > sequence::SequenceCommandQueue::Capacity - playback.commands().pendingCount())
178 return std::unexpected("sequence command queue cannot fit deed value redraws");
179 if (redraws == 0) return {};
180 }
181
182 if (rows.empty())
183 {
184 if (!layoutChanged) return {};
185 for (const auto& object : published_)
186 {
187 if (!playback.commands().enqueue(
189 object.id, DeedValueTextPriority, false}))
190 return std::unexpected(
191 "validated UDStats Deed value-text stop rejected");
192 }
193 published_.clear();
194 content_.clear();
195 return {};
196 }
197
198 if (fontRuntime == nullptr || !fontRuntime->ready())
199 return std::unexpected(
200 "UDStats Deed value-text font runtime is unavailable");
201
202 std::vector<data::LegacyBitmapRGBA8> images;
203 images.reserve(rows.size());
204 {
205 RestoreFont restore{*fontRuntime, modernAA ? callerSettings : std::nullopt};
206 if (const auto restored = fontRuntime->restoreSettings(0);
207 !restored)
208 return std::unexpected(restored.error().detail);
209 if (const auto sized = fontRuntime->setSize(8); !sized)
210 return std::unexpected(sized.error().detail);
211 fontRuntime->setWeight(500);
212
213 for (const auto& row : rows)
214 {
215 auto image = blankValueImage(modernAA ? 3 : 1);
216 const auto metrics = fontRuntime->measure(row.text);
217 if (!metrics)
218 return std::unexpected(metrics.error().detail);
219 const int x =
220 static_cast<int>(DeedValueTextWidth) - metrics->width;
221 if (!modernAA)
222 {
223 const auto copied = fontRuntime->blitText(image, row.text, x, 0, TextColour);
224 if (!copied) return std::unexpected(copied.error().detail);
225 }
226 else
227 {
228 const auto enlarged = fontRuntime->setSize(24);
229 if (!enlarged) return std::unexpected(enlarged.error().detail);
230 const auto raster = fontRuntime->render(row.text, TextColour, true);
231 const auto native = fontRuntime->setSize(8);
232 if (!native) return std::unexpected(native.error().detail);
233 if (!raster) return std::unexpected(raster.error().detail);
235 if (!copied) return std::unexpected(copied.error());
236 }
237 images.push_back(std::move(image));
238 }
239 }
240
241 while (surfaces_.size() < rows.size())
242 surfaces_.push_back(std::nullopt);
243
244 std::vector<std::size_t> changedRows;
245 for (std::size_t index = 0; index < rows.size(); ++index)
246 {
247 if (!surfaces_[index])
248 {
249 const auto created = playback.runtimeBitmaps().create(
251 if (!created) return std::unexpected(created.error());
252 surfaces_[index] = *created;
253 }
254
255 const bool changed =
256 presentationChanged || index >= content_.size() ||
257 content_[index] != rows[index].text;
258 if (changed)
259 {
260 const auto updated = playback.runtimeBitmaps().update(
261 *surfaces_[index], std::move(images[index]), modernAA ? std::optional<std::array<float,4>>{{0,0,float(DeedValueTextWidth),float(DeedValueTextHeight)}} : std::nullopt, modernAA);
262 if (!updated) return updated;
263 changedRows.push_back(index);
264 }
265 }
266
267 if (!layoutChanged)
268 {
269 for (const auto index : changedRows)
270 {
271 const auto forced = playback.forceRedraw(
272 *surfaces_[index], DeedValueTextPriority);
273 if (!forced) return forced;
274 }
275 content_.resize(rows.size());
276 for (std::size_t index = 0; index < rows.size(); ++index)
277 content_[index] = rows[index].text;
278 fontSettings_ = effectiveSettings; modernAA_ = modernAA;
279 return {};
280 }
281
282 for (const auto& object : published_)
283 {
284 if (!playback.commands().enqueue(
286 object.id, DeedValueTextPriority, false}))
287 return std::unexpected(
288 "validated UDStats Deed value-text stop rejected");
289 }
290
291 std::vector<Published> next;
292 next.reserve(rows.size());
293 for (std::size_t index = 0; index < rows.size(); ++index)
294 {
295 const auto program = sequence::SequenceProgram::rawBitmap(
297 if (!program)
298 return std::unexpected(program.error().detail);
299 if (!playback.commands().enqueue(
301 *program, DeedValueTextPriority, {},
303 rows[index].x, rows[index].y)}))
304 return std::unexpected(
305 "validated UDStats Deed value-text start rejected");
306 next.push_back({
307 *surfaces_[index], rows[index].x, rows[index].y});
308 }
309
310 published_ = std::move(next);
311 content_.resize(rows.size());
312 for (std::size_t index = 0; index < rows.size(); ++index)
313 content_[index] = rows[index].text;
314 fontSettings_ = effectiveSettings; modernAA_ = modernAA;
315 return {};
316 }
317
318 void DeedValueTextPlayback::reset() noexcept
319 {
320 surfaces_.clear();
321 published_.clear();
322 content_.clear();
323 fontSettings_.reset(); modernAA_ = false;
324 }
325}
std::string_view text
LegacyBitmapRGBA8 image
fonts::Runtime & font
fonts::Metrics metrics
unsigned state
unsigned index
std::optional< fonts::Settings > caller
CardTag native
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > rawBitmap(data::DataId id, data::LegacyDataType sourceType, DescriptionLimits limits={})
std::expected< void, std::string > sync(const State &state, const rules::GameState &gameState, const PlayerPlaybackInputs &inputs, int monetarySystem, display::Screen2D desiredView, fonts::Runtime *fontRuntime, engine::SequencePlayback &playback, bool modernAA=false)
std::expected< void, std::string > blitStraightRGBA8(LegacyBitmapRGBA8 &destination, const LegacyBitmapRGBA8 &source, std::int32_t destinationX, std::int32_t destinationY, BitmapBlitMode mode)
std::expected< std::string, std::string > format(std::int64_t defaultValue, int monetarySystem, bool printSymbol, data::BoardEdition edition)
constexpr std::size_t SquareCount
Definition RuleTypes.hpp:24
SequenceTransform moveXYTransform(std::int32_t x, std::int32_t y) noexcept
constexpr int DeedOwnerBarOffsetX
constexpr std::uint32_t DeedValueTextWidth
constexpr int DeedOwnerBarOffsetY
std::expected< std::vector< DeedPlayback::Published >, std::string > planDeedGrid(const State &state, const rules::GameState &gameState, const PlayerPlaybackInputs &inputs)
constexpr std::uint16_t DeedValueTextPriority
constexpr std::uint32_t DeedValueTextHeight