Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
TradePartnerSelectionPlayback.cpp
Go to the documentation of this file.
2#include "FontRuntime.hpp"
4#include <algorithm>
5#include <iomanip>
6#include <cmath>
7#include <sstream>
8namespace monopoly::tradeui
9{
10 namespace
11 {
12 std::optional<data::LegacyBitmapRGBA8> thumbnailWell(const data::LegacyBitmapRGBA8& source)
13 {
14 if(source.width!=768 || source.height!=640 || source.pixels.size()!=std::size_t(768)*640*4)
15 return std::nullopt;
16 unsigned left=768,top=640,right=0,bottom=0;bool transparent=false,visible=false;
17 for(unsigned y=0;y<640;++y)for(unsigned x=0;x<768;++x)
18 {
19 const auto alpha=source.pixels[(std::size_t(y)*768+x)*4+3];
21 if(!alpha)continue;
22 visible=true;left=std::min(left,x);top=std::min(top,y);
23 right=std::max(right,x+1);bottom=std::max(bottom,y+1);
24 }
25 if(!visible || !transparent)return std::nullopt;
26 constexpr unsigned width=44*3,height=28*3;
27 data::LegacyBitmapRGBA8 result{width,height,std::vector<std::uint8_t>(width*height*4,0)};
28 const unsigned cropWidth=right-left,cropHeight=bottom-top;
29 const double scale=std::min(double(width)/cropWidth,double(height)/cropHeight);
30 const unsigned drawWidth=std::clamp(unsigned(std::lround(cropWidth*scale)),1u,width);
31 const unsigned drawHeight=std::clamp(unsigned(std::lround(cropHeight*scale)),1u,height);
32 const unsigned offsetX=(width-drawWidth)/2,offsetY=(height-drawHeight)/2;
33 for(unsigned y=0;y<drawHeight;++y)for(unsigned x=0;x<drawWidth;++x)
34 {
35 const unsigned sx=left+std::min(cropWidth-1,unsigned((std::uint64_t(x)*cropWidth)/drawWidth));
36 const unsigned sy=top+std::min(cropHeight-1,unsigned((std::uint64_t(y)*cropHeight)/drawHeight));
37 std::copy_n(source.pixels.data()+(std::size_t(sy)*768+sx)*4,4,
38 result.pixels.data()+(std::size_t(y+offsetY)*width+x+offsetX)*4);
39 }
40 return result;
41 }
42 }
44 {
45 tokenImages_=std::move(provider);key_.clear();
46 }
48 {
49 if(visible_)
50 if(const auto stopped=playback.stop(surface_,PlayerSelectPriority);!stopped)return stopped;
51 if(surface_!=data::EmptyDataId)(void)playback.runtimeBitmaps().remove(surface_);
52 surface_=data::EmptyDataId;visible_=false;key_.clear();resources_=nullptr;bitmaps_.clear();return {};
53 }
54 std::expected<void,std::string> PartnerSelectionPlayback::sync(const State& state,
57 {
58 const bool desired=view==display::Screen2D::Trade && state.playerSelectVisible;
59 if(!desired)
60 {
61 if(visible_)
62 { if(const auto stopped=playback.stop(surface_,PlayerSelectPriority);!stopped)return stopped;visible_=false; }
63 return {};
64 }
65 if(!font || !font->ready())return std::unexpected("Trade partner chooser font unavailable");
66 const auto resources=playback.resources();
67 if(!resources || !resources->language() || !resources->language()->catalog)
68 return std::unexpected("Trade partner chooser LANG unavailable");
69 const bool modern=modernPresentation && resources->context().board==data::BoardEdition::Usa &&
70 resources->context().language==data::LanguageId::EnglishUs;
71 if(resources_!=resources){bitmaps_.clear();key_.clear();resources_=resources;}
72 const auto title16=resources->language()->catalog->message(2015); // TMN_TRADE_SELECT
73 if(!title16)return std::unexpected(title16.error().detail);
74 const auto title=fonts::transcodeUtf8(std::u16string_view(**title16));
75 if(!title)return std::unexpected(title.error().detail);
76 struct Row { std::string label; rules::PlayerNumber player; unsigned token; Rect rect; };
77 std::vector<Row> rows;std::ostringstream signature;
78 signature<<modern<<'|'<<*title<<'|'<<font->settings().fontPath.generic_string();
79 for(rules::PlayerNumber player=0;player<game.numberOfPlayers && player<rules::MaxPlayers;++player)
80 {
81 const auto rect=playerTokenRect(state,game,player);if(!rect)continue;
82 if(game.players[player].token>10)return std::unexpected("Trade partner token outside retail catalog");
83 const auto name=fonts::transcodeUtf8(std::wstring_view(game.players[player].name));
84 if(!name)return std::unexpected(name.error().detail);
85 std::ostringstream label;label<<std::setw(6)<<unsigned(player+1)<<": "<<*name;
86 rows.push_back({label.str(),player,game.players[player].token,*rect});
87 signature<<'|'<<unsigned(player)<<':'<<game.players[player].token<<':'<<rect->top<<':'<<name->size()<<':'<<*name;
88 }
89 if(rows.empty())return std::unexpected("Trade partner chooser has no eligible players");
90 const auto nextKey=signature.str();
91 if(nextKey!=key_ || !surface_)
92 {
93 const auto load=[&](data::DataId id)->std::expected<std::shared_ptr<const data::BitmapRuntimeAsset>,std::string>
94 {
95 const auto metadata=resources->data().metadata(id);const auto bytes=resources->data().load(id);
96 if(!metadata)return std::unexpected(metadata.error().detail);
97 if(!bytes)return std::unexpected(bytes.error().detail);
98 const auto asset=bitmaps_.resolve(id,metadata->type,*bytes);
99 if(!asset)return std::unexpected(asset.error().detail);return *asset;
100 };
101 const auto paper=load(0x0003058F); // TAB_tnfi0 exact native chooser template
102 if(!paper)return std::unexpected(paper.error());
103 if((*paper)->image.width!=188 || (*paper)->image.height!=209)
104 return std::unexpected("Trade partner chooser template must be188x209");
105 const unsigned scale=modern?3:1;
106 data::LegacyBitmapRGBA8 image{188*scale,209*scale,std::vector<std::uint8_t>(188*209*scale*scale*4,255)};
107 for(unsigned y=0;y<image.height;++y)for(unsigned x=0;x<image.width;++x)
108 {
109 const auto dst=(std::size_t(y)*image.width+x)*4;
110 if(modern)
111 {
112 const bool rim=x<3 || y<3 || x+3>=image.width || y+3>=image.height;
113 image.pixels[dst]=rim?188:22;image.pixels[dst+1]=rim?157:60;image.pixels[dst+2]=rim?94:61;
114 }
115 else
116 { const auto src=(std::size_t(y)*188+x)*4;for(unsigned c=0;c<4;++c)image.pixels[dst+c]=(*paper)->image.pixels[src+c]; }
117 }
118 const auto text=[&](std::string_view value,int x,int y,bool center)->std::expected<void,std::string>
119 {
120 const auto raster=font->renderPresentation(value,modern?0xD3EBF5:0xDCDCDC,
121 int((modern?(center?9:10):8)*scale),400,false,false,false,modern);
122 if(!raster)return std::unexpected(raster.error().detail);
123 if(center)x=(int(image.width)-int(raster->width))/2;
125 if(!center && clipped.width>116*scale)
126 {
127 clipped.width=116*scale;clipped.pixels.resize(std::size_t(clipped.width)*clipped.height*4);
128 for(unsigned row=0;row<clipped.height;++row)
129 std::copy_n(raster->pixels.data()+std::size_t(row)*raster->width*4,clipped.width*4,
130 clipped.pixels.data()+std::size_t(row)*clipped.width*4);
131 }
133 if(!blit)return std::unexpected(blit.error());return {};
134 };
135 if(const auto rendered=text(*title,0,12*scale,true);!rendered)return rendered;
136 for(const auto& row:rows)
137 {
138 const int localTop=row.rect.top-PlayerSelectY;
139 if(const auto rendered=text(row.label,5*scale,(localTop+9)*scale,false);!rendered)return rendered;
140 const auto token=load(0x000201C0+row.token);if(!token)return std::unexpected(token.error());
141 const auto& source=(*token)->image;
142 if(!source.width || !source.height || source.width>128 || source.height>128)
143 return std::unexpected("Trade partner token dimensions invalid");
144 if(modern && tokenImages_)
145 {
146 std::shared_ptr<const data::LegacyBitmapRGBA8> thumbnail;
147 try { thumbnail=tokenImages_(static_cast<std::uint8_t>(row.token)); }
148 catch (...) { thumbnail.reset(); }
149 if(thumbnail)
150 if(const auto well=thumbnailWell(*thumbnail))
151 {
152 const auto blit=data::blitStraightRGBA8(image,*well,126*scale,localTop*scale,
154 if(!blit)return std::unexpected(blit.error());
155 continue;
156 }
157 }
159 std::vector<std::uint8_t>(source.pixels.size()*scale*scale)};
160 for(unsigned y=0;y<enlarged.height;++y)for(unsigned x=0;x<enlarged.width;++x)
161 for(unsigned c=0;c<4;++c)enlarged.pixels[(std::size_t(y)*enlarged.width+x)*4+c]=
162 source.pixels[(std::size_t(y/scale)*source.width+x/scale)*4+c];
163 const int x=126+(44-int(source.width))/2;
164 const int y=localTop+(28-int(source.height))/2;
166 if(!blit)return std::unexpected(blit.error());
167 }
168 bool created=false;
169 if(!surface_)
170 { const auto id=playback.runtimeBitmaps().create(188,209,true);if(!id)return std::unexpected(id.error());surface_=*id;created=true; }
171 const auto updated=playback.runtimeBitmaps().update(surface_,std::move(image),
172 modern?std::optional<std::array<float,4>>{{0,0,188,209}}:std::nullopt,modern);
173 if(!updated){if(created){(void)playback.runtimeBitmaps().remove(surface_);surface_=data::EmptyDataId;}return updated;}
174 key_=nextKey;
175 }
176 if(!visible_)
177 {
178 const auto started=playback.startMoved(surface_,PlayerSelectPriority,sequence::translate2D(PlayerSelectX,PlayerSelectY));
179 if(!started)return started;visible_=true;
180 }
181 return {};
182 }
183}
const char * name
std::string_view text
std::atomic< float > left
std::atomic< float > right
LegacyBitmapMetadata metadata
LegacyBitmapRGBA8 image
std::vector< std::byte > bytes
int bottom
int top
fonts::Runtime & font
data::DataId id
std::shared_ptr< const data::HmdTextureImage > source
std::uint32_t height
std::uint32_t width
unsigned state
unsigned index
std::string_view label
int token
Rect rect
std::string error
std::uint16_t value
Definition Timers.cpp:16
std::expected< std::shared_ptr< const BitmapRuntimeAsset >, BitmapError > resolve(DataId id, LegacyDataType sourceType, SharedDataBytes bytes)
std::expected< void, std::string > sync(const State &state, const rules::GameState &game, display::Screen2D view, fonts::Runtime *font, engine::SequencePlayback &playback, bool modernPresentation=false)
std::expected< void, std::string > reset(engine::SequencePlayback &playback)
std::function< std::shared_ptr< const data::LegacyBitmapRGBA8 >(std::uint8_t)> TokenImageProvider
std::uint32_t DataId
Definition DataBanks.hpp:10
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
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)
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
Matrix2D translate2D(std::int32_t x, std::int32_t y) noexcept
constexpr int PlayerSelectY
Definition TradeUI.hpp:21
constexpr std::uint16_t PlayerSelectPriority
Definition TradeUI.hpp:24
constexpr int PlayerSelectX
Definition TradeUI.hpp:20
std::optional< Rect > playerTokenRect(const State &state, const rules::GameState &gameState, rules::PlayerNumber player) noexcept
Definition TradeUI.cpp:728
std::array< PlayerState, MaxPlayers > players