Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
IBarScoreTextPlayback.cpp
Go to the documentation of this file.
2
3#include "FontRuntime.hpp"
4#include "MoneyFormat.hpp"
7
8#include <algorithm>
9#include <string_view>
10#include <utility>
11
12namespace monopoly::ibar
13{
14 namespace
15 {
16 constexpr std::uint16_t TextPriority = ScoreBoxPriority;
17 constexpr std::uint32_t SurfaceWidth = layout::ScoreBoxLargeWidth;
18 constexpr std::uint32_t SurfaceHeight = 32;
19 constexpr std::uint32_t Black = 0x00000000U;
20 class FontSettingsGuard final
21 {
22 public:
23 explicit FontSettingsGuard(fonts::Runtime& runtime)
24 : runtime_(runtime), settings_(runtime.settings())
25 {
26 }
28 {
29 (void)runtime_.setSize(settings_.size);
30 runtime_.setWeight(settings_.weight);
31 runtime_.setItalic(settings_.italic);
32 runtime_.setUnderline(settings_.underline);
33 runtime_.setStrikeOut(settings_.strikeOut);
34 }
35
36 private:
37 fonts::Runtime& runtime_;
38 fonts::Settings settings_;
39 };
40
41 [[nodiscard]] data::LegacyBitmapRGBA8 blankImage(int scale)
42 {
43 data::LegacyBitmapRGBA8 result{SurfaceWidth * scale, SurfaceHeight * scale, {}};
44 result.pixels.assign(
45 static_cast<std::size_t>(result.width) * result.height * 4U, 0U);
46 return result;
47 }
48
49 [[nodiscard]] std::expected<void, std::string> draw(
50 data::LegacyBitmapRGBA8& destination,
51 fonts::Runtime& fontRuntime,
52 std::string_view text,
53 int x,
54 int y, bool antialiased)
55 {
56 if (text.empty()) return {};
57 if (antialiased)
58 {
59 const auto rendered = fontRuntime.render(text, Black, true);
60 if (!rendered) return std::unexpected(rendered.error().detail);
63 }
64 const auto blitted = fontRuntime.blitText(
65 destination, text, x, y, Black);
66 if (!blitted) return std::unexpected(blitted.error().detail);
67 return {};
68 }
69 }
70
71 std::expected<void, std::string> ScoreTextPlayback::ensureSurface(
72 std::size_t player,
73 engine::SequencePlayback& playback)
74 {
75 if (surfaces_[player]) return {};
76 const auto created = playback.runtimeBitmaps().create(
78 if (!created) return std::unexpected(created.error());
79 surfaces_[player] = *created;
80 return {};
81 }
82
83 std::expected<void, std::string> ScoreTextPlayback::sync(
84 const ScoreStripPlan& plan,
85 const std::array<ScoreTextState, rules::MaxPlayers>& textStates,
86 int monetarySystem,
87 data::BoardEdition edition,
89 engine::SequencePlayback& playback, bool presentation)
90 {
91 const bool modern = presentation && edition == data::BoardEdition::Usa;
92 const int scale = modern ? 3 : 1;
93 if (fontRuntime == nullptr || !fontRuntime->ready())
94 {
95 const bool anyVisible = std::any_of(plan.players.begin(), plan.players.end(),
96 [](const auto& player) { return player.visible; });
97 if (anyVisible)
98 return std::unexpected("IBar score text font runtime is unavailable");
99 }
100
101 for (std::size_t player = 0; player < rules::MaxPlayers; ++player)
102 {
103 const auto& desired = plan.players[player];
104 if (!desired.visible)
105 {
106 if (published_[player].visible && surfaces_[player])
107 {
108 if (!playback.commands().enqueue(sequence::StopSequenceCommand{
109 *surfaces_[player], TextPriority, false}))
110 return std::unexpected("IBar score text stop command rejected");
111 }
112 published_[player] = {};
113 continue;
114 }
115
116 const auto ready = ensureSurface(player, playback);
117 if (!ready) return ready;
118
119 const auto cash = money::format(
120 textStates[player].displayedCash,
121 monetarySystem, true, edition);
122 if (!cash) return std::unexpected(cash.error());
124 std::wstring_view(textStates[player].printedName));
125 if (!encodedName) return std::unexpected(encodedName.error().detail);
126 const auto& name = *encodedName;
127 const bool large = desired.width == layout::ScoreBoxLargeWidth;
128 const bool wideCurrency = monetarySystem == 2;
129 const std::string key = name + '\n' + *cash + '\n' +
130 (large ? "L" : "S") + (wideCurrency ? "W" : "N") + (modern ? "A" : "R");
131
132 const bool contentChanged =
133 !cache_[player] || *cache_[player] != key;
134 if (contentChanged)
135 {
136 FontSettingsGuard guard(*fontRuntime);
137 fontRuntime->setUnderline(false);
138 fontRuntime->setItalic(false);
139 if (const auto size = fontRuntime->setSize((large ? 11 : 7) * scale); !size)
140 return std::unexpected(size.error().detail);
141 fontRuntime->setWeight(large ? 600 : 400);
142
143 auto image = blankImage(scale);
144 if (const auto drawn = draw(image, *fontRuntime, name, 57 * scale, 5 * scale, modern); !drawn)
145 return drawn;
146
147 const int cashSize = large ? (wideCurrency ? 10 : 11) : 8;
148 if (const auto size = fontRuntime->setSize(cashSize * scale); !size)
149 return std::unexpected(size.error().detail);
150 fontRuntime->setWeight(large ? 600 : 400);
151 const auto metrics = fontRuntime->measure(*cash);
152 if (!metrics) return std::unexpected(metrics.error().detail);
153 const int cashX = desired.width * scale - metrics->width - 7 * scale;
154 if (const auto drawn = draw(image, *fontRuntime, *cash, cashX, 18 * scale, modern); !drawn)
155 return drawn;
156
157 const auto updated = modern
158 ? playback.runtimeBitmaps().update(*surfaces_[player], std::move(image),
159 std::array<float, 4>{0, 0, float(SurfaceWidth), float(SurfaceHeight)}, true)
160 : playback.runtimeBitmaps().update(*surfaces_[player], std::move(image));
161 if (!updated) return std::unexpected(updated.error());
162 cache_[player] = key;
163 }
164
165 const Published next{
166 true,
167 desired.x,
168 layout::ScoreY + (desired.hovered ? 1 : 0)};
169 if (!published_[player].visible)
170 {
171 const auto program = sequence::SequenceProgram::rawBitmap(
172 *surfaces_[player], data::LegacyDataType::Native);
173 if (!program) return std::unexpected(program.error().detail);
174 if (playback.commands().pendingCount() >
176 return std::unexpected("sequence command queue cannot fit IBar score text start");
177 if (!playback.commands().enqueue(sequence::StartSequenceCommand{
178 *program, TextPriority, {},
180 return std::unexpected("IBar score text start command rejected");
181 }
182 else if (published_[player].x != next.x ||
183 published_[player].y != next.y)
184 {
185 if (!playback.commands().enqueue(sequence::makeMoveXY(
186 *surfaces_[player], TextPriority, next.x, next.y)))
187 return std::unexpected("IBar score text move command rejected");
188 }
189 else if (contentChanged)
190 {
191 const auto forced = playback.forceRedraw(
192 *surfaces_[player], TextPriority);
193 if (!forced) return forced;
194 }
195 published_[player] = next;
196 }
197 return {};
198 }
199
200 void ScoreTextPlayback::reset() noexcept
201 {
202 surfaces_.fill(std::nullopt);
203 cache_.fill(std::nullopt);
204 published_ = {};
205 }
206}
decision::EconomicActionPlan plan
const char * name
std::string_view text
LegacyBitmapRGBA8 image
fonts::Metrics metrics
fonts::Runtime & runtime
std::uint64_t key
unsigned index
std::expected< void, std::string > sync(const ScoreStripPlan &plan, const std::array< ScoreTextState, rules::MaxPlayers > &textStates, int monetarySystem, data::BoardEdition edition, fonts::Runtime *fontRuntime, engine::SequencePlayback &playback, bool presentation=false)
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > rawBitmap(data::DataId id, data::LegacyDataType sourceType, DescriptionLimits limits={})
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, Error > transcodeUtf8(std::u16string_view text)
constexpr int ScoreBoxLargeWidth
constexpr std::uint16_t ScoreBoxPriority
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
MoveSequenceCommand makeMoveXY(data::DataId dataId, std::uint16_t priority, std::int32_t x, std::int32_t y) noexcept