Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
IBar.cpp
Go to the documentation of this file.
1#include "IBar.hpp"
2
4#include "CardTypes.hpp"
5
6#include "Display.hpp"
7#include "LocalPlayers.hpp"
8#include "Messaging.hpp"
9#include "PlayerSelection.hpp"
10#include "RuntimeState.hpp"
11#include "Timers.hpp"
12#include "UserInterface.hpp"
13#include "UISound.hpp"
14
15#include <SDL3/SDL_scancode.h>
16
17#include <algorithm>
18#include <cstddef>
19
20namespace monopoly::ibar
21{
22 namespace
23 {
24 State globalState;
25 inline constexpr std::uint64_t EscapeMenuTimeoutTicks =
26 static_cast<std::uint64_t>(timers::BasicClockRateHz) * 10u;
27
28 void openEscapeMenu(bool newGame, bool exit) noexcept
29 {
30 globalState.userRequestedNewGame = newGame;
31 globalState.userRequestedExit = exit;
32 globalState.escapeMenuUp = true;
33 globalState.escapeMenuOpenedTick = timers::tickCount();
34 }
35
36 void closeEscapeMenu(bool restoreOptions, bool playClick) noexcept
37 {
38 if (playClick) engine::playClickSound();
39 globalState.escapeMenuUp = false;
40 globalState.userRequestedExit = false;
41 globalState.userRequestedNewGame = false;
42 if (restoreOptions && !globalState.userChoseToExit &&
44 {
46 }
47 }
48
50 {
56 }
57
59 {
60 globalState.userChoseToExit = true;
61 auto& options = userinterface::optionsState();
62 options.currentScreen = optionsui::Screen::Credits;
63 options.active = true;
65 }
66
68 {
70 if (globalState.userRequestedNewGame)
71 {
73 }
74 else if ((display::isIBarVisible(display::stateReadOnly().desired2DView) ||
75 runtime::state().gameInProgress) &&
76 !globalState.userRequestedExit)
77 {
79 }
80 else
81 {
83 }
84 // Retail routes Yes through the same hitN teardown, including its
85 // second Click SFX and request-flag reset.
86 closeEscapeMenu(true, true);
87 }
88
90 {
91 globalState.userChoseToExit = false;
94 runtime::state().gameQuitRequested = true;
95 }
96
97 [[nodiscard]] bool processEscapeInput(const uimsg::Message& message) noexcept
98 {
99 if (globalState.userChoseToExit &&
100 (message.type == uimsg::Type::MouseLeftDown ||
101 message.type == uimsg::Type::KeyboardPressed))
102 {
104 return true;
105 }
106
107 if (message.type == uimsg::Type::KeyboardPressed)
108 {
109 const auto key = static_cast<SDL_Scancode>(message.numberA);
111 {
112 globalState.escapeMenuUp = !globalState.escapeMenuUp;
113 if (globalState.escapeMenuUp)
114 globalState.escapeMenuOpenedTick = timers::tickCount();
115 return true;
116 }
117 if (globalState.escapeMenuUp && key == SDL_SCANCODE_Y)
118 {
120 return true;
121 }
122 if (globalState.escapeMenuUp && key == SDL_SCANCODE_N)
123 {
124 closeEscapeMenu(true, true);
125 return true;
126 }
127 }
128
129 if (globalState.escapeMenuUp && message.type == uimsg::Type::MouseLeftDown)
130 {
131 const int x = static_cast<int>(message.numberA);
132 const int y = static_cast<int>(message.numberB);
133 if (x >= 299 && x <= 381 && y >= 183 && y <= 219)
134 {
136 return true;
137 }
138 if (x >= 419 && x <= 501 && y >= 183 && y <= 219)
139 {
140 closeEscapeMenu(true, true);
141 return true;
142 }
143 }
144 return false;
145 }
146
147
148 [[nodiscard]] bool clearsBuyAuctionPopup(actions::Type action) noexcept
149 {
150 switch (action)
151 {
193 return true;
194 default:
195 return false;
196 }
197 }
198
199
200 bool playerSelectVisible()
201 {
202 const auto view =
204 .desired2DView;
205
206
207 return
208 view ==
209 display::Screen2D::
210 PlayerSelect ||
211 view ==
214 }
215
216
217 bool playerBarVisible()
218 {
219 const auto view =
221 .desired2DView;
222
223
224 // DISPLAY_IsIBarVisible + les deux ecrans de setup.
225 return
226 playerSelectVisible() ||
228 view == display::Screen2D::Main ||
230 }
231
232
233 int playerHit(
234 int x,
235 int y)
236 {
237 for (
238 int player = 0;
239 player <
240 static_cast<int>(
242 );
243 ++player)
244 {
245 const PlayerDisplay& slot =
247 static_cast<std::size_t>(
248 player
249 )
250 ];
251
252
253 if (
254 slot.visible &&
255 slot.rect.contains(
256 x,
257 y
258 ))
259 {
260 return player;
261 }
262 }
263
264
265 return -1;
266 }
267
268
269 int playerOrBankHit(int x, int y)
270 {
271 const int player = playerHit(x, y);
272 if (player >= 0) return player;
273 if (layout::bankHitRect().contains(x, y))
274 return static_cast<int>(rules::BankPlayer);
275 return -1;
276 }
277
278
279 std::optional<layout::ActionButtonSlot> actionHit(
280 int x,
281 int y) noexcept
282 {
284 x,
285 y,
286 globalState.actionButtonLayout,
287 globalState.activeActionButtonSlots);
288 if (ruleButton)
289 return ruleButton;
290
292 {
295 globalState.actionButtonLayout).contains(x, y))
296 {
298 }
299
300 // IBAR_Status is a global view button, independent of the
301 // projected RULE action mask. Its art changes between Status
302 // and Main, but the physical hit rectangle is shared.
303 if (runtime::state().gameInProgress &&
306 globalState.actionButtonLayout).contains(x, y))
307 {
308 const auto view = display::stateReadOnly().desired2DView;
309 if (view == display::Screen2D::Main ||
312 {
314 }
315 }
316 }
317 return std::nullopt;
318 }
319
320
321 bool sendRuleAction(
322 actions::Type action,
323 std::int64_t numberA = 0,
324 std::int64_t numberB = 0,
325 std::int64_t numberC = 0,
326 std::int64_t numberD = 0)
327 {
328 if (globalState.actionRemote ||
329 globalState.actionPlayer >= rules::MaxPlayers)
330 {
331 return false;
332 }
333
335 action,
336 globalState.actionPlayer,
338 numberA,
339 numberB,
340 numberC,
341 numberD);
342 }
343
346 const uimsg::Message& message) noexcept
347 {
350 {
351 const auto view = display::stateReadOnly().desired2DView;
352 if (view == display::Screen2D::Main)
353 {
355 globalState.pendingPressedButton = StatusButtonIndex;
356 return true;
357 }
358 if (view == display::Screen2D::Portfolio ||
360 {
362 globalState.pendingPressedButton = MainButtonIndex;
363 return true;
364 }
365 return false;
366 }
367
369 {
371 return false;
372 globalState.pendingPressedButton = OptionsButtonIndex;
373 return true;
374 }
375
377 {
378 auto tradePlayer = resolveRulePlayer(globalState.projectedRulePlayer);
379 const auto validTradePlayer = [&](rules::PlayerNumber player) noexcept {
380 return player < rules::MaxPlayers &&
381 player < uiState.numberOfPlayers;
382 };
384 tradePlayer = globalState.projectedRulePlayer;
386 tradePlayer = uiState.currentPlayer;
389 {
390 return false;
391 }
392 globalState.pendingPressedButton = TradeButtonIndex;
393 return true;
394 }
395
397 return false;
398
399 auto cameraPlayer = resolveRulePlayer(globalState.projectedRulePlayer);
400 const auto validPlayer = [&](rules::PlayerNumber player) noexcept {
401 return player < rules::MaxPlayers &&
402 player < uiState.numberOfPlayers;
403 };
405 cameraPlayer = globalState.projectedRulePlayer;
407 cameraPlayer = uiState.currentPlayer;
408
409 const std::int32_t currentSquare = validPlayer(cameraPlayer)
410 ? static_cast<std::int32_t>(uiState.players[cameraPlayer].currentSquare)
411 : 0;
412 const bool sequential = display::stateReadOnly().mouseRightPressed ||
413 (message.numberE & uimsg::MouseModifierControl) != 0;
414
415 display::cycleIBarCamera(currentSquare, sequential);
416 globalState.pendingPressedButton = CameraButtonIndex;
417 return true;
418 }
419
420
422 {
423 globalState.localRuleModeActive = false;
424 globalState.localRuleMode = RuleMode::Nothing;
425 globalState.localRulePlayer = rules::NobodyPlayer;
426 globalState.selectedDeed.reset();
427 }
428
429
431 {
432 if (player == rules::BankPlayer ||
433 (player < rules::MaxPlayers &&
435 {
437 }
439 }
440
441
443 RuleMode mode,
445 {
446 if (player <= rules::BankPlayer)
447 {
448 globalState.localRulePlayer = player;
449 }
450 else if (!globalState.localRuleModeActive)
451 {
452 globalState.localRulePlayer = globalState.actionPlayer;
453 }
454
455 globalState.localRuleModeActive = true;
456 globalState.localRuleMode = mode;
457 if (mode != RuleMode::DeedActive)
458 globalState.selectedDeed.reset();
459 }
460
461
463 {
464 if (globalState.localRulePlayer == globalState.projectedRulePlayer)
465 {
467 return;
468 }
469
470 globalState.localRuleModeActive = true;
471 globalState.localRuleMode = otherPlayerMode(globalState.localRulePlayer);
472 globalState.selectedDeed.reset();
473 }
474
475
476 bool handlePlayerOrBankClick(int hit) noexcept
477 {
478 if (hit < 0 || hit > static_cast<int>(rules::BankPlayer))
479 return false;
480
481 const auto selected = static_cast<rules::PlayerNumber>(hit);
482 if (selected == globalState.projectedRulePlayer)
483 {
485 return true;
486 }
487
488 enterLocalRuleMode(otherPlayerMode(selected), selected);
489 return true;
490 }
491
492
495 {
496 using Slot = layout::ActionButtonSlot;
497 switch (globalState.actionRuleMode)
498 {
500 if (slot == Slot::Main)
502 break;
503
505 if (slot == Slot::Main)
507 break;
508
510 if (slot == Slot::Main)
512 break;
513
515 if (slot == Slot::Main)
517 break;
518
520 if (slot == Slot::Main)
522 if (slot == Slot::General3)
524 break;
525
527 if (slot == Slot::Main)
529 if (slot == Slot::General3)
531 break;
532
537 if (slot == Slot::Main)
539 if (slot == Slot::General2)
541 if (slot == Slot::General3)
543 break;
544
546 if (slot == Slot::Main)
548 if (slot == Slot::General2)
550 if (slot == Slot::General3)
552 break;
553
555 if (slot == Slot::Main)
557 break;
558
561 if (slot == Slot::Main)
563 break;
564
565 default:
566 break;
567 }
568
569 return false;
570 }
571
572
574 {
575 using Slot = layout::ActionButtonSlot;
576 const auto enterBssm = [&](RuleMode mode, std::uint8_t buttonIndex)
577 {
578 globalState.pendingPressedButton = buttonIndex;
579 enterLocalRuleMode(mode);
581 switch (mode)
582 {
586 default: break;
587 }
590 return true;
591 };
592
593 switch (globalState.actionRuleMode)
594 {
598 if (slot == Slot::General1) return enterBssm(RuleMode::Build, BuildButtonIndex);
599 if (slot == Slot::General2) return enterBssm(RuleMode::Sell, SellButtonIndex);
600 if (slot == Slot::General3) return enterBssm(RuleMode::Mortgage, MortgageButtonIndex);
601 if (slot == Slot::General4) return enterBssm(RuleMode::UnMortgage, UnmortButtonIndex);
602 break;
603
605 if (slot == Slot::General1) return enterBssm(RuleMode::Build, BuildButtonIndex);
606 if (slot == Slot::General2) return enterBssm(RuleMode::Sell, SellButtonIndex);
607 if (slot == Slot::General3) return enterBssm(RuleMode::Mortgage, MortgageButtonIndex);
608 if (slot == Slot::General4) return enterBssm(RuleMode::UnMortgage, UnmortButtonIndex);
609 if (slot == Slot::Main)
610 {
611 globalState.pendingPressedButton = DoneButtonIndex;
613 return true;
614 }
615 break;
616
618 if (slot == Slot::Main)
619 {
620 globalState.pendingPressedButton = DoneButtonIndex;
622 return true;
623 }
624 break;
625
627 if (slot == Slot::General2) return enterBssm(RuleMode::Sell, SellButtonIndex);
628 if (slot == Slot::General3) return enterBssm(RuleMode::Mortgage, MortgageButtonIndex);
629 break;
630
632 if (slot == Slot::General2)
633 {
639 {
640 return false;
641 }
642 globalState.pendingPressedButton = NewGameButtonIndex;
643 return true;
644 }
645 if (slot == Slot::Main)
646 {
647 // Retail sets g_UserRequestedExit then feeds an Escape key
648 // back through UDIBAR_ProcessMessage.
649 globalState.pendingPressedButton = ExitButtonIndex;
650 openEscapeMenu(false, true);
651 return true;
652 }
653 break;
654
655 case RuleMode::Build:
656 case RuleMode::Sell:
659 if (slot == Slot::Main)
660 {
661 globalState.pendingPressedButton = DoneButtonIndex;
663 return true;
664 }
665 break;
666
668 if (slot == Slot::Main)
669 {
670 globalState.pendingPressedButton = DoneButtonIndex;
672 return true;
673 }
674 if (!globalState.selectedDeed) break;
675 if (slot == Slot::General1)
677 *globalState.selectedDeed, 0, 0, 1);
678 if (slot == Slot::General2)
680 *globalState.selectedDeed, 0, 0, 1);
681 if (slot == Slot::General3 || slot == Slot::General4)
683 *globalState.selectedDeed, 0, 0, 1);
684 break;
685
686 default:
687 break;
688 }
689
690 return false;
691 }
692
693
694 bool handlePropertyClick(int square)
695 {
696 switch (globalState.actionRuleMode)
697 {
707 if (square >= 0 && square < static_cast<int>(rules::SquareCount) &&
709 static_cast<std::size_t>(square)].owner == globalState.actionPlayer)
710 {
711 globalState.selectedDeed = static_cast<std::uint8_t>(square);
713 return true;
714 }
715 break;
716
719
722 return sendRuleAction(actions::Type::Mortgaging, square, 0, 0, 1);
723
724 case RuleMode::Build:
725 return sendRuleAction(actions::Type::BuyHouse, square, 0, 0, 1);
726
727 case RuleMode::Sell:
729 return sendRuleAction(actions::Type::SellBuildings, square, 0, 0, 1);
730
734
735 default:
736 break;
737 }
738
739 return false;
740 }
741 }
742
743
744
746 {
747 // DISPLAY_UDIBAR_Initialize().
748
749 globalState = {};
750
751 globalState.playerLastMouseOver =
752 -1;
753
754 globalState.playerCurrentMouseOver =
755 -1;
756
757 globalState.initialized =
758 true;
759
760
761 return true;
762 }
763
764
765 void shutdown()
766 {
767 // DISPLAY_UDIBAR_Destroy().
768
769 globalState = {};
770 }
771
772
774 std::uint64_t numberOfTicks)
775 {
776 (void)numberOfTicks;
777 if (globalState.escapeMenuUp &&
778 globalState.escapeMenuOpenedTick + EscapeMenuTimeoutTicks < timers::tickCount())
779 {
780 closeEscapeMenu(true, true);
781 }
782 }
783
784
786 {
787 if (!globalState.initialized || globalState.userChoseToExit) return false;
788 openEscapeMenu(true, false);
789 return true;
790 }
791
793 {
794 if (!globalState.initialized || globalState.userChoseToExit) return false;
795 openEscapeMenu(false, true);
796 return true;
797 }
798
799
800 void show()
801 {
802 if (!globalState.initialized)
803 {
804 return;
805 }
806
807
810
811
814
815
816 const int numberOfPlayers =
817 std::clamp(
818 static_cast<int>(
819 uiState.numberOfPlayers
820 ),
821 0,
822 static_cast<int>(
824 )
825 );
826
827
828 const bool showPlayerBar =
830
831
832 for (
833 int player = 0;
834 player <
835 static_cast<int>(
837 );
838 ++player)
839 {
840 PlayerDisplay& result =
841 globalState.players[
842 static_cast<std::size_t>(
843 player
844 )
845 ];
846
847
848 result = {};
849
850
851 if (
852 !showPlayerBar ||
853 player >= numberOfPlayers)
854 {
855 continue;
856 }
857
858
859 const auto playerNo =
860 static_cast<
862 >(player);
863
864
865 result.local =
869 );
870
871
872 result.localHuman =
876 );
877
878
879 result.localAI =
883 );
884
885
886 // ------------------------------------------------
887 // DISPLAY_UDIBAR_Show() original :
888 //
889 // if ShowOnlyLocalPlayers && !local -> invisible
890 //
891 // else if ShowOnlyLocalAIPlayers && !localAI
892 // -> invisible
893 //
894 // else visible.
895 // ------------------------------------------------
896
897 if (
899 .showOnlyLocalPlayersOnIBar &&
900 !result.local)
901 {
902 continue;
903 }
904
905
906 if (
908 .showOnlyLocalAIPlayersOnIBar &&
909 !result.localAI)
910 {
911 continue;
912 }
913
914
915 result.visible = true;
916
917
918 result.rect =
920 player,
921 numberOfPlayers
922 );
923 }
924
925
926 // Si le joueur sous la souris vient d'être masqué,
927 // retirer le hover.
928 if (globalState.playerCurrentMouseOver ==
929 static_cast<int>(rules::BankPlayer) &&
930 !display::isIBarVisible(displayState.desired2DView))
931 {
932 globalState.playerCurrentMouseOver = -1;
933 }
934 else if (
935 globalState.playerCurrentMouseOver >= 0 &&
936 globalState.playerCurrentMouseOver != static_cast<int>(rules::BankPlayer))
937 {
938 const auto index =
939 static_cast<std::size_t>(
941 .playerCurrentMouseOver
942 );
943
944
945 if (
946 index >=
947 globalState.players.size() ||
948 !globalState.players[
949 index
950 ].visible)
951 {
953 .playerCurrentMouseOver =
954 -1;
955 }
956 }
957 }
958
959
961 const uimsg::Message& message)
962 {
963 if (!globalState.initialized)
964 {
965 return;
966 }
967
968 // Escape confirmation is global in the retail IBar and must run even
969 // while Options or player setup hides the ordinary bar.
970 if (processEscapeInput(message)) return;
971
972 if (playerSelectVisible())
973 {
974 if (message.type == uimsg::Type::MouseMoved)
975 {
976 globalState.playerLastMouseOver =
977 globalState.playerCurrentMouseOver;
978 globalState.playerCurrentMouseOver = playerHit(
979 static_cast<int>(message.numberA),
980 static_cast<int>(message.numberB));
981 return;
982 }
983
984 if (message.type != uimsg::Type::MouseLeftDown ||
985 message.numberB < 413)
986 {
987 return;
988 }
989
990 const int player = playerHit(
991 static_cast<int>(message.numberA),
992 static_cast<int>(message.numberB));
993 if (player < 0)
994 {
995 return;
996 }
997
999 static_cast<rules::PlayerNumber>(player));
1000 return;
1001 }
1002
1003 if (!playerBarVisible())
1004 {
1005 return;
1006 }
1007
1008 if (message.type == uimsg::Type::MouseMoved)
1009 {
1010 globalState.playerLastMouseOver =
1011 globalState.playerCurrentMouseOver;
1012 globalState.playerCurrentMouseOver = playerOrBankHit(
1013 static_cast<int>(message.numberA),
1014 static_cast<int>(message.numberB));
1015
1016 globalState.actionButtonLastMouseOver =
1017 globalState.actionButtonCurrentMouseOver;
1018 const auto action = actionHit(
1019 static_cast<int>(message.numberA),
1020 static_cast<int>(message.numberB));
1021 globalState.actionButtonCurrentMouseOver = action
1022 ? static_cast<int>(*action)
1023 : -1;
1024
1025 globalState.propertyLastMouseOver =
1026 globalState.propertyCurrentMouseOver;
1027 const auto property = layout::propertyHit(
1028 static_cast<int>(message.numberA),
1029 static_cast<int>(message.numberB),
1030 globalState.visiblePropertySlots);
1031 globalState.propertyCurrentMouseOver = property ? *property : -1;
1032 return;
1033 }
1034
1035 const bool mainShortcut = message.type == uimsg::Type::KeyboardPressed &&
1036 message.numberA == SDL_SCANCODE_SPACE;
1037 if (message.type != uimsg::Type::MouseLeftDown && !mainShortcut)
1038 {
1039 return;
1040 }
1041
1042 // UDIBar.cpp maps Space to the Main rectangle for the current layout.
1043 // Use the same hit test so disabled and remote actions keep their guards.
1045 layout::ActionButtonSlot::Main, globalState.actionButtonLayout);
1046 const int hitX = mainShortcut ? mainRect.left : static_cast<int>(message.numberA);
1047 const int hitY = mainShortcut ? mainRect.top : static_cast<int>(message.numberB);
1048 const auto action = actionHit(hitX, hitY);
1049 if (action)
1050 {
1051 globalState.actionButtonCurrentMouseOver =
1052 static_cast<int>(*action);
1053 if (handleGlobalAction(*action, message)) return;
1054 if (handleLocalRuleAction(*action)) return;
1056 return;
1057 }
1058
1059 const auto property = layout::propertyHit(
1060 hitX, hitY, globalState.visiblePropertySlots);
1061 if (property)
1062 {
1063 globalState.propertyCurrentMouseOver = *property;
1064 (void)handlePropertyClick(*property);
1065 return;
1066 }
1067
1068 const int playerOrBank = playerOrBankHit(hitX, hitY);
1069 if (playerOrBank >= 0)
1071 }
1072
1073
1075 const actions::Message& message,
1076 RuleMode projectedMode,
1077 std::uint64_t tick) noexcept
1078 {
1079 if (clearsBuyAuctionPopup(message.action))
1080 globalState.desiredBuyAuctionSquare.reset();
1081
1082 if (message.action == actions::Type::NotifyDecomposeSale)
1083 {
1084 globalState.cashAnimationAmount.reset();
1085 globalState.decompositionHousesToSell = -message.numberB;
1086 globalState.buttonBarForceUpdate = true;
1087 }
1088
1089 if (message.action == actions::Type::NotifyCashAnimation)
1090 {
1091 if (projectedMode != RuleMode::HotelDecomposition &&
1092 projectedMode != RuleMode::RaiseMoney)
1093 {
1094 globalState.decompositionHousesToSell.reset();
1095 globalState.cashAnimationAmount = message.numberC;
1096 globalState.cashAnimationTick = tick;
1097 ++globalState.cashAnimationSerial;
1098 globalState.buttonBarForceUpdate = true;
1099 }
1100 return;
1101 }
1102
1103 if (message.action == actions::Type::NotifyBuyOrAuctionDecision)
1104 {
1105 const int square = static_cast<int>(message.numberB);
1106 if (square >= 0 && square < static_cast<int>(rules::SquareCount) &&
1107 layout::propertyIndex(square) >= 0)
1108 {
1109 globalState.desiredBuyAuctionSquare =
1110 static_cast<std::uint8_t>(square);
1111 }
1112 return;
1113 }
1114
1115 if (message.action == actions::Type::NotifyPickedUpCard)
1116 {
1117 const auto deck = message.numberB;
1118 const auto card = message.numberC;
1119 if (deck == static_cast<std::int64_t>(rules::DeckType::Chance) &&
1121 {
1122 globalState.desiredCardIndex = static_cast<std::uint8_t>(
1124 }
1125 else if (deck == static_cast<std::int64_t>(rules::DeckType::Community) &&
1127 {
1128 globalState.desiredCardIndex = static_cast<std::uint8_t>(
1130 }
1131 return;
1132 }
1133
1134 if (message.action == actions::Type::NotifyPutAwayCard)
1135 {
1136 globalState.desiredCardIndex.reset();
1137 return;
1138 }
1139
1140 if (message.action != actions::Type::NotifyActionCompleted ||
1141 message.numberB == 0 ||
1142 message.numberA < 0 ||
1143 message.numberA > static_cast<std::int64_t>(
1145 {
1146 return;
1147 }
1148
1149 const auto action = static_cast<actions::Type>(message.numberA);
1150 if (action == actions::Type::CardSeen)
1151 globalState.desiredCardIndex.reset();
1152
1153 std::optional<std::uint8_t> pressed;
1154 switch (action)
1155 {
1160 break;
1163 break;
1165 if (message.numberD == 0) pressed = RollDiceButtonIndex;
1166 else if (message.numberD == 1) pressed = PayButtonIndex;
1167 else if (message.numberD == 2) pressed = UseCardButtonIndex;
1168 break;
1171 break;
1173 pressed = message.numberD != 0 ? BuyButtonIndex : AuctionButtonIndex;
1174 break;
1176 if (message.numberD == 0) pressed = FlatTaxButtonIndex;
1177 else if (message.numberD == 1) pressed = PercentageButtonIndex;
1178 break;
1180 if (projectedMode == RuleMode::HousingShort)
1182 else if (projectedMode == RuleMode::HotelShort)
1184 break;
1185 default:
1186 break;
1187 }
1188
1189 if (pressed)
1190 globalState.pendingPressedButton = *pressed;
1191 }
1192
1193
1194 void clearPendingPressedButton(std::uint8_t buttonIndex) noexcept
1195 {
1196 if (globalState.pendingPressedButton == buttonIndex)
1197 globalState.pendingPressedButton.reset();
1198 }
1199
1204
1206 {
1207 if (player > rules::BankPlayer)
1208 {
1210 return;
1211 }
1212 enterLocalRuleMode(otherPlayerMode(player), player);
1213 }
1214
1216 RuleMode projectedMode,
1218 {
1219 globalState.projectedRuleMode = projectedMode;
1220 globalState.projectedRulePlayer = projectedPlayer;
1221
1222 return globalState.localRuleModeActive
1223 ? globalState.localRuleMode
1224 : projectedMode;
1225 }
1226
1227
1230 {
1231 return globalState.localRuleModeActive &&
1232 globalState.localRulePlayer <= rules::BankPlayer
1233 ? globalState.localRulePlayer
1235 }
1236
1237
1240 layout::ActionButtonMask activeSlots,
1241 RuleMode mode,
1242 rules::PlayerNumber player,
1243 bool remote) noexcept
1244 {
1245 if (globalState.actionButtonLayout != buttonLayout ||
1246 globalState.activeActionButtonSlots != activeSlots)
1247 {
1248 globalState.actionButtonLastMouseOver =
1249 globalState.actionButtonCurrentMouseOver;
1250 globalState.actionButtonCurrentMouseOver = -1;
1251 }
1252
1253 globalState.actionButtonLayout = buttonLayout;
1254 globalState.activeActionButtonSlots = activeSlots;
1255 globalState.actionRuleMode = mode;
1256 globalState.actionPlayer = player;
1257 globalState.actionRemote = remote;
1258 }
1259
1260
1262 layout::PropertyMask visibleProperties) noexcept
1263 {
1264 globalState.visiblePropertySlots = visibleProperties;
1265 if (globalState.propertyCurrentMouseOver >= 0 &&
1266 (layout::propertyBit(globalState.propertyCurrentMouseOver) &
1267 visibleProperties) == 0)
1268 {
1269 globalState.propertyLastMouseOver =
1270 globalState.propertyCurrentMouseOver;
1271 globalState.propertyCurrentMouseOver = -1;
1272 }
1273 }
1274
1275
1276 bool activateProperty(int square)
1277 {
1278 if (!globalState.initialized || square < 0 ||
1279 square >= static_cast<int>(rules::SquareCount))
1280 return false;
1281 globalState.propertyCurrentMouseOver = square;
1282 return handlePropertyClick(square);
1283 }
1284
1286 {
1287 return globalState;
1288 }
1289
1290
1292 {
1293 return globalState;
1294 }
1295}
PlayerNumber slot
std::uint64_t key
unsigned index
constexpr bool isIBarVisible(Screen2D screen) noexcept
Definition Display.hpp:49
void setBackdrop(Screen2D screen)
Definition Display.cpp:363
const State & stateReadOnly()
Definition Display.cpp:704
void cycleIBarCamera(std::int32_t currentSquare, bool sequential) noexcept
Definition Display.cpp:471
void playClickSound() noexcept
Definition Engine.cpp:1898
void playPennybagsVoice(udsound::PennybagsVoice line, udsound::TokenVoiceClipPolicy policy, bool watchAfterStart) noexcept
Definition Engine.cpp:1940
int propertyIndex(int square) noexcept
std::uint32_t PropertyMask
PropertyMask propertyBit(int square) noexcept
std::optional< int > propertyHit(int x, int y, PropertyMask visibleProperties) noexcept
std::optional< ActionButtonSlot > actionButtonHit(int x, int y, ActionButtonLayout layout, ActionButtonMask activeSlots) noexcept
Rect actionButtonRect(ActionButtonSlot slot, ActionButtonLayout layout) noexcept
unsigned int ActionButtonMask
constexpr Rect bankHitRect() noexcept
Rect playerSetupHitRect(int playerIndex, int numberOfPlayers) noexcept
constexpr std::uint8_t ExitButtonIndex
constexpr std::uint8_t MortgageButtonIndex
bool activateProperty(int square)
Definition IBar.cpp:1276
bool initialize()
Definition IBar.cpp:745
constexpr std::uint8_t DoneButtonIndex
constexpr std::uint8_t BuildButtonIndex
void processRuleMessage(const actions::Message &message, RuleMode projectedMode, std::uint64_t tick) noexcept
Definition IBar.cpp:1074
constexpr std::uint8_t AuctionButtonIndex
constexpr std::uint8_t PayButtonIndex
void clearPendingPressedButton(std::uint8_t buttonIndex) noexcept
Definition IBar.cpp:1194
void shutdown()
Definition IBar.cpp:765
void inspectPlayer(rules::PlayerNumber player) noexcept
Definition IBar.cpp:1205
bool requestNewGameConfirmation() noexcept
Definition IBar.cpp:785
void processLibraryMessage(const uimsg::Message &message)
Definition IBar.cpp:960
constexpr std::uint8_t TradeButtonIndex
constexpr std::uint8_t RollDiceButtonIndex
void setRuleActionHitState(layout::ActionButtonLayout buttonLayout, layout::ActionButtonMask activeSlots, RuleMode mode, rules::PlayerNumber player, bool remote) noexcept
Definition IBar.cpp:1238
constexpr std::uint8_t BankruptButtonIndex
constexpr std::uint8_t SellButtonIndex
constexpr std::uint8_t UseCardButtonIndex
constexpr std::uint8_t OptionsButtonIndex
constexpr std::uint8_t AuctionHotelButtonIndex
bool requestExitConfirmation() noexcept
Definition IBar.cpp:792
constexpr std::uint8_t AuctionHouseButtonIndex
void show()
Definition IBar.cpp:800
constexpr std::uint8_t BuyButtonIndex
constexpr std::uint8_t StatusButtonIndex
void restoreRuleTracking() noexcept
Definition IBar.cpp:1200
void tickActions(std::uint64_t numberOfTicks)
Definition IBar.cpp:773
constexpr std::uint8_t NewGameButtonIndex
constexpr std::uint8_t MainButtonIndex
State & state()
Definition IBar.cpp:1285
constexpr std::uint8_t CameraButtonIndex
RuleMode resolveRuleMode(RuleMode projectedMode, rules::PlayerNumber projectedPlayer) noexcept
Definition IBar.cpp:1215
rules::PlayerNumber resolveRulePlayer(rules::PlayerNumber projectedPlayer) noexcept
Definition IBar.cpp:1228
constexpr std::uint8_t FlatTaxButtonIndex
const State & stateReadOnly()
Definition IBar.cpp:1291
constexpr std::uint8_t PercentageButtonIndex
void setPropertyHitState(layout::PropertyMask visibleProperties) noexcept
Definition IBar.cpp:1261
constexpr std::uint8_t UnmortButtonIndex
bool sendAction(const actions::Message &message)
void reset(State &state) noexcept
void playerButtonClicked(rules::PlayerNumber player)
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
constexpr std::uint8_t CommunityCount
Definition CardTypes.hpp:52
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr std::uint8_t CommunityFirst
Definition CardTypes.hpp:51
constexpr std::size_t SquareCount
Definition RuleTypes.hpp:24
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
constexpr PlayerNumber NobodyPlayer
Definition RuleTypes.hpp:16
constexpr std::uint8_t ChanceFirst
Definition CardTypes.hpp:48
constexpr std::uint8_t ChanceCount
Definition CardTypes.hpp:49
std::uint64_t tickCount()
Definition Timers.cpp:183
constexpr std::uint32_t BasicClockRateHz
Definition Timers.hpp:9
PlayerNumber anyLocalPlayer(const GameState &uiState)
bool slotIsLocalAIPlayer(PlayerNumber slot)
bool slotIsLocalHumanPlayer(PlayerNumber slot)
bool slotIsLocalPlayer(PlayerNumber slot)
constexpr std::int64_t MouseModifierControl
const rules::GameState & ruleStateReadOnly()
const optionsui::State & optionsStateReadOnly() noexcept
bool beginTradeFromIBar(rules::PlayerNumber iBarPlayer) noexcept
bool beginOptionsFromIBar() noexcept
optionsui::State & optionsState() noexcept
std::array< PlayerDisplay, rules::MaxPlayers > players
Definition IBar.hpp:34
bool contains(int x, int y) const noexcept