Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
RuleTrade.cpp
Go to the documentation of this file.
1#include "RuleTrade.hpp"
2
3#include "BoardRules.hpp"
4#include "CardDeckRuntime.hpp"
5#include "LegacyTextIds.hpp"
6#include "Messaging.hpp"
7#include "PhaseStack.hpp"
8#include "RuleRejections.hpp"
9#include "RuleEconomy.hpp"
11
12#include <algorithm>
13#include <array>
14#include <cstddef>
15#include <cstdint>
16
18{
19 namespace
20 {
21 constexpr std::uint8_t OffBoardSquare = 41;
22
23 // Rule.cpp :
24 // -1 counter
25 // 0 reject
26 // 1 accept
27 // 2 nothing
28 // 3 non-involved AI accepted
29 int tradeStatus = 2;
30
31
32 void sendFive(
33 actions::Type type,
34 std::int64_t a,
35 std::int64_t b,
36 std::int64_t c,
37 std::int64_t d,
38 std::int64_t e)
39 {
40 actions::Message message{};
41
42 message.action = type;
43 message.fromPlayer = BankPlayer;
44 message.toPlayer = AllPlayers;
45
46 message.numberA = a;
47 message.numberB = b;
48 message.numberC = c;
49 message.numberD = d;
50 message.numberE = e;
51
52 messaging::sendAction(message);
53 }
54
55
56 void completed(
57 const actions::Message& message,
58 bool success)
59 {
64 static_cast<std::int64_t>(
65 message.action
66 ),
67 success ? 1 : 0,
68 message.fromPlayer
69 );
70 }
71
72
73 bool activePlayer(
74 const GameState& state,
75 PlayerNumber player)
76 {
77 return
78 player < state.numberOfPlayers &&
79 state.players[player].currentSquare <
80 OffBoardSquare;
81 }
82
83
84 void wrongPhase(
85 const GameState& state,
86 const actions::Message& message)
87 {
89 }
90
91
92 void wrongPlayer(
93 const GameState& state,
94 const actions::Message& message)
95 {
97 }
98
99
100 void sendRestart()
101 {
106 );
107 }
108
109
110 void setEmptyTrade(
112 {
113 // SetEmptyTrade() original.
114 //
115 // Ne pas toucher aux anciennes propriétés déjà
116 // détenues par ESCROW.
117
118 for (SquareState& square :
120 {
121 if (square.owner < MaxPlayers)
122 {
123 square.offeredInTradeTo =
125 }
126 }
127
128
129 for (PlayerState& player :
131 {
132 player.cashGivenInTrade.fill(0);
133 }
134
135
136 for (CardDeck& deck :
137 state.cards)
138 {
139 deck.jailOfferedInTradeTo =
141 }
142
143
144 for (CountHitRecord& hit :
146 {
147 if (hit.tradedItem)
148 {
149 hit.toPlayer =
151
152 hit.tradedItem =
153 false;
154 }
155 }
156 }
157
158
159 bool playerInvolved(
160 const GameState& state,
161 PlayerNumber player)
162 {
163 for (const SquareState& square :
165 {
166 if (
167 square.owner < MaxPlayers &&
168 square.offeredInTradeTo !=
170 {
171 if (
172 square.owner == player ||
173 square.offeredInTradeTo ==
174 player)
175 {
176 return true;
177 }
178 }
179 }
180
181
182 for (PlayerNumber from = 0;
183 from < MaxPlayers;
184 ++from)
185 {
186 for (PlayerNumber to = 0;
187 to < MaxPlayers;
188 ++to)
189 {
190 if (
191 state.players[from]
192 .cashGivenInTrade[to] > 0 &&
193 (
194 from == player ||
195 to == player
196 ))
197 {
198 return true;
199 }
200 }
201 }
202
203
204 for (const CardDeck& deck :
205 state.cards)
206 {
207 if (
208 deck.jailOfferedInTradeTo !=
209 NobodyPlayer &&
210 (
211 deck.jailOwner == player ||
212 deck.jailOfferedInTradeTo ==
213 player
214 ))
215 {
216 return true;
217 }
218 }
219
220
221 for (const CountHitRecord& hit :
223 {
224 if (
225 hit.toPlayer != NobodyPlayer &&
226 hit.tradedItem &&
227 (
228 hit.fromPlayer == player ||
229 hit.toPlayer == player
230 ))
231 {
232 return true;
233 }
234 }
235
236
237 return false;
238 }
239
240
242 const GameState& state)
243 {
244 for (const SquareState& square :
246 {
247 if (
248 square.owner < MaxPlayers &&
249 square.offeredInTradeTo <
251 {
252 return true;
253 }
254 }
255
256
257 for (const CardDeck& deck :
258 state.cards)
259 {
260 if (
261 deck.jailOfferedInTradeTo <
263 {
264 return true;
265 }
266 }
267
268
269 for (PlayerNumber from = 0;
270 from < state.numberOfPlayers;
271 ++from)
272 {
273 for (PlayerNumber to = 0;
275 ++to)
276 {
277 if (
278 state.players[from]
279 .cashGivenInTrade[to] > 0)
280 {
281 return true;
282 }
283 }
284 }
285
286
287 for (const CountHitRecord& hit :
289 {
290 if (
291 hit.toPlayer != NobodyPlayer &&
292 hit.tradedItem)
293 {
294 return true;
295 }
296 }
297
298
299 return false;
300 }
301
302
304 const GameState& state)
305 {
306 // ResendTradeProposal().
307
312 phases::current(state).fromPlayer,
314 );
315
316
317 // Properties.
318
319 for (std::size_t squareNo = 0;
321 ++squareNo)
322 {
323 const SquareState& square =
325
326
327 if (
328 square.owner < MaxPlayers &&
329 square.offeredInTradeTo !=
331 {
332 sendFive(
334 square.owner,
335 square.offeredInTradeTo,
336 static_cast<std::int64_t>(
338 ),
339 squareNo,
340 0
341 );
342 }
343 }
344
345
346 // Cash.
347
348 for (PlayerNumber from = 0;
349 from < MaxPlayers;
350 ++from)
351 {
352 for (PlayerNumber to = 0;
353 to < MaxPlayers;
354 ++to)
355 {
356 const std::int64_t amount =
357 state.players[from]
358 .cashGivenInTrade[to];
359
360
361 if (amount <= 0)
362 {
363 continue;
364 }
365
366
367 sendFive(
369 from,
370 to,
371 static_cast<std::int64_t>(
373 ),
374 amount,
375 0
376 );
377 }
378 }
379
380
381 // Jail cards.
382
383 for (std::size_t deckNo = 0;
384 deckNo <
385 static_cast<std::size_t>(
387 );
388 ++deckNo)
389 {
390 const CardDeck& deck =
392
393
394 if (
395 deck.jailOfferedInTradeTo ==
397 {
398 continue;
399 }
400
401
402 sendFive(
404 deck.jailOwner,
405 deck.jailOfferedInTradeTo,
406 static_cast<std::int64_t>(
408 ),
409 deckNo,
410 0
411 );
412 }
413
414
415 // Immunities / futures.
416
417 for (const CountHitRecord& hit :
419 {
420 if (
421 hit.toPlayer == NobodyPlayer ||
422 !hit.tradedItem)
423 {
424 continue;
425 }
426
427
428 sendFive(
430 hit.fromPlayer,
431 hit.toPlayer,
432 static_cast<std::int64_t>(
433 hit.hitType ==
437 ),
438 hit.hitCount,
439 hit.properties
440 );
441 }
442 }
443
444
448 {
449 const PlayerNumber proposer =
451 .fromPlayer;
452
453
455 state,
457 proposer,
458 newEditor,
459 0
460 );
461
462
463 sendRestart();
464 }
465
466
467 void cancelTrade(
469 PlayerNumber player)
470 {
476 0,
477 player
478 );
479
480
482
483
485 state,
487 0,
488 0,
489 0
490 );
491
492
493 sendRestart();
494 }
495
496
497 std::int64_t playerTradeAssets(
498 const GameState& state,
499 PlayerNumber player)
500 {
501 std::int64_t assets =
502 state.players[player].cash;
503
504
505 for (std::size_t squareNo = 0;
507 ++squareNo)
508 {
509 const SquareState& square =
511
512
513 if (
514 square.owner == player &&
515 square.offeredInTradeTo >=
516 MaxPlayers &&
517 !square.mortgaged)
518 {
519 assets +=
521 static_cast<board::SquareType>(
523 )
524 ).mortgageCost;
525 }
526
527
528 if (square.owner == player)
529 {
530 assets +=
531 (
532 static_cast<std::int64_t>(
533 square.houses
534 ) *
536 static_cast<board::SquareType>(
538 )
539 ).housePurchaseCost
540 + 1
541 ) / 2;
542 }
543 }
544
545
546 return assets;
547 }
548
549
550 std::int64_t playerTradeExpenses(
551 const GameState& state,
552 PlayerNumber player)
553 {
554 std::int64_t expenses = 0;
555
556
557 // Intérêts sur propriétés hypothéquées reçues.
558
559 for (std::size_t squareNo = 0;
561 ++squareNo)
562 {
563 const SquareState& square =
565
566
567 if (
568 square.owner < MaxPlayers &&
569 square.offeredInTradeTo ==
570 player &&
571 square.mortgaged)
572 {
573 expenses +=
574 (
576 static_cast<board::SquareType>(
578 )
579 ).mortgageCost *
581 + 50
582 ) / 100;
583 }
584 }
585
586
587 // Cash donné.
588 //
589 // Le cash reçu n'est délibérément PAS compté comme
590 // actif disponible : il peut arriver trop tard à cause
591 // de l'ordre LIFO des dettes.
592
593 for (PlayerNumber other = 0;
595 ++other)
596 {
597 expenses +=
598 state.players[player]
599 .cashGivenInTrade[other];
600 }
601
602
603 return expenses;
604 }
605
606
609 {
610 const PlayerNumber proposer =
612 .fromPlayer;
613
614
616 state,
618 0,
619 0,
620 0
621 );
622
623
624 std::array<std::uint32_t, MaxPlayers>
626
627
628 std::array<std::int64_t, MaxPlayers>
630
631
632 // ------------------------------------------------
633 // Properties -> ESCROW.
634 // Buildings are sold automatically first.
635 // ------------------------------------------------
636
637 for (std::size_t squareNo = 0;
639 ++squareNo)
640 {
641 SquareState& square =
643
644
645 const PlayerNumber to =
646 square.offeredInTradeTo;
647
648
649 if (
650 square.owner >= MaxPlayers ||
651 to >= MaxPlayers)
652 {
653 continue;
654 }
655
656
657 const auto squareType =
658 static_cast<board::SquareType>(
660 );
661
662
666 );
667
668
670 state,
671 static_cast<std::uint8_t>(
673 )
674 );
675
676
677 if (square.mortgaged)
678 {
682 ).mortgageCost;
683 }
684
685
687 state,
688 static_cast<std::uint8_t>(
690 ),
691 to
692 );
693 }
694
695
696 // ------------------------------------------------
697 // Escrow + intérêts.
698 //
699 // Ordre cyclique descendant depuis le proposant,
700 // exactement comme Rule.cpp.
701 // ------------------------------------------------
702
703 if (state.numberOfPlayers > 0)
704 {
705 for (std::size_t offset = 0;
706 offset <
708 ++offset)
709 {
710 const PlayerNumber player =
711 static_cast<PlayerNumber>(
712 (
713 proposer +
715 offset
716 ) %
718 );
719
720
721 if (
723 player
724 ] != 0)
725 {
727 state,
728 GamePhase::
730 0,
731 player,
733 player
734 ]
735 );
736 }
737
738
739 if (
741 player
742 ] > 0)
743 {
745 state,
746 player,
748 (
750 player
751 ] *
753 + 50
754 ) / 100
755 );
756 }
757 }
758 }
759
760
761 // ------------------------------------------------
762 // Jail cards.
763 // ------------------------------------------------
764
765 for (std::size_t deckNo = 0;
766 deckNo <
767 static_cast<std::size_t>(
769 );
770 ++deckNo)
771 {
772 const PlayerNumber to =
774 .jailOfferedInTradeTo;
775
776
777 if (to < MaxPlayers)
778 {
780 state,
782 .jailOwner,
783 to,
784 static_cast<DeckType>(
785 deckNo
786 )
787 );
788 }
789 }
790
791
792 // ------------------------------------------------
793 // Cash.
794 // ------------------------------------------------
795
796 if (state.numberOfPlayers > 0)
797 {
798 for (std::size_t fromOffset = 0;
799 fromOffset <
801 ++fromOffset)
802 {
803 const PlayerNumber from =
804 static_cast<PlayerNumber>(
805 (
806 proposer +
809 ) %
811 );
812
813
814 for (std::size_t toOffset = 0;
815 toOffset <
817 ++toOffset)
818 {
819 const PlayerNumber to =
820 static_cast<PlayerNumber>(
821 (
822 from +
825 ) %
827 );
828
829
830 const std::int64_t cash =
831 state.players[from]
832 .cashGivenInTrade[to];
833
834
835 if (cash > 0)
836 {
838 state,
839 from,
840 to,
841 cash
842 );
843 }
844 }
845 }
846 }
847
848
849 sendRestart();
850 }
851
852
853 void finishTrade(
855 {
857 false;
858
859
860 // ------------------------------------------------
861 // Activer les nouveaux futures / immunités.
862 //
863 // Duplicates :
864 // mêmes properties + toPlayer + hitType.
865 //
866 // Le test fromPlayer est COMMENTÉ dans le source.
867 // ------------------------------------------------
868
869 for (std::size_t i = 0;
870 i < state.countHits.size();
871 ++i)
872 {
873 CountHitRecord& current =
875
876
877 if (
878 current.toPlayer ==
879 NobodyPlayer ||
880 !current.tradedItem)
881 {
882 continue;
883 }
884
885
886 int count =
887 current.hitCount;
888
889
890 for (std::size_t j = 0;
891 j < state.countHits.size();
892 ++j)
893 {
894 if (j == i)
895 {
896 continue;
897 }
898
899
902
903
904 if (
905 other.properties ==
906 current.properties &&
907 other.toPlayer ==
908 current.toPlayer &&
909 other.hitType ==
910 current.hitType)
911 {
912 count +=
913 other.hitCount;
914
915
916 other.toPlayer =
918 }
919 }
920
921
922 count =
923 std::clamp(
924 count,
925 0,
926 127
927 );
928
929
930 current.hitCount =
931 count;
932
933 current.tradedItem =
934 false;
935
936
937 sendFive(
938 current.hitType ==
940 ? actions::Type::
941 NotifyImmunityCount
944 current.toPlayer,
945 count,
946 OffBoardSquare,
947 current.fromPlayer,
948 current.properties
949 );
950
951
952 sendFive(
954 current.hitType ==
956 ? legacy_text::
957 ErrorImmunityGranted
960 current.fromPlayer,
961 current.toPlayer,
962 count,
963 current.properties
964 );
965
966
967 if (count <= 0)
968 {
969 current.toPlayer =
971 }
972 }
973
974
980 );
981
982
983 tradeStatus = 2;
984
985
987
988 sendRestart();
989 }
990 }
991
992
994 {
995 tradeStatus = 2;
996 }
997
998
1001 const actions::Message& message)
1002 {
1003 const PlayerNumber player =
1004 message.fromPlayer;
1005
1006
1007 if (!activePlayer(state, player))
1008 {
1009 wrongPlayer(state, message);
1010 return;
1011 }
1012
1013
1014 const GamePhase phase =
1016
1017
1018 const bool allowed =
1019 phase == GamePhase::WaitMoveRoll ||
1020 phase == GamePhase::WaitEndTurn ||
1021 phase == GamePhase::WaitJailRoll ||
1022 phase == GamePhase::WaitUtilityRoll ||
1024 phase ==
1025 GamePhase::
1026 JailRollOrPayOrCardDecision ||
1027 phase ==
1028 GamePhase::
1029 FlatOrFractionTaxDecision ||
1030 phase ==
1031 GamePhase::
1032 AuctionOrBuyDecision ||
1033 phase ==
1034 GamePhase::
1035 CollectingPayment ||
1036 phase ==
1037 GamePhase::
1038 FreeUnmortgage ||
1039 phase ==
1042
1043
1044 if (!allowed)
1045 {
1046 wrongPhase(state, message);
1047 return;
1048 }
1049
1050
1051 // ----------------------------------------------------
1052 // Reprendre l'édition pendant ACCEPTANCE.
1053 // ----------------------------------------------------
1054
1055 if (
1056 phase ==
1058 {
1059 if (
1060 phases::current(state).amount != 0 &&
1062 state,
1063 player
1064 ))
1065 {
1068 BankPlayer,
1069 AllPlayers,
1070 legacy_text::
1071 ErrorTradeCantEditDuringAccept,
1072 0,
1073 player
1074 );
1075
1076
1077 completed(
1078 message,
1079 false
1080 );
1081
1082 return;
1083 }
1084
1085
1086 completed(
1087 message,
1088 true
1089 );
1090
1091
1093 state,
1094 player
1095 );
1096
1097
1098 return;
1099 }
1100
1101
1102 // ----------------------------------------------------
1103 // Une transaction précédente doit être entièrement
1104 // réglée avant d'en démarrer une autre.
1105 // ----------------------------------------------------
1106
1108 {
1109 completed(
1110 message,
1111 false
1112 );
1113
1114
1117 BankPlayer,
1118 player,
1119 legacy_text::
1120 ErrorCantTradeUntilSettled,
1121 0,
1122 player
1123 );
1124
1125
1126 return;
1127 }
1128
1129
1131
1132
1134 state,
1136 player,
1137 player,
1138 0
1139 );
1140
1141
1143 true;
1144
1145
1146 // ActionStartTradeEditing() original :
1147 // IgnoreWaitForEverybodyReady = FALSE.
1149
1150
1151 sendRestart();
1152
1153
1154 completed(
1155 message,
1156 true
1157 );
1158 }
1159
1160
1163 const actions::Message& message)
1164 {
1165 const PlayerNumber editor =
1166 message.fromPlayer;
1167
1168
1169 if (
1170 phases::current(state).phase !=
1172 {
1173 wrongPhase(state, message);
1174 return;
1175 }
1176
1177
1178 if (
1179 editor !=
1180 phases::current(state).toPlayer)
1181 {
1182 wrongPlayer(state, message);
1183 return;
1184 }
1185
1186
1187 PlayerNumber from =
1188 static_cast<PlayerNumber>(
1189 message.numberA
1190 );
1191
1192
1193 PlayerNumber to =
1194 static_cast<PlayerNumber>(
1195 message.numberB
1196 );
1197
1198
1199 const auto kind =
1200 static_cast<TradeItemKind>(
1201 message.numberC
1202 );
1203
1204
1205 std::int64_t value =
1206 message.numberD;
1207
1208
1209 const std::uint32_t propertySet =
1210 static_cast<std::uint32_t>(
1211 message.numberE
1212 );
1213
1214
1215 bool success = true;
1216 bool outOfContracts = false;
1217
1218
1219 if (!activePlayer(state, to))
1220 {
1221 success = false;
1222 }
1223
1224
1225 if (success)
1226 {
1227 switch (kind)
1228 {
1230 {
1231 if (
1232 !activePlayer(
1233 state,
1234 from
1235 ) ||
1236 value < 0)
1237 {
1238 success = false;
1239 break;
1240 }
1241
1242
1243 // Pas d'argent simultanément dans les
1244 // deux directions.
1245
1246 if (
1247 state.players[to]
1248 .cashGivenInTrade[from] != 0)
1249 {
1250 state.players[to]
1251 .cashGivenInTrade[from] = 0;
1252
1253
1254 sendFive(
1256 NotifyTradeItem,
1257 to,
1258 from,
1259 static_cast<std::int64_t>(
1261 ),
1262 0,
1263 0
1264 );
1265 }
1266
1267
1268 state.players[from]
1269 .cashGivenInTrade[to] =
1270 value;
1271
1272
1273 break;
1274 }
1275
1276
1278 {
1279 if (
1280 value < 0 ||
1281 value >=
1282 static_cast<std::int64_t>(
1284 ))
1285 {
1286 success = false;
1287 break;
1288 }
1289
1290
1291 const auto squareType =
1292 static_cast<
1294 >(value);
1295
1296
1297 if (
1300 ) == 0)
1301 {
1302 success = false;
1303 break;
1304 }
1305
1306
1307 SquareState& square =
1308 state.squares[
1309 static_cast<std::size_t>(
1310 value
1311 )
1312 ];
1313
1314
1315 from =
1316 square.owner;
1317
1318
1319 if (from >= MaxPlayers)
1320 {
1321 success = false;
1322 break;
1323 }
1324
1325
1326 if (from == to)
1327 {
1328 square.offeredInTradeTo =
1330
1331 to = NobodyPlayer;
1332 }
1333 else
1334 {
1335 square.offeredInTradeTo =
1336 to;
1337 }
1338
1339
1340 break;
1341 }
1342
1343
1345 {
1346 if (
1347 value < 0 ||
1348 value >=
1349 static_cast<std::int64_t>(
1351 ))
1352 {
1353 success = false;
1354 break;
1355 }
1356
1357
1358 CardDeck& deck =
1359 state.cards[
1360 static_cast<std::size_t>(
1361 value
1362 )
1363 ];
1364
1365
1366 from =
1367 deck.jailOwner;
1368
1369
1370 if (from >= MaxPlayers)
1371 {
1372 success = false;
1373 break;
1374 }
1375
1376
1377 if (from == to)
1378 {
1379 deck.jailOfferedInTradeTo =
1381
1382 to = NobodyPlayer;
1383 }
1384 else
1385 {
1386 deck.jailOfferedInTradeTo =
1387 to;
1388 }
1389
1390
1391 break;
1392 }
1393
1394
1397 {
1398 if (
1399 !activePlayer(
1400 state,
1401 from
1402 ) ||
1403 propertySet == 0)
1404 {
1405 success = false;
1406 break;
1407 }
1408
1409
1410 if (
1411 kind ==
1413 !state.options
1415 {
1416 success = false;
1417 break;
1418 }
1419
1420
1421 if (
1422 kind ==
1424 !state.options
1426 {
1427 success = false;
1428 break;
1429 }
1430
1431
1432 value =
1433 std::clamp<std::int64_t>(
1434 value,
1435 -128,
1436 127
1437 );
1438
1439
1440 const CountHitType hitType =
1441 kind ==
1445
1446
1448 nullptr;
1449
1451 nullptr;
1452
1453
1454 for (CountHitRecord& hit :
1456 {
1457 if (
1458 hit.properties ==
1459 propertySet &&
1460 hit.toPlayer == to &&
1461 hit.hitType ==
1462 hitType &&
1463 hit.tradedItem)
1464 {
1465 found = &hit;
1466 break;
1467 }
1468
1469
1470 if (
1471 free == nullptr &&
1472 hit.toPlayer ==
1474 {
1475 free = &hit;
1476 }
1477 }
1478
1479
1480 if (found != nullptr)
1481 {
1482 if (value != 0)
1483 {
1484 found->hitCount =
1485 static_cast<std::int32_t>(
1486 value
1487 );
1488 }
1489 else
1490 {
1491 found->toPlayer =
1493
1494 found->tradedItem =
1495 false;
1496 }
1497 }
1498 else if (free != nullptr)
1499 {
1500 free->properties =
1502
1503 free->fromPlayer =
1504 from;
1505
1506 free->toPlayer =
1507 to;
1508
1509 free->hitType =
1510 hitType;
1511
1512 free->tradedItem =
1513 true;
1514
1515 free->hitCount =
1516 static_cast<std::int32_t>(
1517 value
1518 );
1519 }
1520 else
1521 {
1522 success = false;
1523 outOfContracts = true;
1524 }
1525
1526
1527 break;
1528 }
1529
1530
1531 default:
1532 success = false;
1533 break;
1534 }
1535 }
1536
1537
1538 if (!success)
1539 {
1540 completed(
1541 message,
1542 false
1543 );
1544
1545
1546 if (outOfContracts)
1547 {
1550 BankPlayer,
1551 AllPlayers,
1554 editor
1555 );
1556 }
1557 else
1558 {
1561 BankPlayer,
1562 editor,
1564 0,
1565 editor
1566 );
1567 }
1568
1569
1570 return;
1571 }
1572
1573
1574 completed(
1575 message,
1576 true
1577 );
1578
1579
1580 // Le dernier joueur ayant modifié l'offre devient
1581 // le nouveau proposant.
1582
1583 if (
1584 phases::current(state).fromPlayer !=
1585 editor)
1586 {
1589 BankPlayer,
1590 editor,
1592 0,
1593 editor
1594 );
1595
1596
1597 state.phaseStack[0]
1598 .fromPlayer =
1599 editor;
1600 }
1601
1602
1603 sendFive(
1605 from,
1606 to,
1607 static_cast<std::int64_t>(
1608 kind
1609 ),
1610 value,
1612 );
1613
1614
1615 // ----------------------------------------------------
1616 // Avertissement :
1617 // les bâtiments du monopole seront vendus.
1618 // ----------------------------------------------------
1619
1620 if (
1621 kind ==
1623 value >= 0 &&
1624 value <
1625 static_cast<std::int64_t>(
1627 ) &&
1628 state.squares[
1629 static_cast<std::size_t>(
1630 value
1631 )
1632 ].offeredInTradeTo !=
1634 {
1637 static_cast<board::SquareType>(
1638 value
1639 )
1640 ).group;
1641
1642
1643 for (std::size_t squareNo = 0;
1645 ++squareNo)
1646 {
1647 if (
1649 static_cast<board::SquareType>(
1650 squareNo
1651 )
1652 ).group == group &&
1653 state.squares[
1654 squareNo
1655 ].houses > 0)
1656 {
1659 NotifyErrorMessage,
1660 BankPlayer,
1661 editor,
1664 0,
1665 state.squares[
1666 squareNo
1667 ].owner,
1668 squareNo
1669 );
1670
1671
1672 break;
1673 }
1674 }
1675 }
1676 }
1677
1678
1681 const actions::Message& message)
1682 {
1683 if (
1684 phases::current(state).phase !=
1686 {
1687 wrongPhase(state, message);
1688 return;
1689 }
1690
1691
1692 if (
1693 message.fromPlayer !=
1695 {
1696 wrongPlayer(state, message);
1697 return;
1698 }
1699
1700
1702
1703
1704 completed(
1705 message,
1706 true
1707 );
1708
1709
1710 sendRestart();
1711 }
1712
1713
1716 const actions::Message& message)
1717 {
1718 if (
1719 phases::current(state).phase !=
1721 {
1722 wrongPhase(state, message);
1723 return;
1724 }
1725
1726
1727 if (
1728 message.fromPlayer !=
1730 {
1731 wrongPlayer(state, message);
1732 return;
1733 }
1734
1735
1736 completed(
1737 message,
1738 true
1739 );
1740
1741
1742 // numberB = TO player.
1743 // numberC = CountHitType.
1744 //
1745 // numberA/fromPlayer est volontairement ignoré dans
1746 // le Rule.cpp final.
1747
1748 if (message.numberB >= MaxPlayers)
1749 {
1750 return;
1751 }
1752
1753
1754 for (CountHitRecord& hit :
1756 {
1757 if (
1758 hit.toPlayer ==
1759 static_cast<PlayerNumber>(
1760 message.numberB
1761 ) &&
1762 hit.hitType ==
1763 static_cast<CountHitType>(
1764 message.numberC
1765 ) &&
1766 hit.tradedItem)
1767 {
1768 sendFive(
1770 hit.fromPlayer,
1771 hit.toPlayer,
1772 static_cast<std::int64_t>(
1773 hit.hitType ==
1777 ),
1778 0,
1779 hit.properties
1780 );
1781
1782
1783 hit.toPlayer =
1785 }
1786 }
1787 }
1788
1789
1792 const actions::Message& message)
1793 {
1794 const PlayerNumber editor =
1795 message.fromPlayer;
1796
1797
1798 if (
1799 phases::current(state).phase !=
1801 {
1802 wrongPhase(state, message);
1803 return;
1804 }
1805
1806
1807 if (
1808 editor !=
1809 phases::current(state).toPlayer)
1810 {
1811 wrongPlayer(state, message);
1812 return;
1813 }
1814
1815
1816 completed(
1817 message,
1818 true
1819 );
1820
1821
1822 std::int64_t opCode =
1823 message.numberA;
1824
1825
1826 if (
1827 opCode == 0 &&
1829 {
1830 // Le source convertit automatiquement
1831 // "vote sur trade vide" en annulation.
1832 opCode = 2;
1833 }
1834
1835
1836 switch (opCode)
1837 {
1838 // ------------------------------------------------
1839 // Soumettre au vote.
1840 // ------------------------------------------------
1841
1842 case 0:
1843 {
1844 for (PlayerState& player :
1845 state.players)
1846 {
1847 player.tradeAccepted =
1848 false;
1849 }
1850
1851
1852 const PlayerNumber proposer =
1854 .fromPlayer;
1855
1856
1858 state,
1860 proposer,
1861 0,
1862 message.numberB != 0
1863 ? 1
1864 : 0
1865 );
1866
1867
1868 sendRestart();
1869
1870
1871 // Le joueur qui soumet le deal
1872 // l'accepte automatiquement via
1873 // ActionTradeAccept(), exactement comme le faux
1874 // message du source.
1875
1877
1880
1881 automaticAccept.fromPlayer =
1882 editor;
1883
1884 automaticAccept.toPlayer =
1885 BankPlayer;
1886
1887 automaticAccept.numberA = 1;
1888 automaticAccept.numberB = 1;
1889
1890
1892 state,
1894 );
1895
1896
1897 break;
1898 }
1899
1900
1901 // ------------------------------------------------
1902 // Passer l'édition à un autre joueur.
1903 // ------------------------------------------------
1904
1905 case 1:
1906 {
1907 const PlayerNumber nextEditor =
1908 static_cast<PlayerNumber>(
1909 message.numberB
1910 );
1911
1912
1913 if (
1915 state,
1917 ))
1918 {
1919 state.phaseStack[0]
1920 .toPlayer =
1921 nextEditor;
1922
1923
1926 NotifyTradeEditor,
1927 BankPlayer,
1928 AllPlayers,
1930 );
1931 }
1932 else
1933 {
1934 wrongPlayer(
1935 state,
1936 message
1937 );
1938 }
1939
1940
1941 break;
1942 }
1943
1944
1945 // ------------------------------------------------
1946 // Annuler.
1947 // ------------------------------------------------
1948
1949 case 2:
1950 {
1952 state,
1953 editor
1954 );
1955
1956 break;
1957 }
1958
1959
1960 default:
1961 sendRestart();
1962 break;
1963 }
1964 }
1965
1966
1969 const actions::Message& message)
1970 {
1971 if (
1972 phases::current(state).phase !=
1974 {
1975 wrongPhase(state, message);
1976 return;
1977 }
1978
1979
1980 const PlayerNumber player =
1981 message.fromPlayer;
1982
1983
1984 const bool privateTrade =
1986
1987
1988 if (
1989 !activePlayer(
1990 state,
1991 player
1992 ) ||
1993 (
1994 privateTrade &&
1996 state,
1997 player
1998 )
1999 ))
2000 {
2001 // Cas normal pour un AI extérieur à un deal privé :
2002 // il est simplement ignoré.
2003
2004 completed(
2005 message,
2006 false
2007 );
2008
2009 return;
2010 }
2011
2012
2013 // Un non-participant ne peut pas rejeter une offre
2014 // publique ; il peut seulement accepter ou l'éditer.
2015
2016 if (
2018 state,
2019 player
2020 ) &&
2021 message.numberA == 0)
2022 {
2023 wrongPlayer(
2024 state,
2025 message
2026 );
2027
2028 return;
2029 }
2030
2031
2032 if (message.numberB != 3)
2033 {
2034 tradeStatus =
2035 static_cast<int>(
2036 message.numberB
2037 );
2038 }
2039
2040
2041 completed(
2042 message,
2043 true
2044 );
2045
2046
2047 // ----------------------------------------------------
2048 // Rejet.
2049 // ----------------------------------------------------
2050
2051 if (message.numberA == 0)
2052 {
2054 state,
2055 player
2056 );
2057
2058 return;
2059 }
2060
2061
2062 // ----------------------------------------------------
2063 // Validation juridique :
2064 //
2065 // chaque participant doit soit :
2066 // - donner ET recevoir
2067 // - ne rien donner ET ne rien recevoir
2068 // ----------------------------------------------------
2069
2070 bool gettingSomething = false;
2071 bool givingSomething = false;
2072
2073
2074 for (const SquareState& square :
2075 state.squares)
2076 {
2077 if (
2078 square.owner < MaxPlayers &&
2079 square.offeredInTradeTo ==
2080 player)
2081 {
2082 gettingSomething = true;
2083 }
2084 else if (
2085 square.offeredInTradeTo <
2086 MaxPlayers &&
2087 square.owner ==
2088 player)
2089 {
2090 givingSomething = true;
2091 }
2092 }
2093
2094
2095 for (const CardDeck& deck :
2096 state.cards)
2097 {
2098 if (
2099 deck.jailOfferedInTradeTo ==
2100 player)
2101 {
2102 gettingSomething = true;
2103 }
2104 else if (
2105 deck.jailOfferedInTradeTo <
2106 MaxPlayers &&
2107 deck.jailOwner ==
2108 player)
2109 {
2110 givingSomething = true;
2111 }
2112 }
2113
2114
2115 for (const CountHitRecord& hit :
2117 {
2118 if (
2119 hit.toPlayer != NobodyPlayer &&
2120 hit.tradedItem)
2121 {
2122 if (hit.toPlayer == player)
2123 {
2124 gettingSomething = true;
2125 }
2126
2127 if (hit.fromPlayer == player)
2128 {
2129 givingSomething = true;
2130 }
2131 }
2132 }
2133
2134
2135 for (PlayerNumber from = 0;
2136 from < state.numberOfPlayers;
2137 ++from)
2138 {
2139 for (PlayerNumber to = 0;
2141 ++to)
2142 {
2143 const std::int64_t cash =
2144 state.players[from]
2145 .cashGivenInTrade[to];
2146
2147
2148 if (cash <= 0)
2149 {
2150 continue;
2151 }
2152
2153
2154 if (from == player)
2155 {
2156 givingSomething = true;
2157 }
2158
2159 if (to == player)
2160 {
2161 gettingSomething = true;
2162 }
2163 }
2164 }
2165
2166
2167 if (
2170 {
2173 BankPlayer,
2174 AllPlayers,
2176 0,
2177 player
2178 );
2179
2180
2182
2183
2185 state,
2187 0,
2188 0,
2189 0
2190 );
2191
2192
2193 sendRestart();
2194
2195 return;
2196 }
2197
2198
2199 // ----------------------------------------------------
2200 // Le deal ne doit pas rendre le joueur insolvable.
2201 // ----------------------------------------------------
2202
2203 const std::int64_t assets =
2205 state,
2206 player
2207 );
2208
2209
2210 const std::int64_t expenses =
2212 state,
2213 player
2214 );
2215
2216
2217 if (expenses > assets)
2218 {
2221 BankPlayer,
2222 player,
2224 expenses,
2225 player,
2226 assets
2227 );
2228
2229
2231
2232
2234 state,
2236 0,
2237 0,
2238 0
2239 );
2240
2241
2242 sendRestart();
2243
2244 return;
2245 }
2246
2247
2248 state.players[player]
2249 .tradeAccepted =
2250 true;
2251
2252
2255 BankPlayer,
2256 AllPlayers,
2258 0,
2259 player
2260 );
2261
2262
2263 // ----------------------------------------------------
2264 // Tout le monde a accepté ?
2265 // ----------------------------------------------------
2266
2267 bool allAccepted = true;
2268
2269
2270 for (PlayerNumber testPlayer = 0;
2271 testPlayer <
2273 ++testPlayer)
2274 {
2275 if (
2276 !activePlayer(
2277 state,
2279 ))
2280 {
2281 continue;
2282 }
2283
2284
2285 if (
2286 privateTrade &&
2288 state,
2290 ))
2291 {
2292 continue;
2293 }
2294
2295
2296 if (
2297 !state.players[
2299 ].tradeAccepted)
2300 {
2301 allAccepted = false;
2302 break;
2303 }
2304 }
2305
2306
2307 if (!allAccepted)
2308 {
2309 sendRestart();
2310 return;
2311 }
2312
2313
2315 state
2316 );
2317 }
2318
2319
2322 const actions::Message& message)
2323 {
2324 switch (
2325 phases::current(state).phase)
2326 {
2327 // ------------------------------------------------
2328 // GF_EDITING_TRADE
2329 // ------------------------------------------------
2330
2332 {
2334 state
2335 );
2336
2337
2338 // Premier passage :
2339 // synchroniser tous les clients.
2340 if (
2342 state,
2344 NotifyTradeStarted
2345 ))
2346 {
2347 return true;
2348 }
2349
2350
2351 // Second passage :
2352 // le trade est resynchronisé, on annonce
2353 // l'éditeur réel.
2354
2357 BankPlayer,
2358 AllPlayers,
2360 .toPlayer
2361 );
2362
2363
2364 return true;
2365 }
2366
2367
2368 // ------------------------------------------------
2369 // GF_TRADE_ACCEPTANCE
2370 // ------------------------------------------------
2371
2373 {
2374 std::uint32_t playerSet = 0;
2375 std::uint32_t involvedSet = 0;
2376
2377
2378 const bool privateTrade =
2380
2381
2382 for (PlayerNumber player = 0;
2383 player <
2385 ++player)
2386 {
2387 if (
2388 !activePlayer(
2389 state,
2390 player
2391 ))
2392 {
2393 continue;
2394 }
2395
2396
2397 const bool involved =
2399 state,
2400 player
2401 );
2402
2403
2404 if (involved)
2405 {
2406 involvedSet |=
2407 1u << player;
2408 }
2409
2410
2411 if (
2412 !state.players[player]
2413 .tradeAccepted &&
2414 (
2415 !privateTrade ||
2416 involved
2417 ))
2418 {
2419 playerSet |=
2420 1u << player;
2421 }
2422 }
2423
2424
2427 NotifyTradeAcceptanceDecision,
2428 BankPlayer,
2429 AllPlayers,
2430 playerSet,
2432 );
2433
2434
2435 return true;
2436 }
2437
2438
2439 // ------------------------------------------------
2440 // GF_TRADE_FINISHED
2441 // ------------------------------------------------
2442
2444 {
2445 // Le source ignore les restart externes.
2446
2447 if (
2448 message.fromPlayer !=
2449 BankPlayer)
2450 {
2451 return true;
2452 }
2453
2454
2455 // Attendre que toutes les notifications,
2456 // dettes et escrows aient quitté la queue.
2457
2458 if (
2460 {
2461 return true;
2462 }
2463
2464
2466
2467 return true;
2468 }
2469
2470
2471 default:
2472 return false;
2473 }
2474 }
2475
2476
2479 {
2480 // ActionTick() original :
2481 //
2482 // GF_TRADE_FINISHED attend la queue vide,
2483 // puis injecte ACTION_RESTART_PHASE.
2484
2485 if (
2489 {
2490 return;
2491 }
2492
2493
2494 if (
2496 {
2497 return;
2498 }
2499
2500
2501 sendRestart();
2502 }
2503}
2504
2505
std::size_t count
std::size_t offset
std::uint16_t group
unsigned index
Kind kind
std::uint16_t value
Definition Timers.cpp:16
CardTag other
constexpr std::int64_t ErrorTradeGiveAndGet
constexpr std::int64_t ErrorTradeHouseSale
constexpr std::int64_t ErrorTradeAccepted
constexpr std::int64_t ErrorTradeChanging
constexpr std::int64_t ErrorTradeBankruptcy
constexpr std::int64_t ErrorCantTradeThat
constexpr std::int64_t ErrorImmunityOutOf
constexpr std::int64_t ErrorFutureRentGranted
constexpr std::int64_t ErrorTradeRejected
std::size_t currentQueueSize()
bool sendAction(const actions::Message &message)
const SquareDefinition & definition(SquareType square)
PropertySet propertyBit(SquareType square)
bool transferGetOutOfJail(GameState &state, PlayerNumber fromPlayer, PlayerNumber toPlayer, DeckType deckType)
void stackDebt(GameState &state, PlayerNumber fromPlayer, PlayerNumber toPlayer, std::int64_t amount)
void transferPropertyForSettlement(GameState &state, std::uint8_t squareNo, PlayerNumber toPlayer)
void sellAllBuildingsForSettlement(GameState &state, std::uint8_t squareNo)
bool pop(GameState &state)
const PendingPhase & current(const GameState &state)
bool push(GameState &state, GamePhase phase, PlayerNumber fromPlayer, PlayerNumber toPlayer, std::int64_t amount)
void switchTo(GameState &state, GamePhase phase, PlayerNumber fromPlayer, PlayerNumber toPlayer, std::int64_t amount)
void wrongPlayer(const GameState &state, const actions::Message &message)
void wrongPhase(const GameState &state, const actions::Message &message)
bool beginWaitOnce(GameState &state, actions::Type hint)
void actionTradeItem(GameState &state, const actions::Message &message)
void onIdleTick(GameState &state)
void actionStartTradeEditing(GameState &state, const actions::Message &message)
void actionClearTradedContracts(GameState &state, const actions::Message &message)
void actionTradeAccept(GameState &state, const actions::Message &message)
void actionClearTradeItems(GameState &state, const actions::Message &message)
void actionTradeEditingDone(GameState &state, const actions::Message &message)
bool restartTradePhase(GameState &state, const actions::Message &message)
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr std::size_t MaxCountHitSets
Definition RuleTypes.hpp:53
constexpr std::size_t SquareCount
Definition RuleTypes.hpp:24
const GameState & state()
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
constexpr PlayerNumber NobodyPlayer
Definition RuleTypes.hpp:16
constexpr PlayerNumber AllPlayers
Definition RuleTypes.hpp:17
rules::PlayerNumber fromPlayer
Definition Actions.hpp:128
std::array< CardDeck, static_cast< std::size_t >(DeckType::Count) > cards
std::array< CountHitRecord, MaxCountHitSets > countHits
std::array< SquareState, SquareCount > squares
std::array< PendingPhase, MaxPendingPhases > phaseStack
std::uint8_t numberOfPendingPhases
std::array< PlayerState, MaxPlayers > players