Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
StatsBankPlayback.cpp
Go to the documentation of this file.
2
3#include "IBarLayout.hpp"
4#include "LegacyBitmap.hpp"
5
6#include <algorithm>
7#include <map>
8#include <memory>
9#include <set>
10#include <utility>
11#include <variant>
12
13namespace monopoly::statsui
14{
15 namespace
16 {
17 [[nodiscard]] constexpr data::DataId mainId(data::DataTag tag) noexcept
18 {
20 }
21
22 [[nodiscard]] constexpr data::DataId languageId(data::DataTag tag) noexcept
23 {
25 }
26
27 [[nodiscard]] std::expected<std::pair<int, int>, std::string>
28 bitmapSize(const sequence::SequenceProgram& program)
29 {
30 const auto resources = program.resources();
31 if (!resources)
32 return std::unexpected("UDStats Bank sequence has no resource snapshot");
33
34 for (const auto& description : program.descriptions())
35 {
36 if (!std::holds_alternative<data::SequenceBitmapData>(
37 description.record.data) || !description.contentsDataId)
38 continue;
39
40 const auto id = *description.contentsDataId;
41 const auto metadata = resources->data().metadata(id);
42 const auto bytes = resources->data().load(id);
43 if (!metadata || !bytes)
44 return std::unexpected("UDStats Bank bitmap dependency failed");
46 {
48 if (!bitmap) return std::unexpected(bitmap.error().detail);
49 return std::pair{bitmap->width, bitmap->height};
50 }
52 {
53 const auto bitmap = data::inspectLegacyUap(**bytes);
54 if (!bitmap) return std::unexpected(bitmap.error().detail);
55 return std::pair{static_cast<int>(bitmap->width),
56 static_cast<int>(bitmap->height)};
57 }
58 return std::unexpected(
59 "UDStats Bank 2D sequence content is not a bitmap");
60 }
61 return std::unexpected("UDStats Bank sequence contains no bitmap record");
62 }
63
64 using Objects = std::vector<BankPlayback::Published>;
65
66 [[nodiscard]] std::optional<Rect> deedRectForSquare(int target) noexcept
67 {
68 int column = 0;
69 int row = 1;
70 for (int square = 0; square < static_cast<int>(rules::SquareCount); ++square)
71 {
72 if (ibar::layout::propertyIndex(square) < 0) continue;
73 Rect rect{};
74 switch (square)
75 {
76 case 5: rect = {583,100,619,142}; break;
77 case 12: rect = {714,100,750,142}; break;
78 case 15: rect = {583,49,619,91}; break;
79 case 25: rect = {648,100,684,142}; break;
80 case 28: rect = {714,49,750,91}; break;
81 case 35: rect = {648,49,684,91}; break;
82 case 37:
83 --row;
84 rect = {38 + column * 69, 49 + row * 51,
85 74 + column * 69, 91 + row * 51};
86 --row;
87 break;
88 default:
89 rect = {38 + column * 69, 49 + row * 51,
90 74 + column * 69, 91 + row * 51};
91 --row;
92 if (row < 0)
93 {
94 ++column;
95 row = 2;
96 }
97 break;
98 }
99 if (square == target) return rect;
100 }
101 return std::nullopt;
102 }
103
104 [[nodiscard]] data::DataId deedId(int square) noexcept
105 {
106 const auto property = ibar::layout::propertyIndex(square);
107 if (property < 0) return data::EmptyDataId;
109 static_cast<data::DataTag>(BankDeedBaseTag + property));
110 }
111
112 [[nodiscard]] std::expected<Objects, std::string> houseObjects(
113 const State& state, const rules::GameState& gameState,
114 engine::SequencePlayback& playback)
115 {
116 Objects result;
117 const auto count = std::min<std::size_t>(
118 gameState.numberOfPlayers, rules::MaxPlayers);
119 const bool anyHotels = std::any_of(
120 state.bankPlayerHotels.begin(), state.bankPlayerHotels.begin() + count,
121 [](int value) { return value > 0; });
122 int hotelWidth = 0;
123 if (anyHotels)
124 {
126 playback.resources(), mainId(BankHotelTag));
127 if (!hotel) return std::unexpected(hotel.error().detail);
128 const auto size = bitmapSize(**hotel);
129 if (!size) return std::unexpected(size.error());
130 hotelWidth = size->first;
131 }
132 const bool anyHouses = std::any_of(
133 state.bankPlayerHouses.begin(), state.bankPlayerHouses.begin() + count,
134 [](int value) { return value > 0; });
135 const int housesPerRow = gameState.options.maximumHouses / 2;
136 if (anyHouses && housesPerRow <= 0)
137 return std::unexpected("UDStats Bank house capacity cannot form retail rows");
138 const int houseWidth = housesPerRow > 0 ? 346 / housesPerRow : 0;
139
140 for (std::size_t slot = 0; slot < count; ++slot)
141 {
142 const auto colour = gameState.players[slot].colour;
144 return std::unexpected("UDStats Bank player colour is out of range");
145 result.push_back({mainId(static_cast<data::DataTag>(
147
148 const int top = 300 + static_cast<int>(colour) * 22;
149 for (int hotel = 0; hotel < state.bankPlayerHotels[slot]; ++hotel)
150 result.push_back({mainId(BankHotelTag), BankIconPriority,
151 418 + hotel * hotelWidth, top + 2, true});
152 for (int house = 0; house < state.bankPlayerHouses[slot]; ++house)
153 {
154 const bool secondRow = house >= housesPerRow;
155 const int column = secondRow ? house - housesPerRow : house;
156 result.push_back({mainId(BankHouseTag), BankIconPriority,
157 38 + column * houseWidth + (secondRow ? 12 : 2),
158 top + (secondRow ? 6 : 1), true});
159 }
160 }
161 return result;
162 }
163
164 [[nodiscard]] std::expected<Objects, std::string> propertyObjects(
165 const State& state, engine::SequencePlayback& playback)
166 {
167 Objects result;
169 playback.resources(), languageId(BankSoldSignTag));
170 if (!sold) return std::unexpected(sold.error().detail);
171 const auto soldSize = bitmapSize(**sold);
172 if (!soldSize) return std::unexpected(soldSize.error());
173 const int signX = (BankDeedWidth - soldSize->first) / 2;
174 const int signY = (BankDeedHeight - soldSize->second) / 2;
175
176 for (int square = 0; square < static_cast<int>(rules::SquareCount); ++square)
177 {
178 const auto rect = deedRectForSquare(square);
179 const auto id = deedId(square);
180 if (!rect || id == data::EmptyDataId) continue;
181 const int x = BankContentX + rect->left;
182 const int y = BankContentY + rect->top;
183 result.push_back({id, BankOverlayPriority, x, y, true});
184
185 const auto status = state.bankDeeds[static_cast<std::size_t>(square)];
186 if (status == BankDeedState::Sold)
187 result.push_back({languageId(BankSoldSignTag), BankSignPriority,
188 x + signX, y + signY, true});
189 else if (status == BankDeedState::Mortgaged)
190 result.push_back({languageId(BankMortgageSignTag), BankSignPriority,
191 x + signX, y + signY, true});
192 }
193 return result;
194 }
195
197 engine::SequencePlayback& playback)
198 {
199 Objects result;
200 const auto resources = playback.resources();
201 if (!resources || resources->context().board != data::BoardEdition::Usa)
202 return result;
203 result.push_back({languageId(BankLiabilityDividendCardTag),
204 51, 365, 275, true, BankLiabilityCardScale});
205 result.push_back({languageId(BankLiabilityErrorCardTag),
206 51, 365, 358, true, BankLiabilityCardScale});
207 return result;
208 }
209 }
210
211 std::optional<Rect> bankDeedRect(int square) noexcept
212 {
213 return deedRectForSquare(square);
214 }
215
216 data::DataId bankDeedSequence(int square) noexcept
217 {
218 return deedId(square);
219 }
220
221 std::expected<void, std::string> BankPlayback::sync(
222 const State& state, const rules::GameState& gameState,
224 const AccountState* accounts)
225 {
227 const bool visible = desiredView == display::Screen2D::Portfolio &&
228 state.screen == Screen::Bank;
229 if (visible && state.activeSort == 0)
230 {
231 auto objects = houseObjects(state, gameState, playback);
232 if (!objects) return std::unexpected(objects.error());
233 desired = std::move(*objects);
234 }
235 else if (visible && state.activeSort == 1)
236 {
237 auto objects = propertyObjects(state, playback);
238 if (!objects) return std::unexpected(objects.error());
239 desired = std::move(*objects);
240 }
241 else if (visible && state.activeSort == 2)
243 else if (visible && state.activeSort == 3 && accounts)
244 {
245 for (int arrow = 0; arrow < 2; ++arrow)
246 {
247 const bool enabled = arrow == 0 ? accounts->scrollLines < accounts->scrollLimit :
248 accounts->scrollLines > 0;
249 if (!enabled) continue;
250 const auto tag = static_cast<data::DataTag>(0x0007 + arrow * 2 +
251 (state.historyArrowPressed == arrow ? 1 : 0));
252 desired.push_back({mainId(tag), 620, 0, 0, false});
253 }
254 }
255
256 if (desired == current_) return {};
257
258 std::map<data::DataId, std::shared_ptr<const sequence::SequenceProgram>> programs;
259 for (const auto& object : desired)
260 {
261 if (programs.contains(object.id)) continue;
262 auto loaded = sequence::SequenceProgram::load(playback.resources(), object.id);
263 if (!loaded)
264 return std::unexpected("UDStats Bank overlay resource failed: " +
265 loaded.error().detail);
266 programs.emplace(object.id, std::move(*loaded));
267 }
268
269 std::set<std::pair<data::DataId, std::uint16_t>> stops;
270 for (const auto& object : current_)
271 stops.emplace(object.id, object.priority);
272
273 const std::size_t required = stops.size() + desired.size();
275 playback.commands().pendingCount())
276 {
277 return std::unexpected(
278 "sequence command queue cannot fit UDStats Bank overlay transition");
279 }
280
281 for (const auto& [id, priority] : stops)
282 {
283 if (!playback.commands().enqueue(
285 return std::unexpected("validated UDStats Bank stop rejected");
286 }
287
288 for (const auto& object : desired)
289 {
290 std::optional<sequence::SequenceTransform> transform;
291 if (object.positioned && object.scale != 1.0F)
292 transform = sequence::SequenceTransform{
294 object.x, object.y, object.scale, 0.0F)};
295 else if (object.positioned)
296 transform = sequence::moveXYTransform(object.x, object.y);
297 if (!playback.commands().enqueue(sequence::StartSequenceCommand{
298 programs.at(object.id), object.priority, {}, std::move(transform)}))
299 return std::unexpected("validated UDStats Bank start rejected");
300 }
301
302 current_ = std::move(desired);
303 return {};
304 }
305}
std::size_t count
LegacyBitmapMetadata metadata
std::vector< std::byte > bytes
int top
bool enabled
Definition Engine.cpp:170
data::DataId id
std::uint16_t priority
PlayerNumber slot
unsigned state
unsigned index
int colour
Rect rect
std::uint16_t value
Definition Timers.cpp:16
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, engine::SequencePlayback &playback, const AccountState *accounts=nullptr)
std::uint32_t DataId
Definition DataBanks.hpp:10
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
std::uint16_t DataTag
Definition DataBanks.hpp:11
std::expected< LegacyUapMetadata, BitmapError > inspectLegacyUap(std::span< const std::byte > bytes)
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39
std::expected< LegacyBitmapMetadata, BitmapError > inspectLegacyBitmap(std::span< const std::byte > bytes)
int propertyIndex(int square) noexcept
constexpr std::size_t MaxPlayerColours
Definition RuleTypes.hpp:33
constexpr std::size_t SquareCount
Definition RuleTypes.hpp:24
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
std::variant< std::monostate, Matrix2D, Matrix3D > SequenceTransform
Matrix2D moveXYSRTransform(std::int32_t x, std::int32_t y, float scale, float rotate) noexcept
SequenceTransform moveXYTransform(std::int32_t x, std::int32_t y) noexcept
std::shared_ptr< const data::ResourceSnapshot > resources() noexcept
constexpr data::DataTag BankSoldSignTag
data::DataId bankDeedSequence(int square) noexcept
constexpr data::DataTag BankLiabilityDividendCardTag
constexpr data::DataTag BankHotelTag
std::optional< Rect > bankDeedRect(int square) noexcept
constexpr int BankContentX
constexpr int BankDeedWidth
constexpr int BankDeedHeight
constexpr data::DataTag BankDeedBaseTag
constexpr std::uint16_t BankOverlayPriority
constexpr data::DataTag BankHouseTag
constexpr int BankContentY
constexpr data::DataTag BankPlayerBarBaseTag
constexpr data::DataTag BankLiabilityErrorCardTag
constexpr data::DataTag BankMortgageSignTag
constexpr std::uint16_t BankIconPriority
constexpr float BankLiabilityCardScale
constexpr std::uint16_t BankSignPriority
std::size_t size()