Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
TradeCashTextPlayback.cpp
Go to the documentation of this file.
2
3#include "FontRuntime.hpp"
4#include "MoneyFormat.hpp"
8
9#include <algorithm>
10#include <array>
11#include <memory>
12#include <utility>
13
14namespace monopoly::tradeui
15{
16 namespace
17 {
18 constexpr std::uint32_t CashWidth = 50;
19 constexpr std::uint32_t CashHeight = 11;
20 constexpr std::uint32_t White = 0x00FFFFFFU;
21
23 {
25 }
26
27 [[nodiscard]] bool wanted(
28 const State& state,
29 const rules::GameState& gameState,
31 std::size_t index) noexcept
32 {
34 state.playerSelectVisible || index >= 4)
35 return false;
36 if ((index == 0 || index == 2) &&
37 (state.playerA >= gameState.numberOfPlayers ||
38 state.playerA >= rules::MaxPlayers))
39 return false;
40 if ((index == 1 || index == 3) &&
41 (state.playerB >= gameState.numberOfPlayers ||
42 state.playerB >= rules::MaxPlayers))
43 return false;
44 return index < 2 || state.cashDesired[index] != 0;
45 }
46
47 [[nodiscard]] data::LegacyBitmapRGBA8 blankCashImage(int scale)
48 {
49 data::LegacyBitmapRGBA8 image{CashWidth * scale, CashHeight * scale, {}};
50 image.pixels.assign(std::size_t(image.width) * image.height * 4U, 0U);
51 return image;
52 }
53
54 struct RestoreDefaultFont final
55 {
56 fonts::Runtime* runtime{};
57 std::optional<fonts::Settings> caller;
59 {
60 if (runtime != nullptr)
61 {
62 if (!caller) (void)runtime->restoreSettings(0);
63 else
64 {
65 (void)runtime->setSize(caller->size); runtime->setWeight(caller->weight);
66 runtime->setItalic(caller->italic); runtime->setUnderline(caller->underline);
67 runtime->setStrikeOut(caller->strikeOut);
68 }
69 }
70 }
71 };
72 }
73
74 std::expected<void, std::string> CashTextPlayback::ensureSurfaces(
75 engine::SequencePlayback& playback)
76 {
77 for (auto& surface : textSurfaces_)
78 {
79 if (surface) continue;
80 const auto created = playback.runtimeBitmaps().create(
81 CashWidth, CashHeight, true);
82 if (!created) return std::unexpected(created.error());
83 surface = *created;
84 }
85 return {};
86 }
87
88 std::expected<void, std::string> CashTextPlayback::sync(
89 const State& state,
92 int monetarySystem,
95 {
96 std::array<bool, 4> visible{};
97 bool anyVisible{};
98 for (std::size_t index = 0; index < visible.size(); ++index)
99 {
101 anyVisible = anyVisible || visible[index];
102 }
103
104 const auto resources = playback.resources();
105 const bool readoutVisible = modernCashReadout && modernAA && resources &&
106 resources->context().board == data::BoardEdition::Usa &&
107 resources->context().language == data::LanguageId::EnglishUs &&
108 desiredView == display::Screen2D::Trade && state.cashDialogVisible &&
109 !state.playerSelectVisible && state.cashDialogSide < 2 &&
110 state.playerA < gameState.numberOfPlayers && state.playerA < rules::MaxPlayers &&
111 state.playerB < gameState.numberOfPlayers && state.playerB < rules::MaxPlayers;
112 if ((anyVisible || readoutVisible) && (fontRuntime == nullptr || !fontRuntime->ready()))
113 return std::unexpected("Trade cash text font runtime is unavailable");
114 if (anyVisible)
115 {
116 const auto ready = ensureSurfaces(playback);
117 if (!ready) return ready;
118 }
119
120 std::vector<Published> desired;
121 desired.reserve(9);
122 std::vector<data::DataId> changedTextSurfaces;
123 for (std::size_t index = 0; index < visible.size(); ++index)
124 {
125 if (!visible[index]) continue;
126 desired.push_back({*textSurfaces_[index], TradeCashTextPriority,
130 }
131
132 if (readoutVisible)
133 {
134 if (!readoutSurface_)
135 {
136 const auto created = playback.runtimeBitmaps().create(
138 if (!created) return std::unexpected(created.error());
139 readoutSurface_ = *created;
140 }
141 const auto text = money::format(state.cashTradeAmount, monetarySystem, true,
142 resources->context().board);
143 if (!text) return std::unexpected(text.error());
144 const auto settings = fontRuntime->settings();
145 if (!readoutText_ || *readoutText_ != *text || !readoutFont_ || *readoutFont_ != settings)
146 {
147 constexpr int scale = 3;
148 auto raster = fontRuntime->renderPresentation(*text, 0x00D3EBF5U,
149 9 * scale, 600, false, false, false, true);
150 if (!raster) return std::unexpected(raster.error().detail);
152 TradeCashReadoutHeight * scale, {}};
153 image.pixels.assign(std::size_t(image.width) * image.height * 4, 0);
154 if (raster->width > image.width || raster->height > image.height)
155 return std::unexpected("Trade cash popup amount exceeds its qualified header: "+
156 std::to_string(raster->width)+"x"+std::to_string(raster->height)+" vs "+
157 std::to_string(image.width)+"x"+std::to_string(image.height));
159 (int(image.width)-int(raster->width))/2,
160 (int(image.height)-int(raster->height))/2,
162 if (!blitted) return std::unexpected(blitted.error());
163 const auto updated = playback.runtimeBitmaps().update(*readoutSurface_, std::move(image),
164 std::array<float,4>{0,0,float(TradeCashReadoutWidth),float(TradeCashReadoutHeight)}, true);
165 if (!updated) return updated;
166 readoutText_ = *text;
167 readoutFont_ = settings;
168 changedTextSurfaces.push_back(*readoutSurface_);
169 }
170 desired.push_back({*readoutSurface_, TradeCashReadoutPriority,
171 TradeCashDialogX[state.cashDialogSide]+8, TradeCashDialogY[state.cashDialogSide]+1});
172 }
173
174 std::vector<std::shared_ptr<const sequence::SequenceProgram>> programs;
175 programs.reserve(desired.size());
176 std::shared_ptr<const sequence::SequenceProgram> iconProgram;
177 for (const auto& object : desired)
178 {
179 if (data::isRuntimeBitmapDataId(object.id))
180 {
183 if (!program) return std::unexpected(program.error().detail);
184 programs.push_back(std::move(*program));
185 continue;
186 }
187 if (!iconProgram)
188 {
190 playback.resources(), object.id);
191 if (!loaded) return std::unexpected(loaded.error().detail);
192 iconProgram = std::move(*loaded);
193 }
194 programs.push_back(iconProgram);
195 }
196
197 const std::size_t required = current_.size() + desired.size();
199 playback.commands().pendingCount())
200 return std::unexpected(
201 "sequence command queue cannot fit Trade cash-text transition");
202
203 if (anyVisible)
204 {
205 const auto resources = playback.resources();
206 if (!resources)
207 return std::unexpected("Trade cash text has no resource snapshot");
208
209 RestoreDefaultFont restore{fontRuntime, modernAA ? std::optional{fontRuntime->settings()} : std::nullopt};
210 const auto selected = fontRuntime->restoreSettings(8);
211 if (!selected)
212 return std::unexpected(
213 "Trade cash title-font slot is unavailable: " +
214 selected.error().detail);
215 const auto selectedSettings = fontRuntime->settings();
216 const int scale = modernAA ? 3 : 1;
217
218 for (std::size_t index = 0; index < visible.size(); ++index)
219 {
220 if (!visible[index]) continue;
221 const auto text = money::format(
222 state.cashDesired[index], monetarySystem, true,
223 resources->context().board);
224 if (!text) return std::unexpected(text.error());
225 if (textCache_[index] && *textCache_[index] == *text && modernAA_[index] == modernAA &&
226 fontSettings_[index] && *fontSettings_[index] == selectedSettings)
227 continue;
228
229 auto image = blankCashImage(scale);
230 const auto metrics = fontRuntime->measure(*text);
231 if (!metrics) return std::unexpected(metrics.error().detail);
232 const int x = (static_cast<int>(CashWidth) - metrics->width) / 2;
233 if (!modernAA)
234 {
235 const auto blitted = fontRuntime->blitText(image, *text, x, 0, White);
236 if (!blitted) return std::unexpected(blitted.error().detail);
237 }
238 else
239 {
240 const auto enlarged = fontRuntime->setSize(selectedSettings.size * scale);
241 if (!enlarged) return std::unexpected(enlarged.error().detail);
242 const auto raster = fontRuntime->render(*text, White, true);
243 const auto native = fontRuntime->setSize(selectedSettings.size);
244 if (!native) return std::unexpected(native.error().detail);
245 if (!raster) return std::unexpected(raster.error().detail);
247 if (!blitted) return std::unexpected(blitted.error());
248 }
249 const auto updated = playback.runtimeBitmaps().update(
250 *textSurfaces_[index], std::move(image), modernAA ? std::optional<std::array<float,4>>{{0,0,float(CashWidth),float(CashHeight)}} : std::nullopt, modernAA);
251 if (!updated) return std::unexpected(updated.error());
252 textCache_[index] = *text;
253 modernAA_[index] = modernAA;
254 fontSettings_[index] = selectedSettings;
255 changedTextSurfaces.push_back(*textSurfaces_[index]);
256 }
257 }
258
259 if (desired == current_)
260 {
261 for (const auto id : changedTextSurfaces)
262 {
263 const auto forced =
264 playback.forceRedraw(id, readoutSurface_ && id == *readoutSurface_
266 if (!forced) return forced;
267 }
268 return {};
269 }
270 const bool retainPublished = readoutVisible || (readoutSurface_ &&
271 std::any_of(current_.begin(), current_.end(),
272 [&](const Published& object) { return object.id == *readoutSurface_; }));
273 for (const auto& object : current_)
274 {
275 if (retainPublished && std::find(desired.begin(), desired.end(), object) != desired.end()) continue;
276 if (!playback.commands().enqueue(sequence::StopSequenceCommand{
277 object.id, object.priority, false}))
278 return std::unexpected("validated Trade cash-text stop rejected");
279 }
280 for (std::size_t index = 0; index < desired.size(); ++index)
281 {
282 const auto& object = desired[index];
283 if (retainPublished && std::find(current_.begin(), current_.end(), object) != current_.end()) continue;
284 if (!playback.commands().enqueue(sequence::StartSequenceCommand{
285 programs[index], object.priority, {},
286 sequence::moveXYTransform(object.x, object.y)}))
287 return std::unexpected("validated Trade cash-text start rejected");
288 }
289 if (retainPublished) for (const auto id : changedTextSurfaces)
290 {
291 const auto retained = std::find_if(current_.begin(), current_.end(),
292 [&](const Published& object) { return object.id == id &&
293 std::find(desired.begin(), desired.end(), object) != desired.end(); });
294 if (retained != current_.end())
295 {
296 const auto forced = playback.forceRedraw(id, retained->priority);
297 if (!forced) return forced;
298 }
299 }
300 current_ = std::move(desired);
301 return {};
302 }
303
304 void CashTextPlayback::reset() noexcept
305 {
306 textSurfaces_.fill(std::nullopt);
307 textCache_.fill(std::nullopt);
308 current_.clear();
309 readoutSurface_.reset(); readoutText_.reset(); readoutFont_.reset();
310 modernAA_.fill(false); fontSettings_.fill(std::nullopt);
311 }
312}
std::string_view text
LegacyBitmapRGBA8 image
fonts::Metrics metrics
fonts::Runtime & runtime
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={})
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::expected< void, std::string > sync(const State &state, const rules::GameState &gameState, display::Screen2D desiredView, int monetarySystem, fonts::Runtime *fontRuntime, engine::SequencePlayback &playback, bool modernAA=false, bool modernCashReadout=false)
std::uint32_t DataId
Definition DataBanks.hpp:10
std::expected< void, std::string > blitStraightRGBA8(LegacyBitmapRGBA8 &destination, const LegacyBitmapRGBA8 &source, std::int32_t destinationX, std::int32_t destinationY, BitmapBlitMode mode)
bool isRuntimeBitmapDataId(DataId id) noexcept
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39
std::expected< std::string, std::string > format(std::int64_t defaultValue, int monetarySystem, bool printSymbol, data::BoardEdition edition)
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
SequenceTransform moveXYTransform(std::int32_t x, std::int32_t y) noexcept
constexpr std::uint16_t TradeCashReadoutPriority
constexpr std::uint16_t TradeCashTextPriority
constexpr std::uint32_t TradeCashReadoutHeight
constexpr std::array< std::int32_t, 2 > TradeCashDialogX
constexpr std::array< std::int32_t, 2 > TradeCashDialogY
constexpr data::DataTag TradeCashIconTag
constexpr std::array< std::int32_t, 4 > TradeCashIconY
constexpr std::uint32_t TradeCashReadoutWidth
constexpr std::array< std::int32_t, 4 > TradeCashTextY
constexpr std::array< std::int32_t, 4 > TradeCashIconX
constexpr std::array< std::int32_t, 4 > TradeCashTextX
constexpr std::array< std::uint16_t, 4 > TradeCashIconPriorities
std::vector< std::uint8_t > pixels