1225 for (
const auto& definition :
1231 if (definition.staticIdleReplacement &&
1243 playback = std::make_unique<SequencePlayback>(
1244 std::move(resources),
1249 const auto rasterText = [](std::string_view
text,
int size,
bool bold =
true,
bool italic =
false)
1250 -> std::expected<data::LegacyBitmapRGBA8, std::string>
1253 if(!
font || !
font->ready())
return std::unexpected(
"HUD font unavailable");
1254 auto rendered=
font->renderPresentation(
text,0xFFFFFF,size,bold ? 700 : 400,italic);
1258 auto menuSkin = std::make_shared<menu::ModernMenuSkin>(
1261 menuSkin->configureTradeCashPresentation([]
1267 state.city == 0 &&
state.system == 13 &&
state.customBoardPath.empty();
1272 "assets/modern/presentation/menu_city.png", std::ios::binary | std::ios::ate);
1275 std::vector<std::byte>
bytes(
static_cast<std::size_t
>(
preview.tellg()));
1277 if (
preview.read(
reinterpret_cast<char*
>(
bytes.data()),
1278 static_cast<std::streamsize
>(
bytes.size())))
1280 {8U * 1024U * 1024U, 4096, 64U * 1024U * 1024U});
decoded)
1282 auto image = std::make_shared<data::LegacyBitmapRGBA8>();
1283 image->width = (*decoded)->width;
1284 image->height = (*decoded)->height;
1285 image->pixels = (*decoded)->rgba;
1289 auto tokenPreviews = std::make_shared<menu::ModernTokenPreview>(
1290 std::filesystem::path(
SDL_GetBasePath()) /
"assets/modern/presentation/tokens");
1296 auto skin = std::make_shared<ibar::ModernIBarSkin>(
1303 skin->configurePresentationContext([]
1309 state.city == 0 &&
state.system == 13 &&
state.customBoardPath.empty();
1320 const auto&
m =
raster.values;
1321 if (
m[0] != 1 ||
m[1] != 0 ||
m[2] != 0 ||
m[3] != 0 ||
1322 m[4] != 1 ||
m[5] != 0 ||
m[8] != 1)
return {};
1324 return std::array<float, 4>{
panelLeft+1.37665F-
m[6], -
m[7],
1329 -> std::optional<ibar::ModernIBarSkin::IdleCardPresentation>
1335 state.city != 0 ||
state.system != 13 || !
state.customBoardPath.empty() ||
1336 state.current2DView !=
state.desired2DView ||
1340 if (
m[0] != 1 ||
m[1] != 0 ||
m[2] != 0 ||
m[3] != 0 ||
1341 m[4] != 1 ||
m[5] != 0 ||
m[8] != 1)
return {};
1365 skin->configurePropertyDescriptors(
1366 [](
unsigned propertyIndex) -> std::optional<ibar::ModernIBarSkin::PropertyDescriptor>
1370 !snapshot->language() || !snapshot->language()->catalog)
return {};
1372 if (
state.city != 0 ||
state.system != 13 || !
state.customBoardPath.empty())
return {};
1373 for (
int square = 0; square < 40; ++square)
1386 static_cast<std::uint8_t
>(definition.
group), *
price};
1393 snapshot->language() && snapshot->language()->catalog)
1395 std::map<data::DataId, ibar::ModernIBarSkin::DeedDescriptor>
descriptors;
1396 for (
int square = 0; square < 40; ++square)
1399 if (
index < 0)
continue;
1404 if (!
name)
continue;
1405 for (
const bool front : {
true,
false})
1408 if (!
plan ||
plan->text.empty())
continue;
1410 for (
const auto& fill :
plan->fills)
1411 descriptor.
fills.push_back({fill.x, fill.y, fill.width, fill.height, fill.color});
1422 case 0x0CD2:
case 0x0CDA:
case 0x0CE1:
case 0x0CE9:
1435 std::map<data::DataId, ibar::ModernIBarSkin::DrawCardDescriptor>
drawCards;
1438#include "ModernDrawCardText.inc"
1449 -> std::expected<data::LegacyBitmapRGBA8, std::string>
1452 if (
state.city != 0 ||
state.system != 13 || !
state.customBoardPath.empty())
1453 return std::unexpected(
"USA card presentation context changed");
1458 [
rasterText](std::string_view
text,
int size,
bool bold,
bool italic)
1459 -> std::expected<data::LegacyBitmapRGBA8, std::string>
1462 if (
state.city != 0 ||
state.system != 13 || !
state.customBoardPath.empty())
1463 return std::unexpected(
"USA deed presentation context changed");
1467 playback->world2D().configureModernIBarSkin(std::move(
skin));
2233 messaging::pumpNetwork();
2234 if (messaging::consumeHostDisconnected())
2240 if (
auto help = optionsui::pollFullHelp(); !
help)
2241 std::cerr <<
"Full help: " <<
help.error() <<
'\n';
2250 voicechat::resetSession();
2256 messaging::queuedActionCount() < messaging::MessageQueueCapacity)
2258 const std::string networkError(messaging::networkError());
2260 std::cerr <<
"Voice network: " << networkError <<
'\n';
2269 std::cerr <<
"Voice chat capture stopped: "
2270 <<
pumped.error() <<
'\n';
2290 const auto tick = timers::tickCount();
2291 if (tick >
static_cast<std::uint64_t
>(std::numeric_limits<std::int32_t>::max()))
2292 return SDL_SetError(
"Sequence parent clock exceeds signed runtime range");
2313 std::cerr <<
"Opening video cleanup queued for retry: "
2320 message.
type = uimsg::Type::VideoJump;
2321 message.numberA =
notice.event.decisionFrame;
2322 message.numberB =
notice.event.jumpToFrame;
2323 message.numberC =
notice.event.alternativeTaken ? 1 : 0;
2324 (
void)uimsg::send(message);
2332 movieResources->context().language == data::LanguageId::EnglishUs &&
2337 movie.boundingBox &&
movie.options.drawSolid &&
movie.options.alphaLevel == 255 &&
2338 movie.worldTransform.values == sequence::identity2D().values)
2340 const auto&
box = *
movie.boundingBox;
2341 if (
box.left >= 3 &&
box.top >= 3 &&
box.right <= 797 &&
box.bottom <= 597 &&
2350 if (!
font || !
font->ready())
return std::unexpected(
"opening skip hint font unavailable");
2351 auto image =
font->renderPresentation(
text,0xD3EBF5,18,400,
false);
2352 if (!
image)
return std::unexpected(
image.error().detail);
2353 return std::move(*
image);
2356 std::cerr <<
"Opening movie decoration unavailable: " <<
movieDecoration.error() <<
'\n';
2359 const bool boardVisible =
2363 const auto& ruleState = userinterface::ruleStateReadOnly();
2366 if (boardVisible ||
displayState.desired2DView == display::Screen2D::Auction)
2378 playerselection::visualPhaseStarted(*started);
2383 userinterface::auctionStateReadOnly(), ruleState,
2388 userinterface::auctionStateReadOnly(), ruleState,
2395 userinterface::auctionState(), ruleState,
2398 -> std::expected<void, std::string>
2400 return userinterface::sendReadyResponses(
2401 playerMask, serial);
2409 std::expected<void, std::string>
played{};
2410 if (sound.specificOffset)
2415 const auto* choice = udsound::pennybagsChoice(
2416 output->boardEdition(), sound.voice);
2417 if (choice ==
nullptr || *sound.specificOffset >= choice->alternateCount)
2418 return SDL_SetError(
"Auction Pennybags specific offset is invalid");
2419 const auto wave = data::packDataId(data::LegacyGroupId::LanguageDialog,
2420 static_cast<data::DataTag>(choice->firstTag + *sound.specificOffset));
2422 udsound::TokenVoiceClipPolicy::ClipOldSoundIfPlaying,
false);
2428 udsound::TokenVoiceClipPolicy::ClipOldSoundIfPlaying,
false);
2435 userinterface::tradeStateReadOnly(), ruleState,
2441 userinterface::tradeStateReadOnly(), ruleState,
2447 userinterface::tradeStateReadOnly(),
2450 return SDL_SetError(
"Trade action-button playback: %s",
2453 userinterface::optionsStateReadOnly(),
2456 return SDL_SetError(
"Options File-screen playback: %s",
2459 userinterface::optionsSaveStateReadOnly(),
2465 userinterface::optionsCustomBoardState(),
2468 return SDL_SetError(
"Options custom-board playback: %s",
2473 return SDL_SetError(
"Escape confirmation playback: %s",
2476 chat::stateReadOnly(), ruleState, userinterface::chatSenderPlayer(),
2481 chat::stateReadOnly(), ruleState, *
session);
2486 chat::stateReadOnly(), *
session);
2491 chat::stateReadOnly(), *
session,
true);
2496 userinterface::statsStateReadOnly(),
2503 userinterface::statsCalculatorStateReadOnly(),
2510 userinterface::statsCalculatorStateReadOnly(),
2513 return SDL_SetError(
"UDStats calculator deed picker: %s",
2517 userinterface::optionsState(),
displayState.desired2DView,
2522 userinterface::optionsStateReadOnly(),
2528 userinterface::optionsStateReadOnly(),
2531 return SDL_SetError(
"Options Option-screen playback: %s",
2534 userinterface::optionsStateReadOnly(),
2540 userinterface::optionsStateReadOnly(),
2543 return SDL_SetError(
"Options Help-screen playback: %s",
2546 userinterface::tradeState(), ruleState,
2553 userinterface::tradeStateReadOnly(),
2559 userinterface::tradeStateReadOnly(), ruleState,
2566 userinterface::tradeStateReadOnly(), ruleState,
displayState.desired2DView,
2571 userinterface::tradeStateReadOnly(), ruleState,
2577 userinterface::tradeStateReadOnly(),
2583 userinterface::tradeState(),
2589 userinterface::tradeStateReadOnly(), ruleState,
2593 return SDL_SetError(
"Trade contract-text playback: %s",
2596 userinterface::tradeStateReadOnly(),
2599 return SDL_SetError(
"Trade contract-dialog playback: %s",
2601 auto&
dicePrompt = userinterface::dicePromptState();
2603 const auto&
iBarRules = userinterface::iBarRuleStateReadOnly();
2618 ui::localplayers::tradeSourcePlayer(
2631 statsui::setPropertyActionContext(
2647 return SDL_SetError(
"UDStats Player cash playback: %s",
2655 statsui::setFutureImmunityIcons(
2656 userinterface::statsFutureImmunityState(),
2659 userinterface::statsFutureImmunityStateReadOnly(),
2662 return SDL_SetError(
"UDStats Future/Immunity playback: %s",
2666 userinterface::statsFutureImmunityStateReadOnly(), ruleState,
2670 return SDL_SetError(
"UDStats Future/Immunity text playback: %s",
2679 userinterface::statsCalculatorStateReadOnly().picker == statsui::CalculatorPicker::Deed;
2685 return SDL_SetError(
"UDStats Deed floater playback: %s",
2692 return SDL_SetError(
"UDStats Deed floater text playback: %s",
2698 return SDL_SetError(
"UDStats Deed owner-bar playback: %s",
2705 return SDL_SetError(
"UDStats Deed value-text playback: %s",
2712 userinterface::statsCalculatorStateReadOnly(),
2717 statsResources->context().language == data::LanguageId::EnglishUs &&
2722 const auto& stats = userinterface::statsStateReadOnly();
2723 if (
displayState.desired2DView == display::Screen2D::Portfolio &&
2724 stats.screen == statsui::Screen::Bank && stats.activeSort == 3)
2728 userinterface::statsStateReadOnly(), ruleState,
2733 const auto&
iBarState = ibar::stateReadOnly();
2734 const auto selectedDeed =
iBarState.selectedDeed;
2736 ? ibar::layout::propertyBit(*selectedDeed)
2743 iBarInputs.trackRules = !ibar::stateReadOnly().localRuleModeActive;
2744 iBarInputs.gameInProgress = runtime::state().gameInProgress;
2768 static_cast<int>(rules::BankPlayer);
2780 scoreInputs.tick =
static_cast<std::uint32_t
>(tick);
2781 for (std::size_t player = 0; player <
iBarState.players.size(); ++player)
2790 (
displayState.desired2DView == display::Screen2D::Main ||
2791 displayState.desired2DView == display::Screen2D::Trade);
2803 ibar::setPropertyHitState(
iBarInputs.propertyTitles.visibleProperties);
2810 ibar::setRuleActionHitState(
2822 std::array<ibar::ScoreTextState, rules::MaxPlayers>
scoreTextStates{};
2825 const auto resources = startup::resources();
2828 resources ? resources->context().board : data::BoardEdition::Usa,
2831 resources->context().board == data::BoardEdition::Usa &&
2832 resources->context().language == data::LanguageId::EnglishUs &&
2840 displayState.desired2DView == display::Screen2D::Main ||
2841 displayState.desired2DView == display::Screen2D::Trade,
2843 resources ? resources->context().board : data::BoardEdition::Usa,
2845 resources->context().board == data::BoardEdition::Usa &&
2846 resources->context().language == data::LanguageId::EnglishUs &&
2856 const auto wave = penny::cardReadWave(
2863 udsound::TokenVoiceClipPolicy::WaitForAnyOldSoundThenPlay,
2878 ibar::clearPendingPressedButton(*consumed);
2889 boardVisible, tick);
2915 displayState.desiredBoardCamera,
static_cast<std::uint32_t
>(tick),
2926 const auto ownershipPlan = boarddisplay::planOwnershipHighlights(
2942 mouse::stateReadOnly(), *
session);
2946 std::cerr <<
"Mouse pointer DAT playback unavailable: "
2955 resources->context().board == data::BoardEdition::Usa &&
2956 resources->context().language == data::LanguageId::EnglishUs &&
2963 ? mouse::NativeCursorKind::Hidden
2964 : mouse::nativeCursorKind(
2967 return SDL_SetError(
"Mouse cursor synchronization failed: %s",
2976 static_cast<std::uint8_t
>(RenderSlot::World3D));
2987 const auto updated =
session->update(
static_cast<std::int32_t
>(tick));
2989 if (!updated)
return SDL_SetError(
"Sequence playback: %s", updated.error().c_str());
2990 std::vector<sequence::SequenceMeshRenderItem>
decorations;
2998 for (
const auto node :
session->world().order())
3000 const auto* board =
session->world().find(node);
3001 if (!board || board->contentsDataId != data::modernSceneDefinition(
3002 data::ModernSceneKind::ParisBoard).legacyMeshId ||
3004 board->asset->origin != data::MeshAssetOrigin::ModernGltf)
continue;
3008 return instance.node == node &&
3009 instance.rootSequencePriority == display::Board3DPriority;
3012 const auto geometry = board->renderData ? board->renderData : board->asset->renderData;
3028 static_cast<std::uint32_t
>(tick),
true,
3058 [&](
const auto& asset) { return asset == decoration.asset; }))
3068 std::cerr <<
"Startup CPU profile: phase=sequence_update elapsed_ms="
3070 <<
" scope=CPU elapsed only; no added GPU fence\n";
3088 std::cerr <<
"Modern static GPU scene: retained=" <<
retainedCount <<
'\n';
3105 if (
const auto* command =
session->commands().viewportState(
3106 static_cast<std::uint8_t
>(RenderSlot::World3D)))
3107 viewport = {command->viewport.
left, command->viewport.top,
3108 command->viewport.right, command->viewport.bottom};
3113 if (
const auto* command =
session->commands().cameraState(
3114 static_cast<std::uint8_t
>(RenderSlot::World3D)))
3118 *command,
session->runtime()))
3127 int pixelWidth=0,pixelHeight=0;
3141 resources->context().board == data::BoardEdition::Usa &&
3142 resources->context().language == data::LanguageId::EnglishUs &&
3144 displayState.desired2DView == display::Screen2D::Main &&
3153 static_cast<std::uint8_t
>(RenderSlot::World3D)))
3158 const auto configured =
session->world().configureView(viewport, camera);
3159 if (!configured)
return SDL_SetError(
"Invalid DISPLAY World3D camera/viewport");
3167 "assets/modern/lighting/studio_environment.mstudio");
3217 const auto paris =
static_cast<std::size_t
>(data::ModernSceneKind::ParisBoard);
3218 const auto usa =
static_cast<std::size_t
>(data::ModernSceneKind::UsaBoard);
3219 const auto procedural =
static_cast<std::size_t
>(data::ModernSceneKind::ProceduralUsaBoard);
3287 std::cerr <<
"Modern presentation GPU: shadows=enabled, MSAA="