Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
IBarRuntimeTextPlayback.cpp
Go to the documentation of this file.
2
3#include "FontRuntime.hpp"
4#include "MoneyFormat.hpp"
5
6#include <algorithm>
7#include <utility>
8
9namespace monopoly::ibar
10{
11 namespace
12 {
13 constexpr std::array<std::uint16_t, 4> Priorities{256, 257, 258, 5000};
14 constexpr std::array<int, 4> X{740, 750, 336, 676};
15 constexpr std::array<int, 4> Y{506, 525, 530, 508};
16 constexpr std::array<data::DataTag, 3> BaseTags{0x01BE, 0x01BD, 0x01B9};
17
18 class FontGuard
19 {
20 public:
21 explicit FontGuard(fonts::Runtime& font) : font_(font), old_(font.settings()) {}
23 {
24 (void)font_.setSize(old_.size);
25 font_.setWeight(old_.weight);
26 font_.setItalic(old_.italic);
27 font_.setUnderline(old_.underline);
28 font_.setStrikeOut(old_.strikeOut);
29 }
30 private:
31 fonts::Runtime& font_;
32 fonts::Settings old_;
33 };
34 }
35
36 std::expected<void, std::string> RuntimeTextPlayback::sync(
37 const rules::GameState& game, const State& ui,
38 const RuleProjection& projection, bool visible, bool propertyBar,
39 rules::PlayerNumber activePlayer, std::uint64_t tick,
40 int monetarySystem, data::BoardEdition edition,
42 {
43 auto nextMessage = message_;
44 auto nextColor = messageColor_;
45 auto nextWanted = wantedTick_;
46 auto nextCash = cashSerial_;
47 auto nextPinned = messagePinned_;
48 if (projection.mode == RuleMode::RaiseMoney)
49 {
50 const auto amount = money::format(-projection.raiseCashNeeded, monetarySystem, true, edition);
51 if (!amount) return std::unexpected(amount.error());
52 nextMessage = *amount; nextColor = 0x004040FF; nextWanted = tick; nextPinned = true;
53 }
55 {
56 nextMessage = std::to_string(*ui.decompositionHousesToSell);
57 if (edition == data::BoardEdition::Usa) nextMessage += " houses";
58 nextColor = 0x000000FF; nextWanted = tick; nextPinned = true;
59 }
60 else if (ui.cashAnimationAmount && (nextCash != ui.cashAnimationSerial ||
61 monetarySystem_ != monetarySystem || edition_ != edition))
62 {
63 const auto amount = money::format(*ui.cashAnimationAmount, monetarySystem, true, edition);
64 if (!amount) return std::unexpected(amount.error());
65 nextMessage = *amount; nextColor = 0x00FFFFFF;
67 }
68 const std::array<bool, 4> desired{
71 visible && game.currentPlayer < game.numberOfPlayers && game.currentPlayer < rules::MaxPlayers,
72 visible && nextWanted && tick >= *nextWanted &&
73 tick - *nextWanted < (nextPinned ? 1U : 240U)};
74 std::size_t required{};
75 for (std::size_t i = 0; i < desired.size(); ++i) required += desired[i] != shown_[i];
76 if (required > sequence::SequenceCommandQueue::Capacity - playback.commands().pendingCount())
77 return std::unexpected("IBar runtime text transition exceeds command capacity");
78
79 std::array<std::optional<data::LegacyBitmapRGBA8>, 4> images;
80 auto keys = keys_;
81 std::array<std::string, 4> text;
82 if (desired[0])
83 {
84 auto houses = game.options.maximumHouses;
85 auto hotels = game.options.maximumHotels;
86 for (const auto& square : game.squares)
87 if (square.houses < game.options.housesPerHotel) houses -= square.houses;
88 else --hotels;
89 text[0] = std::to_string(houses); text[1] = std::to_string(hotels);
90 for (std::size_t i = 0; i < 2; ++i)
91 if (text[i].size() < 3) text[i].insert(0, 3 - text[i].size(), ' ');
92 }
93 if (desired[2])
94 {
96 std::wstring_view(game.players[game.currentPlayer].name));
97 if (!encodedName) return std::unexpected(encodedName.error().detail);
98 text[2] = *encodedName;
99 }
100 text[3] = nextMessage;
101 // The caller qualifies the procedural USA/en-US presentation context.
102 // Keep the existing font, native footprint and coverage; only its ink
103 // changes to readable cream against the modern teal property bar.
104 const std::uint32_t nameInk = modernPresentation && edition == data::BoardEdition::Usa ?
105 0x00D7E8EDU : 0U;
106
107 for (std::size_t i = 0; i < desired.size(); ++i)
108 {
109 if (!desired[i]) continue;
110 keys[i] = text[i] + ':' + std::to_string(i == 3 ? nextColor : i == 2 ? nameInk : 0);
111 if (surfaces_[i] != data::EmptyDataId && keys[i] == keys_[i]) continue;
112 if (!font || !font->ready()) return std::unexpected("IBar runtime text requires a ready font");
113 FontGuard guard(*font);
114 if (const auto sized = font->setSize(12); !sized) return std::unexpected(sized.error().detail);
115 font->setWeight(i == 3 ? 900 : 700);
116 font->setItalic(false); font->setUnderline(false); font->setStrikeOut(false);
117 data::LegacyBitmapRGBA8 image{120, 20, std::vector<std::uint8_t>(120 * 20 * 4)};
118 if (i < 3)
119 {
120 const auto resources = playback.resources();
121 if (!resources) return std::unexpected("IBar runtime text requires resources");
123 const auto metadata = resources->data().metadata(id);
124 if (!metadata) return std::unexpected(metadata.error().detail);
125 const auto bytes = resources->data().load(id);
126 if (!bytes) return std::unexpected(bytes.error().detail);
127 const auto base = bases_.resolve(id, metadata->type, *bytes);
128 if (!base) return std::unexpected(base.error().detail);
129 image = (*base)->image;
130 if (i == 2) std::fill(image.pixels.begin(), image.pixels.end(), 0);
131 }
132 if (text[i].empty())
133 {
134 images[i] = std::move(image);
135 continue;
136 }
137 const auto measured = font->measure(text[i]);
138 if (!measured) return std::unexpected(measured.error().detail);
139 const auto x = i < 2 ? (static_cast<int>(image.width) - measured->width) / 2 :
140 i == 2 ? std::max(0, (140 - measured->width) / 2) : 120 - measured->width;
141 const auto blitted = font->blitText(
142 image, text[i], x, i < 2 ? 4 : 0,
143 i < 2 ? 0x00FFFFFF : i == 2 ? nameInk : nextColor);
144 if (!blitted) return std::unexpected(blitted.error().detail);
145 images[i] = std::move(image);
146 }
147
148 // All resource/font work and queue preflight precede publication.
149 for (std::size_t i = 0; i < desired.size(); ++i)
150 {
151 if (images[i])
152 {
153 if (surfaces_[i] == data::EmptyDataId)
154 {
155 const auto created = playback.runtimeBitmaps().create(images[i]->width, images[i]->height, true);
156 if (!created) return std::unexpected(created.error());
157 surfaces_[i] = *created;
158 }
159 const auto updated = playback.runtimeBitmaps().update(
160 surfaces_[i], std::move(*images[i]));
161 if (!updated) return std::unexpected(updated.error());
162 if (shown_[i] && desired[i])
163 {
164 const auto forced =
165 playback.forceRedraw(surfaces_[i], Priorities[i]);
166 if (!forced) return forced;
167 }
168 }
169 if (desired[i] == shown_[i]) continue;
170 if (desired[i])
171 {
172 const auto started = playback.startXY(surfaces_[i], Priorities[i], X[i], Y[i]);
173 if (!started) return started;
174 }
175 else
176 {
177 const auto stopped = playback.stop(surfaces_[i], Priorities[i]);
178 if (!stopped) return stopped;
179 }
180 }
181 shown_ = desired; keys_ = std::move(keys);
182 message_ = std::move(nextMessage); messageColor_ = nextColor;
183 wantedTick_ = nextWanted; cashSerial_ = nextCash; messagePinned_ = nextPinned;
184 monetarySystem_ = monetarySystem; edition_ = edition;
185 return {};
186 }
187
189 {
190 surfaces_ = {}; keys_ = {}; shown_ = {}; bases_.clear();
191 cashSerial_.reset(); wantedTick_.reset(); monetarySystem_.reset(); edition_.reset();
192 messagePinned_ = false; message_.clear(); messageColor_ = 0;
193 }
194}
std::string_view text
LegacyBitmapMetadata metadata
LegacyBitmapRGBA8 image
std::vector< std::byte > bytes
fonts::Runtime & font
std::uint32_t height
std::uint32_t width
unsigned index
std::expected< std::shared_ptr< const BitmapRuntimeAsset >, BitmapError > resolve(DataId id, LegacyDataType sourceType, SharedDataBytes bytes)
std::expected< void, std::string > sync(const rules::GameState &game, const State &ui, const RuleProjection &projection, bool visible, bool propertyBar, rules::PlayerNumber activePlayer, std::uint64_t tick, int monetarySystem, data::BoardEdition edition, fonts::Runtime *font, engine::SequencePlayback &playback, bool modernPresentation=false)
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39
std::expected< std::string, Error > transcodeUtf8(std::u16string_view text)
std::expected< std::string, std::string > format(std::int64_t defaultValue, int monetarySystem, bool printSymbol, data::BoardEdition edition)
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
std::uint64_t cashAnimationTick
Definition IBar.hpp:63
std::optional< std::int64_t > decompositionHousesToSell
Definition IBar.hpp:65
std::optional< std::int64_t > cashAnimationAmount
Definition IBar.hpp:62
std::uint64_t cashAnimationSerial
Definition IBar.hpp:64
std::array< SquareState, SquareCount > squares
std::array< PlayerState, MaxPlayers > players